English | Support | GitHub
Technical Guide 2026-04-05 20 min read

MCP導入でよくある問題と解決策 — 開発者・SaaS企業向け

MCP(Model Context Protocol)はAIエージェントと外部サービスを接続する標準プロトコルとして急速に普及している。しかし、導入時に多くの開発者が同じ問題に直面している。
本記事では、KanseiLinkが14ツールのMCP Server運用で蓄積した実践知をもとに、よくある問題TOP 10とその解決策をコード例付きで解説する。

1. MCP導入時のよくある問題 TOP 10

以下は、KanseiLinkのサポートデータと開発者コミュニティのフィードバックから集計した、MCP導入時に最も頻繁に報告される問題のランキングである。

1 認証エラー(OAuth token期限切れ) HIGH

最も報告が多い問題。MCP ServerがOAuth 2.0でSaaSに接続する際、アクセストークンの有効期限が切れているにもかかわらず、リフレッシュ処理が実装されていないケースが頻発。

原因

access_tokenの有効期限(通常1時間)後にリクエストを送信。refresh_tokenによる自動更新ロジックが未実装。

解決策

リクエスト前にtoken有効期限を確認し、期限切れの場合は自動でrefresh_tokenを使って更新する。

TypeScript - Token Auto-Refresh class TokenManager { private accessToken: string; private refreshToken: string; private expiresAt: number; async getValidToken(): Promise<string> { // 有効期限の5分前にリフレッシュ if (Date.now() >= this.expiresAt - 300000) { await this.refresh(); } return this.accessToken; } private async refresh(): Promise<void> { const res = await fetch('https://oauth.example.com/token', { method: 'POST', body: new URLSearchParams({ grant_type: 'refresh_token', refresh_token: this.refreshToken, client_id: process.env.CLIENT_ID!, }), }); const data = await res.json(); this.accessToken = data.access_token; this.expiresAt = Date.now() + data.expires_in * 1000; } }
2 CORS問題(ブラウザからのMCP接続) HIGH

ブラウザベースのMCPクライアントからHTTP TransportのMCP Serverに接続する際、CORSポリシーによりリクエストがブロックされる。

原因

MCP ServerにCORSヘッダーが設定されていない。ブラウザのプリフライトリクエスト(OPTIONS)が拒否される。

解決策

MCP ServerにCORSミドルウェアを追加する。または、サーバーサイドプロキシ経由で接続する。

TypeScript - CORS設定(Express) import cors from 'cors'; app.use(cors({ origin: [ 'https://your-app.com', 'http://localhost:3000' ], methods: ['GET', 'POST', 'OPTIONS'], allowedHeaders: [ 'Content-Type', 'Authorization', 'X-MCP-Session' ], credentials: true, }));
3 Rate Limit超過 HIGH

エージェントが高頻度でAPIを呼び出し、Rate Limitに達してリクエストがブロックされる。429エラーが連続し、タスク全体が失敗する。

原因

リトライロジック未実装、Rate Limit値の未確認、バースト的なリクエスト送信。

解決策

Exponential Backoff + ヘッダー監視による自動スロットリング。

TypeScript - Exponential Backoff async function fetchWithRetry( url: string, options: RequestInit, maxRetries = 3 ): Promise<Response> { for (let i = 0; i < maxRetries; i++) { const res = await fetch(url, options); if (res.status === 429) { const retryAfter = res.headers.get('Retry-After'); const delay = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, i) * 1000; // 1s, 2s, 4s console.warn(`Rate limited. Retry in ${delay}ms`); await new Promise(r => setTimeout(r, delay)); continue; } return res; } throw new Error('Max retries exceeded'); }
4 stdio vs HTTP Transport選択ミス MEDIUM

ローカル実行にHTTP Transportを使用したり、リモート接続にstdioを使用する設計ミス。パフォーマンスの低下や接続不能の原因になる。

原因

MCP仕様の2つのTransport(stdio / Streamable HTTP)の使い分けを理解していない。

解決策

ローカル実行 = stdio、リモート/マルチユーザー = Streamable HTTP。SSEは非推奨(2025仕様)。

判断フロー // Transport選択の判断基準 // // Q: MCP Serverはどこで動作する? // ├─ ローカルマシン上 → stdio Transport // │ ✓ 低レイテンシ、認証不要、シンプル // │ // └─ リモートサーバー → Streamable HTTP Transport // ✓ マルチユーザー対応、ファイアウォール越え // ✓ セッション管理、認証ヘッダー対応 // // ⚠️ SSE Transport は 2025年MCP仕様で非推奨 // 新規実装ではStreamable HTTPを使用すること
5 日本語文字化け(UTF-8 BOM問題) MEDIUM

日本語を含むAPIリクエスト・レスポンスで文字化けが発生。特にWindows環境でのBOM付きUTF-8ファイルが原因となるケースが多い。

原因

UTF-8 BOM(EF BB BF)がJSONパーサーに渡される。Content-Typeにcharsetが未指定。

解決策

BOM除去処理の追加、Content-Typeヘッダーへのcharset=utf-8明記。

TypeScript - BOM除去 function removeBOM(text: string): string { // UTF-8 BOM (EF BB BF) をバイト列で除去 if (text.charCodeAt(0) === 0xFEFF) { return text.slice(1); } return text; } // MCP Server側のレスポンスヘッダー res.setHeader( 'Content-Type', 'application/json; charset=utf-8' );
6 Tool定義の不備(Zod schemaエラー) MEDIUM

MCP ServerのTool定義でZod schemaを使用する際、inputSchemaの型定義ミスにより、クライアントがToolを呼び出せない。

原因

Zodスキーマの定義がJSON Schemaとして正しく変換されない。optionalとrequiredの混在。

解決策

zodToJsonSchemaでの変換結果を必ず検証。MCP Inspectorでツール定義をテスト。

TypeScript - 正しいTool定義 import { z } from 'zod'; // 正しい: descriptionを全フィールドに付与 const SearchSchema = z.object({ query: z.string() .describe('検索キーワード(日本語可)'), category: z.enum(['crm', 'accounting', 'hr']) .optional() .describe('サービスカテゴリで絞り込み'), limit: z.number() .min(1).max(50) .default(10) .describe('取得件数(1-50, デフォルト10)'), }); server.tool( 'search_services', 'SaaSサービスをキーワードで検索', SearchSchema, async ({ query, category, limit }) => { // Tool実装 } );
7 タイムアウト(大量データ返却時) MEDIUM

MCP Serverが大量のデータを返却する際、クライアント側のタイムアウトに達して接続が切断される。

原因

ページネーション未実装で全データを一括返却。レスポンスサイズが数MBに達する。

解決策

ページネーション実装 + レスポンスサイズ制限。大量データはcursorベースページネーションが推奨。

TypeScript - Cursor Pagination // Tool定義にcursorパラメータを追加 const ListSchema = z.object({ cursor: z.string().optional() .describe('次ページのカーソル'), limit: z.number().default(20) .describe('1ページあたりの件数'), }); // レスポンスに次ページ情報を含める return { content: [{ type: 'text', text: JSON.stringify({ items: results, nextCursor: hasMore ? lastId : null, totalCount: total, }), }], };
8 バージョン不整合(SDK更新時) LOW

MCP SDKをアップデートした際に、APIの破壊的変更により既存のMCP Serverが動作しなくなる。

原因

package.jsonで^(キャレット)指定によりマイナーバージョンが自動更新。CHANGELOG未確認。

解決策

SDKバージョンを固定(~ではなく正確なバージョン指定)。更新前にCHANGELOGを確認。

package.json - バージョン固定 { "dependencies": { // NG: メジャー互換の範囲で自動更新 "@modelcontextprotocol/sdk": "^1.12.0", // OK: 正確なバージョンを固定 "@modelcontextprotocol/sdk": "1.12.0" } }
9 セキュリティ(API Key露出) HIGH

MCP ServerのソースコードやクライアントサイドコードにAPI Keyがハードコードされ、GitHubリポジトリ経由で漏洩するケース。

原因

API Keyをソースコードに直接記述。.envファイルをコミット。フロントエンドにAPI Keyを埋め込み。

解決策

環境変数管理 + .gitignore設定 + サーバーサイド専用のシークレット管理。

セキュリティチェックリスト # .gitignore に必ず追加 .env .env.local .env.production # 環境変数で管理(コードに直接書かない) # .env ファイル SAAS_API_KEY=sk-xxxxxxxxxxxxx OAUTH_CLIENT_SECRET=cs-xxxxxxxxxxxxx # コード側 const apiKey = process.env.SAAS_API_KEY; if (!apiKey) { throw new Error('SAAS_API_KEY is not set'); }
10 テスト環境の不足 MEDIUM

MCP Serverのテスト方法がわからず、手動テストのみで品質が安定しない。本番環境で直接テストしてしまうリスク。

原因

MCP専用のテストフレームワークの認知度が低い。SaaS側にsandbox環境がない。

解決策

MCP Inspector + ユニットテスト + モックサーバーの3層テスト戦略。

テスト戦略 // 1. MCP Inspector でTool定義を検証 // npx @modelcontextprotocol/inspector // 2. ユニットテスト(Vitest) import { describe, it, expect } from 'vitest'; describe('search_services tool', () => { it('returns results for valid query', async () => { const result = await searchServices({ query: '会計', limit: 5, }); expect(result.items.length).toBeLessThanOrEqual(5); expect(result.items[0]).toHaveProperty('name'); }); }); // 3. モックサーバーで外部API依存を排除 import { setupServer } from 'msw/node'; const mockServer = setupServer( http.get('https://api.saas.com/v1/*', () => { return HttpResponse.json({ data: mockData }); }) );

2. SaaS企業がMCP対応する際のチェックリスト

自社SaaSをMCP対応(エージェント対応)する際、以下のチェックリストを確認してください。全項目をクリアすることで、AEOスコアの大幅な改善が期待できます。

3. KanseiLink MCPの実装例から学ぶベストプラクティス

KanseiLinkは14のMCP Toolを提供する実運用MCP Serverである。ここでは、その設計から得られたベストプラクティスを共有する。

14ツールの設計パターン

KanseiLinkのMCP Serverは以下の14ツールで構成されている。各ツールは単一責任の原則に基づき、1つのツールが1つの明確な目的を持つ。

KanseiLink MCP - 14 Tools Architecture // 検索・発見系 search_services // SaaSサービス検索(FTS5全文検索) get_service_detail // サービス詳細取得 find_combinations // サービス組み合わせ提案 get_recipe // 自動化レシピ取得 // インテリジェンス系 get_insights // AEO分析・市場インサイト get_service_tips // サービス活用Tips check_updates // 更新情報チェック // 品質管理系 get_inspection_queue // 検査キュー取得 submit_inspection // 検査結果提出 report_outcome // 実行結果レポート // コンテンツ生成系 generate_aeo_report // AEOレポート生成 generate_aeo_article // AEO記事生成 // 設計原則: // 1. 1ツール = 1目的(Single Responsibility) // 2. 全ツールに日本語description // 3. inputSchemaに詳細なdescribe()付与 // 4. エラーはisError: trueで明示的に返却

PIIマスキング

個人情報保護のためのマスキング実装

KanseiLinkのMCP Serverは、全てのレスポンスに対してPII(個人識別情報)マスキングを適用する。メールアドレス、電話番号、クレジットカード番号などは、出力前に自動的にマスクされる。

これにより、エージェントが意図せず個人情報をLLMに送信するリスクを最小化している。MCP Server側でのマスキングは、クライアント側の実装に依存しないため、より堅牢なセキュリティを実現する。

TypeScript - PII Masking Pattern const PII_PATTERNS = [ { regex: /[\w.-]+@[\w.-]+\.\w+/g, replace: '[EMAIL]' }, { regex: /\d{3}-\d{4}-\d{4}/g, replace: '[PHONE]' }, { regex: /\d{4}-\d{4}-\d{4}-\d{4}/g, replace: '[CARD]' }, ]; function maskPII(text: string): string { let masked = text; for (const { regex, replace } of PII_PATTERNS) { masked = masked.replaceAll(regex, replace); } return masked; } // MCP Serverのレスポンス生成時に適用 return { content: [{ type: 'text', text: maskPII(JSON.stringify(data)), }], };

FTS5全文検索

SQLite FTS5による高速日本語検索

KanseiLinkのsearch_servicesツールは、SQLite FTS5(Full-Text Search 5)エンジンを使用した全文検索を実装している。日本語のトークナイズにはtrigramを使用し、部分一致検索にも対応。

これにより、エージェントが「会計 クラウド」「経費精算 自動化」などの日本語キーワードで高速に検索でき、1,000件以上のサービスデータから10ms以下でヒットする。

SQL - FTS5 Table Setup -- FTS5仮想テーブルの作成 CREATE VIRTUAL TABLE services_fts USING fts5( name, description, category, tags, content='services', content_rowid='id', tokenize='trigram' ); -- 検索クエリ(ランキング付き) SELECT s.*, rank AS relevance FROM services_fts JOIN services s ON services_fts.rowid = s.id WHERE services_fts MATCH '会計 OR クラウド' ORDER BY rank LIMIT 10;

エラーハンドリングのパターン

MCP仕様に準拠したエラー返却

KanseiLinkでは、ツール実行時のエラーをMCP仕様のisErrorフラグで明示的に返却する。これにより、LLMがエラーを正しく認識し、ユーザーへの適切なフィードバックやリトライ判断が可能になる。

TypeScript - Error Handling Pattern server.tool( 'get_service_detail', 'サービスの詳細情報を取得', { slug: z.string().describe('サービスのslug') }, async ({ slug }) => { try { const service = await db.getService(slug); if (!service) { return { content: [{ type: 'text', text: `サービス "${slug}" は見つかりませんでした。`, }], isError: true, }; } return { content: [{ type: 'text', text: maskPII(JSON.stringify(service)), }], }; } catch (error) { return { content: [{ type: 'text', text: `内部エラー: ${error.message}`, }], isError: true, }; } } );

MCP対応でお困りですか?

KanseiLinkはSaaS企業のMCP対応・AEOスコア改善を支援しています。
14ツールの実運用知見をベースにした実践的なコンサルティング。

AEO Ratings を見る