From b064f990a168ea1a791b93efada13d865bbfd313 Mon Sep 17 00:00:00 2001 From: kriptoburak Date: Thu, 2 Jul 2026 14:32:38 +0300 Subject: [PATCH] test: cover MCP API key header auth --- tests/features.test.ts | 87 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tests/features.test.ts b/tests/features.test.ts index dabcca0..07b6f68 100644 --- a/tests/features.test.ts +++ b/tests/features.test.ts @@ -8,6 +8,7 @@ import { buildIR } from "../src/ir.js"; import { generateTargets } from "../src/generators/index.js"; import { renderConnectFiles } from "../src/connectgen.js"; import { paginationShape } from "../src/generators/common.js"; +import { renderMcpFiles } from "../src/mcpgen.js"; const SPEC = { openapi: "3.0.0", @@ -114,6 +115,81 @@ function buildFeatureIR() { }); } +function buildXquikMcpIR() { + const spec = { + openapi: "3.1.0", + info: { title: "Xquik API", version: "1.0" }, + servers: [{ url: "https://xquik.com" }], + components: { + securitySchemes: { + ApiKeyAuth: { type: "apiKey", in: "header", name: "x-api-key" }, + }, + schemas: { + SearchResults: { + type: "object", + properties: { + data: { type: "array", items: { type: "object" } }, + }, + }, + }, + }, + paths: { + "/api/v1/x/tweets/search": { + get: { + operationId: "searchTweets", + tags: ["x"], + summary: "Search public tweets", + parameters: [ + { name: "q", in: "query", required: true, schema: { type: "string" } }, + { name: "limit", in: "query", schema: { type: "integer" } }, + ], + responses: { + "200": { + description: "Search results", + content: { "application/json": { schema: { $ref: "#/components/schemas/SearchResults" } } }, + }, + }, + security: [{ ApiKeyAuth: [] }], + }, + }, + }, + }; + const config = { + sdkgen: 1, + project: { name: "xquik", display_name: "Xquik" }, + spec: { path: "./openapi.yaml" }, + targets: { + typescript: { package_name: "@example/xquik" }, + }, + client: { + class_name: "Xquik", + env_prefix: "XQUIK", + base_url: { default: "https://xquik.com" }, + auth: { + api_key: { + env: "XQUIK_API_KEY", + security_scheme: "ApiKeyAuth", + header: "x-api-key", + }, + }, + }, + mcp: { + tools: "typed", + permissions: { + allow_http_gets: true, + }, + }, + }; + + return buildIR({ + config: config as never, + configRaw: JSON.stringify(config), + spec: spec as never, + specRaw: JSON.stringify(spec), + diagnostics: [], + }); +} + async function read(dir: string, rel: string): Promise { return readFile(join(dir, rel), "utf8"); } @@ -156,6 +232,17 @@ async function main(): Promise { assert.equal(ir.client.oauth2?.device_authorization_url, "/oauth/device", "oauth device_authorization_url"); assert.ok(ir.client.auth?.basic, "basic auth configured"); + // MCP auth: API-key header APIs should not fall back to bearer auth. + const xquikMcpServer = renderMcpFiles(buildXquikMcpIR())["src/server.ts"] ?? ""; + assert.match(xquikMcpServer, /"bearerEnv": null/, "MCP disables bearer fallback"); + assert.match(xquikMcpServer, /"name": "x-api-key"/, "MCP keeps API-key header name"); + assert.match(xquikMcpServer, /"env": "XQUIK_API_KEY"/, "MCP keeps API-key env"); + assert.match( + xquikMcpServer, + /headers\[authConfig\.apiKeyHeader\.name\.toLowerCase\(\)\] = key/, + "MCP applies API-key header", + ); + // --- Generated output across languages --- const dir = await mkdtemp("/tmp/feat-test-"); try {