Skip to content

Commit cb5cd6d

Browse files
authored
Merge pull request #88 from UiPath/feat/slack_agent_smaple
samples: improve dynamic server with params descriptions
2 parents 0207938 + 078e195 commit cb5cd6d

File tree

3 files changed

+87
-21
lines changed

3 files changed

+87
-21
lines changed

samples/mcp-dynamic-server/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mcp-dynamic-server"
3-
version = "0.0.3"
3+
version = "0.0.7"
44
description = "Dynamic tools MCP Server"
55
authors = [{ name = "John Doe" }]
66
dependencies = [

samples/mcp-dynamic-server/server.py

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,43 @@
55
# Initialize the MCP server
66
mcp = FastMCP("Self-Extending MCP Server")
77

8+
built_in_tools = {
9+
"get_tools": {
10+
"description": "Get a list of all available tools in the MCP server.",
11+
"parameters": {}
12+
},
13+
"add_tool": {
14+
"description": "Add a new tool to the MCP server.",
15+
"parameters": {
16+
"name": "Name of the tool (required)",
17+
"code": "Python code implementing the tool function (required)",
18+
"description": "Description of what the tool does (required)",
19+
"param_descriptions": "Dictionary of parameter names to descriptions (optional)"
20+
}
21+
},
22+
"call_tool": {
23+
"description": "Call a registered tool with the given arguments.",
24+
"parameters": {
25+
"name": "Name of the tool to call (required)",
26+
"args": "Dictionary of arguments to pass to the tool"
27+
}
28+
}
29+
}
830

931
# Tool registry to track dynamically added tools
1032
class ToolRegistry:
1133
def __init__(self):
1234
self.tools = {} # name -> function
1335
self.metadata = {} # name -> metadata
1436

15-
def register(self, name: str, func: Callable, description: str):
37+
def register(self, name: str, func: Callable, description: str, param_descriptions: Dict[str, str] = None):
1638
"""Register a new tool in the registry."""
1739
self.tools[name] = func
18-
self.metadata[name] = {"name": name, "description": description}
40+
self.metadata[name] = {
41+
"name": name,
42+
"description": description,
43+
"parameters": param_descriptions or {}
44+
}
1945

2046
def get_tool(self, name: str) -> Optional[Callable]:
2147
"""Get a tool by name."""
@@ -49,19 +75,23 @@ def get_tools() -> Dict[str, Any]:
4975
try:
5076
tools = registry.list_tools()
5177

52-
# Add the built-in tools
53-
built_in_tools = ["get_tools", "add_tool", "call_tool"]
54-
5578
# Combine built-in tools with dynamic tools
56-
all_tools = [{"name": tool, "built_in": True} for tool in built_in_tools]
79+
all_tools = []
80+
for name, info in built_in_tools.items():
81+
all_tools.append({
82+
"name": name,
83+
"description": info["description"],
84+
"parameters": info["parameters"],
85+
"built_in": True
86+
})
87+
5788
for tool in tools:
58-
all_tools.append(
59-
{
60-
"name": tool["name"],
61-
"description": tool["description"],
62-
"built_in": False,
63-
}
64-
)
89+
all_tools.append({
90+
"name": tool["name"],
91+
"description": tool["description"],
92+
"parameters": tool["parameters"],
93+
"built_in": False
94+
})
6595

6696
return {"status": "success", "tools": all_tools}
6797
except Exception as e:
@@ -70,18 +100,35 @@ def get_tools() -> Dict[str, Any]:
70100

71101
# Core functionality: Add a new tool
72102
@mcp.tool()
73-
def add_tool(name: str, code: str, description: str) -> Dict[str, Any]:
103+
def add_tool(name: str = None, code: str = None, description: str = None, param_descriptions: Dict[str, str] = None) -> Dict[str, Any]:
74104
"""Add a new tool to the MCP server.
75105
76106
Args:
77107
name: Name of the tool
78108
code: Python code implementing the tool function
79109
description: Description of what the tool does
110+
param_descriptions: Dictionary of parameter names to descriptions
80111
81112
Returns:
82113
Dictionary with operation status
83114
"""
84115
try:
116+
# Validate required parameters
117+
missing_params = []
118+
if name is None:
119+
missing_params.append("name")
120+
if code is None:
121+
missing_params.append("code")
122+
if description is None:
123+
missing_params.append("description")
124+
125+
if missing_params:
126+
return {
127+
"status": "error",
128+
"message": f"Missing required parameters: {', '.join(missing_params)}",
129+
"usage_example": "add_tool(name='tool_name', code='def tool_name(param1, param2):\\n # code here\\n return {\"status\": \"success\"}', description='Tool description', param_descriptions={'param1': 'Description of param1', 'param2': 'Description of param2'})"
130+
}
131+
85132
# Check if tool already exists
86133
if registry.has_tool(name) or hasattr(mcp, name):
87134
return {"status": "error", "message": f"Tool '{name}' already exists"}
@@ -109,9 +156,16 @@ def add_tool(name: str, code: str, description: str) -> Dict[str, Any]:
109156
}
110157

111158
# Register the tool with our registry
112-
registry.register(name, func, description)
159+
registry.register(name, func, description, param_descriptions)
113160

114-
return {"status": "success", "message": f"Tool '{name}' added successfully"}
161+
# Get the parameter information to return
162+
params = registry.get_metadata(name)["parameters"]
163+
164+
return {
165+
"status": "success",
166+
"message": f"Tool '{name}' added successfully",
167+
"parameters": params
168+
}
115169

116170
except SyntaxError as e:
117171
return {
@@ -127,7 +181,7 @@ def add_tool(name: str, code: str, description: str) -> Dict[str, Any]:
127181

128182
# Core functionality: Call a tool
129183
@mcp.tool()
130-
def call_tool(name: str, args: Dict[str, Any]) -> Dict[str, Any]:
184+
def call_tool(name: str, args: Dict[str, Any] = None) -> Dict[str, Any]:
131185
"""Call a registered tool with the given arguments.
132186
133187
Args:
@@ -137,29 +191,41 @@ def call_tool(name: str, args: Dict[str, Any]) -> Dict[str, Any]:
137191
Returns:
138192
Dictionary with the tool's response
139193
"""
194+
195+
args = args or {}
196+
140197
try:
141198
# Check if it's a built-in tool
142-
if name in ["get_tools", "add_tool", "call_tool"]:
199+
if name in built_in_tools:
143200
return {
144201
"status": "error",
145202
"message": f"Cannot call built-in tool '{name}' using call_tool",
203+
"note": f"Use the {name} function directly instead of call_tool",
204+
"parameters": built_in_tools[name]["parameters"]
146205
}
147206

148207
# Get the tool
149208
tool = registry.get_tool(name)
150209

151210
if not tool:
152-
return {"status": "error", "message": f"Tool '{name}' not found"}
211+
return {
212+
"status": "error",
213+
"message": f"Tool '{name}' not found",
214+
"available_tools": [t["name"] for t in registry.list_tools()]
215+
}
153216

154217
# Call the tool with the provided arguments
155218
try:
156219
result = tool(**args)
157220
return result
158221
except TypeError as e:
159222
# Likely an argument mismatch
223+
params = registry.get_metadata(name)["parameters"]
160224
return {
161225
"status": "error",
162226
"message": f"Argument error calling tool '{name}': {str(e)}",
227+
"expected_parameters": params,
228+
"usage_example": f"call_tool(name='{name}', args={{'param1': value1, 'param2': value2, ...}})"
163229
}
164230
except Exception as e:
165231
return {

samples/mcp-dynamic-server/uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)