Add org-level access groups for MCP connections - #1597
Open
TheLiberal wants to merge 2 commits into
Open
Conversation
Tenant-scoped access_group/access_group_member tables plus a nullable connection.access_group column. A restricted org connection is invisible and uninvokable for non-members on every read and invoke surface, with no existence oracle; membership is read live per call. Management rides executor.accessGroups behind host admin gates (cloud /org/access-groups*, self-host /api/admin/access-groups*), never the any-member ExecutorApi. Platform view and subject-less org bindings stay unfiltered by design.
The cascade tripwire caught the gap: the hand-written purge list must name every tenant table, and access_group/access_group_member were missing.
This was referenced Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Department-level access control for org connections: a connection can be restricted to a named access group, and for anyone outside the group it does not exist — on any read or invoke surface, with no existence oracle.
access_group,access_group_member) built on the sametenantExecutorTablefactory assubject, plus a nullableconnection.access_groupcolumn (nullable is required for SQLite boot-ensure hosts).owner-policy.tsand theOwnerunion are untouched — groups are an additive axis, not a third owner scope.toolsList(unconditionally, outsideincludeBlocked— the HTTP tools list defaultsincludeBlocked=true, so a policy-block model would leak on the wire),connectionsList/get/update/remove/refresh/checkHealth,toolSchema, andexecute. Inexecutethe gate runs before policy resolution and not-found suggestions: a blocked verdict or a suggestion list naming a hidden connection's tools would each be an oracle. Hidden ≡ nonexistent: same errors, empty suggestions.executor.accessGroupsbehind each host's existing admin gate (cloud: WorkOS admin-role session; self-host:requireInstanceAdmin), never the any-memberExecutorApi.listConnectionsdrops literal<integration>.org.<connection>patterns that don't resolve for the caller — the pattern text is otherwise an oracle.Tests
packages/core/sdk/src/access-groups.test.ts— CRUD + integrity rules.packages/core/sdk/src/access-group-enforcement.test.ts— the red-team invariants: no read/invoke path returns a restricted row to a non-member (incl.includeBlocked=true), non-member ≡ nonexistent error parity, an org policy approve cannot re-expose a restricted tool, live mid-session roster edits, unfiltered platform/subject-less views, unrestricted rows unaffected.0016(additive); local/self-host/cloudflare boot-ensure fromcoreTables.Part 1 of 3 (followed by per-toolkit grants and the console UI).