From 8fa6c6b9d265da23b29d76df971355df9e64bdac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fahreddin=20=C3=96zcan?= <88107904+fahreddinozcan@users.noreply.github.com> Date: Thu, 27 Aug 2026 12:43:23 +0300 Subject: [PATCH] fix(mcp): honor advertised X-Context7-API-Key header (#3091) * fix(mcp): honor advertised API key header (CTX7-2534) * chore: add changeset for MCP API key header --- .changeset/honest-keys-authenticate.md | 5 +++++ packages/mcp/src/index.ts | 1 + packages/mcp/test/integration.test.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 .changeset/honest-keys-authenticate.md diff --git a/.changeset/honest-keys-authenticate.md b/.changeset/honest-keys-authenticate.md new file mode 100644 index 00000000..233676b6 --- /dev/null +++ b/.changeset/honest-keys-authenticate.md @@ -0,0 +1,5 @@ +--- +"@upstash/context7-mcp": patch +--- + +Honor the advertised `X-Context7-API-Key` header in HTTP MCP requests. diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 4f834761..a10e3952 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -355,6 +355,7 @@ async function main() { const extractApiKey = (req: express.Request): string | undefined => { return ( extractBearerToken(req.headers.authorization) || + extractHeaderValue(req.headers["x-context7-api-key"]) || extractHeaderValue(req.headers["context7-api-key"]) || extractHeaderValue(req.headers["x-api-key"]) || extractHeaderValue(req.headers["context7_api_key"]) || diff --git a/packages/mcp/test/integration.test.ts b/packages/mcp/test/integration.test.ts index 06d2a9cc..08cdb6c7 100644 --- a/packages/mcp/test/integration.test.ts +++ b/packages/mcp/test/integration.test.ts @@ -135,6 +135,32 @@ describe("OAuth discovery", () => { }); }); +describe("HTTP API key headers", () => { + test("accepts the advertised X-Context7-API-Key header", async () => { + const apiKey = "ctx7sk-advertised-header-test"; + const client = new Client({ name: "api-key-header-test", version: "1.0.0" }); + + await client.connect( + new StreamableHTTPClientTransport(new URL(httpUrl), { + requestInit: { headers: { "X-Context7-API-Key": apiKey } }, + }) + ); + + try { + requests.length = 0; + await client.callTool({ + name: "query-docs", + arguments: { libraryId: "/vercel/next.js", query: "app router" }, + }); + + const apiCall = requests.find((request) => request.path === "/v2/context"); + expect(apiCall?.headers.authorization).toBe(`Bearer ${apiKey}`); + } finally { + await client.close(); + } + }); +}); + describe.each([ ["http", "modern"], ["http", "legacy"],