feat(mcp-store): personal/shared MCP installation scope#3311
Conversation
Port of PostHog/posthog#69658 to the Code client. The backend now supports a scope on MCP Store installations: personal (per-user, the existing default) or shared (team-wide, visible to all project members and autonomous agents). - api-client: add scope to MCPServerInstallation and the install_custom/install_template request contracts, mirroring the regenerated OpenAPI types; export McpInstallationScope. - core: thread scope through CustomServerFormValues, buildCustomServerRequest, and the OAuth install flows. - ui: Visibility (Personal/Shared) selector in the add-server form, with the shared option gated to admins (creation is admin-only on the backend since a shared install exposes the installer's credential); Shared badge in the installed rail and server detail header. Shared installations returned by the list endpoint flow into agent sessions automatically via the existing workspace-server wiring.
…-wins dedupe
Builds on the personal/shared installation scope:
- Share with project / Unshare actions in the server detail view. Sharing
is owner+admin only and sits behind a consent dialog spelling out that
project members and autonomous agents will act as the sharer on the
connected service. Backed by new shareMcpInstallation /
unshareMcpInstallation api-client methods (POST {id}/share|unshare).
- Owner/admin gating driven by the new is_owner serializer field: tool
policy toggles, bulk actions, refresh, and the enable switch are
owner-only on shared servers; Remove is owner-or-admin. Unknown
is_owner (older backends) keeps controls usable — the backend is the
enforcement point.
- "Connect personally" on a teammate's shared server so members can use
their own identity instead of the shared credential.
- Personal wins over shared: agent session wiring dedupes installations
by URL, dropping a shared install when the user has their own personal
connection to the same server.
Pairs with the posthog backend phase-1 branch (share/unshare endpoints,
admin delete override, is_owner, sandbox dedupe, basic audit logging).
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
React Doctor found 3 issues in 2 files · 3 warnings. 3 warnings
Reviewed by React Doctor for commit |
|
Reviews (1): Last reviewed commit: "chore(mcp-servers): tighten share dialog..." | Re-trigger Greptile |
| description?: string | undefined; | ||
| auth_type?: MCPAuthTypeEnum | undefined; |
There was a problem hiding this comment.
scope typed as required but absent on older backends
scope: MCPServerInstallationScopeEnum is non-optional in the generated type, but the PR description explicitly notes that older backends won't include it. At runtime scope will be undefined for those environments, even though TypeScript asserts it's always a string union. Every call site in this PR handles this correctly (scope === "shared" evaluates to false for undefined), but the misleading type means future code won't get a compiler warning if it treats scope as guaranteed — e.g. a switch on scope without a default. Marking it scope?: MCPServerInstallationScopeEnum would make the contract accurate. If this file is purely auto-generated it can only be fixed upstream in the schema, but it's worth noting the discrepancy given auth-adapter.ts already declares it as optional in its local type.
- Clear the pending-install id snapshot when "Connect personally" fails, so a later-appearing row (e.g. a teammate sharing a server) can't hijack the view - Only dedupe personal-over-shared agent installations on truthy URLs; absent URLs no longer collapse unrelated servers. Align local types with the generated schema (url is optional) and end name fallbacks at the always-present id - Prefer the personal row in the marketplace template->installation map so card navigation doesn't depend on API ordering - Don't flash the admins-only visibility hint while the org role loads - Drop a redundant installation guard; document phase-1 template-only scope of "Connect personally" - Tests: share/unshare client methods (paths + error surfaces), missing- URL dedupe case
Older backends without PostHog/posthog#69658 omit scope from the serializer, so the field is undefined at runtime even though the hand-mirrored type asserted it was always present. Type it optional — matching is_owner, which shipped with the same forward-compat treatment — so future call sites can't assume it's guaranteed. Becomes required again on the next regen once the backend is deployed everywhere.
Problem
MCP Store installations are user-scoped only, so team-wide (shared) MCP connections aren't usable from PostHog Code, and autonomous agents can only use the task creator's personal connections. Backend support is being added in PostHog/posthog#69658 plus the stacked phase-1 branch
feat/shared-mcp-phase1(share/unshare escalation, admin delete override,is_owner, sandbox dedupe, basic audit logging). This is the Code-client half of phase 1.Phase 2 (tracked separately) covers full audit logs, richer in-session differentiation, correct attribution, and per-MCP access controls.
Changes
scope(personal|shared) andis_owneronMCPServerInstallation;scopeaccepted byinstall_custom/install_template; newshareMcpInstallation/unshareMcpInstallation(POST{id}/share|unshare/).scopethreaded through the custom-server form builder and OAuth install flows.is_owner: tool policy, refresh, and enable/disable are owner-only on shared servers; Remove is owner-or-admin. Unknownis_owner(older backends) keeps controls usable — the backend enforces.Forward-compatible with today's backend: without the posthog changes deployed,
scopeis ignored server-side,is_owner/share endpoints simply aren't exercised, and everything behaves as before.How did you test this?
pnpm typecheck(23/23 packages)biome checkclean on all touched files;biome lint packages/coreshows zeronoRestrictedImportsAutomatic notifications