Model Context Protocol (MCP)
AIエージェントが標準のMCPプロトコル経由でバックエンドツールにアクセスできるよう、カスタムMCPサーバーを作成します。
MCPを使用する理由
AIエージェントが外部サーバー上でホストされているツールを呼び出す必要がある、または自分のバックエンドロジックを、MCP互換クライアントが検出して呼び出せるツールとして公開したい場合があります。
MCPがない場合、エージェントとツールを接続するたびに、個別のカスタム統合ロジックを構築する必要があります。MCPを使えば、サーバー上でツールを定義し、互換エージェントが標準プロトコルでそれらを検出・呼び出せます。
// Backend: define an MCP server with a tool
@mcpServer({
name: 'weather',
id: 'weather',
description: 'Provides weather data',
version: '1.0.0',
})
export class WeatherMcpService extends SquidService {
@mcpTool({
description: 'Returns the current weather for a city',
inputSchema: {
type: 'object',
properties: {
city: { type: 'string', description: 'City name' },
},
required: ['city'],
},
})
async getWeather({ city }: { city: string }): Promise<string> {
return `The weather in ${city} is sunny, 25°C.`;
}
}
これで、MCP互換のAIエージェントは標準プロトコルを通じて getWeather を検出して呼び出せるようになります。
概要
MCP (Model Context Protocol) は、AIエージェントが外部サーバー上のツールを検出し呼び出す方法を標準化するオープンプロトコルです。Squid にはバックエンドでMCPサーバーを作成するための組み込みサポートがあり、デコレーター付きメソッドとしてツールを定義し、JSON-RPC 経由でエージェントが呼び出せます。
MCPを使うべきとき
| Use Case | Recommendation |
|---|---|
| どのMCP互換エージェントにもバックエンドツールを公開したい | MCP server |
| 会話中にエージェントがカスタムのサーバーサイドロジックを必要とする | AI functions を使用 |
| エージェントが外部MCPサーバー上のツールを呼び出す必要がある | MCP connector を使用 |
| エージェントが接続済みサービス(CRM、APIなど)へアクセスする必要がある | connected integration を使用 |
仕組み
SquidServiceを継承する service で、クラスに@mcpServerを付与します- そのクラス内のメソッドに
@mcpToolを付与して、サーバーへツールを追加します - Squid はデプロイ時にMCPサーバーを登録し、JSON-RPC エンドポイントとして公開します
- MCP互換クライアントが接続し、
tools/listで利用可能なツールを検出し、tools/callで呼び出します - 任意で、アクセス制御のために
@mcpAuthorizerメソッドを追加します
クイックスタート
前提条件
squid init-backendで初期化された Squid backend project@squidcloud/backendパッケージがインストールされていること- AI agent が作成済みであること(MCP server を Squid agent に接続したい場合)
Step 1: ツール付きのMCPサーバーを作成する
SquidService を継承する service クラスを作成し、@mcpServer を付与し、ツールを追加します。
import { mcpServer, mcpTool, SquidService } from '@squidcloud/backend';
@mcpServer({
name: 'greetingServer',
id: 'greetingServer',
description: 'A simple MCP server that greets users',
version: '1.0.0',
})
export class McpService extends SquidService {
@mcpTool({
description: 'Returns a greeting for the given name',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'The name to greet' },
},
required: ['name'],
},
})
async greet({ name }: { name: string }): Promise<string> {
return `Hello, ${name}!`;
}
}
Step 2: service をエクスポートする
service が service index ファイルからエクスポートされていることを確認します。
export * from './mcp-service';
Step 3: バックエンドをデプロイする
ローカル開発では、Squid CLI を使ってバックエンドをローカルで実行します。
squid start
クラウドへデプロイするには、deploying your backend を参照してください。
Step 4: MCPサーバーをエージェントに接続する
デプロイ後、MCP server を connector として追加し、エージェントの abilities に紐づけます。
- Squid Console で、デプロイ済みのMCP serverを指す MCP connector を追加します
- Agent Studio で、その connector をエージェントの abilities に追加します
これで、エージェントは会話中にMCPツールを検出・呼び出せるようになります。
コアコンセプト
@mcpServer デコレーター
@mcpServer デコレーターは、SquidService クラスをMCP serverとしてマークします。次のフィールドを持つ設定オブジェクトを受け取ります。
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | MCP endpoint URL で使用される一意の識別子 |
name | string | Yes | MCP manifest で公開される server 名 |
description | string | Yes | server の目的を説明 |
version | string | Yes | MCP manifest で公開される server version |
各 id はアプリケーション内のすべてのMCP server間で一意である必要があります。IDが重複するとデプロイエラーになります。
import { mcpServer, SquidService } from '@squidcloud/backend';
@mcpServer({
id: 'inventory',
name: 'inventoryServer',
description: 'Provides product inventory lookup and management tools',
version: '1.0.0',
})
export class InventoryMcpService extends SquidService {
// Tools go here
}
@mcpTool デコレーター
@mcpTool デコレーターは、メソッドをMCP clientが検出して呼び出せる tool として公開します。次のフィールドを持つ設定オブジェクトを受け取ります。
| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | tool が何をし、いつ呼び出すべきかをエージェントに伝える |
inputSchema | JSONSchema | Yes | tool の入力パラメータを定義する JSON Schema |
outputSchema | JSONSchema | No | tool の出力形式を説明する JSON Schema |
メソッド名が MCP manifest 上の tool 名になります。各 tool 名は同一 server 内で一意である必要があります。
@mcpTool({
description: 'Looks up the current stock level for a product by SKU',
inputSchema: {
type: 'object',
properties: {
sku: {
type: 'string',
description: 'The product SKU code',
},
},
required: ['sku'],
},
})
async getStockLevel({ sku }: { sku: string }): Promise<number> {
const product = await this.squid.collection('products').doc(sku).snapshot();
if (!product) {
throw new Error(`Product with SKU ${sku} not found`);
}
return product.data.stockLevel;
}
Input schema
inputSchema は JSON Schema 形式に従います。tool メソッドは、スキーマに一致するプロパティを持つ単一のオブジェクト引数を受け取ります。
@mcpTool({
description: 'Searches products by category and price range',
inputSchema: {
type: 'object',
properties: {
category: {
type: 'string',
description: 'Product category to search',
enum: ['electronics', 'clothing', 'home', 'sports'],
},
maxPrice: {
type: 'number',
description: 'Maximum price in USD',
},
inStockOnly: {
type: 'boolean',
description: 'If true, only return items currently in stock',
},
},
required: ['category'],
},
})
async searchProducts({
category,
maxPrice,
inStockOnly,
}: {
category: string;
maxPrice?: number;
inStockOnly?: boolean;
}): Promise<string> {
// Query logic here
return JSON.stringify(results);
}
Output schema
任意の outputSchema は tool の返り値の構造を記述し、client がレスポンス形式を理解するのに役立ちます。
@mcpTool({
description: 'Returns product details for a given SKU',
inputSchema: {
type: 'object',
properties: {
sku: { type: 'string', description: 'Product SKU' },
},
required: ['sku'],
},
outputSchema: {
type: 'object',
properties: {
name: { type: 'string' },
price: { type: 'number' },
inStock: { type: 'boolean' },
},
},
})
async getProduct({ sku }: { sku: string }) {
return { name: 'Widget', price: 9.99, inStock: true };
}
@mcpAuthorizer デコレーター
@mcpAuthorizer デコレーターは、MCP server への各リクエストの前に実行されるメソッドを指定します。受信リクエストを検証し、未認可の呼び出し元を拒否するために使用します。
authorizer メソッドは McpAuthorizationRequest オブジェクトを受け取り、boolean(または Promise<boolean>)を返す必要があります。true を返すと許可、false を返すと "Unauthorized" エラーで拒否します。
import {
mcpAuthorizer,
McpAuthorizationRequest,
mcpServer,
mcpTool,
SquidService,
} from '@squidcloud/backend';
@mcpServer({
name: 'secureMcp',
id: 'secureMcp',
description: 'An MCP server with authorization',
version: '1.0.0',
})
export class SecureMcpService extends SquidService {
@mcpAuthorizer()
async authorize(request: McpAuthorizationRequest): Promise<boolean> {
const token = request.headers['authorization'];
return token === `Bearer ${this.secrets['MCP_AUTH_TOKEN']}`;
}
@mcpTool({
description: 'Returns sensitive data',
inputSchema: {
type: 'object',
properties: {
query: { type: 'string', description: 'The data query' },
},
required: ['query'],
},
})
async getSensitiveData({ query }: { query: string }): Promise<string> {
// This tool is only accessible if the authorizer returns true
return `Results for: ${query}`;
}
}
McpAuthorizationRequest のフィールド
| Field | Type | Description |
|---|---|---|
body | any | パース済みの JSON-RPC リクエスト body |
queryParams | Record<string, string> | リクエストURLからの query parameters |
headers | Record<string, string> | リクエストの HTTP headers |
@mcpAuthorizer メソッドが定義されていない場合、MCP server へのすべてのリクエストが許可されます。
エラーハンドリング
Tool errors
tool メソッドがエラーを throw すると、MCP server はそれを捕捉し、isError: true を伴う tool response として返します。呼び出し元のエージェントはエラーメッセージを受け取り、それを伝えるか、どう進めるかを判断できます。
エージェントが有用なフィードバックを提供できるよう、明確で説明的なエラーを投げてください。
@mcpTool({
description: 'Cancels an order by ID',
inputSchema: {
type: 'object',
properties: {
orderId: { type: 'string', description: 'The order ID to cancel' },
},
required: ['orderId'],
},
})
async cancelOrder({ orderId }: { orderId: string }): Promise<string> {
const order = await this.squid.collection('orders').doc(orderId).snapshot();
if (!order) {
throw new Error(`Order ${orderId} not found`);
}
if (order.data.status === 'shipped') {
throw new Error(`Order ${orderId} has already shipped and cannot be cancelled`);
}
await this.squid.collection('orders').doc(orderId).update({ status: 'cancelled' });
return `Order ${orderId} has been cancelled`;
}
Protocol-level errors
MCP server は、プロトコルレベルの問題に対して標準の JSON-RPC error code を使用します。
| Error Code | Meaning | Cause |
|---|---|---|
-32001 | Unauthorized | @mcpAuthorizer メソッドが false を返した |
-32601 | Method not found | 要求された JSON-RPC method または tool 名が存在しない |
-32000 | Server error | MCP server ID が見つからない、または内部エラーが発生した |
よくある問題
| Issue | Cause | Solution |
|---|---|---|
| 重複IDエラーでデプロイに失敗する | 2つの @mcpServer クラスが同じ id を使用している | 各MCP server に一意の id を使用する |
| 重複tool名でデプロイに失敗する | 1つのserver内で2つの @mcpTool メソッドが同じ名前になっている | どちらかのメソッドをリネームする |
| エージェントがtoolを一度も呼び出さない | tool の description がユーザーのプロンプトと一致していない | tool が何をするかを明確に示すよう description を書き直す |
| Authorization が常に失敗する | token または header のチェックが誤っている | デバッグのために McpAuthorizationRequest の各フィールドをログ出力する |
ベストプラクティス
明確な tool description を書く
description は、エージェントが tool をいつ呼ぶべきかを判断するための主要な手がかりです。tool が何を行い、どの情報を返すのかを具体的に記述してください。
// Good: specific about capability and when to use
description: 'Returns the current stock level for a product. Use when asked about inventory or availability.'
// Bad: vague
description: 'Gets product info'
input schema を慎重に設計する
- tool が機能するために必須の場合にのみ、パラメータを
requiredにする enumを使って値を既知の集合に制約する- 各プロパティの
descriptionを明確に書き、エージェントが提供すべき形式を理解できるようにする - 適切な JSON Schema type(
string,number,boolean,array,object)を使用する
MCP server を安全に運用する
- server が機密操作を公開する場合は、必ず
@mcpAuthorizerを追加する - authorization token はハードコードではなく、保存された secrets と照合して検証する
- authentication(誰が呼んでいるか)と authorization(何ができるか)の両方をチェックする
tool input を検証する
input schema は型制約を提供しますが、エッジケースを扱うために tool メソッド内でも入力検証を行ってください。
@mcpTool({
description: 'Transfers funds between accounts',
inputSchema: {
type: 'object',
properties: {
fromAccount: { type: 'string', description: 'Source account ID' },
toAccount: { type: 'string', description: 'Destination account ID' },
amount: { type: 'number', description: 'Amount to transfer in USD' },
},
required: ['fromAccount', 'toAccount', 'amount'],
},
})
async transferFunds({
fromAccount,
toAccount,
amount,
}: {
fromAccount: string;
toAccount: string;
amount: number;
}): Promise<string> {
if (amount <= 0) {
throw new Error('Transfer amount must be positive');
}
if (fromAccount === toAccount) {
throw new Error('Source and destination accounts must be different');
}
// Process transfer...
return `Transferred $${amount} from ${fromAccount} to ${toAccount}`;
}
tool を小さく保つ
各 tool は1つのことをうまく行うようにしてください。多くの操作を扱う単一の tool よりも、小さく目的が明確な tool を複数用意するほうが望ましいです。これにより、エージェントがタスクに適した tool を選びやすくなります。
次のステップ
- MCP connectors - Squid Console からエージェントをMCP serverに接続する
- Abilities - MCP connectors やその他のツールをエージェントに紐づける
- AI functions - バックエンドロジックをエージェントに公開する別の方法
- AI agent documentation - AIエージェントを構築・設定する