diff --git a/packages/openapi-mcp-server/src/utils/http.ts b/packages/openapi-mcp-server/src/utils/http.ts index 39ac759..b322bdd 100644 --- a/packages/openapi-mcp-server/src/utils/http.ts +++ b/packages/openapi-mcp-server/src/utils/http.ts @@ -1,11 +1,25 @@ import fetch, { Response } from 'node-fetch'; import FormData from 'form-data'; import qs from 'qs'; +import * as fs from 'fs'; +import * as path from 'path'; import { HttpMethod } from '@app/types'; import logger from './logger'; +function getPackageVersion(): string { + try { + const packageJsonPath = path.resolve(__dirname, '../../package.json'); + const packageJsonData = fs.readFileSync(packageJsonPath, 'utf8'); + const packageJson = JSON.parse(packageJsonData); + return packageJson.version; + } catch (error) { + logger.error(`Failed to read package.json: ${error}`); + return 'unknown'; + } +} + type SuccessResponse = { ok: true; statusCode: number; @@ -106,11 +120,16 @@ export default class Http { private readonly logger; + private readonly version: string; + constructor(config: Configuration) { + this.version = getPackageVersion(); this.defaultRequest = { headers: { 'Content-Type': 'application/json', ...getAuthorization(config.authorization), + 'x-mcp-server-id': `twilio-openapi-mcp-server/${this.version}`, + 'user-agent': `twilio-openapi-mcp-server/${this.version}`, }, }; @@ -123,11 +142,11 @@ export default class Http { private async make(request: HttpRequest): Promise> { try { logger.debug(`request object: ${JSON.stringify(request)}`); - const options: RequestInit = { ...this.defaultRequest, method: request.method, }; + if (request.headers) { // @ts-ignore options.headers = { diff --git a/packages/openapi-mcp-server/tests/utils/http.spec.ts b/packages/openapi-mcp-server/tests/utils/http.spec.ts index 6512f65..8158797 100644 --- a/packages/openapi-mcp-server/tests/utils/http.spec.ts +++ b/packages/openapi-mcp-server/tests/utils/http.spec.ts @@ -47,6 +47,10 @@ describe('Http', () => { headers: expect.objectContaining({ Authorization: 'Basic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk', 'Content-Type': 'application/json', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), }), ); @@ -139,6 +143,10 @@ describe('Http', () => { method: 'POST', headers: expect.objectContaining({ 'Content-Type': 'application/x-www-form-urlencoded', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), body: expect.any(String), // We're not testing the exact encoding, just that it's a string }), @@ -193,6 +201,10 @@ describe('Http', () => { headers: expect.objectContaining({ Authorization: 'Basic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk', 'Content-Type': 'application/json', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), body: JSON.stringify(requestBody), }), @@ -229,6 +241,10 @@ describe('Http', () => { method: 'PUT', headers: expect.objectContaining({ 'Content-Type': 'application/x-www-form-urlencoded', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), body: expect.any(String), }), @@ -278,6 +294,10 @@ describe('Http', () => { method: 'DELETE', headers: expect.objectContaining({ 'Content-Type': 'application/json', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), }), ); @@ -454,6 +474,10 @@ describe('Http', () => { headers: expect.objectContaining({ 'content-type': 'multipart/form-data; boundary=---boundary', Authorization: 'Basic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), body: formData, }), @@ -522,6 +546,10 @@ describe('Http', () => { 'content-type': 'multipart/form-data; boundary=---boundary', 'X-Custom-Header': 'custom-value', Authorization: 'Basic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), body: formData, }), @@ -558,6 +586,10 @@ describe('Http', () => { method: 'POST', headers: expect.objectContaining({ Authorization: 'Basic dGVzdC11c2VybmFtZTp0ZXN0LXBhc3N3b3Jk', + 'x-mcp-server-id': expect.stringMatching( + 'twilio-openapi-mcp-server', + ), + 'user-agent': expect.stringMatching('twilio-openapi-mcp-server'), }), body: formData, }),