File size: 5,947 Bytes
2804df5 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 2804df5 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 49cb9f5 76d5023 2804df5 76d5023 2804df5 49cb9f5 2804df5 49cb9f5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
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.") |