index.js•9.26 kB
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, ErrorCode, McpError, } from '@modelcontextprotocol/sdk/types.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { Swagger2InterfaceOutput } from '@swiftcode/api';
import { Template2ListOutput, createTemplate } from '@swiftcode/list';
import path from 'node:path';
const TOOLS = [
{
name: 'generate_api_client',
description: 'Generate TypeScript API client from Swagger/OpenAPI specification',
inputSchema: {
type: 'object',
properties: {
source: {
type: 'string',
description: "使用本地文件路径或 swagger URL 生成 API 和类型文件 / URL or JSON file path. Examples: For full path: '//user/file.json', For file only: 'file.json' returns 'file json'. URLs should start with '/' for proper routing.",
},
dir: {
type: 'string',
description: 'workspace dir',
},
},
required: ['source', 'dir'],
},
},
{
name: 'generate_sfc_template_client',
description: '下载转换 sfc / vue 列表的模板文件当前目录下 / Download the transform sfc template files',
inputSchema: {
type: 'object',
properties: {
dir: {
type: 'string',
description: 'workspace dir',
},
},
required: ['dir'],
},
},
{
name: 'generate_sfc_client',
description: "使用 sfc 模板生成 vue 列表组件 / Generate vue sfc component page.Examples: For file only returns vue sfc pages, default file name: 'template.js'",
inputSchema: {
type: 'object',
properties: {
source: {
type: 'string',
description: 'sfc 模板文件路径 / sfc template file path',
},
dir: {
type: 'string',
description: 'workspace dir',
},
},
required: ['source', 'dir'],
},
},
];
const PROMPTS = [
{
name: 'generate_api_client',
description: '使用 swiftcode 生成 typescript api 接口文件和类型',
arguments: [
{
name: 'api',
description: '',
required: false,
},
],
},
{
name: 'generate_sfc_template_client',
description: '下载 swiftcode 生成 sfc 的模板文件',
arguments: [
{
name: 'template',
description: '',
required: false,
},
],
},
{
name: 'generate_sfc_client',
description: '使用 swiftcode sfc 模板生成 vue 列表组件',
arguments: [
{
name: 'sfc',
description: '',
required: false,
},
],
},
];
class SwiftcodeMCP {
server;
constructor() {
this.server = new Server({
name: 'swiftcode-mcp',
version: '1.0.0',
}, {
capabilities: {
tools: {
listChanged: true,
},
prompts: {
listChanged: true,
},
},
});
this.setupHandlers();
this.setupErrorHandling();
}
setupErrorHandling() {
this.server.onerror = error => {
console.error('[MCP Error]', error);
};
process.on('SIGINT', async () => {
await this.stop();
process.exit(0);
});
}
setupHandlers() {
// List available tools
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: TOOLS,
}));
// Handle tool calls
this.server.setRequestHandler(CallToolRequestSchema, async (request) => this.handleToolCall(request.params.name, request.params.arguments ?? {}));
// List available prompts
this.server.setRequestHandler(ListPromptsRequestSchema, async () => ({
prompts: PROMPTS,
}));
}
/**
* Handles tool call requests
*/
async handleToolCall(name, args) {
switch (name) {
case 'generate_api_client': {
const { source, dir } = args;
// 判断 source 是否是文件路径
const isFilePath = source.startsWith('/');
const filePath = isFilePath ? `/${source}` : source;
try {
await Swagger2InterfaceOutput({
source: filePath,
isDev: false,
dir: path.join(dir, 'apis'),
});
return {
content: [
{
type: 'text',
text: 'API client generated successfully. Please check the apis directory. output file list',
},
],
};
}
catch (error) {
console.error('Error while generating API client:', error);
throw new McpError(ErrorCode.InternalError, 'Failed to generate API client', {
code: ErrorCode.InternalError,
message: 'Failed to generate API client',
});
}
}
case 'generate_sfc_template_client': {
const { dir } = args;
// 直接使用导入的模板内容,写入到目标目录
try {
createTemplate('template.js', '', dir);
return {
content: [
{
type: 'text',
text: `SFC template file created successfully at ${path.join(dir, 'template.js')}`,
},
],
};
}
catch (error) {
console.error('Error while generating SFC template files:', error);
throw new McpError(ErrorCode.InternalError, 'Failed to generate SFC template files', {
code: ErrorCode.InternalError,
message: `Failed to generate SFC template files: ${error instanceof Error ? error.message : 'Unknown error'}`,
});
}
}
case 'generate_sfc_client': {
const { source, dir } = args;
try {
// 判断 source 是否是文件路径
const isFilePath = source.startsWith('/');
const filePath = isFilePath ? `/${source}` : source;
const dirPath = path.join(dir, '.lists');
await Template2ListOutput({
source: filePath,
dir: dirPath
});
return {
content: [
{
type: 'text',
text: 'SFC files generated successfully.',
},
],
};
}
catch (error) {
console.error('Error while generating SFC files:', error);
throw new McpError(ErrorCode.InternalError, 'Failed to generate SFC files', {
code: ErrorCode.InternalError,
message: 'Failed to generate SFC files',
});
}
}
default: {
throw new McpError(ErrorCode.MethodNotFound, `Tool ${name} not found`, {
code: ErrorCode.MethodNotFound,
message: `Tool ${name} not found`,
});
}
}
}
/**
* Starts the server
*/
async start() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
}
/**
* Stops the server
*/
async stop() {
try {
await this.server.close();
}
catch (error) {
console.error('Error while stopping server:', error);
}
}
}
const server = new SwiftcodeMCP();
// Main execution
async function main() {
try {
console.log('Swiftcode MCP Server starting...');
await server.start();
}
catch (error) {
console.error('Server failed to start:', error);
process.exit(1);
}
}
main().catch(error => {
console.error('Fatal server error:', error);
process.exit(1);
});
process.on('SIGINT', async () => {
await server.stop();
process.exit(0);
});