MCP(Model Context Protocol)とは?AIエコシステムの新たな標準
MCP(Model Context Protocol)は、2024年にAnthropicによって発表された革新的なプロトコルです。AIアシスタント(Claude、ChatGPT、Geminiなど)と外部システム(データベース、API、ファイルシステムなど)を標準化された安全な方法で接続することを可能にします。
従来、AIアシスタントと外部システムの連携は、各プロバイダーが独自の方法で実装しており、開発者にとって複雑で断片化された環境でした。MCPはこの問題を解決し、統一されたインターフェースを提供することで、AIアプリケーションの開発を大幅に簡素化します。
MCPが解決する主要な課題
- 標準化の欠如: 各AIプロバイダーが独自のAPI形式を使用
- セキュリティの複雑さ: 外部システムとの安全な通信の実装が困難
- 開発効率の低下: システム間の連携に大量のボイラープレートコードが必要
- 拡張性の制限: 新しいシステムを追加する際の高い開発コスト
// MCPの基本概念を示すTypeScriptの例
interface MCPConnection {
// 標準化されたプロトコルインターフェース
protocol: 'mcp://';
version: '1.0';
authentication: MCPAuth;
capabilities: MCPCapabilities;
}
interface MCPMessage {
type: 'request' | 'response' | 'notification';
id?: string;
method: string;
params?: any;
result?: any;
error?: MCPError;
}
// 使用例:データベースへのクエリ
const databaseQuery: MCPMessage = {
type: 'request',
id: 'req-001',
method: 'database.query',
params: {
query: 'SELECT * FROM customers WHERE region = ?',
parameters: ['North America']
}
};MCPの主要なメリット
- 統一されたインターフェース: 一度学習すれば、あらゆるAIアシスタントで利用可能
- セキュリティの向上: 組み込みの認証・認可機能
- 開発効率の向上: 標準化されたライブラリとツール
- 相互運用性: 異なるAIプロバイダー間でのシームレスな移行
2025年現在、主要なAIプロバイダー(Anthropic、OpenAI、Google)がMCPサポートを表明しており、エンタープライズ環境でのAI導入において重要な技術標準となっています。
最短で課題解決する一冊
この記事の内容と高い親和性が確認できたベストマッチです。早めにチェックしておきましょう。
MCPアーキテクチャ:クライアント、サーバー、トランスポート層の詳細
MCPは、クライアント・サーバーアーキテクチャを基盤とした3層構造を採用しています。この設計により、柔軟性とセキュリティを両立させながら、シンプルな実装を可能にしています。
アーキテクチャの概要
graph TB
AI[AIアシスタント] --> Client[MCPクライアント]
Client --> Transport[トランスポート層]
Transport --> Server[MCPサーバー]
Server --> Resource[外部リソース]
subgraph "MCPクライアント"
ClientAPI[Client API]
Auth[認証管理]
Cache[キャッシュ]
end
subgraph "トランスポート層"
HTTP[HTTP/HTTPS]
WS[WebSocket]
IPC[プロセス間通信]
end
subgraph "MCPサーバー"
ServerAPI[Server API]
Handler[リクエストハンドラ]
Security[セキュリティ層]
end
subgraph "外部リソース"
DB[(データベース)]
API[外部API]
File[ファイルシステム]
endMCPクライアントの実装
// MCPクライアントの実装例
import { EventEmitter } from 'events';
class MCPClient extends EventEmitter {
private connection: MCPConnection;
private requestId = 0;
private pendingRequests = new Map<string, any>();
constructor(config: MCPClientConfig) {
super();
this.connection = this.createConnection(config);
}
async connect(): Promise<void> {
try {
await this.connection.connect();
// 初期化ハンドシェイク
const initResponse = await this.request('initialize', {
clientInfo: {
name: 'MyAIApp',
version: '1.0.0'
},
capabilities: {
roots: true,
sampling: true
}
});
console.log('MCP接続が確立されました:', initResponse);
this.emit('connected');
} catch (error) {
console.error('MCP接続エラー:', error);
this.emit('error', error);
}
}
async request(method: string, params?: any): Promise<any> {
const id = `req-${++this.requestId}`;
const message: MCPMessage = {
type: 'request',
id,
method,
params
};
return new Promise((resolve, reject) => {
this.pendingRequests.set(id, { resolve, reject });
this.connection.send(message);
// タイムアウト設定
setTimeout(() => {
if (this.pendingRequests.has(id)) {
this.pendingRequests.delete(id);
reject(new Error(`リクエストタイムアウト: ${method}`));
}
}, 30000);
});
}
async listResources(): Promise<MCPResource[]> {
const response = await this.request('resources/list');
return response.resources;
}
async readResource(uri: string): Promise<any> {
const response = await this.request('resources/read', { uri });
return response.contents;
}
async callTool(name: string, arguments_?: any): Promise<any> {
const response = await this.request('tools/call', {
name,
arguments: arguments_
});
return response.content;
}
private handleMessage(message: MCPMessage): void {
if (message.type === 'response' && message.id) {
const pending = this.pendingRequests.get(message.id);
if (pending) {
this.pendingRequests.delete(message.id);
if (message.error) {
pending.reject(new Error(message.error.message));
} else {
pending.resolve(message.result);
}
}
}
}
}MCPサーバーの実装
// MCPサーバーの実装例
class MCPServer {
private tools = new Map<string, MCPTool>();
private resources = new Map<string, MCPResource>();
private prompts = new Map<string, MCPPrompt>();
constructor(private config: MCPServerConfig) {
this.setupDefaultHandlers();
}
// ツールの登録
registerTool(tool: MCPTool): void {
this.tools.set(tool.name, tool);
console.log(`ツールが登録されました: ${tool.name}`);
}
// リソースの登録
registerResource(resource: MCPResource): void {
this.resources.set(resource.uri, resource);
console.log(`リソースが登録されました: ${resource.uri}`);
}
async handleRequest(message: MCPMessage): Promise<MCPMessage> {
try {
switch (message.method) {
case 'initialize':
return this.handleInitialize(message);
case 'tools/list':
return this.handleToolsList(message);
case 'tools/call':
return this.handleToolCall(message);
case 'resources/list':
return this.handleResourcesList(message);
case 'resources/read':
return this.handleResourceRead(message);
default:
throw new Error(`未対応のメソッド: ${message.method}`);
}
} catch (error) {
return {
type: 'response',
id: message.id,
error: {
code: -32603,
message: error.message
}
};
}
}
private async handleToolCall(message: MCPMessage): Promise<MCPMessage> {
const { name, arguments: args } = message.params;
const tool = this.tools.get(name);
if (!tool) {
throw new Error(`ツールが見つかりません: ${name}`);
}
// セキュリティチェック
this.validateToolAccess(tool, args);
const result = await tool.execute(args);
return {
type: 'response',
id: message.id,
result: {
content: [{
type: 'text',
text: result
}]
}
};
}
private validateToolAccess(tool: MCPTool, args: any): void {
// 権限チェック
if (tool.requiresAuth && !this.isAuthenticated()) {
throw new Error('認証が必要です');
}
// 引数の検証
if (tool.schema) {
this.validateArguments(args, tool.schema);
}
}
private setupDefaultHandlers(): void {
// ファイルシステムアクセスツール
this.registerTool({
name: 'file_read',
description: 'ファイルの内容を読み取り',
schema: {
type: 'object',
properties: {
path: { type: 'string' }
},
required: ['path']
},
execute: async (args) => {
const fs = await import('fs/promises');
return await fs.readFile(args.path, 'utf-8');
}
});
// データベースクエリツール
this.registerTool({
name: 'database_query',
description: 'データベースクエリの実行',
requiresAuth: true,
schema: {
type: 'object',
properties: {
query: { type: 'string' },
parameters: { type: 'array' }
},
required: ['query']
},
execute: async (args) => {
// データベース接続とクエリ実行
return this.executeQuery(args.query, args.parameters);
}
});
}
}トランスポート層の実装
// WebSocketトランスポートの実装例
class WebSocketTransport implements MCPTransport {
private ws: WebSocket;
private messageHandlers = new Set<(message: MCPMessage) => void>();
constructor(private url: string) {}
async connect(): Promise<void> {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket接続が確立されました');
resolve();
};
this.ws.onerror = (error) => {
console.error('WebSocket接続エラー:', error);
reject(error);
};
this.ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
this.messageHandlers.forEach(handler => handler(message));
} catch (error) {
console.error('メッセージ解析エラー:', error);
}
};
});
}
send(message: MCPMessage): void {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message));
} else {
throw new Error('WebSocket接続が閉じています');
}
}
onMessage(handler: (message: MCPMessage) => void): void {
this.messageHandlers.add(handler);
}
close(): void {
if (this.ws) {
this.ws.close();
}
}
}
// HTTP/RESTトランスポートの実装例
class HTTPTransport implements MCPTransport {
constructor(private baseUrl: string, private apiKey?: string) {}
async send(message: MCPMessage): Promise<MCPMessage> {
const headers: Record<string, string> = {
'Content-Type': 'application/json'
};
if (this.apiKey) {
headers['Authorization'] = `Bearer ${this.apiKey}`;
}
const response = await fetch(`${this.baseUrl}/mcp`, {
method: 'POST',
headers,
body: JSON.stringify(message)
});
if (!response.ok) {
throw new Error(`HTTP エラー: ${response.status}`);
}
return await response.json();
}
}このアーキテクチャにより、MCPは柔軟性とパフォーマンスを両立させながら、さまざまな外部システムとの連携を可能にしています。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
実装手順:ゼロからMCPサーバーを構築する
実際のプロジェクトでMCPサーバーを構築する手順を、具体的なコード例とともに詳しく解説します。今回は、ECサイトのデータ分析をサポートするMCPサーバーを構築してみましょう。
1. プロジェクト初期化とセットアップ
# プロジェクトディレクトリの作成
mkdir mcp-ecommerce-server
cd mcp-ecommerce-server
# Node.jsプロジェクトの初期化
npm init -y
# 必要なパッケージのインストール
npm install @anthropic-ai/mcp-server typescript @types/node
npm install express cors helmet sqlite3 zod dotenv
npm install -D @types/express @types/cors ts-node nodemon
# TypeScript設定ファイルの作成
npx tsc --init// package.json のスクリプト設定
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "nodemon --exec ts-node src/index.ts",
"test": "jest"
}
}// tsconfig.json の設定
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}2. データベーススキーマとモックデータの準備
// src/database/schema.ts
import sqlite3 from 'sqlite3';
import { Database, open } from 'sqlite';
export interface Customer {
id: number;
name: string;
email: string;
region: string;
registrationDate: string;
}
export interface Product {
id: number;
name: string;
category: string;
price: number;
stockQuantity: number;
}
export interface Order {
id: number;
customerId: number;
productId: number;
quantity: number;
orderDate: string;
totalAmount: number;
}
export class DatabaseManager {
private db: Database;
async initialize(): Promise<void> {
this.db = await open({
filename: './ecommerce.db',
driver: sqlite3.Database
});
await this.createTables();
await this.insertMockData();
}
private async createTables(): Promise<void> {
await this.db.exec(`
CREATE TABLE IF NOT EXISTS customers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
region TEXT NOT NULL,
registration_date TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
category TEXT NOT NULL,
price REAL NOT NULL,
stock_quantity INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
order_date TEXT NOT NULL,
total_amount REAL NOT NULL,
FOREIGN KEY (customer_id) REFERENCES customers (id),
FOREIGN KEY (product_id) REFERENCES products (id)
);
`);
}
private async insertMockData(): Promise<void> {
// 顧客データの挿入
const customers = [
{ name: '田中太郎', email: 'tanaka@example.com', region: 'Tokyo', registrationDate: '2024-01-15' },
{ name: '佐藤花子', email: 'sato@example.com', region: 'Osaka', registrationDate: '2024-02-20' },
{ name: 'John Smith', email: 'john@example.com', region: 'US', registrationDate: '2024-03-10' },
{ name: 'Alice Johnson', email: 'alice@example.com', region: 'EU', registrationDate: '2024-04-05' }
];
for (const customer of customers) {
await this.db.run(
'INSERT OR IGNORE INTO customers (name, email, region, registration_date) VALUES (?, ?, ?, ?)',
[customer.name, customer.email, customer.region, customer.registrationDate]
);
}
// 商品データの挿入
const products = [
{ name: 'ノートパソコン', category: 'Electronics', price: 89999, stockQuantity: 50 },
{ name: 'スマートフォン', category: 'Electronics', price: 59999, stockQuantity: 100 },
{ name: 'コーヒーメーカー', category: 'Appliances', price: 12999, stockQuantity: 30 },
{ name: 'ワイヤレスヘッドホン', category: 'Electronics', price: 19999, stockQuantity: 75 }
];
for (const product of products) {
await this.db.run(
'INSERT OR IGNORE INTO products (name, category, price, stock_quantity) VALUES (?, ?, ?, ?)',
[product.name, product.category, product.price, product.stockQuantity]
);
}
// 注文データの挿入
const orders = [
{ customerId: 1, productId: 1, quantity: 1, orderDate: '2024-05-01', totalAmount: 89999 },
{ customerId: 2, productId: 2, quantity: 2, orderDate: '2024-05-03', totalAmount: 119998 },
{ customerId: 3, productId: 3, quantity: 1, orderDate: '2024-05-05', totalAmount: 12999 },
{ customerId: 1, productId: 4, quantity: 1, orderDate: '2024-05-07', totalAmount: 19999 }
];
for (const order of orders) {
await this.db.run(
'INSERT OR IGNORE INTO orders (customer_id, product_id, quantity, order_date, total_amount) VALUES (?, ?, ?, ?, ?)',
[order.customerId, order.productId, order.quantity, order.orderDate, order.totalAmount]
);
}
}
async query(sql: string, params: any[] = []): Promise<any[]> {
return await this.db.all(sql, params);
}
async close(): Promise<void> {
await this.db.close();
}
}3. MCPツールの実装
// src/tools/ecommerce-tools.ts
import { z } from 'zod';
import { DatabaseManager } from '../database/schema';
export interface MCPTool {
name: string;
description: string;
inputSchema: z.ZodSchema;
execute: (params: any, db: DatabaseManager) => Promise<any>;
}
// 顧客分析ツール
export const customerAnalysisTool: MCPTool = {
name: 'analyze_customers',
description: '顧客データの分析を実行します(地域別、登録日別など)',
inputSchema: z.object({
analysisType: z.enum(['region', 'registration_trend', 'summary']),
dateRange: z.object({
start: z.string().optional(),
end: z.string().optional()
}).optional()
}),
execute: async (params, db) => {
switch (params.analysisType) {
case 'region':
const regionData = await db.query(`
SELECT region, COUNT(*) as customer_count
FROM customers
GROUP BY region
ORDER BY customer_count DESC
`);
return {
type: 'region_analysis',
data: regionData,
summary: `地域別顧客分布: ${regionData.map(r => `${r.region}: ${r.customer_count}人`).join(', ')}`
};
case 'registration_trend':
const trendData = await db.query(`
SELECT DATE(registration_date) as date, COUNT(*) as registrations
FROM customers
GROUP BY DATE(registration_date)
ORDER BY date
`);
return {
type: 'registration_trend',
data: trendData,
summary: `登録トレンド: 合計${trendData.reduce((sum, d) => sum + d.registrations, 0)}人の顧客が登録`
};
case 'summary':
const summary = await db.query(`
SELECT
COUNT(*) as total_customers,
COUNT(DISTINCT region) as unique_regions,
MIN(registration_date) as first_registration,
MAX(registration_date) as latest_registration
FROM customers
`);
return {
type: 'customer_summary',
data: summary[0],
summary: `顧客概要: 総顧客数${summary[0].total_customers}人、${summary[0].unique_regions}地域にサービス展開`
};
default:
throw new Error('不正な分析タイプです');
}
}
};
// 売上分析ツール
export const salesAnalysisTool: MCPTool = {
name: 'analyze_sales',
description: '売上データの分析を実行します(期間別、商品別、地域別など)',
inputSchema: z.object({
analysisType: z.enum(['period', 'product', 'customer_segment']),
dateRange: z.object({
start: z.string().optional(),
end: z.string().optional()
}).optional(),
groupBy: z.enum(['day', 'week', 'month']).optional()
}),
execute: async (params, db) => {
let dateFilter = '';
const queryParams: any[] = [];
if (params.dateRange?.start && params.dateRange?.end) {
dateFilter = 'WHERE o.order_date BETWEEN ? AND ?';
queryParams.push(params.dateRange.start, params.dateRange.end);
}
switch (params.analysisType) {
case 'period':
const groupBy = params.groupBy || 'day';
let dateFormat = '%Y-%m-%d';
if (groupBy === 'week') dateFormat = '%Y-%W';
if (groupBy === 'month') dateFormat = '%Y-%m';
const periodData = await db.query(`
SELECT
strftime('${dateFormat}', o.order_date) as period,
SUM(o.total_amount) as total_sales,
COUNT(*) as order_count,
AVG(o.total_amount) as average_order_value
FROM orders o
${dateFilter}
GROUP BY strftime('${dateFormat}', o.order_date)
ORDER BY period
`, queryParams);
return {
type: 'period_sales',
data: periodData,
summary: `期間別売上分析: 総売上${periodData.reduce((sum, d) => sum + d.total_sales, 0).toLocaleString()}円`
};
case 'product':
const productData = await db.query(`
SELECT
p.name as product_name,
p.category,
SUM(o.quantity) as total_quantity,
SUM(o.total_amount) as total_sales,
AVG(o.total_amount / o.quantity) as average_price
FROM orders o
JOIN products p ON o.product_id = p.id
${dateFilter}
GROUP BY p.id, p.name, p.category
ORDER BY total_sales DESC
`, queryParams);
return {
type: 'product_sales',
data: productData,
summary: `商品別売上: トップ商品は${productData[0]?.product_name}(${productData[0]?.total_sales.toLocaleString()}円)`
};
case 'customer_segment':
const segmentData = await db.query(`
SELECT
c.region,
COUNT(DISTINCT c.id) as customer_count,
SUM(o.total_amount) as total_sales,
AVG(o.total_amount) as average_order_value,
COUNT(o.id) as total_orders
FROM customers c
JOIN orders o ON c.id = o.customer_id
${dateFilter}
GROUP BY c.region
ORDER BY total_sales DESC
`, queryParams);
return {
type: 'customer_segment_sales',
data: segmentData,
summary: `地域別顧客セグメント: 最高売上地域は${segmentData[0]?.region}(${segmentData[0]?.total_sales.toLocaleString()}円)`
};
default:
throw new Error('不正な分析タイプです');
}
}
};
// 在庫管理ツール
export const inventoryTool: MCPTool = {
name: 'check_inventory',
description: '在庫状況の確認と分析を実行します',
inputSchema: z.object({
action: z.enum(['check_low_stock', 'product_details', 'category_summary']),
threshold: z.number().optional(),
productId: z.number().optional(),
category: z.string().optional()
}),
execute: async (params, db) => {
switch (params.action) {
case 'check_low_stock':
const threshold = params.threshold || 20;
const lowStockProducts = await db.query(`
SELECT
id,
name,
category,
stock_quantity,
price
FROM products
WHERE stock_quantity <= ?
ORDER BY stock_quantity ASC
`, [threshold]);
return {
type: 'low_stock_alert',
data: lowStockProducts,
summary: `在庫不足商品: ${lowStockProducts.length}商品が閾値${threshold}以下`
};
case 'product_details':
if (!params.productId) {
throw new Error('商品IDが必要です');
}
const productDetails = await db.query(`
SELECT
p.*,
COALESCE(SUM(o.quantity), 0) as total_sold,
COALESCE(SUM(o.total_amount), 0) as total_revenue
FROM products p
LEFT JOIN orders o ON p.id = o.product_id
WHERE p.id = ?
GROUP BY p.id
`, [params.productId]);
return {
type: 'product_details',
data: productDetails[0],
summary: `商品詳細: ${productDetails[0]?.name} - 在庫${productDetails[0]?.stock_quantity}個、売上${productDetails[0]?.total_revenue.toLocaleString()}円`
};
case 'category_summary':
const categoryData = await db.query(`
SELECT
category,
COUNT(*) as product_count,
SUM(stock_quantity) as total_stock,
AVG(price) as average_price,
MIN(stock_quantity) as min_stock,
MAX(stock_quantity) as max_stock
FROM products
${params.category ? 'WHERE category = ?' : ''}
GROUP BY category
ORDER BY product_count DESC
`, params.category ? [params.category] : []);
return {
type: 'category_inventory',
data: categoryData,
summary: `カテゴリ別在庫概要: ${categoryData.length}カテゴリ、総在庫${categoryData.reduce((sum, c) => sum + c.total_stock, 0)}個`
};
default:
throw new Error('不正なアクションです');
}
}
};
export const allTools = [
customerAnalysisTool,
salesAnalysisTool,
inventoryTool
];4. MCPサーバーのメイン実装
// src/server.ts
import { MCPServer } from '@anthropic-ai/mcp-server';
import { DatabaseManager } from './database/schema';
import { allTools } from './tools/ecommerce-tools';
export class EcommerceMCPServer {
private server: MCPServer;
private db: DatabaseManager;
constructor() {
this.db = new DatabaseManager();
this.server = new MCPServer(
{
name: 'ecommerce-analytics',
version: '1.0.0'
},
{
capabilities: {
tools: {},
resources: {}
}
}
);
this.setupToolHandlers();
this.setupResourceHandlers();
}
async initialize(): Promise<void> {
await this.db.initialize();
console.log('ECサイト分析MCPサーバーが初期化されました');
}
private setupToolHandlers(): void {
// ツールリストの提供
this.server.setRequestHandler('tools/list', async () => {
return {
tools: allTools.map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema
}))
};
});
// ツール実行の処理
this.server.setRequestHandler('tools/call', async (request) => {
const { name, arguments: args } = request.params;
const tool = allTools.find(t => t.name === name);
if (!tool) {
throw new Error(`ツールが見つかりません: ${name}`);
}
try {
// 入力の検証
const validatedArgs = tool.inputSchema.parse(args);
// ツールの実行
const result = await tool.execute(validatedArgs, this.db);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2)
}
]
};
} catch (error) {
throw new Error(`ツール実行エラー: ${error.message}`);
}
});
}
private setupResourceHandlers(): void {
// 利用可能なリソースの一覧
this.server.setRequestHandler('resources/list', async () => {
return {
resources: [
{
uri: 'schema://database/customers',
name: '顧客テーブルスキーマ',
mimeType: 'application/json'
},
{
uri: 'schema://database/products',
name: '商品テーブルスキーマ',
mimeType: 'application/json'
},
{
uri: 'schema://database/orders',
name: '注文テーブルスキーマ',
mimeType: 'application/json'
}
]
};
});
// リソースの読み取り
this.server.setRequestHandler('resources/read', async (request) => {
const { uri } = request.params;
switch (uri) {
case 'schema://database/customers':
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify({
table: 'customers',
columns: [
{ name: 'id', type: 'INTEGER', primary: true },
{ name: 'name', type: 'TEXT', required: true },
{ name: 'email', type: 'TEXT', unique: true },
{ name: 'region', type: 'TEXT' },
{ name: 'registration_date', type: 'TEXT' }
]
}, null, 2)
}
]
};
case 'schema://database/products':
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify({
table: 'products',
columns: [
{ name: 'id', type: 'INTEGER', primary: true },
{ name: 'name', type: 'TEXT', required: true },
{ name: 'category', type: 'TEXT' },
{ name: 'price', type: 'REAL' },
{ name: 'stock_quantity', type: 'INTEGER' }
]
}, null, 2)
}
]
};
case 'schema://database/orders':
return {
contents: [
{
uri,
mimeType: 'application/json',
text: JSON.stringify({
table: 'orders',
columns: [
{ name: 'id', type: 'INTEGER', primary: true },
{ name: 'customer_id', type: 'INTEGER', foreign_key: 'customers.id' },
{ name: 'product_id', type: 'INTEGER', foreign_key: 'products.id' },
{ name: 'quantity', type: 'INTEGER' },
{ name: 'order_date', type: 'TEXT' },
{ name: 'total_amount', type: 'REAL' }
]
}, null, 2)
}
]
};
default:
throw new Error(`リソースが見つかりません: ${uri}`);
}
});
}
async start(): Promise<void> {
await this.server.start();
console.log('MCPサーバーが開始されました');
}
async stop(): Promise<void> {
await this.db.close();
await this.server.close();
console.log('MCPサーバーが停止されました');
}
}5. エントリーポイントの作成
// src/index.ts
import { EcommerceMCPServer } from './server';
import dotenv from 'dotenv';
dotenv.config();
async function main() {
const server = new EcommerceMCPServer();
try {
await server.initialize();
await server.start();
console.log('ECサイト分析MCPサーバーが正常に開始されました!');
console.log('利用可能なツール:');
console.log('- analyze_customers: 顧客データ分析');
console.log('- analyze_sales: 売上データ分析');
console.log('- check_inventory: 在庫状況確認');
} catch (error) {
console.error('サーバー開始エラー:', error);
process.exit(1);
}
// グレースフルシャットダウン
process.on('SIGINT', async () => {
console.log('\nシャットダウン中...');
await server.stop();
process.exit(0);
});
}
if (require.main === module) {
main().catch(console.error);
}これで、ECサイト分析をサポートする本格的なMCPサーバーが完成しました。次のセクションでは、このサーバーをClaude Desktopなどのクライアントと連携させる方法を説明します。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
Claude Desktop・ChatGPTとの統合方法
構築したMCPサーバーを実際のAIアシスタントと連携させる方法を詳しく解説します。Claude Desktop、ChatGPT、その他のAIクライアントとの統合手順を示します。
Claude Desktopとの連携設定
Claude Desktopは、MCPを公式サポートする最初のクライアントの一つです。設定ファイルを通じて簡単にMCPサーバーを追加できます。
# Claude Desktop設定ファイル(~/.claude/config.json)
{
"mcpServers": {
"ecommerce-analytics": {
"command": "node",
"args": ["/path/to/your/mcp-server/dist/index.js"],
"env": {
"NODE_ENV": "production"
},
"description": "ECサイトデータ分析サーバー"
},
"file-system": {
"command": "npx",
"args": ["-y", "@anthropic-ai/mcp-server-filesystem", "/Users/username/Documents"],
"description": "ファイルシステムアクセス"
},
"git-integration": {
"command": "node",
"args": ["/path/to/git-mcp-server/index.js"],
"env": {
"GIT_REPOS_PATH": "/Users/username/repositories"
},
"description": "Git操作サポート"
}
},
"globalSettings": {
"mcpEnabled": true,
"mcpTimeout": 30000,
"maxConcurrentRequests": 5
}
}Claude DesktopでのMCP使用例
Claude Desktopでは、以下のようにMCPサーバーを活用できます:
// Claude Desktopでの実際の使用例
class ClaudeDesktopIntegration {
private setupMCPPrompts(): void {
// ECサイト分析の例
const analysisPrompt = `
MCPサーバーを使用してECサイトの包括的な分析を実行してください。
実行してほしい分析:
1. 顧客の地域別分布を分析
2. 過去30日間の売上トレンドを確認
3. 在庫が20個以下の商品をチェック
4. 最も売れている商品カテゴリを特定
各分析結果に基づいて、ビジネス改善のための具体的な提案をしてください。
`;
// Claude Desktopはこのプロンプトを受けて、自動的に適切なMCPツールを選択・実行
return analysisPrompt;
}
private generateReportPrompt(): string {
return `
MCPツールを使用して月次レポートを生成してください:
レポート内容:
- 顧客獲得数とトレンド
- 商品別売上ランキング
- 地域別パフォーマンス
- 在庫状況サマリー
- KPIダッシュボード
結果はMarkdown形式で整理し、グラフやチャートの説明も含めてください。
`;
}
}ChatGPT(OpenAI)との統合
ChatGPTでMCPを使用するには、カスタムアクション機能を活用します:
// OpenAI Custom Actions用のMCPブリッジ
class OpenAIMCPBridge {
constructor(mcpServerUrl, apiKey) {
this.mcpServerUrl = mcpServerUrl;
this.apiKey = apiKey;
}
async createCustomAction() {
return {
openapi: "3.1.0",
info: {
title: "ECサイト分析API",
description: "MCPサーバー経由でECサイトデータを分析",
version: "1.0.0"
},
servers: [
{
url: this.mcpServerUrl,
description: "MCP Server"
}
],
paths: {
"/mcp/tools/analyze_customers": {
post: {
operationId: "analyzeCustomers",
summary: "顧客データ分析",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
analysisType: {
type: "string",
enum: ["region", "registration_trend", "summary"]
},
dateRange: {
type: "object",
properties: {
start: { type: "string" },
end: { type: "string" }
}
}
},
required: ["analysisType"]
}
}
}
},
responses: {
"200": {
description: "分析結果",
content: {
"application/json": {
schema: {
type: "object",
properties: {
type: { type: "string" },
data: { type: "array" },
summary: { type: "string" }
}
}
}
}
}
}
}
},
"/mcp/tools/analyze_sales": {
post: {
operationId: "analyzeSales",
summary: "売上データ分析",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
analysisType: {
type: "string",
enum: ["period", "product", "customer_segment"]
},
dateRange: {
type: "object",
properties: {
start: { type: "string" },
end: { type: "string" }
}
},
groupBy: {
type: "string",
enum: ["day", "week", "month"]
}
},
required: ["analysisType"]
}
}
}
}
}
}
}
};
}
// Express.jsサーバーでMCPブリッジエンドポイントを作成
setupExpressEndpoints(app) {
app.post('/mcp/tools/:toolName', async (req, res) => {
try {
const { toolName } = req.params;
const { arguments: args } = req.body;
// MCPサーバーへのプロキシリクエスト
const mcpResponse = await this.callMCPTool(toolName, args);
res.json(mcpResponse);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
}
async callMCPTool(toolName, args) {
const response = await fetch(`${this.mcpServerUrl}/tools/call`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
name: toolName,
arguments: args
})
});
return await response.json();
}
}Web UI統合とダッシュボード作成
より実用的なMCP活用のため、Web UIを作成します:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ECサイト分析ダッシュボード</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">
<div id="app" class="container mx-auto px-4 py-8">
<h1 class="text-3xl font-bold mb-8">ECサイト分析ダッシュボード</h1>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8">
<!-- KPIカード -->
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-2">総顧客数</h3>
<p class="text-3xl font-bold text-blue-600" id="totalCustomers">-</p>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-2">月間売上</h3>
<p class="text-3xl font-bold text-green-600" id="monthlySales">-</p>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-2">在庫アラート</h3>
<p class="text-3xl font-bold text-red-600" id="lowStockItems">-</p>
</div>
</div>
<!-- チャートセクション -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4">地域別顧客分布</h3>
<canvas id="regionChart"></canvas>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4">売上トレンド</h3>
<canvas id="salesChart"></canvas>
</div>
</div>
<!-- コントロールパネル -->
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4">分析実行</h3>
<div class="flex flex-wrap gap-4">
<button onclick="analyzeCustomers()" class="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600">
顧客分析
</button>
<button onclick="analyzeSales()" class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-600">
売上分析
</button>
<button onclick="checkInventory()" class="bg-red-500 text-white px-4 py-2 rounded hover:bg-red-600">
在庫確認
</button>
<button onclick="generateReport()" class="bg-purple-500 text-white px-4 py-2 rounded hover:bg-purple-600">
レポート生成
</button>
</div>
</div>
<!-- 結果表示エリア -->
<div id="results" class="mt-8"></div>
</div>
<script src="dashboard.js"></script>
</body>
</html>// public/dashboard.js
class MCPDashboard {
constructor() {
this.mcpApiUrl = '/api/mcp';
this.initialize();
}
async initialize() {
await this.loadInitialData();
this.setupCharts();
}
async loadInitialData() {
try {
// 顧客概要データの読み込み
const customerSummary = await this.callMCPTool('analyze_customers', {
analysisType: 'summary'
});
document.getElementById('totalCustomers').textContent =
customerSummary.data.total_customers;
// 在庫アラートの確認
const lowStock = await this.callMCPTool('check_inventory', {
action: 'check_low_stock',
threshold: 20
});
document.getElementById('lowStockItems').textContent =
lowStock.data.length;
} catch (error) {
console.error('初期データ読み込みエラー:', error);
}
}
async callMCPTool(toolName, args) {
const response = await fetch(`${this.mcpApiUrl}/tools/${toolName}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ arguments: args })
});
if (!response.ok) {
throw new Error(`API呼び出しエラー: ${response.statusText}`);
}
return await response.json();
}
setupCharts() {
// 地域別顧客分布チャート
this.regionChart = new Chart(document.getElementById('regionChart'), {
type: 'pie',
data: {
labels: [],
datasets: [{
data: [],
backgroundColor: [
'#FF6384',
'#36A2EB',
'#FFCE56',
'#4BC0C0',
'#9966FF'
]
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
// 売上トレンドチャート
this.salesChart = new Chart(document.getElementById('salesChart'), {
type: 'line',
data: {
labels: [],
datasets: [{
label: '売上',
data: [],
borderColor: '#36A2EB',
backgroundColor: '#36A2EB20',
tension: 0.1
}]
},
options: {
responsive: true,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return '¥' + value.toLocaleString();
}
}
}
}
}
});
}
async updateRegionChart() {
try {
const data = await this.callMCPTool('analyze_customers', {
analysisType: 'region'
});
this.regionChart.data.labels = data.data.map(item => item.region);
this.regionChart.data.datasets[0].data = data.data.map(item => item.customer_count);
this.regionChart.update();
} catch (error) {
console.error('地域チャート更新エラー:', error);
}
}
async updateSalesChart() {
try {
const data = await this.callMCPTool('analyze_sales', {
analysisType: 'period',
groupBy: 'day'
});
this.salesChart.data.labels = data.data.map(item => item.period);
this.salesChart.data.datasets[0].data = data.data.map(item => item.total_sales);
this.salesChart.update();
} catch (error) {
console.error('売上チャート更新エラー:', error);
}
}
displayResults(title, data) {
const resultsDiv = document.getElementById('results');
resultsDiv.innerHTML = `
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-4">${title}</h3>
<pre class="bg-gray-100 p-4 rounded overflow-auto">${JSON.stringify(data, null, 2)}</pre>
</div>
`;
}
}
// グローバル関数(ボタンから呼び出される)
let dashboard;
document.addEventListener('DOMContentLoaded', () => {
dashboard = new MCPDashboard();
});
async function analyzeCustomers() {
try {
const result = await dashboard.callMCPTool('analyze_customers', {
analysisType: 'region'
});
dashboard.displayResults('顧客分析結果', result);
await dashboard.updateRegionChart();
} catch (error) {
alert('顧客分析エラー: ' + error.message);
}
}
async function analyzeSales() {
try {
const result = await dashboard.callMCPTool('analyze_sales', {
analysisType: 'product'
});
dashboard.displayResults('売上分析結果', result);
await dashboard.updateSalesChart();
} catch (error) {
alert('売上分析エラー: ' + error.message);
}
}
async function checkInventory() {
try {
const result = await dashboard.callMCPTool('check_inventory', {
action: 'check_low_stock',
threshold: 20
});
dashboard.displayResults('在庫状況', result);
} catch (error) {
alert('在庫確認エラー: ' + error.message);
}
}
async function generateReport() {
try {
// 複数の分析を組み合わせてレポート生成
const [customers, sales, inventory] = await Promise.all([
dashboard.callMCPTool('analyze_customers', { analysisType: 'summary' }),
dashboard.callMCPTool('analyze_sales', { analysisType: 'period', groupBy: 'month' }),
dashboard.callMCPTool('check_inventory', { action: 'category_summary' })
]);
const report = {
generatedAt: new Date().toISOString(),
customerSummary: customers,
salesTrend: sales,
inventoryStatus: inventory
};
dashboard.displayResults('統合レポート', report);
} catch (error) {
alert('レポート生成エラー: ' + error.message);
}
}この統合により、MCPサーバーがさまざまなAIクライアントと連携し、実用的なビジネス分析ツールとして機能します。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
実用例とベストプラクティス
MCPを実際のプロジェクトで効果的に活用するための実用例と、開発・運用における重要なベストプラクティスを紹介します。
エンタープライズ環境での実用例
1. 人事システム統合によるAI人事アシスタント
// 人事システム用MCPサーバーの実装例
class HRMCPServer {
private hrDatabase: HRDatabase;
private slackClient: SlackClient;
private calendarAPI: CalendarAPI;
constructor() {
this.setupHRTools();
}
private setupHRTools(): void {
// 従業員情報検索ツール
this.registerTool({
name: 'search_employees',
description: '従業員情報の検索と分析',
inputSchema: z.object({
searchType: z.enum(['by_department', 'by_skill', 'by_performance', 'by_availability']),
criteria: z.object({
department: z.string().optional(),
skills: z.array(z.string()).optional(),
performanceRating: z.number().min(1).max(5).optional(),
availabilityDate: z.string().optional()
}),
includeDetails: z.boolean().default(false)
}),
execute: async (params) => {
const employees = await this.hrDatabase.searchEmployees(params);
if (params.includeDetails) {
// 詳細情報を含める場合は、プライバシー設定を確認
return await this.enrichEmployeeData(employees);
}
return employees.map(emp => ({
id: emp.id,
name: emp.name,
department: emp.department,
title: emp.title,
availability: emp.availability
}));
}
});
// プロジェクトアサインメント最適化ツール
this.registerTool({
name: 'optimize_project_assignment',
description: 'スキルマッチングと可用性に基づくプロジェクトアサインメント最適化',
inputSchema: z.object({
projectRequirements: z.object({
requiredSkills: z.array(z.string()),
startDate: z.string(),
endDate: z.string(),
teamSize: z.number(),
priority: z.enum(['low', 'medium', 'high', 'critical'])
}),
constraints: z.object({
excludeEmployees: z.array(z.string()).optional(),
preferredDepartments: z.array(z.string()).optional(),
maxWorkload: z.number().default(100)
}).optional()
}),
execute: async (params) => {
// AI最適化アルゴリズムを使用してチーム編成を提案
const optimization = await this.optimizeTeamAssignment(
params.projectRequirements,
params.constraints
);
return {
recommendedTeam: optimization.team,
confidenceScore: optimization.confidence,
alternativeOptions: optimization.alternatives,
potentialRisks: optimization.risks,
timeline: optimization.timeline
};
}
});
// パフォーマンス分析ツール
this.registerTool({
name: 'analyze_performance',
description: '個人・チーム・部門レベルのパフォーマンス分析',
inputSchema: z.object({
analysisScope: z.enum(['individual', 'team', 'department', 'company']),
targetId: z.string().optional(),
timeframe: z.object({
start: z.string(),
end: z.string()
}),
metrics: z.array(z.enum(['productivity', 'quality', 'collaboration', 'innovation'])),
includeComparisons: z.boolean().default(true)
}),
execute: async (params) => {
const analytics = await this.performanceAnalytics.analyze(params);
return {
overallScore: analytics.score,
metricBreakdown: analytics.metrics,
trends: analytics.trends,
benchmarks: params.includeComparisons ? analytics.benchmarks : null,
recommendations: analytics.recommendations,
actionItems: analytics.actionItems
};
}
});
}
private async optimizeTeamAssignment(requirements: any, constraints: any): Promise<any> {
// 機械学習ベースの最適化アルゴリズム
const availableEmployees = await this.hrDatabase.findAvailableEmployees(
requirements.startDate,
requirements.endDate
);
const skillMatches = await this.calculateSkillMatches(
availableEmployees,
requirements.requiredSkills
);
const workloadAnalysis = await this.analyzeCurrentWorkloads(availableEmployees);
// 最適化計算
const optimizedTeam = this.runOptimizationAlgorithm({
employees: availableEmployees,
skillMatches,
workloadAnalysis,
requirements,
constraints
});
return optimizedTeam;
}
}
// 使用例:Claude Desktopでの人事AI活用
const hrPromptExample = `
新しいAIプロジェクトチームを編成する必要があります。以下の要件でMCPツールを使用して最適なチーム構成を提案してください:
プロジェクト要件:
- 必要スキル: Python, Machine Learning, Cloud Architecture, Project Management
- プロジェクト期間: 2025年8月1日〜2025年12月31日
- チームサイズ: 5名
- 優先度: High
制約条件:
- データサイエンス部門から最低2名を含める
- 現在の工数が80%を超えている従業員は除外
- 過去に類似プロジェクトの経験がある人を優先
分析結果に基づいて、チーム編成の理由、想定されるリスク、プロジェクト成功のための推奨事項も含めてください。
`;2. 金融取引監視システム
// 金融取引監視用MCPサーバー
class FinancialMonitoringMCPServer {
private transactionDB: TransactionDatabase;
private riskEngine: RiskAssessmentEngine;
private complianceRules: ComplianceRuleEngine;
constructor() {
this.setupFinancialTools();
}
private setupFinancialTools(): void {
// 異常取引検出ツール
this.registerTool({
name: 'detect_anomalous_transactions',
description: '機械学習を使用した異常取引パターンの検出',
inputSchema: z.object({
timeframe: z.object({
start: z.string(),
end: z.string()
}),
accountTypes: z.array(z.enum(['checking', 'savings', 'credit', 'investment'])).optional(),
thresholds: z.object({
amountThreshold: z.number().optional(),
frequencyThreshold: z.number().optional(),
velocityThreshold: z.number().optional()
}).optional(),
includeRiskScores: z.boolean().default(true)
}),
execute: async (params) => {
const transactions = await this.transactionDB.getTransactionsInTimeframe(
params.timeframe.start,
params.timeframe.end,
params.accountTypes
);
const anomalies = await this.riskEngine.detectAnomalies(
transactions,
params.thresholds
);
const results = anomalies.map(anomaly => ({
transactionId: anomaly.id,
accountId: anomaly.accountId,
amount: anomaly.amount,
timestamp: anomaly.timestamp,
anomalyType: anomaly.type,
riskScore: params.includeRiskScores ? anomaly.riskScore : undefined,
pattern: anomaly.detectedPattern,
recommendation: anomaly.recommendedAction
}));
return {
totalAnomalies: results.length,
highRiskCount: results.filter(r => r.riskScore > 0.8).length,
mediumRiskCount: results.filter(r => r.riskScore > 0.5 && r.riskScore <= 0.8).length,
lowRiskCount: results.filter(r => r.riskScore <= 0.5).length,
anomalies: results,
summary: this.generateAnomalySummary(results)
};
}
});
// コンプライアンスチェックツール
this.registerTool({
name: 'compliance_check',
description: '規制要件とポリシーへの準拠性チェック',
inputSchema: z.object({
checkType: z.enum(['aml', 'kyc', 'fatca', 'gdpr', 'sox', 'all']),
entityType: z.enum(['customer', 'transaction', 'account', 'product']),
entityIds: z.array(z.string()),
severity: z.enum(['info', 'warning', 'violation']).optional()
}),
execute: async (params) => {
const complianceResults = await this.complianceRules.runChecks(
params.checkType,
params.entityType,
params.entityIds
);
return {
overallStatus: complianceResults.overallCompliance,
checkedEntities: params.entityIds.length,
violations: complianceResults.violations,
warnings: complianceResults.warnings,
recommendations: complianceResults.recommendations,
nextReviewDate: complianceResults.nextReviewDate,
reportingRequirements: complianceResults.reportingRequirements
};
}
});
// リスクエクスポージャー分析ツール
this.registerTool({
name: 'analyze_risk_exposure',
description: 'ポートフォリオリスクエクスポージャーの包括的分析',
inputSchema: z.object({
portfolioId: z.string().optional(),
analysisTypes: z.array(z.enum(['market', 'credit', 'liquidity', 'operational'])),
confidenceLevel: z.number().min(0.9).max(0.99).default(0.95),
timeHorizon: z.enum(['1d', '1w', '1m', '3m', '6m', '1y']),
includeStressTesting: z.boolean().default(false)
}),
execute: async (params) => {
const riskAnalysis = await this.riskEngine.analyzeExposure(params);
let stressTestResults = null;
if (params.includeStressTesting) {
stressTestResults = await this.riskEngine.runStressTests(
params.portfolioId,
params.timeHorizon
);
}
return {
riskMetrics: riskAnalysis.metrics,
var: riskAnalysis.valueAtRisk,
expectedShortfall: riskAnalysis.expectedShortfall,
riskContributions: riskAnalysis.contributions,
correlationMatrix: riskAnalysis.correlations,
stressTests: stressTestResults,
recommendations: riskAnalysis.recommendations,
alerts: riskAnalysis.alerts
};
}
});
}
}
// Claude Desktopでの金融監視AI活用例
const financialMonitoringPrompt = `
過去24時間の金融取引データを分析し、以下の包括的なリスク評価を実行してください:
1. 異常取引検出:
- 全アカウントタイプで異常パターンを検出
- リスクスコア0.7以上の高リスク取引を特定
- 取引頻度と金額の両方を考慮
2. コンプライアンスチェック:
- AMLとKYC要件の準拠性確認
- 新規検出事項の重要度評価
3. リスクエクスポージャー分析:
- 市場リスクと信用リスクの両方を分析
- 1週間の時間軸でストレステストを実施
結果に基づいて、即座に対応が必要な項目と推奨アクションプランを提示してください。
規制当局への報告が必要な事項があれば、その詳細も含めてください。
`;開発・運用のベストプラクティス
1. セキュリティベストプラクティス
// セキュリティ強化されたMCPサーバー実装
class SecureMCPServer {
private authManager: AuthenticationManager;
private auditLogger: AuditLogger;
private rateLimiter: RateLimiter;
constructor() {
this.setupSecurityMiddleware();
}
private setupSecurityMiddleware(): void {
// 認証・認可レイヤー
this.authManager = new AuthenticationManager({
tokenValidation: true,
roleBasedAccess: true,
sessionTimeout: 3600, // 1時間
multiFactorAuth: true
});
// 監査ログ設定
this.auditLogger = new AuditLogger({
logLevel: 'detailed',
includePayloads: false, // セキュリティのため
retentionDays: 90,
encryptLogs: true
});
// レート制限設定
this.rateLimiter = new RateLimiter({
windowMs: 60000, // 1分
maxRequests: 100,
skipSuccessfulRequests: false
});
}
async handleSecureRequest(request: MCPMessage, clientInfo: any): Promise<MCPMessage> {
try {
// 1. レート制限チェック
await this.rateLimiter.checkLimit(clientInfo.clientId);
// 2. 認証確認
const authResult = await this.authManager.validateToken(request.headers.authorization);
if (!authResult.valid) {
throw new Error('認証に失敗しました');
}
// 3. 権限チェック
const hasPermission = await this.authManager.checkPermission(
authResult.user,
request.method,
request.params
);
if (!hasPermission) {
throw new Error('この操作を実行する権限がありません');
}
// 4. 入力の検証とサニタイズ
const sanitizedRequest = await this.sanitizeInput(request);
// 5. 監査ログの記録
await this.auditLogger.logRequest({
userId: authResult.user.id,
method: request.method,
timestamp: new Date(),
clientInfo,
success: true
});
// 6. 実際の処理実行
const response = await this.processRequest(sanitizedRequest);
// 7. 応答のフィルタリング(機密データの除去)
return await this.filterSensitiveData(response, authResult.user.permissions);
} catch (error) {
// エラーログの記録
await this.auditLogger.logError({
userId: clientInfo.userId,
method: request.method,
error: error.message,
timestamp: new Date()
});
return {
type: 'response',
id: request.id,
error: {
code: -32603,
message: 'サーバーエラーが発生しました'
}
};
}
}
private async sanitizeInput(request: MCPMessage): Promise<MCPMessage> {
// SQLインジェクション対策
if (request.params) {
request.params = this.sanitizeObject(request.params);
}
return request;
}
private sanitizeObject(obj: any): any {
if (typeof obj === 'string') {
return obj.replace(/[;<>'"\\]/g, ''); // 危険な文字の除去
}
if (Array.isArray(obj)) {
return obj.map(item => this.sanitizeObject(item));
}
if (typeof obj === 'object' && obj !== null) {
const sanitized: any = {};
for (const [key, value] of Object.entries(obj)) {
sanitized[key] = this.sanitizeObject(value);
}
return sanitized;
}
return obj;
}
}2. パフォーマンス最適化
// パフォーマンス最適化されたMCPサーバー
class OptimizedMCPServer {
private cache: RedisCache;
private connectionPool: ConnectionPool;
private metricsCollector: MetricsCollector;
constructor() {
this.setupPerformanceOptimizations();
}
private setupPerformanceOptimizations(): void {
// Redis キャッシュ設定
this.cache = new RedisCache({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
ttl: 300, // 5分間のデフォルトTTL
maxMemoryPolicy: 'allkeys-lru'
});
// コネクションプール設定
this.connectionPool = new ConnectionPool({
min: 5,
max: 20,
acquireTimeoutMillis: 30000,
createTimeoutMillis: 30000,
idleTimeoutMillis: 30000
});
// メトリクス収集設定
this.metricsCollector = new MetricsCollector({
collectInterval: 10000, // 10秒間隔
metrics: ['response_time', 'error_rate', 'throughput', 'cache_hit_rate']
});
}
async handleOptimizedRequest(request: MCPMessage): Promise<MCPMessage> {
const startTime = Date.now();
const cacheKey = this.generateCacheKey(request);
try {
// 1. キャッシュチェック
const cachedResponse = await this.cache.get(cacheKey);
if (cachedResponse) {
this.metricsCollector.recordCacheHit();
return cachedResponse;
}
// 2. 処理の実行
const response = await this.processRequestWithPooling(request);
// 3. 結果のキャッシュ
if (this.isCacheable(request, response)) {
await this.cache.set(cacheKey, response, this.getTTL(request));
}
// 4. メトリクス記録
const responseTime = Date.now() - startTime;
this.metricsCollector.recordResponseTime(responseTime);
this.metricsCollector.recordSuccess();
return response;
} catch (error) {
this.metricsCollector.recordError();
throw error;
}
}
private async processRequestWithPooling(request: MCPMessage): Promise<MCPMessage> {
const connection = await this.connectionPool.acquire();
try {
return await this.executeWithConnection(request, connection);
} finally {
await this.connectionPool.release(connection);
}
}
private generateCacheKey(request: MCPMessage): string {
return `mcp:${request.method}:${JSON.stringify(request.params)}`;
}
private isCacheable(request: MCPMessage, response: MCPMessage): boolean {
// 読み取り専用操作のみキャッシュ
const readOnlyMethods = ['resources/read', 'tools/list', 'analyze_customers'];
return readOnlyMethods.includes(request.method) && !response.error;
}
private getTTL(request: MCPMessage): number {
// メソッドに応じたTTL設定
const ttlMap: Record<string, number> = {
'resources/list': 3600, // 1時間
'analyze_customers': 1800, // 30分
'check_inventory': 300 // 5分
};
return ttlMap[request.method] || 300;
}
}3. 監視とアラート設定
// 包括的な監視システム
class MCPMonitoringSystem {
private alertManager: AlertManager;
private healthChecker: HealthChecker;
private performanceTracker: PerformanceTracker;
constructor() {
this.setupMonitoring();
}
private setupMonitoring(): void {
// ヘルスチェック設定
this.healthChecker = new HealthChecker({
checks: [
new DatabaseHealthCheck(),
new CacheHealthCheck(),
new ExternalAPIHealthCheck(),
new MemoryHealthCheck(),
new DiskSpaceHealthCheck()
],
interval: 30000, // 30秒間隔
timeout: 5000 // 5秒タイムアウト
});
// アラート設定
this.alertManager = new AlertManager({
channels: [
new SlackAlert(process.env.SLACK_WEBHOOK),
new EmailAlert(process.env.ALERT_EMAIL),
new PagerDutyAlert(process.env.PAGERDUTY_KEY)
],
thresholds: {
errorRate: 0.05, // 5%以上のエラー率
responseTime: 5000, // 5秒以上の応答時間
memoryUsage: 0.85, // 85%以上のメモリ使用率
diskUsage: 0.9 // 90%以上のディスク使用率
}
});
// パフォーマンス追跡
this.performanceTracker = new PerformanceTracker({
sampleRate: 0.1, // 10%のリクエストをサンプリング
metrics: ['latency', 'throughput', 'error_distribution'],
aggregationWindow: 60000 // 1分間の集計ウィンドウ
});
}
async startMonitoring(): Promise<void> {
// 定期ヘルスチェック開始
this.healthChecker.start();
// アラート監視開始
this.alertManager.start();
// パフォーマンス追跡開始
this.performanceTracker.start();
console.log('MCP監視システムが開始されました');
}
async checkSystemHealth(): Promise<HealthReport> {
const healthResults = await this.healthChecker.runAllChecks();
const performanceMetrics = await this.performanceTracker.getCurrentMetrics();
const overallHealth = healthResults.every(result => result.status === 'healthy');
if (!overallHealth) {
await this.alertManager.sendAlert({
level: 'warning',
message: 'システムヘルスチェックで問題が検出されました',
details: healthResults.filter(r => r.status !== 'healthy')
});
}
return {
overall: overallHealth ? 'healthy' : 'unhealthy',
checks: healthResults,
metrics: performanceMetrics,
timestamp: new Date()
};
}
}
// 使用例
const monitoring = new MCPMonitoringSystem();
await monitoring.startMonitoring();
// 定期的なヘルスレポート
setInterval(async () => {
const health = await monitoring.checkSystemHealth();
console.log('システムヘルス:', health);
}, 60000); // 1分間隔これらのベストプラクティスにより、MCPサーバーは企業環境での本格的な運用に耐えうる、安全で高性能なシステムとして構築できます。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
まとめ:MCPが切り開くAI連携の未来
MCP(Model Context Protocol)は、AIアシスタントと外部システムの連携における複雑さを解決し、統一されたインターフェースを提供する革新的な技術です。本記事で紹介した技術的詳細、実装方法、実用例を通じて、MCPの可能性を理解いただけたことと思います。
MCPの主要な価値
- 標準化による開発効率向上: 一度学習すれば、あらゆるAIプロバイダーで利用可能
- 企業システムとの安全な統合: 組み込みのセキュリティ機能による信頼性の高い連携
- スケーラブルなアーキテクチャ: 小規模な実験から大規模な企業システムまで対応
- 相互運用性: ベンダーロックインを避けた柔軟なAI戦略の実現
今後の展開予測
2025年以降、MCPは以下の分野でさらなる進化が期待されます:
- 業界標準としての確立: 主要なAIプロバイダーによる本格的な採用
- エンタープライズ統合の加速: 既存の企業システムとの深い統合
- セキュリティ機能の強化: ゼロトラスト原則に基づくセキュリティモデル
- パフォーマンス最適化: 大規模環境での高速処理とリアルタイム応答
学習の次のステップ
MCPをより深く学習するために、以下のステップを推奨します:
- 実践的な実装: 本記事のコード例を参考に、実際のMCPサーバーを構築
- コミュニティ参加: Anthropic公式ドキュメントやGitHubリポジトリの追跡
- 企業での試験導入: 小規模なパイロットプロジェクトでの実証実験
- セキュリティとパフォーマンスの深堀り: 本格運用に向けた技術的課題の解決
MCPは、AI技術の実用化において重要な役割を果たす技術標準として、今後のAI開発エコシステムの中核を担うことになるでしょう。継続的な学習と実践を通じて、この革新的な技術を効果的に活用していくことが、AI時代における競争優位性の確立につながります。



![Amazon Bedrock 生成AIアプリ開発入門 [AWS深掘りガイド]](https://m.media-amazon.com/images/I/51KtyIMPsYL._SL500_.jpg)
