import gradio as gr | |
import typing as ty | |
# Importing the tool files automatically registers them with the mcp_tool registry | |
# This assumes all tool files are in the 'tools' directory and end with '_tool.py' | |
# A more robust approach might explicitly import each tool file. | |
# For this example, we explicitly import the one tool file. | |
import tools.time_tool | |
# Import the mcp_tool registry instance | |
from utils.mcp_decorator import mcp_tool | |
def update_tool_info(selected_api_name: str) -> ty.List[ty.Union[gr.update, dict]]: | |
""" | |
Updates the displayed docstring and the visibility of tool UI groups | |
based on the selected tool from the dropdown. | |
Args: | |
selected_api_name: The api_name of the tool selected in the dropdown. | |
Returns: | |
A list of gr.update objects for the docstring and each tool UI group. | |
The list must match the order of outputs specified in the dropdown.change call. | |
""" | |
updates = [] | |
# 1. Update Docstring | |
docstring_value = "" | |
docstring_visible = False | |
if selected_api_name: | |
tool_info = mcp_tool.get_tool_info(selected_api_name) | |
if tool_info and tool_info.get('tool_func') and tool_info['tool_func'].__doc__: | |
docstring_value = f"### Tool Documentation\n---\n{tool_info['tool_func'].__doc__}\n---" | |
docstring_visible = True | |
# Correct way to generate an update for the docstring component | |
updates.append(gr.update(visible=docstring_visible, value=docstring_value)) | |
# 2. Update Visibility of Tool UI Groups | |
all_tools_api_names = [api_name for _, api_name in mcp_tool.get_tools_list()] | |
# Generate visibility updates for each tool group | |
for api_name_in_list in all_tools_api_names: | |
is_selected_tool = (api_name_in_list == selected_api_name) | |
# Correct way to generate an update for a Group component | |
updates.append(gr.update(visible=is_selected_tool)) | |
# The total number of updates must match the number of outputs in the dropdown.change call. | |
# outputs = [doc_display, *tool_ui_groups_list] | |
# inputs = [dropdown] | |
return updates | |
# --- Gradio App Layout --- | |
with gr.Blocks(title="MCP Server Demo") as demo: | |
gr.Markdown("# Gradio MCP Server Demo") | |
gr.Markdown("Select a tool to view its documentation and UI controls.") | |
# Get defined tools | |
tool_options = mcp_tool.get_tools_list() # Returns list of (name, api_name) | |
if not tool_options: | |
gr.Warning("No tools defined. Please check the 'tools' directory.") | |
gr.Markdown("No tools available.") | |
else: | |
# Dropdown to select tool. Using api_name as value for easier lookup. | |
dropdown = gr.Dropdown( | |
choices=[(name, api_name) for name, api_name in tool_options], | |
label="Select a Tool", | |
interactive=True, | |
value=None # Start with no tool selected | |
) | |
# Markdown component to display tool documentation | |
# Needs a specific elem_id or be directly referenced as an output | |
doc_display = gr.Markdown(label="Tool Documentation", visible=False) | |
# Container to hold dynamic UI controls. | |
tool_uis_container = gr.Column() | |
# List to hold the UI groups for each tool. | |
# The order in this list MUST match the order in the outputs list of dropdown.change | |
tool_ui_groups_list = [] | |
# Dynamically create UI components for each tool | |
with tool_uis_container: | |
for tool_name, api_name in tool_options: | |
ui_builder = mcp_tool.get_tool_ui_builder(api_name) | |
if ui_builder: | |
# Call the UI builder function to get the components (should be a gr.Group/Column) | |
tool_ui_group = ui_builder() | |
# Ensure the group is initially hidden (build_ui_control should ideally do this, but reinforce here) | |
tool_ui_group.visible = False # Overwrite potential builder default if needed | |
# Add the group to our list for output mapping | |
tool_ui_groups_list.append(tool_ui_group) | |
else: | |
# If a tool has no UI builder, we still need a placeholder in the outputs list | |
# to keep the order consistent for the updates list. | |
# An empty hidden Group works as a placeholder. | |
with gr.Group(visible=False) as empty_group_placeholder: | |
# Optional: add a tiny markdown saying no UI | |
gr.Markdown(f"No UI defined for {tool_name} ({api_name})", visible=False) | |
tool_ui_groups_list.append(empty_group_placeholder) | |
# --- Event Handling --- | |
# When the dropdown selection changes, update the displayed info and controls | |
# The outputs list defines which components will be updated by update_tool_info, | |
# and their order must match the order of elements in the list returned by update_tool_info. | |
outputs_list = [doc_display] + tool_ui_groups_list | |
dropdown.change( | |
fn=update_tool_info, | |
inputs=[dropdown], # Input is the selected api_name from the dropdown | |
outputs=outputs_list, # Outputs are the doc display and all the tool UI groups | |
) | |
# Trigger an initial update after the layout is built if you want | |
# a default tool's UI to show on load. If dropdown value is None, | |
# calling this will hide everything initially, which is also a valid state. | |
# demo.load(fn=update_tool_info, inputs=[dropdown], outputs=outputs_list) | |
# --- Launch the Gradio App as an MCP Server --- | |
if __name__ == "__main__": | |
print("Launching Gradio app with MCP server enabled...") | |
demo.launch( | |
server_name="0.0.0.0", # Required for Spaces | |
mcp_server=True, # Enable the MCP server endpoint | |
) | |
print("Gradio app launched.") |