Files
jenna-mcp/xxx/tool_convert.ts
bipproduction e0fdb88c32 tambahan
2025-10-28 14:05:53 +08:00

101 lines
3.3 KiB
TypeScript

import _ from "lodash";
import { v4 as uuidv4 } from "uuid";
interface McpTool {
name: string;
description: string;
inputSchema: any;
"x-props": {
method: string;
path: string;
operationId?: string;
tag?: string;
deprecated?: boolean;
summary?: string;
};
}
/**
* Convert OpenAPI 3.x JSON spec into MCP-compatible tool definitions (without run()).
* Each tool corresponds to an endpoint, with metadata stored under `x-props`.
*/
export function convertOpenApiToMcpTools(openApiJson: any, baseUrl: string = ""): McpTool[] {
const tools: McpTool[] = [];
const paths = openApiJson.paths || {};
for (const [path, methods] of Object.entries(paths)) {
// ✅ skip semua path internal MCP
if (path.startsWith("/mcp")) continue;
for (const [method, operation] of Object.entries<any>(methods as any)) {
const rawName = _.snakeCase(operation.operationId || `${method}_${path}`) || "unnamed_tool";
const name = cleanToolName(rawName);
const summary = operation.summary || `Execute ${method.toUpperCase()} ${path}`;
const description =
operation.description ||
operation.summary ||
`Execute ${method.toUpperCase()} ${path}`;
const schema =
operation.requestBody?.content?.["application/json"]?.schema || {
type: "object",
properties: {},
additionalProperties: true,
};
const tool: McpTool = {
name,
description,
"x-props": {
method: method.toUpperCase(),
path,
operationId: operation.operationId,
tag: Array.isArray(operation.tags) ? operation.tags[0] : undefined,
deprecated: operation.deprecated || false,
summary: operation.summary, // ✅ tambahkan summary ke metadata
},
inputSchema: {
...schema,
additionalProperties: true,
$schema: "http://json-schema.org/draft-07/schema#",
},
};
tools.push(tool);
}
}
return tools;
}
/**
* Bersihkan nama agar valid untuk digunakan sebagai tool name
* - hapus karakter spesial
* - ubah slash jadi underscore
* - hilangkan prefix umum (get_, post_, api_, dll)
* - rapikan underscore berganda
*/
function cleanToolName(name: string): string {
return name
.replace(/[{}]/g, "")
.replace(/[^a-zA-Z0-9_]/g, "_")
.replace(/_+/g, "_")
.replace(/^_|_$/g, "")
.replace(/^(get|post|put|delete|patch|api)_/i, "")
.replace(/^(get_|post_|put_|delete_|patch_|api_)+/gi, "")
.replace(/(^_|_$)/g, "");
}
// === Contoh Pemakaian ===
// import openApiJson from "./openapi.json";
// const tools = convertOpenApiToMcpTools(openApiJson, "https://api.wibudev.com");
// console.log(JSON.stringify(tools, null, 2));
if (import.meta.main) {
const data = await fetch("http://localhost:3000/docs/json");
const openApiJson = await data.json();
const tools = convertOpenApiToMcpTools(openApiJson, "http://localhost:3000");
Bun.write("./tools.json", JSON.stringify(tools, null, 2));
}