🎃
簡単なMCPサーバーを作成してみよう
はじめに
MCPサーバーを結構簡単に作成できることを学んだので、自分の環境でも簡単に実装してみる。
構築するぞ
こんな感じで必要なパッケージを追加していく。
$ pnpm init
$ pnpm add @modelcontextprotocol/sdk zod @anthropic-ai/sdk dotenv
$ pnpm add -D @types/node typescript
そのあとこのようなディレクトリ構成で作成していきます。
├── README.md
├── package.json
├── pnpm-lock.yaml
├── src
│ └── index.ts // ここに処理を書いていく
└── tsconfig.json
tsconfig.json
はこんな感じ。
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"lib": ["ES2020"],
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "build"]
}
定義した処理はこんな感じ。
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import Anthropic from "@anthropic-ai/sdk";
import dotenv from "dotenv";
// 環境変数の読み込み
dotenv.config();
// 環境変数の検証
if (!process.env.ANTHROPIC_API_KEY) {
console.error("エラー: ANTHROPIC_API_KEY が設定されていません。");
process.exit(1);
}
// デフォルト設定
const DEFAULT_MODEL = "claude-3-sonnet-20240229"; // より経済的なモデル
const DEFAULT_MAX_TOKENS = 1000;
// Claude API クライアントの初期化
const anthropic = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY,
});
// Claude APIを使用してメッセージを送信し、テキスト応答を取得する関数
async function askClaude(
prompt: string,
model: string = DEFAULT_MODEL,
maxTokens: number = DEFAULT_MAX_TOKENS
): Promise<string> {
try {
const message = await anthropic.messages.create({
model: model,
max_tokens: maxTokens,
messages: [{ role: "user", content: prompt }],
});
// メッセージの内容を抽出
if (message.content.length > 0) {
if (message.content[0].type === "text") {
return message.content[0].text;
} else {
console.warn("警告: 非テキスト形式のレスポンスを受信しました");
return "非テキスト形式のレスポンスを受信しました";
}
}
return "レスポンスにコンテンツがありませんでした";
} catch (error) {
console.error("Claude API エラー:", error);
throw error; // エラーを上位に伝播させる
}
}
const server = new McpServer({
name: "sugoi-mcp-server",
version: "1.0.0",
});
// Claude APIを使用して応答を生成するツール
server.tool(
"ask_claude",
"入力された文言に対してClaudeの回答を返すツール",
{
prompt: z.string().describe("Claudeに質問する文言"),
model: z
.string()
.optional()
.describe("使用するClaudeモデル(デフォルト: claude-3-sonnet-20240229)"),
max_tokens: z
.number()
.optional()
.describe("生成する最大トークン数(デフォルト: 1000)"),
},
async ({ prompt, model, max_tokens }) => {
try {
const responseText = await askClaude(
prompt,
model || DEFAULT_MODEL,
max_tokens || DEFAULT_MAX_TOKENS
);
return {
content: [
{
type: "text",
text: responseText,
},
],
};
} catch (error) {
console.error("Claude API エラー:", error);
let errorMessage = "申し訳ありませんが、エラーが発生しました。";
// より詳細なエラーメッセージ
if (error instanceof Error) {
errorMessage += ` エラー詳細: ${error.message}`;
}
return {
content: [
{
type: "text",
text: errorMessage,
},
],
};
}
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`MCP Server 起動完了 - ${new Date().toLocaleString()}`);
console.error(`デフォルトモデル: ${DEFAULT_MODEL}`);
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
ビルドして、いざ実践!
とりあえずビルドしていこう。
$ pnpm run build
> mcp-server@1.0.0 build /Users/xxx/Documents/workspace/typescript/mcp-server
> tsc && chmod 755 build/index.js
良い感じ。
あとは、各種ツールのMCPの設定箇所に挿入すれば良いっぽい。今回はCursor!
mcp.json
{
"mcpServers": {
"mcp-example": {
"command": "node",
"args": [
"/Users/xxx/Documents/workspace/typescript/mcp-server/build/index.js"
],
"env": {
"ANTHROPIC_API_KEY": {key}
}
}
}
}
おー。登録された。あとは利用してみる。
起動されたけど、あれ?エラーになっちゃった。
モデルをもう一度見てみたらないっぽいので、変更してみよう。
できた。
まとめ
この例だとやってること一緒やーん感がありますが、こんな感じでサクッと作成できたりするから、ぜひ試してみてください。
Discussion