CJSでMCPサーバを作る

ESM形式のTypescriptでMCPサーバを作る例はありますが、CJS形式のNode.jsのJavascriptでの例はあまりなかったので、ここでまとめておきます。

# 準備

```
> mkdir mcp-server-sample
> cd mcp-server-sample
> npm init -y
> npm install @modelcontextprotocol/sdk zod
```

```js:index.js
const { McpServer, ResourceTemplate } = require('@modelcontextprotocol/sdk/server/mcp.js');
const { StdioServerTransport } = require('@modelcontextprotocol/sdk/server/stdio.js');
const { z } = require("zod");

(async () => {

    // Create an MCP server
    const server = new McpServer({
        name: "Esp32Demo",
        version: "1.0.0",
        capabilities: {
            resources: {},
            tools: {},
        },
    });

    // Add an addition tool
    server.tool("add_number", "add two numbers",
        { a: z.number().describe("数値1"), b: z.number().describe("数値2") },
        async ({ a, b }) => {
          return {
            content: [
              {
                type: "text",
                text: String(a + b)
              }
            ]
          }
        }
    );
    server.tool("subtract_number", "subtract two numbers",
      { a: z.number().describe("数値1"), b: z.number().describe("数値2") },
      async ({ a, b }) => {
        return {
          content: [
            {
              type: "text",
              text: String(a - b)
            }
          ]
        }
      }
    );

    // Add a dynamic gpio resource
    server.resource(
        "gpio",
        new ResourceTemplate("gpio://PORT{name}", { list: async () => {
            return {
                resources:
                    [
                      { name: "37", uri: "gpio://PORT37"},
                      { name: "39", uri: "gpio://PORT39"},
                    ]
            };
          }}
        ),
        async (uri, variables) => {
          return {
              contents: [{
                  uri: uri.href,
                  text: variables.name
              }]
          }
        }
    );
      
    // Start receiving messages on stdin and sending messages on stdout
    const transport = new StdioServerTransport();
    await server.connect(transport);
})();
```

# デバッグ

```
> npx @modelcontextprotocol/inspector node index.js
・・・
Set up MCP proxy
🔍 MCP Inspector is up and running at http://127.0.0.1:6274 🚀
```

上記のURLをブラウザで開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/c9404c5e-e28b-4adf-a855-3baa34e34cf2.png)

Transport Types: STDIO
Command: node
Auguments: index.js

として、Connectボタンを押下します。
List ResourcesとList Templatesを押すとこうなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/825658dc-3bbc-4e43-aeaa-90ec9d14c01a.png)

Resoucesの39を押すと、以下のように値を取得できています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/15ab99c6-7464-4fd1-887f-1f89383e5aee.png)

次に、Toolsタブをクリックして、List Toolsを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/88a1eb16-84ff-415b-aea9-cbdfa6d8b5e8.png)

add_numberをクリックして、aに10、bに6を入れるて、Toolを押すと、16が返ってくるのがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/4c2687d5-ec49-40e1-a6f7-64745844e030.png)

# Claude for Desktopで動かす

claude_desktop_config.jsonのmcpServersの下に追記します。Esp32Testという名前で登録します。
C:\Users\XXXXX\AppData\Roaming\Claude にあります。

```json:claude_desktop_config.json
{
    "mcpServers": {
        "Esp32Test": {
        	"command": "node",
        	"args": ["c:\\Users\\XXXXXXXX\\Documents\\Node_Projects\\mcp-server-sample\\index.js"]
        }
    }
}
```

Claude for Desktopを再起動します。
右下のタスクバーに生き残っているので、終了してから再起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/964528c5-72e2-40b2-aa22-efb41ad86b7e.png)

設定ボタンを押すと、Esp32Testが有効化されているのがわかります。

それでは、```add_numberで、10足す6は？```と入力してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/13e94c80-e73c-4312-868a-b3c5395b4ae3.png)

許可をするとちゃんと処理されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/6def302a-86ca-4dac-8911-191e10bc3858.png)

以上