MCPサーバの実装練習をSymbol API RESTサーバでしてみる

AI、便利ですよね。
しかし、現状のAI（LLM）はそれ自身が持っている知識に回答が依存している部分があります。

将来的には透過的に利用できるよう、AI側で解決してしまうのかもしれませんが、
現時点での選択肢として MCP（Model Context Protocol）があります。
これは簡単に言ってしまえば、AIが外部リソース（ファイル、データベース、メディア等）へアクセスする際の決め事の規格です。

ブロックチェーン上の情報（に限りませんが）をAIが利用・活用するために、
MCPの仕組みを用いることで、AIのできる回答がより豊かになります。

本記事ではClaude DesktopをMCPクライアントとし、
Symbolブロックチェーン上の情報扱うMCPサーバを実装して、動作を確認します。


※追記

動的にすべてのエンドポイントを実装しました。こちらをごらんください。

- [Symbol REST API OpenAPI定義からMCPサーバを作る \#ブロックチェーン \- Qiita](https://qiita.com/husqva_luna/items/75e66627e0df19cc5a63)


# 前提条件・知識

- Node.js 16以上（たぶん、20くらいだと安心）
- Claude Desktop β版

それから今回の記事で利用するシステムやライブラリについての簡単な説明。


## API定義

Symbolブロックチェーンは有志が立ち上げているノード兼REST APIサーバに問い合わせ、ブロック情報を取得するのが通常です。

REST APIのリクエスト・レスポンス定義はOPEN API形式で記述されたものが公開されているので、これを利用します。

- [Endpoints \- Symbol Developers](https://symbol.github.io/symbol-openapi/v1.0.4/)


## openapi-zod-client & zod

Open API定義からAPIクライアントを生成するツール。
定義があれば、シンプルに使い出せて、型安全なものをすぐに用意できるので便利です。

- [astahmer/openapi\-zod\-client: Generate a zodios \(typescript http client with zod validation\) from an OpenAPI spec \(json/yaml\)](https://github.com/astahmer/openapi-zod-client)


## @modelcontextprotocol/sdk

MCPの実装フレームワーク。
入力と出力のルールに従うだけで簡単にMCPサーバを実装できます。

- [modelcontextprotocol/typescript\-sdk: The official Typescript SDK for Model Context Protocol servers and clients](https://github.com/modelcontextprotocol/typescript-sdk)


## Claude Desktop

話題沸騰、説明不要のAIであるClaudeのデスクトップクライアントです。
ここではインターフェイスかつMPCクライアントとして働きます。

- [Download \- Claude](https://claude.ai/download)


# 実装したソースコード

**＊リポジトリは削除してしまいました。
以降は設定方法や雰囲気を見るためのものとして残しておきます。**

- [husqvaluna/spike\-symbol\-mcp\-server](https://github.com/husqvaluna/spike-symbol-mcp-server)

クローンして、お好きな場所にソースを置いてください。

練習なので、アカウント情報とモザイク情報の取得だけを実装してみました。


# 動作確認

MCPサーバの利用前と利用時の比較、設定などをやっていきます。


## MCPサーバの未使用時（Before)

まずは、利用前でプロンプトを渡した場合の結果例を確認しておきます。

> TDK2E5VGKH4YSPVBYL2IPI2QFKXLDCSNHDOURRI の資産一覧と資産ごとの詳細な情報をリスト化してください。

![before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994773/f14c09d2-8c69-4f9d-9c8e-181e3d74cda0.png)

アドレスについても、そもそもコンテキスト（文脈）が分かっていないようです


## Claude Desktopの設定

現状では、まず開発者モードを有効にして、設定項目を出現させます。

- [For Claude Desktop Users \- Model Context Protocol](https://modelcontextprotocol.io/quickstart/user#2-add-the-filesystem-mcp-server)


## MCPサーバの設定

ファイルを開いて、`symbol`キーを書き足して、それ以下にパラメタ定義を書きます。
見るとわかるように、サーバを起動するためのコマンド定義を記述しています。

```json:/path/to/your/claude_desktop_config.json
{
  "mcpServers": {
    "symbol": {
      "command": "npx",
      "args": [
        "-y",
        "tsx",
        "/path/to/spike-symbol-mcp-server/src/index.ts"
      ]
    }
  }
}
```

`/path/to/your/claude_desktop_config.json`
場所が分かれば、移行は直接エディタで開いて編集してもいいと思います。

書き換えたら、Claude Desktopを再起動して読み込みなおさせる必要があります。

プロンプト入力の右端にあるハンマーのアイコンから確認できます。

![available-mcp-tools.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994773/028e26cf-c9ac-4d13-a342-c7fe16923d96.png)


## MCPサーバの使用時（After）

実装したMCPサーバが利用可能な状態で、同じプロンプトを流してみましょう。

![after-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994773/b9519602-5707-4b38-a625-6a413445325f.png)

ツールの利用について許可を求めてくるので「このチャットで許可」をクリックします。

![after-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994773/8f42905d-190f-4478-8f4b-f915df7ba86b.png)

アカウント情報のAPIにアクセスし、そのアカウントの持つモザイクについて詳細をまとめた返答が帰ってきました。
途中に、MCPサーバを利用したログも出ています。

こうして外部リソースとしてのブロックチェーン上の情報を利用することができました。


# 感想

ひとまずの実験として、アカウント情報とモザイク情報のAPIだけを実装しましたが、
他のAPIも、単にAPIレスポンスを返すだけでも十分実用性はありそうです。

コーディングプロンプトの設計次第では、AIでOpen API定義からMCPを生成できてしまうかもしれません。

触ってみて理解した限りでは、結局のところリソースへのアクセス方法を一定のルールで実装したプロキシサーバを実装するようなものです。


## っていうか

理解のためにわざわざ実装を行いましたが、Open API定義があれば、それをそのままMCPサーバにできるコードがもうありました。
試してませんが、今回のケースで言えばこれでいいじゃん。

- [openapi\-mcp\-serverとHonoでClaude Desktop用ツールを作ろう \#TypeScript \- Qiita](https://qiita.com/hosaka_/items/b685bdae034886361cc5)
- [snaggle\-ai/openapi\-mcp\-server](https://github.com/snaggle-ai/openapi-mcp-server)

こういうことができるということが分かったのと、勉強にはなったのでよかったです。

結局のところ、カチッとした仕様書が存在し、継続的にメンテナンスされていることが、AIコーディングを活用するためのキーになるのかもしれませんね。

### もっと簡単に動かせたら…（失敗）

`claude_desktop_config.json`に次を追記するだけ…といいたいところなのだけど、これだけでは上手く行かない。

```json:/path/to/your/claude_desktop_config.json
{
    "mcpServers": {
        "symbol-rest-api": {
            "command": "npx",
            "args": [
                "openapi-mcp-server",
                "https://symbol.github.io/symbol-openapi/v1.0.4/openapi3.yml"
            ]
        }
    }
}
```

そのままでは使えないため、ローカルにダウンロードして書き換える。


#### 課題1 `servers`にアクセスできるRESTノードのURLを書き込む

```json
servers:
  - url: 'http://localhost:3000'
    description: Local development
```

- [Symbol node list](https://symbolnodes.org/nodes/)

APIが有効になっているノードから任意のものを選んで書き換えてください。

例) `https://pasomi.net:3001`


#### 定義が多すぎてエラーになる（？）

直接的な原因がわからないのですが、次のようなエラーがでました。

![error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994773/95531fcb-5665-4ab7-be67-b31af2228606.png)

たぶんコンテキストが大きすぎるんでしょうね…。
そう思って、前述で実装したコードと同じだけのAPIにだけ定義を切り捨ててみました。


#### ローカルファイルを指定する

書き換えたAPI定義ファイルを指定するように書き換えます。

```json:/path/to/your/claude_desktop_config.json
{
    "mcpServers": {
        "symbol-rest-api": {
            "command": "npx",
            "args": [
                "openapi-mcp-server",
                "/path/to/your/openapi3.yml"
            ]
        }
    }
}
```

#### 結論

これでClaude Desktopを再起動して、読み込み直してください。
前述と同じ結果が得られることを確認し…ようとおもって同じプロンプトを流したのですが、

![claude-error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3994773/21c0b5e4-cfd5-4383-89fb-fae62770ed34.png)

これが、たまたま実行したときのClaude側の問題なのか、なにかリクエストに問題があるのかまで切り分けられてません。

残高の取得だけを指示すれば通るので、コンテキストの大きさが悪さしているような気もしますが…。
引き続き検証や実験は試みてみようと思います。

そもそも使い方として間違ってたりするのかな…