fragmentary notesITエンジニアの気まぐれメモ

Node.jsでMCPサーバーを作りWebAPIをcallしてみた(Claude連携)

Abstract

Model Context Protocol(MCP)は、様々な外部システムのデータ提供をLLMが統一的に利用できる仕組み。USB-Cのようにデータアクセスの統一インターフェースとして機能する。個人の家計簿APIをMCPサーバー経由でLLMに接続し、収支分析を試した。最小構成のMCPサーバーを構築後、WebAPIコール処理を追加。Claude DesktopにMCPサーバーを設定し、プロンプトに基づいた家計簿レポート作成に成功。dotenvのバージョン、環境変数の設定、返却値の形式に注意が必要。MCPサーバーはLLMが自らデータを選んで利用できる世界を実現し、企業の情報系システム分析などへの応用が期待される。

はじめに

生成AIの進化が著しい今日このごろ、最近よく耳にするのがModel Context Protocol(MCP)。USB-Cによく例えられるプロトコルですが、「結局何ができるの?」「どう便利なの?」というのは実体験がないとピンときませんでした。

多分、実体験がないとそのすごさがわからないのだろうと思い、MCPサーバーのサンプルプログラムに触れ、ざっくり理解した内容。その上で、自分ならこういう使い方をすると便利なのでは?ということを試してみたので、その備忘をいつものように書き留めてみます。

ざっくりModel Context Protocol(MCP)とは?

(間違いや不足があるかもしれませんが)ざっくりした私の理解は、
MCPサーバーは、さまざまな外部システムのデータ提供手段の違いを吸収し、LLMなどのクライアントからは統一された手順で利用できるようにする仕組みです。 USB-Cが用途ごとにバラバラだった端子を1つに統一したように、MCPは「データアクセスの統一インターフェース」として機能します。

何ができるだろうか?

では、実際にMCPサーバー経由でLLMにデータ取得させて、それをもとにいろいろ仕事をしてもらいたいと思います。何をさせようか?と考えた際に、比較的簡単にWebAPIが用意でき、面白いデータが取れそうな、私のセルフホスト家計簿とつないでみることにしました。

家計簿の期間指定明細検索APIをMCPサーバー経由で呼び出し、LLMに家計の収支情報を分析してもらう、というテーマを実際に試してみます。

実際にやってみた

手順1:最小構成のMCPサーバーの準備

下記エントリを参考に、最小構成のMCPサーバーを準備します。今回はローカルで動かす前提なのでより簡易なstandard input/output(標準入出力/stdio)を使った実装を試します。

PJフォルダ上で、下記nodeパッケージをインストールします。

npm i @modelcontextprotocol/sdk zod

次に参考エントリに記載のあるMCPサーバーのコードを記述したのち、inspector機能を利用しMCPサーバーの動作確認をします。

npx -y @modelcontextprotocol/inspector

手順2:WebAPI callに必要な追加パッケージのインストール

上記サンプルが動くことを確認出来たら、次にWebAPI call処理を追記していきます。最初に、axiosとdotenvをインストールします。

npm i axios dotenv

この時、dotenvのバージョンが 17.2.1 のように17系のバージョンがインストールされた場合は、16系に入れなおします。理由をちゃんと理解できていませんが、17系は実際には dotenv ではなく dotenvx というものがインストールされるらしく、これをdotenvとして利用すると、後続のClaude Desktopに設定する際にエラーになりました。

確認方法は、package.json を開き、"dotenv" 部分の記述をみること。16系になっていればOK。なっていなければ下記のように書き直した上で、npm installを実行し、変更後のバージョンを再インストールしてください。

{
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.17.3",
    "axios": "^1.11.0",
    "dotenv": "^16.4.5",
    "zod": "^3.25.76"
  }
}

手順3:WebAPI call処理の記述

WebAPIをcallし、結果を返却する処理を記述します。ここは各々の呼び出すWebAPIに合わせた記述をしてください。私のケースの場合、最初にログイン認証のAPIをcallして認証トークンを取得したのち、引数(検索対象の年/月/日)と合わせて家計簿の期間指定明細検索APIをcallしています。

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import dotenv from "dotenv";
dotenv.config();
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import axios from "axios";

const server = new McpServer({
  name: "Node Stdio MCP",
  version: "1.0.0",
});

server.tool(
  "fennel_docs",
  "家計簿システムの伝票検索",
  { year: z.string(), month: z.string().optional(), day: z.string().optional() },
  async ({ year, month, day }) => {
    // ログイン認証
    const auth = await GetAuth();

    // 家計簿検索
    const hfaDocs = await SearchHfaDocs(auth.idToken, year, month, day);

    // 検索結果明細を返却
    return {content: [{ type: "text", text: JSON.stringify(hfaDocs.res.entries) }]}
  }
);

// ログイン認証
async function GetAuth()
{
  const apiKey = process.env.FENNEL_API_KEY;
  const user = process.env.FENNEL_USER;
  const pw = process.env.FENNEL_PASSWORD;
  const url = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
  const reqUlr = url + "?key=" + apiKey;

  try{
    const response = await axios.post(reqUlr, {email:user, password:pw, returnSecureToken:true}, {headers:{'Content-Type': 'application/json'}});
    return response.data;
  }
  catch(err){
    console.error("認証エラー:", err);
    return { error: "認証失敗" };

  }
}

// 家計簿検索
async function SearchHfaDocs(token, year, month, day)
{
  const appcode = process.env.FENNEL_APPCODE;
  const hfa_gp = process.env.FENNEL_HFAGP;
  const url = process.env.FENNEL_HFADOC_API;
  
  const reqYear = year;
  const reqMonth = (month == null || month == undefined) ? "null" : month;
  const reqDay = (day == null || day == undefined) ? "null" : day;
  const reqUrl = url + hfa_gp + "/" + reqYear + "/" + reqMonth + "/" + reqDay + "?app=" + appcode;
  try{
    const response = await axios.get(reqUrl, {headers:{'Authorization' : 'Bearer ' + token}});
    return response.data;
  }
  catch(err){
    console.error("API実行エラー:", err);
    return { error: "API実行エラー" };
  }
}


function AddTest(a, b)
{
  return String(a + b + 20);
}

const transport = new StdioServerTransport();
await server.connect(transport);

改めてinspectorを起動、年/月/日を指定し、対象の明細が取得できることを確認しました。

手順4:Claude DesktopにMCPサーバーを設定

続いて、いま作成したMCPサーバーをClaude Desktopに設定し、Claude君から呼び出しできるようにします。手順は下記エントリを参考にします。

Claude Desktopの設定から、開発者メニューを開き、ローカルMCPサーバーの設定ファイル(claude_desktop_config.json)を開きます。ここに設定を記述します。

{
  "mcpServers": {
    "NodeLocalMcp(←任意の名前)" : {
      "command": "node",
      "args": [
        "C:\\Users\\shunya\\Documents\\dev\\MCP\\NodeMCP\\mcpMain.js(←作成したMCPサーバーのjsファイルのフルパス)"
      ],
      "env":{
        "FENNEL_API_KEY" : "xxxx(↓環境変数、必要に応じて)",
        "FENNEL_USER" : "xxxx@example.com",
        "FENNEL_PASSWORD" : "xxxxx",
        "FENNEL_APPCODE" : "xxxx",
        "FENNEL_HFAGP" : "xxx",
        "FENNEL_HFADOC_API" : "xxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

記述できたら、設定ファイルを閉じて、Claude Desktopを再起動します(バックグラウンドで動いているので注意)。再度、設定を開いてMCPサーバーが表示・running状態になっていればOK。

もしエラーが発生していたら、エラー内容をAIに渡して相談しましょう。

手順5:実際に使ってみる

これでClaudeが必要に応じてMCPサーバー経由でデータを取得してくれるはずなので、実際に使ってみます。

こんなプロンプトを投げてみました。

2023年から2025年の支出レポートを作成・比較してください

すると、Claudeは、2023/7・2024/7・2025/7 の3回データ取得を実行し、その結果を踏まえたレポートを作ってくれました、素晴らしい。
(数字は生々しいので、黒塗りにしています。。。)

従来、エクスポートしたデータをExcelに貼り付けてグリグリする必要のあった分析が一瞬で終わりました。恐ろしいレベルでもある。

ハマりポイント

いくつかinspector上での検証ではOKだったものの、Claudeから実行しようとするとエラーになる躓きポイントがあったので、記録しておきます。

その1. dotenvのバージョン指定

上記でも記載しましたが、dotenvのバージョンが17系を指定されてしまうと動かない事象にぶち当たりました。こちらはdotenvのバージョンを16系にしたところ解消。

その2. envファイルに環境変数記載しても動かない

inspector上での検証では環境変数(APIキーやユーザ・パスワード情報等)は.envファイルに記述することで機能しましたが、Claude上で動かした際はこれを上手く参照できていないようでした。前述の通り環境変数値をclaude_desktop_config.json に記述することで解消しました。

その3. 返却値はJSONではなく文字列にする

APIレスポンス情報をそのままの形(JSON)で返却したところ、これもClaude上でエラーとなりました。そのためJSON.stringify(レスポンスオブジェクト)を利用し文字列形式にして返してあげることで解消しました。

終わりに、MCPサーバー活用の展望

MCPサーバーを介することで、LLMが直接データソースにアクセスし必要な情報を取得した上でユーザーからリクエストされたタスクをこなすことを体験することができました。LLMにデータを食べさせるのではなく、LLMが食べたいものを選んで食べる世界。食べてよいものとダメなものの分別さえ付けさせることができれば(データアクセス権限の設定)かなり有用だと実感しました。

今後、どんな場面で使えるのだろう?と考えた際、今回 私が実施したような分析が企業の情報系システムでもできるのでは?とふと思いました。

例えば、基幹システムからEAI/ETLツールで抽出したデータをBIツールで分析するケースがありますが、 同じ考え方で、EAI/ETLツールがMCPサーバーとしてふるまい、LLMからデータ抽出できれば、LLMによる分析のinputとして有用ではと思いました(基幹系のシステム導入のお仕事している身として)。軽く調べた限りよくあるEAI製品のMCPサーバー対応はまだされていないようですが、今後広がっていくのではないかな、と個人的に思いました。

  • Home
  • /
  • Posts
  • /
  • Node.jsでMCPサーバーを作りWebAPIをcallしてみた(Claude連携)
Tech-TIPS

Comments

© 2025 shunya_wisteria