MCPで社内システムをClaude Codeに接続する方法を解説。汎用fetchから専用Server構築まで、デプロイ基盤・監視・チケット管理などの接続パターンとツール設計の原則を網羅。
前回はデータベースとの接続について解説した。データベースには標準プロトコルがあるので、接続は比較的シンプルだ。しかし、多くのチームの日常業務はさまざまな社内システムに依存している:デプロイ基盤、監視ダッシュボード、チケット管理、社内API、設定管理など。
これらのシステムには既成のMCP Serverがないことがほとんどだが、ほぼすべてHTTP APIを提供している。この記事では、MCPを使ってこれらの社内ツールをClaude Codeに接続し、監視の確認やデプロイ状況の把握、チケット操作を直接行えるようにする方法を解説する。
社内ツールの接続には2つの方法がある:
方法1:汎用HTTP MCP Serverを使う
コミュニティには汎用的なMCP Serverがいくつかあり、任意のREST APIをMCPツールとしてラップできる。API定義ファイルを書けば、Claudeが呼び出せるツールに変換してくれる。APIの構造がシンプルで、複雑なロジックが不要な場合に適している。
方法2:専用のMCP Serverを自作する
TypeScriptまたはPythonのMCP SDKを使って専用Serverを書き、ツールの定義、パラメータのバリデーション、エラーハンドリングを完全にコントロールする。複数のAPIを組み合わせたり、データ変換やビジネスロジックを加えたりする場合に適している。
この記事では両方を解説する。まずはシンプルな方から始めよう。
最も軽量なのは、公式の @anthropic-ai/mcp-server-fetch を使う方法だ。Claudeが直接HTTPリクエストを送れるようになる。設定は極めてシンプル:
{
"mcpServers": {
"fetch": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-server-fetch"]
}
}
}
設定が済めば、Claudeから直接社内APIを呼び出せる:
デプロイ基盤 https://deploy.internal.com/api/v1/services/user-service の現在の状態を確認して
ClaudeがGETリクエストを送り、レスポンスを取得してパースしてくれる。
ただし、この方法には明らかな制限がある:
一時的な用途には向いているが、長期的な運用には適さない。
特定の社内システムを繰り返し使う場合は、専用のMCP Serverを書くほうがよい。ここでは実際のユースケースとして、社内デプロイ基盤への接続をデモする。
デプロイ基盤が以下のAPIを提供していると仮定する:
GET /api/v1/services — すべてのサービスを一覧表示GET /api/v1/services/:name/status — サービスの状態を確認POST /api/v1/services/:name/deploy — デプロイをトリガーGET /api/v1/services/:name/logs — 最近のデプロイログを表示まずプロジェクトを初期化する:
mkdir mcp-deploy && cd mcp-deploy
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npx tsc --init
コアとなるコード src/index.ts:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const API_BASE = process.env.DEPLOY_API_URL!;
const API_TOKEN = process.env.DEPLOY_API_TOKEN!;
async function api(path: string, method = "GET", body?: unknown) {
const res = await fetch(`${API_BASE}${path}`, {
method,
headers: {
Authorization: `Bearer ${API_TOKEN}`,
"Content-Type": "application/json",
},
body: body ? JSON.stringify(body) : undefined,
});
if (!res.ok) {
throw new Error(`API error: ${res.status} ${await res.text()}`);
}
return res.json();
}
const server = new McpServer({
name: "deploy-platform",
version: "1.0.0",
});
// すべてのサービスを一覧表示
server.tool("list_services", "デプロイ基盤上のすべてのサービスとその状態を一覧表示する", {}, async () => {
const data = await api("/api/v1/services");
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
});
// 個別サービスの状態を確認
server.tool(
"service_status",
"指定されたサービスの現在のデプロイ状態、バージョン、ヘルスチェック結果を確認する",
{ name: z.string().describe("サービス名(例:user-service)") },
async ({ name }) => {
const data = await api(`/api/v1/services/${name}/status`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// デプロイログを確認
server.tool(
"deploy_logs",
"指定されたサービスの最近のデプロイログを表示する",
{
name: z.string().describe("サービス名"),
limit: z.number().optional().default(10).describe("取得件数(デフォルト:10)"),
},
async ({ name, limit }) => {
const data = await api(`/api/v1/services/${name}/logs?limit=${limit}`);
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
// デプロイをトリガー
server.tool(
"trigger_deploy",
"指定されたサービスのデプロイをトリガーする。これは書き込み操作であり、本番環境に影響を与える",
{
name: z.string().describe("サービス名"),
version: z.string().describe("デプロイするバージョン番号またはgit ref"),
},
async ({ name, version }) => {
const data = await api(`/api/v1/services/${name}/deploy`, "POST", {
version,
});
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}
);
const transport = new StdioServerTransport();
server.connect(transport);
コンパイルして実行:
npx tsc
{
"mcpServers": {
"deploy": {
"command": "node",
"args": ["/path/to/mcp-deploy/dist/index.js"],
"env": {
"DEPLOY_API_URL": "https://deploy.internal.com",
"DEPLOY_API_TOKEN": "your-api-token"
}
}
}
}
tokenは .claude/settings.local.json(gitにコミットしない)に、URLは .claude/settings.json(gitにコミットしてチームで共有)に配置する。
設定が完了すると、会話が自然になる:
user-serviceの現在の状態は?
→ Claudeが service_status("user-service") を呼び出す
→ 結果:稼働中、バージョンv2.3.1、最終デプロイは2時間前、ヘルスチェックはすべてパス
最近のデプロイで失敗したものはある?
→ Claudeが deploy_logs("user-service", 20) を呼び出す
→ ログを分析し、3回目のデプロイがロールバックされたこと、原因はヘルスチェックのタイムアウトだと報告
user-serviceをv2.3.2にデプロイして
→ Claudeが trigger_deploy("user-service", "v2.3.2") を呼び出す
→ ツールの説明に「書き込み操作」と記載されているため、Claudeはまず確認を求める
これは慎重に検討すべき問題だ。
読み取り操作は安心して接続できる。状態の確認、ログの閲覧、チケットの検索——これらは副作用がなく、Claudeが間違えても損害はない。
書き込み操作は2つのケースに分かれる:
低リスクな書き込み操作は接続してよいが、ツールの説明に明記すること。Claudeは副作用が明記された操作に対して自動的にユーザーの確認を求める。例えばチケットの作成、メッセージの送信、設定の更新など。
高リスクな書き込み操作は接続しないことを推奨する。リソースの削除、ロールバックのトリガー、権限の変更——これらは結果が深刻で不可逆なため、手動操作のままにしておくほうが安全だ。
どうしても書き込み操作を接続する場合は、少なくとも2つのことを行う:
| システム | 公開するツール | 注意点 |
|---|---|---|
| デプロイ基盤(K8s / Kamal) | サービス状態の確認、ログの表示、デプロイのトリガー | 書き込み操作には確認を追加 |
| 監視システム(Grafana / Datadog) | メトリクスの表示、アラート履歴の確認 | クエリの時間範囲を制限し、大量のデータ取得を避ける |
| チケット管理(Jira / Linear) | チケットの検索、作成、ステータスの更新 | チケット作成は書き込み操作だが低リスク |
| 社内ドキュメント(Notion / Confluence) | ドキュメントの検索、ページ内容の読み取り | ページネーションに注意し、一度に大量に取得しない |
| 設定管理(Consul / etcd) | 設定の読み取り、環境間の差分比較 | 読み取りのみにし、書き込み操作は接続しない |
| CI/CD(GitHub Actions / Jenkins) | ビルド状態の確認、ビルドのトリガー | ビルドのトリガーは中リスクの書き込み操作 |
MCPツールの設計はAPI設計とは異なる。APIはエンジニアが使うものだが、ツールはAIが使うものだ。いくつかの原則を押さえておきたい:
ツール名は分かりやすく
✗ get_svc_stat — Claudeが略語の意味を正しく推測できるとは限らない
✓ service_status — 見ればすぐに何をするか分かる
説明はAI向けに書く
ツールの説明は人間向けのドキュメントではなく、Claudeが「いつこのツールを呼び出すべきか」を判断するための根拠だ。説明には、このツールが何をするか、何を返すか、いつ使うべきかを明記する。
✗ "サービス状態を取得する"
✓ "指定されたサービスの現在のデプロイ状態、バージョン、ヘルスチェック結果を確認する。ユーザーがあるサービスが正常に稼働しているか質問した場合に使用する"
パラメータはzodで明確に定義する
.describe() を付けたパラメータだけが、Claudeに何を入力すべきかを伝えられる。説明のないパラメータは、名前から推測するしかない。
構造化されたデータを返す
MCPツールが返すのはテキストだが、できるだけフォーマットされたJSONを返すようにする。Claudeは構造化されたデータのほうがプレーンテキストよりも正確に処理できる。
適切な粒度を保つ
複雑なフローを1つのツールに詰め込まないこと。逆に、シンプルなクエリを3つのツールに分割するのもよくない。原則は、1つのツールで1つの独立した意味のある操作を完結させることだ。
MCP Serverのコードはいくつかの場所に配置できる:
プロジェクトリポジトリ内(推奨のスタート方法)
your-project/
├── .claude/settings.json
├── mcp-servers/
│ └── deploy/
│ ├── src/index.ts
│ ├── package.json
│ └── tsconfig.json
└── ...
コードと設定が一箇所にまとまるため、チームがcloneして依存関係をインストールすればすぐに使える。
独立リポジトリ
MCP Serverを複数のプロジェクトで使う場合は、独立したリポジトリに配置し、npmパッケージやDockerイメージとして公開する。
{
"mcpServers": {
"deploy": {
"command": "npx",
"args": ["-y", "@yourcompany/mcp-deploy-server"]
}
}
}
グローバルインストール
全社共通のMCP Server(例えば、統一認証や統一ログ基盤との接続)は、グローバルにインストールして ~/.claude/settings.json に設定する。
MCP Server開発でよくある問題は「Claudeがツールを呼び出さない」や「呼び出したがエラーになる」だ。
Serverが起動していることを確認する
Claude Codeを再起動した後、/mcp と入力して接続済みのMCP Server一覧を確認する。自分のServerが一覧にない場合は、commandとargsが正しいか確認する。
Serverを単独でテストする
MCP Serverはstdioで通信するので、ターミナルから直接テストできる:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/index.js
ツール一覧が返ってくれば、Server自体は正常だ。
Claudeのツール呼び出しログを確認する
Claude Codeは毎回のツール呼び出しの入力と出力を表示する。パラメータが間違っている場合は、たいていツールの説明やパラメータ定義が不十分で、Claudeが誤解したことが原因だ。
実際のユースケースでまとめてみよう。SentryをClaude Codeに接続し、本番のエラーを直接確認できるようにする。
server.tool(
"search_errors",
"在 Sentry 中搜索最近的錯誤。用於排查線上問題、查看錯誤趨勢",
{
query: z.string().describe("搜索關鍵詞,如錯誤訊息、函式名"),
hours: z.number().optional().default(24).describe("查看最近多少小時的錯誤"),
},
async ({ query, hours }) => {
const since = new Date(Date.now() - hours * 3600000).toISOString();
const data = await api(
`/api/0/projects/${ORG}/${PROJECT}/issues/?query=${encodeURIComponent(query)}&start=${since}&sort=date`
);
const summary = data.map((issue: any) => ({
title: issue.title,
count: issue.count,
firstSeen: issue.firstSeen,
lastSeen: issue.lastSeen,
link: issue.permalink,
}));
return {
content: [{ type: "text", text: JSON.stringify(summary, null, 2) }],
};
}
);
server.tool(
"error_details",
"查看 Sentry 中某個錯誤的詳細資訊,包括堆疊和最近一次事件",
{ issueId: z.string().describe("Sentry issue ID") },
async ({ issueId }) => {
const [issue, latest] = await Promise.all([
api(`/api/0/issues/${issueId}/`),
api(`/api/0/issues/${issueId}/events/latest/`),
]);
return {
content: [
{
type: "text",
text: JSON.stringify(
{
title: issue.title,
count: issue.count,
users: issue.userCount,
stacktrace: latest.entries?.find(
(e: any) => e.type === "exception"
),
},
null,
2
),
},
],
};
}
);
接続が完了すると、本番の問題調査はこんな会話になる:
直近4時間で新しい500エラーは出ている?
→ ClaudeがSentryを検索
→ 3件の新しいissueを発見、最も深刻なものは120人のユーザーに影響
→ 自動的にスタックトレースを取得し、あるNull Pointer Exceptionが原因と特定
→ コード内の該当箇所を見つけ、修正案を提示
問題の発見からコードの特定まで、すべて1回の会話で完結する。
この記事では、MCPを使って社内ツールを接続する方法を解説した。核心となる考え方は、社内システムにHTTP APIがある → MCP Serverでラップする → Claudeが直接使えるようになる、というものだ。
この記事のすべての例は、既存のAPIのラッピングだ。デプロイ基盤もSentryも元々インターフェースを持っており、MCP Serverは転送と適応のレイヤーにすぎない。次回は異なるシナリオを扱う:必要な機能に既成のAPIがない場合に、ゼロからMCP Serverを構築し、自前でロジックを実装し、状態を管理し、複雑なマルチステップのインタラクションを処理する方法だ。