Skip to content

chore: add a backfill that gives legacy toolset aliases explicit endpoints - #8254

Open
daviddanialy wants to merge 9 commits into
mainfrom
daviddanialy/aim-821-alias-endpoint-backfill
Open

daviddanialy wants to merge 9 commits into
mainfrom
daviddanialy/aim-821-alias-endpoint-backfill

Conversation

@daviddanialy

@daviddanialy daviddanialy commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

AIM-821

Summary

Adds go run ./cmd/tools/migrations legacy-alias-endpoints (run from server/), a one-off backfill that gives each hosted MCP server an explicit endpoint for every address its legacy toolset alias still serves and no live endpoint holds.

  • Addresses: the alias's own address, plus the platform address the loose lookup serves for a custom-domain alias (no live platform alias with the slug, no other live-project custom-domain alias with it, no custom-domain lockdown), including aliases on deleted domains. Servers the fallback refuses are skipped: deleted toolset, project or server, a non-canonical server, private_only network mode, organization drift or missing metadata. Disabled servers still get endpoints.
  • Rules: every address goes through the hosted create path's checks (checkHostedNewAddress: slug length, verified and activated org domain, private ingress) and slug availability, under the same locks and order, with the organization's custom domain also locked so the lockdown read is consistent. A rejected address is a skip with its reason; one held by another server's endpoint or alias is a conflict and never taken.
  • Org slug prefix: the backfill passes slugPrefixExemptLegacyAlias, so an unprefixed slug the alias already serves keeps its URL. API creates still pass slugPrefixRequired. mcpEndpoints.update and UpdateMcpEndpointAddressInTransaction now check the prefix against the locked endpoint row and only when the address changes, so a grandfathered endpoint can be re-saved.
  • Alias moves: the alias follows the endpoints only when its own address ended create/already_held or its domain was deleted, so it never leaves a pending, unverified, foreign or ingress-rejected domain. Every move is printed and journaled.
  • Advertised URL: the toolset view (ListCanonicalHostingByToolsetIDs), mcpmetadata export and assistant connections (LoadAssistantMcpServers, hosted rows) now pick endpoints in endpointaddr.PrimaryEndpoint's order (live custom domain first), as plugin generation already does. Without this the backfill's new platform endpoints would switch those surfaces from a customer's custom domain to the platform URL.
  • Writes: mcp-endpoint:create audit rows by system:legacy-alias-endpoint-backfill via audit.WithoutOutbox(); no plugin attachment or publication.
  • Runner: dry run by default (same transaction, rolled back); -apply needs -environment/-confirm-environment, -confirm-target, -confirm-apply=legacy-alias-endpoints and a new 0600 -journal. A failed commit is reported as commit_outcome_unknown; the exit is non-zero while any server failed. Reruns report already_held.
  • Runbook: server/cmd/tools/migrations/LEGACY_ALIAS_ENDPOINTS_BACKFILL.md (dev first, verification, journal-driven rollback).

Motivation

AIM-670 (#8248) deletes the alias and its /mcp/{slug} fallback. Applying this first keeps every stored client URL working and the slugs reserved. Once this has run in prod, #8248 must also delete server/internal/mcpendpoints/alias_backfill.go, the slugPrefixExemptLegacyAlias branch and the three backfill queries in legacyalias/queries.sql.

🤖 Generated with Claude Code


Summary by cubic

Adds legacy-alias-endpoints, a one-off backfill that creates an explicit MCP endpoint for every address a hosted server's legacy toolset alias still serves and no live endpoint holds, so stored client URLs keep working once the alias is removed (prerequisite for AIM-670). The run defaults to a dry run and needs explicit confirmations plus a journal to apply.

Backfill

  • Addresses the alias's own slug plus the platform address a loose custom-domain alias serves, skipping deleted or non-canonical servers, private_only network mode, org drift, and missing metadata.
  • Every address goes through the regular hosted create checks and slug availability under the same locks; a rejected address is a skip and one owned elsewhere a conflict.
  • The unprefixed alias slug is grandfathered, while API creates still require the org slug prefix; endpoint updates check the prefix only when the address changes, so grandfathered endpoints can be re-saved.
  • The alias follows the new endpoints only when its own address can no longer serve, so it never leaves a pending, unverified, foreign, or ingress-rejected domain.
  • Writes audit rows without outbox fan-out, including a distinct toolset:move_legacy_alias event for each alias move, and reports a failed commit as commit_outcome_unknown instead of failing the rerun.

Advertised URLs

  • Toolset views, exported MCP metadata, and assistant connections now pick the hosted server's primary endpoint (endpointaddr.PrimaryEndpoint order: live custom domain first), matching the URL plugins already use instead of preferring the platform URL.

Written for commit ac8e3f1. Summary will update on new commits.

View guided diff Turn on auto-fix

daviddanialy and others added 5 commits October 28, 2026 16:02
…oints

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@daviddanialy
daviddanialy requested a review from a team as a code owner October 28, 2026 23:02
@changeset-bot

changeset-bot Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ac8e3f1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@linear-code

linear-code Bot commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

AIM-821

@daviddanialy

Copy link
Copy Markdown
Contributor Author

E2E on 12aa7ca650 (later commits: audit actor display name and a rebase), local lane; 17 alias fixtures across 6 orgs plus a 600-alias bulk org; tool run through its CLI.

  • Dry run matched the expected plan row by row (13 create, 1 already held, 1 conflict, 9 skips, 1 alias move) and wrote nothing. PASS
  • Apply guards refuse without -journal / confirm flags, with a wrong target or an existing journal. PASS
  • Apply: exactly 13 endpoints, journal pending→committed per server, audit rows by the system actor, no outbox rows, only the deleted-domain alias moved. PASS
  • All 14 URLs serve the same server with the same status afterwards, on this build and on main's (rolling deploy); fallback log gone for backfilled slugs. PASS
  • Rerun: 0 creates. PASS
  • API: unprefixed create still 422; unchanged-slug update of a grandfathered endpoint 200; unprefixed rename 422. PASS
  • Advertised URL uses the custom domain (toolsets.get, export); audit log renders the new rows. PASS
  • Rollback from the journal restores endpoints and alias moves. PASS
  • SIGINT and SIGKILL mid-run leave a reconcilable journal; rerun completes. PASS
  • NOT RUN: custom-domain Host requests (local env skips that middleware) and the assistant runtime URL (local identity conflict blocks the assistant turn).

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 22 files

This PR changes a database schema or migrates data. Ultrareviews find 2.4x more serious bugs than standard reviews. Comment @cubic-dev-ai ultrareview to run one.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread server/internal/assistants/queries.sql Resolved
Comment thread server/internal/mcpmetadata/impl.go Resolved
Comment thread server/internal/toolsets/queries.sql Resolved
Comment thread server/internal/assistants/queries.sql Resolved

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant