chore: remove the old toolset MCP hosting code path

Merged
DA
3d ago
+1534-5859

AIM-731 (2 of 3). Stacked on #8125.

Customer impact: none. On the prod replica (2026-10-26), every non-deleted toolset in a live project has a hosted MCP server. The 52 toolsets without one are all in deleted projects (40 MCP-enabled, 12 not). In the last 30 days, the legacy fallback reached a toolset without a hosted server only once: 4 requests on 10-12, to a toolset in a deleted project.

Summary

  • A toolset without a canonical hosted MCP server is not hosted. The toolset's own hosting columns no longer stand in when it has none:
    • The legacy /mcp/<slug> fallback, install page, export and well-known metadata treat such a toolset as not found.
    • Toolset views report it as disabled with no address.
    • It is no longer plugin-eligible, a mint target, or an issuer owner.
    • The usage counts, the indexing scan, access visibility and the external MCP migration ignore it.
    • Admin no longer lists toolset_only servers: the project server count and health view cover mcp_servers rows only.
  • Toolset views: hosting fields now come from the hosted server: its servable endpoint, else its legacy alias (which still serves until AIM-670), else nothing. Without a server, a toolset reads as disabled with no address.
  • External MCP migration: issuers now come from the server fronting the toolset. A toolset without a hosted server is now listed for deletion, because nothing serves it.
  • Logging: a legacy alias that resolves to a toolset without a hosted server now logs a warning (legacy MCP alias names a toolset without a hosted MCP server) before the 404, so the rollout can be watched.
  • Writers removed:
    • CreateToolset and the clone no longer pass hosting values.
    • The issuer migration no longer moves toolset issuer columns.
    • The external MCP migration no longer runs DisableToolset; the switched server is not canonical, so the alias stops serving anyway.
    • The UpdateToolset and UpdateToolsetUserSessionIssuer test fixtures lose their hosting fields.
  • Retired:
    • hostedmcp.BackfillSync and the hosted-mcp-wrappers migration tool (cmd/tools/migrations/hostedmcpbackfill).
    • testenv.LinkRetiredOAuthProxy: the column it set is dropped on main (#8020).
  • New test fixtures:
    • testenv.SetHostedMCP writes visibility, issuer and endpoint onto the canonical server.
    • testenv.SetLegacyAlias sets the alias.
    • The oauthtest legacy fixtures now create an endpointless canonical server with an alias.
  • No API fields are removed. The toolset response hosting fields stay and are derived from the server only.

Motivation

Code must stop reading the toolset MCP hosting columns before AIM-671 drops them. The only remaining readers here are the wrapperless fallbacks, and those serve nothing real:

  • New toolsets always get a canonical server: create and clone both call hostedmcp.CreateCanonical.
  • The prod replica had 0 live wrapperless toolsets with an address. The 52 that exist are in soft-deleted projects.

After this PR, only the legacy alias (mcp_slug/custom_domain_id, owned by AIM-670) still names the columns. The next PR fences that off.

Rollout

  • Pre-merge check: Datadog shows 4 fallback hits with canonical_wrapper=false, all from one toolset on 2026-10-12. Confirm on the replica that it now has a hosted server or is dead.

  • Admin and API enums keep toolset_only and the issuer reference kind toolset for wire compatibility. Both are no longer emitted. Admin MCP needs no change: its health tool only reads the source the API reports.

  • Old pods still serve a wrapperless toolset from its columns. Since there are none live, a mixed fleet behaves the same.

  • Old pods' toolset create and clone still write mcp_slug = NULL / mcp_enabled = false, which this code ignores.

  • Reverting restores the column fallback.

    • One write here can leave stale state for a revert: deleting an issuer that only a wrapperless toolset's column names.
    • The reverted code then fails closed for that toolset: its alias returns 401 and the well-known metadata returns 404.

šŸ¤– Generated with Claude Code