-
Notifications
You must be signed in to change notification settings - Fork 3k
Let Gradio apps also be MCP Servers #10984
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🪼 branch checks and previews
Install Gradio from this PR pip install https://gradio-pypi-previews.s3.amazonaws.com/37eed343820421df566b8aaec4210c92b60e2087/gradio-5.27.1-py3-none-any.whl Install Gradio Python Client from this PR pip install "gradio-client @ git+https://github.com/gradio-app/gradio@37eed343820421df566b8aaec4210c92b60e2087#subdirectory=client/python" Install Gradio JS Client from this PR npm install https://gradio-npm-previews.s3.amazonaws.com/37eed343820421df566b8aaec4210c92b60e2087/gradio-client-1.14.2.tgz Use Lite from this PR <script type="module" src="https://gradio-lite-previews.s3.amazonaws.com/37eed343820421df566b8aaec4210c92b60e2087/dist/lite.js""></script> |
🦄 change detectedThis Pull Request includes changes to the following packages.
With the following changelog entry.
Maintainers or the PR author can modify the PR title to modify this entry.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
@@ -12,11 +12,14 @@ | |||
import InstallSnippet from "./InstallSnippet.svelte"; | |||
import CodeSnippet from "./CodeSnippet.svelte"; | |||
import RecordingSnippet from "./RecordingSnippet.svelte"; | |||
import CopyButton from "./CopyButton.svelte"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
code={JSON.stringify( | ||
{ | ||
mcpServers: { | ||
gradio: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use the space-id here if available so that mcp serves have unique names for spaces.
print("output", output) | ||
return self.postprocess_output_data(output["data"]) | ||
|
||
@server.list_tools() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an issue when using multiple gradio apps as mcp servers at the same time in cursor.
I had two active gradio mcp sessions, one was a dummy audio reversal interface, and the other was a dummy audio transcription server. I gave each server a unique name but each had one tool called predict
.
When I told it to transcribe audio, it kept using the predict function from the dummy audio reversal interface instead of the audio transcription server. Even after I prompted it to use the correct mcp server! Only after disabling the other mcp server did it work.
This is an app issue instead of a server issue (we should file an issue with cursor?), but should we give our tools unique names (perhaps by prepending with the space id when available) to maximize the chance of gradio mcp servers "just working"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything should be addressed now @freddyaboulton! MCP tool names now include a prefix if running on Spaces: ![]() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome PR! Left some questions mainly on the code in the new guide. Functionality is great. let's ship ✅
guides/10_other-tutorials/building-an-mcp-client-with-gradio.md
Outdated
Show resolved
Hide resolved
guides/10_other-tutorials/building-an-mcp-client-with-gradio.md
Outdated
Show resolved
Hide resolved
stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params)) | ||
self.stdio, self.write = stdio_transport | ||
|
||
self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the benefit of using AsyncExitStack
over just using ClientSession
and stdio_client
in an async with
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, will leave it to @yvrjsharma to make the change if needed
self.anthropic = Anthropic() | ||
self.tools = [] | ||
|
||
def connect(self, server_path: str) -> str: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think connect can be async if it's just just used in the connect button click event.
{"role": "assistant", "content": "Please connect to an MCP server first."} | ||
], gr.Textbox(value="") | ||
|
||
new_messages = loop.run_until_complete(self._process_query(message, history)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be clearer if everything was async rather than mixing sync and async scopes via loop.run_until_complete
. Anthropic has an async client so I think it's possible just to make everything async.
Switched over the MCP Client Guide to use the Gradio Python Client. I don't have the full context for the other changes, so I think it's fine to leave as is until @yvrjsharma is back! |
Thanks so much for the reviews and contributions @freddyaboulton @yvrjsharma @evalstate @cocktailpeanut! |
* changes * testing * add changeset * changes * changes * changes * add changeset * changes * changes * revert demo * changes * fixes * changes * format * changes * changes * format * notebook * lcean * format * format * add changeset * add changeset * changes * changes * changes * changes * changes * format * changes * changes * changes * changes * add changeset * Changes * changes * changes * changes * changes * changes * changes * add changeset * hygiene * next * changes * changes * changes * changes * changes * changes * changes * add demo * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * revert demos * changes * changes * format * fix utils * changes * format * changes * more tests * changes * changes * changes * changes * pydantic * changes * change * mcp * calculator * remove base64 * changes * changes * changes * changes * add changeset * changes * add changeset * guide * changes * changes * guides * change * changes * changes * guide * changes * changes * changes * changes * tests * reqs * fix * change * changes * changes * changes * changes * changes * changes * fixes * fixes * changes * format * guide --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
Gradio apps are also now MCP Servers. TLDR: to use, just add
mcp_server=True
in.launch()
This works for Gradio apps running locally as well as hosted Gradio apps on Spaces. Keep reading on how to test this PR...
Testing MCP Server Running on Spaces
To make this easy to test, I've launched two Gradio apps on Spaces using this PR that you can use to test:
Testing MCP Server Running Locally
To test this out locally, first install
gradio
from this PR following the instructions below. Also installmcp
e.g.pip install mcp
Then, launch a Gradio app like this:
Note the
.launch(mcp_server=True)
.Once you launch the Gradio app, the "view API" page should include a 4th "language" option called MCP. There will also be a code box that shows that configuration that you need to paste inside your MCP Client (e.g. Cline).
Note that since this uses SSE protocol, this will work for any client that supports that such as Cline or Cursor. I've also added instructions for integrating with clients like Claude Desktop that do not
You can also test with the MCP Inspector, which is a fantastic debugging tool.
Once you post the config json in e.g. Cline, you should be able to ask a question like: "how many rs in strawberry" and the model should respond like this:
Closes: #10949