Spaces:
Running
Running
| import atexit | |
| import os | |
| import threading | |
| from typing import Any | |
| import gradio as gr | |
| import spaces | |
| from huggingface_hub import get_token | |
| from smolagents import CodeAgent, InferenceClientModel, MCPClient | |
| # Your existing MCP sentiment server. | |
| MCP_SERVER_URL = os.getenv( | |
| "MCP_SERVER_URL", | |
| "https://zlysunshine-mcp-sentiment.hf.space/gradio_api/mcp/", | |
| ) | |
| # This can be changed later through a Space variable. | |
| MODEL_ID = os.getenv( | |
| "MODEL_ID", | |
| "Qwen/Qwen3-Next-80B-A3B-Thinking", | |
| ) | |
| _agent: CodeAgent | None = None | |
| _mcp_client: MCPClient | None = None | |
| _initialization_lock = threading.Lock() | |
| def create_agent() -> CodeAgent: | |
| """ | |
| Lazily connect to the remote MCP server and create the agent. | |
| Returns: | |
| A CodeAgent configured with the remote MCP tools. | |
| """ | |
| global _agent | |
| global _mcp_client | |
| if _agent is not None: | |
| return _agent | |
| with _initialization_lock: | |
| if _agent is not None: | |
| return _agent | |
| # On Hugging Face Spaces, this comes from the HF_TOKEN secret. | |
| # In Codespaces, it can fall back to the locally saved HF login. | |
| token = os.getenv("HF_TOKEN") or get_token() | |
| if not token: | |
| raise RuntimeError( | |
| "No Hugging Face token was found. " | |
| "Set HF_TOKEN or run `hf auth login`." | |
| ) | |
| client: MCPClient | None = None | |
| try: | |
| client = MCPClient( | |
| { | |
| "url": MCP_SERVER_URL, | |
| "transport": "streamable-http", | |
| }, | |
| structured_output=True, | |
| ) | |
| tools = client.get_tools() | |
| if not tools: | |
| raise RuntimeError( | |
| "The MCP server connected successfully but returned no tools." | |
| ) | |
| print("MCP tools discovered:") | |
| for tool in tools: | |
| print(f"- {tool.name}: {tool.description}") | |
| model = InferenceClientModel( | |
| model_id=MODEL_ID, | |
| token=token, | |
| timeout=120, | |
| max_tokens=1200, | |
| ) | |
| agent = CodeAgent( | |
| tools=[*tools], | |
| model=model, | |
| max_steps=4, | |
| additional_authorized_imports=[ | |
| "json", | |
| "ast", | |
| ], | |
| ) | |
| _mcp_client = client | |
| _agent = agent | |
| return agent | |
| except Exception: | |
| if client is not None: | |
| client.disconnect() | |
| raise | |
| def close_mcp_connection() -> None: | |
| """Close the long-lived MCP connection when the app stops.""" | |
| global _mcp_client | |
| if _mcp_client is not None: | |
| try: | |
| _mcp_client.disconnect() | |
| except Exception as error: | |
| print(f"Error while closing MCP client: {error}") | |
| finally: | |
| _mcp_client = None | |
| atexit.register(close_mcp_connection) | |
| def respond(message: str, history: list[dict[str, Any]]) -> str: | |
| """ | |
| Answer a user question using tools from the remote MCP server. | |
| Args: | |
| message: The user's latest chat message. | |
| history: Previous Gradio chat messages. | |
| Returns: | |
| The agent's final response. | |
| """ | |
| del history # The course example treats each request independently. | |
| cleaned_message = message.strip() | |
| if not cleaned_message: | |
| return "Please enter a question." | |
| try: | |
| agent = create_agent() | |
| result = agent.run(cleaned_message) | |
| return str(result) | |
| except Exception as error: | |
| return ( | |
| "The MCP client could not complete the request.\n\n" | |
| f"Error type: {type(error).__name__}\n" | |
| f"Details: {error}\n\n" | |
| "Check that the sentiment MCP server is running and that " | |
| "the client Space has an HF_TOKEN secret with inference access." | |
| ) | |
| demo = gr.ChatInterface( | |
| fn=respond, | |
| title="Gradio MCP Client Agent", | |
| description=( | |
| "An AI agent that connects to the " | |
| "zlysunshine/mcp-sentiment MCP server and uses its tools." | |
| ), | |
| examples=[ | |
| ( | |
| "You must use the sentiment_analysis tool to analyze: " | |
| "'This MCP course is excellent, but deployment was frustrating.'" | |
| ), | |
| ( | |
| "Call the sentiment tool for: " | |
| "'I am happy that the application finally works.'" | |
| ), | |
| ], | |
| ) | |
| if __name__ == "__main__": | |
| demo.queue().launch( | |
| server_name="0.0.0.0", | |
| ) | |