Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions router-tests/mcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,71 @@ func TestMCP(t *testing.T) {
})
})

t.Run("List user Operations / Tool names omit prefix when StripToolNamePrefix is enabled", func(t *testing.T) {
testenv.Run(t, &testenv.Config{
MCP: config.MCPConfiguration{
Enabled: true,
StripToolNamePrefix: true,
},
}, func(t *testing.T, xEnv *testenv.Environment) {

toolsRequest := mcp.ListToolsRequest{}
resp, err := xEnv.MCPClient.ListTools(xEnv.Context, toolsRequest)
require.NoError(t, err)
require.NotNil(t, resp)

var foundMyEmployees, foundUpdateMood bool
var foundPrefixedMyEmployees, foundPrefixedUpdateMood bool

for _, tool := range resp.Tools {
switch tool.Name {
case "my_employees":
foundMyEmployees = true
case "update_mood":
foundUpdateMood = true
case "execute_operation_my_employees":
foundPrefixedMyEmployees = true
case "execute_operation_update_mood":
foundPrefixedUpdateMood = true
}
}

require.True(t, foundMyEmployees, "Tool 'my_employees' should be registered when StripToolNamePrefix is true")
require.True(t, foundUpdateMood, "Tool 'update_mood' should be registered when StripToolNamePrefix is true")

require.False(t, foundPrefixedMyEmployees, "Tool 'execute_operation_my_employees' should NOT be registered when StripToolNamePrefix is true")
require.False(t, foundPrefixedUpdateMood, "Tool 'execute_operation_update_mood' should NOT be registered when StripToolNamePrefix is true")
})
})

t.Run("Execute operation using short tool name when StripToolNamePrefix is enabled", func(t *testing.T) {
testenv.Run(t, &testenv.Config{
MCP: config.MCPConfiguration{
Enabled: true,
StripToolNamePrefix: true,
},
}, func(t *testing.T, xEnv *testenv.Environment) {

req := mcp.CallToolRequest{}
req.Params.Name = "my_employees"
req.Params.Arguments = map[string]interface{}{
"criteria": map[string]interface{}{},
}

resp, err := xEnv.MCPClient.CallTool(xEnv.Context, req)
assert.NoError(t, err)
assert.NotNil(t, resp)

assert.Len(t, resp.Content, 1)

content, ok := resp.Content[0].(mcp.TextContent)
assert.True(t, ok)

assert.Equal(t, content.Type, "text")
assert.Contains(t, content.Text, "findEmployees")
})
})

t.Run("List user Operations / Static operations of type mutation aren't exposed when excludeMutations is set", func(t *testing.T) {
testenv.Run(t, &testenv.Config{
MCP: config.MCPConfiguration{
Expand Down
1 change: 1 addition & 0 deletions router/core/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,7 @@ func (r *Router) bootstrap(ctx context.Context) error {
mcpserver.WithExcludeMutations(r.mcp.ExcludeMutations),
mcpserver.WithEnableArbitraryOperations(r.mcp.EnableArbitraryOperations),
mcpserver.WithExposeSchema(r.mcp.ExposeSchema),
mcpserver.WithStripToolNamePrefix(r.mcp.StripToolNamePrefix),
mcpserver.WithStateless(r.mcp.Session.Stateless),
}

Expand Down
37 changes: 20 additions & 17 deletions router/pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,25 +360,25 @@ func (r *ResponseHeaderRule) GetMatching() string {
}

type EngineDebugConfiguration struct {
PrintOperationTransformations bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_OPERATION_TRANSFORMATIONS" yaml:"print_operation_transformations"`
PrintOperationEnableASTRefs bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_OPERATION_ENABLE_AST_REFS" yaml:"print_operation_enable_ast_refs"`
PrintPlanningPaths bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_PLANNING_PATHS" yaml:"print_planning_paths"`
PrintQueryPlans bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_QUERY_PLANS" yaml:"print_query_plans"`
PrintIntermediateQueryPlans bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_INTERMEDIATE_QUERY_PLANS" yaml:"print_intermediate_query_plans"`
PrintNodeSuggestions bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_NODE_SUGGESTIONS" yaml:"print_node_suggestions"`
ConfigurationVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_CONFIGURATION_VISITOR" yaml:"configuration_visitor"`
PlanningVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_PLANNING_VISITOR" yaml:"planning_visitor"`
DatasourceVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_DATASOURCE_VISITOR" yaml:"datasource_visitor"`
ReportWebSocketConnections bool `envDefault:"false" env:"ENGINE_DEBUG_REPORT_WEBSOCKET_CONNECTIONS" yaml:"report_websocket_connections"`
ReportMemoryUsage bool `envDefault:"false" env:"ENGINE_DEBUG_REPORT_MEMORY_USAGE" yaml:"report_memory_usage"`
EnableResolverDebugging bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_RESOLVER_DEBUGGING" yaml:"enable_resolver_debugging"`
PrintOperationTransformations bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_OPERATION_TRANSFORMATIONS" yaml:"print_operation_transformations"`
PrintOperationEnableASTRefs bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_OPERATION_ENABLE_AST_REFS" yaml:"print_operation_enable_ast_refs"`
PrintPlanningPaths bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_PLANNING_PATHS" yaml:"print_planning_paths"`
PrintQueryPlans bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_QUERY_PLANS" yaml:"print_query_plans"`
PrintIntermediateQueryPlans bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_INTERMEDIATE_QUERY_PLANS" yaml:"print_intermediate_query_plans"`
PrintNodeSuggestions bool `envDefault:"false" env:"ENGINE_DEBUG_PRINT_NODE_SUGGESTIONS" yaml:"print_node_suggestions"`
ConfigurationVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_CONFIGURATION_VISITOR" yaml:"configuration_visitor"`
PlanningVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_PLANNING_VISITOR" yaml:"planning_visitor"`
DatasourceVisitor bool `envDefault:"false" env:"ENGINE_DEBUG_DATASOURCE_VISITOR" yaml:"datasource_visitor"`
ReportWebSocketConnections bool `envDefault:"false" env:"ENGINE_DEBUG_REPORT_WEBSOCKET_CONNECTIONS" yaml:"report_websocket_connections"`
ReportMemoryUsage bool `envDefault:"false" env:"ENGINE_DEBUG_REPORT_MEMORY_USAGE" yaml:"report_memory_usage"`
EnableResolverDebugging bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_RESOLVER_DEBUGGING" yaml:"enable_resolver_debugging"`
// EnablePersistedOperationsCacheResponseHeader is deprecated, use EnableCacheResponseHeaders instead.
EnablePersistedOperationsCacheResponseHeader bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_PERSISTED_OPERATIONS_CACHE_RESPONSE_HEADER" yaml:"enable_persisted_operations_cache_response_header"`
// EnableNormalizationCacheResponseHeader is deprecated, use EnableCacheResponseHeaders instead.
EnableNormalizationCacheResponseHeader bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_NORMALIZATION_CACHE_RESPONSE_HEADER" yaml:"enable_normalization_cache_response_header"`
EnableCacheResponseHeaders bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_CACHE_RESPONSE_HEADERS" yaml:"enable_cache_response_headers"`
AlwaysIncludeQueryPlan bool `envDefault:"false" env:"ENGINE_DEBUG_ALWAYS_INCLUDE_QUERY_PLAN" yaml:"always_include_query_plan"`
AlwaysSkipLoader bool `envDefault:"false" env:"ENGINE_DEBUG_ALWAYS_SKIP_LOADER" yaml:"always_skip_loader"`
EnableNormalizationCacheResponseHeader bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_NORMALIZATION_CACHE_RESPONSE_HEADER" yaml:"enable_normalization_cache_response_header"`
EnableCacheResponseHeaders bool `envDefault:"false" env:"ENGINE_DEBUG_ENABLE_CACHE_RESPONSE_HEADERS" yaml:"enable_cache_response_headers"`
AlwaysIncludeQueryPlan bool `envDefault:"false" env:"ENGINE_DEBUG_ALWAYS_INCLUDE_QUERY_PLAN" yaml:"always_include_query_plan"`
AlwaysSkipLoader bool `envDefault:"false" env:"ENGINE_DEBUG_ALWAYS_SKIP_LOADER" yaml:"always_skip_loader"`
}

type EngineExecutionConfiguration struct {
Expand Down Expand Up @@ -997,7 +997,10 @@ type MCPConfiguration struct {
ExcludeMutations bool `yaml:"exclude_mutations" envDefault:"false" env:"MCP_EXCLUDE_MUTATIONS"`
EnableArbitraryOperations bool `yaml:"enable_arbitrary_operations" envDefault:"false" env:"MCP_ENABLE_ARBITRARY_OPERATIONS"`
ExposeSchema bool `yaml:"expose_schema" envDefault:"false" env:"MCP_EXPOSE_SCHEMA"`
RouterURL string `yaml:"router_url,omitempty" env:"MCP_ROUTER_URL"`
// StripToolNamePrefix removes the "execute_operation_" prefix from MCP tool names.
// When enabled, GetUser becomes get_user. When disabled (default), GetUser becomes execute_operation_get_user.
StripToolNamePrefix bool `yaml:"strip_tool_name_prefix" envDefault:"false" env:"MCP_STRIP_TOOL_NAME_PREFIX"`
RouterURL string `yaml:"router_url,omitempty" env:"MCP_ROUTER_URL"`
}

type MCPSessionConfig struct {
Expand Down
5 changes: 5 additions & 0 deletions router/pkg/config/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2131,6 +2131,11 @@
"type": "boolean",
"default": false,
"description": "Expose the full GraphQL schema through MCP. When enabled, AI models can request the complete schema of your API."
},
"strip_tool_name_prefix": {
"type": "boolean",
"default": false,
"description": "When enabled, MCP tool names generated from GraphQL operations omit the 'execute_operation_' prefix. For example, the GraphQL operation 'GetUser' results in a tool named 'get_user' instead of 'execute_operation_get_user'. This produces cleaner tool names for AI models."
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion router/pkg/config/fixtures/full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ mcp:
expose_schema: false
enable_arbitrary_operations: false
exclude_mutations: false
strip_tool_name_prefix: false
graph_name: cosmo
router_url: https://cosmo-router.wundergraph.com
server:
Expand Down Expand Up @@ -177,7 +178,7 @@ telemetry:
schema_usage:
enabled: true
include_operation_sha: true
sample_rate: 1.0 # Supports any rate: 1.0, 0.8, 0.5, 0.1, 0.01, etc.
sample_rate: 1.0 # Supports any rate: 1.0, 0.8, 0.5, 0.1, 0.01, etc.

cache_control_policy:
enabled: true
Expand Down
35 changes: 9 additions & 26 deletions router/pkg/config/testdata/config_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,9 @@
},
"CORS": {
"Enabled": true,
"AllowOrigins": [
"*"
],
"AllowMethods": [
"HEAD",
"GET",
"POST"
],
"AllowHeaders": [
"Origin",
"Content-Length",
"Content-Type"
],
"AllowOrigins": ["*"],
"AllowMethods": ["HEAD", "GET", "POST"],
"AllowHeaders": ["Origin", "Content-Length", "Content-Type"],
"AllowCredentials": true,
"MaxAge": 300000000000
},
Expand Down Expand Up @@ -137,6 +127,7 @@
"ExcludeMutations": false,
"EnableArbitraryOperations": false,
"ExposeSchema": false,
"StripToolNamePrefix": false,
"RouterURL": ""
},
"DemoMode": false,
Expand Down Expand Up @@ -213,9 +204,7 @@
},
"Router": {
"Fields": null,
"IgnoreQueryParamsList": [
"variables"
]
"IgnoreQueryParamsList": ["variables"]
},
"Subgraphs": {
"Enabled": false,
Expand Down Expand Up @@ -408,15 +397,11 @@
},
"ForwardUpgradeHeaders": {
"Enabled": true,
"AllowList": [
"Authorization"
]
"AllowList": ["Authorization"]
},
"ForwardUpgradeQueryParams": {
"Enabled": true,
"AllowList": [
"Authorization"
]
"AllowList": ["Authorization"]
},
"ForwardInitialPayload": true,
"Authentication": {
Expand Down Expand Up @@ -450,9 +435,7 @@
"AttachServiceName": true,
"DefaultExtensionCode": "DOWNSTREAM_SERVICE_ERROR",
"AllowAllExtensionFields": false,
"AllowedExtensionFields": [
"code"
],
"AllowedExtensionFields": ["code"],
"AllowedFields": null
},
"StorageProviders": {
Expand Down Expand Up @@ -556,4 +539,4 @@
"Maximum": 10000000000
}
}
}
}
Loading
Loading