chore: hosted MCP readers take hosting state from the MCP server

Merged
DA
8d ago
+857-296

Part of AIM-668 (1 of 3).

Summary

  • Every reader of a hosted MCP server's enabled, public and session-issuer state now reads the server's canonical mcp_servers row (hostedmcp.WithServerState / LoadServerState), and falls back to the toolset columns only when no canonical row exists. This covers the legacy /mcp/<slug> fallback and install page, gateway members (including RPC cache hints), consent, adding a server to a plugin by toolset id, the assistant runtime list, access visibility, usage metrics, search-indexing gates, the issuer attach preview, the deployment impact report and the external MCP migration candidates.
  • The drift checks that deny a hosted server whose MCP server and toolset disagree are kept on purpose, now expressed as hostedmcp.InSync. This PR changes nothing while the two copies agree; the flip PR removes the checks.
  • The test-only setters SetToolsetCustomDomain, SetToolsetMCPPublic* and SetToolsetMCPEnabledByID move out of the toolsets repo into the testenv fixtures. Their public/enabled variants now also update the canonical server, the same way production writes do.

Motivation

The toolset is still the writer of hosting state, and hostedmcp.Sync copies it onto the MCP server. The next two PRs make the MCP server and its endpoints the only writer and freeze the toolset columns as a legacy alias. For that to be safe, nothing may still read the frozen columns. This PR moves the readers first, while both copies are equal, so it can deploy with no behaviour change.

The PrimaryEndpoint helpers are kept; the reasons are recorded on the ticket.

🤖 Generated with Claude Code


Summary by cubic

Reads hosted MCP servers' enabled, public, and session-issuer state from the canonical mcp_servers row instead of the toolset columns, falling back to the toolset only when no canonical row exists. Both copies agree today, so behavior is unchanged; the new state readers (SQL and hostedmcp.LoadServerState) also deny any hosted server whose wrapper and toolset disagree, matching the existing drift checks now expressed as hostedmcp.InSync and kept on purpose until the MCP server becomes the only writer.

  • Covers the legacy /mcp/<slug> fallback and install page, gateway members and RPC cache hints, consent, plugin attachments, assistant runtime lists, access visibility, usage metrics, search-indexing gates, issuer attach preview, deployment impact report, and external MCP migration candidates.
  • A toolset whose id a server kept but which no longer canonical is treated as disabled, so it is skipped by indexing and migration.
  • Moves the test-only setters SetToolsetCustomDomain, SetToolsetMCPPublic*, and SetToolsetMCPEnabledByID from the toolsets repo into testenv fixtures, where the public/enabled variants now also update the canonical server like production writes do.