Editorial

Research guide · MCP · Go SDK · middleware · 14 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 fresh upstream report shows a Go typed-nil trap: a valid request can omit the JSON-RPC params member, while GetParams() still appears non-nil at the interface boundary and then panics when middleware calls GetMeta().

Published 14 September 2026 · upstream status checked 14 September 2026

tools/list

Common reported trigger

GetParams() != nil

Unsafe presence test in affected versions

typed nil

Go interface boundary causing the trap

PR #1269 open

Proposed upstream HasParams fix

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 symptomWhat to inspect next
panic: runtime error: invalid memory address or nil pointer dereferenceCheck whether a generic receiving middleware dereferences req.GetParams() on a params-optional method.
(*ListToolsParams).GetMetaStrong match for the currently reported typed-nil path on tools/list.
Panic only when logging, auth or tracing middleware is enabledInspect 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. 1. Capture the top useful stack frame. Look for GetMeta on a params type such as *ListToolsParams, not just the generic nil-pointer headline.
  2. 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. 3. Record the exact method. The fresh report specifically reproduces params-optional list methods and notifications/initialized. Do not generalize a fix to every request shape without evidence.
  4. 4. Inspect the raw JSON-RPC shape safely. Confirm whether params is actually absent rather than empty. Never log credentials, authorization headers or private tool arguments to prove the point.
  5. 5. Find every generic GetParams() presence check. A check against plain nil is the dangerous pattern described upstream when the interface contains a typed nil pointer.
  6. 6. Check the deployed Go SDK version and upstream status. The proposed mcp.HasParams API is in open PR #1269 as of this page update; do not compile production guidance against an unmerged symbol by assumption.
  7. 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 the released SDK owns the check

If your pinned SDK does not yet expose a safe presence check, keep the compatibility logic local and easy to delete. The open upstream PR proposes HasParams using reflection so middleware never has 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

}

This mirrors the shape proposed upstream; it is not a substitute for tracking the released SDK. Pin the dependency, add a regression test with a params-less tools/list, and remove the local helper when a reviewed upstream fix lands in the version you deploy.

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.

Import an unmerged fork into production by default

The proposed HasParams patch is useful evidence, but release status matters. Prefer a small local guard plus a pinned dependency until upstream ships a reviewed fix.

Sources

Primary evidence

Continue the implementation path