取り組みの背景
Antigravity は強力なコード生成と編集機能を提供してくれますが、「外部のデータソースと連携させたい」「自社のAPIをAIに操作させたい」といったニーズが出てくると、標準機能だけでは対応が難しくなります。
そこで活躍するのが MCP(Model Context Protocol)です。MCP を使えば、外部の REST API や Webhook をAIが直接操作できるツールとして統合でき、Antigravity をあなた専用のAIエージェントプラットフォームに拡張できます。
MCP の基本アーキテクチャを理解する
MCP とは何か
MCP(Model Context Protocol)は、AIモデルが外部ツールやデータソースとやり取りするための標準プロトコルです。Antigravity はネイティブで MCP をサポートしており、MCP サーバーを追加するだけで AIの機能を無限に拡張できます。
MCP のアーキテクチャは以下の3層で構成されています。
- ホスト(Antigravity): AIモデルを実行し、ユーザーとのインタラクションを管理する
- MCP クライアント: ホスト内で動作し、MCP サーバーとの通信を仲介する
- MCP サーバー: 外部ツールやリソースを公開する独立プロセス
MCP サーバーが提供する3つの機能
MCP サーバーは以下の3種類の機能を公開できます。
- Tools(ツール): AIが呼び出せる関数。外部APIの操作、データベースクエリ、ファイル操作など
- Resources(リソース): AIが読み取れるデータソース。設定ファイル、ドキュメント、ライブデータなど
- Prompts(プロンプト): 定型のプロンプトテンプレート。複雑な操作を簡略化するショートカット
MCP サーバーの実装 — 外部APIラッパーの構築
プロジェクトのセットアップ
まず、TypeScript で MCP サーバープロジェクトを作成します。
mkdir my-api-mcp-server
cd my-api-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
tsconfig.json を作成します。
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*"]
}
基本的な MCP サーバーの実装
外部の天気 API をラップする MCP サーバーの例を示します。
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// MCP サーバーインスタンスを作成
const server = new McpServer({
name: "weather-api",
version: "1.0.0",
});
// ツール: 指定した都市の天気情報を取得
server.tool(
"get_weather",
"指定した都市の現在の天気情報を取得します",
{
city: z.string().describe("都市名(例: Tokyo, New York)"),
units: z
.enum(["metric", "imperial"])
.optional()
.default("metric")
.describe("温度の単位"),
},
async ({ city, units }) => {
try {
const apiKey = process.env.WEATHER_API_KEY;
if (!apiKey) {
return {
content: [
{
type: "text",
text: "エラー: WEATHER_API_KEY が設定されていません",
},
],
};
}
const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&units=${units}&appid=${apiKey}`;
const response = await fetch(url);
if (!response.ok) {
return {
content: [
{
type: "text",
text: `API エラー: ${response.status} ${response.statusText}`,
},
],
};
}
const data = await response.json();
// レスポンスを人間が読みやすい形式に整形
const summary = [
`都市: ${data.name}(${data.sys.country})`,
`天気: ${data.weather[0].description}`,
`気温: ${data.main.temp}°${units === "metric" ? "C" : "F"}`,
`湿度: ${data.main.humidity}%`,
`風速: ${data.wind.speed} ${units === "metric" ? "m/s" : "mph"}`,
].join("\n");
return { content: [{ type: "text", text: summary }] };
} catch (error) {
return {
content: [
{
type: "text",
text: `接続エラー: ${error instanceof Error ? error.message : "不明なエラー"}`,
},
],
};
}
}
);
// サーバーを起動
const transport = new StdioServerTransport();
await server.connect(transport);
Antigravity への接続設定
MCP サーバーを実装したら、Antigravity の設定ファイルに登録します。
// .antigravity/settings.json(プロジェクト設定)
{
"mcpServers": {
"weather-api": {
"command": "npx",
"args": ["tsx", "./my-api-mcp-server/src/index.ts"],
"env": {
"WEATHER_API_KEY": "YOUR_API_KEY"
}
}
}
}
登録後、Antigravity で「東京の天気を教えて」と入力すると、AIが get_weather ツールを自動的に呼び出して天気情報を取得します。
複数APIを統合した実用的なエージェント
プロジェクト管理エージェントの設計
実際の業務で使うエージェントは、複数のAPIを組み合わせることが一般的です。ここでは、GitHub Issues と Slack を統合したプロジェクト管理エージェントの例を示します。
// src/project-agent.ts
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: "project-manager",
version: "1.0.0",
});
// GitHub Issues から未完了タスクを取得
server.tool(
"list_open_issues",
"GitHubリポジトリの未完了Issueを一覧取得します",
{
repo: z.string().describe("リポジトリ名(owner/repo形式)"),
labels: z
.string()
.optional()
.describe("フィルターするラベル(カンマ区切り)"),
},
async ({ repo, labels }) => {
const headers: Record<string, string> = {
Accept: "application/vnd.github.v3+json",
};
const token = process.env.GITHUB_TOKEN;
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
let url = `https://api.github.com/repos/${repo}/issues?state=open&per_page=20`;
if (labels) {
url += `&labels=${encodeURIComponent(labels)}`;
}
const response = await fetch(url, { headers });
if (!response.ok) {
return {
content: [
{ type: "text", text: `GitHub APIエラー: ${response.status}` },
],
};
}
const issues = await response.json();
const summary = issues
.map(
(issue: any) =>
`#${issue.number}: ${issue.title} [${issue.labels.map((l: any) => l.name).join(", ")}]`
)
.join("\n");
return {
content: [
{
type: "text",
text: `未完了Issue(${issues.length}件):\n${summary}`,
},
],
};
}
);
// Slack にメッセージを送信
server.tool(
"send_slack_message",
"Slackチャンネルにメッセージを送信します",
{
channel: z.string().describe("チャンネルID"),
message: z.string().describe("送信するメッセージ"),
},
async ({ channel, message }) => {
const token = process.env.SLACK_BOT_TOKEN;
if (!token) {
return {
content: [
{ type: "text", text: "エラー: SLACK_BOT_TOKEN が未設定です" },
],
};
}
const response = await fetch("https://slack.com/api/chat.postMessage", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ channel, text: message }),
});
const data = await response.json();
if (!data.ok) {
return {
content: [
{ type: "text", text: `Slackエラー: ${data.error}` },
],
};
}
return {
content: [{ type: "text", text: "メッセージを送信しました" }],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
この構成により、Antigravity に「今週の未完了Issueを確認して、チームのSlackに要約を送って」と指示するだけで、AI が GitHub API から Issue を取得し、要約を作成し、Slack に通知するという一連の操作を自律的に実行してくれます。
本番運用のためのベストプラクティス
レート制限の実装
外部APIには必ずレート制限があります。MCP サーバー内でレート制限を管理することで、APIの利用制限に達するリスクを軽減できます。
// src/rate-limiter.ts
class RateLimiter {
private timestamps: number[] = [];
private maxRequests: number;
private windowMs: number;
constructor(maxRequests: number, windowMs: number) {
this.maxRequests = maxRequests;
this.windowMs = windowMs;
}
async waitIfNeeded(): Promise<void> {
const now = Date.now();
// ウィンドウ外のタイムスタンプを除去
this.timestamps = this.timestamps.filter(
(t) => now - t < this.windowMs
);
if (this.timestamps.length >= this.maxRequests) {
const oldestInWindow = this.timestamps[0];
const waitTime = this.windowMs - (now - oldestInWindow) + 100;
await new Promise((resolve) => setTimeout(resolve, waitTime));
}
this.timestamps.push(Date.now());
}
}
// 使用例: 1分あたり最大60リクエスト
export const githubLimiter = new RateLimiter(60, 60_000);
// ツール内で使用: await githubLimiter.waitIfNeeded();
認証トークンの安全な管理
MCP サーバーでは、APIキーやトークンを環境変数経由で受け取ります。本番環境では以下のパターンを推奨します。
// src/auth.ts
function getRequiredEnv(key: string): string {
const value = process.env[key];
if (!value) {
throw new Error(
`必須環境変数 ${key} が設定されていません。` +
`Antigravity の MCP 設定で env に追加してください。`
);
}
return value;
}
// トークンのリフレッシュ対応
class TokenManager {
private token: string;
private refreshToken: string;
private expiresAt: number = 0;
constructor() {
this.token = getRequiredEnv("API_ACCESS_TOKEN");
this.refreshToken = getRequiredEnv("API_REFRESH_TOKEN");
}
async getValidToken(): Promise<string> {
if (Date.now() < this.expiresAt - 60_000) {
return this.token;
}
// トークンをリフレッシュ
const response = await fetch("https://api.example.com/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "refresh_token",
refresh_token: this.refreshToken,
}),
});
const data = await response.json();
this.token = data.access_token;
this.expiresAt = Date.now() + data.expires_in * 1000;
return this.token;
}
}
エラーハンドリングのパターン
MCP ツールでは、エラーが発生してもプロセスをクラッシュさせずに、AIが理解できるエラーメッセージを返す点が肝心です。
// src/error-handler.ts
type ApiResult<T> = { success: true; data: T } | { success: false; error: string };
async function safeApiCall<T>(
fn: () => Promise<T>,
context: string
): Promise<ApiResult<T>> {
try {
const data = await fn();
return { success: true, data };
} catch (error) {
if (error instanceof TypeError && error.message.includes("fetch")) {
return { success: false, error: `${context}: ネットワーク接続エラー` };
}
return {
success: false,
error: `${context}: ${error instanceof Error ? error.message : "不明なエラー"}`,
};
}
}
// ツール内での使用例
// const result = await safeApiCall(
// () => fetch(url).then(r => r.json()),
// "GitHub Issues取得"
// );
デバッグとトラブルシューティング
MCP Inspector を使ったデバッグ
MCP には公式のデバッグツール「MCP Inspector」が用意されています。
# MCP Inspector をインストール
npx @modelcontextprotocol/inspector
# サーバーを指定して起動
npx @modelcontextprotocol/inspector npx tsx src/index.ts
Inspector を使うと、ツールの入出力をリアルタイムで確認でき、APIレスポンスの中身やエラーの原因を素早く特定できます。
よくあるトラブルと対処法
- ツールが表示されない:
settings.json のパスが正しいか確認。command と args が実行可能か単体でテスト
- タイムアウトエラー: 外部APIの応答が遅い場合、
fetch に AbortController でタイムアウトを設定
- 環境変数が読めない: MCP 設定の
env ブロックに追加されているか確認。シェル環境変数は MCP サーバーに自動で渡されない
個人開発者の視点から(実体験メモ)
まとめ
Antigravity と MCP の組み合わせは、AIエージェントの可能性を大きく広げてくれます。標準の MCP SDK を使えば、どんな外部 API でもAIが操作できるツールに変換でき、複数のサービスを横断した自律的なワークフローを構築することが可能です。
まずはシンプルなAPIラッパーから始めて、段階的にツールを追加していく進め方がおすすめです。MCP サーバー構築の基本を参考にしつつ、マルチエージェントオーケストレーションやプロンプトエンジニアリングの実践テクニックと組み合わせることで、あなただけの高度なAIエージェントシステムを実現できるでしょう。