-
Notifications
You must be signed in to change notification settings - Fork 239
before interceptors fail to marshal request: json: unsupported type func(mcp.CloseSSEStreamArgs) #358
Description
Summary
When running docker mcp gateway run with a before interceptor (exec, docker, or http), the gateway can fail with:
json: unsupported type: func(mcp.CloseSSEStreamArgs)
This prevents before-interceptors from working (notably on streaming/sse transports).
Environment
- Repo:
docker/mcp-gateway - Version:
v0.37.0(commitcc7998a51493a26bd9e51e1f7fb25cee60fc03a5) - Transport:
streaming(also seen onsse) - Host: Docker Desktop (macOS)
Steps to reproduce
- Start the gateway with a before exec interceptor:
docker mcp gateway run --port 8080 --transport streaming \
--interceptor 'before:exec:echo Arguments=$(jq -r ".params.arguments") >&2'- Connect a client and invoke any tool call (
tools/call).
Expected behavior
- The before interceptor receives JSON and can read
.params.arguments. - The call proceeds (passthrough) unless the interceptor returns a non-empty JSON response.
Actual behavior
- Gateway fails with an error like:
executing interceptor: marshalling request: json: unsupported type: func(mcp.CloseSSEStreamArgs)
Root cause
In pkg/interceptors/interceptors.go, ToMiddleware() attempts to serialize the full mcp.Request for before interceptors:
message, err := json.Marshal(req)On streaming/SSE transports, the mcp.Request may contain internal callback functions (e.g. func(mcp.CloseSSEStreamArgs)), which are not JSON-serializable.
Proposed fix
For "before" interceptors, marshal only the serializable tool-call payload (matching the documented/observed interceptor request shape):
{ "method": "tools/call", "params": { "name": "...", "arguments": { ... } } }Patch sketch:
var payload any
if callReq, ok := req.(*mcp.CallToolRequest); ok && callReq.Params != nil {
payload = map[string]any{"method": "tools/call", "params": callReq.Params}
} else {
payload = map[string]any{"method": "tools/call", "params": map[string]any{}}
}
message, err := json.Marshal(payload)This preserves compatibility with interceptors using jq -r ".params.arguments".
Workaround
No reliable workaround other than disabling before interceptors.