mcp-role-server

mcp-role-server

A role-based access control MCP server template with authentication, role-based tool exposure, and rate limiting, enabling secure tool management for AI assistants.

Category
Visit Server

README

mcp-role-server

役割ベースのアクセス制御つき MCP サーバーの雛形。 Next.js の App Router で動きます。

認証・ロール別のツール公開・レート制限という、本番で必要になる3点を最初から備えた状態から始められます。 あなたがやることは、自分のツールを書いて一覧に1行足すだけです。


これは何か

MCP(Model Context Protocol)は、AIに道具を渡すための共通の差し込み口です。 公式のサンプルは「とりあえず動く」ところまでを示していますが、実際に業務で使おうとすると、すぐに次の3つが必要になります。

必要になるもの この雛形での扱い
誰が呼んでいるのか Bearer トークンによる認証(タイミング攻撃対策込み)
その人にどこまで見せるか 役割(role)ごとにツールを出し分け
叩かれすぎたらどうするか レート制限(超過時は 429 と Retry-After)

この3つを毎回ゼロから書かなくていい、というのがこの雛形の目的です。


特徴

1. 権限がないツールは「拒否」ではなく「存在しない」

役割に合わないツールは、そもそもサーバーに登録されません。

viewer で tools/list  →  ping, echo
admin  で tools/list  →  ping, echo, server_config

viewer が server_config を呼ぶ  →  "Tool server_config not found"

「権限がありません」と返すのではなく、相手から見て最初から存在しない状態になります。

2. 不明な役割は、必ず弱い方へ倒れる

X-User-Role が未設定・空・知らない値のときは、**必ず viewer(最小権限)**として扱われます。 「知らない値だから admin にしておく」は起こりません。

3. 検査は「安い順」に並んでいる

レート制限(カウンターを見るだけ)
    ↓
認証(暗号計算が走る)
    ↓
サーバー組み立て(一番重い)

大量に叩かれたときに、弾く処理そのもので潰れないようにするための順番です。


動かし方

git clone <このリポジトリ>
cd mcp-role-server
pnpm install

# 環境変数を用意する
cp .env.example .env.local

# シークレットを生成して .env.local の MCP_SHARED_SECRET に入れる
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"

pnpm dev

http://localhost:3000/api/mcp で待ち受けます。

動作確認(curl)

SECRET="(.env.local に入れた値)"

curl -s -X POST http://localhost:3000/api/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "Authorization: Bearer $SECRET" \
  -H "X-User-Role: viewer" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
送るもの 返るもの
鍵なし 401 Authorization header missing or malformed
誤った鍵 401 Invalid bearer token
正しい鍵 200 + ツール一覧
上限を超えた回数 429 + Retry-After

自分のツールを足す

3ステップで終わります。server.ts には手を入れません。

1. src/features/mcp/tools/ にファイルを1つ作る

import { z } from "zod";
import type { ToolEntry } from "./types";

export const myTool: ToolEntry = {
  name: "my_tool",
  roles: ["admin"],          // ← ここに書いた役割にだけ公開される
  register(server) {
    server.registerTool(
      "my_tool",
      {
        title: "My tool",
        description: "AI がこれを読んで、使うかどうかを判断します。",
        inputSchema: { query: z.string().min(1) },
      },
      async ({ query }) => ({
        content: [{ type: "text", text: JSON.stringify({ result: query }) }],
      }),
    );
  },
};

2. src/features/mcp/tools/index.ts の TOOLS に1行足す

export const TOOLS: ToolEntry[] = [pingTool, echoTool, serverConfigTool, myTool];

3. 以上です。 roles に書いた役割のサーバーにだけ、自動的に載ります。

description は、AI がツールを選ぶ根拠になります。 ここが曖昧だと、AI は間違ったツールを呼びます。実装よりも文章の精度が効く部分です。


設計の理由

なぜ「拒否」ではなく「登録しない」のか

「権限がありません」と返す方式だと、そのツールが存在することは相手に伝わります。 存在が分かれば「どうすれば呼べるのか」を探られます。

登録しない方式なら、相手から見て最初から存在しません。呼びようがありません。

そしてもう1つ。拒否方式は、各ツールの中に権限チェックを書く必要があります。 ツールが増えるほど、書き忘れが起きます。登録の段階で振り分けてしまえば、書き忘れようがありません。

なぜ役割をヘッダーで受け取るのか

このサーバーは、信頼できる呼び出し元(Bot やゲートウェイ)の後ろに置く前提で設計しています。 本人確認はその手前で済んでおり、このサーバーは「誰として扱うか」だけを受け取ります。

役割の判定を各サービスで二重に実装すると、判定がずれたときに気づけません。 判定は一箇所に集め、後段はその結果を受け取る——という分担です。

この前提が崩れる置き方をすると、そのまま脆弱性になります。(次の警告を参照)

なぜレート制限をメモリで持つのか

この雛形の目的は「認証とロール制御の型を示すこと」であって、レート制限の完全な実装ではありません。 外部ストアを必須にすると、動かすまでの手間が増えて雛形として使いにくくなります。

そのかわり、限界を隠さずに書いています(下記)。差し替える場所は1ファイルに閉じてあります。


⚠️ セキュリティ上の重要な注意

このサーバーを、そのままインターネットに直接公開しないでください。

役割は X-User-Role ヘッダーで受け取っています。つまり——

Authorization: Bearer <正しいトークン>
X-User-Role: admin          ← 送れば admin になれる

トークンを持っている相手なら、ヘッダーを書き換えるだけで admin になれます。

これは想定どおりの動作です。この設計は、次の条件が満たされている場合にのみ安全です。

条件 意味
呼び出し元が信頼できる Bot・BFF・ゲートウェイなど、自分が管理しているものだけが呼ぶ
呼び出し元が本人確認を済ませている 実際のユーザー認証(Clerk / Auth0 / 自前)はその手前で完了している
このエンドポイントが公衆に晒されていない ネットワークやトークン管理で到達経路が絞られている

この条件を満たせない場所に置く場合は、verifyMcpAuth を書き換えて、 役割をヘッダーではなくトークン自体(署名付きトークンなど)から導出してください。


既知の限界

限界 内容 本番でどうするか
レート制限がプロセスメモリ保持 サーバーレスや複数インスタンスでは実行のたびに別のメモリになりうるため、全体としては上限を超えて通ることがある Redis 等の外部ストアに差し替える(rate-limit.ts の1ファイルのみ)
固定ウィンドウ方式 境界で最大2倍通る(59秒目に60回+61秒目に60回) スライディングウィンドウ方式にする
レート制限が全体で1つ 呼び出し元ごとに分かれていない checkRateLimit(key) の key を呼び出し元の識別子にする
役割が2つ固定 admin / viewer のみ McpRole 型と normalizeRole を拡張する

構成

src/
├── app/api/mcp/route.ts        HTTPの入口(レート制限 → 認証 → 応答)
└── features/mcp/
    ├── auth.ts                 Bearer検証・役割の正規化
    ├── rate-limit.ts           固定ウィンドウのレート制限
    ├── server.ts               役割に合うツールだけを載せてサーバーを組み立てる
    └── tools/
        ├── types.ts            ツール定義の型
        ├── index.ts            ★ ツールの一覧(ここに足す)
        ├── ping.ts             引数なしの例
        ├── echo.ts             引数ありの例(zod による入力検証)
        └── server-config.ts    admin 限定の例

環境変数

変数 必須 説明
MCP_SHARED_SECRET ✅ 接続に使う共有シークレット。未設定なら 500 を返し、素通しにはしない
MCP_RATE_LIMIT — 1分あたりの上限リクエスト数(既定 60)

ライセンス

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured