MCP Create Server
by tesla0225
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { fileURLToPath } from "url";
import { dirname } from "path";
import {
CallToolRequest,
CallToolRequestSchema,
ListToolsRequestSchema,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import { spawn, ChildProcess } from "child_process";
import * as fs from "fs/promises";
import * as path from "path";
import { v4 as uuidv4 } from "uuid";
import * as os from "os";
// Type definitions for tool arguments
interface CreateServerArgs {
code: string;
language: "typescript" | "javascript" | "python";
}
interface CreateServerFromTemplateArgs {
language: "typescript" | "python";
code?: string;
dependencies?: Record<string, string>; // 例: { "axios": "^1.0.0" }
}
interface ExecuteToolArgs {
serverId: string;
toolName: string;
args: Record<string, any>;
}
interface GetServerToolsArgs {
serverId: string;
}
interface UpdateServerArgs {
serverId: string;
code: string;
}
interface DeleteServerArgs {
serverId: string;
}
interface ConnectedServer {
process: ChildProcess;
client: Client;
transport: StdioClientTransport;
language: string;
filePath: string;
}
// Get current file path and directory in ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// コマンドの絶対パスを取得する関数を追加
async function getCommandPath(command: string): Promise<string> {
try {
// whichコマンドの代わりにJavaScriptで絶対パスを探す
const possiblePaths = [
"/usr/local/bin",
"/usr/bin",
"/bin",
"/usr/local/sbin",
"/usr/sbin",
"/sbin",
];
for (const dir of possiblePaths) {
const fullPath = path.join(dir, command);
try {
await fs.access(fullPath, fs.constants.X_OK);
console.error(`Found command ${command} at ${fullPath}`);
return fullPath;
} catch {
// このパスにコマンドが存在しない場合は次を試す
}
}
// コマンドが見つからない場合は元のコマンド名を返す
console.error(
`Command ${command} not found in standard paths, returning as is`
);
return command;
} catch (error) {
console.error(`Error resolving path for ${command}:`, error);
return command;
}
}
// Server manager class
class ServerManager {
private servers: Map<string, ConnectedServer> = new Map();
private templatesDir: string = path.join(__dirname, "templates");
private serversDir: string = path.join(os.tmpdir(), "mcp-create-servers");
constructor() {
// Ensure servers directory exists
this.initDirectories();
}
private async initDirectories() {
try {
await fs.mkdir(this.serversDir, { recursive: true });
// 権限を明示的に設定(Docker内でも動作するように)
await fs.chmod(this.serversDir, 0o777);
console.error(`Created servers directory: ${this.serversDir}`);
} catch (error) {
console.error(`Error creating servers directory: ${error}`);
}
}
// Create a new server from code
async createServer(
code: string,
language: string,
dependencies?: Record<string, string>
): Promise<string> {
const serverId = uuidv4();
const serverDir = path.join(this.serversDir, serverId);
try {
// Create server directory
await fs.mkdir(serverDir, { recursive: true });
await fs.chmod(serverDir, 0o777); // 権限を追加
// 依存関係がある場合はインストール(シンボリックリンクは作成しない)
if (dependencies && Object.keys(dependencies).length > 0) {
await this.installDependencies(serverDir, dependencies, language);
} else {
// 依存関係がない場合のみシンボリックリンクを作成
try {
await fs.symlink(
"/app/node_modules",
path.join(serverDir, "node_modules")
);
console.error(`Created symlink to node_modules in ${serverDir}`);
} catch (error) {
console.error(`Error creating symlink: ${error}`);
// エラーがあっても続行する
}
}
// Write server code to file
let filePath: string;
let command: string;
let args: string[] = [];
// 共通の環境変数設定
const appNodeModules = path.resolve("/app/node_modules");
const commonEnv = {
...process.env,
PATH: process.env.PATH || "/usr/local/bin:/usr/bin:/bin",
NODE_PATH: appNodeModules,
};
console.error(`Current PATH: ${process.env.PATH}`);
console.error(`Current NODE_PATH: ${process.env.NODE_PATH}`);
switch (language) {
case "typescript":
filePath = path.join(serverDir, "index.ts");
const jsFilePath = path.join(serverDir, "index.js");
const tsConfigPath = path.join(__dirname, "tsconfig.json");
await fs.writeFile(filePath, code);
// 絶対パスを取得して出力
command = await getCommandPath("npx");
console.error(`Using command path for npx: ${command}`);
// TypeScriptをコンパイルする方法に変更
await new Promise<void>((resolve, reject) => {
const tscCommand = "npx";
const tscArgs = [
"tsc",
"--allowJs",
filePath,
"--outDir",
serverDir,
"--target",
"ES2020",
"--module",
"NodeNext",
"--moduleResolution",
"NodeNext",
"--esModuleInterop",
"--skipLibCheck",
"--resolveJsonModule",
];
console.error(
`Compiling TypeScript: ${tscCommand} ${tscArgs.join(" ")}`
);
const compileProcess = spawn(tscCommand, tscArgs, {
stdio: ["ignore", "pipe", "pipe"],
shell: true,
env: commonEnv,
cwd: "/app", // アプリケーションのルートディレクトリを指定
});
compileProcess.stdout.on("data", (data) => {
console.error(`TSC stdout: ${data}`);
});
compileProcess.stderr.on("data", (data) => {
console.error(`TSC stderr: ${data}`);
});
compileProcess.on("exit", (code) => {
if (code === 0) {
console.error(`TypeScript compilation successful`);
resolve();
} else {
console.error(
`TypeScript compilation failed with code ${code}`
);
reject(
new Error(`TypeScript compilation failed with code ${code}`)
);
}
});
});
// コンパイルされたJavaScriptを実行
command = await getCommandPath("node");
args = [jsFilePath];
break;
case "javascript":
filePath = path.join(serverDir, "index.js");
await fs.writeFile(filePath, code);
command = await getCommandPath("node");
args = [filePath];
break;
case "python":
filePath = path.join(serverDir, "server.py");
await fs.writeFile(filePath, code);
command = await getCommandPath("python");
args = [filePath];
break;
default:
throw new Error(`Unsupported language: ${language}`);
}
console.error(`Spawning process: ${command} ${args.join(" ")}`);
// サーバープロセスを起動(パイプに変更)
const childProcess = spawn(command, args, {
stdio: ["pipe", "pipe", "pipe"], // inheritではなくpipeを使用
shell: true,
env: commonEnv,
cwd: process.cwd(),
});
// 標準エラー出力のログ取得
childProcess.stderr.on("data", (data) => {
console.error(`Child process stderr: ${data}`);
});
// 標準出力のログ取得
childProcess.stdout.on("data", (data) => {
console.error(`Child process stdout: ${data}`);
});
// Create MCP client to communicate with the server
const transport = new StdioClientTransport({
command,
args,
env: commonEnv, // 同じ環境変数を使用
});
const client = new Client(
{
name: "mcp-create-client",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
try {
await client.connect(transport);
console.error(`Connected to server ${serverId}`);
} catch (error) {
console.error(`Error connecting to server ${serverId}:`, error);
childProcess.kill();
throw error;
}
// Store server info
this.servers.set(serverId, {
process: childProcess,
client,
transport,
language,
filePath,
});
// Handle process exit
childProcess.on("exit", (code) => {
console.error(`Server ${serverId} exited with code ${code}`);
this.servers.delete(serverId);
});
return serverId;
} catch (error) {
// Clean up on error
console.error(`Error creating server:`, error);
try {
await fs.rm(serverDir, { recursive: true, force: true });
} catch (cleanupError) {
console.error(`Error cleaning up server directory: ${cleanupError}`);
}
throw error;
}
}
// Create a server from template
// async createServerFromTemplate(
// language: string
// ): Promise<{ serverId: string; message: string }> {
// // Template code for different languages
// let templateCode: string;
// switch (language) {
// case "typescript":
// templateCode = `
// import { Server } from "@modelcontextprotocol/sdk/server/index.js";
// import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// import {
// CallToolRequestSchema,
// ListToolsRequestSchema
// } from "@modelcontextprotocol/sdk/types.js";
// const server = new Server({
// name: "dynamic-test-server",
// version: "1.0.0"
// }, {
// capabilities: {
// tools: {}
// }
// });
// // Server implementation - 正しいスキーマ型を使用
// server.setRequestHandler(ListToolsRequestSchema, async () => {
// return {
// tools: [{
// name: "echo",
// description: "Echo back a message",
// inputSchema: {
// type: "object",
// properties: {
// message: { type: "string" }
// },
// required: ["message"]
// }
// }]
// };
// });
// server.setRequestHandler(CallToolRequestSchema, async (request) => {
// if (request.params.name === "echo") {
// return {
// content: [
// {
// type: "text",
// text: \`Echo: \${request.params.arguments.message}\`
// }
// ]
// };
// }
// throw new Error("Tool not found");
// });
// // Server startup
// const transport = new StdioServerTransport();
// server.connect(transport);
// `;
// break;
// case "python":
// templateCode = `
// import asyncio
// from mcp.server import Server
// from mcp.server.stdio import stdio_server
// app = Server("dynamic-test-server")
// @app.list_tools()
// async def list_tools():
// return [
// {
// "name": "echo",
// "description": "Echo back a message",
// "inputSchema": {
// "type": "object",
// "properties": {
// "message": {"type": "string"}
// },
// "required": ["message"]
// }
// }
// ]
// @app.call_tool()
// async def call_tool(name, arguments):
// if name == "echo":
// return [{"type": "text", "text": f"Echo: {arguments.get('message')}"}]
// raise ValueError(f"Tool not found: {name}")
// async def main():
// async with stdio_server() as streams:
// await app.run(
// streams[0],
// streams[1],
// app.create_initialization_options()
// )
// if __name__ == "__main__":
// asyncio.run(main())
// `;
// break;
// default:
// throw new Error(`Unsupported template language: ${language}`);
// }
// const serverId = await this.createServer(templateCode, language);
// return {
// serverId,
// message: `Created server from ${language} template`,
// };
// }
// 以下は他のメソッドも同様に修正することになりますが、
// 主要な変更点は上記の通りです
// 依存関係をインストールするメソッド
async installDependencies(
serverDir: string,
dependencies: Record<string, string>,
language: string
): Promise<void> {
console.error(`Installing dependencies for ${language} in ${serverDir}`);
switch (language) {
case "typescript":
case "javascript":
await this.installNodeDependencies(serverDir, dependencies);
break;
case "python":
await this.installPythonDependencies(serverDir, dependencies);
break;
default:
throw new Error(`Unsupported language for dependencies: ${language}`);
}
}
// Node.js (TypeScript/JavaScript) 用の依存関係インストール
private async installNodeDependencies(
serverDir: string,
dependencies: Record<string, string>
): Promise<void> {
try {
// 既存のpackage.jsonを読み込む(存在する場合)
let packageJson: any = {
name: "mcp-dynamic-server",
version: "1.0.0",
type: "module",
dependencies: {}
};
// アプリケーションのpackage.jsonを読み込む
try {
const appPackageJsonPath = path.join("/app", "package.json");
const appPackageJsonContent = await fs.readFile(appPackageJsonPath, 'utf-8');
const appPackageJson = JSON.parse(appPackageJsonContent);
// 必要な依存関係をマージ
if (appPackageJson.dependencies) {
// 特に@modelcontextprotocol関連の依存関係をコピー
Object.entries(appPackageJson.dependencies).forEach(([pkg, ver]) => {
if (pkg.startsWith('@modelcontextprotocol') || pkg === 'mcp') {
packageJson.dependencies[pkg] = ver;
}
});
}
console.error(`Merged dependencies from app package.json`);
} catch (error) {
console.error(`Error reading app package.json:`, error);
// エラーがあっても続行
}
// ユーザー指定の依存関係をマージ
Object.entries(dependencies).forEach(([pkg, ver]) => {
packageJson.dependencies[pkg] = ver;
});
// package.jsonを書き込む
await fs.writeFile(
path.join(serverDir, "package.json"),
JSON.stringify(packageJson, null, 2)
);
// npm install の実行
const npmCommand = await getCommandPath("npm");
await new Promise<void>((resolve, reject) => {
const installProcess = spawn(
npmCommand,
["install"],
{
stdio: ["ignore", "pipe", "pipe"],
shell: true,
env: { ...process.env },
cwd: serverDir
}
);
installProcess.stdout.on("data", (data) => {
console.error(`NPM stdout: ${data}`);
});
installProcess.stderr.on("data", (data) => {
console.error(`NPM stderr: ${data}`);
});
installProcess.on("exit", (code) => {
if (code === 0) {
console.error(`NPM install successful`);
resolve();
} else {
console.error(`NPM install failed with code ${code}`);
reject(new Error(`NPM install failed with code ${code}`));
}
});
});
} catch (error) {
console.error(`Error installing Node.js dependencies:`, error);
throw error;
}
}
// Python 用の依存関係インストール
private async installPythonDependencies(
serverDir: string,
dependencies: Record<string, string>
): Promise<void> {
try {
// requirements.txt の作成
const requirementsContent = Object.entries(dependencies)
.map(([pkg, ver]) => `${pkg}${ver}`)
.join("\n");
await fs.writeFile(
path.join(serverDir, "requirements.txt"),
requirementsContent
);
// pip install の実行
const pipCommand = await getCommandPath("pip");
await new Promise<void>((resolve, reject) => {
const installProcess = spawn(
pipCommand,
["install", "-r", "requirements.txt"],
{
stdio: ["ignore", "pipe", "pipe"],
shell: true,
env: { ...process.env },
cwd: serverDir
}
);
installProcess.stdout.on("data", (data) => {
console.error(`PIP stdout: ${data}`);
});
installProcess.stderr.on("data", (data) => {
console.error(`PIP stderr: ${data}`);
});
installProcess.on("exit", (code) => {
if (code === 0) {
console.error(`PIP install successful`);
resolve();
} else {
console.error(`PIP install failed with code ${code}`);
reject(new Error(`PIP install failed with code ${code}`));
}
});
});
} catch (error) {
console.error(`Error installing Python dependencies:`, error);
throw error;
}
}
// Execute a tool on a server
async executeToolOnServer(
serverId: string,
toolName: string,
args: Record<string, any>
): Promise<any> {
const server = this.servers.get(serverId);
if (!server) {
throw new Error(`Server ${serverId} not found`);
}
try {
// Call the tool on the server using the MCP client
const result = await server.client.callTool({
name: toolName,
arguments: args,
});
return result;
} catch (error) {
console.error(`Error executing tool on server ${serverId}:`, error);
throw error;
}
}
// Get tools from a server
async getServerTools(serverId: string): Promise<any> {
const server = this.servers.get(serverId);
if (!server) {
throw new Error(`Server ${serverId} not found`);
}
try {
// Get tools from the server using the MCP client
const tools = await server.client.listTools();
return tools;
} catch (error) {
console.error(`Error getting tools from server ${serverId}:`, error);
throw error;
}
}
// Update a server
async updateServer(
serverId: string,
code: string
): Promise<{ success: boolean; message: string }> {
const server = this.servers.get(serverId);
if (!server) {
throw new Error(`Server ${serverId} not found`);
}
try {
// Update server code
await fs.writeFile(server.filePath, code);
// Close the client connection
await server.transport.close();
// Kill the server process
server.process.kill();
// Wait for process to exit
await new Promise<void>((resolve) => {
server.process.on("exit", () => {
resolve();
});
});
// Remove the server from the map
this.servers.delete(serverId);
// Create new server with updated code
const newServerId = await this.createServer(code, server.language);
return {
success: true,
message: `Server ${serverId} updated and restarted as ${newServerId}`,
};
} catch (error) {
console.error(`Error updating server ${serverId}:`, error);
throw error;
}
}
// Delete a server
async deleteServer(
serverId: string
): Promise<{ success: boolean; message: string }> {
const server = this.servers.get(serverId);
if (!server) {
throw new Error(`Server ${serverId} not found`);
}
try {
// Close the client connection
await server.transport.close();
// Kill server process
server.process.kill();
// Remove server from map
this.servers.delete(serverId);
// Delete server directory
const serverDir = path.dirname(server.filePath);
await fs.rm(serverDir, { recursive: true, force: true });
return {
success: true,
message: `Server ${serverId} deleted`,
};
} catch (error) {
console.error(`Error deleting server ${serverId}:`, error);
throw error;
}
}
// List all servers
listServers(): string[] {
return Array.from(this.servers.keys());
}
// Close all servers
async closeAll(): Promise<void> {
for (const [serverId, server] of this.servers.entries()) {
try {
await server.transport.close();
server.process.kill();
console.error(`Closed server ${serverId}`);
} catch (error) {
console.error(`Error closing server ${serverId}:`, error);
}
}
this.servers.clear();
}
}
// Tool definitions
// const createServerTool: Tool = {
// name: "create-server",
// description: "Create a new MCP server from code",
// inputSchema: {
// type: "object",
// properties: {
// code: {
// type: "string",
// description: "The server code",
// },
// language: {
// type: "string",
// enum: ["typescript", "javascript", "python"],
// description: "The programming language of the server code",
// },
// },
// required: ["code", "language"],
// },
// };
const createServerFromTemplateTool: Tool = {
name: "create-server-from-template",
description: `Create a new MCP server from a template.
以下のテンプレートコードをベースに、ユーザーの要求に合わせたサーバーを実装してください。
言語に応じて適切なテンプレートを選択し、必要に応じて機能を追加・変更してください。
TypeScriptテンプレート:
\`\`\`typescript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server({
name: "dynamic-test-server",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// ここでツールを実装してください
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "echo",
description: "Echo back a message",
inputSchema: {
type: "object",
properties: {
message: { type: "string" }
},
required: ["message"]
}
}]
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "echo") {
// TypeScriptの型を適切に扱うため、型アサーションを使用
const message = request.params.arguments.message as string;
// または any を使う: const message: any = request.params.arguments.message;
return {
content: [
{
type: "text",
text: \`Echo: \${message}\`
}
]
};
}
throw new Error("Tool not found");
});
// Server startup
const transport = new StdioServerTransport();
server.connect(transport);
\`\`\`
Pythonテンプレート:
\`\`\`python
import asyncio
from mcp.server import Server
from mcp.server.stdio import stdio_server
app = Server("dynamic-test-server")
@app.list_tools()
async def list_tools():
return [
{
"name": "echo",
"description": "Echo back a message",
"inputSchema": {
"type": "object",
"properties": {
"message": {"type": "string"}
},
"required": ["message"]
}
}
]
@app.call_tool()
async def call_tool(name, arguments):
if name == "echo":
return [{"type": "text", "text": f"Echo: {arguments.get('message')}"}]
raise ValueError(f"Tool not found: {name}")
async def main():
async with stdio_server() as streams:
await app.run(
streams[0],
streams[1],
app.create_initialization_options()
)
if __name__ == "__main__":
asyncio.run(main())
\`\`\`
注意事項:
- TypeScript実装時は、引数の型を適切に扱うために型アサーション(as string)を使用するか、
明示的に型を宣言してください(例:const value: string = request.params.arguments.someValue)。
- 複雑な型を扱う場合は、interface や type を定義して型安全性を確保することをお勧めします。
ユーザーの要求に応じて上記のテンプレートを参考にカスタマイズしてください。その際、基本的な構造を維持しつつ、ツール名や機能を変更できます。`,
inputSchema: {
type: "object",
properties: {
language: {
type: "string",
enum: ["typescript", "python"],
description: "The programming language for the template",
},
code: {
type: "string",
description:
"カスタマイズしたサーバーコード。テンプレートを元に変更したコードを入力してください。省略した場合はデフォルトのテンプレートが使用されます。",
},
dependencies: {
type: "object",
description: "使用するライブラリとそのバージョン(例: { \"axios\": \"^1.0.0\" })",
},
},
required: ["language"],
},
};
const executeToolTool: Tool = {
name: "execute-tool",
description: "Execute a tool on a server",
inputSchema: {
type: "object",
properties: {
serverId: {
type: "string",
description: "The ID of the server",
},
toolName: {
type: "string",
description: "The name of the tool to execute",
},
args: {
type: "object",
description: "The arguments to pass to the tool",
},
},
required: ["serverId", "toolName"],
},
};
const getServerToolsTool: Tool = {
name: "get-server-tools",
description: "Get the tools available on a server",
inputSchema: {
type: "object",
properties: {
serverId: {
type: "string",
description: "The ID of the server",
},
},
required: ["serverId"],
},
};
// const updateServerTool: Tool = {
// name: "update-server",
// description: `Update a server's code.まずupdate前のコードを読み、その内容からupdateの差分を考えてください。
// その差分をもとに、update後のコードを作成してください。`,
// inputSchema: {
// type: "object",
// properties: {
// serverId: {
// type: "string",
// description: "The ID of the server",
// },
// code: {
// type: "string",
// description: `The new server code.
// `,
// },
// },
// required: ["serverId", "code"],
// },
// };
const deleteServerTool: Tool = {
name: "delete-server",
description: "Delete a server",
inputSchema: {
type: "object",
properties: {
serverId: {
type: "string",
description: "The ID of the server",
},
},
required: ["serverId"],
},
};
const listServersTool: Tool = {
name: "list-servers",
description: "List all running servers",
inputSchema: {
type: "object",
properties: {},
},
};
async function main() {
try {
console.error("Starting MCP Create Server...");
const server = new Server(
{
name: "MCP Create Server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Create server manager
const serverManager = new ServerManager();
// Register tool handlers
server.setRequestHandler(ListToolsRequestSchema, async () => {
console.error("Received ListToolsRequest");
return {
tools: [
createServerFromTemplateTool,
executeToolTool,
getServerToolsTool,
deleteServerTool,
listServersTool,
],
};
});
server.setRequestHandler(
CallToolRequestSchema,
async (request: CallToolRequest) => {
console.error("Received CallToolRequest:", request);
try {
if (!request.params.arguments) {
throw new Error("No arguments provided");
}
switch (request.params.name) {
case "create-server": {
const args = request.params
.arguments as unknown as CreateServerArgs;
if (!args.code || !args.language) {
throw new Error(
"Missing required arguments: code and language"
);
}
const serverId = await serverManager.createServer(
args.code,
args.language
);
return {
content: [
{
type: "text",
text: JSON.stringify({ serverId }),
},
],
};
}
case "create-server-from-template": {
const args = request.params
.arguments as unknown as CreateServerFromTemplateArgs;
if (!args.language) {
throw new Error("Missing required argument: language");
}
// LLMから提供されたカスタムコードがあればそれを使用し、なければデフォルトのテンプレートを使用
let serverCode = args.code;
// コードが提供されていない場合はデフォルトテンプレートを使用
if (!serverCode) {
// 既存のテンプレート選択ロジック
switch (args.language) {
case "typescript":
serverCode = `/* TypeScriptテンプレート */`;
break;
case "python":
serverCode = `# Pythonテンプレート`;
break;
default:
throw new Error(
`Unsupported template language: ${args.language}`
);
}
}
const result = await serverManager.createServer(
serverCode,
args.language,
args.dependencies
);
return {
content: [
{
type: "text",
text: JSON.stringify({
serverId: result,
message: args.code
? `Created server from custom code in ${args.language}`
: `Created server from ${args.language} template`,
}),
},
],
};
}
case "execute-tool": {
const args = request.params
.arguments as unknown as ExecuteToolArgs;
if (!args.serverId || !args.toolName) {
throw new Error(
"Missing required arguments: serverId and toolName"
);
}
const result = await serverManager.executeToolOnServer(
args.serverId,
args.toolName,
args.args || {}
);
return {
content: [
{
type: "text",
text: JSON.stringify(result),
},
],
};
}
case "get-server-tools": {
const args = request.params
.arguments as unknown as GetServerToolsArgs;
if (!args.serverId) {
throw new Error("Missing required argument: serverId");
}
const tools = await serverManager.getServerTools(args.serverId);
return {
content: [
{
type: "text",
text: JSON.stringify({ tools }),
},
],
};
}
case "update-server": {
const args = request.params
.arguments as unknown as UpdateServerArgs;
if (!args.serverId || !args.code) {
throw new Error(
"Missing required arguments: serverId and code"
);
}
const result = await serverManager.updateServer(
args.serverId,
args.code
);
return {
content: [
{
type: "text",
text: JSON.stringify(result),
},
],
};
}
case "delete-server": {
const args = request.params
.arguments as unknown as DeleteServerArgs;
if (!args.serverId) {
throw new Error("Missing required argument: serverId");
}
const result = await serverManager.deleteServer(args.serverId);
return {
content: [
{
type: "text",
text: JSON.stringify(result),
},
],
};
}
case "list-servers": {
const servers = serverManager.listServers();
return {
content: [
{
type: "text",
text: JSON.stringify({ servers }),
},
],
};
}
default:
throw new Error(`Unknown tool: ${request.params.name}`);
}
} catch (error) {
console.error("Error executing tool:", error);
return {
content: [
{
type: "text",
text: JSON.stringify({
error: error instanceof Error ? error.message : String(error),
}),
},
],
};
}
}
);
// Set up transport and connect
const transport = new StdioServerTransport();
console.error("Connecting server to transport...");
await server.connect(transport);
console.error("MCP Create Server running on stdio");
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`Failed to start server: ${errorMessage}`);
process.exit(1);
}
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});