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.")