Slack MCP Agent — Custom Tool 設計¶
v1.6 / 2026-05-06
ツールカタログ、共通スキーマ、ベンダー抽象化の設計を定義する。
Custom Tool Executor は freee HR 3 ツールの本番実行パスである(docs/architecture.mdSection 4.4, 8.2 参照)。
MCP Server hr-freee は read-only の freee HR 棚卸し gateway としても利用する。出力系ツール (Google Sheets, Slack DM) は MCP Server common で実行する。
docs/tech-decisions.mdADR-010 (Custom Tool Executor) の詳細化。
1. 設計原則¶
| # | 原則 | 説明 |
|---|---|---|
| T-1 | 認証情報は管理プロセス内で完結 | OAuth トークンは Backend または freee MCP gateway のサーバープロセスのみが保持。LLM API / Agent コンテキストには渡さない |
| T-2 | ベンダー抽象レイヤーで将来に備える | freee 固有型をそのまま返さず、共通スキーマに変換して Agent に提供 |
| T-3 | ツールスキーマは JSON Schema | Managed Agent が理解できる JSON Schema 形式でツール定義 |
| T-4 | MVP は段階的にツール拡張 | 基本 HR ツールから開始し、Lane B/Lane C の分析系ツールを段階追加 |
| T-5 | エラーはテキストで Agent に返す | 例外を握りつぶさず、Agent が判断できる形式のエラーメッセージを返す |
2. ツールカタログ¶
2.1 MVP ツール一覧¶
| # | ツール名 | カテゴリ | 実行方式 | ベンダー | 説明 |
|---|---|---|---|---|---|
| 1 | list_employees |
HR | Custom Tool | freee | 従業員一覧取得 |
| 2 | list_attendance |
HR | Custom Tool | freee | 勤怠データ取得 |
| 3 | calculate_overtime |
HR | Custom Tool | freee | 残業時間計算 |
| 4 | write_rows_to_google_sheet |
Output | MCP Server | スプレッドシート書込 | |
| 5 | send_slack_dm |
Output | MCP Server | Slack | DM 送信 |
| 6 | list_work_record_summaries |
HR Inventory | MCP Server (read-only) | freee | 勤怠サマリ、有給残数、残業内訳の取得可否確認 |
| 7 | list_employee_group_memberships |
HR Inventory | MCP Server (read-only) | freee | 所属グループと login_email の取得可否確認 |
| 8 | list_payroll_statements |
HR Inventory | MCP Server (read-only) | freee | 給与明細の取得可否確認 |
| 9 | list_leave_related_work_records |
HR Inventory | MCP Server (read-only) | freee | 有給・休暇関連の日次勤怠取得可否確認 |
| 10 | detect_attendance_anomalies |
HR Analysis | Custom Tool | freee / SQLite | 指定日の打刻欠損・0分勤務・24h超過・休日勤務を検出 |
| 11 | aggregate_monthly_attendance_diff |
HR Analysis | Custom Tool | freee / SQLite | 前月/当月の勤怠サマリ差異を検出 |
| 12 | detect_36_agreement_violations |
HR Compliance | Custom Tool | freee / SQLite | 36協定違反・超過リスクを検出し監査ログへ記録 |
| 13 | calculate_prorated_salary |
Payroll Draft | Custom Tool | freee / SQLite | 入退社月の日割り給与ドラフトを算出(確定は人) |
| 14 | detect_payroll_differences |
Payroll Analysis | Custom Tool | freee / SQLite | 前月比差異を検出し大差異をC候補として抽出 |
| 15 | generate_payroll_diff_explanations |
Payroll Explanation | Custom Tool | SQLite | 差異の根拠・前提・不確実性付き説明ドラフトを生成 |
| 16 | detect_payroll_violation_risks |
Payroll Compliance | Custom Tool | SQLite | 最低賃金・残業・社保の法令リスク候補を検出 |
2.2 実行方式の使い分け¶
| 判断基準 | Custom Tool (Backend 内) | MCP Server (Agent 経由) |
|---|---|---|
| 認証情報を扱う (freee) | 採用 | |
| 個人情報を処理する | 採用 | |
| 書込・更新系 (Sheet/DM) | 採用 | |
| 参照系のみ | 両方可 | allowlist された read-only tool のみ可 |
| 監査要件が高い | 採用 (audit_logs 統合) |
2.3 freee MCP read-only gateway¶
Issue #46 の Phase 0 / No.1 では、freee Human Resources API の read endpoint を棚卸しするため、hr-freee MCP Server に allowlist 方式の read-only tool を追加する。MOCK_MODE=true では mock data、MOCK_MODE=false では DB に保存済みの freee OAuth credentials を使って実 API を読む。raw endpoint dispatcher は作らない。
境界条件:
- OAuth token / client secret は MCP Server の環境変数または Backend 管理 DB に閉じ、Agent のプロンプトや tool input には含めない。
- tool は
GET相当の read 操作に限定する。PUT / POST / DELETE は対象外。 - レスポンスは Shared Schema または棚卸し用の安定したフィールド名に正規化する。
- 実 API のレスポンスサンプルは
scripts/freee_hr_inventory.pyでメール・氏名・token をマスクして記録する。
2.4 Lane B E2E seed¶
Lane B のE2Eでは、開発検証用ダミーテナントに限定して backend/src/scripts/seed_freee_e2e_data.py
から日次勤怠 seed を投入できる。これは通常の Custom Tool / MCP Tool ではなく、検証前準備用のCLIである。
対象は work_records/{date} の作成・更新に限定し、実行時に tenant_id, employee_id, month を明示する。
Slack DM / Google Sheets 書き込みとは異なり、dummy tenant での freee write はユーザーの明示許可がある場合のみ行う。
2.5 Lane C payroll draft policy¶
Lane C の給与系ツールは、最終確定ではなく draft + rationale + assumptions + uncertainty を返す。
calculate_prorated_salary と detect_payroll_differences は決定論的な計算ロジックでドラフトを作成し、
generate_payroll_diff_explanations は差異データに対する説明文ドラフトを返す。
detect_payroll_violation_risks は違反確定ではなく、Cルーティング対象となるリスク候補を返す。
freee payroll_statements が権限不足(403)の場合は、detect_payroll_differences に statements を直接渡す代替入力を許可し、
E2E ではこの代替経路を使って判定ロジックを検証できる。
Lane C の検証データテンプレートは backend/src/scripts/seed_payroll_lane_c_data.py で生成できる。
このCLIは freee へ書き込まず、detect_payroll_differences / detect_payroll_violation_risks の入力JSONを出力する。
3. Custom Tool Executor アーキテクチャ¶
3.0 custom_tool_use イベント処理フロー¶
L4 ポーリングループ(docs/architecture.md Section 4.3)内で agent.custom_tool_use イベントを検知し、Backend 内で処理する。
L4 ポーリングループ
│
├── sessions.events.list()
│ └── イベント走査
│ ├── agent.mcp_tool_use → MCP Server 経由 (common ツール)
│ └── agent.custom_tool_use → ★ Backend 内で処理
│
├── agent.custom_tool_use 検知時:
│ ├── tool_name, tool_input を抽出
│ ├── handle_custom_tool(tool_name, tool_input, tenant_id, conn)
│ │ └── Custom Tool Router → freee Adapter → freee API
│ └── events.send(tool_result) → Managed Agent Session 続行
│
└── 次のポーリングへ
Custom Tool Executor が扱うツール: list_employees, list_attendance, calculate_overtime,
detect_attendance_anomalies, aggregate_monthly_attendance_diff,
detect_36_agreement_violations(全て freee HR / HR analysis 系)。
MCP Server common が扱うツール (write_rows_to_google_sheet, send_slack_dm) は agent.mcp_tool_use として処理され、Backend は介入しない。
3.1 全体フロー¶
flowchart TD
A["Managed Agent Session\n(Anthropic Cloud)"] -->|"agent.custom_tool_use event\n(tool_name, tool_input)"| B["Backend L4 ポーリングループ"]
B -->|"_handle_custom_tool()"| C{"Custom Tool Router"}
C -->|list_employees| D1["freee Adapter\nGET /hr/api/v1/employees\n→ SharedSchema.Employee[]"]
C -->|list_attendance| D2["freee Adapter\nGET /hr/api/v1/employees/id/work_records\n→ SharedSchema.Attendance[]"]
C -->|calculate_overtime| D3["freee Adapter\nlist_attendance 集計\n→ SharedSchema.OvertimeSummary"]
D1 --> E["freee API Client"]
D2 --> E
D3 --> E
E -->|"OAuth Token Manager\nRate Limiter\nError Handler"| F["freee API"]
D1 --> G["tool_result (JSON)"]
D2 --> G
D3 --> G
G -->|"events.send(tool_result)"| A
3.2 Router 実装パターン¶
from typing import Any
TOOL_EXECUTORS: dict[str, Callable] = {
"list_employees": execute_list_employees,
"list_attendance": execute_list_attendance,
"calculate_overtime": execute_calculate_overtime,
}
async def handle_custom_tool(
tool_name: str,
tool_input: dict[str, Any],
tenant_id: str,
conn: aiosqlite.Connection,
) -> str:
"""Custom Tool を実行し、結果を JSON 文字列で返す。"""
executor = TOOL_EXECUTORS.get(tool_name)
if executor is None:
return json.dumps({"error": f"Unknown tool: {tool_name}"})
try:
result = await executor(tool_input, tenant_id, conn)
await insert_audit_log(conn, tenant_id, "system", None,
f"tool.{tool_name}", "execution", None,
{"input": tool_input, "output_summary": _summarize(result)})
return json.dumps(result, ensure_ascii=False)
except FreeeAPIError as e:
return json.dumps({"error": str(e), "retryable": e.retryable})
4. ツールスキーマ定義¶
4.1 list_employees¶
{
"name": "list_employees",
"description": "従業員一覧を取得する。部署やステータスでフィルタ可能。",
"parameters": {
"type": "object",
"properties": {
"company_id": {
"type": "string",
"description": "事業所 ID(省略時はデフォルト事業所)"
},
"department": {
"type": "string",
"description": "部署名でフィルタ(省略時は全部署)"
},
"status": {
"type": "string",
"enum": ["active", "retired", "on_leave"],
"description": "在籍ステータスでフィルタ(省略時は全件)"
}
}
}
}
出力:
{
"employees": [
{
"id": "string",
"name": "string",
"department": "string",
"position": "string",
"employment_type": "full_time | part_time | contract",
"email": "string",
"slack_user_id": "string | null",
"status": "active | retired | on_leave",
"hire_date": "YYYY-MM-DD"
}
],
"total_count": 42
}
4.2 list_attendance¶
{
"name": "list_attendance",
"description": "指定従業員の指定月の勤怠データを取得する。",
"parameters": {
"type": "object",
"properties": {
"company_id": { "type": "string" },
"employee_id": { "type": "string", "description": "従業員 ID" },
"month": { "type": "string", "description": "対象月 (YYYY-MM)" }
},
"required": ["employee_id", "month"]
}
}
出力:
{
"employee_id": "string",
"month": "YYYY-MM",
"records": [
{
"date": "YYYY-MM-DD",
"day_type": "weekday | weekend | holiday",
"clock_in": "HH:MM",
"clock_out": "HH:MM",
"break_minutes": 60,
"work_minutes": 480,
"overtime_minutes": 0,
"is_absent": false,
"is_paid_leave": false
}
],
"total_records": 20
}
4.3 calculate_overtime¶
{
"name": "calculate_overtime",
"description": "指定従業員の指定月の残業時間を集計する。list_attendance の結果を基に計算する。",
"parameters": {
"type": "object",
"properties": {
"company_id": { "type": "string" },
"employee_id": { "type": "string" },
"month": { "type": "string", "description": "対象月 (YYYY-MM)" }
},
"required": ["employee_id", "month"]
}
}
出力:
{
"employee_id": "string",
"month": "YYYY-MM",
"total_work_minutes": 9600,
"total_overtime_minutes": 120,
"working_days": 20,
"breakdown": {
"normal_overtime_minutes": 100,
"late_night_overtime_minutes": 20,
"holiday_work_minutes": 0
}
}
4.4 write_rows_to_google_sheet (MCP Server)¶
{
"name": "write_rows_to_google_sheet",
"description": "Google Sheets にデータ行を書き込む。",
"parameters": {
"type": "object",
"properties": {
"spreadsheet_id": { "type": "string" },
"range": { "type": "string", "description": "A1 表記 (例: Sheet1!A1)" },
"rows": {
"type": "array",
"items": { "type": "array" },
"description": "書き込む行データの配列"
}
},
"required": ["spreadsheet_id", "range", "rows"]
}
}
4.5 send_slack_dm (MCP Server)¶
{
"name": "send_slack_dm",
"description": "Slack でユーザーに DM を送信する。",
"parameters": {
"type": "object",
"properties": {
"user_id": { "type": "string", "description": "Slack ユーザー ID" },
"message": { "type": "string", "description": "メッセージ本文" }
},
"required": ["user_id", "message"]
}
}
5. 共通スキーマ (Shared Schema)¶
5.1 設計方針¶
freee API のレスポンスをそのまま Agent に返さず、ベンダー非依存の共通型に変換する。将来 KOT 等の別ベンダーを追加する際に、L1-L3 のプロンプトを変更せずに済む。
flowchart LR
A["freee API Response"] --> B["freee Adapter\n(変換ロジック)"]
B --> C["Shared Schema\n(Agent に返す)"]
D["KOT API Response"] -.-> E["KOT Adapter\n(Post-MVP)"]
E -.-> C
5.2 型定義¶
from dataclasses import dataclass
from datetime import date
@dataclass(frozen=True)
class Employee:
"""ベンダー共通の従業員型。"""
id: str
name: str
department: str
position: str
employment_type: str # full_time | part_time | contract
email: str
slack_user_id: str | None
status: str # active | retired | on_leave
hire_date: date
@dataclass(frozen=True)
class AttendanceRecord:
"""ベンダー共通の勤怠レコード型。"""
date: date
day_type: str # weekday | weekend | holiday
clock_in: str | None # HH:MM
clock_out: str | None # HH:MM
break_minutes: int
work_minutes: int
overtime_minutes: int
is_absent: bool
is_paid_leave: bool
@dataclass(frozen=True)
class OvertimeSummary:
"""ベンダー共通の残業集計型。"""
employee_id: str
month: str # YYYY-MM
total_work_minutes: int
total_overtime_minutes: int
working_days: int
normal_overtime_minutes: int
late_night_overtime_minutes: int
holiday_work_minutes: int
@dataclass(frozen=True)
class WorkRecordSummary:
"""勤怠サマリと有給残数の棚卸し型。"""
employee_id: str
month: str
num_paid_holidays_left: float
total_overtime_except_normal_work_mins: int
total_overtime_work_mins: int
normal_work_mins: int
@dataclass(frozen=True)
class EmployeeGroupMembership:
"""人物マスタ突合用の所属グループ型。"""
employee_id: str
group_id: str
group_name: str
login_email: str
@dataclass(frozen=True)
class PayrollStatement:
"""給与明細の代表フィールド型。"""
employee_id: str
year: int
month: int
gross_pay: int
net_pay: int
paid_at: str
@dataclass(frozen=True)
class LeaveRelatedWorkRecord:
"""有給・休暇関連の日次勤怠棚卸し型。"""
employee_id: str
date: str
paid_holiday: float
special_holiday: float
is_absence: bool
5.3 freee アダプタ¶
class FreeeAdapter:
"""freee API レスポンスを共通スキーマに変換する。"""
@staticmethod
def to_employee(raw: dict) -> Employee:
return Employee(
id=str(raw["id"]),
name=f"{raw['last_name']} {raw['first_name']}",
department=raw.get("department", {}).get("name", ""),
position=raw.get("position", ""),
employment_type=_map_employment_type(raw.get("employment_type", "")),
status=_map_status(raw.get("employment_status", "")),
hire_date=date.fromisoformat(raw.get("hire_date", "2000-01-01")),
)
@staticmethod
def to_attendance_record(raw: dict) -> AttendanceRecord:
return AttendanceRecord(
date=date.fromisoformat(raw["date"]),
day_type=_map_day_type(raw.get("day_pattern", "")),
clock_in=raw.get("clock_in_at"),
clock_out=raw.get("clock_out_at"),
break_minutes=raw.get("break_minutes", 0),
work_minutes=raw.get("normal_work_minutes", 0),
overtime_minutes=raw.get("overtime_work_minutes", 0),
is_absent=raw.get("is_absence", False),
is_paid_leave=raw.get("is_paid_holiday", False),
)
def _map_employment_type(freee_type: str) -> str:
mapping = {"regular": "full_time", "part_time": "part_time",
"contract": "contract", "temporary": "contract"}
return mapping.get(freee_type, "full_time")
def _map_status(freee_status: str) -> str:
mapping = {"employed": "active", "retired": "retired",
"on_leave": "on_leave"}
return mapping.get(freee_status, "active")
def _map_day_type(freee_pattern: str) -> str:
mapping = {"normal_day": "weekday", "prescribed_holiday": "weekend",
"legal_holiday": "holiday"}
return mapping.get(freee_pattern, "weekday")
5.4 Post-MVP: KOT アダプタの追加例¶
class KOTAdapter:
"""KOT API レスポンスを共通スキーマに変換する。"""
@staticmethod
def to_employee(raw: dict) -> Employee:
return Employee(
id=raw["employeeKey"],
name=raw["fullName"],
department=raw.get("divisionName", ""),
# ... KOT 固有のマッピング
)
ツール実行時のベンダー切替:
async def execute_list_employees(tool_input: dict, tenant_id: str, conn) -> dict:
vendor = await get_tenant_vendor(conn, tenant_id) # "freee" | "kot"
if vendor == "freee":
raw = await freee_client.list_employees(tool_input)
employees = [FreeeAdapter.to_employee(e) for e in raw]
elif vendor == "kot":
raw = await kot_client.list_employees(tool_input)
employees = [KOTAdapter.to_employee(e) for e in raw]
return {
"employees": [asdict(e) for e in employees],
"total_count": len(employees),
}
6. freee API Client¶
6.1 エンドポイントマッピング¶
| Custom Tool | freee API | メソッド | パス |
|---|---|---|---|
list_employees |
従業員一覧 | GET | /hr/api/v1/companies/{company_id}/employees |
list_attendance |
勤怠データ | GET | /hr/api/v1/employees/{employee_id}/work_record_summaries/{year}/{month}?work_records=true |
calculate_overtime |
(Backend 集計) | - | list_attendance 結果を計算 |
list_work_record_summaries |
勤怠サマリ | GET | /hr/api/v1/employees/{employee_id}/work_record_summaries/{year}/{month} |
list_employee_group_memberships |
所属グループ | GET | /hr/api/v1/employee_group_memberships |
list_payroll_statements |
給与明細 | GET | /hr/api/v1/salaries/employee_payroll_statements |
list_leave_related_work_records |
休暇関連 (日次勤怠) | GET | /hr/api/v1/employees/{employee_id}/work_records/{date} |
ベース URL は
https://api.freee.co.jp(backend/src/tools/freee_client.py参照)。MOCK_MODE=trueのときはfreee_adapter.pyがモックデータを返すため外部通信は発生しない。
6.1.1 Phase 0 / No.1 データ棚卸し¶
| 対象 | 必要フィールド | 後続 Phase 利用 | 分類 | 代替策 / 備考 |
|---|---|---|---|---|
employees |
id, display_name, entry_date, retire_date |
Phase 0 / No.2 人物マスタ | 取得可能 | entry_date を入社日、retire_date の有無を在籍ステータス判定に利用する |
work_record_summaries |
num_paid_holidays_left, total_overtime_except_normal_work_mins, total_overtime_work_mins, normal_work_mins |
Phase ½ 勤怠・有給残数 | 取得可能 | work_records=true で日次勤怠も同時取得できる |
employee_group_memberships |
id, login_email, group_memberships |
Phase 0 / No.2 人物マスタ突合 | 代替あり | endpoint は取得可能。検証データでは login_email が null、group_memberships が空のため Slack email / Google Sheet 人物マスタで補完する |
payroll_statements |
employee_id, gross_pay, net_pay, paid_at |
Phase 3 / No.13, Phase 4 / No.16 | 不足あり | 現 freee アプリ権限では 403。給与明細スコープ追加または CSV 手動取込が必要 |
leave_related_work_records |
paid_holiday, special_holiday, is_absence |
Phase ½ 有給申請・休暇確認 | 取得可能 | 専用申請 endpoint ではなく日次勤怠から有給・休暇実績を確認する |
実 API の取得結果は token / 氏名 / email をマスクしたうえで docs/custom-tools.md のこの表へ反映する。取得できなかったフィールドは 不足あり、別経路で運用可能なものは 代替あり に分類する。2026-04-29 の検証では、給与明細のみ権限不足で 403 となった。
2026-04-29 レスポンスサンプル要約¶
scripts/freee_hr_inventory.py で token / 氏名 / email をマスクして確認した結果。
| 対象 | ステータス | マスク済みサンプル要約 |
|---|---|---|
employees |
200 | id, num, display_name, entry_date, retire_date, email, payroll_calculation, closing_day, pay_day, month_of_pay_day を確認。display_name は ***MASKED***、email は null |
work_record_summaries |
200 | num_paid_holidays_left, total_overtime_except_normal_work_mins, total_work_mins, work_records[].date, work_records[].paid_holiday, work_records[].special_holiday, work_records[].is_absence を確認 |
employee_group_memberships |
200 | employee_group_memberships[].id, display_name, entry_date, retire_date, login_email, group_memberships を確認。ただし検証データでは login_email が null、group_memberships が空 |
payroll_statements |
403 | {\"error\":\"access_denied\",\"message\":\"このアプリケーションにはアクセス権限がないエンドポイントです\"}。給与明細用の権限追加が必要 |
leave_related_work_records |
200 | date, day_pattern, paid_holiday, paid_holidays, special_holiday, special_holiday_setting_id, is_absence, total_overtime_work_mins を確認 |
6.2 OAuth Token Manager¶
class FreeeTokenManager:
"""freee OAuth トークンの管理。自動リフレッシュ対応。"""
async def get_access_token(self, tenant_id: str, conn) -> str:
integration = await get_integration(conn, tenant_id, "freee")
creds = decrypt_credentials(integration.credentials)
if self._is_expired(creds):
creds = await self._refresh(creds)
await update_integration_credentials(conn, integration.id, creds)
return creds["access_token"]
async def _refresh(self, creds: dict) -> dict:
resp = await httpx.post("https://accounts.secure.freee.co.jp/public_api/token", data={
"grant_type": "refresh_token",
"refresh_token": creds["refresh_token"],
"client_id": settings.freee_client_id,
"client_secret": settings.freee_client_secret,
})
resp.raise_for_status()
return resp.json()
6.3 エラーハンドリング¶
| HTTP ステータス | 対処 | リトライ |
|---|---|---|
| 401 | トークンリフレッシュ → リトライ | 1 回 |
| 429 | Retry-After ヘッダに従い待機 |
最大 3 回 |
| 5xx | Exponential backoff | 最大 3 回 |
| 4xx (その他) | エラーテキストを Agent に返す | しない |
class FreeeAPIError(Exception):
def __init__(self, message: str, status_code: int, retryable: bool = False):
super().__init__(message)
self.status_code = status_code
self.retryable = retryable
7. L2/L3 用ツールカタログ¶
7.1 L2 用ツールカタログ (自然言語)¶
L2 で Agent に「使えるツール」を伝えるためのテキスト。L2 の user message に含める。
利用可能なツール:
- list_employees: 従業員一覧を取得(部署・ステータスでフィルタ可能)
- list_attendance: 指定従業員の指定月の勤怠データを取得
- calculate_overtime: 指定従業員の指定月の残業時間を計算
- list_work_record_summaries: 勤怠サマリ・有給残数・残業内訳の取得可否を確認
- list_employee_group_memberships: 所属グループと login_email を取得
- list_payroll_statements: 給与明細の代表フィールドを取得
- list_leave_related_work_records: 有給・休暇関連の日次勤怠を取得
- detect_attendance_anomalies: 指定日の打刻欠損・0分勤務・24h超過・休日勤務を検出
- aggregate_monthly_attendance_diff: 前月/当月の勤怠サマリ差異を検出
- detect_36_agreement_violations: 36協定違反・超過リスクを検出
- write_rows_to_google_sheet: Google Sheets にデータを書き込み
- send_slack_dm: Slack で DM を送信
7.2 L3 用ツールスキーマ (JSON)¶
L3 で Agent がステップ計画を作成する際に参照する JSON Schema。Section 4 のスキーマをまとめて渡す。
DEFAULT_TOOL_SCHEMAS = json.dumps([
# Section 4.1 ~ 4.5 のスキーマを配列で提供
], ensure_ascii=False, indent=2)
8. Post-MVP 拡張¶
| 項目 | MVP | Post-MVP |
|---|---|---|
| ベンダー | freee のみ | KOT, SmartHR 等のアダプタ追加 |
| ツール数 | 5 | 給与計算、社会保険、年末調整等 |
| Policy Engine | 全ツール自動承認 | tool_permissions テーブルで allow/deny/requires_confirmation |
| 動的カタログ | ハードコード | テナント設定から有効ツールを動的生成 |
| Shared Schema | dataclass | Pydantic v2 でバリデーション強化 |
| MCP ハイブリッド | 2 パターン並行 | Custom Tool + MCP の判定を Policy Engine が自動化 |
変更履歴¶
| 日付 | バージョン | 変更内容 |
|---|---|---|
| 2026-04-18 | v1.0 | 初版作成 |
| 2026-04-19 | v1.1 | Custom Tool Executor が freee 本番パスであることを明確化。custom_tool_use イベント処理フローを追記 (issue #11) |
| 2026-04-20 | v1.2 | freee API パスを /hr/api/v1/ に修正、イベント名を agent.custom_tool_use に統一(実装整合) |
| 2026-04-29 | v1.3 | read-only freee MCP gateway と Phase 0 / No.1 データ棚卸し対象を追加 (issue #46) |
| 2026-05-05 | v1.4 | Lane B E2E 用 freee work_records seed CLI を追加 |
| 2026-05-05 | v1.5 | Lane B analysis tools を Custom Tool Executor / L3 catalog に追加 |
| 2026-05-06 | v1.6 | Lane C payroll tools(#60/#61/#58/#62)と draft-first 運用方針を追加 |