2025-2026年にSaaSプロダクトを構築しているなら、すべてのプロダクトマネージャーが「AI機能」を欲しがっていることに気づいているはずです。もっともです。しかし本当の問題は、AIを追加するかどうかではなく、AIモデルにアプリケーションのデータと機能への構造化された安全なアクセスを提供する方法です。それはまさにモデルコンテキストプロトコル(MCP)が解決するもので、VercelでNextjs MCP サーバーをデプロイすることは、新しいインフラを構築することなく高速に動きたいSaaSチームにとって最も実用的なパターンの1つになっています。

過去数ヶ月間、クライアント向けにMCPサーバーを構築してきました。シンプルなツール提供セットアップもあれば、複雑なマルチテナント認証フローを備えたものもあります。この記事では、VercelでNextjsを使ってMCPサーバーを構築、デプロイ、スケーリングすることについて学んだすべてのことをカバーしています。

MCP Server Development: Deploy on Vercel with Next.js for SaaS

目次

MCPとは何か、なぜSaaSチームが気を付けるべきか

モデルコンテキストプロトコル(MCP)はオープンスタンダードで、もともとAnthropicによって開発されており、現在は広く採用されています。AIモデルが外部ツールおよびデータソースとどのように相互作用するかを定義しています。AIのUSB-Cポートと考えてください。あらゆるAIクライアントが任意のMCP互換サーバーに接続するために使用できる標準化されたインターフェースです。

MCPの前は、ClaudeやGPTなたのモデルをSaaS アプリと相互作用させたい場合、各AIプロバイダー用にカスタム統合を構築する必要がありました。OpenAIの関数呼び出しはAnthropicのツール利用と異なります。MCPはこれを変えます。1つのサーバーを構築すれば、MCP互換クライアントはそれを使用できます。

SaaSチームにとってこれが重要な理由は:

  • ユーザーはAI統合を期待しています。 2026年半ばまでに、B2B SaaS ユーザーの約68%がプライマリツールと一緒にAIアシスタントを使用していると報告しています(Gartner、2026年Q1)。
  • MCPはデフォルトになりつつあります。 Claude Desktop、Cursor、Windsurf、VS Code Copilot、その他多くのクライアントが現在MCPをネイティブにサポートしています。
  • MCPサーバーを構築することは、すべてのAIプロバイダー用にカスタム統合を構築するより安価です。

MCPと従来のAPI統合の比較

側面 従来のAPI MCPサーバー
クライアント互換性 プロバイダーごとに1対1 MCP互換クライアント
検出 ドキュメント手動読取 自動ツール/リソース検出
認証フロー 統合ごとにカスタム 標準化されたOAuth 2.1 / APIキー
メンテナンス負担 高い(N統合) 低い(1サーバー)
リアルタイムデータ ポーリングまたはWebhook サーバー送信イベント/ストリーミング
セットアップ時間 クライアントごとに数日から数週間 サーバーは数時間、クライアントごとに数分

アーキテクチャ概要: Vercel上のMCP

こちらは、いくつかのアプローチをを反復した後に着地したアーキテクチャです:

┌─────────────────┐     ┌──────────────────────┐     ┌─────────────────┐
│  MCPクライアント│     │  Vercel (Next.js)    │     │  SaaS           │
│                 │     │                      │     │  バックエンド   │
│  - Claude       │────▶│  /api/mcp (HTTP+SSE) │────▶│  - データベース │
│  - Cursor       │     │  /api/mcp/sse        │     │  - API          │
│  - カスタムアプリ│◀────│  /api/auth/[...mcp]  │     │  - サービス     │
└─────────────────┘     └──────────────────────┘     └─────────────────┘

重要な洞察:MCPサーバーは既存のAPIを置き換えるのではなく、その前に翻訳層として座ります。MCPサーバーは既存のSaaS機能にマップされるツールとリソースを公開しますが、AIモデルが検出して使用できる形式になっています。

Vercel上では、これはサーバーレス関数として実行されます。最新のMCP仕様(v2025-12)はトランスポートとしてサーバー送信イベント(SSE)を備えたHTTPをサポートし、Next.jsRoute Handlerのストリーミングサポートでうまく機能します。

なぜVercelのNext.jsを使用するのか?

任意のフレームワークでMCPサーバーを構築できます。Express、Fastify、Honoなど。しかしVercel上のNext.jsはSaaS向けにいくつかの本当の利点を提供します:

  1. マーケティングサイト、アプリ、MCPサーバーが1つのリポジトリに住んでいます。 管理するインフラが少なくなります。
  2. エッジミドルウェアはリクエストがMCPエンドポイントにヒットする前に認証を処理します。
  3. VercelのストリーミングサポートはSSEベースのMCPトランスポートでうまく機能します。
  4. 自動スケーリング -- サーバーについて考える必要がありません。
  5. すでにNext.jsを実行している場合(統計的には、おそらくそうです)、新しいインフラはゼロです。

Social AnimalではたくさんのNext.js開発を行っており、このパターンは最もリクエストされるアーキテクチャの1つになっています。

MCP Server Development: Deploy on Vercel with Next.js for SaaS - architecture

Next.js MCPサーバーのセットアップ

これをビルドしましょう。Next.js 15以上でApp Routerを使用していると想定しています。

依存関係のインストール

pnpm add @modelcontextprotocol/sdk zod
pnpm add -D @types/node

@modelcontextprotocol/sdkパッケージ(2026年初頭の時点でv1.12以上)には、HTTP+SSEトランスポート用に必要なすべてが含まれています。以前のバージョンはstdioのみをサポートしており、サーバーレスでは機能しません。

MCPルートハンドラーの作成

// app/api/mcp/route.ts
import { McpServer } from '@modelcontextprotocol/sdk/server';
import { httpTransport } from '@modelcontextprotocol/sdk/server/http';
import { z } from 'zod';

const server = new McpServer({
  name: 'your-saas-mcp',
  version: '1.0.0',
  description: 'YourSaaSプラットフォーム用MCPサーバー',
});

// ツールを登録します(次のセクションで詳しく説明します)
server.tool(
  'get-projects',
  '認証されたユーザーのすべてのプロジェクトをリストします',
  {
    status: z.enum(['active', 'archived', 'all']).optional().default('active'),
    limit: z.number().min(1).max(100).optional().default(20),
  },
  async ({ status, limit }, context) => {
    // 実際のビジネスロジックがここに入ります
    const projects = await fetchProjects(context.auth.userId, { status, limit });
    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(projects, null, 2),
        },
      ],
    };
  }
);

const handler = httpTransport(server, {
  sessionManagement: true,
  cors: {
    origin: '*', // 本番環境では厳密にしてください
  },
});

export const GET = handler;
export const POST = handler;
export const DELETE = handler;

ストリーミング用のSSEエンドポイント

一部のMCPクライアントは長時間実行操作のためにSSEトランスポートを優先します:

// app/api/mcp/sse/route.ts
import { sseTransport } from '@modelcontextprotocol/sdk/server/sse';
import { server } from '../mcp-server'; // サーバー設定を共有モジュールに抽出します

export const GET = sseTransport(server, {
  // Vercelはホビープランで30秒、プロで300秒のタイムアウトがあります
  // 長時間実行ツールの場合、最小限プロプランが必要です
  keepAliveInterval: 15000,
});

MCPツールとリソースの実装

これが本当の仕事が行われるところです。MCPはツール(AIが実行できるアクション)とリソース(AIが読み取ることができるデータ)を区別しています。これを正しく理解すれば、AIクライアントが大好きなMCPサーバーと、クライアントが苦労するサーバーの違いが生まれます。

良いツールの設計

私が見る最大の間違い:粒度が細かすぎたり広すぎたりするツール。50のシンプルツールを公開すると、AIモデルはパニックになります。20のパラメータを各々取るメガツール3つを公開すると、モデルは誤った決定を下します。

私のルールオブサム:ユーザー意図ごとに1つのツール。ユーザーが「最近の請求書を表示して」と言う場合、それは1つのツールです。list-invoices + filter-invoices + format-invoicesに分割しないでください。

// 良い例:明確な意図、合理的なパラメータ
server.tool(
  'search-customers',
  '名前、メール、またはアカウントIDで顧客を検索します。最近のアクティビティを含む一致する顧客プロフィールを返します。',
  {
    query: z.string().describe('検索用語 - 名前、メール、またはアカウントIDが可能です'),
    includeInactive: z.boolean().optional().default(false),
  },
  async ({ query, includeInactive }, context) => {
    const customers = await customerService.search({
      query,
      tenantId: context.auth.tenantId,
      includeInactive,
    });
    
    return {
      content: [{
        type: 'text',
        text: JSON.stringify(customers.map(c => ({
          id: c.id,
          name: c.name,
          email: c.email,
          plan: c.plan,
          mrr: c.mrr,
          lastActive: c.lastActiveAt,
        })), null, 2),
      }],
    };
  }
);

リソースの公開

リソースはAIクライアントがコンテキストとして取り込むことができる読み取り専用データです。モデルが参照できるファイルと考えてください:

server.resource(
  'api-docs',
  'SaaS APIドキュメント',
  'text/markdown',
  async () => {
    const docs = await fs.readFile('./docs/api-reference.md', 'utf-8');
    return { content: docs };
  }
);

// URIテンプレート付きのダイナミックリソース
server.resourceTemplate(
  'project/{projectId}/analytics',
  '特定プロジェクトの分析サマリー',
  'application/json',
  async ({ projectId }, context) => {
    const analytics = await analyticsService.getSummary(projectId, context.auth.tenantId);
    return { content: JSON.stringify(analytics) };
  }
);

認証とマルチテナンシー

これは最初の試みで誰もが間違える部分です。MCPはOAuth 2.1認証をサポートし、マルチテナントSaaSを構築している場合、これは絶対に必要です。

MCP用のOAuth 2.1フロー

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  if (request.nextUrl.pathname.startsWith('/api/mcp')) {
    const authHeader = request.headers.get('authorization');
    
    if (!authHeader?.startsWith('Bearer ')) {
      return NextResponse.json(
        { error: ' ヘッダーが見つからないか無効な認可です' },
        { status: 401 }
      );
    }
    
    // トークンを検証し、テナントコンテキストを挿入します
    // これはここで既存の認証システムが組み込まれるポイントです
  }
}

export const config = {
  matcher: '/api/mcp/:path*',
};

MCPクライアントが必要とするOAuth検出エンドポイント用:

// app/.well-known/oauth-authorization-server/route.ts
export function GET() {
  return Response.json({
    issuer: 'https://your-saas.com',
    authorization_endpoint: 'https://your-saas.com/oauth/authorize',
    token_endpoint: 'https://your-saas.com/api/oauth/token',
    registration_endpoint: 'https://your-saas.com/api/oauth/register',
    scopes_supported: ['mcp:read', 'mcp:write', 'mcp:admin'],
    response_types_supported: ['code'],
    code_challenge_methods_supported: ['S256'],
  });
}

マルチテナント分離

すべてのMCPツール呼び出しは認証されたテナントにスコープされている必要があります。認証コンテキストがすべてのツールハンドラーに自動的に注入されるパターンを使用します:

const withTenant = (handler) => async (params, context) => {
  const tenant = await resolveTenant(context.auth.token);
  if (!tenant) throw new McpError('無効なテナント');
  return handler(params, { ...context, tenant });
};

テナント識別子のためにツールパラメータを信頼しないでください。常に認証トークンから導出します。

Vercelへのデプロイ: 構成と落とし穴

vercel.json設定

{
  "functions": {
    "app/api/mcp/route.ts": {
      "maxDuration": 60
    },
    "app/api/mcp/sse/route.ts": {
      "maxDuration": 300
    }
  },
  "headers": [
    {
      "source": "/api/mcp/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "no-store" }
      ]
    }
  ]
}

誰も教えてくれない落とし穴

1. 関数タイムアウト。 VercelホビープランはPlanetScale最大30秒です。プロは300秒になります。遅いAPIをクエリしたりデータを処理するMCPツールの場合、最小限プロが必要です。チームメンバーあたり月20ドルで、ほとんどのSaaSチームにとって大きな問題ではありません。

2. コールドスタート。 サーバーレスコールドスタートは最初のリクエストに200~800msを追加できます。MCPクライアントは一般的にこれをうまく処理します。50ms以下の応答を期待していません。これが気になる場合は、Vercelのcronを使用して関数を暖かく保ちます。

3. SSEとストリーミング。 Vercelはストリーミング応答をサポートしていますが、CDNレイヤーに注意点があります。すべてのMCPルートでCache-Control: no-storeを設定します。キャッシュされたSSE応答がクライアントに古いツールリストを受け取ることになった場合、私はこれを硬い方法で学びました。

4. リクエスト本文のサイズ。 Vercelはサーバーレス関数でのリクエスト本文を4.5MBに制限しています。MCPツールがファイルアップロードまたは大きなペイロードを処理する場合、代わりに署名付きアップロードURLを使用する必要があります。

5. 環境変数。 MCPサーバーのパブリックURLを環境変数として設定することを忘れないでください。開発中はngrokまたはVercelプレビューURLのようなものを使用しますが、本番環境では正式なドメインである必要があります。

# .env.production
MCP_SERVER_URL=https://your-saas.com/api/mcp
MCP_SERVER_NAME=your-saas-mcp

パフォーマンス最適化とスケーリング

キャッシング戦略

MCPツール応答は、データが頻繁に変わらない場合にキャッシュできます:

import { unstable_cache } from 'next/cache';

const getCachedAnalytics = unstable_cache(
  async (tenantId: string, projectId: string) => {
    return analyticsService.getSummary(tenantId, projectId);
  },
  ['analytics-summary'],
  { revalidate: 300 } // 5分
);

コネクションプーリング

MCPツールがデータベースにヒットする場合、接続プーリングを使用します。Vercel上では、各関数呼び出しは独自の実行コンテキストを取得するため、プーリングなしではデータベース接続をすぐに枯渇させます。

import { Pool } from '@neondatabase/serverless';

// Neonのサーバーレスドライバーはプーリングを自動的に処理します
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

Vercel上のデータベース支援MCPツールにはNeonまたはPlanetScaleをお勧めします。両者ともサーバーレス接続モデルをうまく処理します。

ベンチマーク

Vercel Pro上でのいくつかの本番MCPデプロイ全体で計測したもの:

メトリック コールドスタート ウォーム P99
シンプルツール(DBなし) 420ms 45ms 180ms
DB支援ツール(Neon) 680ms 95ms 320ms
外部API付きツール 850ms 280ms 1200ms
SSE接続セットアップ 520ms 60ms 250ms
ツール検出(リスト) 380ms 30ms 120ms

これらの数字はAIクライアント相互作用に問題ありません。モデルはとにかく応答生成に数秒かかります。MCPサーバーはボトルネックになりません。

モニタリングと可観測性

本番環境で何が起こっているかを知る必要があります。MCPサーバーには一意の可観測性の必要性があります。「ユーザー」はAIモデルであり、人間ではないからです。

追跡する対象

  • ツール呼び出し頻度 -- モデルは実際にどのツールを使用していますか?
  • ツールごとのエラー率 -- 特定のツールが他より失敗していますか?
  • トークン/テナント分布 -- 1つのテナントがサーバーをハンマーしていますか?
  • 応答ペイロードサイズ -- 過度に大きい応答はモデルコンテキストウィンドウを浪費しますか?
// MCPツール用のシンプルなログミドルウェア
const withLogging = (toolName: string, handler: Function) => {
  return async (params: any, context: any) => {
    const start = performance.now();
    try {
      const result = await handler(params, context);
      const duration = performance.now() - start;
      
      console.log(JSON.stringify({
        type: 'mcp_tool_invocation',
        tool: toolName,
        tenant: context.auth?.tenantId,
        duration,
        success: true,
        responseSize: JSON.stringify(result).length,
      }));
      
      return result;
    } catch (error) {
      console.error(JSON.stringify({
        type: 'mcp_tool_error',
        tool: toolName,
        tenant: context.auth?.tenantId,
        error: error.message,
      }));
      throw error;
    }
  };
};

これらのログをAxiom(Vercelの統合ログ)、Datadog、または既に使用しているものにパイプします。Vercelの組み込みログドレインはこれを簡単にしています。

コスト分析: VercelでMCPサーバーを実行する

お金について話しましょう。2026年のVercelでMCPサーバーを実行する中規模SaaS向けの現実的なコスト分析:

コンポーネント ホビー プロ エンタープライズ
基本プラン $0/月 シートあたり$20/月 カスタム
関数呼び出し(含む) 100K 1M カスタム
追加呼び出し N/A 100万あたり$0.60 交渉可能
帯域幅(含む) 100GB 1TB カスタム
最大関数時間 30秒 300秒 900秒
エッジミドルウェア 含まれる 含まれる 含まれる
見積月額(1日10,000MCP要求) 実行不可 約$25-40 カスタム

ほとんどのSaaS製品の場合、プロプランはMCPトラフィックを快適に処理します。1日あたり約10,000MCPツール呼び出し(かなり活動的)では、月あたり約300K関数実行です。プロの含まれるアロケーション内です。

AWSで専用MCPサーバーを実行することと比較してください。最低限EC2インスタンス(月30~50ドル)、ロードバランサー(月18ドル)、インフラを管理する時間が必要です。Vercelは運用上の単純性で勝ちます。

SaaS向けの正しいアーキテクチャを評価している場合、これを支援できます。価格設定ページをチェックするか、直接お問い合わせください

FAQ

モデルコンテキストプロトコル(MCP)とは何で、関数呼び出しとどう異なりますか?

MCPは、AIモデルを外部ツールとデータに接続するためのオープンスタンダードです。プロバイダー固有の関数呼び出し(OpenAIの関数呼び出し、Anthropicのツール利用)と異なり、MCPは普遍的です。1つのMCPサーバーを構築すれば、互換性のあるクライアント(Claude、Cursor、カスタムアプリ)はツールを自動的に検出して使用できます。関数呼び出しでは、各AIプロバイダーのために別々ツールを定義する必要があります。

VercelのフリーホビープランでMCPサーバーをデプロイできますか?

技術的にはい、しかし本番環境にはお勧めしません。30秒の関数タイムアウトは、データベースをクエリしたり外部APIを呼び出すMCPツールには制限的すぎます。また、限定的な呼び出し(月100K)も利用できます。シートあたり月20ドルのプロプランは、実際のワークロードに対する最小限の推奨です。

MCPクライアントとSaaS間の認証をどのように処理しますか?

MCP仕様はOAuth 2.1認証をサポートしています。MCPクライアントが自動的に検出する.well-known/oauth-authorization-serverエンドポイントを公開します。ユーザーがClaudeなどのAIクライアント経由で接続すると、標準的なOAuthフローにリダイレクトされ、権限を付与し、クライアントはスコープ付きアクセストークンを受け取ります。このトークンはすべてのMCPリクエストで送信されます。

MCPツールとMCPリソースの違いは何ですか?

ツールはアクション -- AIが実行できるもの(プロジェクトを作成、メールを送信、クエリを実行)。リソースはデータ -- AIがコンテキスト用に読み取ることができるもの(ドキュメント、設定ファイル、分析サマリー)。ツールはオンデマンドで呼び出されます。リソースはモデルのコンテキストウィンドウに読み込まれます。ツールをアクション用に、リソースを参照資料用に設計します。

MCPサーバーは何個のツールを公開すべきですか?

私の経験から、ほとんどのSaaS製品で5~15ツールが最適です。5未満ではMCPサーバーは非常に有用ではありません。20を超えるとAIモデルは低いツール選択判断を下し始めます。関連操作をすべてのCRUD操作を公開するのではなく、パラメータオプション付きの単一ツールにグループ化します。

これはNext.js以外のフレームワークで機能しますか?

もちろん。@modelcontextprotocol/sdkはあらゆるNode.jsフレームワークで機能します。Hono、Express、またはSSRエンドポイント付きAstroさえ使用できます。VercelのNext.jsは、組み込みストリーミングサポート、エッジミドルウェア、ゼロ設定デプロイのため、特に便利な組み合わせです。別のスタックを使用している場合、我々のヘッドレスCMS開発チームは複数のフレームワーク全体でMCPサーバーを構築しています。

開発中にMCPサーバーをテストするにはどうしますか?

MCPインスペクター(公式MCPツールキットの一部)があなたの最良の友人です。ローカルサーバーに接続し、ツールを呼び出したり、リソースを閲覧したり、対話的に応答をデバッグしたりできます。自動テストの場合、MCPサーバーをインプロセスでインスタンス化してプログラムでツールを呼び出すが統合テストを作成します。SDKはHTTPトランスポートなしでこれをサポートしています。

MCPリクエスト中にVercel関数がコールドスタートされるとどうなりますか?

MCPクライアントは潜在的遅延に対して耐久性があるよう設計されています。通常、AIモデル応答を待っており、それは数秒かかります。400~800msのコールドスタートは実際には知覚できません。懸念がある場合、Vercel Pro プランではクロンベースの暖機化を設定でき、SDKには瞬間的な故障に対する自動リトライロジックが含まれています。6ヶ月の本番使用で、コールドスタートはユーザーが報告した問題ではありませんでした。