メインコンテンツまでスキップ

モデルコンテキストプロトコル (MCP)

標準の MCP protocol を通じて AI agents がバックエンドツールにアクセスできるように、カスタム MCP servers を作成します。

MCP を使う理由

AI agent が外部サーバー上でホストされている tools を呼び出す必要がある場合、または自分の backend logic を、任意の MCP-compatible client が発見して実行できる tools として公開したい場合に役立ちます。

MCP がない場合、agent-to-tool 接続ごとにカスタム integration logic を構築する必要があります。MCP を使えば、サーバー上で tools を定義し、互換性のある任意の agent が標準 protocol を通じてそれらを発見し、呼び出せます。

Backend code
// 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-compatible AI agent が標準 protocol を通じて getWeather を発見し、呼び出せるようになります。

概要

MCP (Model Context Protocol) は、AI agents が外部サーバー上の tools をどのように発見し呼び出すかを標準化するオープンな protocol です。Squid は、バックエンドで MCP servers を作成するための組み込みサポートを提供し、agents が JSON-RPC 経由で呼び出せるように、デコレーター付きメソッドとして tools を定義できます。

MCP を使うべき場面

ユースケース推奨
任意の MCP-compatible agent に backend tools を公開したいMCP server
会話中に agent がカスタム server-side logic を必要とするAI functions を使用
agent が外部の MCP server 上の tools を呼び出す必要があるMCP connector を使用
agent が接続済みサービス (CRM, API など) にアクセスしたいconnected integration を使用

仕組み

  1. SquidService を拡張した service 内で、クラスに @mcpServer を付与します
  2. そのクラス内のメソッドに @mcpTool を付与して server に tools を追加します
  3. Squid が deploy 時に MCP server を登録し、JSON-RPC endpoint として公開します
  4. MCP-compatible clients が接続し、tools/list で利用可能な tools を発見し、tools/call で呼び出します
  5. 必要に応じて、アクセス制御のために @mcpAuthorizer メソッドを追加します

クイックスタート

前提条件

  • squid init で初期化された Squid backend project
  • @squidcloud/backend package がNPMからインストールされていること
  • AI agent が作成済みであること(MCP server を Squid agent に接続したい場合)

Step 1: tool を持つ MCP server を作成する

SquidService を拡張する service class を作成し、@mcpServer を付与し、tool を追加します。

Backend code
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 を export する

service index file から service が export されていることを確認します。

service/index.ts
export * from './mcp-service';

Step 3: backend を deploy する

ローカル開発では、Squid CLI を使って backend をローカルで起動します。

squid start

クラウドへの deploy については、deploying your backend を参照してください。

Step 4: MCP server を agent に接続する

deploy 後、MCP server を connector として追加し、agent の abilities にアタッチします。

  1. Squid Console で MCP connector を追加し、deploy 済みの MCP server を指すように設定します
  2. Agent Studio で connector を agent の abilities に追加します

これで agent は会話中に MCP tools を発見し、呼び出せるようになります。

コア概念

@mcpServer デコレーター

@mcpServer デコレーターは、SquidService クラスを MCP server としてマークします。次のフィールドを持つ設定オブジェクトを受け取ります。

フィールド必須説明
idstringYesMCP endpoint URL で使用される一意の識別子
namestringYesMCP manifest で公開される server 名
descriptionstringYesserver の目的を説明します
versionstringYesMCP manifest で公開される server version

id は、アプリケーション内のすべての MCP servers で一意である必要があります。ID が重複すると deployment error になります。

Backend code
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 clients が発見して呼び出せる tool として公開します。次のフィールドを持つ設定オブジェクトを受け取ります。

フィールド必須説明
descriptionstringYestool の内容と、いつ呼び出すべきかを agent に伝えます
inputSchemaJSONSchemaYestool の入力パラメータを定義する JSON Schema
outputSchemaJSONSchemaNotool の出力フォーマットを説明する JSON Schema

メソッド名が MCP manifest 内の tool 名になります。各 tool 名は server 内で一意である必要があります。

Backend code
@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

inputSchemaJSON Schema 形式に従います。tool method は、schema に一致する properties を持つ単一の object parameter を受け取ります。

Backend code
@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 の返り値の構造を記述し、clients がレスポンス形式を理解しやすくします。

Backend code
@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 method は McpAuthorizationRequest object を受け取り、boolean(または Promise<boolean>)を返す必要があります。true を返すとリクエストを許可し、false を返すと "Unauthorized" error で拒否します。

Backend code
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 のフィールド

フィールド説明
bodyanyパース済みの JSON-RPC request body
queryParamsRecord<string, string>request URL からの query parameters
headersRecord<string, string>request の HTTP headers

@mcpAuthorizer メソッドが定義されていない場合、MCP server へのすべてのリクエストが許可されます。

エラーハンドリング

Tool errors

tool method が error を throw すると、MCP server はそれをキャッチし、isError: true を含む tool response として返します。呼び出し元の agent は error message を受け取り、それをユーザーに伝えるか、どのように進めるかを判断できます。

agent が有用なフィードバックを提供できるように、明確で説明的な error を throw してください。

Backend code
@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 は protocol-level の問題に対して標準の JSON-RPC error codes を使用します。

Error Code意味原因
-32001Unauthorized@mcpAuthorizer method が false を返した
-32601Method not foundリクエストされた JSON-RPC method または tool 名が存在しない
-32000Server errorMCP server ID が見つからない、または内部 error が発生した

よくある問題

問題原因解決策
duplicate ID error で deployment が失敗する2 つの @mcpServer クラスが同じ id を使用している各 MCP server に一意の id を使用する
duplicate tool name で deployment が失敗する1 つの server 内で 2 つの @mcpTool メソッド名が同じどちらかのメソッドをリネームする
agent によって tool が一度も呼ばれないtool description が user prompts と一致していないtool が何をするかを明確に書くよう description を書き直す
Authorization が常に失敗するtoken または header のチェックが誤っているデバッグのため McpAuthorizationRequest のフィールドを log する

ベストプラクティス

明確な tool descriptions を書く

description は、agent がいつ tool を呼び出すかを判断する主な手がかりです。tool が何をし、どんな情報を返すのかを具体的に書いてください。

Backend code
// 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 schemas を慎重に設計する

  • tool がそれなしでは動作できない場合にのみ、parameters を required にする
  • enum を使って値を既知のセットに制約する
  • agent がどの形式で渡せばよいか分かるよう、各 property に明確な description を書く
  • 適切な JSON Schema types(string, number, boolean, array, object)を使用する

MCP servers をセキュアにする

  • server が機密性の高い操作を公開する場合は、常に @mcpAuthorizer を追加する
  • authorization tokens はハードコードではなく、保存された secrets と照合して検証する
  • authentication(誰が呼び出しているか)と authorization(何ができるか)の両方をチェックする

tool inputs を検証する

input schema によって型制約は提供されますが、エッジケースに対応するため tool methods 内でも inputs を検証してください。

Backend code
@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}`;
}

tools を小さく集中させる

各 tool は 1 つのことをうまく行うべきです。多くの操作を扱う 1 つの tool より、複数の小さく集中した tools を推奨します。これにより agent がタスクに適した tool を選びやすくなります。

次のステップ

  • MCP connectors - Squid Console を通じて agent を MCP server に接続する
  • Abilities - MCP connectors やその他の tools を agent にアタッチする
  • AI functions - backend logic を agents に公開する別の方法
  • AI agent documentation - AI agents の構築と設定を行う