Skip to content

feat(security): mcp_reject_query_apikey refuses ?apikey= credentials on MCP endpoints - #1696

Merged
Dumbris merged 7 commits into
smart-mcp-proxy:mainfrom
james-crowley:feat/disable-apikey-query
Oct 22, 2026
Merged

Dumbris merged 7 commits into
smart-mcp-proxy:mainfrom
james-crowley:feat/disable-apikey-query

Conversation

@james-crowley

Copy link
Copy Markdown
Contributor

Description

Fixes #1695.

Adds the opt-in config key mcp_reject_query_apikey (default false, so behaviour is unchanged unless set). When true, every /mcp* endpoint (/mcp, /mcp/all, /mcp/call, /mcp/code, /mcp/p/...) answers a request that carries an apikey query parameter with 401 and a message that names the header forms, before any credential lookup. A credential in a URL ends up in reverse-proxy logs, browser history and Referer headers; this lets a deployment whose agents can all send a header make that impossible.

The refusal is explicit rather than "treat as no token": with require_mcp_auth off an unrecognised credential is served as the anonymous administrator, so silently ignoring the parameter would downgrade a caller that believes it is authenticated.

Scope: MCP endpoints only. The REST API, Web UI and /events keep accepting ?apikey= (the UI builds such URLs for SSE and exports). The key is live (listed in undiffedHotConfigFields), no restart.

Changes: internal/config/config.go, internal/server/server.go (mcpAuthMiddleware), internal/runtime/config_hotreload.go, docs (agent-tokens.md, config-file.md), CHANGELOG, regenerated oas/ (via make swagger, which is clean on main without this change).

Checked with a built binary: with the key on, /mcp, /mcp/all and /mcp/p/x with ?apikey= return 401 with the message, a header credential is not refused, and GET /api/v1/status?apikey= still returns 200. go build -tags server ./... builds.

Testing

  • I have tested these changes locally
  • I have added/updated tests that prove my fix is effective or my feature works (internal/server/mcp_reject_query_apikey_test.go: refused on /mcp, /mcp/all, /mcp/code for populated, empty and agent-token values with the handler never reached; header credentials and unrelated query parameters unaffected; default keeps the query form working and authenticated, not anonymous)
  • All existing tests pass (go test ./internal/config ./internal/runtime ./cmd/release-gate, and the TestMCPAuthMiddleware* tests in internal/server; I did not run the full internal/server e2e tests that need a built binary or npx)

Opt-in setting (default false). When true every /mcp* endpoint answers a
request carrying ?apikey= with 401 instead of honouring it, so an agent token
or the API key is never carried in a URL. The refusal is explicit because with
require_mcp_auth off an unrecognised credential is served as the anonymous
administrator. The REST API, Web UI and /events are unchanged.

Fixes smart-mcp-proxy#1695
@codecov-commenter

codecov-commenter commented Oct 20, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…protect, and the Connect caveat

The option forces clients off ?apikey= on MCP endpoints (including the
legacy /v1/tool_code aliases); it cannot keep a URL that was already sent
out of upstream logs. The Connect wizard still writes Codex and generic
entries with ?apikey= in the URL, so those clients need a header carrier
before the option is turned on.

Related smart-mcp-proxy#1696
@Dumbris
Dumbris enabled auto-merge (squash) October 20, 2026 15:52
@Dumbris

Dumbris commented Oct 20, 2026

Copy link
Copy Markdown
Member

Thanks @james-crowley, the implementation is tight: the refusal sits before token handling, the header and bearer paths are untouched, it hot-reloads, and the live matrix (every MCP route, off/on/on+require_mcp_auth, header, bearer, agent token) behaved as described. I pushed one docs commit on top of your branch: the option forces clients to migrate rather than keeping an already-sent URL out of upstream logs, the legacy /v1/tool_code aliases are covered too, and there is a caveat that Connect still writes Codex and generic entries with ?apikey= (tracked in #1709). I also merged main so the Build Binaries flake is gone. Auto-merge is armed; it lands when CI is green.

auto-merge was automatically disabled October 20, 2026 17:35

Head branch was pushed to by a user without write access

@Dumbris

Dumbris commented Oct 22, 2026

Copy link
Copy Markdown
Member

Pushed a merge of main onto your branch with the OpenAPI artifacts regenerated (make swagger), since the only conflict was the generated oas/docs.go. No change to your code. Merging once CI is green on the new head.

@Dumbris Dumbris left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified on a merge with main: build, CI lint config, race tests for config, runtime and the MCP auth middleware (mutation check: disabling the guard fails TestMCPAuthMiddleware_RejectQueryAPIKey); cross-reviewed clean; live matrix checked earlier.

@Dumbris
Dumbris merged commit 5e9c9cd into smart-mcp-proxy:main Oct 22, 2026
51 checks passed
@Dumbris

Dumbris commented Oct 22, 2026

Copy link
Copy Markdown
Member

Merged, and #1695 is closed with it. Thank you, @james-crowley! mcp_reject_query_apikey gives a deployment whose agents can all send a header a way to make credentials-in-URLs impossible, with the refusal sitting before any token lookup and hot-reloading like the rest of the config. The implementation needed no changes at all; only docs went on top.

Contributions like this are what keep MCPProxy moving, and they matter a lot to the project.

One small ask, only if you feel like it: a short public note about your change (a tweet, a LinkedIn post, a line in a community you are part of) helps more people discover MCPProxy. No pressure at all, and I would be glad to repost it.

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.

[Feature]: opt-in setting to refuse ?apikey= credentials on MCP endpoints

3 participants