mcpField
M
MCP Gateway

MCP Gateway

リモート
github.com/PanosSaltセキュリティ64 週間前に更新
リポジトリ

MCP Gateway について

MCP Gatewayは、組織内のデータベース、ファイルシステム、カスタムMCPツールをAIクライアントに公開するマルチテナント基盤です。認証、ロール権限、監査ログを一元管理し、OAuth 2.1とEntra IDに対応します。

主な機能

  1. 01マルチテナントのツールアクセス
  2. 02OAuth 2.1とEntra ID認証
  3. 03ロールベースのアクセス制御
  4. 044種類のデータベース向けSQLツール
  5. 05サンドボックス化されたファイルシステムツール
  6. 06監査ログと管理画面

活用シーン

  1. 01Claude Desktopに許可済みのデータベースツールを提供
  2. 02ロール別にSQLとファイル操作を制限
  3. 03組織ごとのMCPツールとユーザーを管理

MCP 対応

テナント専用のSSEエンドポイントを通じて、データベース、ファイルシステム、カスタムMCPツールをMCPクライアントに公開します。ユーザー向けのOAuth 2.1 PKCEと、非対話型クライアント向けのAPIキー認証に対応します。

設定

リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。

エンドポイントhttps://your-gateway.example.com/t/your-org/mcp/sse
インストールコマンドをコピー
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。

ツール

詳細

MCP Gateway

Tests License: MIT Python 3.12

MCP ツール向けの本番環境用プラットフォームです。

Claude Desktop は、単一の認証済みエンドポイントを介して、データベース、ファイルシステム、API など、あらゆる内部ツールに接続できます。どのユーザーがどのツールを使用できるかを制御でき、すべての操作がログに記録されます。また、生の認証情報がサーバーの外部に出ることはありません。

組み込みツール:SQL クエリ(Postgres、MySQL、SQLite、MSSQL)、ファイルシステムアクセス。 カスタムツール:MCP ツールインターフェースを実装するものなら、どんなツールでも接続できます。

動作を見る — MCP Gateway を介して Claude Desktop がデータベースにクエリを実行する短いデモです。

目次


概要

MCP Gateway は AI アシスタントとデータベースの間に位置します。次の機能を備えています。

  1. パスワードログイン、Microsoft Entra ID(Azure AD)、または API キーを使用してユーザーを認証します
  2. ロールベースアクセス制御(viewer / analyst / admin)を適用します
  3. データベースを、AI アシスタントが検出して呼び出せる MCP ツールとして公開します
  4. Claude を介して自然言語の質問を SQL に変換し、クエリを実行して、結果を要約します
  5. すべてのアクティビティを構造化された監査証跡に記録します
Claude Desktop / mcp-remote
        │
        │ MCP over SSE (OAuth 2.1 + PKCE)
        ▼
┌─────────────────────────────────────────────────────────┐
│                      MCP Gateway                        │
│                                                         │
│  ┌──────────┐  ┌──────────┐  ┌───────────────────────┐ │
│  │ Auth /   │  │  Admin   │  │   MCP SSE Endpoint    │ │
│  │ OAuth    │  │   UI     │  │  /t/{slug}/mcp/sse    │ │
│  └──────────┘  └──────────┘  └───────────────────────┘ │
│                                          │              │
│  ┌──────────────────────────────────────┐│              │
│  │         Tool Providers               ││              │
│  │  sql.py → get_schema / execute_sql   ││              │
│  └──────────────────────────────────────┘│              │
└─────────────────────────────────────────┼───────────────┘
                                          │ Decrypted DSN
                    ┌─────────────────────┼────────────────┐
                    │   Your Databases    │                │
                    │  Postgres  MySQL  MSSQL  SQLite      │
                    └────────────────────────────────────  ┘

すぐに利用できる機能

組織向け

  • Claude Desktop 用の URL は 1 つだけです。ユーザーは一度認証すれば、許可されたすべてのものにアクセスできます
  • Microsoft Entra ID SSO — Azure AD グループからロールが自動的に割り当てられます
  • 完全な監査証跡 — すべてのツール呼び出し、すべてのクエリ、すべてのログインについて、誰が何をいつ実行したかを記録します

ツール向け

  • MCP ツールを Gateway に追加するだけで、認証、RBAC、ログ記録が自動的に適用されます
  • ツールごとのロール上書き — SQL 実行を analyst に、ファイルシステムへの書き込みを admin に制限できます
  • 同梱ツール:SQL ツール(4 種類のデータベース)、ファイルシステムツール(読み取り、書き込み、検索、ツリー)

セキュリティチーム向け

  • 従業員のマシンに認証情報を置く必要はありません
  • テナント分離 — 組織 A が組織 B のツールやデータを見ることはできません
  • CI/CD 向けの API キー、人間のユーザー向けの OAuth 2.1 + PKCE
対応データベース
データベースドライバーDSN 形式
PostgreSQLpsycopg2postgresql://user:pass@host/db
MySQL / MariaDBPyMySQLmysql+pymysql://user:pass@host/db
Microsoft SQL Serverpymssqlmssql+pymssql://user:pass@host/db
SQLite組み込みsqlite:///path/to/file.db
ファイルシステムツール
  • MCP ツールとして公開される、サンドボックス化されたファイルの読み取り/書き込み/検索
  • FILESYSTEM_ALLOWED_DIRS 環境変数で有効化します
  • 読み取り操作(analyst 以上):fs_read_file、fs_list_directory、fs_directory_tree、fs_search_files、fs_get_file_info
  • 書き込み操作(admin):fs_write_file、fs_create_directory、fs_move_file
管理 UI
  • /admin/ で提供されるウェブインターフェース
  • 接続、ユーザー、SSO 設定、API キー、ツールのロールを管理します
  • 監査ログ、生成された SQL、クエリ結果を表示します

アーキテクチャ

テクノロジースタック
レイヤーテクノロジーバージョン
API フレームワークFastAPI + Starlette0.136.1 / 1.3.1
ASGI サーバーUvicorn0.34.0
バリデーションPydantic + pydantic-settings2.12.5 / 2.7.1
ORMSQLAlchemy2.0.30
マイグレーションAlembic1.13.1
認証 / JWTPyJWT + bcrypt2.14.0 / 4.0.1
暗号化cryptography (Fernet)50.0.0
LLMAnthropic SDK0.42.0
MCP プロトコルmcp1.28.1
SQL バリデーションsqlglot25.1.0
レート制限slowapi0.1.9
HTTP クライアントhttpx0.28.1
DB ドライバーpsycopg2-binary / PyMySQL / pymssql2.9.10 / 1.1.1 / 2.3.1
フロントエンドReact 18 + TypeScript + Vite—

開発ツール(requirements-dev.txt):pytest、pytest-asyncio、ruff、mypy。 上記の固定バージョンは requirements.txt から生成されています。両方を合わせて更新してください。

プロジェクト構成
app/
├── main.py               # FastAPI app setup, middleware, routing
├── config.py             # Environment config (Pydantic Settings)
├── database.py           # SQLAlchemy engine + session factory
├── api/
│   ├── auth.py           # POST /auth/login
│   ├── auth_entra.py     # Entra SSO (legacy admin UI paths)
│   ├── oauth.py          # OAuth 2.1 endpoints (/t/{slug}/oauth/*)
│   ├── connections.py    # DB connection CRUD
│   ├── query.py          # Natural language query endpoint
│   ├── tenants.py        # Tenant + user management
│   ├── tools.py          # Tool listing + role overrides
│   ├── mcp_sse.py        # MCP SSE transport
│   ├── api_keys.py       # API key management
│   └── audit_logs.py     # GET /audit-logs/ (admin)
├── core/
│   ├── auth.py           # JWT creation/validation, password hashing
│   ├── dependencies.py   # FastAPI dependency injection
│   ├── rbac.py           # Role hierarchy helpers
│   ├── security.py       # Fernet encrypt/decrypt
│   ├── api_keys.py       # API key generation + hashing
│   ├── limiter.py        # slowapi rate limiter setup
│   └── log_filter.py     # Health-check log noise filter
├── constants.py          # Non-tunable application-wide constants (pagination caps, etc.)
├── models/__init__.py    # All SQLAlchemy ORM models
├── schemas/__init__.py   # All Pydantic request/response schemas
├── services/
│   ├── entra.py          # Microsoft Graph API client
│   ├── llm.py            # Anthropic API (SQL gen + summarization)
│   ├── mcp_client.py     # Direct SQLAlchemy schema introspection + query execution
│   └── audit.py          # Audit log writer
└── tools/
    ├── __init__.py       # Tool provider framework + registry
    ├── sql.py            # DB schema + execute_sql tools
    ├── example.py        # Example custom tools
    └── filesystem.py     # Sandboxed file read/write/search tools

app/static/               # Built admin UI, served at /admin/ (generated by the
                          # frontend build; not edited by hand)

frontend/src/
├── main.tsx              # Vite entry point
├── App.tsx               # Root component, auth context, tab routing
├── api.ts                # API client, token management
├── types.ts              # TypeScript types (mirrors Pydantic schemas)
├── constants.ts          # Frontend constants (timeouts, retry config)
├── app.css               # Global styles
├── hooks/
│   └── useForm.ts        # Shared form state helper
└── components/
    ├── Login.tsx          # Sign-in form
    ├── Setup.tsx          # Tenant registration
    ├── Dashboard.tsx      # Tenant info + role display
    ├── Connections.tsx    # DB connection management
    ├── ConnectionCreateForm.tsx  # Add-connection form
    ├── ConnectionEditRow.tsx     # Inline connection editor
    ├── Query.tsx          # Natural language query UI
    ├── Users.tsx          # User management (admin)
    ├── SsoConfig.tsx      # Entra ID configuration (admin)
    ├── Tools.tsx          # Tool browser + role overrides
    ├── ToolGroup.tsx      # Grouped tool listing
    ├── ToolRoleOverride.tsx      # Per-tool role control
    ├── ApiKeys.tsx        # API key management
    ├── AuditLog.tsx       # Filterable audit log viewer (admin)
    ├── AuditLogFilters.tsx       # Audit log filter controls
    ├── AuditLogDetail.tsx        # Single audit entry detail
    ├── ConfirmModal.tsx   # Reusable confirmation dialog
    └── ErrorBoundary.tsx  # Top-level error boundary

alembic/versions/         # Database migrations
docs/                     # Guides (OAuth flow, deployment, audit logging, …)
tests/                    # pytest suite (SQLite in-memory, no services needed)
データベーススキーマ
Tenants ─┬─► Users ──────► APIKeys
         ├─► APIKeys              (also a direct tenant_id FK, not only via Users)
         ├─► DBConnections
         ├─► TenantEntraConfig
         ├─► AuditLogs                (tenant_id and user_id both nullable)
         ├─► OAuthAuthorizationCodes
         ├─► OAuthRefreshTokens
         └─► ToolRoleOverrides

OAuthStates                          (no tenant_id FK — holds a plain
                                      tenant_slug string, since the row is
                                      created before the tenant is resolved)

クイックスタート

前提条件
  • Docker と Docker Compose
  • Anthropic API キー(/query/ エンドポイント用。MCP ツールへの直接アクセスには不要です)
1. クローンして設定する
git clone <repo-url>
cd MCP-Gateway
cp .env.example .env

.env を編集します:

# Required — generate unique values
SECRET_KEY=<random 64-char string>
ENCRYPTION_KEY=<random string, min 32 chars — longer is better>
POSTGRES_PASSWORD=<strong password>

# Required for natural language query
ANTHROPIC_API_KEY=sk-ant-...

# Update to your server's public URL in production
BASE_URL=http://localhost:8000

安全なランダム値を生成します:

# SECRET_KEY
python3 -c "import secrets; print(secrets.token_hex(32))"

# ENCRYPTION_KEY (min 32 chars; full key consumed via BLAKE2b derivation)
python3 -c "import secrets; print(secrets.token_hex(32))"
2. スタックを起動する
docker compose up -d

起動するサービス:

  • ポート 8000 の api(FastAPI + 管理 UI)
  • ポート 5432 の db(PostgreSQL、内部のみ)
3. 最初のテナントを登録する
curl -s -X POST http://localhost:8000/tenants/ \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Organization",
    "slug": "my-org",
    "admin_email": "[email protected]",
    "admin_password": "SuperSecret123!"
  }' | jq

slug は MCP URL の一部になります:http://localhost:8000/t/my-org/mcp/sse

4. 管理 UI を開く

http://localhost:8000/admin/ にアクセスし、管理者の認証情報でサインインします。

5. データベース接続を追加する

管理 UI → 接続 → 接続を作成、または API 経由で追加します:

TOKEN=$(curl -s -X POST http://localhost:8000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"SuperSecret123!"}' \
  | jq -r .access_token)

curl -s -X POST http://localhost:8000/connections/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production DB",
    "db_type": "postgres",
    "connection_string": "postgresql://user:pass@host/mydb",
    "min_role": "viewer"
  }' | jq
6. Claude Desktop に接続する

Claude Desktop の MCP 設定(macOS の場合は ~/Library/Application Support/Claude/claude_desktop_config.json)に追加します:

{
  "mcpServers": {
    "my-org-gateway": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "http://localhost:8000/t/my-org/mcp/sse"
      ]
    }
  }
}

Claude Desktop を再起動します。OAuth ログイン用のブラウザーウィンドウが開きます。認証後、Claude はデータベースツールを使用できるようになります。


設定

すべての設定は環境変数で行います。テンプレートについては .env.example を参照してください。

必須
変数説明
SECRET_KEYJWT 署名用シークレット — ランダムな 64 文字の文字列を使用してください
ENCRYPTION_KEYDB 認証情報用の Fernet AES キー — 32 文字以上。キー全体が BLAKE2b で使用されます
DATABASE_URLPostgreSQL DSN — docker-compose によって自動設定されます。ローカル(Docker を使用しない)開発でのみ必要です。デフォルト値はありません。設定しない場合、アプリは起動しません

POSTGRES_PASSWORD も必要ですが、必要とするのはアプリケーションではなく docker-compose です。この値は db サービスの初期設定に使われ、DATABASE_URL に埋め込まれます。POSTGRES_PASSWORD、SECRET_KEY、ENCRYPTION_KEY のいずれかが未設定の場合、Compose は即座に失敗します。

オプション
変数デフォルト説明
ANTHROPIC_API_KEY—/query/ NL クエリエンドポイントに必要です
BASE_URLhttp://localhost:8000公開 URL(OAuth コールバックで使用)
CORS_ORIGINSBASE_URLCORS で許可するオリジンをカンマ区切りで指定します。絶対 URL を指定する必要があります。ワイルドカード(*)は拒否されます
ACCESS_TOKEN_EXPIRE_MINUTES15JWT アクセストークンの有効期間
REFRESH_TOKEN_EXPIRE_DAYS30OAuth リフレッシュトークンの有効期間
OAUTH_STATE_TTL_MINUTES10OAuth PKCE state の有効期間。レイテンシーの高い SSO プロバイダーを使用する場合は、この値を大きくしてください
OAUTH_CODE_TTL_MINUTES5OAuth 認可コードの有効期間
LLM_MODELclaude-sonnet-4-6SQL 生成に使用する Anthropic モデル
LLM_MAX_TOKENS_SQL1024SQL 生成の最大トークン数
LLM_MAX_TOKENS_SUMMARY500結果の要約に使用する最大トークン数
FILESYSTEM_ALLOWED_DIRS—MCP ファイルシステムツールがアクセスできるディレクトリをカンマ区切りで指定します。エントリは絶対パスである必要があり、.. を含めることはできません。そうでない場合、アプリは起動を拒否します。空の場合、ファイルシステムツールは公開されません
LOG_LEVELINFOルートログレベル(DEBUG、INFO、WARNING、ERROR)
ALGORITHMHS256JWT 署名アルゴリズム
REDIS_URL—レート制限ストレージを共有します。空の場合、制限はメモリ内に保存されるため、ワーカーごとに適用されます
WEB_CONCURRENCY1Uvicorn ワーカー数。REDIS_URL が設定されていない状態で 1 を超えると、レート制限はこの数だけ乗算されます
TRUST_PROXY_HEADERSfalseX-Forwarded-For からレート制限キーを導出します。信頼できるプロキシの背後にある場合にのみ有効にしてください。そうでない場合、クライアントが値を偽装できます
Entra ID
変数デフォルト説明
ENTRA_AUTHORITY_URLhttps://login.microsoftonline.comMicrosoft ID プラットフォームのベース URL
ENTRA_GRAPH_URLhttps://graph.microsoft.com/v1.0Microsoft Graph API のベース URL

認証

ローカルログイン
POST /auth/login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "SuperSecret123!",
  "tenant_slug": "my-org"   // optional, disambiguates if same email is in multiple tenants
}

レスポンス:

{
  "access_token": "eyJ...",
  "token_type": "bearer"
}

以降のリクエストにはトークンを含めます:

Authorization: Bearer eyJ...

アクセストークンはデフォルトで 15 分後に有効期限が切れます。OAuth トークンエンドポイントでリフレッシュトークンを使用すると、新しいトークンのペアを取得できます。

API キー

キーを生成します(認証が必要です):

curl -s -X POST http://localhost:8000/api-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "CI pipeline"}' | jq

レスポンスの raw_key は一度だけ表示されるため、すぐに保存してください:

{
  "id": "...",
  "name": "CI pipeline",
  "prefix": "mgw_abcd1234",
  "raw_key": "mgw_abcd1234...",
  "created_at": "..."
}

クエリパラメーターで使用します:

curl "http://localhost:8000/connections/?api_key=mgw_abcd1234..."
OAuth 2.1(MCP ブラウザークライアント)

ゲートウェイは RFC 8414 OAuth ディスカバリーを実装しています。MCP クライアントは次のフローを自動的に実行します:

  1. クライアントが /t/{slug}/mcp/sse に接続します。401 と、OAuth ディスカバリー URL を示す WWW-Authenticate ヘッダーを受け取ります
  2. クライアントが /.well-known/oauth-authorization-server/t/{slug} を取得します
  3. クライアントが POST /t/{slug}/oauth/register で動的に登録します
  4. クライアントがブラウザーを開き、ユーザーが /t/{slug}/oauth/authorize でログインします
  5. クライアントが POST /t/{slug}/oauth/token でコードと PKCE 検証子をトークンに交換します
  6. クライアントが Bearer トークンを使って再接続します

手動での設定は不要です。mcp-remote にテナントの SSE URL を指定するだけです。

API キー認証(MCP 非対話型クライアント)

CI/CD、スクリプト、またはブラウザーログインを省略したい場合は、URL に API キーを指定します:

{
  "mcpServers": {
    "gateway": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8000/t/my-org/mcp/sse?api_key=mgw_..."]
    }
  }
}

SSE エンドポイントがキーを検証し、セッションを直接確立します。OAuth フローもブラウザーウィンドウも必要ありません。詳しくはAPI キーを参照してください。


API リファレンス

すべての管理エンドポイントは、標準パス(例:/tenants/)とバージョン付きプレフィックス /api/v1/(例:/api/v1/tenants/)の両方で利用できます。バージョンなしのパスは、現在のフロントエンドとの後方互換性のために維持されています。新しい統合では /api/v1/ を使用してください。プロトコルで定義されたルート(OAuth /t/{slug}/…、MCP /t/{slug}/…、/.well-known/)とインフラストラクチャルート(/health、/admin)には、意図的にバージョンが付いていません。

以下の curl の例では、稼働中の管理 UI と一致するよう、バージョンなしのパスを使用しています。 新しい統合では、先頭に /api/v1 を付けてください。

認証
メソッドパスロール説明
POST/auth/login公開メールアドレスとパスワードでログインし、JWT を返します
テナントとユーザー
メソッドパスロール説明
POST/tenants/公開新しいテナントと管理者ユーザーを登録します
GET/tenants/me全員自分のテナントの詳細を取得します
GET/tenants/users管理者テナント内のすべてのユーザーを一覧表示します
POST/tenants/users管理者ローカルユーザーを作成します
PATCH/tenants/users/{user_id}管理者ユーザーのロールを更新します
DELETE/tenants/users/{user_id}管理者テナントからユーザーを削除します
Entra ID / SSO
メソッドパスロール説明
POST/auth/entra/config管理者テナントの Entra 設定を作成または置き換えます
GET/auth/entra/config管理者現在の Entra 設定を読み取ります(シークレットは伏せられます)
DELETE/auth/entra/config管理者Entra 設定を削除します
GET/auth/entra/login公開管理 UI の SSO ログインを開始します(Microsoft にリダイレクトします)
GET/auth/entra/callback公開管理 UI フローにおける Microsoft からのリダイレクト先です
GET/auth/entra/exchange公開1 回限りの SSO ノンスを JWT と交換します

テナントの登録:

curl -X POST http://localhost:8000/tenants/ \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp",
    "slug": "acme",
    "admin_email": "[email protected]",
    "admin_password": "SuperSecret123!"
  }'

ユーザーの作成:

curl -X POST http://localhost:8000/tenants/users \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "AnotherSecret456!",
    "role": "analyst"
  }'

ロールは viewer(デフォルト)、analyst、admin です。パスワードは 12 文字以上である必要があります。


接続
メソッドパスロール説明
POST/connections/管理者データベース接続を追加します
GET/connections/Viewer 以上アクセス可能な接続を一覧表示します
PATCH/connections/{id}管理者接続を更新します
DELETE/connections/{id}管理者接続を論理削除します

接続の追加:

curl -X POST http://localhost:8000/connections/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sales DB",
    "db_type": "postgres",
    "connection_string": "postgresql://user:pass@db-host/sales",
    "description": "Production sales database",
    "min_role": "analyst"
  }'

min_role は、この接続にクエリできるユーザーを指定します。このロールより下位のユーザーは、接続を表示したり使用したりできません。


自然言語クエリ
メソッドパスロールレート制限説明
POST/query/Analyst 以上30/分自然言語クエリを実行します
GET/query/history管理者60/分ページ分割されたクエリ監査履歴を取得します

クエリ:

curl -X POST http://localhost:8000/query/ \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "connection_id": "...",
    "question": "What are the top 5 customers by total revenue this quarter?"
  }'

レスポンス:

{
  "sql_generated": "SELECT customer_name, SUM(amount) AS total FROM orders ...",
  "result": [
    {"customer_name": "Acme Corp", "total": 125000}
  ],
  "summary": "The top customer this quarter is Acme Corp with $125,000 in revenue."
}

クエリパイプライン:

  1. データベースからスキーマを取得します
  2. スキーマと質問を Claude に送信し、SQL を生成します
  3. SQL が SELECT ステートメントであることを検証します(すべての書き込みをブロックします)
  4. SQL を実行します(タイムアウトは 30 秒です)
  5. 質問と結果を Claude に送信し、要約を生成します

ツール
メソッドパスロール説明
GET/tools/全員ロールのメタデータ付きで MCP ツールを一覧表示します
PATCH/tools/{tool_name}管理者ロールの上書きを設定またはリセットします

ツールの一覧表示:

curl http://localhost:8000/tools/ \
  -H "Authorization: Bearer $TOKEN"

レスポンス:

[
  {
    "tool_name": "execute_sql_sales-db_abcd1234",
    "description": "Execute SQL on Sales DB",
    "connection_id": "...",
    "default_min_role": "analyst",
    "effective_min_role": "admin",
    "accessible": false
  }
]

ツールロールの上書き:

# Restrict to admin only
curl -X PATCH "http://localhost:8000/tools/execute_sql_sales-db_abcd1234" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"min_role": "admin"}'

# Reset to connection default
curl -X PATCH "http://localhost:8000/tools/execute_sql_sales-db_abcd1234" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"min_role": null}'

API キー
メソッドパス説明
POST/api-keys新しいキーを生成します
GET/api-keys自分のキーを一覧表示します
DELETE/api-keys/{id}キーを無効化します
# Generate (expires_at is optional)
curl -X POST http://localhost:8000/api-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "CI pipeline", "expires_at": "2027-01-01T00:00:00Z"}'

# List
curl http://localhost:8000/api-keys \
  -H "Authorization: Bearer $TOKEN"

# Revoke
curl -X DELETE "http://localhost:8000/api-keys/{id}" \
  -H "Authorization: Bearer $TOKEN"

監査ログ
メソッドパスロールレート制限説明
GET/audit-logs/管理者60/分監査イベントを一覧表示します(フィルタリング可能)
# All events (paginated)
curl "http://localhost:8000/audit-logs/?limit=50" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

# Filter by event type (comma-separated prefixes)
curl "http://localhost:8000/audit-logs/?event_prefix=query,tool&limit=100" \
  -H "Authorization: Bearer $ADMIN_TOKEN"

クエリパラメーター:skip(オフセット、デフォルト 0)、limit(最大 200、デフォルト 50)、event_prefix(カンマ区切り。例:query、login、fs)。


ヘルスチェック
curl http://localhost:8000/health
# {"status": "ok"}

データベースに接続できない場合は 503 を返します。Kubernetes のライブネスプローブおよびレディネスプローブに適しています。


MCP の統合

Claude Desktop への接続

mcp-remote をインストールします:

npm install -g mcp-remote

claude_desktop_config.json に追加します:

{
  "mcpServers": {
    "gateway": {
      "command": "npx",
      "args": ["mcp-remote", "http://localhost:8000/t/my-org/mcp/sse"]
    }
  }
}

初回接続時に、OAuth ログイン用のブラウザーウィンドウが開きます。認証後、mcp-remote はトークンをキャッシュし、自動的に再接続します。トークンはバックグラウンドでサイレントに更新されます。

利用可能な MCP ツール

ユーザーがアクセスできるアクティブなデータベース接続ごとに、ゲートウェイは 2 つのツールを公開します。

get_schema_{connection-name}_{id} データベーススキーマ全体(テーブル、カラム、型、制約、インデックス)を返します。Claude は SQL を生成する前にデータ構造を把握するため、最初にこのツールを呼び出します。

execute_sql_{connection-name}_{id} SELECT 文を実行し、行を JSON として返します。SELECT 以外の文(INSERT、UPDATE、DELETE、DROP など)はすべて拒否されます。実行タイムアウト:30 秒。

list_connections ユーザーがアクセスできるすべてのデータベース接続と、その名前および種類を返します。

get_current_time 現在の UTC 時刻を ISO 8601 形式で返します。すべてのロールで利用できます。

ファイルシステムツール(FILESYSTEM_ALLOWED_DIRS が設定されている場合のみ):

ツールロール説明
fs_read_fileAnalyst+ファイルを UTF-8 テキストとして読み取ります
fs_list_directoryAnalyst+ディレクトリの内容を一覧表示します
fs_directory_treeAnalyst+再帰的なディレクトリツリー(JSON)
fs_search_filesAnalyst+Glob パターンで検索します
fs_get_file_infoAnalyst+ファイルのメタデータ(サイズ、タイムスタンプ)
fs_write_fileAdminファイルを作成または上書きします
fs_create_directoryAdminディレクトリを作成します(親ディレクトリも作成)
fs_move_fileAdminファイルを移動または名前変更します
SSE エンドポイント
エンドポイント認証説明
GET /t/{slug}/mcp/sseBearer JWT または ?api_key=テナント単位の SSE(推奨)
POST /t/{slug}/mcp/messagesBearer JWT、?api_key=、またはセッション IDテナント単位のメッセージハンドラー
GET /mcp/sse?api_key= または ?token=レガシー SSE(非推奨、2027-03-01 に廃止)
POST /mcp/messagesBearer JWT、?api_key=、または ?token=レガシーのメッセージハンドラー(非推奨)
OAuth 検出エンドポイント
エンドポイントRFC説明
GET /.well-known/oauth-authorization-server/t/{slug}RFC 8414認可サーバーのメタデータ
GET /.well-known/oauth-protected-resource/t/{slug}/mcp/sseRFC 9728保護されたリソースのメタデータ
GET /t/{slug}/.well-known/oauth-authorization-server—同じメタデータ、テナントプレフィックス付きパス
GET /t/{slug}/.well-known/oauth-protected-resource—同じメタデータ、テナントプレフィックス付きパス
POST /t/{slug}/oauth/registerRFC 7591動的クライアント登録
GET /t/{slug}/oauth/authorizeRFC 6749認可エンドポイント(PKCE S256)
POST /t/{slug}/oauth/login—ローカルログインフォームの送信(SSO を使用しないテナント)
GET /t/{slug}/oauth/entra-callback—MCP OAuth フローにおける Microsoft のリダイレクト先
POST /t/{slug}/oauth/tokenRFC 6749トークンエンドポイント(code + refresh_token)

独自のツールを作成する

Python 関数はすべて、認証と監査に対応した MCP ツールになります。

# app/tools/my_tool.py
from app.tools import register_tool, ToolContext
from mcp.types import TextContent

@register_tool(name="my_custom_tool", min_role="analyst")
async def my_tool(arguments: dict, ctx: ToolContext) -> list[TextContent]:
    # ctx.user gives you the authenticated user + their role
    # ctx.db gives you the database session
    result = do_something(arguments["input"])
    return [TextContent(type="text", text=result)]

ゲートウェイを再起動します。認証と監査ログが含まれたツールが、自動的に Claude Desktop に表示されます。


ロールベースのアクセス制御

権限の低い順に 3 つのロールがあります:viewer → analyst → admin

デフォルトの権限
アクションViewerAnalystAdmin
接続を表示✓✓✓
自然言語クエリを実行—✓✓
ファイルシステムツールを使用(読み取り)—✓✓
ファイルシステムツールを使用(書き込み)——✓
監査ログを表示——✓
クエリ履歴を表示——✓
接続を管理——✓
ユーザーを管理——✓
SSO を設定——✓
API キーを管理✓✓✓
ツールのロールを上書き——✓
接続ごとのロール

各接続には min_role があります。このロールに満たないユーザーは、その接続や、そこから生成される MCP ツールを表示または使用できません。

例:min_role: admin に設定された機密性の高い本番データベースは、アナリストや閲覧者には一切表示されません。/connections/ や /tools/ に表示されず、その MCP ツールも一覧に表示されません。

ツールごとの上書き

管理者は、接続の min_role とは独立して、任意の MCP ツールに適用される最小ロールを上書きできます:

# Lock down SQL execution on prod, but keep schema browsing open
PATCH /tools/execute_sql_prod-db_abcd1234  {"min_role": "admin"}
PATCH /tools/get_schema_prod-db_abcd1234   {"min_role": "analyst"}

# Reset to connection default
PATCH /tools/execute_sql_prod-db_abcd1234  {"min_role": null}

Entra ID / SSO

Azure AD でのセットアップ
  1. Azure Active Directory でアプリケーションを登録します(App registrations → New registration)
  2. リダイレクト URI を追加します:
    • http://<gateway-url>/auth/entra/callback(管理 UI の SSO)
    • http://<gateway-url>/t/<slug>/oauth/entra-callback(MCP OAuth フロー)
  3. API permissions で以下を追加します:
    • Delegated(Microsoft Graph):openid、profile、email、User.Read、GroupMember.Read.All
    • Application(Microsoft Graph):GroupMember.Read.All(トークン更新時のロール同期に必要)
    • 委任された GroupMember.Read.All とアプリケーションの GroupMember.Read.All の両方について、admin consent を付与します
  4. Client secret を作成します(Certificates & secrets → New client secret)
  5. Azure テナント ID、アプリのクライアント ID、クライアントシークレットの値を控えます
MCP Gateway での設定

管理 UI の SSO Config タブ、または API から設定します:

curl -X POST http://localhost:8000/auth/entra/config \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "entra_tenant_id": "your-azure-tenant-uuid",
    "client_id": "your-app-client-id",
    "client_secret": "your-client-secret",
    "admin_group_id": "azure-group-uuid-for-admins",
    "analyst_group_id": "azure-group-uuid-for-analysts",
    "viewer_group_id": "azure-group-uuid-for-viewers"
  }'

グループ ID は任意です。必要なものだけ設定してください。複数のマッピング済みグループに所属するユーザーには、最も高いロールが割り当てられます。

ログインフロー

ユーザーを次の URL に誘導します:http://<gateway>/auth/entra/login?tenant_slug=<slug>

ゲートウェイは Microsoft にリダイレクトします。認証後、次の処理を行います:

  1. Microsoft Graph からユーザーのプロフィールを取得します(/me)
  2. 推移的なグループメンバーシップを取得します(/me/transitiveMemberOf)
  3. グループをロールにマッピングします(最も高いロールを適用:admin > analyst > viewer)
  4. ユーザーが存在しない場合は作成します(ジャストインタイムプロビジョニング)
  5. JWT を返します

開発

ローカルでのセットアップ(Docker を使用しない場合)
# Python environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt   # includes requirements.txt plus pytest/ruff/mypy

# Configure
cp .env.example .env
# Edit .env: set DATABASE_URL to a local Postgres instance (or use SQLite for quick testing)

# Run migrations
alembic upgrade head

# Start API (with auto-reload)
uvicorn app.main:app --reload --port 8000
フロントエンド開発
cd frontend
npm install
npm run dev   # Vite dev server on port 5173 with API proxy

Vite 開発サーバーはすべての API パスを http://localhost:8000 にプロキシするため、開発中はフロントエンドと API を個別に実行できます。

サンプルデータベース
docker compose --profile dev up -d

データが事前投入されたサンプルデータベースを起動します:

  • sample_postgres(ポート 5433)→ postgresql://sampleuser:samplepass@localhost:5433/sampledb
  • sample_mysql(ポート 3307)→ mysql+pymysql://sampleuser:samplepass@localhost:3307/sampledb

自然言語クエリ機能を試すには、管理 UI でこれらを接続として追加します。

テストの実行
# Full suite (no external services required — uses SQLite in-memory)
.venv/bin/python -m pytest

# Verbose output
.venv/bin/python -m pytest -v

# Single test file
.venv/bin/python -m pytest tests/test_connections_api.py -v

# Single test
.venv/bin/python -m pytest tests/test_oauth.py::test_token_exchange_valid_code_returns_tokens -v
データベースのマイグレーション
# Apply all pending migrations
alembic upgrade head

# Create a new migration after changing models/__init__.py
alembic revision --autogenerate -m "describe your change"

# Roll back one step
alembic downgrade -1

# Show history
alembic history --verbose
本番環境向けビルド
# Build frontend static files
cd frontend && npm run build && cd ..

# The Dockerfile builds both in a multi-stage build:
docker build -t mcp-gateway .
docker compose up -d

トラブルシューティング

「SECRET_KEY must be set」/「ENCRYPTION_KEY must be set」

docker-compose では ${VAR:?error message} 構文を使用しており、これらが設定されていない場合はすぐにエラーになります。次のコマンドで生成します:

python3 -c "import secrets; print(secrets.token_hex(32))"           # SECRET_KEY
python3 -c "import secrets; print(secrets.token_hex(32))"           # ENCRYPTION_KEY

docker compose up を実行する前に、.env ファイルに追加してください。

ヘルスチェックで API が 503 を返す

データベースに接続できません。次を確認してください:

docker compose ps        # are all containers running?
docker compose logs db   # any Postgres startup errors?
docker compose restart api  # restart API if db was slow to start
ログイン時に Claude Desktop でブラウザーが開かない

mcp-remote がインストールされていることを確認します:npm install -g mcp-remote。.env の BASE_URL が claude_desktop_config.json に記載した URL と一致していることを確認してください。URL が一致しないと、OAuth コールバックはエラーを表示せずに失敗します。

クエリが "INVALID_QUERY" を返す

LLM が質問に対する有効な SELECT を生成できなかったか、SELECT 以外のステートメントを生成したため(ブロックされます)、次を試してください:

  • 質問をより具体的にする
  • データベースの列名とテーブル名が内容を表すものになっていることを確認する
  • ANTHROPIC_API_KEY が設定され、有効であることを確認する
Entra ログインで 400「Entra ID not configured for this tenant」が返る

このテナントには Entra ID の設定がありません。Admin UI → SSO Config または POST /auth/entra/config から追加してください。

Entra コールバックで 403「Not a member of any authorized group」が返る

Azure AD ユーザーが、テナントに設定された 3 つのグループのいずれにも所属していません。次のいずれかを行ってください:

  • Azure AD でユーザーをマッピング済みグループのいずれかに追加する
  • ゲートウェイ設定のグループ ID を、ユーザーが実際に所属しているグループと一致するように更新する(POST /auth/entra/config)
更新トークンが「Invalid or expired」として拒否される

更新トークンは一度だけ使用できます。使用するたびに新しいペアが発行され、古いトークンは無効になります。同じ更新トークンを 2 つのリクエストが同時に使用しようとすると、2 つ目のリクエストは失敗します。再認証して新しいペアを取得してください。

レート制限による429レスポンス
エンドポイント制限
POST /tenants/5/min
POST /auth/login10/min
POST /t/{slug}/oauth/login10/min
POST /api-keys10/min
POST /t/{slug}/oauth/register10/min
GET /auth/entra/login20/min
GET /auth/entra/exchange20/min
GET /t/{slug}/oauth/entra-callback20/min
GET /t/{slug}/oauth/authorize30/min
POST /t/{slug}/oauth/token30/min(authorization_code と refresh_token の両方のグラントに適用)
POST /query/30/min
GET /audit-logs/60/min
GET /query/history60/min

制限ウィンドウがリセットされるまで60秒待ってください。

制限はクライアントのIPアドレスに基づきます。デプロイ時には、次の点に注意してください。

  • REDIS_URL がない場合、リミッターはカウンターをプロセス内メモリに保存するため、各uvicornワーカーが個別に制限を適用します。WEB_CONCURRENCY=4 の場合、実質的な制限は上記の値のおよそ4倍になります。制限を共有するには REDIS_URL を設定してください。
  • リバースプロキシの背後で運用する場合は、キーの取得元が X-Forwarded-For になるように TRUST_PROXY_HEADERS=true を設定してください。設定しないと、すべてのリクエストがプロキシから送信されたように見え、すべてのクライアントで同じバケットが共有されます。信頼できるプロキシが実際にこのヘッダーを設定している場合を除き、有効にしないでください。有効にすると、クライアントがヘッダーを偽装できる可能性があります。

セキュリティ

保存時の認証情報
データ保存方法
パスワードbcrypt(平文では保存しません)
JWT署名SECRET_KEY(HS256)
DB接続文字列Fernet AES-256で暗号化
EntraクライアントシークレットFernet AES-256で暗号化
APIキーSECRET_KEY を鍵とするHMAC-SHA-256(生のキーは一度だけ返され、保存されません)
リフレッシュトークンSHA-256ハッシュ
HTTPセキュリティヘッダー

すべてのレスポンスに次の内容が含まれます。

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Strict-Transport-Security: max-age=31536000
  • 認証エンドポイントの Cache-Control: no-store
OAuthの保護
  • PKCE S256 — 認可コードの傍受攻撃を防ぎます
  • 一度限りの認可コード — コードは5分後に期限切れとなり、初回使用時に削除されます
  • ローテーションするリフレッシュトークン — 更新のたびに前のトークンが無効化されます(リプレイを防止)
  • ループバック限定のリダイレクトURI — リダイレクト先として受け付けられるのは localhost、127.0.0.1、::1 のみです(RFC 8252に準拠)
SQLの安全性

execute_sql MCPツールは、クエリがデータベースに到達する前にsqlglotのAST解析を行い、SELECT以外のすべての文を拒否します。INSERT、UPDATE、DELETE、DROP、CREATE、ALTER、TRUNCATE、EXECは、形式にかかわらずすべてブロックされます。

テナントの分離

すべてのデータベースクエリは current_user.tenant_id の範囲に限定されます。外部キー制約によりスキーマレベルで分離が強制されるため、あるテナントのデータが別のテナントのレスポンスに現れるコードパスはありません。

シークレットのローテーション

SECRET_KEY のローテーション: 既存のJWTはすべてただちに無効になります。ユーザーは再認証が必要です。別途ハッシュ化されたリフレッシュトークンも無効になります。APIキーも無効になります — APIキーは SECRET_KEY を鍵とするHMACで生成されるため、ローテーション後に既存のキーを失効させ、新たに発行する必要があります。

ENCRYPTION_KEY のローテーション: 古いキーを削除する前に、保存されているすべての接続文字列とEntraクライアントシークレットを新しいキーで再暗号化する必要があります。メンテナンス時間を確保してください。ローテーション中、ゲートウェイは接続を提供できません。

監査ログ

すべての重要なイベントは audit_logs テーブルに記録されます。

イベント発生時
login.success / login.failureログイン試行のたび
oauth.login / oauth.entra_loginOAuth 認可時
oauth.token_issued / oauth.token_refreshedトークンの交換および更新時
query.success / query.failureNL クエリのたび
tool.execute_sql / tool.execute_sql.rejected / tool.execute_sql.errorMCP SQL ツールの使用時
fs.*(例:fs.fs_read_file、fs.fs_write_file.error)ファイルシステムツールの使用時
connection.created / connection.updated / connection.deleted接続の変更時
tenant.createdテナントの登録時
user.deleted / user.role_updatedユーザー管理時
key.created / key.revokedAPI キーのライフサイクル

監査ログをクエリします。

curl "http://localhost:8000/audit-logs/?limit=100" \
  -H "Authorization: Bearer $ADMIN_TOKEN" | jq

その他のドキュメント

ガイド説明
Claude Desktop でのテストエンドツーエンドの手順:ローカルユーザー + Entra SSO
デプロイガイドRailway、Render、および一般的な Docker/VPS へのデプロイ
OAuth 2.1 フローPKCE フロー全体、エンドポイント、トークンのライフサイクル
ファイルシステムツールMCP によるサンドボックス化されたファイルアクセス
監査ログイベント一覧、API、メタデータのリファレンス
API キーキーのライフサイクル、セキュリティモデル、使用方法
ツールのロールオーバーライドツールごとの RBAC 設定

今後の予定 — SaltMine AI

MCP Gateway はオープンソースの基盤です。SaltMine AI というマネージドプラットフォームを現在開発中です。このプロジェクトを基盤とし、同じセキュリティ原則に従って構築されており、インフラストラクチャを管理せずにデータをクエリしたいビジネスチームを対象としています。

予定されている機能は次のとおりです。

  • データベース、データレイク、API を横断する複数のデータソースへのクエリを、1 つの質問で実行
  • ビジネスユーザー向けの、ビジュアライゼーション機能を備えたチャットインターフェイス — SQL の知識は不要
  • フィールドレベルのマスキングとクエリレベルの監査証跡によるデータプライバシー管理
  • AI におけるベンダーロックインの排除
  • ビジネスで使われる言葉を理解

詳細について知りたい方、ご相談したいユースケースがある方、または進捗を追いたい方は、以下をご覧ください。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。