Paradaux

PAR-204

0

MCP RBAC enforcement denied all writes: tenant/permission context lost across the streamable-transport thread boundary

DoneHighUnassignedTesksBug

Symptom

After PAR-185/186 server-side RBAC enforcement deployed, every permission-gated MCP tool (tesks_create_task, tesks_update_task, tesks_add_comment, tesks_create_release, tesks_freeze_release) returned You don't have permission to do that (issue.create|...) for all keys — including admin keys and the static service key. Ungated reads (list_*, get_task) worked but silently ran against the default workspace.

Root cause

PermissionService.permissions() authorizes from the TenantContext/KeyContext ThreadLocals bound by TenantBearerFilter, and short-circuits to an empty set when TenantContext is null. But the streamable-HTTP MCP transport (HttpServletStreamableServerTransportProvider + McpSyncServer) executes SyncToolSpecification handlers off the servlet request thread, so those ThreadLocals were null at perms.require(...) time → empty permission set → denied. Reads weren't gated and PostgresTaskSystem falls back to the default workspace when TenantContext is null, which masked the loss. Verified the role model was fine (V19 seeds admin with issue.create and backfills existing keys → admin; ApiKeyService.resolve() returns the grants) — the permissions simply weren't reaching the handler thread. The static-key path additionally never set a tenant at all.

Fix (shipped — commit 114c3b6 on develop)

  • McpRequestContextExtractor resolves tenant + key permissions on the request thread and stashes them in the MCP McpTransportContext (handles per-workspace keys, and static-key/open-dev as full-trust on the default workspace).
  • TaskMcpServer wires it via .contextExtractor(...).
  • TaskMcpTools.ok(exchange, …) rebinds TenantContext/KeyContext from exchange.transportContext() on the handler thread (try/finally). Also fixes reads silently using the default workspace.

Acceptance

  • An admin/per-workspace MCP key can create/update/comment (no spurious 403). ✅ verified by this very issue being created via MCP.
  • Reads are scoped to the key's workspace, not the default.

Relates: PAR-185 (REST/service enforcement), PAR-186 (MCP enforcement — this is its missing context plumbing), PAR-187 (UI alignment).

Deploy note (separate infra issue)

The rollout was blocked for ~hours by an Argo CD ComparisonError on github.com/ParadauxIO/gitops.git ("Password authentication is not supported") affecting tesks-development, tesks-ui-development, and tesks-production (Degraded). Argo's gitops repo credential needs rotating (PAT/SSH) — worth its own ticket.

Comments

No comments yet.

Activity

  • tesks changed status to Status → Done
  • tesks changed status to Status → Pending Release
  • tesks created the issue