Skip to main content
Glama
custom-headers-implementation.md23.2 kB
# MCP Swagger 自定义请求头实现方案 ## 1. 实现架构 ### 1.1 类型定义扩展 #### 更新 TransformerOptions 接口 ```typescript // packages/mcp-swagger-parser/src/transformer/types.ts /** * 自定义请求头配置 */ export interface CustomHeaders { /** * 静态头:固定值的请求头 */ static?: Record<string, string>; /** * 环境变量头:从环境变量获取值 * key: 请求头名称, value: 环境变量名称 */ env?: Record<string, string>; /** * 动态头:通过函数动态生成 */ dynamic?: Record<string, () => string | Promise<string>>; /** * 条件头:根据条件动态添加 */ conditional?: Array<{ condition: (context: RequestContext) => boolean; headers: Record<string, string>; }>; } /** * 请求上下文 */ export interface RequestContext { method: string; path: string; args: any; operation?: OperationObject; } export interface TransformerOptions { baseUrl?: string; includeDeprecated?: boolean; includeTags?: string[]; excludeTags?: string[]; requestTimeout?: number; defaultHeaders?: Record<string, string>; customHandlers?: Record<string, (extra: any) => Promise<MCPToolResponse>>; pathPrefix?: string; stripBasePath?: boolean; authConfig?: AuthConfig; // 新增:自定义请求头配置 customHeaders?: CustomHeaders; // 新增:是否启用请求头调试 debugHeaders?: boolean; // 新增:受保护的请求头列表(不允许被覆盖) protectedHeaders?: string[]; // ... 其他现有选项 } ``` ### 1.2 核心实现类 #### CustomHeadersManager 类 ```typescript // packages/mcp-swagger-parser/src/headers/CustomHeadersManager.ts import { CustomHeaders, RequestContext } from '../transformer/types'; /** * 自定义请求头管理器 */ export class CustomHeadersManager { private config: CustomHeaders; private protectedHeaders: Set<string>; private debugMode: boolean; constructor(config: CustomHeaders = {}, options: { protectedHeaders?: string[], debugMode?: boolean } = {}) { this.config = config; this.protectedHeaders = new Set([ 'content-type', 'content-length', 'host', 'connection', 'transfer-encoding', 'upgrade', ...(options.protectedHeaders || []) ]); this.debugMode = options.debugMode || false; } /** * 获取所有自定义请求头 */ async getHeaders(context: RequestContext): Promise<Record<string, string>> { const headers: Record<string, string> = {}; try { // 1. 添加静态头 if (this.config.static) { Object.assign(headers, this.config.static); if (this.debugMode) { console.log('Added static headers:', this.config.static); } } // 2. 添加环境变量头 if (this.config.env) { const envHeaders = await this.resolveEnvHeaders(this.config.env); Object.assign(headers, envHeaders); if (this.debugMode) { console.log('Added env headers:', envHeaders); } } // 3. 添加动态头 if (this.config.dynamic) { const dynamicHeaders = await this.resolveDynamicHeaders(this.config.dynamic); Object.assign(headers, dynamicHeaders); if (this.debugMode) { console.log('Added dynamic headers:', dynamicHeaders); } } // 4. 添加条件头 if (this.config.conditional) { const conditionalHeaders = await this.resolveConditionalHeaders(this.config.conditional, context); Object.assign(headers, conditionalHeaders); if (this.debugMode) { console.log('Added conditional headers:', conditionalHeaders); } } // 5. 过滤受保护的头 const filteredHeaders = this.filterProtectedHeaders(headers); if (this.debugMode) { console.log('Final custom headers:', filteredHeaders); } return filteredHeaders; } catch (error) { console.error('Error resolving custom headers:', error); return {}; } } private async resolveEnvHeaders(envConfig: Record<string, string>): Promise<Record<string, string>> { const headers: Record<string, string> = {}; for (const [headerName, envName] of Object.entries(envConfig)) { const value = process.env[envName]; if (value) { headers[headerName] = value; } else if (this.debugMode) { console.warn(`Environment variable ${envName} not found for header ${headerName}`); } } return headers; } private async resolveDynamicHeaders(dynamicConfig: Record<string, () => string | Promise<string>>): Promise<Record<string, string>> { const headers: Record<string, string> = {}; for (const [headerName, generator] of Object.entries(dynamicConfig)) { try { const value = await generator(); if (value) { headers[headerName] = value; } } catch (error) { console.warn(`Failed to generate dynamic header ${headerName}:`, error); } } return headers; } private async resolveConditionalHeaders( conditionalConfig: Array<{ condition: (context: RequestContext) => boolean; headers: Record<string, string> }>, context: RequestContext ): Promise<Record<string, string>> { const headers: Record<string, string> = {}; for (const rule of conditionalConfig) { try { if (rule.condition(context)) { Object.assign(headers, rule.headers); } } catch (error) { console.warn('Error evaluating conditional header rule:', error); } } return headers; } private filterProtectedHeaders(headers: Record<string, string>): Record<string, string> { const filtered: Record<string, string> = {}; for (const [key, value] of Object.entries(headers)) { const normalizedKey = key.toLowerCase(); if (!this.protectedHeaders.has(normalizedKey)) { filtered[key] = value; } else if (this.debugMode) { console.warn(`Protected header ignored: ${key}`); } } return filtered; } } ``` ### 1.3 Transformer 集成 #### 更新 OpenAPIToMCPTransformer 类 ```typescript // packages/mcp-swagger-parser/src/transformer/index.ts import { CustomHeadersManager } from '../headers/CustomHeadersManager'; export class OpenAPIToMCPTransformer { private customHeadersManager?: CustomHeadersManager; constructor(spec: OpenAPISpec, options: TransformerOptions = {}) { // ... 现有代码 ... // 初始化自定义请求头管理器 if (options.customHeaders) { this.customHeadersManager = new CustomHeadersManager( options.customHeaders, { protectedHeaders: options.protectedHeaders, debugMode: options.debugHeaders } ); } } private async executeHttpRequest( method: string, path: string, args: any, operation: OperationObject ): Promise<MCPToolResponse> { try { const { url, queryParams } = this.buildUrlWithParams(path, args, operation); // 1. 系统默认头 const headers = { ...this.options.defaultHeaders }; // 2. 自定义头(在认证头之前添加,优先级较低) if (this.customHeadersManager) { const customHeaders = await this.customHeadersManager.getHeaders({ method, path, args, operation }); Object.assign(headers, customHeaders); } // 3. 认证头(最高优先级,可能覆盖自定义头) if (this.authManager) { const authHeaders = await this.authManager.getAuthHeaders({ method, path, args }); Object.assign(headers, authHeaders); } // 4. 调试输出最终请求头 if (this.options.debugHeaders) { console.log(`[${method} ${path}] Final headers:`, headers); } // 5. 执行请求 const response = await axios({ method: method.toLowerCase() as any, url, params: queryParams, data: this.buildRequestBody(args, operation), headers, timeout: this.options.requestTimeout, validateStatus: () => true, maxRedirects: 5, responseType: 'json' }); return this.formatHttpResponse(response, method, path, operation); } catch (error) { return this.handleRequestError(error, method, path); } } } ``` ## 2. CLI 参数扩展 ### 2.1 新增 CLI 参数 ```typescript // packages/mcp-swagger-server/src/cli.ts interface ServerOptions { // ... 现有选项 ... // 自定义请求头相关选项 customHeaders?: string[]; // --custom-header "Key=Value" customHeadersConfig?: string; // --custom-headers-config headers.json customHeadersEnv?: string[]; // --custom-header-env "X-Client-ID=CLIENT_ID" debugHeaders?: boolean; // --debug-headers } // 解析参数 const { values, positionals } = parseArgs({ options: { // ... 现有选项 ... // 自定义请求头选项 "custom-header": { type: "string", multiple: true, }, "custom-headers-config": { type: "string", }, "custom-header-env": { type: "string", multiple: true, }, "debug-headers": { type: "boolean", default: false, }, // ... 其他选项 ... }, allowPositionals: true, }); ``` ### 2.2 配置解析函数 ```typescript // packages/mcp-swagger-server/src/cli.ts interface CustomHeadersConfig { static?: Record<string, string>; env?: Record<string, string>; dynamic?: Record<string, string>; // 用于存储动态头的配置 conditional?: Array<{ condition: string; // 条件表达式字符串 headers: Record<string, string>; }>; } /** * 解析自定义请求头配置 */ function parseCustomHeaders( options: ServerOptions & { 'custom-header'?: string[], 'custom-headers-config'?: string, 'custom-header-env'?: string[] }, config?: ConfigFile, envVars?: Record<string, string> ): CustomHeaders | undefined { const customHeaders: CustomHeaders = {}; let hasConfig = false; // 1. 从配置文件读取 if (config?.customHeaders) { Object.assign(customHeaders, config.customHeaders); hasConfig = true; } // 2. 从专用配置文件读取 if (options['custom-headers-config']) { try { const configFile = JSON.parse(fs.readFileSync(options['custom-headers-config'], 'utf8')); Object.assign(customHeaders, configFile); hasConfig = true; } catch (error) { console.error(`Error loading custom headers config: ${error.message}`); } } // 3. 从命令行参数读取静态头 if (options['custom-header']) { if (!customHeaders.static) customHeaders.static = {}; for (const header of options['custom-header']) { const [key, value] = header.split('=', 2); if (key && value) { customHeaders.static[key] = value; hasConfig = true; } } } // 4. 从命令行参数读取环境变量头 if (options['custom-header-env']) { if (!customHeaders.env) customHeaders.env = {}; for (const header of options['custom-header-env']) { const [key, envName] = header.split('=', 2); if (key && envName) { customHeaders.env[key] = envName; hasConfig = true; } } } // 5. 从环境变量读取(MCP_CUSTOM_HEADERS_* 格式) const customHeadersFromEnv = extractCustomHeadersFromEnv(envVars); if (Object.keys(customHeadersFromEnv).length > 0) { if (!customHeaders.static) customHeaders.static = {}; Object.assign(customHeaders.static, customHeadersFromEnv); hasConfig = true; } return hasConfig ? customHeaders : undefined; } /** * 从环境变量中提取自定义请求头 * 格式:MCP_CUSTOM_HEADERS_<HEADER_NAME>=<VALUE> */ function extractCustomHeadersFromEnv(envVars: Record<string, string> = {}): Record<string, string> { const headers: Record<string, string> = {}; const prefix = 'MCP_CUSTOM_HEADERS_'; // 合并系统环境变量和 .env 文件变量 const allEnvVars = { ...envVars, ...process.env }; for (const [key, value] of Object.entries(allEnvVars)) { if (key.startsWith(prefix) && value) { const headerName = key.substring(prefix.length).replace(/_/g, '-'); headers[headerName] = value; } } return headers; } ``` ### 2.3 配置文件格式 ```typescript // packages/mcp-swagger-server/src/types/config.ts export interface ConfigFile { openapi?: string; transport?: string; port?: number; auth?: { type: string; bearer?: { token?: string; envName?: string; }; }; // 新增:自定义请求头配置 customHeaders?: { static?: Record<string, string>; env?: Record<string, string>; conditional?: Array<{ condition: string; headers: Record<string, string>; }>; }; // 新增:调试选项 debugHeaders?: boolean; } ``` ## 3. 使用示例 ### 3.1 命令行使用 ```bash # 1. 基本静态头 mcp-swagger-server \ --openapi https://api.example.com/swagger.json \ --custom-header "User-Agent=MCP-Client/1.0" \ --custom-header "X-Client-Version=1.0.0" # 2. 环境变量头 mcp-swagger-server \ --openapi https://api.example.com/swagger.json \ --custom-header-env "X-Client-ID=CLIENT_ID" \ --custom-header-env "X-Request-Source=REQUEST_SOURCE" # 3. 配置文件 mcp-swagger-server \ --config config.json \ --custom-headers-config headers.json \ --debug-headers # 4. 环境变量格式 export MCP_CUSTOM_HEADERS_USER_AGENT="MCP-Client/1.0" export MCP_CUSTOM_HEADERS_X_CLIENT_VERSION="1.0.0" mcp-swagger-server --openapi https://api.example.com/swagger.json ``` ### 3.2 配置文件示例 ```json // config.json { "openapi": "https://api.example.com/swagger.json", "transport": "stdio", "customHeaders": { "static": { "User-Agent": "MCP-Swagger-Client/1.0", "X-Client-Version": "1.0.0", "Accept": "application/json" }, "env": { "X-Client-ID": "CLIENT_ID", "X-Request-Source": "REQUEST_SOURCE" }, "conditional": [ { "condition": "method === 'POST'", "headers": { "X-Request-Type": "mutation" } }, { "condition": "path.includes('/admin')", "headers": { "X-Admin-Request": "true" } } ] }, "debugHeaders": true } ``` ```json // headers.json (专用头配置文件) { "static": { "User-Agent": "MCP-Swagger-Client/1.0", "X-Client-Version": "1.0.0", "Accept": "application/json" }, "env": { "X-API-Client": "API_CLIENT_NAME", "X-Request-ID": "REQUEST_ID_PREFIX" }, "dynamic": { "X-Request-ID": "generateRequestId", "X-Timestamp": "generateTimestamp" } } ``` ### 3.3 .env 文件示例 ```bash # .env MCP_OPENAPI_URL=https://api.example.com/swagger.json MCP_TRANSPORT=stdio # 自定义请求头 MCP_CUSTOM_HEADERS_USER_AGENT=MCP-Client/1.0 MCP_CUSTOM_HEADERS_X_CLIENT_VERSION=1.0.0 MCP_CUSTOM_HEADERS_ACCEPT=application/json # 环境变量映射 CLIENT_ID=my-client-123 REQUEST_SOURCE=mcp-swagger-server ``` ## 4. 预定义动态头函数 ### 4.1 常用动态头生成器 ```typescript // packages/mcp-swagger-parser/src/headers/generators.ts export const predefinedGenerators = { /** * 生成唯一请求ID */ generateRequestId: () => { return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }, /** * 生成时间戳 */ generateTimestamp: () => { return new Date().toISOString(); }, /** * 生成Unix时间戳 */ generateUnixTimestamp: () => { return Math.floor(Date.now() / 1000).toString(); }, /** * 生成UUID v4 */ generateUUID: () => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } }; ``` ### 4.2 动态头配置支持 ```typescript // 在 CustomHeadersManager 中添加对预定义函数的支持 import { predefinedGenerators } from './generators'; private async resolveDynamicHeaders(dynamicConfig: Record<string, string | (() => string | Promise<string>)>): Promise<Record<string, string>> { const headers: Record<string, string> = {}; for (const [headerName, generator] of Object.entries(dynamicConfig)) { try { let value: string; if (typeof generator === 'string') { // 字符串形式,查找预定义函数 const predefinedFn = predefinedGenerators[generator]; if (predefinedFn) { value = await predefinedFn(); } else { console.warn(`Unknown predefined generator: ${generator}`); continue; } } else if (typeof generator === 'function') { // 函数形式,直接调用 value = await generator(); } else { console.warn(`Invalid generator type for header ${headerName}`); continue; } if (value) { headers[headerName] = value; } } catch (error) { console.warn(`Failed to generate dynamic header ${headerName}:`, error); } } return headers; } ``` ## 5. 测试用例 ### 5.1 单元测试 ```typescript // packages/mcp-swagger-parser/src/headers/__tests__/CustomHeadersManager.test.ts import { CustomHeadersManager } from '../CustomHeadersManager'; describe('CustomHeadersManager', () => { beforeEach(() => { // 清理环境变量 delete process.env.TEST_HEADER; }); it('should add static headers', async () => { const manager = new CustomHeadersManager({ static: { 'X-Test': 'value' } }); const headers = await manager.getHeaders({ method: 'GET', path: '/test', args: {} }); expect(headers['X-Test']).toBe('value'); }); it('should add environment headers', async () => { process.env.TEST_HEADER = 'env-value'; const manager = new CustomHeadersManager({ env: { 'X-Test': 'TEST_HEADER' } }); const headers = await manager.getHeaders({ method: 'GET', path: '/test', args: {} }); expect(headers['X-Test']).toBe('env-value'); }); it('should filter protected headers', async () => { const manager = new CustomHeadersManager({ static: { 'Content-Type': 'application/xml' } }); const headers = await manager.getHeaders({ method: 'GET', path: '/test', args: {} }); expect(headers['Content-Type']).toBeUndefined(); }); it('should handle conditional headers', async () => { const manager = new CustomHeadersManager({ conditional: [ { condition: (context) => context.method === 'POST', headers: { 'X-Post-Only': 'true' } } ] }); const getHeaders = await manager.getHeaders({ method: 'GET', path: '/test', args: {} }); const postHeaders = await manager.getHeaders({ method: 'POST', path: '/test', args: {} }); expect(getHeaders['X-Post-Only']).toBeUndefined(); expect(postHeaders['X-Post-Only']).toBe('true'); }); }); ``` ### 5.2 集成测试 ```typescript // packages/mcp-swagger-parser/src/transformer/__tests__/CustomHeaders.integration.test.ts import { OpenAPIToMCPTransformer } from '../index'; import axios from 'axios'; jest.mock('axios'); const mockedAxios = axios as jest.Mocked<typeof axios>; describe('OpenAPIToMCPTransformer with CustomHeaders', () => { it('should include custom headers in HTTP requests', async () => { const transformer = new OpenAPIToMCPTransformer( mockOpenAPISchema, { customHeaders: { static: { 'X-Test': 'integration' } } } ); mockedAxios.mockResolvedValue({ status: 200, data: { success: true }, headers: {}, config: {} }); const tools = transformer.transformToMCPTools(); const tool = tools[0]; await tool.handler({}); expect(mockedAxios).toHaveBeenCalledWith( expect.objectContaining({ headers: expect.objectContaining({ 'X-Test': 'integration' }) }) ); }); it('should respect header priority order', async () => { const transformer = new OpenAPIToMCPTransformer( mockOpenAPISchema, { defaultHeaders: { 'X-Priority': 'default' }, customHeaders: { static: { 'X-Priority': 'custom' } }, authConfig: { type: 'bearer', bearer: { token: 'test-token' } } } ); mockedAxios.mockResolvedValue({ status: 200, data: { success: true }, headers: {}, config: {} }); const tools = transformer.transformToMCPTools(); const tool = tools[0]; await tool.handler({}); expect(mockedAxios).toHaveBeenCalledWith( expect.objectContaining({ headers: expect.objectContaining({ 'X-Priority': 'custom', // 自定义头覆盖默认头 'Authorization': 'Bearer test-token' // 认证头优先级最高 }) }) ); }); }); ``` ## 6. 文档更新 ### 6.1 README 更新 需要更新以下文档: - `packages/mcp-swagger-parser/README.md` - `packages/mcp-swagger-server/README.md` - `docs/usage-guide.md` ### 6.2 API 文档更新 需要更新: - `packages/mcp-swagger-parser/docs/API_DOCUMENTATION.md` - `packages/mcp-swagger-parser/docs/TECHNICAL_DOCUMENTATION.md` ## 7. 向后兼容性 ### 7.1 兼容性保证 1. **现有的 `defaultHeaders` 选项继续工作** 2. **现有的认证配置不受影响** 3. **新功能是可选的,不会破坏现有配置** ### 7.2 迁移路径 ```typescript // 旧配置 const options = { defaultHeaders: { 'User-Agent': 'MyApp/1.0', 'X-Client-ID': 'client123' } }; // 新配置(推荐) const options = { defaultHeaders: { 'Content-Type': 'application/json' }, customHeaders: { static: { 'User-Agent': 'MyApp/1.0', 'X-Client-ID': 'client123' } } }; ``` ## 8. 实施计划 ### 8.1 开发阶段 1. **阶段 1**:实现核心类型定义和 `CustomHeadersManager` 2. **阶段 2**:集成到 `OpenAPIToMCPTransformer` 3. **阶段 3**:扩展 CLI 参数解析 4. **阶段 4**:添加预定义动态头生成器 5. **阶段 5**:完善测试用例和文档 ### 8.2 测试策略 1. **单元测试**:测试 `CustomHeadersManager` 的各种配置场景 2. **集成测试**:测试与 `OpenAPIToMCPTransformer` 的集成 3. **端到端测试**:测试完整的 CLI 到 HTTP 请求的流程 4. **性能测试**:确保动态头生成不影响性能 这个设计方案提供了: - 灵活的配置选项 - 清晰的优先级规则 - 完整的 CLI 支持 - 良好的向后兼容性 - 完善的测试覆盖 实现后,用户可以通过多种方式配置自定义请求头,满足各种场景的需求。

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zaizaizhao/mcp-swagger-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server