#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import {
getToolDefinitions,
openapiLoad,
openapiListEndpoints,
openapiGetEndpoint,
openapiListSpecs,
} from "./tools.js";
import type { LoadInput, ListEndpointsInput, GetEndpointInput } from "./types.js";
// Parse command line arguments for pre-configured specs
// Format: --spec alias=url or -s alias=url
function parseArgs(): Array<{ alias: string; source: string }> {
const specs: Array<{ alias: string; source: string }> = [];
const args = process.argv.slice(2);
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "--spec" || arg === "-s") {
const value = args[++i];
if (value) {
const eqIndex = value.indexOf("=");
if (eqIndex > 0) {
specs.push({
alias: value.substring(0, eqIndex),
source: value.substring(eqIndex + 1),
});
}
}
} else if (arg.startsWith("--spec=")) {
const value = arg.substring(7);
const eqIndex = value.indexOf("=");
if (eqIndex > 0) {
specs.push({
alias: value.substring(0, eqIndex),
source: value.substring(eqIndex + 1),
});
}
}
}
return specs;
}
// Create server instance
const server = new Server(
{
name: "openapi-mcp",
version: "1.2.0",
},
{
capabilities: {
tools: {},
},
}
);
// Register tool list handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: getToolDefinitions(),
};
});
// Register tool call handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
let result: unknown;
switch (name) {
case "openapi_list_specs":
result = openapiListSpecs();
break;
case "openapi_load":
result = await openapiLoad(args as unknown as LoadInput);
break;
case "openapi_list_endpoints":
result = openapiListEndpoints(args as unknown as ListEndpointsInput);
break;
case "openapi_get_endpoint":
result = openapiGetEndpoint(args as unknown as GetEndpointInput);
break;
default:
return {
content: [
{
type: "text",
text: JSON.stringify({
error: true,
code: "VALIDATION_ERROR",
message: `Unknown tool: ${name}`,
}),
},
],
};
}
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: true,
code: "VALIDATION_ERROR",
message: `Tool execution failed: ${message}`,
}),
},
],
};
}
});
// Pre-load specs from command line arguments
async function preloadSpecs() {
const specs = parseArgs();
for (const spec of specs) {
console.error(`Pre-loading spec: ${spec.alias} from ${spec.source}`);
const result = await openapiLoad(spec);
if ("error" in result && result.error) {
console.error(`Failed to load ${spec.alias}: ${result.message}`);
} else {
console.error(`Loaded ${spec.alias}: ${(result as { title: string }).title}`);
}
}
}
// Start the server
async function main() {
await preloadSpecs();
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("OpenAPI MCP server running on stdio");
}
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});