Research guide · MCP · Go SDK · middleware · updated 15 September 2026
MCP Go SDK nil pointer on tools/list: fix missing params middleware panic
If a Go MCP server crashes inside receiving middleware on an otherwise ordinary tools/list, prompts/list, resources/list or notifications/initialized message, check how the middleware tests req.GetParams(). A valid request can omit the JSON-RPC params member, while affected released SDK versions can still make GetParams() appear non-nil at the interface boundary and then panic when middleware calls GetMeta().
Published 14 September 2026 · upstream status checked 15 September 2026
tools/list
Common reported trigger
GetParams() != nil
Unsafe presence test in affected releases
typed nil
Go interface boundary causing the trap
PR #1269 merged
HasParams is on main; release inclusion must be checked
Status update
The upstream fix is merged, but main is not the same as a released dependency
MCP Go SDK pull request #1269 merged on 14 September 2026 at 16:45 UTC. It adds mcp.HasParams(req Request) bool, updates the SDK's own per-request metadata path to use the safer check, and documents that a received message need not carry params.
The latest release at the time of that merge was v1.8.0, published earlier on 14 September at 08:05 UTC. Because the release predates the merge, v1.8.0 does not contain PR #1269. Treat HasParams as available on main, not as available in every released build.
The failure boundary
A non-nil interface can still contain a nil params pointer
JSON-RPC allows params to be omitted, and the MCP Go SDK accepts missing params for several methods. The upstream issue opened 13 September 2026 demonstrates a complete tools/list request with no params member.
In the reported path, the SDK leaves the typed params pointer at its zero value. Request.GetParams() boxes that nil pointer into an interface. In Go, an interface that contains a typed nil pointer is itself non-nil, so req.GetParams() != nil reports true. Calling the promoted GetMeta() method then dereferences the nil pointer and can crash the process.
That makes this a middleware/API-shape problem, not evidence that the client sent malformed JSON-RPC. Adding params: may make one reproduction disappear, but forcing every client to do that would paper over the server-side boundary rather than repair it.
Error map
Match the stack before changing protocol behavior
| Observed symptom | What to inspect next |
|---|---|
panic: runtime error: invalid memory address or nil pointer dereference | Check whether generic receiving middleware dereferences req.GetParams() on a params-optional method. |
(*ListToolsParams).GetMeta | Strong match for the reported typed-nil path on tools/list. |
Panic only when logging, auth or tracing middleware is enabled | Inspect middleware that reads _meta, protocol version, client information or capabilities before the real handler runs. |
Request succeeds after adding params: {} | That is a useful reproduction clue, not a protocol requirement; valid callers may still omit params. |
Diagnosis
Seven checks before blaming the MCP client
- 1. Capture the top useful stack frame. Look for
GetMetaon a params type such as*ListToolsParams, not just the generic nil-pointer headline. - 2. Disable only the suspect middleware in a controlled test. If the request succeeds without logging/auth/tracing middleware, inspect that layer before changing tools or transport code.
- 3. Record the exact method. The report specifically reproduces params-optional list methods and
notifications/initialized. Do not generalize a fix to every request shape without evidence. - 4. Inspect the raw JSON-RPC shape safely. Confirm whether
paramsis actually absent rather than empty. Never log credentials, authorization headers or private tool arguments to prove the point. - 5. Find every generic
GetParams()presence check. A check against plainnilis the dangerous pattern described upstream when the interface contains a typed nil pointer. - 6. Check the deployed Go SDK version, not only upstream main. PR #1269 is merged, but v1.8.0 predates that merge. Use
mcp.HasParamsonly when the version you actually build contains it. - 7. Re-test both missing and present params. Your mitigation must preserve requests with legitimate params while allowing params-optional messages to pass without a middleware panic.
Version-scoped mitigation
Use a typed-nil-safe predicate until your released SDK owns the check
If your pinned SDK does not yet expose mcp.HasParams, keep compatibility logic local and easy to delete. The merged upstream implementation uses a typed-nil-safe presence check so middleware does not need to call a method on the potentially nil params value first.
func hasParams(req mcp.Request) bool {
params := req.GetParams()
if params == nil { return false }
v := reflect.ValueOf(params)
return v.Kind() != reflect.Pointer || !v.IsNil()
}
if hasParams(req) {
meta := req.GetParams().GetMeta()
_ = meta
}
Pin the dependency, add a regression test with a params-less tools/list, and remove the local helper after you deploy a released SDK version that contains PR #1269 and exposes mcp.HasParams.
What not to do
Do not turn a middleware panic into a protocol incompatibility
Reject every request without params
The reported methods are allowed to omit params. Rejecting them converts a server-side middleware defect into avoidable client incompatibility.
Patch clients to always send params: {}
Useful as a reproduction control, not a durable fix. You do not control every legitimate MCP client that may omit the optional member.
Assume merged means released
PR #1269 is on upstream main, but the latest v1.8.0 release predates the merge. Check the exact dependency version before calling HasParams in production.
Sources
Primary evidence
- MCP Go SDK issue #1261 — 13 September 2026 report reproducing the typed-nil
GetParamspanic on params-optional messages. - MCP Go SDK pull request #1269 — merged 14 September 2026 at 16:45 UTC, adding
mcp.HasParams, docs and regression tests. - MCP Go SDK v1.8.0 release — published 14 September 2026 at 08:05 UTC, before PR #1269 merged.
- JSON-RPC 2.0 specification — the request
paramsmember is optional.
Continue the implementation path
Migrate safely to MCP 2026-07-28
Keep version-envelope and request-lifecycle migration work separate from this Go middleware failure.
Diagnose Streamable HTTP response failures
If the middleware path is healthy but the connection ends mid-response, move to SSE and server timeout diagnosis.
Verify an MCP server before connecting
Separate endpoint, transport, authentication and tool-surface evidence before trusting a discovered server.
Inspect zFinia machine APIs
Machine-readable services for agents and operators working across the machine economy.