MCPでいろいろやってみた内容をまとめてみる

投稿日:

はじめに

最近話題のMCP(Model Context Protocol)についていろいろやっているよというのをまとめてみます。 ただMCPの話をする前に、先にn8nの話をします。

n8nとは

n8nはローコードのワークフローツールです。 IFTTTみたいなものを思い浮かべてもらったらいいと思うのですが、自由度が高く、多くのデータを並列処理できるので気に入っています。

ただライセンスはオープンソースではなく、Sustainable Use Licenseという独自ライセンスです。 とは言え最近よくある「競合サービス以外は使える」系のライセンスで、個人とか社内で使うには問題ないライセンスで、(自分的には)許容範囲だと判断して使っています。

n8nとカスタムノード

このn8nですが、GUIでワークフローを作成できます。その中にはCodeというものがあり、 JavaScriptやPythonでコードを書くことができます。

ただこのコードはテストができないのが不満でした。そこで自分が目をつけたのがカスタムノードという仕組みです。 特定の形式(n8n- で始まるとかpackage.jsonにいろいろ書くとか)を満たしたnpmライブラリを用意することで、自分が作ったコードをノードとして提供できます。 具体的にはn8n-nodes-starterを参照してください。

これをセルフホスティングしているn8nに入れることができます。自分はGitHubのプライベートパッケージとしてリリースしたものを入れています。こんな感じで。

- name: install private community node in container
  vars:
    n8n_nodes_package: "@ikemo3/<パッケージ名>@<バージョン>"
  command: docker compose exec -e GITHUB_TOKEN={{ lookup('env','GITHUB_TOKEN') }} n8n sh -c "cd ~/.n8n/nodes && npm config set @ikemo3:registry https://npm.pkg.github.com && npm config set //npm.pkg.github.com/:_authToken={{ lookup('env','GITHUB_TOKEN') }} && npm i {{ n8n_nodes_package }}"
  args:
    chdir: ~/n8n

そして出来たのがこれ。

Cloudflare Workers + Honoを使ってこんな感じにしています。 CLI としていますが、これは stdio サーバのことです。

Claude DesktopとMCPに出会った

2月末、Claude 3.7 Sonnetがリリースされ、性能の高さに盛り上がりました。それからClaude Codeが話題になりました。 そこに「Limited time offer: 25% off Claude Pro with the new Claude 3.7 Sonnet」というメールが来ました。 自分は最近LibreChat + GPT-4o miniをケチケチ使ってたのですが、 Claudeの方針が好きなのと、性能のいいモデルを使っておかないとちょっとマズそうな思ったので、思い切って年間契約しました。

契約したものの、普通にWebで使っているだけだと微妙だよねということで他に何かできないかなと思って見つけたのがClaude Desktop。 そこでMCPというのが使えるぞというのを知り試してみました。

コードの実体はこれです。

  • 処理を共通化するためにAPIはPOST、パラメータはJSON bodyに限定
  • HonoのRPC機能を使用
  • zodスキーマをインポートして .shape で再利用
  • handleResponse はLLMに書かせた
import { applePushMessageSchema } from "...";
import { ClientResponse, hc } from "hono/client";

// appはHonoインスタンス
const BASE_URL = "...";
const client = hc<typeof app>(BASE_URL, {
  headers: {
    // 認証のためのヘッダ追加
  },
});

// Create an MCP server
const server = new McpServer({
  name: "...",
  version: "...",
});

server.tool(
  "apple-push-message",
  "Apple Push Notification用のメッセージを送信します",
  applePushMessageSchema.shape,
  async (args) => {
    return handleResponse(
      client["apple-push-message"].$post({
        json: args,
      }),
    );
  },
);

async function handleResponse(responsePromise: Promise<ClientResponse<any>>) {
  try {
    const res = await responsePromise;
    const body = await res.json();

    if (res.ok) {
      return {
        content: [
          {
            type: "text" as const,
            text: JSON.stringify(body, null, 2),
          },
        ],
      };
    } else {
      return {
        content: [
          {
            type: "text" as const,
            text: `エラーが発生しました: ${res.status} ${res.statusText}\n${JSON.stringify(body, null, 2)}`,
          },
        ],
        isError: true,
      };
    }
  } catch (error) {
    return {
      content: [
        {
          type: "text" as const,
          text: `処理中にエラーが発生しました: ${error instanceof Error ? error.message : String(error)}`,
        },
      ],
      isError: true,
    };
  }
}

APIで何でも繋げていく

このAPIですが何でも出来ます(Cloudflare Workersの制限に引っ掛からなければ)。 たとえば自分はRaindropというブックマークサービスを使っていますが、これに流すためにURLをクリーンにするAPIを作っています。 100行未満のごく小さいコードですが、こんなのでも役立ちます。

そこで、自分が以前Fessで作っていた検索サーバと繋げてみました。 この検索サーバには自分の過去の発言とかを溜め込んでいる、自分だけの検索サーバです。 あとCloudflare Accessで自分しかアクセスできないようにしている、プライベートのコンテンツを取得するAPIも作りました。

これをClaude Desktopからアクセスすると、次のような結果が得られました。

ここには載せていませんが、結果の一部を掲載します。ディープリサーチっぽい感じになっています。

情報のソースも出すことができます。たとえば「ニコニコ市場で115本も売れている」の元はこの投稿です(「自分だけの検索サーバ」なので自分の投稿が出る)。

やっていることはRAGと同様ですが、Claudeが「初音ミク」「初音ミク ボーカロイド」「初音ミク 誕生日」「初音ミク 歴史」とかキーワードを変えて何度も検索してくれます。 しかも検索した結果が0件ならより一般的な単語で検索してくれたりと、ある程度気を利かせてくれます。

こんな感じでいろいろ繋げています。

「@天気」で天気を検索

次に「@天気」で天気を出せるようにしました。

これはちょっとトリックがあって、「"@" から始まる会話については、custom_instruction を使ってカスタムインストラクションを取得し、取得したカスタムインストラクションに基づいて会話してください。」という命令を入れています。そしてカスタムインストラクション自体もMCPで、「天気を要求された時はここにアクセスしてね」という内容が書かれています。

これは何を意識しているのかというと、2006年頃に流行したPlaggerや、2024年まであったニコニコ動画の@ピザです。MCPってめっちゃPlaggerっぽいんです。

PlaywrightのMCPも出たことだし、きっとこれ使えばClaudeがピザ注文してくれるはず。

VS Code + GitHub CopilotでもMCPが使えるしMacのローカルアプリも繋げられる

次にVS Codeのカスタム指示のネタが話題になりました。

自分はツンデレスキーということでVS Code + GitHub Copilotで試していたのですが、VS CodeのInsider版にMCP対応が公式で対応したということで試してみました(このときは公式ではなく別の拡張機能だったかもしれません)。それがこれ。セルフツッコミができるのがポイント高いです。

これはOmniFocusのInboxを検索していますが、確かapple-mcpだったと思います。 いくつかはAppleScriptを使っているようです。

モバイル対応していない → LibreChatがあるよ

このClaude DesktopでのMCPですが、デスクトップということで、モバイルに対応していないのが不満でした。 Transportにはstdioの他にSSE(Server-sent events)があったのですが、この実装ハードルが高いということで、Streamable HTTPによる対応が議論されていました。

ちなみにこの仕様は2025-03-26版としてリリースされています。

ただこれがクライアントに実装されるのはまだなので、他にいいのないかなとawesome-mcp-clientsを眺めていました。 ほとんどのアプリがデスクトップだったのですが、WebアプリにLibreChatが入っているのを確認しました。 サクッと動きました(ENDPOINTS の指定漏れに注意)。

ただ stdio だといろいろと面倒なのでやっぱりSSE対応クライアントが欲しくなりました。 stdioをSSEに変えてくれるものありそうだなと思って探したらmcp-proxyというのを見つけました。 これを入れたらサクッとSSE対応でき、iPhone + LibreChat + MCPでいろいろできるようになりました。

スケジュール実行したいよね → n8nがあるよ

これでモバイル対応できるようになりましたが、チャットUIだけでやるのは限界があるかなと感じて、自動できるプラットフォームがないかと探してみました。 まだどこにもないし、Mastraでclient作るしかないのかなと思ってたんですが、 ふとn8nにAgent機能があるのに気づきました。

Conversationと呼ばれるチャットUIの他にも色々なパターンがあります。

ただ、これは組み込みのノードには対応していますが、自作カスタムノードには対応していませんでした。 n8n-nodes-mcpというのも試したのですが、自分の今のコードとは合わないのでやめました。 Issue上がっていないかなと調べたら次のを見つけました。

読み進めてみるとすでに対応済み、ただし次の2つの設定が必要のこと。

  • カスタムノードに usableAsTool: true を追加
  • 環境変数に N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true を追加

Tools AI Agentを使って、データベースからデータを取得して、それをメールするタスクを試してみたんですが、無事動きました。動かないこともあったのですが、プロンプトが雑なのと、GPT-4o miniでケチったのがおそらく原因なので、ちゃんと組めばそこそこの精度は出るかなと。

n8nのAgent Toolは正確にはMCPではないんですが、MCPとAPIを共有しているので実質同じような感じに扱えます。

たろっとさん作ったよ

自分が昔作ったたろっとさんですが、とりあえずこれもMCPサーバ化。

ブラウザ拡張とも繋げられる?

自分はChrome/Firefox拡張の「ゴシップサイトブロッカー」を作っているのですが、この拡張機能をMCPサーバにできないかなと考えました。 拡張機能はWebSocketを使ってローカルのアプリとも通信できるようなので同じようなのを作ってないかなと思って調べたところ見つけました。

ただこれはPoC的リポジトリで、Chrome拡張機能も開発者モードでのインストールで、 デフォルトだとMCPクライアントからアラートダイアログを取得とUserAgentを取得する機能しかありませんでした。 そこで改造して document.body を取得して返すというのを試してみました。

ただこれは原理的にものすごく危なくて(MCP自体悪意があるコードに弱いのでいまさらですが)、普段使っているブラウザの情報を引っこ抜けます。 普通にブラウザ操作するならPlaywrightを使えばいいので、こんなこともできるという実験です。

数値の比較は出来なかった

LLMは9.9と9.11の比較が苦手なので、数値を比較するだけのMCPサーバを作れば解決できるんじゃね?と判断して試してみました。 ですがダメでした。

この比較を間違えるのはコンテキスト依存なので「ソフトウェア開発者かどうか」を渡せばうまくいくと思ったんですが。

悪意があるプロンプトに弱い

MCPサーバが返す文字列として、LLMを操作する命令を入れるとどうかなと試してみました。 あっさりひっかかってしまいました。

おわりに

MCPに今後期待することとか今後試してみたいことも書こうと思ったんですが、これまでやってきたことを並べるだけで結構な量になったので一旦これまでにしておきます。

前の記事:
国産クラウドやVPS、SaaSに提供して欲しいもの