mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-10-02 11:31:57 +00:00
146 lines
6.9 KiB
Markdown
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.
|