Claude Media
業務SaaS MCP連携ガイド — ConnectorなしでClaudeに繋ぐ自作手順

業務SaaS MCP連携ガイド — ConnectorなしでClaudeに繋ぐ自作手順

公式ConnectorもMCPサーバーも無い業務SaaSは、REST APIを自作MCPサーバーでラップしてClaudeに繋ぎます。認証設計からInspectorでの検証、配布までの手順です。

Connectorが無い業務SaaSをMCPで繋ぐ方法

業務で日常的に使うSaaSの多くはWeb APIだけを公開し、公式のMCPサーバーやConnectorディレクトリへの掲載を持ちません。freee(freee-mcp)やkintone(kintone/mcp-server)のようにベンダー自身がMCPサーバーをGitHubで配布している例は、まだ一部にとどまります。公式の連携が無いSaaSをClaudeから直接操作するには、そのREST APIをMCPサーバーでラップし、claude mcp addかカスタムConnectorとして登録する経路が現実的です。

本記事はこの「既存REST APIをMCPサーバー化する」作業に絞ります。MCPのプロトコル自体の仕組みやtool・resource・promptの定義構文はMCPサーバー自作ガイドに譲り、本記事では業務SaaS特有の認証設計とエラー処理を中心に扱います。対象SaaSがOpenAPI仕様を公開しているなら、OpenAPIからMCPサーバーを自動生成する方法で手動定義を省けます。

前提: 対象SaaSのAPI仕様を確認する

着手前に、対象SaaSの開発者向けドキュメントで次の3点を確認します。認証方式(APIキー・OAuth2のクライアントクレデンシャル・OAuth2の認可コード)、レート制限の有無と上限、そしてAPIキー発行に管理者権限が必要かどうかです。国内の業務SaaSでは、管理画面から発行するAPIキーをヘッダーに付ける方式か、OAuth2のクライアントクレデンシャル方式が多い傾向です。認可コード方式(ユーザーごとのログインを伴う方式)を採用しているSaaSは、MCPサーバー側でもOAuthフローの実装が必要になり難度が上がります。

手順1: 呼び出す操作を絞り込む

業務SaaSのAPIは数十から数百のエンドポイントを持つことがありますが、全部をツール化する必要はありません。実際にClaudeに任せたい作業から逆算し、最初は3〜5個の操作に絞ります。典型的には「一覧取得」「詳細取得」「新規作成」の3種類があれば、日常的な問い合わせと定型作業の大半をカバーできます。エンドポイントを絞ることは、後述するツール出力のトークン消費を抑える意味でも有効です。

手順2: 認証ヘッダーをMCPサーバー内に隠す

APIキーをClaude側の設定ファイルに直接書かず、MCPサーバーの環境変数として渡し、サーバー内部でリクエストヘッダーに組み込む設計にします。TypeScript SDKでの最小構成は次の形です。

import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";
 
const API_BASE = "https://api.example-saas.jp/v1";
const API_KEY = process.env.SAAS_API_KEY;
 
const server = new McpServer({ name: "saas-connector", version: "0.1.0" });
 
server.registerTool(
  "list-requests",
  {
    title: "申請一覧の取得",
    description: "指定した期間の申請データを一覧取得する",
    inputSchema: {
      from: z.string().describe("開始日(YYYY-MM-DD)"),
      to: z.string().describe("終了日(YYYY-MM-DD)"),
    },
  },
  async ({ from, to }) => {
    const res = await fetch(`${API_BASE}/requests?from=${from}&to=${to}`, {
      headers: { Authorization: `Bearer ${API_KEY}` },
    });
    if (!res.ok) {
      return {
        content: [{ type: "text", text: `APIエラー: ${res.status} ${await res.text()}` }],
        isError: true,
      };
    }
    const data = await res.json();
    return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
  },
);
 
await server.connect(new StdioServerTransport());

APIキーはコードに埋め込まず、claude mcp add時の--envか.mcp.jsonの${VAR}展開で渡します。エラー時は例外を投げて落とすのではなく、isError: trueのツール結果として返すと、Claudeがエラー内容を読んで次の対応を判断できます。

手順3: ページネーションとレート制限に対応する

業務SaaSのAPIは1回のリクエストで返す件数に上限があり、超えると次ページ取得用のカーソルやトークンが返る設計が一般的です。ツール側で全ページを辿って結合すると便利です。ただ、件数が多いAPIでは出力がClaude Code側の上限に当たります。取得件数に上限を設ける、必要な列だけに絞って返す、集計はサーバー側で済ませてから返す、のいずれかで出力量を抑えます。

出力量

ツール出力の大きさと3つの境目

  • 1万トークン超で警告

    MCPツールの出力が10,000トークンを超えると警告が出ます。この閾値は固定です。

  • 2万5千トークンが既定の上限

    既定の最大は25,000トークンです。環境変数MAX_MCP_OUTPUT_TOKENSで引き上げられますが、画像を返すツールにもこの上限がかかります。

  • ツール単位で上限を宣言

    ツール定義の_metaにanthropic/maxResultSizeCharsを入れると、テキスト出力はその文字数まで受け付けられ、上限は50万文字です。入れない場合、閾値を超えた結果はディスクに保存され、会話にはファイル参照だけが残ります。

スキーマ取得のように大きな出力が避けられないツールだけ、次の形で宣言します。

{
  "name": "get_schema",
  "description": "Returns the full database schema",
  "_meta": {
    "anthropic/maxResultSizeChars": 200000
  }
}

レート制限(429エラー)を返すAPIでは、単純な再試行ではなくRetry-Afterヘッダーの秒数に従って待機を入れます。実装しないまま連続でツールを呼ばせると、正常なリクエストまで拒否され続ける事態になります。

手順4: Inspectorで検証して登録する

実装したサーバーは、Claude Codeにつなぐ前にMCP Inspectorで動作確認します。

npx @modelcontextprotocol/inspector npx tsx src/server.ts

InspectorのToolsタブから実際の期間を入れてlist-requestsを呼び、APIキーが正しくヘッダーに載っているか、エラー時の応答が期待通りかを確認します。問題なければClaude Codeに登録します。登録コマンドのオプションはv2.1.289のclaude mcp add --helpで、次のように表示されます。

Usage: claude mcp add [options] <name> <commandOrUrl> [args...]
 
  -e, --env <env...>           Set environment variables (e.g. -e KEY=value)
  -H, --header <header...>     Set headers for HTTP/SSE servers
  -s, --scope <scope>          Configuration scope (local, user, or project)
                               (default: "local")
  -t, --transport <transport>  Transport type (stdio, sse, http). Defaults to
                               stdio if not specified.

既定のスコープはlocalです。

claude mcp add --env SAAS_API_KEY=your-key --transport stdio saas-connector \
  -- npx tsx /absolute/path/to/saas-connector/src/server.ts

チームで使わないローカル専用サーバーであれば、これで完了です。Claude.aiのチャットからも使いたい場合は、HTTPトランスポートに書き換えてリモートで動かし、カスタムConnectorとして追加する経路もあります。追加手順はClaude Connectorsとはにまとめています。

手順5: チームに配布する

--scope projectで登録すると、設定はリポジトリ直下の.mcp.jsonに書き込まれます。これをコミットして共有すれば、クローンした全員が同じサーバー定義を使えます。

落とし穴は、手順4の登録コマンドに--scope projectを足しただけだと、--envに書いたキーの実値がそのまま.mcp.jsonに入ることです。v2.1.289では、-eの値に'${SAAS_API_KEY}'と書くと、.mcp.jsonは次の内容になります。

claude mcp add --scope project -e SAAS_API_KEY='${SAAS_API_KEY}' \
  --transport stdio saas-connector -- npx tsx /abs/server.ts
{
  "mcpServers": {
    "saas-connector": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "/abs/server.ts"],
      "env": { "SAAS_API_KEY": "${SAAS_API_KEY}" }
    }
  }
}

HTTPサーバーも同じで、--header 'Authorization: Bearer ${SAAS_API_KEY}'と書けば、headersにその文字列のまま保存されます。このとき登録コマンドの画面表示では、ヘッダーの値が[REDACTED]と伏せ字になります。実値を-eに書いた場合は、キーがコミット対象のファイルにそのまま入る点が違いです。

${VAR:-default}の形でデフォルト値も書けます。.mcp.jsonはプロジェクトを開いた初回に承認が必要で、未承認のサーバーはclaude mcp listでPending approvalと表示され、接続も行われません。

手順6: ツールに渡す権限を必要最小限に絞る

APIキーやOAuthのクライアントには、たいてい複数のスコープ(読み取り・書き込み・削除など)を選べます。MCPサーバーに発行するキーは、実装したツールが実際に使う範囲だけに絞り込みます。一覧取得と詳細取得しかツール化していないのに、書き込み・削除まで含む広いスコープのキーを使うと、Claudeが誤って(あるいはプロンプトインジェクションの結果として)意図しない操作を実行するリスクが上がります。

削除や送信のような取り返しのつかない操作をツール化する場合は、実行前に確認を挟む設計も検討します。そのための仕組みが、ツール定義の_metaに"anthropic/requiresUserInteraction": trueを入れる方法です。このツールは、acceptEdits・auto・bypassPermissionsの各モードでも毎回許可プロンプトが出ます。「今後は確認しない」の選択肢も出ません。v2.1.246より前は、この選択肢が出てしまう不具合がありました。一致する許可ルールがあっても省略されず、dontAskモードでは拒否されます。値は真偽値のtrueでなければ無視されます。社内API呼び出しの許可リスト設計は、Hooksでコマンド実行を制限する考え方と共通する部分が多く、Hooks実例カタログのコマンド検証レシピが参考になります。

自作するか、既製の連携で足りるか

すべてのSaaS連携を自作MCPサーバーで解決する必要はありません。判断の軸は「会話の中でデータを参照・分析したいか」「決まったトリガーで機械的に処理を流したいか」です。SalesforceやHubSpot、monday.comのような営業系SaaSは、自作MCPを組む前にベンダー提供の接続経路が既にあることが多く、Claude営業連携ガイドで接続経路とプラン条件を先に確認しておくと無駄な実装を避けられます。

やりたいこと向いている手段
Claudeとの会話の中でSaaSのデータを参照・分析したい向いている手段自作MCPサーバー
SaaS間でデータを定期的に転記・同期したい向いている手段Zapier・Makeのような自動化ツール
対応済みの操作をノーコードで組みたい向いている手段Zapier・MakeのMCP対応機能
APIに無い独自ロジックを挟みたい向いている手段自作MCPサーバー

ZapierやMakeを挟む選択肢との具体的な使い分けはZapier MCP使い分けガイドで扱っています。すでに自社がZapierやMakeを契約しているなら、まずそちらのMCP対応で足りないかを確認してから自作に進むと、開発と保守のコストを避けられます。

よくあるつまずき

OAuthトークンの期限切れで急に失敗する

クライアントクレデンシャル方式のトークンには有効期限があります。起動時に一度取得したトークンをそのまま使い続けるサーバーは、数時間後に401エラーで失敗し始めます。トークンの有効期限を見てリフレッシュする処理を、最初から組み込んでおきます。

--の区切りを忘れてコマンドが渡らない

claude mcp addでサーバー起動コマンドを指定する際、オプションと起動コマンドの間に--を挟み忘れると、-yのようなフラグがclaude mcp add自身のオプションとして読まれます。--は、Claudeのオプションとサーバーの起動コマンドを分ける区切りで、--以降はそのままサーバーに渡ります。手順4の例のように-- npx tsx ...の形で区切りを明示します。

--envを付け忘れて401になる

claude mcp add実行時に--env SAAS_API_KEY=your-keyを指定し忘れると、サーバー内部でprocess.env.SAAS_API_KEYがundefinedのまま起動し、APIへのリクエストは認証ヘッダーが空の状態で送られます。結果として全ツール呼び出しが401エラーで失敗します。エラーメッセージだけを見るとAPIキー自体が無効に見えるため、まず環境変数が渡っているかを疑います。登録コマンドでは、--envの直後にサーバー名を置くと、名前がもう1組のKEY=valueとして読まれます。手順4の例のように--transportを挟めば、この読み違いは起きません。

.mcp.jsonに${SAAS_API_KEY}と書いたのに環境変数が未設定で、:-のデフォルト値も無い場合です。claude mcp listと/mcpに、その変数名つきの警告が出ます。サーバー自体は${SAAS_API_KEY}という文字列が展開されないまま読み込まれます。

.mcp.jsonの承認ダイアログを見落とす

--scope projectで登録したサーバーは、プロジェクトを開いた初回に承認が必要です。承認前は、.mcp.jsonにサーバー定義があってもツールが一覧に出ず、設定ミスと勘違いしやすくなります。ツールが見えないときは、次の順で切り分けます。

手順

ツールが出てこないときの切り分け

  1. 1

    claude mcp listで状態を見る

    Pending approvalと出ていれば、承認待ちです。

  2. 2

    claudeを対話で起動して承認する

    対話セッションで承認ダイアログに答えます。拒否した選択を戻すならclaude mcp reset-project-choicesを使います。

  3. 3

    リポジトリを初めて開いた場合は信頼を確認する

    v2.1.196以降、信頼ダイアログを承諾するまでは、リポジトリにコミットされた.claude/settings.jsonの承認設定が無視されます。クローン直後は、コミット済みの承認設定が効かずPending approvalのままになります。

stdioの標準出力を汚してプロトコルが壊れる

stdioトランスポートはMCPのメッセージを標準出力でやり取りするため、console.logによるデバッグ出力がそのまま標準出力に混ざるとメッセージが壊れ、Claude Code側で接続エラーになります。MCP公式のサーバー構築ガイドも、STDIOサーバーではconsole.log()を使わないよう明記しています。デバッグ出力は標準エラー出力(console.error)に書くようにします。

よくある質問

対象のSaaSに既製のMCPサーバーが無いか、どう確認しますか

まずAnthropicのConnectorディレクトリと、npmやGitHubで「SaaS名MCP」を検索します。自作に着手する前の確認として、数分で済みます。

まとめ

最初の一歩は、操作を3〜5個に絞ったサーバーをInspectorで動かすところまでです。キーは、そのツールが使う範囲だけのスコープで発行します。チームに配るときは、.mcp.jsonにキーの実値が入っていないかを見てからコミットします。定期的な転記や同期のように会話を要しない処理は、ZapierやMakeのMCP対応で足りるかを先に確かめると、自作の手間を省けることがあります。

この記事を共有:XはてブLinkedIn