# 機能設計書 38-MCPリソース・プロンプト

## 概要

本ドキュメントは、OpenCodeにおけるMCPサーバーが提供するリソースとプロンプトの取得・実行機能について記載する。この機能により、AIアシスタントはMCPサーバーが提供するコンテキスト情報やプロンプトテンプレートを活用できる。

### 本機能の処理概要

**業務上の目的・背景**：MCPサーバーはツールだけでなく、リソース（ファイル、データベースコンテンツ等の静的/動的コンテンツ）とプロンプト（事前定義されたプロンプトテンプレート）も提供できる。これらを活用することで、AIアシスタントは外部システムのコンテキスト情報を取得し、一貫性のあるプロンプトを使用できる。

**機能の利用シーン**：
- MCPサーバーが提供するデータベースの内容をリソースとして読み取る
- 事前定義されたプロンプトテンプレートを取得してユーザーに提供する
- スキルコマンドとしてMCPプロンプトを実行する

**主要な処理内容**：
1. リソース一覧取得（resources）: 接続済みMCPサーバーからリソース一覧を取得
2. リソース読み取り（readResource）: 特定リソースのコンテンツを取得
3. プロンプト一覧取得（prompts）: 接続済みMCPサーバーからプロンプト一覧を取得
4. プロンプト取得（getPrompt）: 特定プロンプトのテンプレートと引数を取得
5. クライアント名のサニタイズと名前空間管理

**関連システム・外部連携**：
- Model Context Protocol SDK（リソース・プロンプトAPI）
- MCPサーバー（リソース・プロンプト提供元）

**権限による制御**：各リソース・プロンプトのアクセス権限はMCPサーバー側で制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 25 | ホーム | 補助機能 | コマンドダイアログによるスキル登録・実行（MCPプロンプトを含む） |

## 機能種別

外部連携 / データ取得

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| clientName | string | Yes | MCPクライアント名 | 存在するクライアント |
| resourceUri | string | Yes (readResource) | リソースURI | - |
| promptName | string | Yes (getPrompt) | プロンプト名 | - |
| args | Record<string, string> | No (getPrompt) | プロンプト引数 | - |

### 入力データソース

- MCPサーバー: リソース・プロンプト定義

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Resource | object | リソース情報 |
| Resource.name | string | リソース名 |
| Resource.uri | string | リソースURI |
| Resource.description | string | リソース説明 |
| Resource.mimeType | string | MIMEタイプ |
| Resource.client | string | 提供元クライアント名 |
| Prompt | object | プロンプト情報 |
| Prompt.name | string | プロンプト名 |
| Prompt.description | string | プロンプト説明 |
| Prompt.arguments | array | 引数定義 |
| Prompt.client | string | 提供元クライアント名 |

### 出力先

- 関数戻り値: リソース/プロンプトデータ

## 処理フロー

### 処理シーケンス

```
1. リソース一覧取得（resources）
   └─ 接続済みクライアントの取得
   └─ 各クライアントでlistResources呼び出し
   └─ クライアント名をサニタイズ
   └─ "client:resource"形式のキーで結果を集約

2. リソース読み取り（readResource）
   └─ クライアント名からクライアント取得
   └─ client.readResource呼び出し
   └─ 結果返却

3. プロンプト一覧取得（prompts）
   └─ 接続済みクライアントの取得
   └─ 各クライアントでlistPrompts呼び出し
   └─ クライアント名をサニタイズ
   └─ "client:prompt"形式のキーで結果を集約

4. プロンプト取得（getPrompt）
   └─ クライアント名からクライアント取得
   └─ client.getPrompt呼び出し
   └─ 結果返却
```

### フローチャート

```mermaid
flowchart TD
    A[リソース/プロンプト取得] --> B[接続済みクライアント取得]
    B --> C[各クライアントで一覧取得]
    C --> D{取得成功?}
    D -->|No| E[エラーログ出力]
    D -->|Yes| F[クライアント名サニタイズ]
    E --> G[次のクライアントへ]
    F --> H[キー生成 client:name]
    H --> I[結果マップに追加]
    I --> G
    G --> J{全クライアント処理完了?}
    J -->|No| C
    J -->|Yes| K[結果返却]

    L[リソース読み取り] --> M[クライアント取得]
    M --> N{クライアント存在?}
    N -->|No| O[undefined返却]
    N -->|Yes| P[readResource呼び出し]
    P --> Q[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-38-01 | 名前サニタイズ | クライアント名・リソース名・プロンプト名の特殊文字を_に置換 | 一覧取得時 |
| BR-38-02 | 名前空間形式 | "sanitizedClient:sanitizedName"形式でキーを生成 | 一覧取得時 |
| BR-38-03 | 接続済みのみ | status === "connected"のクライアントのみ対象 | 一覧取得時 |
| BR-38-04 | エラー時スキップ | 一覧取得失敗時はそのクライアントをスキップ | 一覧取得時 |
| BR-38-05 | クライアント存在確認 | 存在しないクライアント指定時はundefinedを返却 | 個別取得時 |

### 計算ロジック

**キー生成**:
```javascript
const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
const sanitizedResourceName = resource.name.replace(/[^a-zA-Z0-9_-]/g, "_")
const key = sanitizedClientName + ":" + sanitizedResourceName
```

## データベース操作仕様

### 操作別データベース影響一覧

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ListError | 取得エラー | listResources/listPromptsが失敗 | エラーログ出力、スキップ |
| ReadError | 読み取りエラー | readResource/getPromptが失敗 | エラーログ出力、undefined返却 |
| ClientNotFound | クライアントエラー | 指定クライアントが存在しない | undefined返却 |

### リトライ仕様

- 取得失敗時の自動リトライは行わない

## トランザクション仕様

該当なし

## パフォーマンス要件

- 一覧取得は全クライアントに並列でリクエスト

## セキュリティ考慮事項

- リソース・プロンプトのアクセス権限はMCPサーバー側で制御
- クライアント名のサニタイズによりパス操作攻撃を防止

## 備考

MCPリソースの種類:
- 静的リソース: ファイル、設定等
- 動的リソース: データベースクエリ結果等

MCPプロンプトの用途:
- スキルコマンドとしての利用
- AIアシスタントへのコンテキスト提供

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

リソース型の定義を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/opencode/src/mcp/index.ts` | Resource型定義（31-40行目） |
| 1-2 | index.ts | `packages/opencode/src/mcp/index.ts` | PromptInfo、ResourceInfo型（155-158行目） |

**読解のコツ**: Resource型はname, uri, description, mimeType, clientを持つ。clientフィールドはOpenCode側で追加。

#### Step 2: ヘルパー関数を理解する

クライアント別のプロンプト・リソース取得。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/opencode/src/mcp/index.ts` | fetchPromptsForClient関数（213-233行目） |
| 2-2 | index.ts | `packages/opencode/src/mcp/index.ts` | fetchResourcesForClient関数（235-255行目） |

**主要処理フロー**:
- **214-217行目**: listPrompts呼び出しとエラーハンドリング
- **225-228行目**: クライアント名・プロンプト名のサニタイズ
- **230行目**: clientフィールドの追加

#### Step 3: 公開API - プロンプト

プロンプト一覧取得とプロンプト実行。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/opencode/src/mcp/index.ts` | prompts関数（608-627行目） |
| 3-2 | index.ts | `packages/opencode/src/mcp/index.ts` | getPrompt関数（650-676行目） |

**主要処理フロー**:
- **609-623行目**: 接続済みクライアントからプロンプト取得（並列）
- **616-617行目**: status !== "connected"のスキップ
- **650-676行目**: getPrompt - クライアント取得、client.getPrompt呼び出し

#### Step 4: 公開API - リソース

リソース一覧取得とリソース読み取り。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages/opencode/src/mcp/index.ts` | resources関数（629-648行目） |
| 4-2 | index.ts | `packages/opencode/src/mcp/index.ts` | readResource関数（678-703行目） |

**主要処理フロー**:
- **629-648行目**: 接続済みクライアントからリソース取得（並列）
- **678-703行目**: readResource - クライアント取得、client.readResource呼び出し

### プログラム呼び出し階層図

```
MCP.prompts()
    │
    ├─ state() - 状態取得
    │
    ├─ clients() - クライアント一覧取得
    │
    └─ Promise.all([...clients.map(...)])
           │
           └─ fetchPromptsForClient(clientName, client)
                  │
                  ├─ client.listPrompts()
                  │
                  ├─ sanitize(clientName)
                  │
                  └─ key = "client:prompt"

MCP.getPrompt(clientName, name, args)
    │
    ├─ clients() - クライアント一覧取得
    │
    └─ client.getPrompt({name, arguments: args})

MCP.resources()
    │
    └─ [同様の構造]

MCP.readResource(clientName, resourceUri)
    │
    └─ client.readResource({uri: resourceUri})
```

### データフロー図

```
[入力]                    [処理]                         [出力]

MCPクライアント ──────▶ listPrompts/listResources ──▶ 一覧データ
                            │
                            ▼
                    名前サニタイズ
                            │
                            ▼
                    キー生成 (client:name)
                            │
                            ▼
                    clientフィールド追加
                            │
                            ▼
                    結果マップ ───────────────────▶ {key: data}

clientName ────────▶ クライアント取得
resourceUri ───────▶ readResource呼び出し ─────────▶ コンテンツ

clientName ────────▶ クライアント取得
promptName ────────▶ getPrompt呼び出し ────────────▶ プロンプトテンプレート
args ──────────────▶ 引数展開
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/opencode/src/mcp/index.ts` | ソース | resources、readResource、prompts、getPrompt関数 |
| @modelcontextprotocol/sdk | 外部 | ライブラリ | MCPプロトコル実装、listPrompts、listResources |
