mcp-oauth-dcr
by rc-kazu
README.md
# mcp-oauth-dcr
ローカル MCP サーバを OAuth(DCR)で保護するハンズオン用リポジトリです。
- **認可サーバ (AS)**: `node-oidc-provider` @ `http://localhost:4000`
- **MCP サーバ (RS)**: `@modelcontextprotocol/sdk` @ `http://localhost:3000/mcp`
## デモのセットアップ
### 1. 依存関係のインストール
```bash
npm ci
```
### 2. サーバを起動(ターミナルを 2 つ使う)
```bash
# ターミナル 1: 認可サーバ
npm run auth
# ターミナル 2: MCP サーバ
npm run mcp
```
起動確認:
| サービス | URL |
|---|---|
| 認可サーバ | http://localhost:4000 |
| MCP サーバ | http://localhost:3000/mcp |
| 保護リソースメタデータ (PRM) | http://localhost:3000/.well-known/oauth-protected-resource/mcp |
### 3. 自動検証(任意)
両方起動した状態で:
```bash
npx tsx src/verify.ts
```
`401 → メタデータ発見 → DCR → トークン検証 → 接続確立` が成功すれば、サーバ側は問題ありません。
### 4. MCP Inspector で手動デモ
```bash
npx @modelcontextprotocol/inspector
```
ターミナルに表示される **トークン付き URL**(例: `http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=...`)をブラウザで開きます。
Inspector の設定:
| 項目 | 値 |
|---|---|
| Transport Type | **Streamable HTTP** |
| URL | `http://localhost:3000/mcp` |
| Connection Type | **Via Proxy**(Direct は CORS で失敗しやすい) |
**Connect** を押すと OAuth フローが始まります。
1. 最初に 401(`Missing Authorization header`)が出る → **正常**
2. DCR でクライアント登録
3. ブラウザで `http://localhost:4000` のログイン画面が開く
4. **Sign-in** → 任意のユーザー名・パスワード(例: `test` / `password`)
5. **Continue**(同意)
6. Inspector に戻り、接続完了
7. `whoami` / `add` ツールを呼び出せる
### 5. よくあるトラブル
**`invalid_client` / `client is invalid`**
認可サーバ(`npm run auth`)を再起動すると、DCR で登録したクライアント情報は **メモリ上から消えます**。Inspector の sessionStorage には古い `client_id` が残っていると、このエラーになります。
対処:
1. Inspector で **Connect をやり直す**(新規 DCR)
2. 直らなければブラウザの DevTools → Application → Session Storage → `localhost:6274` をクリアしてから再接続
**Continue 後に `Failed to fetch`**
トークン交換(`POST /token`)の CORS 問題です。最新の `auth-server.ts` では `clientBasedCORS` を設定済みです。`npm run auth` が最新コードで動いているか確認してください。
**Direct 接続で `Failed to fetch`**
Connection Type は **Via Proxy** を使ってください。
## jwks.json について
**誰が**: 認可サーバ(`src/auth-server.ts`)
**いつ**: **`npm run auth` の初回起動時**(リポジトリ直下に `jwks.json` が無い場合)
処理の流れ:
1. `loadOrCreateJwks()` が `jwks.json` の有無を確認
2. **無ければ** `jose` で RS256 の鍵ペアを生成し、`jwks.json` に書き込む
3. **あれば** 既存ファイルを読み込んで使う(再起動しても `kid` が変わらない)
用途:
- AS が JWT アクセストークンに署名する **秘密鍵**(ファイル内に含まれる)
- AS が `http://localhost:4000/jwks` で公開する **公開鍵**
- MCP サーバ(RS)がその JWKS を取りに行き、Bearer トークンの署名を検証する
`verify.ts` も同じ `jwks.json` を読んで、デモ用にトークンを自前署名します(本番フローでは AS が `/token` で発行します)。
`.gitignore` に入っているので Git にはコミットしません。削除して `npm run auth` し直すと新しい鍵が生成されます(その間に発行済みトークンは検証できなくなります)。
## 構成
```
src/
├── config.ts 共有設定(ポート、issuer、resource URL)
├── auth-server.ts 認可サーバ :4000
├── mcp-server.ts MCP サーバ(リソースサーバ) :3000
└── verify.ts 自動検証スクリプト
jwks.json AS 署名鍵(初回起動時に自動生成・gitignore)
```
## スクリプト
| コマンド | 説明 |
|---|---|
| `npm run auth` | 認可サーバ起動 |
| `npm run mcp` | MCP サーバ起動 |
| `npm run check` | TypeScript 型チェック |
| `npx tsx src/verify.ts` | OAuth フロー自動検証 |
## ライセンス
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues