Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This CLI tool automates the generation of MCP-compatible servers that proxy requ
- 🔌 **Multiple Transports**: Communicate over stdio, SSE via Hono, or StreamableHTTP.
- 🧰 **Project Scaffold**: Generates a complete Node.js project with `tsconfig.json`, `package.json`, and entry point.
- 🧪 **Built-in HTML Test Clients**: Test API interactions visually in your browser (for web-based transports).
- 📊 **Analytics & Telemetry**: Optional MCPcat analytics and OpenTelemetry support for monitoring and debugging.

---

Expand All @@ -44,6 +45,15 @@ openapi-mcp-generator --input path/to/openapi.json --output path/to/output/dir -

# Generate an MCP StreamableHTTP server
openapi-mcp-generator --input path/to/openapi.json --output path/to/output/dir --transport=streamable-http --port=3000

# Generate with MCPcat analytics enabled
openapi-mcp-generator --input path/to/openapi.json --output path/to/output/dir --with-mcpcat

# Generate with OpenTelemetry tracing enabled
openapi-mcp-generator --input path/to/openapi.json --output path/to/output/dir --with-otel

# Generate with both MCPcat analytics and OpenTelemetry
openapi-mcp-generator --input path/to/openapi.json --output path/to/output/dir --with-mcpcat --with-otel
```

### CLI Options
Expand All @@ -58,6 +68,8 @@ openapi-mcp-generator --input path/to/openapi.json --output path/to/output/dir -
| `--transport` | `-t` | Transport mode: `"stdio"` (default), `"web"`, or `"streamable-http"` | `"stdio"` |
| `--port` | `-p` | Port for web-based transports | `3000` |
| `--default-include` | | Default behavior for x-mcp filtering. Accepts `true` or `false` (case-insensitive). `true` = include by default, `false` = exclude by default. | `true` |
| `--with-mcpcat` | | Enable MCPcat product analytics for usage insights and debugging | `false` |
| `--with-otel` | | Enable OpenTelemetry (OTLP) exporters for distributed tracing and logging | `false` |
| `--force` | | Overwrite existing files in the output directory without confirmation | `false` |

## 📦 Programmatic API
Expand Down Expand Up @@ -168,6 +180,43 @@ Configure auth credentials in your environment:

---

## 📊 Analytics & Telemetry

### MCPcat Analytics
[MCPcat](https://mcpcat.io) provides product analytics and live debugging tools specifically designed for MCP servers. When enabled with `--with-mcpcat`, your generated server will include:

- Usage tracking and analytics
- Tool call monitoring
- Error detection and reporting
- Performance insights

To use MCPcat:
1. Sign up for free at [mcpcat.io](https://mcpcat.io)
2. Get your project ID
3. Set the `MCPCAT_PROJECT_ID` environment variable

### OpenTelemetry Support
Enable distributed tracing and logging with `--with-otel` to integrate with your existing observability stack:

- OTLP trace exporters
- Distributed request tracing
- Performance metrics
- Integration with popular observability platforms (Datadog, New Relic, Grafana, etc.)

Configure the OTLP endpoint with the `OTLP_ENDPOINT` environment variable.

### Combined Usage
Use both flags together (`--with-mcpcat --with-otel`) to get the benefits of both MCPcat's MCP-specific analytics and OpenTelemetry's broader observability ecosystem.

#### Environment Variables
Configure analytics and telemetry:

| Variable | Description | Required When |
| -------------------- | -------------------------------------------------------- | ------------------ |
| `MCPCAT_PROJECT_ID` | Your MCPcat project ID from [mcpcat.io](https://mcpcat.io) | `--with-mcpcat` |
| `OTLP_ENDPOINT` | OpenTelemetry collector endpoint URL | `--with-otel` |


## 🔎 Filtering Endpoints with OpenAPI Extensions

You can control which operations are exposed as MCP tools using a vendor extension flag `x-mcp`. This extension is supported at the root, path, and operation levels. By default, endpoints are included unless explicitly excluded.
Expand Down
18 changes: 17 additions & 1 deletion src/generator/env-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
*/
import { OpenAPIV3 } from 'openapi-types';
import { getEnvVarName } from '../utils/security.js';
import { CliOptions } from '../types/index.js';

/**
* Generates the content of .env.example file for the MCP server
*
* @param securitySchemes Security schemes from the OpenAPI spec
* @param options CLI options
* @returns Content for .env.example file
*/
export function generateEnvExample(
securitySchemes?: OpenAPIV3.ComponentsObject['securitySchemes']
securitySchemes?: OpenAPIV3.ComponentsObject['securitySchemes'],
options?: CliOptions
): string {
let content = `# MCP Server Environment Variables
# Copy this file to .env and fill in the values
Expand Down Expand Up @@ -57,6 +60,19 @@ LOG_LEVEL=info
content += `# No API authentication required\n`;
}

// Add MCPcat environment variables if enabled
if (options?.withMcpcat) {
content += `\n# MCPcat -- MCP product analytics and live debugging tools`;
content += `\n# Sign up and get your project ID for free at https://mcpcat.io\n`;
content += `MCPCAT_PROJECT_ID=proj_0000000 # Replace with your MCPcat project ID\n`;
}

// Add OpenTelemetry environment variables if enabled
if (options?.withOtel) {
content += `\n# OpenTelemetry Configuration for logging and traces\n`;
content += `OTLP_ENDPOINT=http://localhost:4318/v1/traces # OTLP collector endpoint\n`;
}

content += `\n# Add any other environment variables your API might need\n`;

return content;
Expand Down
9 changes: 7 additions & 2 deletions src/generator/package-json.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { CliOptions } from '../types/index.js';

/**
* Generates the content of package.json for the MCP server
*
* @param serverName Server name
* @param serverVersion Server version
* @param transportType Type of transport to use (stdio, web, or streamable-http)
* @param options CLI options
* @returns JSON string for package.json
*/
export function generatePackageJson(
serverName: string,
serverVersion: string,
transportType: string = 'stdio'
options: CliOptions
): string {
const transportType = options.transport || 'stdio';
const includeWebDeps = transportType === 'web' || transportType === 'streamable-http';
const includeMcpcat = options.withMcpcat || options.withOtel;

Comment on lines 11 to 19
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore backward-compatible generatePackageJson signature

Previously this helper accepted either two args (defaulting transport to 'stdio') or an explicit transport string. Making the third argument mandatory—and typing it as the full CliOptions—breaks existing programmatic consumers who relied on the old call shape. Please keep the third parameter optional and scoped to the properties this function actually uses so downstream builds keep compiling.

-export function generatePackageJson(
-  serverName: string,
-  serverVersion: string,
-  options: CliOptions
-): string {
-  const transportType = options.transport || 'stdio';
+export function generatePackageJson(
+  serverName: string,
+  serverVersion: string,
+  options: Pick<CliOptions, 'transport' | 'withMcpcat' | 'withOtel'> = {}
+): string {
+  const transportType = options.transport ?? 'stdio';
   const includeWebDeps = transportType === 'web' || transportType === 'streamable-http';
-  const includeMcpcat = options.withMcpcat || options.withOtel;
+  const includeMcpcat = Boolean(options.withMcpcat || options.withOtel);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function generatePackageJson(
serverName: string,
serverVersion: string,
transportType: string = 'stdio'
options: CliOptions
): string {
const transportType = options.transport || 'stdio';
const includeWebDeps = transportType === 'web' || transportType === 'streamable-http';
const includeMcpcat = options.withMcpcat || options.withOtel;
export function generatePackageJson(
serverName: string,
serverVersion: string,
options: Pick<CliOptions, 'transport' | 'withMcpcat' | 'withOtel'> = {}
): string {
const transportType = options.transport ?? 'stdio';
const includeWebDeps = transportType === 'web' || transportType === 'streamable-http';
const includeMcpcat = Boolean(options.withMcpcat || options.withOtel);

const packageData: any = {
name: serverName,
Expand All @@ -36,6 +40,7 @@ export function generatePackageJson(
dotenv: '^16.4.5',
zod: '^3.24.3',
'json-schema-to-zod': '^2.6.1',
...(includeMcpcat ? { mcpcat: '^0.1.5' } : {}),
},
devDependencies: {
'@types/node': '^22.15.2',
Expand Down
46 changes: 46 additions & 0 deletions src/generator/server-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,46 @@ import {
} from '../utils/code-gen.js';
import { generateExecuteApiToolFunction } from '../utils/security.js';

/**
* Generates MCPcat tracking code based on options
*/
function generateMcpcatTracking(options: CliOptions): string {
if (!options.withMcpcat && !options.withOtel) {
return '';
}

let trackingCode = '\n// MCPcat Product analytics and OpenTelemetry exporters\n';

if (options.withMcpcat && options.withOtel) {
// Both flags enabled
trackingCode += `mcpcat.track(server, process.env.MCPCAT_PROJECT_ID || null, {
exporters: {
otlp: {
type: "otlp",
endpoint: process.env.OTLP_ENDPOINT || "http://localhost:4318/v1/traces"
}
}
});`;
} else if (options.withMcpcat) {
// Only MCPcat enabled
trackingCode += `mcpcat.track(server, process.env.MCPCAT_PROJECT_ID || null);`;
} else if (options.withOtel) {
// Only OTEL enabled
trackingCode += `mcpcat.track(server, null, {
enableToolCallContext: false,
enableReportMissing: false,
exporters: {
otlp: {
type: "otlp",
endpoint: process.env.OTLP_ENDPOINT || "http://localhost:4318/v1/traces"
}
}
});`;
}

return trackingCode + '\n';
}

/**
* Generates the TypeScript code for the MCP server
*
Expand Down Expand Up @@ -80,6 +120,10 @@ export function generateMcpServerCode(
}`;
break;
}
let mcpcatImport = '';
if (options.withMcpcat || options.withOtel) {
mcpcatImport = `import * as mcpcat from "mcpcat";`;
}

// Generate the full server code
return `#!/usr/bin/env node
Expand All @@ -101,6 +145,7 @@ import {
type CallToolResult,
type CallToolRequest
} from "@modelcontextprotocol/sdk/types.js";${transportImport}
${mcpcatImport}

import { z, ZodError } from 'zod';
import { jsonSchemaToZod } from 'json-schema-to-zod';
Expand Down Expand Up @@ -156,6 +201,7 @@ ${listToolsHandlerCode}
${callToolHandlerCode}
${executeApiToolFunctionCode}

${generateMcpcatTracking(options)}
/**
* Main function to start the server
*/
Expand Down
9 changes: 6 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ import { normalizeBoolean } from './utils/helpers.js';
import pkg from '../package.json' with { type: 'json' };

// Export programmatic API
export { getToolsFromOpenApi, McpToolDefinition, GetToolsOptions } from './api.js';
export { getToolsFromOpenApi } from './api.js';
export type { McpToolDefinition, GetToolsOptions } from './api.js';

// Configure CLI
const program = new Command();
Expand Down Expand Up @@ -87,6 +88,8 @@ program
true
)
.option('--force', 'Overwrite existing files without prompting')
.option('--with-mcpcat', 'Enable MCPcat MCP product analytics')
.option('--with-otel', 'Enable OpenTelemetry (OTLP) logging')
.version(pkg.version) // Match package.json version
.action((options) => {
runGenerator(options).catch((error) => {
Expand Down Expand Up @@ -161,7 +164,7 @@ async function runGenerator(options: CliOptions & { force?: boolean }) {
const packageJsonContent = generatePackageJson(
serverName,
serverVersion,
options.transport as TransportType
options
);

console.error('Generating tsconfig.json...');
Expand All @@ -180,7 +183,7 @@ async function runGenerator(options: CliOptions & { force?: boolean }) {
const jestConfigContent = generateJestConfig();

console.error('Generating .env.example file...');
const envExampleContent = generateEnvExample(api.components?.securitySchemes);
const envExampleContent = generateEnvExample(api.components?.securitySchemes, options);

console.error('Generating OAuth2 documentation...');
const oauth2DocsContent = generateOAuth2Docs(api.components?.securitySchemes);
Expand Down
4 changes: 4 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export interface CliOptions {
* false = exclude by default unless x-mcp explicitly enables.
*/
defaultInclude?: boolean;
/** Enable MCPcat analytics tracking */
withMcpcat?: boolean;
/** Enable OpenTelemetry (OTLP) exporters */
withOtel?: boolean;
}

/**
Expand Down
Loading