Skip to content

Commit 673f30e

Browse files
committed
fix: simplify code_execution description and add it to /mcp/call mode
- Remove bloated tool catalog from code_execution description on /mcp/code (was embedding ALL upstream tool names, now references retrieve_tools) - Add code_execution tool to /mcp/call mode (user requested) - Extract buildCodeExecutionTool() helper shared by both routing modes - retrieve_tools on /mcp/call still encourages call_tool_* workflow - retrieve_tools on /mcp/code still encourages code_execution workflow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d44365a commit 673f30e

1 file changed

Lines changed: 54 additions & 89 deletions

File tree

internal/server/mcp_routing.go

Lines changed: 54 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -202,67 +202,13 @@ func (p *MCPProxyServer) makeDirectModeHandler(serverName, toolName string, anno
202202
}
203203

204204
// buildCodeExecModeTools builds the tool set for code_execution routing mode.
205-
// Includes: code_execution (with enhanced description listing available tools) and retrieve_tools (for discovery).
205+
// Includes: code_execution + retrieve_tools (for discovery).
206206
// Does NOT include call_tool_read/write/destructive.
207207
func (p *MCPProxyServer) buildCodeExecModeTools() []mcpserver.ServerTool {
208-
tools := make([]mcpserver.ServerTool, 0, 2)
208+
tools := make([]mcpserver.ServerTool, 0, 4)
209209

210-
// Check if code execution is enabled in config
211-
if p.config != nil && !p.config.EnableCodeExecution {
212-
// Code execution is disabled: register a stub tool that returns a clear error message
213-
codeExecutionTool := mcp.NewTool("code_execution",
214-
mcp.WithDescription("Code execution is currently disabled. Enable it by setting \"enable_code_execution\": true in your mcpproxy config."),
215-
mcp.WithTitleAnnotation("Code Execution (Disabled)"),
216-
mcp.WithReadOnlyHintAnnotation(true),
217-
mcp.WithString("code",
218-
mcp.Required(),
219-
mcp.Description("JavaScript source code (ES5.1+) to execute."),
220-
),
221-
)
222-
tools = append(tools, mcpserver.ServerTool{
223-
Tool: codeExecutionTool,
224-
Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
225-
return mcp.NewToolResultError("Code execution is disabled. Enable it by setting \"enable_code_execution\": true in your mcpproxy configuration file."), nil
226-
},
227-
})
228-
} else {
229-
// Build enhanced description with available tools catalog
230-
ctx := context.Background()
231-
toolCatalog := p.buildToolCatalogDescription(ctx)
232-
233-
codeExecDescription := fmt.Sprintf(
234-
"Execute JavaScript code that orchestrates multiple upstream MCP tools in a single request. "+
235-
"Use this when you need to combine results from 2+ tools, implement conditional logic, loops, or data transformations.\n\n"+
236-
"**Available in JavaScript**:\n"+
237-
"- `input` global: Your input data passed via the 'input' parameter\n"+
238-
"- `call_tool(serverName, toolName, args)`: Call upstream tools (returns {ok, result} or {ok, error})\n"+
239-
"- Standard ES5.1+ JavaScript (no require(), filesystem, or network access)\n\n"+
240-
"**Available tools for orchestration**:\n%s\n\n"+
241-
"Use call_tool('serverName', 'toolName', {args}) to invoke tools.",
242-
toolCatalog,
243-
)
244-
245-
// code_execution tool with enhanced description
246-
codeExecutionTool := mcp.NewTool("code_execution",
247-
mcp.WithDescription(codeExecDescription),
248-
mcp.WithTitleAnnotation("Code Execution"),
249-
mcp.WithDestructiveHintAnnotation(true),
250-
mcp.WithString("code",
251-
mcp.Required(),
252-
mcp.Description("JavaScript source code (ES5.1+) to execute. Use `input` to access input data and `call_tool(serverName, toolName, args)` to invoke upstream tools."),
253-
),
254-
mcp.WithObject("input",
255-
mcp.Description("Input data accessible as global `input` variable in JavaScript code (default: {})"),
256-
),
257-
mcp.WithObject("options",
258-
mcp.Description("Execution options: timeout_ms (1-600000, default: 120000), max_tool_calls (>= 0, 0=unlimited), allowed_servers (array of server names, empty=all allowed)"),
259-
),
260-
)
261-
tools = append(tools, mcpserver.ServerTool{
262-
Tool: codeExecutionTool,
263-
Handler: p.handleCodeExecution,
264-
})
265-
}
210+
// code_execution tool
211+
tools = append(tools, p.buildCodeExecutionTool()...)
266212

267213
// retrieve_tools for discovery — instructs to use code_execution (NOT call_tool_*)
268214
retrieveToolsTool := mcp.NewTool("retrieve_tools",
@@ -295,10 +241,9 @@ func (p *MCPProxyServer) buildCodeExecModeTools() []mcpserver.ServerTool {
295241
}
296242

297243
// buildCallToolModeTools builds the tool set for retrieve_tools routing mode (/mcp/call).
298-
// Includes: retrieve_tools (with call_tool_* instructions) + call_tool_read/write/destructive + read_cache.
299-
// Does NOT include code_execution or upstream_servers.
244+
// Includes: retrieve_tools (with call_tool_* instructions) + call_tool_read/write/destructive + read_cache + code_execution.
300245
func (p *MCPProxyServer) buildCallToolModeTools() []mcpserver.ServerTool {
301-
tools := make([]mcpserver.ServerTool, 0, 5)
246+
tools := make([]mcpserver.ServerTool, 0, 8)
302247

303248
// retrieve_tools — instructs to use call_tool_read/write/destructive
304249
retrieveToolsTool := mcp.NewTool("retrieve_tools",
@@ -424,6 +369,9 @@ func (p *MCPProxyServer) buildCallToolModeTools() []mcpserver.ServerTool {
424369
Handler: p.handleReadCache,
425370
})
426371

372+
// code_execution tool (available but not the primary workflow)
373+
tools = append(tools, p.buildCodeExecutionTool()...)
374+
427375
// Add management tools (upstream_servers, quarantine, registries)
428376
tools = append(tools, p.buildManagementTools()...)
429377

@@ -433,36 +381,53 @@ func (p *MCPProxyServer) buildCallToolModeTools() []mcpserver.ServerTool {
433381
return tools
434382
}
435383

436-
// buildToolCatalogDescription builds a human-readable catalog of available tools for the code_execution description.
437-
func (p *MCPProxyServer) buildToolCatalogDescription(ctx context.Context) string {
438-
tools, err := p.upstreamManager.DiscoverTools(ctx)
439-
if err != nil {
440-
return " (unable to discover tools - use retrieve_tools to search)"
441-
}
442-
443-
if len(tools) == 0 {
444-
return " (no upstream tools available)"
445-
}
446-
447-
var sb strings.Builder
448-
for _, tool := range tools {
449-
// Determine permission tier from annotations
450-
callWith := contracts.DeriveCallWith(tool.Annotations)
451-
perm := contracts.ToolVariantToOperationType[callWith]
452-
if perm == "" {
453-
perm = "read"
454-
}
455-
456-
// Truncate description for catalog listing
457-
desc := tool.Description
458-
if len(desc) > 80 {
459-
desc = desc[:77] + "..."
460-
}
461-
462-
sb.WriteString(fmt.Sprintf("- %s:%s (%s) - %s\n", tool.ServerName, tool.Name, perm, desc))
384+
// buildCodeExecutionTool builds the code_execution tool for routing mode servers.
385+
// Returns a slice (either 1 tool or 1 disabled stub) for easy appending.
386+
func (p *MCPProxyServer) buildCodeExecutionTool() []mcpserver.ServerTool {
387+
if p.config != nil && !p.config.EnableCodeExecution {
388+
// Disabled stub
389+
codeExecutionTool := mcp.NewTool("code_execution",
390+
mcp.WithDescription("Code execution is currently disabled. Enable it by setting \"enable_code_execution\": true in your mcpproxy config."),
391+
mcp.WithTitleAnnotation("Code Execution (Disabled)"),
392+
mcp.WithReadOnlyHintAnnotation(true),
393+
mcp.WithString("code",
394+
mcp.Required(),
395+
mcp.Description("JavaScript source code to execute."),
396+
),
397+
)
398+
return []mcpserver.ServerTool{{
399+
Tool: codeExecutionTool,
400+
Handler: func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
401+
return mcp.NewToolResultError("Code execution is disabled. Enable it by setting \"enable_code_execution\": true in your mcpproxy configuration file."), nil
402+
},
403+
}}
463404
}
464405

465-
return sb.String()
406+
codeExecutionTool := mcp.NewTool("code_execution",
407+
mcp.WithDescription("Execute JavaScript code that orchestrates multiple upstream MCP tools in a single request. "+
408+
"Use when you need to combine results from 2+ tools, implement conditional logic, loops, or data transformations.\n\n"+
409+
"**Available in JavaScript**:\n"+
410+
"- `input` global: Your input data passed via the 'input' parameter\n"+
411+
"- `call_tool(serverName, toolName, args)`: Call upstream tools (returns {ok, result} or {ok, error})\n"+
412+
"- ES5.1+ JavaScript (no require(), filesystem, or network access)\n\n"+
413+
"Use `retrieve_tools` to discover available tools, then call them via `call_tool('serverName', 'toolName', {args})`."),
414+
mcp.WithTitleAnnotation("Code Execution"),
415+
mcp.WithDestructiveHintAnnotation(true),
416+
mcp.WithString("code",
417+
mcp.Required(),
418+
mcp.Description("JavaScript source code (ES5.1+) to execute. Use `input` to access input data and `call_tool(serverName, toolName, args)` to invoke upstream tools."),
419+
),
420+
mcp.WithObject("input",
421+
mcp.Description("Input data accessible as global `input` variable in code (default: {})"),
422+
),
423+
mcp.WithObject("options",
424+
mcp.Description("Execution options: timeout_ms (1-600000, default: 120000), max_tool_calls (>= 0, 0=unlimited), allowed_servers (array of server names, empty=all allowed)"),
425+
),
426+
)
427+
return []mcpserver.ServerTool{{
428+
Tool: codeExecutionTool,
429+
Handler: p.handleCodeExecution,
430+
}}
466431
}
467432

468433
// initRoutingModeServers creates separate MCP server instances for each routing mode.

0 commit comments

Comments
 (0)