Files
ResolveSpec/audit/mcp_plan.md
T

6.9 KiB

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.