Vercel AI SDK でシンプルな MCPクライアント・MCPホストの機能を作る（自作MCPサーバーと組み合わせる）

## はじめに

直近で [TypeScript版の公式SDK](https://github.com/modelcontextprotocol/typescript-sdk) での作成を試した MCPクライアントの機能と、さらに MCPホストの機能にあたる部分を含めた簡単な実装を、「[Vercel の AI SDK](https://ai-sdk.dev/)」を使った別実装でやってみます。

●TypeScript版の公式SDK で自作MCPクライアントを試す（自作MCPサーバーのツール利用） - Qiita
　https://qiita.com/youtoy/items/8d6fc082e8c4b23dcd21

その際、自作MCPサーバーは過去に作成済みのものを利用することとし、今回は以下に当たる機能を実装します。

- ユーザー入力を受け取る（※ 今回は、コード内でユーザーのプロンプトを記載）
- 設定された MCPサーバーとやりとりをする
- LLM で、ユーザーの入力と MCPサーバーのツールを使った処理による結果を得る
- 最終的に得られた結果を出力する


### Vercel AI SDK で MCP を扱う話

Vercel AI SDK に関して、以下の記事に書いているように OpenAI のモデルとの組み合わせを試したことがありました。

●Vercel AI SDK と OpenAI の o3 との組み合わせを試す - Qiita
　https://qiita.com/youtoy/items/538c16b5c1d5ca588eb8

その内容を試すために情報を調べていた時に、Vercel AI SDK が MCP を扱えるようになった話を見かけていました。具体的には以下の内容です。

●AI SDK 4.2 - Vercel
　https://vercel.com/blog/ai-sdk-4-2#model-context-protocol-(mcp)-clients

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/91d6bd70-a0e5-43ec-a692-89d7086ea909.png)

そこでは以下のようなコードが掲載されていたりもしました。

```javascript
import { experimental_createMCPClient as createMCPClient } from 'ai';
import { openai } from '@ai-sdk/openai';

const mcpClient = await createMCPClient({
  transport: {
    type: 'sse',
    url: 'https://my-server.com/sse',
  },
});

const response = await generateText({
  model: openai('gpt-4o'),
  tools: await mcpClient.tools(), // use MCP tools
  prompt: 'Find products under $100',
});
```

これを見ると、OpenAI のモデルなどを使い、「ユーザーの入力を、自作MCPサーバーのツールで処理してもらって結果を得る」ということを実現できそうです。


### さらに公式情報を見てみる

さらに他のページの公式情報を見てみます。[以下のページ](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#initializing-an-mcp-client)を見ると、自分が自作MCPサーバーで使っている stdio も扱えそうです（※ MCPサーバーと MCPクライアントの間の通信に関する部分）。

●AI SDK Core: Tool Calling
　https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#mcp-tools

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/068a6120-7927-4e63-8e1f-3853431ebe42.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3e5aad17-8c7d-4ce2-b24b-e61ad94dadc0.png)


## Vercel AI SDK で MCPクライアント・MCPホストの機能を作成する

それでは、Vercel AI SDK で MCPクライアント・MCPホストの機能を作ってみます。


### 下準備

下準備として、[前回の記事](https://qiita.com/youtoy/items/538c16b5c1d5ca588eb8)でも使った以下 2つをインストールします。

●ai - npm
　https://www.npmjs.com/package/ai
●@ai-sdk/openai - npm
　https://www.npmjs.com/package/@ai-sdk/openai


インストール用のコマンドは以下のとおりです。

```zsh
npm install ai @ai-sdk/openai
```


### 実装したコード


#### 実験用のもの

まずは試しに以下のコードを作成・実行してみて、自作MCPサーバーで実装した内容を呼び出せるかを確認してみました。


```javascript
import { experimental_createMCPClient as createMCPClient } from "ai";
import { Experimental_StdioMCPTransport as StdioMCPTransport } from "ai/mcp-stdio";

const mcpClient = await createMCPClient({
  transport: new StdioMCPTransport({
    command: "node",
        args: ["【実装した自作MCPサーバーのファイルへのフルパス】"],
  }),
});

const tools = await mcpClient.tools();
console.dir(tools, { depth: null });

mcpClient.close();
```

なお、上記で呼び出している自作MCPサーバーの実装内容（※ 過去に記事にも書いていた内容）は以下のとおりです。


```javascript:自作MCPサーバーの実装
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

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

server.tool(
  "add_test",
  "与えられた数値の足し算をする（さらに10を足す）",
  { a: z.number(), b: z.number() },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b + 10) }],
  })
);

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

上記の Vercel AI SDK を使った処理を実行したところ、以下のように自作MCPサーバーからの情報を得られていることが分かりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/724b3c40-72fb-4980-b569-d63bebb479c9.png)


#### MCPクライアント・MCPホストの機能に関する内容

さらに以下のコードも参考にして、実験的に作ったコードに修正を加えて、コードを仕上げていきます。


※「MCP Tools」の部分
●Node: Model Context Protocol (MCP) Tools
　https://ai-sdk.dev/cookbook/node/mcp-tools#mcp-tools

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/fd284171-5236-4069-90fe-c7821ec3d6c2.png)


最終的に作成したコードは以下のとおりです。

```javascript:app.js
import {
  experimental_createMCPClient as createMCPClient,
  generateText,
} from "ai";
import { Experimental_StdioMCPTransport as StdioMCPTransport } from "ai/mcp-stdio";
import { openai } from "@ai-sdk/openai";

let mcpClient;

try {
  mcpClient = await createMCPClient({
    transport: new StdioMCPTransport({
      command: "node",
      args: ["【実装した自作MCPサーバーのファイルへのフルパス】"],
    }),
  });
  const tools = await mcpClient.tools();

  const inputText = "ツールで10と20を足して";
  console.log(`プロンプト： ${inputText}`);

  const response = await generateText({
    model: openai("gpt-4o"),
    tools,
    messages: [
      {
        role: "user",
        content: inputText,
      },
    ],
    maxSteps: 2,
  });
  console.log(`レスポンス： ${response.text}`);
} catch (error) {
  console.error(error);
} finally {
  await mcpClient.close();
}
```


環境変数 OPENAI_API_KEY に OpenAI の APIキーをセットし、上記を nodeコマンドで実行します。

その結果、以下のように想定通りの出力を得ることができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/92346caf-8473-4f18-9fe0-43c31a4c8fe9.png)


#### 最終版のコードに関する補足

それとコードに関する補足ですが、 `generateText()` の部分の中で `maxSteps: 2` というオプションを追加しています（2以上の数字にしても良いです）。これは当初、maxSteps を指定していなかった時に最終出力が得られず、対応した部分です。

もう少し補足すると、以下の公式ドキュメントの記載で `generateText` や `streamText` のデフォルトの挙動では、 `maxSteps: 1` を設定したのと同じ 1ステップの処理になるようでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/cee2a1ba-9342-4764-b870-8c1c0763defe.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/d939c1f0-61fe-470a-939c-d699c2c41394.png)


このため当初は、自作MCPサーバーのツールを利用したステップで処理が終わり、その結果を使った最終出力を得るステップが実行されなかったのだと思われます。


## おわりに

今回、Vercel AI SDK を使うことで 自作MCPサーバーを使った処理で、OpenAI のモデルと組み合わせた処理を簡単に実現することができました。

今後さらに、Vercel AI SDK でできることを色々と試していければと思っています。



### 【追記1】

今回やってみた内容を、Mastra でもやってみました。

●Mastra でシンプルな MCPクライアント・MCPホストの機能を作って Node.js で実行（自作MCPサーバーと組み合わせる） - Qiita
　https://qiita.com/youtoy/items/062165c2918cfdbc5590


### 【追記2】

以下に色々なサンプルがあるようなので、これらも見ていければと思いました。


●ai/examples at main · vercel/ai
　https://github.com/vercel/ai/tree/main/examples