Files
ResolveSpec/audit/mcp_plan.md
T

146 lines
6.9 KiB
Markdown

# resolvemcp rewrite plan
Source: `audit/pkg/resolvemcp.audit.md`. Status: plan only, no code changed.
## Goal
Replace 4 tools + 1 resource per model with a fixed set of meta tools.
Endpoint guarded by OAuth / session token / API key; tools run as the authenticated caller.
Same rules as resolvespec CRUD, plus guardrails.
## Decisions
| Topic | Decision |
|---|---|
| Tools | Fixed meta tools; per-model tools/resources removed (breaking) |
| Functions | Explicit registry `Handler.RegisterFunction`; two kinds: Go callback (`func(ctx, tx, args)` + JSON-schema params) and SQL procedure by name (declared params); both behind `call_function`, run in tx with hooks |
| Create | `insert_into_table` included |
| Writes | update/delete by id **or** filters |
| Guardrails | require id or filters, max rows, `dry_run`, confirm token |
| Token scope | filter writes only; id writes = single row, no token |
| Confirm token store | in-memory, TTL, bound to user/table/filter hash; lost on restart, single instance |
| Read limits | server caps: limit, offset, batch, preload depth, timeout |
| Model exposure | all registered models visible; rules only restrict operations |
| Visibility | list tools show only what the caller may do (rules) |
| Identity | the authenticated caller's `UserContext`; **no fixed MCP user, no `SetUsername`, no service session, no background refresh** |
| Guard | endpoint always requires one of: OAuth bearer, session token, API key; no guest/optional mode |
| API key login | new `DatabaseAuthenticator.LoginWithAPIKey(ctx, rawKey)` + procedure `resolvespec_login_api_key`; validates key via keystore, creates session, returns `LoginResponse` |
| Session SQL | procedure mode + direct-SQL fallback (`ShouldUseProcedure`), same as `Login` |
| OAuth routes | `oauth2.go`/`oauth2_server.go` kept, part of the guard |
| Annotations | opt-in `Config.EnableAnnotations`, via `BeforeHandle` |
## Open
- None.
## Tools
| Tool | Purpose |
|---|---|
| `list_tables` | visible `schema.entity` + allowed ops |
| `describe_table` | columns, PK, relations, writable columns, rules, limits |
| `select_table` | filters, sort, columns, preloads, cursor; capped |
| `insert_into_table` | one or batch (capped); column allowlist |
| `update_table` | validated keys; id or filters; guardrails |
| `delete_from_table` | id or filters; guardrails |
| `list_functions` | registered functions + parameter schemas |
| `call_function` | validated args; tx + hooks + rules |
## Config additions
| Field | Purpose |
|---|---|
| `DefaultLimit`, `MaxLimit`, `MaxOffset` | read paging caps |
| `MaxBatch` | insert batch cap |
| `MaxPreloadDepth` | preload cap |
| `MaxWriteRows` | filter-write row cap |
| `QueryTimeout` | per-call context timeout |
| `ConfirmTTL` | confirm token lifetime |
| `EnableAnnotations` | opt-in annotate tool |
## Guardrail rules
| Rule | Behaviour |
|---|---|
| Target required | update/delete with neither id nor filters rejected |
| Max rows | count matches inside tx; abort above `MaxWriteRows` |
| `dry_run` | returns match count + preview, no write |
| Confirm token | filter write: first call returns token + preview; second call with token executes; bound to user, table, filter hash; expires at `ConfirmTTL` |
| Id write | single row, no token |
## Work items
### 1. API key login (`pkg/security`)
- Existing: keystore has `ValidateKey` and `KeyStoreAuthenticator`; `Login` needs a password; no key-to-session path.
- Add `resolvespec_login_api_key` to `SQLNames` (default + override) and a SQL script beside the existing procedures. Contract: `p_success, p_error, p_data`, input raw key; hashes, validates active/non-expired key, creates session for the key's user.
- Add `DatabaseAuthenticator.LoginWithAPIKey(ctx, rawKey)`; procedure first, direct-SQL fallback via `ShouldUseProcedure`.
- Hashed lookup; same generic error for unknown, expired or inactive key; no key material in logs.
- Expose through the chain/composite authenticators so the middleware can accept it.
### 2. Endpoint guard
- Wire `security.NewAuthMiddleware` with a chain of OAuth bearer, session token (header/cookie) and API key.
- `SetupMux*`/`SetupBunRouter*` helpers require the guard; unauthenticated serving only when explicitly constructed without it, logged loudly. Remove `OptionalAuth*` from the MCP path.
- Caller `UserContext` flows to every tool call context; rules, RLS and `OnTxBegin` apply to that user.
### 3. Security fixes (audit #1-6)
- Put model rules in request context (`withRequestData`) and/or `AddRegistry` on construction.
- Add `BeforeCreate` -> `CheckModelCreateAllowed` (new in `pkg/security`).
- Call `BeforeHandle` first in `executeUpdate`.
- Validate create/update keys against `ColumnValidator`; reject unknown; resolve column names from model, not json tags.
- Apply row security to update/delete pre-read; fail if row not visible.
- Annotate tool: opt-in + `BeforeHandle`.
### 4. Limits (audit #7, #13)
- Apply default/max limit, max offset, batch cap, preload depth cap, timeout.
- Validate preload names against model relations.
- Count only when requested.
### 5. Error and panic surface (audit #9, #17)
- Stable error codes + short message to client.
- Details and stack logged server-side.
- Recover hook panics.
### 6. Update/create semantics (audit #10-12)
- `SET` from validated incoming keys only; explicit null supported.
- Lock row (`FOR UPDATE`) on update.
- Refetch and `After*` hooks inside the same tx, or report committed write with warning if not possible (see `audit/single_tran.md`).
### 7. Smaller fixes (audit #8, #14, #15)
- SSE pool: require `BaseURL` or cap/evict; allowlist Host.
- Uniform not-found vs hook error text.
- Add mutex to `HookRegistry`.
### 8. Meta tools
- New file for meta tools; reuse parse helpers and `buildModelInfo` for `describe_table`.
- Remove per-model register functions and resources.
- `RegisterModel` only registers to registry.
- Function registry (Go callback kind + SQL procedure kind) + validation of args against declared schema.
### 9. Tests
- Update `tx_test.go` (calls `executeRead/Create/Update/Delete`) and `tools_test.go`.
- New: rule enforcement, unknown keys, limits, guardrails (cap, dry_run, token expiry/binding), guard rejects unauthenticated, API key login (valid, expired, inactive, unknown), visibility filtering, `-race`.
- Check for existing test data first; ask before generating any.
### 10. Docs
- Rewrite `pkg/resolvemcp/README.md` cheatsheet style.
- Document `resolvespec_login_api_key` in `pkg/security` docs.
- Update root README references.
- Update audit file when findings are closed.
## Order
1. `LoginWithAPIKey` + procedure in `pkg/security` (1)
2. Guard + security fixes (2-3)
3. Limits, errors, update/create semantics (4-6)
4. Meta tools + function registry (8)
5. Smaller fixes (7)
6. Tests (9), docs (10)
## Breaking changes
- Per-model tools and resources gone.
- MCP endpoint requires authentication.
- Annotate tool off by default.
- Update/create reject unknown keys.
- Reads capped by default.