Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import config | |
| import utils | |
| # Import obb (don't reload it) | |
| if gr.NO_RELOAD: | |
| from openbb import obb | |
| # A set of submodules to include when searching for tools, registering | |
| # them to the MCP server, and dynamically building the Gradio interface | |
| # List of valid values: https://docs.openbb.co/platform/reference | |
| INCLUDE = {"currency", "equity", "news"} | |
| # Get tool names and guides | |
| print("Getting tool names and guides...") | |
| ARGS = "obb", obb # type: ignore | |
| tool_names = utils.get_callable_names(*ARGS, include=INCLUDE) | |
| tool_guides = utils.generate_callable_guides(*ARGS, tool_names) | |
| # Build the demo interface | |
| print("Building the demo...") | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# OpenBB MCP") | |
| # Reference guide | |
| with gr.Accordion("Tool Reference Guide ๐", open=False): | |
| for tool_name, tool_guide in zip(tool_names, tool_guides): | |
| # Create a collapsible for each tool | |
| with gr.Accordion(f"{tool_name}", open=False): | |
| gr.Markdown(f"```\n{tool_guide}```") | |
| # Dynamically generate tool test UI | |
| tool_params = utils.get_callable_params(*ARGS, tool_name) | |
| tool_param_inputs = [] | |
| tool_param_kinds = [] | |
| with gr.Row(): | |
| for param in tool_params: | |
| tool_param_inputs.append( | |
| gr.Textbox( | |
| label=param["name"], | |
| value=str(param["default"]) if param["default"] else "" | |
| ) | |
| ) | |
| tool_param_kinds.append(param["kind"]) | |
| output = gr.Textbox(label="Output", lines=10) | |
| test_btn = gr.Button("Run") | |
| # Format vars for dynamic function generation | |
| t = tool_name | |
| tool_name = tool_name.replace(".", "_") | |
| tool_guide = tool_guide.replace(t, tool_name) | |
| tool_param_names = [ | |
| param["name"] for param in tool_params | |
| ] | |
| csv_tool_names = ", ".join(tool_param_names) | |
| # Dynamically generate function | |
| # TODO: Fragile. Refactor if possible | |
| namespace = { # Create a local namespace | |
| "utils": utils, | |
| "ARGS": ARGS, | |
| "tool_param_kinds": tool_param_kinds | |
| } | |
| exec(f"""\ | |
| import inspect | |
| import json | |
| def {tool_name}({csv_tool_names}) -> str: | |
| \"\"\" | |
| {tool_guide} | |
| \"\"\" | |
| print("{tool_name}:", locals()) | |
| args = [] | |
| try: | |
| kwargs = json.loads(kwargs) | |
| except Exception: | |
| kwargs = {{}} | |
| for kind, name, value in zip( | |
| tool_param_kinds, | |
| {tool_param_names}, | |
| [{csv_tool_names}], | |
| ): | |
| if kind in {{ | |
| inspect.Parameter.POSITIONAL_ONLY, | |
| inspect.Parameter.POSITIONAL_OR_KEYWORD, | |
| }}: | |
| args.append(value if value else None) | |
| return utils.test_callable(*ARGS, "{tool_name}", *args, **kwargs) | |
| """, namespace) | |
| # Extract tool from namespace and register to the MCP server | |
| test_btn.click( | |
| fn=namespace[tool_name], # type: ignore | |
| inputs=tool_param_inputs, | |
| outputs=output, | |
| ) | |
| # Usage instructions | |
| with gr.Accordion("Use via MCP ๐ ๏ธ", open=False): | |
| gr.Markdown("""\ | |
| **SSE support**: To add this MCP to clients that support SSE (e.g. Cursor, Windsurf, Cline), simply add the following configuration to your MCP config: | |
| ``` | |
| { | |
| "mcpServers": { | |
| "OpenBB-MCP": { | |
| "url": "http://xarical-openbb-mcp.hf.space/gradio_api/mcp/sse" | |
| } | |
| } | |
| } | |
| ``` | |
| **Stdio support**: For clients that only support stdio, first install Node.js. Then, you can use the following command: | |
| ``` | |
| { | |
| "mcpServers": { | |
| "OpenBB-MCP": { | |
| "command": "npx", | |
| "args": [ | |
| "mcp-remote", | |
| "http://xarical-openbb-mcp.hf.space/gradio_api/mcp/sse", | |
| "--transport", | |
| "sse-only" | |
| ] | |
| } | |
| } | |
| } | |
| ``` | |
| """) | |
| # Launch the demo | |
| if __name__ == "__main__": | |
| print("Starting the demo...") | |
| demo.launch( | |
| server_port=7860, | |
| show_api=False, | |
| mcp_server=True, | |
| ) | |