Skip to content

Preserve versioned STDIO API user agents across MCP protocols - #3323

Draft
jidicula wants to merge 1 commit into
mainfrom
jidicula/graphql-user-agent-mcp-stdio
Draft

jidicula wants to merge 1 commit into
mainfrom
jidicula/graphql-user-agent-mcp-stdio

Conversation

@jidicula

Copy link
Copy Markdown

Summary

Give STDIO GitHub API requests a server/version user agent before any MCP handshake, and preserve upstream client metadata per request across both legacy and modern MCP protocols. Source builds identify their actual source revision rather than reporting version or dev.

Why

The GraphQL transport starts without a server user agent and currently relies on middleware for the literal initialize method. The pinned MCP SDK's default client instead negotiates 2026-07-28 through server/discover, and the protocol also permits a direct metadata-bearing tools/call. In addition, the existing concrete *mcp.InitializeRequest assertion does not match incoming server requests in this SDK: that name aliases a client request.

Before the fix, six local wire-level regressions reached the production list_issues handler and observed Go-http-client/1.1, including the legacy-initialisation control. The fix does not depend on a particular handshake or mutate a shared HTTP transport when client metadata changes.

What changed

  • Install a default versioned GraphQL user agent when constructing the STDIO clients; preserve bearer authentication and GraphQL feature headers.
  • Use the SDK's per-request ClientInfo accessor and a request-scoped transport context. Keep the existing server product, optional parenthesised upstream identity and insiders marker. Existing transport defaults, including remote-server markers, remain unchanged when no override is supplied.
  • Resolve STDIO executable versions from explicit release metadata, an explicit source revision, embedded VCS metadata, or an installed main-module version. Preserve release strings; represent source revisions as vcs-<full-sha> with a dirty suffix when appropriate. Missing or malformed metadata produces an explicit startup error.
  • Add initial-client, real MCP/GraphQL wire, changed-client, concurrent transport-isolation and version-source coverage. Document source builds and correct the single-file build example to compile the complete package.

MCP impact

  • No tool or API changes — MCP schemas, tool results and permissions are unchanged. There is an additive exported transport-context helper; existing transport fields and default behaviour remain compatible.
  • Tool schema or behavior changed
  • New tool added

Prompts tested (tool changes only)

Not applicable: no tool definition or schema changed. Automated protocol tests invoke the actual list_issues handler through newline-delimited MCP transports and record its HTTP request at a local test server. They cover SDK discovery, direct modern calls, missing optional client information, legacy initialisation, insiders and changed server/client versions.

Security / limits

  • No security or limits impact
  • Auth / permissions considered — the existing bearer-token and host-restriction transports remain intact. Wire tests retain authentication and GraphQL feature headers; all test authentication values are deliberately non-functional fixtures.
  • Data exposure, filtering, or token/size limits considered — no additional request payload, credential or user data is emitted. The existing upstream client name/version metadata is retained separately from the server identity.

Tool renaming

  • I am renaming tools as part of this PR (e.g. a part of a consolidation effort)
    • I have added the new tool aliases in deprecated_tool_aliases.go
  • I am not renaming tools as part of this PR — all existing tool names and aliases are unchanged.

Note: if you're renaming tools, you must add the tool aliases. For more information on how to do so, please refer to the official docs.

Lint & tests

  • Linted locally with ./script/lint — run in a Codespace with GOTOOLCHAIN=go1.25.12, matching the checked-in lint workflow's Go 1.25 configuration.
  • Tested locally with ./script/test — the complete go test -race ./... suite passed in the Codespace.

The initial lint attempt used the image's Go 1.27.1 and failed because the pinned golangci-lint v2.9.0 could not decode that toolchain's standard-library export data. The CI-compatible toolchain passed with zero issues. No linter, dependency or CI configuration was changed. The formatter's unrelated pre-existing indentation change in pkg/http/oauth/oauth_test.go was removed, and the standalone analyser passed again.

The actual compiled STDIO executable was also exercised against a local HTTP fixture with a direct modern tools/call. The source build emitted its full embedded source revision with -dirty; a separately compiled release-labelled fixture retained exactly github-mcp-server/v1.2.3 (binary-probe/1.0.0). This exposed and corrected precedence for newer Go toolchains that infer a main-module pseudo-version even in a source checkout.

After committing, the clean source binary was rebuilt and the same actual HTTP probe emitted github-mcp-server/vcs-e99ab3cda12c0259aa4319fb9188a64016d5f3eb (binary-probe/1.0.0), matching the published source rather than the previous build.

go test -race -count=1 ./internal/ghmcp ./pkg/http/transport ./cmd/github-mcp-server
GOTOOLCHAIN=go1.25.12 script/lint
script/test
go build -o /tmp/github-mcp-server ./cmd/github-mcp-server
GOTOOLCHAIN=go1.25.12 bin/golangci-lint run
git diff --check

The tagged live-service E2E suite, full container build and production rollout were not run. This remains an unsigned draft for review.

Docs

  • Not needed
  • Updated (README / docs / examples) — documented the source-version contract and whole-package build command. Tool documentation generation is not needed because schemas and toolsets are unchanged.

Preserve release identities, resolve actual source revisions, and attach upstream client metadata per request without mutating shared transports.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6dde41da-ec25-4370-b26c-b036d0a53b3c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants