この記事でわかること
– MCP(Model Context Protocol)の仕組みと3つの提供物
– TypeScriptでMCPサーバーをゼロから作る手順
– ツール・リソース・プロンプトの実装方法
– Claude Codeへの登録と動作確認
Claude Codeに「社内DBを直接クエリしてほしい」「自社APIを叩いてほしい」——そんな要望に応えるのがMCPサーバー自作だ。
MCPはAnthropicが策定したオープンプロトコルで、AIクライアントに外部ツールを接続するための標準仕様。TypeScriptの公式SDKを使えば、30分程度で独自のMCPサーバーが作れる。
MCPが提供できる3つのもの
| 種類 | 説明 | 例 |
|---|---|---|
| Tools | AIが呼び出せる関数 | DB検索・API呼び出し・ファイル変換 |
| Resources | AIが読めるデータソース | ファイル・DB・ログ・設定値 |
| Prompts | 再利用可能なプロンプトテンプレート | コードレビュー定型・ドキュメント生成 |
今回はToolsの実装をメインに、Resourcesも触れる。
セットアップ
mkdirmy-mcp-server
cdmy-mcp-server
npminit-y
npminstall@modelcontextprotocol/sdk
npminstall-Dtypescript@types/nodetsx
npxtsc--init
tsconfig.json を調整:
{
"compilerOptions":{
"target":"ES2022",
"module":"Node16",
"moduleResolution":"Node16",
"outDir":"./dist",
"strict":true,
"esModuleInterop":true
},
"include":["src/**/*"]
}
最小構成のMCPサーバー
src/index.ts を作る:
import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";
import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";
import{z}from"zod";
constserver=newMcpServer({
name:"my-mcp-server",
version:"1.0.0",
});
// ツールを定義
server.tool(
"add", // ツール名
"2つの数を足す", // 説明(AIが使い方を判断する)
{
a:z.number().describe("1つ目の数"),
b:z.number().describe("2つ目の数"),
},
async({a,b})=>({
content:[{type:"text",text:String(a+b)}],
})
);
// stdioトランスポートで起動
consttransport=newStdioServerTransport();
awaitserver.connect(transport);
これで動くMCPサーバーの完成。ツール名・説明・入力スキーマ・ハンドラの4つを書くだけだ。
実践例:天気APIを叩くツール
import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";
import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";
import{z}from"zod";
constserver=newMcpServer({
name:"weather-server",
version:"1.0.0",
});
server.tool(
"get_weather",
"指定した都市の現在の天気を取得する",
{
city:z.string().describe("都市名(例: Tokyo, Osaka)"),
},
async({city})=>{
constapiKey=process.env.WEATHER_API_KEY;
constres=awaitfetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric&lang=ja`
);
if(!res.ok){
return{
content:[{type:"text",text:`エラー:${res.status}${res.statusText}`}],
isError:true,
};
}
constdata=awaitres.json()as{
name:string;
main:{temp:number;humidity:number};
weather:Array<{description:string}>;
};
return{
content:[{
type:"text",
text:`${data.name}:${data.weather[0].description}, 気温${data.main.temp}℃, 湿度${data.main.humidity}%`,
}],
};
}
);
consttransport=newStdioServerTransport();
awaitserver.connect(transport);
実践例:SQLiteを直接クエリするツール
社内ツールや個人プロジェクトでよくあるパターン。
npminstallbetter-sqlite3
npminstall-D@types/better-sqlite3
importDatabasefrom"better-sqlite3";
import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";
import{StdioServerTransport}from"@modelcontextprotocol/sdk/server/stdio.js";
import{z}from"zod";
constdb=newDatabase(process.env.DB_PATH??"./data.db");
constserver=newMcpServer({
name:"sqlite-server",
version:"1.0.0",
});
// SELECT専用(書き込みは別ツールに分離してリスク管理)
server.tool(
"query_db",
"SQLiteデータベースにSELECTクエリを実行する",
{
sql:z.string().describe("実行するSELECT文"),
limit:z.number().default(50).describe("最大行数"),
},
async({sql,limit})=>{
// SELECT以外は拒否
if(!/^\s*SELECT/i.test(sql)){
return{
content:[{type:"text",text:"SELECTクエリのみ許可されています"}],
isError:true,
};
}
constrows=db.prepare(`${sql} LIMIT${limit}`).all();
return{
content:[{type:"text",text:JSON.stringify(rows,null,2)}],
};
}
);
server.tool(
"list_tables",
"データベースのテーブル一覧を返す",
{},
async()=>{
consttables=db
.prepare("SELECT name FROM sqlite_master WHERE type='table'")
.all()asArray<{name:string}>;
return{
content:[{type:"text",text:tables.map(t=>t.name).join(", ")}],
};
}
);
consttransport=newStdioServerTransport();
awaitserver.connect(transport);
Resourcesの実装
ToolsはAIが能動的に呼ぶ関数だが、Resourcesは「読めるデータ」として公開する仕組みだ。
// ログファイルをリソースとして公開
server.resource(
"app-logs",
"logs://app/latest",
async(uri)=>{
constcontent=awaitfs.readFile("/var/log/app.log","utf-8");
constlines=content.split("\n").slice(-100).join("\n");// 直近100行
return{
contents:[{
uri:uri.href,
mimeType:"text/plain",
text:lines,
}],
};
}
);
Claude Codeへの登録
~/.claude/settings.json に追記:
{
"mcpServers":{
"my-mcp-server":{
"command":"node",
"args":["/path/to/my-mcp-server/dist/index.js"],
"env":{
"WEATHER_API_KEY":"your-api-key",
"DB_PATH":"/path/to/data.db"
}
}
}
}
TypeScriptをビルドせず tsx で直接実行する場合:
{
"mcpServers":{
"my-mcp-server":{
"command":"npx",
"args":["tsx","/path/to/my-mcp-server/src/index.ts"]
}
}
}
登録後、Claude Codeを再起動すれば自動的にMCPサーバーが接続される。Claude Codeに「天気を調べて」と話しかけると、定義した get_weather ツールを呼び出してくれる。
セキュリティ上の考慮点
ツール名・説明は攻撃者に見える
MCPサーバーの情報はAIに渡されるため、悪意あるプロンプトでツールを悪用しようとする攻撃が成立しうる。特にDBアクセス系ツールは「SELECT専用」「対象テーブルを限定」など最小権限で設計する。
環境変数でシークレット管理
APIキー・DB接続文字列はコードに直書きせず、env フィールドで渡す。settings.jsonはgit管理外(~/.claude/ 配下)に置く。
信頼できないMCPサーバーを追加しない
claude-code-third-party-skills-securityで書いたように、野良MCPサーバーはプロンプトインジェクションのベクターになりうる。自作か信頼できるソースのものだけを使う。
まとめ
// 最小構成テンプレート
constserver=newMcpServer({name:"...",version:"1.0.0"});
server.tool("tool_name","説明",{param:z.string()},async({param})=>({
content:[{type:"text",text:"結果"}],
}));
awaitserver.connect(newStdioServerTransport());
やること:
1. npm install @modelcontextprotocol/sdk
2. ツール定義(名前・説明・スキーマ・ハンドラ)
3. settings.json に登録
この3ステップだけ。Claude Codeに独自のDB・API・ファイル操作を追加したいなら、まず小さいツールを1つ作って動作確認するのが早道だ。
あわせて読みたい
- Claude Code hooksシステム完全ガイド — hooksでClaude Codeの動作をカスタマイズ
- CLAUDE.md設計術 — プロジェクト・チームでのClaude Code設定管理
- Claude Codeのサードパーティスキルは安全か? — MCPサーバーのセキュリティリスク
参考: Model Context Protocol Documentation, @modelcontextprotocol/sdk (npm)
見てもらえるだけで応援になります
このブログはアフィリエイトリンクで運営されています。以下のリンクから気になるサービスをチェックしてもらえると、僕たちの活動の支えになります。
この記事を書いたのは わさび(ニホンイシガメ / 3歳 / VTuberあかはら。の家族)です。
あかはらVラボ — Claude特化の情報を発信中。



コメント