はじめに
ちょっと前に、主に公式ドキュメントを見ながら実装を試した Vercel AI SDK + MCP の話です。
●Vercel AI SDK でシンプルな MCPクライアント・MCPホストの機能を作る(自作MCPサーバーと組み合わせる) - Qiita
https://qiita.com/youtoy/items/22fa3faaacd7ae6dd958
今回のお試し
今回は以下の公式サンプルをベースとして利用し、内容を少し書きかえたりなどして使います。
●ai/examples/mcp/src/stdio at main · vercel/ai
https://github.com/vercel/ai/tree/main/examples/mcp/src/stdio

実際に試してみる
公式の情報を見ると、pnpm を使った手順で試せるようです。

今回は上記の pnpm は使わずにやってみました。
必要なパッケージなどを確認
公式サンプルのコードを流用して進めていくのですが、まずは必要なパッケージ・環境などを確認してみることにします。
コードの冒頭を見てみる
コードの冒頭を見て、必要なパッケージ・環境などを確認します。以下は、「client.ts」と「server.ts」のリンクと、冒頭部分のキャプチャです。


上記を見ると、以下をインストールすれば良さそうです。また OpenAI の APIキーをセットする必要もありそうです。
- @ai-sdk/openai
- @modelcontextprotocol/sdk
- ai
それと dotenv がインポートされていますが、ざっと見た感じ、OpenAI の APIキーを OPENAI_API_KEY という名前でセットしておく形で良さそうです。
パッケージのインストール
必要なパッケージを、以下でインストールします。
npm i @ai-sdk/openai @modelcontextprotocol/sdk ai
この後は、先ほどの公式サンプルを書きかえていきます。
コードを書きかえる
元の内容は TypeScript でしたが、今回 JavaScript にして試してみました。変更後の実装内容は、この後に示します。なお、この後に出てくるサーバー側・クライアント側のファイル(server.mjs、client.mjs)は、同じフォルダ内に置く構成にしています。
サーバー側
以下はサーバー側です。こちらは、基本的に元のものを TypeScript から JavaScript にしただけという内容です。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const POKE_API_BASE = "https://pokeapi.co/api/v2";
const server = new McpServer({
name: "pokemon",
version: "1.0.0",
});
server.tool(
"get-pokemon",
"Get Pokemon details by name",
{ name: z.string() },
async ({ name }) => {
const path = `/pokemon/${name.toLowerCase()}`;
const pokemon = await makePokeApiRequest(path);
if (!pokemon) {
return {
content: [
{
type: "text",
text: "Failed to retrieve Pokemon data",
},
],
};
}
return {
content: [
{
type: "text",
text: formatPokemonData(pokemon),
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("Pokemon MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
async function makePokeApiRequest(path) {
try {
const url = `${POKE_API_BASE}${path}`;
const response = await fetch(url);
if (!response.ok) throw new Error(`HTTP Error Status: ${response.status}`);
return await response.json();
} catch (error) {
console.error("[ERROR] Failed to make PokeAPI request:", error);
return null;
}
}
function formatPokemonData(pokemon) {
return [
`Name: ${pokemon.name}`,
`Abilities: ${pokemon.abilities
.map(({ ability }) => ability.name)
.join(", ")}`,
].join("\n");
}
クライアント側
以下はサーバー側です。こちらは、元のものを TypeScript から JavaScript にした後、何ヵ所か書きかえたところがあります。
※ その書きかえを行った後に、「別途書きかえた部分・無効にした部分」をコメントアウトして残しています
import { openai } from "@ai-sdk/openai";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { experimental_createMCPClient, generateText } from "ai";
// import "dotenv/config";
import { z } from "zod";
async function main() {
let mcpClient;
try {
const stdioTransport = new StdioClientTransport({
// command: "node",
command: "【nodeコマンドのフルパス】",
// args: ["src/stdio/dist/server.js"],
args: ["./server.mjs"],
// env: { FOO: "bar" },
});
mcpClient = await experimental_createMCPClient({
transport: stdioTransport,
});
const { text: answer } = await generateText({
model: openai("gpt-4o-mini", { structuredOutputs: true }),
tools: await mcpClient.tools({
schemas: {
"get-pokemon": {
parameters: z.object({ name: z.string() }),
},
},
}),
maxSteps: 10,
onStepFinish: async ({ toolResults }) => {
console.log(`STEP RESULTS: ${JSON.stringify(toolResults, null, 2)}`);
},
system: "You are an expert in Pokemon",
prompt:
// "Which Pokemon could best defeat Feebas? Choose one and share details about it.",
"ヒンバスを最も効果的に倒せるポケモンはどれですか?1匹選んで、その詳細を教えてください。回答は日本語で。",
});
console.log(`FINAL ANSWER: ${answer}`);
} finally {
await mcpClient?.close();
}
}
main().catch(console.error);
手を加えた部分は、不要そうな部分をコメントアウトしたのと、それ以外は以下の部分です。
- プロンプトを日本語に変更
- node コマンドのフルパスを記載
- 【書きかえた理由】
command: "node"
にしていた時、nodeコマンドが見つからないというエラーが出たため
- 【書きかえた理由】
- server.mjs のパスを変更
- 【書きかえた背景】 client.mjs と server.js を同じフォルダに置く構成にしていたため
コードの実行結果
上記のコードを実行します。実行するのはクライアント側で、実行した結果は以下のとおりです。
最終的出力で、回答を得ることができました。さらに「ヒンバス ⇒ ギャラドス」と変えて、再度処理を実行してみました。
それで得られた結果は以下のとおりです。
今回も最終出力の部分で、回答を得ることができました。
おわりに
Vercel AI SDK で MCP を使う公式サンプルを見かけたので、それを試してみました。
本当はこの続きで、以下の「Filesystem MCP Server」をサーバー側にした内容を試そうとしたのですが、かなり苦戦してしまったので、いったんここまでの内容を公開しておきます。
●servers/src/filesystem at main · modelcontextprotocol/servers
https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem