# 複数DB接続機能 実装計画書
**作成日**: 2026年1月8日
**対象プロジェクト**: jaou-ensatsu-kokuryu-filemaker-mcp
**ステータス**: ✅ 実装完了
---
## 1. 概要
### 1.1 目的
jaou-ensatsu-kokuryu-filemaker-mcpに複数FileMakerデータベースへの同時接続機能を追加する。
MCP-Claude-FileMakerの複数DB接続方式を参考に実装する。
### 1.2 決定事項
| 項目 | 決定内容 |
|------|----------|
| 後方互換性 | 不要(新方式に完全移行) |
| 環境変数命名規則 | `FM_SERVER_*`, `FM_DATABASE_*`, `FM_ACCOUNT_*`, `FM_PASSWORD_*` |
| ツール引数名 | `alias`(接続エイリアス) |
| 接続一覧ツール | `fm_list_databases` を追加 |
| キャッシュ機能 | 今回は実装しない |
---
## 2. アーキテクチャ設計
### 2.1 起動時の初期化フロー
```
┌─────────────────────────────────────────────────────────────────────┐
│ 起動時の初期化フロー │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 環境変数スキャン │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ FM_SERVER_PROD=https://prod.example.com │ │
│ │ FM_DATABASE_PROD=Production │ │
│ │ FM_ACCOUNT_PROD=api_user │ │
│ │ FM_PASSWORD_PROD=secret123 │ │
│ │ │ │
│ │ FM_SERVER_DEV=https://dev.example.com │ │
│ │ FM_DATABASE_DEV=Development │ │
│ │ FM_ACCOUNT_DEV=dev_user │ │
│ │ FM_PASSWORD_DEV=devsecret │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ SessionManagerRegistry │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ "PROD" │ │ "DEV" │ │ │
│ │ │ SessionManager │ │ SessionManager │ │ │
│ │ │ ├─ config │ │ ├─ config │ │ │
│ │ │ ├─ httpClient │ │ ├─ httpClient │ │ │
│ │ │ └─ state │ │ └─ state │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
### 2.2 ツール呼び出しフロー
```
┌─────────────────────────────────────────────────────────────────────┐
│ ツール呼び出しフロー │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ fm_get_records({ alias: "PROD", layout: "顧客" }) │
│ ↓ │
│ SessionManagerRegistry.getManager("PROD") │
│ ↓ │
│ sessionManager.withSession(async (token, client) => { ... }) │
│ ↓ │
│ FileMaker Data API リクエスト │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
---
## 3. 変更対象ファイル一覧
| ファイル | 変更種別 | 概要 | 実装状況 |
|----------|----------|------|----------|
| `src/config.ts` | **大幅変更** | 複数DB設定のパース機能を追加 | ✅ 完了 |
| `src/types/filemaker.ts` | **追加** | `DatabaseConfig`, `DatabaseRegistry`型を追加 | ✅ 完了 |
| `src/api/session.ts` | **大幅変更** | `SessionManagerRegistry`クラスを追加 | ✅ 完了 |
| `src/api/index.ts` | **変更** | エクスポートの更新 | ✅ 完了 |
| `src/tools/auth.ts` | **変更** | `alias`引数対応、`fm_list_databases`追加 | ✅ 完了 |
| `src/tools/metadata.ts` | **変更** | 全ツールに`alias`引数追加 | ✅ 完了 |
| `src/tools/records.ts` | **変更** | 全ツールに`alias`引数追加 | ✅ 完了 |
| `src/tools/analysis.ts` | **変更** | 全ツールに`alias`引数追加 | ✅ 完了 |
| `src/server.ts` | **変更** | 初期化処理の更新 | ✅ 完了 |
| `tests/` | **追加** | 複数DB接続のテストケース | ✅ 完了 |
---
## 4. 詳細設計
### 4.1 Phase 1: 型定義と設定管理(基盤)
#### 4.1.1 型定義の追加 (`src/types/filemaker.ts`)
**実装済みコード:**
```typescript
/**
* 単一データベース接続設定
*/
export interface DatabaseConfig {
/** 接続エイリアス(例: "PROD", "DEV")- 大文字に正規化される */
alias: string;
/** FileMakerサーバーURL(https://を含む完全URL) */
server: string;
/** データベース名 */
database: string;
/** ユーザー名(環境変数FM_ACCOUNT_*から読み込み) */
username: string;
/** パスワード */
password: string;
/** Data APIバージョン(デフォルト: vLatest) */
apiVersion: string;
/** SSL証明書検証(デフォルト: true) */
sslVerify: boolean;
/** セッションタイムアウト秒数(デフォルト: 840秒 = 14分) */
sessionTimeout: number;
}
/**
* 複数データベース設定レジストリ
*/
export interface DatabaseRegistry {
/** エイリアスをキーとした設定マップ */
databases: Map<string, DatabaseConfig>;
/** 利用可能なエイリアス一覧(ソート済み) */
aliases: string[];
}
/**
* データベース接続情報(パスワードを除く公開情報)
*/
export interface DatabaseInfo {
/** 接続エイリアス */
alias: string;
/** FileMakerサーバーURL */
server: string;
/** データベース名 */
database: string;
}
```
#### 4.1.2 設定ローダーの変更 (`src/config.ts`)
**実装済み関数:**
- `loadMultiDatabaseConfig(): DatabaseRegistry` - 環境変数から複数DB設定を読み込み
- `validateDatabaseConfig(config, alias): ConfigValidationResult` - 単一設定のバリデーション
- `formatConfigForLog(config)` - パスワードマスキング
- `formatRegistryForLog(registry)` - レジストリのログ表示
---
### 4.2 Phase 2: セッション管理の拡張
#### 4.2.1 SessionManagerRegistry (`src/api/session.ts`)
**実装済みクラスとメソッド:**
```typescript
export class SessionManagerRegistry {
// 主要メソッド
getManager(alias: string): SessionManager;
getAliases(): string[];
getDatabaseInfo(alias: string): DatabaseInfo;
getAllDatabaseInfo(): DatabaseInfo[];
getSessionStatus(): SessionStatusInfo[];
// セッション管理
logoutAll(aliases?: string[]): Promise<LogoutAllResult>;
logoutAllSync(): Promise<LogoutAllResult>;
}
// グローバル関数
export function initializeRegistry(databaseRegistry: DatabaseRegistry): SessionManagerRegistry;
export function getRegistry(): SessionManagerRegistry;
export function resetRegistry(): void;
export function isRegistryInitialized(): boolean;
```
**計画との差分:**
1. **`initializeRegistry` の戻り値**: 計画では `void` だったが、実装では `SessionManagerRegistry` を返却(初期化後のレジストリ参照が容易)
2. **再初期化対応**: 計画では「既に初期化済みの場合はエラー」としていたが、実装では警告ログのみで再初期化を許可(テスト容易性のため)
3. **`logoutAllSync` メソッド追加**: シグナルハンドラ用に同期的なログアウト処理を追加
---
### 4.3 Phase 3: ツールの更新
#### 4.3.1 認証ツール (`src/tools/auth.ts`)
| ツール名 | 変更内容 | 実装状況 |
|----------|----------|----------|
| `fm_login` | `alias`引数追加(必須) | ✅ |
| `fm_logout` | `alias`引数追加(必須) | ✅ |
| `fm_validate_session` | `alias`引数追加(必須) | ✅ |
| `fm_list_databases` | **新規追加** | ✅ |
**fm_list_databases の出力例:**
```json
{
"success": true,
"databases": [
{ "alias": "DEV", "server": "https://dev.example.com", "database": "Development" },
{ "alias": "PROD", "server": "https://prod.example.com", "database": "Production" }
],
"count": 2
}
```
#### 4.3.2 メタデータツール (`src/tools/metadata.ts`)
| ツール名 | 変更内容 | 実装状況 |
|----------|----------|----------|
| `fm_get_layouts` | `alias`引数追加(必須) | ✅ |
| `fm_get_layout_metadata` | `alias`引数追加(必須) | ✅ |
| `fm_get_scripts` | `alias`引数追加(必須) | ✅ |
| `fm_list_value_lists` | `alias`引数追加(必須) | ✅ |
#### 4.3.3 レコード操作ツール (`src/tools/records.ts`)
| ツール名 | 変更内容 | 実装状況 |
|----------|----------|----------|
| `fm_get_records` | `alias`引数追加(必須) | ✅ |
| `fm_get_record_by_id` | `alias`引数追加(必須) | ✅ |
| `fm_find_records` | `alias`引数追加(必須) | ✅ |
| `fm_get_record_count` | `alias`引数追加(必須) | ✅ |
#### 4.3.4 分析ツール (`src/tools/analysis.ts`)
| ツール名 | 変更内容 | 実装状況 |
|----------|----------|----------|
| `fm_export_database_metadata` | `alias`引数追加(必須) | ✅ |
| `fm_infer_relationships` | `alias`引数追加(必須) | ✅ |
| `fm_analyze_portal_data` | `alias`引数追加(必須) | ✅ |
| `fm_global_search_data` | `alias`引数追加(必須) | ✅ |
| `fm_global_search_fields` | `alias`引数追加(必須) | ✅ |
**計画との差分:**
1. **Analyzer関数のシグネチャ変更**: 計画では暗黙的にレジストリからSessionManagerを取得する想定だったが、実装ではanalyzer関数に`SessionManager`を明示的に渡すように変更
```typescript
// 計画
export async function exportDatabaseMetadata(input: Input): Promise<Output>;
// 実装
export async function exportDatabaseMetadata(
input: Input,
sessionManager: SessionManager
): Promise<Output>;
```
**理由**: テスト容易性とDependency Injection原則の遵守
---
### 4.4 Phase 4: 初期化とサーバー起動
#### 4.4.1 サーバー初期化 (`src/server.ts`)
**実装済みの初期化フロー:**
```typescript
async function main() {
// 1. 複数DB設定を読み込み(バリデーション含む)
const databaseRegistry = loadMultiDatabaseConfig();
// 2. レジストリ初期化
initializeRegistry(databaseRegistry);
// 3. MCP サーバー起動
const server = createServer();
const transport = new StdioServerTransport();
await server.connect(transport);
}
```
**計画との差分:**
1. **バリデーションの統合**: 計画では `loadMultiDatabaseConfig` と `validateDatabaseConfig` を別々に呼び出していたが、実装では `loadMultiDatabaseConfig` 内でバリデーションを実行し、エラー時は例外をスロー
2. **シグナルハンドラ追加**: `SIGINT`/`SIGTERM` 時に `logoutAllSync()` を呼び出してセッションをクリーンアップ
---
## 5. テスト計画
### 5.1 ユニットテスト
| テストカテゴリ | テストケース | 実装状況 |
|----------------|--------------|----------|
| **設定読み込み** | 複数DB環境変数のパース | ✅ |
| | 必須項目欠落時のエラー | ✅ |
| | グローバル設定のフォールバック | ✅ |
| | サーバーURLバリデーション(https://必須) | ✅ |
| | エイリアス衝突検出(大文字小文字の衝突) | ✅ |
| **SessionManagerRegistry** | マネージャー取得(正常系) | ✅ |
| | 存在しないエイリアスのエラー | ✅ |
| | エイリアス一覧取得 | ✅ |
| | 全セッションログアウト | ✅ |
| **ツール引数** | alias引数のバリデーション | ✅ |
| | 不正なalias指定時のエラー | ✅ |
### 5.2 テスト結果
```
Test Suites: 6 passed, 6 total
Tests: 193 passed, 193 total
```
---
## 6. 実装フェーズとスケジュール
| フェーズ | 内容 | 依存関係 | 見積もり | 実績 |
|----------|------|----------|----------|------|
| **Phase 1** | 型定義と設定管理 | なし | 1-2時間 | ✅ 完了 |
| **Phase 2** | SessionManagerRegistry | Phase 1 | 2-3時間 | ✅ 完了 |
| **Phase 3** | ツール更新 | Phase 2 | 2-3時間 | ✅ 完了 |
| **Phase 4** | 初期化とテスト | Phase 3 | 1-2時間 | ✅ 完了 |
| **Phase 5** | テストスクリプト更新 | Phase 4 | 1時間 | ✅ 完了 |
**合計見積もり**: 6-10時間
**実績**: 実装完了
---
## 7. 環境変数設定例
### 7.1 最小構成(1DB)
```bash
FM_SERVER_MAIN=https://filemaker.example.com
FM_DATABASE_MAIN=MyDatabase
FM_ACCOUNT_MAIN=api_user
FM_PASSWORD_MAIN=secure_password
```
### 7.2 複数環境構成
```bash
# 本番環境
FM_SERVER_PROD=https://prod.example.com
FM_DATABASE_PROD=Production
FM_ACCOUNT_PROD=api_user
FM_PASSWORD_PROD=secure_password
# 開発環境
FM_SERVER_DEV=https://dev.example.com
FM_DATABASE_DEV=Development
FM_ACCOUNT_DEV=dev_user
FM_PASSWORD_DEV=dev_password
# ステージング環境
FM_SERVER_STAGING=https://staging.example.com
FM_DATABASE_STAGING=Staging
FM_ACCOUNT_STAGING=staging_user
FM_PASSWORD_STAGING=staging_password
# グローバル設定(オプション)
FM_API_VERSION=vLatest
FM_SSL_VERIFY=false
FM_SESSION_TIMEOUT=840
```
---
## 8. 破壊的変更
### 8.1 環境変数の変更
| 旧環境変数 | 新環境変数 | 備考 |
|------------|------------|------|
| `FM_SERVER` | `FM_SERVER_{ALIAS}` | エイリアス付きに変更 |
| `FM_DATABASE` | `FM_DATABASE_{ALIAS}` | エイリアス付きに変更 |
| `FM_USERNAME` | `FM_ACCOUNT_{ALIAS}` | 命名も変更(MCP-Claude-FileMaker互換) |
| `FM_PASSWORD` | `FM_PASSWORD_{ALIAS}` | エイリアス付きに変更 |
### 8.2 ツール引数の変更
全ツールに`alias`引数が追加される。既存の呼び出しは動作しなくなる。
**例外**: `fm_list_databases` は `alias` 引数を必要としない(全DB一覧を返却)
---
## 9. マイグレーションガイド
### 9.1 環境変数の移行
**Before:**
```bash
FM_SERVER=https://filemaker.example.com
FM_DATABASE=MyDatabase
FM_USERNAME=api_user
FM_PASSWORD=secure_password
```
**After:**
```bash
FM_SERVER_MAIN=https://filemaker.example.com
FM_DATABASE_MAIN=MyDatabase
FM_ACCOUNT_MAIN=api_user
FM_PASSWORD_MAIN=secure_password
```
### 9.2 ツール呼び出しの移行
**Before:**
```json
{
"name": "fm_get_records",
"arguments": {
"layout": "顧客"
}
}
```
**After:**
```json
{
"name": "fm_get_records",
"arguments": {
"alias": "MAIN",
"layout": "顧客"
}
}
```
---
## 10. 設計決定事項
### 10.1 解決済みの事項
1. **alias引数は必須**
- 環境変数が1つだけの場合でも`alias`指定は必須とする
- シンプルで一貫性のあるAPI設計を優先
2. **グローバル設定の優先順位**
- エイリアス別設定 > グローバル設定(例: `FM_API_VERSION_PROD` > `FM_API_VERSION`)
3. **エイリアスの正規化ルール**
- 設定読み込み時: 環境変数サフィックスを大文字に正規化して保存
- 例: `FM_SERVER_prod` → エイリアス `PROD` として登録
- ツール入力時: `getManager(alias)` 内で大文字に正規化してから検索
- 例: `alias: "prod"` → `"PROD"` として検索
- これにより `prod`, `PROD`, `Prod` いずれの入力でも同じ接続を参照可能
4. **環境変数と型フィールドのマッピング**
- `FM_ACCOUNT_{ALIAS}` → `DatabaseConfig.username` フィールド
- MCP-Claude-FileMaker互換の環境変数名を採用しつつ、内部型は意味的に明確な`username`を使用
### 10.2 実装時の追加決定事項
1. **Analyzer関数への依存注入**
- Analyzer関数は `SessionManager` を引数で受け取る
- レジストリからの暗黙的な取得ではなく、明示的な依存注入を採用
- テスト容易性と関数の再利用性を向上
2. **レジストリの再初期化許可**
- テスト時の利便性のため、`initializeRegistry` は既存レジストリを上書き可能
- 警告ログを出力して再初期化を通知
3. **出力への `alias` フィールド追加**
- すべてのツール出力に `alias` フィールドを含める
- どのデータベースからの結果かを明確化
---
## 更新履歴
| 日付 | 内容 |
|------|------|
| 2026-01-08 | 初版作成 |
| 2026-01-08 | 環境変数例にhttps://を追加、エイリアス正規化ルールを明記、FM_ACCOUNT→usernameマッピングを明記 |
| 2026-01-08 | getDatabaseInfoにエイリアス正規化を追加、衝突検出ロジックを明記、テスト項目名を修正 |
| 2026-01-09 | 実装完了に伴い計画との差分を反映、ステータスを「実装完了」に更新 |