docs: add Claude AI assistant rules and configuration
Add comprehensive rule documentation for AI-assisted development covering authentication surfaces, outbound-call safety invariants, and other project conventions to guide Claude's understanding of critical system behaviors.
This commit is contained in:
56
.claude/rules/mcp-tools.md
Normal file
56
.claude/rules/mcp-tools.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
description: MCP tools return formatted strings; ToolError-vs-return-string convention; lazy gateway resolution; resources are JSON
|
||||
paths:
|
||||
- "mcp_server/server.py"
|
||||
---
|
||||
|
||||
# MCP tools & resources
|
||||
|
||||
The MCP server ([mcp_server/server.py](../../mcp_server/server.py)) is the
|
||||
AI-assistant control surface. It's built by `create_mcp_server(get_gateway)` and
|
||||
mounted at `/mcp/` before the lifespan runs, so everything is resolved lazily.
|
||||
|
||||
- **Auth is the ASGI `_owner_only_mcp` wrapper in [main.py](../../main.py), not
|
||||
FastMCP.** `create_mcp_server` builds `FastMCP(auth=None)`; the wrapper resolves
|
||||
the `Authorization` bearer (Casdoor JWT or owner-minted PAT) via the shared
|
||||
`resolve_from_header_or_query` + `is_owner` and returns 401/403 before the inner
|
||||
app runs. Don't reintroduce a FastMCP verifier here — one resolver gates all
|
||||
four surfaces (see the [auth-surfaces rule](auth-surfaces.md)). MCP clients use
|
||||
a PAT (`hs_pat_…`) minted from the dashboard's Tokens modal.
|
||||
|
||||
- **Tools return plain formatted strings; resources return JSON strings.** Tools
|
||||
produce human-readable text an assistant reads back to a user (`"Call abc123
|
||||
initiated. …"`). Resources (`gateway://status`, `gateway://call-flows`,
|
||||
`gateway://active-calls`) return `json.dumps(...)`. Don't blur these — a tool
|
||||
that returns raw JSON, or a resource that returns prose, breaks the contract.
|
||||
|
||||
- **Error convention — match the two existing patterns:**
|
||||
- **Raise `ToolError`** when the request is invalid or unsafe: emergency
|
||||
number, bad mode, concurrency cap hit, or "gateway still starting"
|
||||
(`require_gateway`). The assistant should treat these as errors.
|
||||
- **Return an error string** for a lookup that simply found nothing or hit a
|
||||
recoverable snag: `"Call {id} not found."`, `"No stored call flow for …"`,
|
||||
`"Error looking up …: {e}"`. The assistant reads these as content.
|
||||
- Rule of thumb: *"you asked for something invalid/unsafe" → raise; "I looked,
|
||||
here's the (maybe empty/failed) answer" → return.*
|
||||
|
||||
- **`require_gateway()` gates every tool that needs the live gateway.** It raises
|
||||
`ToolError("Gateway is still starting up …")` when `get_gateway()` returns
|
||||
`None`. This is why the MCP app can mount before the lifespan builds the
|
||||
gateway. Call it at the top of any tool that touches the gateway; never assume
|
||||
the gateway exists.
|
||||
|
||||
- **`make_call` is the one tool that dials.** It maps the string `mode` to
|
||||
`CallMode`, defaults unknown modes to `DIRECT`, and lets `gateway.make_call`'s
|
||||
refusals (`ValueError`) surface as `ToolError`. The emergency guard and
|
||||
concurrency cap live in the gateway, **not** here — don't reimplement or skip
|
||||
them at the tool layer (see the call-safety rule).
|
||||
|
||||
- **DB-backed tools use `session_scope()`** and read from `call_persistence`.
|
||||
Completed-call history, summaries, recordings, and stored flows come from the
|
||||
database, not from `active_calls` (those are live only). Keep the
|
||||
`async with session_scope() as session:` pattern; don't open ad-hoc sessions.
|
||||
|
||||
- **Keep the tool count and README table in sync.** There are 15 tools + 3
|
||||
resources. If you add/remove one, update the README's MCP table and the
|
||||
`docs/mcp-server.md` reference — a drifting tool list is a documented lie.
|
||||
Reference in New Issue
Block a user