はじめに
昨今 AI エージェントの流れが本当に激しいですね。
特に最近は MCP 関連の話題に溢れているなと感じているところです。
先週末には Azure Functions で MCP サーバーを作成できるようになりましたね。
私は普段 Azure を触ることが多く、このニュースがきっかけで MCP を触ろうと思ったので、非常に大きな出来事でした。
「MCP」とは「Model Context Protocol」の略で、生成 AI モデルに文脈情報を渡しやすくするための規格です。生成 AI 界の USB-C とも言われていますね。
すでに見られている方も多いかと思いますが、みのるんさんが以下のスライドで大変わかりやすくまとめてくださってます。私もこれで勉強しましたので、非常におすすめです!
ずっと MCP や AI エージェントを触りたいと思っては実行に移せていなかったのですが、今週からようやく触れることができましたので、備忘録も兼ねながら少しずつ記事にしたいと思います。
今回やりたいこと
今回は以下のようなことをやっていこうと思います。
- (サブ)MCP サーバーを Azure Functions で作成する
- (メイン)Vercel AI SDK を MCP クライアントツールとして使い、様々な MCP サーバーを呼び出してみる
Vercel AI SDK は、3/21 にバージョン 4.2 がリリースされ、MCP がサポートされました。
実験的な機能もまだまだございますが、これにより Vercel AI SDK を利用する方は、MCP のクライアント機能を簡単にアプリケーションに組み込み、MCP サーバーが提供するツールを活用できることになります。
今回実装したアプリケーションは GitHub で公開しております。
突貫で作ったので参考にする程度に見ていただけると幸いです...
今回 Vercel AI SDK で作成した MCP クライアントから、自作した Azure Functions の MCP サーバーは実行できておりません。
GitHub Copilot や MCP Inspector からは接続できているのですが、アプリケーションからだと実行時にフリーズしてしまいます...
もしその部分が今後完成したらこちらの記事も更新したいと思っておりますので、この点についてはご容赦いただければと思います。
完成したもの
今回完成したものは以下のようなチャットアプリとなります。
メッセージを送信し、AI から返事が返ってくるシンプルなアプリケーションです。今回はこのアプリケーションに MCP クライアントを構築しています。
以下の画像だと、playwright-mcp を呼び出して特定の Web サイトの情報を抽出させて AI に回答させています。
MCP サーバーを Azure Functions で作成する
本題に入る前に、Azure Functions で MCP サーバーを作成したので、こちらを紹介します。
すでにたくさんの方が記事にされていますが、私も現在時刻を取得する簡単な MCP サーバーを作成します。
Azure Functions プロジェクトの作成 & 各種インストール
まずは Azure Functions のプロジェクトを作成します。公式ドキュメントに手順が記載されておりますので、それに沿って作成します。
今回私は Visual Studio Code で作成し、言語は C#を選択しています。
MCP サーバーは .NET8 以降でないと動作しないため、それより下位のバージョンは使用できません。
また、プロジェクト作成時にどのトリガーで作るか迷われる方もいらっしゃるかと思いますが、ひとまずHTTP トリガーで作っておけば特段問題はございません。
続いて、MCP 拡張機能をインストールします。現在はプレビュー版となっています。
dotnet add package Microsoft.Azure.Functions.Worker.Extensions.Mcp --version 1.0.0-preview.1
MCP 拡張機能は Blob ストレージを使用するため、Azurite をローカル開発用にインストールします。Visual Studio Code の拡張機能から Azurite をインストールし、コマンドパレットから「Azurite: Start」で起動します。
また、local.settings.json
を以下のように書き換えます。ポイントは AzureWebJobsStorage
の部分です。
{
"IsEncrypted": false,
"Values": {
+ "AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
また、ここで <プロジェクト名>.csproj
の一部を変更します。
私の環境だと、公式で作成した Azure Functions のテンプレートの Microsoft.Azure.Functions.Worker.Sdk
の v2.0.0
だと動作しませんでした。そのため、以下のようにバージョンを v2.0.2
にあげる必要があります。
この部分については他の方の記事には存在しなかったため、動作環境起因の可能性があります。
- <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.0" />
+ <PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="2.0.2" />
Azure Functions MCP サーバーの開発
続いて、現在時刻を取得するための MCP ツールを作成します。
namespace AzureFunctionsMcp
{
public class MyMcpTools
{
[Function(nameof(GetCurrentTime))]
public string GetCurrentTime(
[McpToolTrigger("getcurrenttime", "Gets the current time. If no timezone is specified, the tool will return the time in UTC.")] ToolInvocationContext context,
[McpToolProperty("timezone", "string", "The name of the timezone.")] string timezone = "UTC"
)
{
try
{
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timezone);
DateTime currentTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZoneInfo);
var response = new
{
timezone = timeZoneInfo.StandardName,
time = currentTime.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture),
displayName = timeZoneInfo.DisplayName
};
return JsonSerializer.Serialize(response);
}
catch (TimeZoneNotFoundException)
{
return $"The timezone '{timezone}' was not found.";
}
catch (InvalidTimeZoneException)
{
return $"The timezone '{timezone}' is invalid.";
}
catch
{
return "Could not get the current time.";
}
}
}
}
次に、分離ワーカーモデルのエントリーポイントとなる Program.cs
に MCP ツールを登録します。
var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();
+ builder.EnableMcpToolMetadata();
+ builder.ConfigureMcpTool(nameof(MyMcpTools.GetCurrentTime))
+ .WithProperty("timezone", "string", "The timezone.");
builder.Build().Run();
MCP サーバーの起動
実行環境の準備が整いましたら、MCP サーバーを起動します。以下のコマンドで関数を起動します。
func start
しばらくすると以下のような出力結果となれば起動成功です。MCP サーバーは次のアドレスでアクセス可能になります。
http://localhost:7071/runtime/webhooks/mcp/sse
Functions:
GetCurrentTime: mcpToolTrigger
きちんと MCP サーバーが動作しているのか確認したい場合は、GitHub Copilot Agent から呼び出すか、以下の MCP Inspector を利用すると良いかと思います。
https://github.com/modelcontextprotocol/inspector
npx @modelcontextprotocol/inspector
MCP サーバーを Azure にデプロイ
それでは、作成した MCP サーバーを Azure にデプロイします。Azure ポータルで Functions を作成下のち、以下のコマンドを実行します。
MCP サーバーの Azure Functions は現在 Windows で動作しないという情報が散見されますので、作成する際は Linux で作成しましょう。
https://github.com/Azure-Samples/remote-mcp-functions-dotnet/issues/8
func azure functionapp publish <your-function-app-name>
Azure Functions で起動後は次のアドレスでアクセス可能になります。
https://<your-function-app-name>.azurewebsites.net/runtime/webhooks/mcp/sse
Azure Functions の MCP サーバーを実行する際は、mcp_extension
システムキーが必要になります。「アプリ キー」のタブから忘れずに取得しておいてください。
これにて、前半の MCP サーバーを Azure Functions で作成するフェーズは終了です。
Vercel AI SDK を MCP クライアントツールとして使い、様々な MCP サーバーを呼び出してみる
それではメインの方になります。MCP の話題だとどうしてもサーバー側の話が多いかと思いますが、それを呼び出すためのクライアントが今後はどんどん出てくると思っています。
プロジェクトのセットアップ & 各種インストール
Vercel ということで、Next.js でセットアップを行います。
詳しい手順は以下の公式ドキュメントを参照ください。
npx create-next-app@latest
次に、今回は Vercel AI SDK を OpenAI で使用するため、必要なライブラリをインストールしておきます。
npm i ai @ai-sdk/react @ai-sdk/openai zod
OpenAI で LLM を利用するためには API キーが必要です。事前に以下の URL から API キーを取得しておきます。選択するモデルにもよりますが、LLM の使用には料金が発生する場合があるため利用する際はご注意ください。
API キーを取得できたら、.env
に環境変数として入れておきます。Vercel AI SDK の OpenAI Provider を利用するため、デフォルトの OPENAI_API_KEY
というキーで値を入れておきます。
くれぐれも API キーは GitHub や他クラウドに Upload しないでください。
OPENAI_API_KEY=<取得したキー>
利用する MCP サーバーの起動
ここまででプロジェクトのセットアップと各種インストールが終了したので、利用する MCP サーバーを起動しておきます。
今回起動する MCP サーバーは以下の 3 つとなります。
- MCP サーバーを Azure Functions で作成するで作成した Azure Functions:現在時刻を取得する
- AWS MCPのAWS Documentation MCP Server:AWS のドキュメントにアクセスし、推奨する回答を返却する
- Playwright MCP:ブラウザにアクセスし、情報を取得する
AWS Documentation MCP Server の利用
AWS Documentation MCP Server は公開されているものになるため、誰でも利用可能です。ですが、利用するためには以下のコマンドから uv
と Python3.10
以上をインストールしておく必要があります。詳細は公式ドキュメントを参照ください。
curl -LsSf https://astral.sh/uv/install.sh | sh
uv python install 3.10
これだけで AWS Documentation MCP Server を利用する準備が整いました。簡単ですね!!
Playwright MCP サーバーの起動
続いて、Playwright MCP サーバーの起動します。今回はローカルで起動した Playwright MCP サーバーと SSE で通信することとします。
まず、以下のコマンドを実行して Playwright MCP サーバーを起動します。コマンドの詳細は公式ページを参照ください。
npx @playwright/mcp@latest --port 8931 --headless
起動すると http://localhost:8931/sse
が SSE のエンドポイントとなります。
ルートハンドラーの開発
MCP サーバーが起動できたため、それに接続するルートハンドラーを作成します。
今回は Vercel AI SDK の useChat
を利用するため、デフォルトのエンドポイントである api/chat
に相当する app/api/chat/route.ts
を作成します。
ここでポイントとなるのが以下の点です。
- MCP クライアントはサーバーとの接続方法(トランスポート)として、
SSE (Server-Sent Events)
、stdio
、カスタムトランスポート
を提供しているため、使用する MCP サーバーによって使い分ける -
複数の MCP Tools を登録するように
tools
オブジェクトを構成する - ストリーミング通信は Vercel AI SDK の
streamText
を利用する - ストリーミング応答が完了したら、MCP クライアントの接続を閉じる
import { streamText } from 'ai';
import { experimental_createMCPClient as createMCPClient } from "ai";
import { Experimental_StdioMCPTransport as StdioMCPTransport } from 'ai/mcp-stdio';
import { openai } from '@ai-sdk/openai';
export async function POST(req: Request) {
try {
// AWS MCP クライアントの作成
const awsMcpClient = await createMCPClient({
transport: new StdioMCPTransport({
command: "uvx",
args: ["awslabs.aws-documentation-mcp-server@latest"],
env: {
FASTMCP_LOG_LEVEL: "ERROR"
},
}),
})
// Azure Functions MCP クライアントの作成
const azMcpClient = await createMCPClient({
transport: {
type: "sse",
url: "https://<your-function-app-name>.azurewebsites.net/runtime/webhooks/mcp/sse",
headers: {
"x-functions-key": process.env.AZURE_FUNCTIONS_KEY || "",
},
},
});
// Playwright MCP クライアントの作成
const playwrightMcpClient = await createMCPClient({
transport: {
type: "sse",
url: "http://localhost:8931/sse"
},
});
const { messages } = await req.json();
// MCP サーバーからツール定義を取得
const awsMcpTool = await awsMcpClient.tools();
const azMcpTool = await azMcpClient.tools();
const playwrightMcpTool = await playwrightMcpClient.tools();
const tools = {
...awsMcpTool,
...azMcpTool,
...playwrightMcpTool,
}
// Vercel AI SDK の streamText 関数を使用して LLM とのストリーミング通信を開始
const result = streamText({
model: openai('gpt-4o'),
messages,
tools,
onFinish: async () => {
// ストリーミング応答が完了したら、MCP クライアントの接続を閉じる
await awsMcpClient.close();
await azMcpClient.close();
await playwrightMcpClient.close();
},
});
return result.toDataStreamResponse();
} catch (error) {
console.error('Error: ', error);
}
}
チャット画面の開発
続いて、上記のルートハンドラーを呼び出すためのチャット画面を開発します。今回は他に画面はないため、app/page.tsx
を編集します。
ポイントとなる点は以下の点です。
- インタラクティブな画面のため、
use client
でクライアントサイドで実行する -
@ai-sdk/react
のuseChat
フックのhandleSubmit
を実行することでルートハンドラーを呼び出す
'use client';
import { useChat } from '@ai-sdk/react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, status } =
useChat();
return (
<div className='flex flex-col items-center w-full min-h-screen bg-gray-100 dark:bg-gray-900'>
<div className='flex flex-col w-full max-w-5xl p-6 mt-12 bg-white rounded-lg shadow-lg dark:bg-gray-800'>
<h1 className='text-2xl font-bold text-center text-gray-800 dark:text-gray-100'>
Chat with Open AI
</h1>
<div className='flex flex-col gap-4 mt-4 overflow-y-auto max-h-[40rem]'>
{messages
.filter((message) =>
message.parts.some((part) => part.type === 'text' && part.text.trim() !== '')
)
.map((message) => (
<div
key={message.id}
className={`p-3 rounded-lg ${
message.role === 'user'
? 'bg-blue-100 text-blue-900 self-end'
: 'bg-gray-200 text-gray-900 self-start'
}`}
>
<span className='font-semibold'>
{message.role === 'user' ? 'User: ' : 'AI: '}
</span>
{message.parts.map((part, i) => {
switch (part.type) {
case 'text':
return (
<span key={`${message.id}-${i}`} className='block'>
{part.text}
</span>
);
}
})}
</div>
))}
</div>
<form
onSubmit={handleSubmit}
className='flex flex-col gap-2 mt-4'
>
<input
type='text'
value={input}
onChange={handleInputChange}
className='w-full p-3 border border-gray-300 rounded-lg dark:bg-gray-700 dark:text-gray-100 dark:border-gray-600'
placeholder='メッセージを入力してください'
disabled={status !== 'ready'}
/>
<button
type='submit'
className='px-4 py-2 text-white bg-blue-500 rounded-lg hover:bg-blue-600 disabled:bg-gray-400 disabled:cursor-not-allowed'
disabled={status !== 'ready'}
>
{status !== 'ready' ? '・・・' : '送信'}
</button>
</form>
</div>
</div>
);
}
これで実装の全てが完了しました!お疲れ様でした。
Next.js サーバーの起動
実装が完了したため、以下のコマンドで起動します。起動すると、http://localhost:3000
で画面を表示することができます。
npm run dev
試しに
「https://news.yahoo.co.jp/ にアクセスして、今日のニュースでインパクトのあるものを 3 つ挙げてください」
と入力すると、Playwright MCP が呼び出されその Web 情報を出力してくれます。
本当に MCP サーバーが呼ばれているのか分かりにくいため、AWS Documentation MCP でも試してみます。
「AWS Lambda とはどんなサービスですか?」
と質問し、開発者ツールを確認したところ、AWS Documentation MCP の search_documentation
が呼ばれていることが確認できました!!
冒頭にも記載しましたが、Azure Functions の MCP サーバーに接続しようとすると、MCP クライアント初期化時に止まってしまい、実行できていません。
ログからは接続できているように見えるため、設定不備はなさそうですが、この点は今後の課題とし、改善できたらこの記事を更新する予定です。
おわりに
いかがでしたでしょうか。今回は Azure Functions で MCP サーバーを作成した話と、Vercel AI SDK で MCP クライアントを構築し、MCP サーバーを呼び出す方法を紹介しました。
AI エージェントが台頭し、技術の進化が留まるところを知りません。
どうしても触ってみないとキャッチアップが難しいものもありますが、この波に乗り遅れないよう頑張りましょう!
またアップデートがあればお伝えしようと思います。最後まで読んでいただきありがとうございました。