Skip to main content
Glama
tesla0225

MCP Create Server

by tesla0225

create-server-from-template

Generate a custom MCP server using predefined TypeScript or Python templates. Modify code, add dependencies, and tailor functionality to meet specific requirements for server implementation.

Instructions

Create a new MCP server from a template.

以下のテンプレートコードをベースに、ユーザーの要求に合わせたサーバーを実装してください。 言語に応じて適切なテンプレートを選択し、必要に応じて機能を追加・変更してください。

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テンプレート:

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 を定義して型安全性を確保することをお勧めします。

ユーザーの要求に応じて上記のテンプレートを参考にカスタマイズしてください。その際、基本的な構造を維持しつつ、ツール名や機能を変更できます。

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
codeNoカスタマイズしたサーバーコード。テンプレートを元に変更したコードを入力してください。省略した場合はデフォルトのテンプレートが使用されます。
dependenciesNo使用するライブラリとそのバージョン(例: { "axios": "^1.0.0" })
languageYesThe programming language for the template

Implementation Reference

  • Handler logic for the 'create-server-from-template' tool. Parses arguments, determines server code (custom or default template based on language), and calls serverManager.createServer to instantiate the MCP server with optional dependencies.
    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`,
            }),
          },
        ],
      };
    }
  • Tool definition including name, detailed description with embedded templates for TypeScript and Python, and inputSchema defining parameters: language (required, enum), optional code, and dependencies.
    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"],
      },
    };
  • index.ts:1011-1022 (registration)
    Registration of the tool in the ListToolsRequest handler, where createServerFromTemplateTool is included in the returned tools list.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      console.error("Received ListToolsRequest");
      return {
        tools: [
          createServerFromTemplateTool,
          executeToolTool,
          getServerToolsTool,
          deleteServerTool,
          listServersTool,
        ],
      };
    });
  • Core helper method in ServerManager class that implements the actual server creation logic called by the tool handler. Handles temp dir setup, deps install (npm/pip), code compilation (TS), process spawn, and MCP client connection.
    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;
      }
    }
  • TypeScript interface defining the expected arguments for the create-server-from-template tool, used for type casting in the handler.
    interface CreateServerFromTemplateArgs {
      language: "typescript" | "python";
      code?: string;
      dependencies?: Record<string, string>; // 例: { "axios": "^1.0.0" }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions implementation details (e.g., type handling in TypeScript) and customization guidance, but fails to disclose critical traits: whether this is a read/write operation (implied creation but not stated), permission requirements, side effects (e.g., server deployment), error handling, or output format. For a creation tool with zero annotation coverage, this leaves significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is excessively long (over 500 words) and poorly structured, with extensive code blocks and implementation notes that don't efficiently convey tool usage. It's front-loaded with a clear purpose but buried in verbose templates and language-specific details. Sentences like '注意事項:...' and template code could be condensed or moved to external documentation, reducing conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (creation operation, 3 parameters, no output schema, no annotations), the description is incomplete. It lacks output expectations (what happens after creation—e.g., server ID, status), error conditions, and integration context with siblings. While it provides template code, it doesn't address how the created server integrates with other tools (e.g., 'execute-tool' on the new server), leaving gaps for an agent to use it effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all three parameters thoroughly (code, dependencies, language). The description adds minimal parameter semantics beyond the schema—it references 'language' through template examples and implies 'code' customization, but doesn't explain parameter interactions or provide additional syntax/format details. Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the purpose: 'Create a new MCP server from a template' with specific verbs ('create', 'implement') and resources ('MCP server', 'template code'). It distinguishes from siblings like 'delete-server' (destructive) and 'list-servers' (read-only) by focusing on creation from templates. However, it doesn't explicitly contrast with 'execute-tool' or 'get-server-tools', which slightly reduces differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage context through template examples and customization guidance ('ユーザーの要求に応じて上記のテンプレートを参考にカスタマイズしてください'), but lacks explicit when-to-use rules or alternatives. It doesn't specify prerequisites (e.g., when to use this vs. manually coding a server) or compare with sibling tools like 'execute-tool' for tool execution vs. server creation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tesla0225/mcp-create'

If you have feedback or need assistance with the MCP directory API, please join our Discord server