Skip to content

feat: derive hosted MCP server identity from the resolved endpoint instead of the toolset slug - #5919

Merged
daviddanialy merged 7 commits into
mainfrom
daviddanialy/aim-20-hosted-identity-by-endpoint
Sep 2, 2026
Merged

feat: derive hosted MCP server identity from the resolved endpoint instead of the toolset slug#5919
daviddanialy merged 7 commits into
mainfrom
daviddanialy/aim-20-hosted-identity-by-endpoint

Conversation

@daviddanialy

@daviddanialy daviddanialy commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

AIM-20

Summary

  • Well-known documents: for toolset-backed wrappers, the OAuth slug and resource URL key on the endpoint the request arrived at, not toolsets.mcp_slug (the assumption that the two are equal is gone, along with the 404-on-empty-mcp_slug guard). The external OAuth branch is selected by the wrapper (issuer absent) and keeps reading toolsets.external_oauth_server_id.
  • Session mint: usersessions mints for hosted servers by mcp_server_id with the iss claim built from the server's primary endpoint via a new mcpendpoints.PrimaryEndpoint helper (custom-domain endpoint first, domain-root aware; the legacy /x/mcp/{slug} shape only for servers with zero endpoints). The toolset_id arm stays but resolves to the wrapper when one exists — issuer-URN audience, wrapper-keyed RBAC — via a new GetMCPServerByToolsetID query; the legacy toolset binding is preserved when there is no wrapper. Goa design descriptions updated and regenerated.
  • Install page: wrapper visibility decides publicness, the wrapper issuer decides the security mode (the toolset's external OAuth reference is the only remaining toolset input), and the install URL comes from the resolved endpoint when the request routed through one.
  • Instances: hosted server URLs come from the wrapper's primary endpoint when one exists.
  • The OAuth handler family and remote-login slug cross-check were verified to already resolve by endpoint (dispatch on mcp_servers.user_session_issuer_id, cached challenge carrying the server reference) and are now pinned by tests: correct well-known documents at both addresses of a two-endpoint server, mint by mcp_server_id, wrapper-resolved mint by toolset_id, and wrapper-visibility-wins on the install page.

No production behavior changes on deploy: no toolset-backed mcp_servers rows exist yet, so the wrapper paths are dormant until the backfill populates them.

Motivation

A hosted (toolset-backed) MCP server is migrating to carry several mcp_endpoints addresses, none of which has to equal toolsets.mcp_slug. Every surface that derives the server's identity — OAuth issuer slug, protected-resource URL, install URL, mint target — must therefore derive it from the endpoint the request arrived at and the mcp_servers row behind it, with the audience on the issuer URN so one session covers all of a server's endpoints. Legacy no-wrapper paths remain until the fallback removal at the end of the migration.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TCzhsvUYz5VMieXYaQUC2Z


Summary by cubic

Implements AIM-20: hosted MCP servers now derive identity from the resolved endpoint and mcp_servers wrapper instead of toolsets.mcp_slug. Servers without a usable wrapper keep the legacy behavior, so production traffic remains unchanged until backfill.

Behavior

  • Well-known OAuth metadata uses the endpoint that received the request, including servers with multiple endpoints and external OAuth.
  • mcp_server_id minting supports issuer-gated hosted servers and derives iss from the primary addressable endpoint.
  • toolset_id minting uses the wrapper only when it is enabled, issuer-gated, and endpoint-backed; otherwise it uses the legacy toolset binding.
  • Install-page visibility and security mode use wrapper state, while instance and install URLs use the primary or resolved endpoint.
  • Primary endpoint lookup is organization-scoped, skips unavailable custom domains, and prefers domain-root, custom-domain, then platform endpoints, with age and ID tie-breakers.
  • Regenerated API docs and dashboard SDK types describe the wrapper-aware minting behavior.

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

Review in cubic

…et-backed wrappers by endpoint

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TCzhsvUYz5VMieXYaQUC2Z
@daviddanialy
daviddanialy requested a review from a team as a code owner August 31, 2026 22:07
@daviddanialy daviddanialy added enhancement New feature or request go Pull requests that update go code labels Aug 31, 2026
@linear-code

linear-code Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

AIM-20

@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: db57db6

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

This PR includes changesets to release 2 packages
Name Type
server Patch
dashboard 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

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Running ultrareview automatically — Cross-cutting OAuth/session-mint/install-page identity resolution rework touches auth, JWT audience/issuer, and visibility gating across many paths, with behavior changes for any endpoint-backed server — a missed bug could break OAuth flows or expose private installs.. I'll post findings when complete.

@blacksmith-sh

This comment has been minimized.

@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.

Ultrareview completed in 8m 49s

All reported issues were addressed across 18 files

Linked issue analysis

Linked issue: AIM-20: feat: OAuth, well-known, install page, and session mint resolve toolset-backed wrappers by endpoint

Status Acceptance criteria Notes
Well-known OAuth authorization-server and protected-resource documents derive their slug and resource URL from the endpoint receiving the request, including when a server has multiple endpoints. The well-known handler now uses the resolved endpoint slug, and tests exercise both documents for two endpoints.
Issuer-gated OAuth handlers and remote-login challenge state resolve hosted wrappers through the mcp_servers reference and cross-check against the endpoint slug. The PR states the handler family dispatches on the wrapper issuer and cached challenges carry the server reference; the remote-login cross-check is described as endpoint-based and pinned by tests.
Session mint accepts mcp_server_id for hosted issuer-gated servers, uses the issuer URN audience, and derives the issuer URL from the server's primary endpoint; toolset_id resolves to the wrapper when present. Mint handling shares a wrapper-aware server target, selects a primary endpoint, preserves the legacy no-wrapper fallback, and includes tests for direct server and toolset-addressed minting.
Install pages for wrapper-backed hosted servers use wrapper visibility, wrapper issuer security, and the resolved endpoint for the install URL. The install context now gives wrapper state precedence and resolves the URL from the endpoint; tests cover both public and private wrapper visibility.
Hosted instance MCP URLs use the wrapper's primary endpoint when available, while legacy toolset-derived URLs remain available without a wrapper. Instance URL resolution now selects the wrapper primary endpoint and falls back to the legacy toolset columns when no wrapper or endpoint exists.

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread server/internal/instances/impl.go Outdated
Comment thread server/internal/mcpservers/queries.sql
Comment thread server/internal/usersessions/minthandler.go
Comment thread server/internal/usersessions/minthandler.go Outdated
Comment thread server/internal/instances/impl.go Outdated
Comment thread server/design/usersessions/design.go
Comment thread server/internal/mcpendpoints/primary.go Outdated
Comment thread server/internal/mcpendpoints/primary.go Outdated
@daviddanialy
daviddanialy requested a review from a team as a code owner August 31, 2026 22:50

@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 17 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread server/internal/mcpmetadata/impl.go
@daviddanialy

Copy link
Copy Markdown
Contributor Author

End-to-end verification (this PR's changes)

Context: all three Phase-1 branches (#5917, #5918, #5919) were merged into a scratch tree (zero conflicts), the full server suite ran on the merged tree — 14,334 tests, 1 failure: a pre-existing skills-subsystem clock flake, passes in isolation, unrelated to these PRs — and a local stack was booted from the merged tree with simulated backfill rows (a toolset-backed mcp_servers wrapper + mcp_endpoints row whose slug deliberately differs from the toolset's mcp_slug) to exercise the dormant paths over real HTTP.

Scenarios exercising this PR:

Scenario Result Evidence
Well-known identity keyed on the arrival endpoint PASS issuer-gated wrapper: protected-resource and authorization-server docs carry the endpoint slug in resource, issuer, and every OAuth endpoint URL — never the toolset's mcp_slug
No identity leak on the legacy slug PASS well-known doc for the toolset's mcp_slug stays 404 while the endpoint's doc serves
Install page resolved via endpoint PASS 200, correct server title, endpoint slug rendered throughout the page

No panics or unexpected errors in server logs. All test rows and flag changes were reverted after the run (verified by query).

🤖 Generated with Claude Code

@daviddanialy daviddanialy changed the title feat: OAuth, well-known, install page, and session mint resolve toolset-backed wrappers by endpoint feat: derive hosted MCP server identity from the resolved endpoint instead of the toolset slug Sep 2, 2026

@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 2 files (changes from recent commits).

Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread server/internal/usersessions/minthandler_server_test.go Outdated

@bflad bflad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems okay.

Comment thread server/internal/mcpendpoints/primary.go
Comment thread server/internal/mcpendpoints/primary.go Outdated
Comment thread server/internal/mcpmetadata/impl.go Outdated
Comment thread server/internal/mcpservers/queries.sql
daviddanialy and others added 2 commits September 2, 2026 11:48
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@daviddanialy
daviddanialy added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit 54fdd25 Sep 2, 2026
52 of 53 checks passed
@daviddanialy
daviddanialy deleted the daviddanialy/aim-20-hosted-identity-by-endpoint branch September 2, 2026 19:31
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

enhancement New feature or request go Pull requests that update go code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants