feat: Add programmatic API documentation and implementation

- Introduced PROGRAMMATIC_API.md to document the usage of the OpenAPI to MCP Generator programmatically.
- Implemented the getToolsFromOpenApi function in src/api.ts for extracting MCP tool definitions from OpenAPI specifications.
- Updated README.md to reference the new programmatic API documentation.
- Exported the programmatic API and related types from src/index.ts for easier access.
This commit is contained in:
harsha-iiiv 2025-05-18 18:37:27 +05:30
parent 536714a31b
commit 5b88b5e58d
4 changed files with 260 additions and 10 deletions

137
PROGRAMMATIC_API.md Normal file
View File

@ -0,0 +1,137 @@
# OpenAPI to MCP Generator - Programmatic API
This document describes how to use the OpenAPI to MCP Generator programmatically in your Node.js applications.
## Installation
```bash
npm install openapi-mcp-generator
```
## Usage
The package exports a simple, focused API for extracting MCP tool definitions from OpenAPI specifications:
```typescript
import { getToolsFromOpenApi } from 'openapi-mcp-generator';
```
### `getToolsFromOpenApi(specPathOrUrl, options)`
This function extracts an array of tools from an OpenAPI specification.
**Parameters:**
- `specPathOrUrl`: Path to a local OpenAPI spec file or URL to a remote spec
- `options`: (Optional) Configuration options
**Options:**
- `baseUrl`: Override the base URL in the OpenAPI spec
- `dereference`: Whether to resolve $refs (default: false)
- `excludeOperationIds`: Array of operation IDs to exclude from the results
- `filterFn`: Custom function to filter tools (receives tool, returns boolean)
**Returns:**
- Promise that resolves to an array of McpToolDefinition objects
**Example:**
```typescript
import { getToolsFromOpenApi } from 'openapi-mcp-generator';
// Basic usage
const tools = await getToolsFromOpenApi('./petstore.json');
// With options
const filteredTools = await getToolsFromOpenApi('https://petstore3.swagger.io/api/v3/openapi.json', {
baseUrl: 'https://petstore3.swagger.io/api/v3',
dereference: true,
excludeOperationIds: ['addPet', 'updatePet'],
filterFn: (tool) => tool.method.toLowerCase() === 'get'
});
// Process the results
for (const tool of filteredTools) {
console.log(`Tool: ${tool.name}`);
console.log(` Description: ${tool.description}`);
console.log(` Method: ${tool.method.toUpperCase()} ${tool.pathTemplate}`);
console.log(` OperationId: ${tool.operationId}`);
}
```
## Tool Definition Structure
Each tool definition (`McpToolDefinition`) has the following properties:
```typescript
interface McpToolDefinition {
/** Name of the tool, must be unique */
name: string;
/** Human-readable description of the tool */
description: string;
/** JSON Schema that defines the input parameters */
inputSchema: JSONSchema7 | boolean;
/** HTTP method for the operation (get, post, etc.) */
method: string;
/** URL path template with parameter placeholders */
pathTemplate: string;
/** OpenAPI parameter objects for this operation */
parameters: OpenAPIV3.ParameterObject[];
/** Parameter names and locations for execution */
executionParameters: { name: string; in: string }[];
/** Content type for request body, if applicable */
requestBodyContentType?: string;
/** Security requirements for this operation */
securityRequirements: OpenAPIV3.SecurityRequirementObject[];
/** Original operation ID from the OpenAPI spec */
operationId: string;
/** Base URL for the API (if available) */
baseUrl?: string;
}
```
## Advanced Filtering Examples
### Filter by HTTP Method
```typescript
const getTools = await getToolsFromOpenApi(specUrl, {
filterFn: (tool) => tool.method.toLowerCase() === 'get'
});
```
### Filter by Security Requirements
```typescript
const secureTools = await getToolsFromOpenApi(specUrl, {
filterFn: (tool) => tool.securityRequirements.length > 0
});
```
### Filter by Path Pattern
```typescript
const userTools = await getToolsFromOpenApi(specUrl, {
filterFn: (tool) => tool.pathTemplate.includes('/user')
});
```
### Combining Multiple Filters
```typescript
const safeUserTools = await getToolsFromOpenApi(specUrl, {
excludeOperationIds: ['deleteUser', 'updateUser'],
filterFn: (tool) =>
tool.pathTemplate.includes('/user') &&
tool.method.toLowerCase() === 'get'
});
```

View File

@ -58,6 +58,28 @@ 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` |
| `--force` | | Overwrite existing files in the output directory without confirmation | `false` |
## 📦 Programmatic API
You can also use this package programmatically in your Node.js applications:
```javascript
import { getToolsFromOpenApi } from 'openapi-mcp-generator';
// Extract MCP tool definitions from an OpenAPI spec
const tools = await getToolsFromOpenApi('./petstore.json');
// With options
const filteredTools = await getToolsFromOpenApi('https://example.com/api-spec.json', {
baseUrl: 'https://api.example.com',
dereference: true,
excludeOperationIds: ['deletePet'],
filterFn: (tool) => tool.method.toLowerCase() === 'get'
});
```
For full documentation of the programmatic API, see [PROGRAMMATIC_API.md](./PROGRAMMATIC_API.md).
---
## 🧱 Project Structure

81
src/api.ts Normal file
View File

@ -0,0 +1,81 @@
/**
* Programmatic API for OpenAPI to MCP Generator
* Allows direct usage from other Node.js applications
*/
import SwaggerParser from '@apidevtools/swagger-parser';
import { OpenAPIV3 } from 'openapi-types';
import { extractToolsFromApi } from './parser/extract-tools.js';
import { McpToolDefinition } from './types/index.js';
import { determineBaseUrl } from './utils/url.js';
/**
* Options for generating the MCP tools
*/
export interface GetToolsOptions {
/** Optional base URL to override the one in the OpenAPI spec */
baseUrl?: string;
/** Whether to dereference the OpenAPI spec */
dereference?: boolean;
/** Array of operation IDs to exclude from the tools list */
excludeOperationIds?: string[];
/** Optional filter function to exclude tools based on custom criteria */
filterFn?: (tool: McpToolDefinition) => boolean;
}
/**
* Get a list of tools from an OpenAPI specification
*
* @param specPathOrUrl Path or URL to the OpenAPI specification
* @param options Options for generating the tools
* @returns Promise that resolves to an array of tool definitions
*/
export async function getToolsFromOpenApi(
specPathOrUrl: string,
options: GetToolsOptions = {}
): Promise<McpToolDefinition[]> {
try {
// Parse the OpenAPI spec
const api = options.dereference
? (await SwaggerParser.dereference(specPathOrUrl)) as OpenAPIV3.Document
: (await SwaggerParser.parse(specPathOrUrl)) as OpenAPIV3.Document;
// Extract tools from the API
const allTools = extractToolsFromApi(api);
// Add base URL to each tool
const baseUrl = determineBaseUrl(api, options.baseUrl);
// Apply filters to exclude specified operationIds and custom filter function
let filteredTools = allTools;
// Filter by excluded operation IDs if provided
if (options.excludeOperationIds && options.excludeOperationIds.length > 0) {
const excludeSet = new Set(options.excludeOperationIds);
filteredTools = filteredTools.filter(tool => !excludeSet.has(tool.operationId));
}
// Apply custom filter function if provided
if (options.filterFn) {
filteredTools = filteredTools.filter(options.filterFn);
}
// Return the filtered tools with base URL added
return filteredTools.map(tool => ({
...tool,
baseUrl: baseUrl || '',
}));
} catch (error) {
// Provide more context for the error
if (error instanceof Error) {
throw new Error(`Failed to extract tools from OpenAPI: ${error.message}`);
}
throw error;
}
}
// Export types for convenience
export { McpToolDefinition };

View File

@ -31,6 +31,9 @@ import {
// Import types
import { CliOptions, TransportType } from './types/index.js';
// Export programmatic API
export { getToolsFromOpenApi, McpToolDefinition, GetToolsOptions } from './api.js';
// Configure CLI
const program = new Command();
@ -69,18 +72,27 @@ program
(val) => parseInt(val, 10)
)
.option('--force', 'Overwrite existing files without prompting')
.version('2.0.0'); // Match package.json version
.version('3.0.0'); // Match package.json version
// Check if module is being run directly (not imported)
const isMainModule = process.argv[1] === new URL(import.meta.url).pathname;
if (isMainModule) {
// Parse arguments explicitly from process.argv
program.parse(process.argv);
// Retrieve the options AFTER parsing
const options = program.opts<CliOptions & { force?: boolean }>();
// Run with the parsed options
runGenerator(program.opts<CliOptions & { force?: boolean }>())
.catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});
}
/**
* Main function to run the generator
*/
async function main() {
async function runGenerator(options: CliOptions & { force?: boolean }) {
// Use the parsed options directly
const outputDir = options.output;
const inputSpec = options.input;
@ -275,7 +287,5 @@ async function main() {
}
}
main().catch((error) => {
console.error('Unhandled error:', error);
process.exit(1);
});
// Export the run function for programmatic usage
export { runGenerator as generateMcpServer };