@nestia/sdk
Version:
Nestia SDK and Swagger generator
149 lines • 11.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SdkMcpRouteProgrammer = void 0;
const factory_1 = require("@ttsc/factory");
const IdentifierFactory_1 = require("../../factories/IdentifierFactory");
const FilePrinter_1 = require("./FilePrinter");
/**
* Emits a typed client wrapper for an MCP tool.
*
* Object output types are wrapped in `Primitive<T>` because MCP round-trips
* values through JSON. Void MCP tools return `Promise<void>`.
*
* @author wildduck - https://github.com/wildduck2
*/
var SdkMcpRouteProgrammer;
(function (SdkMcpRouteProgrammer) {
SdkMcpRouteProgrammer.write = (project) => (importer) => (route) => [
FilePrinter_1.FilePrinter.description(writeFunction(project)(importer)(route), writeDescription(route)),
writeNamespace(project)(importer)(route),
];
const writeFunction = (_project) => (importer) => (route) => {
const clientType = factory_1.factory.createTypeReferenceNode(importer.external({
declaration: true,
file: "@modelcontextprotocol/sdk/client/index.js",
type: "element",
name: "Client",
alias: "McpClient",
}));
const callToolResultTypeName = importer.external({
declaration: true,
file: "@modelcontextprotocol/sdk/types.js",
type: "element",
name: "CallToolResult",
alias: "McpCallToolResult",
});
const isVoid = isVoidReturn(route);
if (!isVoid)
importer.external({
declaration: true,
file: "typia",
type: "element",
name: "Primitive",
});
const inputRef = route.input
? factory_1.factory.createTypeReferenceNode(`${route.name}.Input`)
: factory_1.factory.createTypeLiteralNode([]);
const outputRef = factory_1.factory.createTypeReferenceNode(`${route.name}.Output`);
const params = [
factory_1.factory.createParameterDeclaration(undefined, undefined, "client", undefined, clientType, undefined),
];
if (route.input)
params.push(factory_1.factory.createParameterDeclaration(undefined, undefined, "args", undefined, inputRef, undefined));
const toolNameExpr = factory_1.factory.createPropertyAccessExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier(route.name), "METADATA"), "tool");
const callToolParams = [
factory_1.factory.createPropertyAssignment("name", toolNameExpr),
];
if (route.input)
callToolParams.push(factory_1.factory.createPropertyAssignment("arguments", factory_1.factory.createAsExpression(factory_1.factory.createAsExpression(factory_1.factory.createIdentifier("args"), factory_1.factory.createKeywordTypeNode(factory_1.SyntaxKind.AnyKeyword)), factory_1.factory.createTypeReferenceNode("Record", [
factory_1.factory.createKeywordTypeNode(factory_1.SyntaxKind.StringKeyword),
factory_1.factory.createKeywordTypeNode(factory_1.SyntaxKind.AnyKeyword),
]))));
const body = factory_1.factory.createBlock([
factory_1.factory.createVariableStatement(undefined, factory_1.factory.createVariableDeclarationList([
factory_1.factory.createVariableDeclaration("raw", undefined, undefined, factory_1.factory.createAwaitExpression(factory_1.factory.createCallExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("client"), "callTool"), undefined, [
factory_1.factory.createObjectLiteralExpression(callToolParams, true),
]))),
], factory_1.NodeFlags.Const)),
factory_1.factory.createIfStatement(factory_1.factory.createCallExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("Object"), "prototype"), "hasOwnProperty"), "call"), undefined, [
factory_1.factory.createIdentifier("raw"),
factory_1.factory.createStringLiteral("toolResult"),
]), throwToolError(toolNameExpr, '" returned a legacy (pre-2024-11-05) compatibility result')),
factory_1.factory.createVariableStatement(undefined, factory_1.factory.createVariableDeclarationList([
factory_1.factory.createVariableDeclaration("result", undefined, factory_1.factory.createTypeReferenceNode(callToolResultTypeName), factory_1.factory.createAsExpression(factory_1.factory.createIdentifier("raw"), factory_1.factory.createTypeReferenceNode(callToolResultTypeName))),
], factory_1.NodeFlags.Const)),
factory_1.factory.createIfStatement(factory_1.factory.createBinaryExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("result"), "isError"), factory_1.factory.createToken(factory_1.SyntaxKind.EqualsEqualsEqualsToken), factory_1.factory.createTrue()), throwToolError(toolNameExpr, '" returned isError')),
...(isVoid
? [factory_1.factory.createReturnStatement()]
: [
factory_1.factory.createVariableStatement(undefined, factory_1.factory.createVariableDeclarationList([
factory_1.factory.createVariableDeclaration("first", undefined, undefined, factory_1.factory.createCallExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("result"), "content"), "find"), undefined, [
factory_1.factory.createArrowFunction(undefined, undefined, [IdentifierFactory_1.IdentifierFactory.parameter("entry")], undefined, undefined, factory_1.factory.createBinaryExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("entry"), "type"), factory_1.factory.createToken(factory_1.SyntaxKind.EqualsEqualsEqualsToken), factory_1.factory.createStringLiteral("text"))),
])),
], factory_1.NodeFlags.Const)),
factory_1.factory.createIfStatement(factory_1.factory.createBinaryExpression(factory_1.factory.createIdentifier("first"), factory_1.factory.createToken(factory_1.SyntaxKind.EqualsEqualsEqualsToken), factory_1.factory.createIdentifier("undefined")), throwToolError(toolNameExpr, '" returned no text content')),
factory_1.factory.createIfStatement(factory_1.factory.createBinaryExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("first"), "type"), factory_1.factory.createToken(factory_1.SyntaxKind.EqualsEqualsEqualsToken), factory_1.factory.createStringLiteral("text")), factory_1.factory.createReturnStatement(factory_1.factory.createAsExpression(factory_1.factory.createCallExpression(factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("JSON"), "parse"), undefined, [
factory_1.factory.createPropertyAccessExpression(factory_1.factory.createIdentifier("first"), "text"),
]), outputRef))),
throwToolError(toolNameExpr, '" returned no text content'),
]),
], true);
return factory_1.factory.createFunctionDeclaration([
factory_1.factory.createModifier(factory_1.SyntaxKind.ExportKeyword),
factory_1.factory.createModifier(factory_1.SyntaxKind.AsyncKeyword),
], undefined, route.name, undefined, params, factory_1.factory.createTypeReferenceNode("Promise", [outputRef]), body);
};
const writeNamespace = (_project) => (importer) => (route) => {
const statements = [];
if (route.input)
statements.push(factory_1.factory.createTypeAliasDeclaration([factory_1.factory.createModifier(factory_1.SyntaxKind.ExportKeyword)], "Input", undefined, factory_1.factory.createTypeReferenceNode(route.input.type.name)));
const outputType = !isVoidReturn(route) && route.returnType !== null
? factory_1.factory.createTypeReferenceNode(importer.external({
declaration: true,
file: "typia",
type: "element",
name: "Primitive",
}), [
factory_1.factory.createTypeReferenceNode(unwrapPromise(route.returnType.name)),
])
: factory_1.factory.createKeywordTypeNode(factory_1.SyntaxKind.VoidKeyword);
statements.push(factory_1.factory.createTypeAliasDeclaration([factory_1.factory.createModifier(factory_1.SyntaxKind.ExportKeyword)], "Output", undefined, outputType));
statements.push(factory_1.factory.createVariableStatement([factory_1.factory.createModifier(factory_1.SyntaxKind.ExportKeyword)], factory_1.factory.createVariableDeclarationList([
factory_1.factory.createVariableDeclaration("METADATA", undefined, undefined, factory_1.factory.createAsExpression(factory_1.factory.createObjectLiteralExpression([
factory_1.factory.createPropertyAssignment("protocol", factory_1.factory.createStringLiteral("mcp")),
factory_1.factory.createPropertyAssignment("tool", factory_1.factory.createStringLiteral(route.toolName)),
factory_1.factory.createPropertyAssignment("title", route.title === null
? factory_1.factory.createNull()
: factory_1.factory.createStringLiteral(route.title)),
factory_1.factory.createPropertyAssignment("description", route.toolDescription === null
? factory_1.factory.createNull()
: factory_1.factory.createStringLiteral(route.toolDescription)),
], true), factory_1.factory.createTypeReferenceNode("const"))),
], factory_1.NodeFlags.Const)));
return factory_1.factory.createModuleDeclaration([factory_1.factory.createModifier(factory_1.SyntaxKind.ExportKeyword)], factory_1.factory.createIdentifier(route.name), factory_1.factory.createModuleBlock(statements), factory_1.NodeFlags.Namespace);
};
const writeDescription = (route) => {
const lines = [];
if (route.toolDescription)
lines.push(...route.toolDescription.split("\n"));
else if (route.description)
lines.push(...route.description.split("\n"));
if (lines.length)
lines.push("");
lines.push(`@controller ${route.controller.class.name}.${route.function.name || route.name}`, `@tool ${route.toolName}`, `@accessor ${["api", "functional", ...route.accessor].join(".")}`, `@protocol mcp`, `@nestia Generated by Nestia - https://github.com/samchon/nestia`);
return lines.join("\n");
};
const unwrapPromise = (name) => {
const m = /^Promise<(.+)>$/.exec(name);
return m ? m[1] : name;
};
const isVoidReturn = (route) => route.returnType === null ||
isVoidName(unwrapPromise(route.returnType.name));
const isVoidName = (name) => name === "void" || name === "undefined";
const throwToolError = (toolNameExpr, suffix) => factory_1.factory.createThrowStatement(factory_1.factory.createNewExpression(factory_1.factory.createIdentifier("Error"), undefined, [
factory_1.factory.createTemplateExpression(factory_1.factory.createTemplateHead('MCP tool "'), [
factory_1.factory.createTemplateSpan(toolNameExpr, factory_1.factory.createTemplateTail(suffix)),
]),
]));
})(SdkMcpRouteProgrammer || (exports.SdkMcpRouteProgrammer = SdkMcpRouteProgrammer = {}));
//# sourceMappingURL=SdkMcpRouteProgrammer.js.map