`@hono/mcp`でMCPサーバーを作りdeno deployにデプロイするまで

Hono v4.8.0で`@hono/mcp`がリリースされました。これを使うと手軽にMCPサーバーを作成することができるということで、試してみました。

https://github.com/honojs/hono/releases/tag/v4.8.0#:~:text=Third%2Dparty%20Middleware%20Updates

:::note warn
本記事ではDenoを用いて実行していますが、Node.jsやBun、Cloudflare Workerなどでも基本的な手順は同じです。適宜読み替えてください。

```shell
$ deno --version
deno 2.3.6 (stable, release, aarch64-apple-darwin)
v8 13.7.152.6-rusty
typescript 5.8.3
```
:::


# 前置き

### リモートMCPサーバー（Streamable HTTP）について

MCP（Model Context Protocol）は、AIが外部のシステムとやり取りするためのプロトコルです。

https://zenn.dev/mkj/articles/0ed4d02ef3439c

さて、MCPには「ローカルで実行するもの」と「サーバー上で実行し、HTTPで通信するもの」の2種類あります。

今回は「サーバー上で実行し、HTTPで通信する」＝Streamable HTTP Transportと呼ばれる仕組みでMCPサーバーを実装してみましょう！

### Honoを使うメリット

[MCPの公式SDKのexample](https://github.com/modelcontextprotocol/typescript-sdk)では、expressが使われています。
しかし、Honoを使うことで、Cloudflare worker や Bun 、 AWS Lambda 、 Deno Deployなど、様々な環境でMCPサーバーを動かすことが可能になります。

# 実装

先に依存関係のインストールを済ませておきます。

:::note warn
Deno以外の環境で実行する際は、`npm install`や`bun install`などに適宜読み替えてください。
:::


```shell
$ deno add npm:hono npm:@hono/mcp npm:@modelcontextprotocol/sdk npm:zod
```

まずはMCPサーバーを実装します。

今回はとりあえず足し算を行うtoolを定義します。

```ts:mcp/server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

/** MCPサーバーを作成する関数 */
export function createMcpServer() {
  const mcpServer = new McpServer({
    name: "my-mcp-server",
    version: "1.0.0",
  });

  // MCPサーバーから呼び出すことができるツールの定義
  // 足し算を行うtoolを定義する
  mcpServer.registerTool("add", {
    // ツール名
    title: "Add Numbers",
    // ツールの説明
    description: "Adds two numbers together.",
    // ツールの情報
    annotations: {
      readOnlyHint: true, // 外部の状態を変更しない
      openWorldHint: false, // 外部システムと接続しない
    },
    // ツールの引数
    inputSchema: {
      a: z.number().describe("First number"),
      b: z.number().describe("Second number"),
    },
    // ツールの出力形式
    outputSchema: {
      result: z.number().describe("The sum of the two numbers"),
    },
  }, ({ a, b }) => {
    // 足し算 a+bを計算
    const structuredContent = {
      result: a + b,
    };

    // textとstructuredContentを以下の形で返却する
    return {
      content: [{
        type: "text",
        text: JSON.stringify(structuredContent),
      }],
      structuredContent,
    };
  });

  return mcpServer;
}
```

次にHonoでHTTPサーバーを作成し、先ほどのMCPサーバーを呼び出せるようにします。

```ts:main.ts
import { StreamableHTTPTransport } from "@hono/mcp";
import { Hono } from "hono";
import { createMcpServer } from "./mcp/server.ts";

const app = new Hono();

app.get("/", (c) => {
  return c.text("Hello, MCP Server is available at /mcp");
});

app.all("/mcp", async (c) => {
  // mcpサーバーを作成
  const mcpServer = createMcpServer();
  // 通信に利用するTransportを作成
  const transport = new StreamableHTTPTransport();
  // mcpサーバーに接続
  await mcpServer.connect(transport);
  // mcpサーバーへリクエストを送り、結果を返却
  return transport.handleRequest(c);
});

// `deno serve`コマンドで実行できるようエクスポート
// Deno以外の環境で起動する場合は適宜読み替えてください
// 参考: https://hono.dev/docs/getting-started/cloudflare-workers
export default app satisfies Deno.ServeDefaultExport;
```

実装は以上です！

次は、このツールをvscodeのCopilot Agentから呼び出してみましょう！

# ローカルで実行し接続してみる

サーバーを起動します。


```shell
$ deno serve --watch --port 5503 ./src/main.ts
```

`.vscode/mcp.json`に、MCPサーバーへの接続設定を書いておきます。

```json:.vscode/mcp.json
{
  "servers": {
    "add-two-numbers-debug": {
      "type": "http",
      "url": "http://localhost:5503/mcp"
    }
  }
}
```

上記設定を書いておくことで、ファイル上に起動ボタンが現れるので、「▶︎起動」を押して起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678625/35f76a61-584d-4fc6-9647-28aaf7f42a91.png)

起動した状態で、Copilotのチャットからmcpサーバーを呼び出してみます。
（入力欄の左下から「Agentモード」にする必要があります。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678625/f6552e2c-b345-4466-a81c-608d04c4dbd3.png)

無事MCPサーバーを呼び出すことができました！

# デプロイしてリモート上のMCPサーバーに接続する

先ほど作成したサーバーを、deno deployにデプロイして公開してみましょう。

:::note warn
Deno Deployは「[Deno Deploy Early Access](https://docs.deno.com/deploy/early-access/)」という名前で次世代版がベータリリースされています。
しかし、本記事で紹介しているのは、従来のDeno Deploy（=classic版）へデプロイする手順です。
Early Accessの方はおそらくデプロイ手順が異なるためご注意ください。
:::

まずデプロイのため、CLIツールである`deployctl`をインストールします。

https://docs.deno.com/deploy/manual/#install-deno-and-deployctl

```shell
$ deno install -A jsr:@deno/deployctl --global
```

次に、`deployctl deploy`コマンドでデプロイします。
GitHubアカウントでの認証などが行われると思うので、手順に従ってアカウントを接続します。

```
$ deployctl deploy
```

以上でデプロイが完了です！デプロイが完了すると、最後にデプロイ先のURLが出力されるので、先程の`.vscode/mcp.json`に設定を追加します。

```:output
View at:
 - https://sour-ferret-80.deno.dev
 - https://sour-ferret-80-mzeqhtqxnmab.deno.dev
```

```diff_json:.vscode/mcp.json
  {
    "servers": {
      "add-two-numbers-debug": {
        "type": "http",
        "url": "http://localhost:5503/mcp"
      },
+     "add-two-numbers": {
+       "type": "http",
+       "url": "https://sour-ferret-80.deno.dev/mcp"
+     }
    }
  }
```

以上で接続設定も完了です！

vscodeからリモートMCPに接続できるようになっているか確認しましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678625/cd85949c-cd47-4990-80a0-a6c7de5cdd99.png)

無事接続できましたね！ :tada:

# まとめ

- `@hono/mcp`を使ってMCPサーバーを作成しました。
    - `@hono/mcp`を使うと、MCPサーバーをBun、Cloudflare Workers、Denoなど、様々な場所で動かせます。
- MCPサーバーをdeno deployへデプロイし、手元のGitHub Copilotから接続して動作確認することができました。

筆者はHonoで故事成語を返すmcpサーバーを作ってみました。手軽に公開して手軽にAIを拡張できるのがいい体験でした！

https://x.com/_ayame113_/status/1936676573631107111