Skip to main content
Glama
mcp-starter-template

mcp-starter-template

by HamzaOuadid

mcp-starter-template

多くの公開MCPサンプルが省略しているセキュリティパターンを組み込んだ、リファレンス用のMCPサーバースキャフォールドです。ユーザー単位の認証パススルー(共有サービスアカウントは不使用)、デフォルトで読み取り専用+明示的な書き込みオプトイン、書き込みツールのドライラン・モード、そしてセッションごとの支出/レート上限(無言のno-opやクラッシュではなく構造化された拒否応答)を備えています。すべてのガードレールは、docstringだけでなく自動テストによって裏付けられています。

これらのガードレールが一切ない本番用MCPフォークから、デッドツールハンドラーを監査して取り除いた後、ポートフォリオ用に構築しました。

関連プロジェクトである mcp-issue-tracker は、このまったく同じセキュリティアーキテクチャ(認証パススルー、許可リストで制御された書き込み、ドライラン、レート制限、監査証跡)を、実際のローカル issue-tracker ドメインに適用したものです。同じパターンが一度ならず二度実証されています。

これが存在する理由

ほとんどの公開MCPサーバーの例では、アシスタントをフル権限のサービスアカウントに直結し、ガードレールを設けていません。そのため、もっともな質問に答えるアシスタントが、質問者に見せるべきでないデータを漏らしたり、誰も承認していない書き込みを黙って実行したりすることになります。このリポジトリは、より安全なデフォルトがどのようなものかを示すものであり、一気に通読できる程度の小ささです。

Related MCP server: Kroki MCP

ガードレールと、それぞれが防ぐもの

ガードレール

場所

防ぐもの

認証パススルー

auth.py, identity.py

ツール呼び出しが共有/包括的な資格情報で実行されることを防ぎます。すべての呼び出しは この特定の呼び出し元の アイデンティティを解決し、以降のすべてのチェックはそのアイデンティティを使用します(管理者/サービスアカウントではありません)。「誰が尋ねたかに関係なく、アシスタントがサービスアカウントに見えるすべてを閲覧できる」ことを防ぎます。

デフォルトで読み取り専用+明示的な書き込み許可リスト

registry.py, server.yaml

誰かが明示的にレビューして有効化する前に、新しく追加された、または誤って構成された書き込みツールが実行されることを防ぎます。ツールが書き込みとして呼び出し可能になるのは、その名前が allowed_write_tools にある場合のみで、それ以外はすべて無効です。「ツールを追加したが、それが削除できることを忘れていた」という事態を防ぎます。

ドライラン・モード

tools/tickets.py, dryrun.py, server.py

オペレーターが動作を検証している間に、書き込みツールの 実際の 下流の副作用が発動することを防ぎます。ドライランでは、実際のAPIクライアントは一切呼び出されません。これは、レスポンスを検査するだけでなく、クライアントメソッド自体をスパイしてテストで検証されています。「ドライランが密かに書き込んでいたため、本番でテストしてしまった」ことを防ぎます。

セッションごとのレート/支出上限

limiter.py

無制限または暴走したクライアントが支出を消費したり、下流のAPIを過剰に叩いたりすることを防ぎます。セッションのウィンドウ予算(呼び出し数またはコスト単位)が尽きると、そのウィンドウ内の後続のすべての呼び出しは、トリップした1回だけではなく、構造化されたエラーと retry_after で拒否されます。「クライアントのバグが無制限のAPI請求書になった」ことを防ぎます。

構造化監査ログ

audit.py

セキュリティインシデントが事後的に再構築できないことを防ぎます。すべての呼び出し(許可されたか拒否されたか、読み取りか書き込みか、ドライランか本番か)は、JSON Lines の1レコードとSQLiteの1行として書き込まれます:タイムスタンプ、セッション、ユーザー、ツール、読み取り/書き込み、ドライランフラグ、許可フラグ、レイテンシ。「実際に何が起こったのか分からない」ことを防ぎます。

アーキテクチャ

                         ┌─────────────────────────────┐
   MCP client  ───────▶  │      transport adapter      │
 (stdio / HTTP)          │  mcp_app.py  /  http_app.py  │
                         └──────────────┬───────────────┘
                                        │ token, session_id, tool_name, args
                                        ▼
                         ┌─────────────────────────────┐
                         │      MCPStarterServer        │   server.py — single
                         │        .call_tool()          │   choke point every
                         └──────────────┬───────────────┘   call passes through
                    1) resolve tool ────┤
                    2) authenticate ────┤──▶ AuthMiddleware ──▶ MockIdentityProvider
                    3) allowlist check ─┤──▶ ToolRegistry
                    4) rate/spend check ┤──▶ SessionLimiter
                    5) execute ─────────┤──▶ tool handler (search_docs / create_ticket)
                    6) audit log ───────┴──▶ AuditLogger ──▶ audit.jsonl + SQLite
  • 認証ミドルウェア (auth.py) は、MockIdentityProvider (identity.py) を介してベアラートークンを User に解決します。これは明確に開発専用とラベル付けされ、2つの異なるテストユーザー(alice/engineering、bob/sales)と管理者がシードされています。欠落している、または認識されないトークンは拒否されます。フォールバックアイデンティティはありません。

  • ツールレジストリ (registry.py) は、すべてのツールの読み取り/書き込み分類が置かれる唯一の場所であり、登録時に server.yaml の tools: セクションと相互チェックされます。コードが宣言している内容と設定が一致しない場合、起動を拒否します。書き込みツールは、その名前が allowed_write_tools に含まれている場合にのみ 呼び出し可能 になります。どちらにせよ list_tools() では 表示 されるため、レビュアーは現在有効なものだけでなく、完全なサーフェスエリアを確認できます。

  • ドライランラッパー: 各書き込みツールのハンドラーは dry_run: bool を受け取り、create_ticket の場合、それが true のときは TicketSystemClient.create(代役の下流API)に一切触れず、代わりに合成の DRYRUN-... ID を返します。dryrun.py は [DRY RUN] 監査行を整形します。

  • レート/支出リミッター (limiter.py) は、session_id ごとの固定ウィンドウカウンターです。calls_per_min と cost_per_session(ツールコストはレジストリから取得)は、window_seconds ごとに一緒にリセットされます。拒否された呼び出し自体は予算を消費しません。

  • 監査ログ (audit.py) は JSON Lines をファイルに書き込み、すべてのレコードを仕様のデータモデルに一致する SQLite の audit_log テーブルにミラーリングするため、テキストとして tail したり、SQL でクエリしたりできます。

2つのトランスポートが同じ MCPStarterServer コアをラップしています:

  • mcp_app.py — 公式の MCP Python SDK (FastMCP) に基づく本格的な MCP stdio サーバーです。stdio は単一のローカルプロセスであり、リクエストごとのヘッダーがないため、token と session_id は明示的なツール引数になります。これはローカル/開発用 MCP サーバーにおける一般的で文書化された簡略化です。実際の MCP クライアント(Claude Desktop、mcp CLI など)が接続するのはこれです。

  • http_app.py — FastAPI の HTTP トランスポートで、トークンは実際の Authorization: Bearer <token> ヘッダーから、セッションは X-Session-Id から取得します。これは実際のマルチテナント展開で使用される形状です。

ツール例

  • search_docs(query) -> list[DocResult] — 読み取り専用。小さな静的なインメモリコーパスを検索し、呼び出し元ユーザーの チームに表示可能なドキュメント(または全社ドキュメント)にフィルタリングします。これにより認証パススルーが証明可能になります。alice(engineering)と bob(sales)からの同じクエリは異なる結果を返します。

  • create_ticket(title, body) -> TicketId — 書き込み、許可リストで制御されます。実際のチケットAPI(TicketSystemClient)の代役を務めます。ドライランはそのクライアントに触れる前にインターセプトします。

データモデル

  • audit_log: timestamp, session_id, user_id, tool_name, read_or_write, dry_run, allowed, latency_ms, error_code, detail — SQLite テーブル+JSON Lines ファイル。すべての呼び出しで書き込まれます。

  • tool_registry の設定(server.yaml の tools: セクション): ツール名ごとに read_only, cost_units, description。

  • session_limits: インメモリのセッションごとのウィンドウ(call_count, cost_used。window_seconds でリセット)。server.yaml の rate_limit: によって駆動されます。

エラー契約

すべての拒否は構造化された MCPError です。{code, message, retry_after?, details?} という形式で、素の例外や黙った no-op はありません。

code

発生時

UNAUTHENTICATED

トークンが欠落しているか認識されない

WRITE_NOT_ALLOWED

書き込みツールが呼び出されたが allowed_write_tools にない

RATE_LIMIT_EXCEEDED

セッションが calls_per_min または cost_per_session を超えた

TOOL_NOT_FOUND

不明なツール名

INVALID_ARGUMENTS

ハンドラーが指定された引数で TypeError を発生させた

HTTP では、これらはそれぞれ 401 / 403 / 429 / 404 / 400 にマッピングされ、レスポンスの detail に同じ {code, message, ...} ボディが含まれます。

インストール

Python 3.10+ が必要です(3.10 で開発・テスト済み。仕様では 3.11+ が求められていました。3.10 を使用した理由については、下記の 逸脱 を参照してください)。

git clone https://github.com/HamzaOuadid/mcp-starter-template.git
cd mcp-starter-template
pip install -e ".[dev]"

使用方法

ツールレジストリの一覧表示(セキュリティレビュー)

mcp-starter tools

このリポジトリからの実際の出力:

  create_ticket    WRITE [DISABLED (not allowlisted)] cost=5   Create a ticket in the downstream ticket system (write, allowlist-gated).
  search_docs      read-only                        cost=1   Search internal docs visible to the calling user's team (read-only).

「これが防ぐもの」の実演を実行

これはマイルストーン4の成果物です。このリポジトリに同梱されている実際の server.yaml(dry_run: true、空の allowed_write_tools)を使用して、2人のテストユーザーシナリオをエンドツーエンドでシミュレートし、権限境界が維持されることを示します。

mcp-starter demo

このリポジトリの server.yaml(rate_limit.calls_per_min: 5)に対する実際の実行出力:

=== 1. Per-user auth passthrough: same tool, same query, different results ===
  alice (engineering): sees docs ['eng-001', 'eng-002', 'all-001']
  bob (sales): sees docs ['sales-001', 'sales-002', 'all-001']

=== 2. Missing/invalid identity is rejected, not defaulted ===
  token=None -> ok=False error={'code': 'UNAUTHENTICATED', 'message': 'Missing or invalid identity token; call rejected.'}

=== 3. Write tool default posture ===
  create_ticket denied: {'code': 'WRITE_NOT_ALLOWED', 'message': "Tool 'create_ticket' is a write tool and is not in allowed_write_tools. Add it to server.yaml's allowlist to enable it."}

=== 4. Rate limit: burst of calls past the cap ===
  call 1/6: allowed
  call 2/6: allowed
  call 3/6: allowed
  call 4/6: allowed
  call 5/6: allowed
  call 6/6: DENIED (RATE_LIMIT_EXCEEDED)

=== Audit log written to <repo>\demo_audit.jsonl ===
  {"allowed": true, "detail": "", "dry_run": false, "error_code": null, "latency_ms": 0.0, "read_or_write": "read", "session_id": "demo-burst-session", ...}
  {"allowed": true, ...}
  {"allowed": false, "error_code": "RATE_LIMIT_EXCEEDED", "detail": "Session 'demo-burst-session' exceeded its rate/spend cap (5 calls or 10 cost units per 60s window).", ...}

alice(engineering)と bob(sales)は、共有の全社ハンドブック(all-001)に加えて、互いに素なドキュメントセットを参照します。まったく同じツールとクエリからでも権限境界は維持されます。None トークンは即座に拒否されます。create_ticket は、許可リストがデフォルトで空であるため拒否されます。1分間に5回のセッションでは、6回目の呼び出しが構造化エラーで拒否されます。

書き込みツールを許可リストに登録して実行すると(設定のデフォルトのため、これもドライランになります)、ドライラン応答の形式を確認できます:

mcp-starter demo --allow-writes
=== 3. Write tool default posture ===
  create_ticket allowed (allowlisted): TicketId(ticket_id='DRYRUN-8ffc09d5', dry_run=True)

実際のチケットは作成されません。ドライランモードでは TicketSystemClient.created は空のままです。これは tests/test_dry_run.py で、クライアントメソッド自体をスパイすることによって直接検証されています。

HTTP トランスポートを実行

mcp-starter serve-http --port 8000
curl http://127.0.0.1:8000/tools

curl -X POST http://127.0.0.1:8000/tools/search_docs/call \
  -H "Authorization: Bearer token-alice" \
  -H "X-Session-Id: demo-1" \
  -H "Content-Type: application/json" \
  -d '{"arguments": {"query": ""}}'

# Write tool, denied by default (empty allowlist):
curl -i -X POST http://127.0.0.1:8000/tools/create_ticket/call \
  -H "Authorization: Bearer token-alice" \
  -H "X-Session-Id: demo-1" \
  -H "Content-Type: application/json" \
  -d '{"arguments": {"title": "Broken build", "body": "CI red on main"}}'
# -> HTTP 403, {"detail":{"code":"WRITE_NOT_ALLOWED", ...}}

開発用トークン:token-alice(engineering)、token-bob(sales)、token-admin(engineering、管理者フラグ設定済み)。

実際の MCP stdio サーバーを実行

mcp-starter serve-stdio

これにより、実際の FastMCP stdio サーバーが起動します。MCPクライアント(例:mcp CLI の mcp dev、または Claude Desktop の設定)を python -m mcp_starter.mcp_app に向けてください。ツール:search_docs(query, token, session_id)、create_ticket(title, body, token, session_id)、list_tools()。

設定

server.yaml を編集:

dry_run: true                 # write tools log-and-simulate instead of executing
allowed_write_tools: []       # empty = no write tool is callable, by design
rate_limit:
  calls_per_min: 5
  cost_per_session: 10
  window_seconds: 60
tools:
  search_docs:
    read_only: true
    cost_units: 1
  create_ticket:
    read_only: false
    cost_units: 5

実際にチケット作成を有効にするには、create_ticket を allowed_write_tools に追加 かつ dry_run: false に設定します。どちらか一方だけでは、書き込み不可のまま、またはシミュレーションのままになります。

テスト

pytest tests/ -v

このリポジトリからの実際の出力(40テスト、すべて成功):

tests/test_audit_log.py::test_audit_jsonl_reconstructs_a_session PASSED
tests/test_audit_log.py::test_audit_sqlite_table_matches_data_model PASSED
tests/test_audit_log.py::test_query_filters_by_session PASSED
tests/test_audit_log.py::test_rate_limit_denial_is_also_audited PASSED
tests/test_auth_passthrough.py::test_two_users_see_different_results_from_same_tool PASSED
tests/test_auth_passthrough.py::test_missing_token_is_rejected_not_defaulted PASSED
tests/test_auth_passthrough.py::test_invalid_token_is_rejected PASSED
tests/test_auth_passthrough.py::test_empty_string_token_is_rejected PASSED
tests/test_auth_passthrough.py::test_unknown_tool_name_does_not_crash PASSED
tests/test_cli.py::test_tools_command_lists_both_example_tools PASSED
tests/test_cli.py::test_demo_command_runs_full_scenario PASSED
tests/test_cli.py::test_demo_command_with_allow_writes_flag PASSED
tests/test_dry_run.py::test_dry_run_never_invokes_the_real_downstream_client PASSED
tests/test_dry_run.py::test_dry_run_logs_the_would_be_action_with_marker PASSED
tests/test_dry_run.py::test_dry_run_off_with_allowlist_actually_calls_downstream PASSED
tests/test_dry_run.py::test_dry_run_plus_write_tool_never_executes_even_when_allowlisted_repeatedly PASSED
tests/test_dry_run.py::test_read_only_tool_is_unaffected_by_dry_run_flag PASSED
tests/test_http_transport.py::test_list_tools_endpoint PASSED
tests/test_http_transport.py::test_auth_header_passthrough_two_users_differ PASSED
tests/test_http_transport.py::test_missing_auth_header_returns_401 PASSED
tests/test_http_transport.py::test_write_not_allowed_returns_403 PASSED
tests/test_http_transport.py::test_rate_limit_returns_429 PASSED
tests/test_http_transport.py::test_unknown_tool_returns_404 PASSED
tests/test_mcp_stdio.py::test_stdio_server_lists_all_three_tools PASSED
tests/test_mcp_stdio.py::test_stdio_server_two_users_differ PASSED
tests/test_mcp_stdio.py::test_stdio_server_write_tool_denied_by_default PASSED
tests/test_mcp_stdio.py::test_stdio_server_missing_token_rejected PASSED
tests/test_rate_limit.py::test_burst_of_n_plus_one_rejects_the_last_call PASSED
tests/test_rate_limit.py::test_calls_keep_being_rejected_until_window_resets PASSED
tests/test_rate_limit.py::test_cost_cap_is_enforced_independent_of_call_count PASSED
tests/test_rate_limit.py::test_sessions_are_isolated_from_each_other PASSED
tests/test_rate_limit.py::test_rate_limit_via_server_returns_structured_error PASSED
tests/test_rate_limit.py::test_denied_write_does_not_consume_rate_budget PASSED
tests/test_registry_allowlist.py::test_registry_describes_every_tool_classification PASSED
tests/test_registry_allowlist.py::test_all_write_tools_default_to_disabled PASSED
tests/test_registry_allowlist.py::test_write_tool_not_in_allowlist_is_denied PASSED
tests/test_registry_allowlist.py::test_write_tool_in_allowlist_becomes_enabled PASSED
tests/test_registry_allowlist.py::test_registration_refuses_undeclared_tool PASSED
tests/test_registry_allowlist.py::test_registration_refuses_classification_mismatch PASSED
tests/test_registry_allowlist.py::test_unknown_tool_call_is_tool_not_found PASSED

======================== 40 passed, 1 warning in 6.91s ========================

関心ごとのカバレッジ:

  • 認証パススルー (test_auth_passthrough.py) — 2つのモックユーザー、同じツール、異なる結果。欠落・不正・空のトークンは拒否され、決してデフォルトにフォールバックしない。不明なツール名はクラッシュせずクリーンに失敗する。

  • レジストリ / 許可リスト (test_registry_allowlist.py) — すべてのツールの分類が検査可能。すべての書き込みツールはデフォルトで無効。登録は、設定にないツール、またはコードと設定の分類が一致しないツールを拒否する。不明なツール名はクリーンな TOOL_NOT_FOUND を返す。

  • ドライラン (test_dry_run.py) — レスポンスが合成されたものに見えるという確認だけではなく、ドライラン中に実際には呼び出されないことを検証するため、TicketSystemClient.create を直接スパイする。caplog を使って [DRY RUN] マーカーが実際にログに記録されることを確認。ドライランがオフになりツールが許可リストに載ると、実際のクライアントが呼び出されることを確認。ドライラン + 許可リスト済み書き込みの組み合わせを複数回繰り返してリグレッションを防止。読み取り専用ツールがフラグの影響を受けないことを確認。

  • レート制限 (test_rate_limit.py) — N+1 件のバーストでは、ちょうど (N+1) 番目が拒否される。フェイククロックを使い、ウィンドウの残り期間中も(トリガーした 1 回だけでなく)呼び出しが拒否され続けることを確認。コスト上限は呼び出し回数とは独立に強制される。セッションは互いに分離される。拒否された書き込みはそれ自体がレート予算を消費しない。

  • 監査ログ (test_audit_log.py) — JSONL と SQLite の両方が、誰が/何を/許可されたか/ドライランかを再構築できる十分な詳細を含む完全なセッションを記録する。SQLite の行はセッションでフィルタリング可能。レート制限による拒否も、成功だけでなくトレイルに記録される。

  • 両トランスポート (test_http_transport.py, test_mcp_stdio.py) — 同じガードレールは、トランスポート非依存のコアを通してだけでなく、FastAPI の TestClient や実物の FastMCP サーバーの非同期 call_tool/list_tools を通して駆動した場合も成立する。

  • CLI (test_cli.py) — tools と demo (--allow-writes の有無にかかわらず) が typer.testing. CliRunner を通じてエンドツーエンドでエラーなく実行される。

仕様からの逸脱とその理由

  • Python 3.10(3.11+ ではなく)。 dev/CI 環境は 3.10 を搭載している。このコードベースで 3.11 専用機能は使っていないため、インタプリタのアップグレードを待つのではなく requires-python の下限を緩めた。CI は実際にテストされているバージョンに合わせて 3.10 に固定している。

  • SQLite(PostgreSQL ではない)、監査ログ用に採用。仕様ではどちらでもよいとされている。この環境には Docker/Postgres がない。監査スキーマ(audit.py の audit_log テーブル)は SQLite 専用構文を含まない素の SQL なので、後で Postgres に移行するのはドライバの交換(sqlite3.connect → psycopg2/asyncpg)と AUTOINCREMENT → SERIAL/IDENTITY だけで済み、再設計は不要。

  • stdio 上の認証パススルーは、トランスポートヘッダーではなく明示的な token 引数を使う。 MCP の stdio トランスポートは単一のローカルプロセスであり、リクエストごとのヘッダーがないため、HTTP の Authorization ヘッダーが HTTP トランスポート(http_app.py)に実際のリクエスト単位の資格情報を与えるのと違って、インターセプトできるものが何もない。トークンを明示的に渡すことで、効果(すべての呼び出しをゲートする、解決済みで非デフォルトのアイデンティティ)は両トランスポートで同一かつテスト可能に保たれる。これは文書化された簡略化であり、stdio に「本物の」マルチユーザー認証があるという主張ではない。本番のマルチユーザー展開では、HTTP トランスポートを実行するか、このコードの上流で実際の資格情報を注入する認証プロキシでラップした stdio トランスポートを実行すべきである。

  • MockIdentityProvider には OAuth/JWT/mTLS なし。 これは静的な token→user ディクショナリであり、仕様自身のリスク注記の通り明らかに開発専用である。実際の検証に差し替えるには、AuthMiddleware.authenticate のトークンルックアップを実際の IdP に対して実装することを意味する。パイプラインの残り(レジストリ、リミッタ、ドライラン、監査)は、User が返ってくることだけに依存するので影響を受けない。

  • 省略: v0.1 git タグ。 マイルストーン 4 では v0.1 リリースにタグを付けることが求められている。このリポジトリはマイルストーンごとの PR ではなくユーザーストーリーごとのコミットであるため、タグ付けは、CI がグリーンでデフォルトブランチに入った後にメンテナーが行うこと(git tag v0.1.0 && git push --tags)として残されており、どこにもプッシュされたことのないリポジトリに自己タグ付けはしない。

  • 省略: 永続的な session_limits/tool_registry テーブルなし。 仕様のデータモデルは、audit_log と並んで session_limits と tool_registry をテーブルとして挙げている。tool_registry の分類は server.yaml にあり(レビュアーが DB テーブルを照会するよりも、おそらくより良い単一の真実源である)、session_limits はメモリ内のみ(limiter.py)で、シングルプロセススターターには正しいが、再起動を生き延びず、プロセスをまたいでスケールしない。複数のサーバープロセスの背後で実行する前に、最初に修正すべき点(例: Redis をバックエンドにしたカウンタ)として指摘しておく。

  • 2つの例示ツール(3以上ではない)。 仕様では「2〜3」とされているが、正確に2つ(読み取り1つ、書き込み1つ)を出荷した。3つ目の読み取り専用ツールは、最初の2つが既にカバーしているガードレールを新たに行使することはないため。

プロジェクトレイアウト

src/mcp_starter/
  identity.py    mock identity provider (dev-only) + User model
  auth.py        auth passthrough middleware
  config.py      server.yaml loading/validation (pydantic)
  registry.py    tool registry: classification + allowlist enforcement
  limiter.py     per-session fixed-window rate/spend limiter
  audit.py       JSONL + SQLite structured audit logging
  dryrun.py      "[DRY RUN]" audit-line formatting
  errors.py      structured MCPError + error codes
  server.py      MCPStarterServer.call_tool — the orchestration core
  mcp_app.py     real MCP stdio server (official MCP Python SDK)
  http_app.py    FastAPI HTTP transport (Authorization header passthrough)
  cli.py         `mcp-starter` CLI: tools / demo / serve-http / serve-stdio
  tools/
    docs.py      search_docs (read-only example tool)
    tickets.py   create_ticket (write example tool) + TicketSystemClient
tests/           37 tests across every guardrail and both transports
server.yaml      tool classification, allowlist, dry-run, rate limits

ライセンス

MIT — LICENSE を参照。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A secure MCP server example that demonstrates how to deploy to Google Cloud Run with authentication and identity token protection. Serves as a tutorial template for building production-ready MCP servers in the cloud.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    A production-ready MCP server scaffold that features built-in authentication, Docker support, and a comprehensive CI/CD release pipeline. It provides a standardized template for deploying servers with multi-transport support and configurable read-only modes.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    A production-ready foundation for building secure, observable MCP servers with built-in authentication, rate limiting, and reference tools like database-query and semantic-search.
    15
    17 npm
    MIT