Skip to content

MCP Cutting-Edge State of the Art — April 2026

MCP Cutting-Edge State of the Art — April 2026

Research date: 2026-04-24 Target: Ascend GTM V5 Gateway (Cloudflare Workers + Hono, ~28 tools) Goal: 100% cloud-native MCP architecture. Zero local MCP processes. Research discipline: All claims drawn from live docs or live-API calls. No training-data assertions.


Executive Summary — The 2026-grade MCP architecture for Ascend

  1. Current spec is 2025-11-25. modelcontextprotocol.io/specification/latest renders this version. Only two transports survive: stdio and Streamable HTTP. The old HTTP+SSE transport from 2024-11-05 is formally deprecated — new servers MUST NOT implement it, though backward-compat mode is allowed. (transports spec)
  2. Empirical registry proof of the shift: of the 216 commercial servers in Anthropic’s registry (live, enumerated today), 211 use streamable-http, 4 still use sse (Plaid, WordPress preview, Buildkite, one other), 1 has no transport listed (broken/stale). SSE is a rounding error in 2026.
  3. OAuth 2.1 + RFC 9728 Protected Resource Metadata is the standard auth floor. MCP clients MUST implement resource indicators (RFC 8707), PKCE with S256, and parse WWW-Authenticate resource_metadata challenges. Client-ID Metadata Documents (CIMD) are now the preferred registration mode over full DCR. (authorization spec)
  4. Ascend’s V5 gateway (28 tools, authenticated via internal bearer) is architecturally a domain-specific MCP server — we are one https://ascend-gateway-v5.ascendgtm.workers.dev/mcp endpoint away from being a first-class remote MCP. The registry proves every serious vendor is converging on https://mcp.<vendor>/mcp as the canonical path.
  5. Cloudflare has shipped the gateway/federation layer nativelyMCP Server Portals in Cloudflare One present N upstream MCPs behind one URL, with Access-enforced identity and Code Mode (token-compression via portal_codemode_search + portal_codemode_execute, reported 94% token reduction in CF’s internal deployment). (mcp-portals docs, blog)
  6. MCP Elicitation (form + url modes) landed in the 2025-11-25 spec — this is the “AskUserQuestion over MCP” primitive and is mandatory for sensitive-data flows (URL mode MUST be used for secrets; form mode MUST NOT request passwords/API keys). (elicitation spec)
  7. Private-visibility registration in Anthropic’s registry is not a user-facing feature today — the visibility field on each server record is ["commercial"] in the public feed; no public API accepts a visibility: private submission. The documented enterprise pattern is: run your own mirror/portal, not “hide a server in Anthropic’s registry”. Cloudflare MCP Server Portals is the sanctioned implementation path.
  8. Recommended 2026 Ascend architecture:
    • V5 Gateway exposes a native Streamable HTTP MCP endpoint at /mcp with OAuth 2.1 (via @cloudflare/workers-oauth-provider), gated per-client via McpAgent Durable Objects.
    • V5 publishes Protected Resource Metadata at /.well-known/oauth-protected-resource.
    • For multi-tenant federation (Ascend + client-owned hosted MCPs like HubSpot, Attio, Klaviyo), stand up a Cloudflare Access MCP Server Portal — one URL per tenant, Access policies bind to Google/Microsoft SSO, audit flows into Cloudflare Logpush + D1 for agent-queryable history.
    • Registry presence: publish to the public Anthropic MCP Registry as net.ascendgtm/gateway once we’re ready for commercial listing; until then, distribute the claude mcp add --transport http ascend https://ascend-gateway-v5.ascendgtm.workers.dev/mcp one-liner to each user, which is what every commercial vendor in the registry currently does.

Top-5 adoption priorities (detailed in final section): (1) publish Streamable HTTP /mcp endpoint with OAuth 2.1, (2) wire OAuthProvider + McpAgent + OAUTH_KV, (3) build Elicitation flows for secret/OAuth onboarding, (4) front client-facing consumption with Cloudflare MCP Server Portals + AI Gateway, (5) submit to Anthropic MCP Registry with CIMD-style client config.


1. MCP Transport Evolution — SSE is dead, Streamable HTTP is the only choice

Current spec

  • Spec version (latest): 2025-11-25. Source: modelcontextprotocol.io/specification/latest references schema/2025-11-25/schema.ts in the repo.
  • Defined transports (exhaustive): stdio and Streamable HTTP. No others. (transports spec)
  • HTTP+SSE (2024-11-05) is deprecated. Spec says: “This replaces the HTTP+SSE transport from protocol version 2024-11-05.” Backwards-compat section defines a probe sequence for clients who need to speak to both-era servers — but new servers should only implement Streamable HTTP.

Streamable HTTP — what a server MUST do

  • Expose one endpoint (e.g. /mcp) accepting both POST and GET.
  • On POST: body is a JSON-RPC request/notification/response. Response either application/json (one-shot) or text/event-stream (server-initiated SSE stream keyed to this request).
  • On GET: optionally open SSE for server→client notifications. Return 405 if unsupported.
  • Include MCP-Protocol-Version: 2025-11-25 header validation — reject mismatches with 400.
  • Optional session state: issue MCP-Session-Id on InitializeResult, require it on subsequent requests, return 404 to terminate.
  • Support Last-Event-ID resumption. Event IDs MUST be globally unique per-session and encode the originating stream.
  • Security mandate: validate Origin header on every request (DNS rebinding defense) — 403 if invalid.

Registry evidence (live-enumerated today)

Live query: curl https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial&limit=100 paginated through nextCursor:

TransportCount (of 216 commercial servers)
streamable-http211
sse4
(missing remotes)1

The 4 remaining SSE holdouts include Plaid Developer Tools (https://api.dashboard.plaid.com/mcp/sse) — these are laggards and will migrate. SSE is effectively dead.

Recommendation for V5

  • Ship Streamable HTTP at /mcp. Do NOT add an SSE transport.
  • Reuse existing Hono routing — mount /mcp via createMcpHandler() (stateless) or McpAgent DO-backed (stateful, needed for Elicitation state).
  • Add MCP-Protocol-Version header validation middleware.
  • Migration from V5’s current “mcp__ascend-gateway__*” bearer-token pseudo-MCP (STDIO over Claude Desktop’s proxy) to real remote MCP is largely a Hono-handler swap — the 28 tool implementations don’t change.

2. OAuth 2.1 Pattern — the 2026 authentication standard

Source: specification/2025-11-25/basic/authorization

The MUST list (non-negotiable for any remote MCP server)

RequirementRFCWhat it means for V5
OAuth 2.1 (draft-ietf-oauth-v2-1-13)draft-ietf-oauth-v2-1-13No implicit grant, no ROPC. Auth code + PKCE only.
PKCE with S256OAuth 2.1 §7.5.2Required; clients MUST refuse servers that don’t advertise code_challenge_methods_supported
RFC 9728 Protected Resource MetadataRFC 9728V5 MUST serve /.well-known/oauth-protected-resource
RFC 8707 Resource IndicatorsRFC 8707Clients MUST send resource=https://ascend-gateway-v5.ascendgtm.workers.dev/mcp in auth+token requests
RFC 7591 DCR (optional, legacy)RFC 7591Included for back-compat; CIMD is preferred
Client ID Metadata Documentsdraft-ietf-oauth-client-id-metadata-document-00Modern pattern — client hosts metadata at HTTPS URL, URL = client_id
RFC 8414 Auth Server MetadataRFC 8414Serve /.well-known/oauth-authorization-server

Discovery flow (what happens on an unauthenticated call)

  1. Client calls POST https://.../mcp without a token.
  2. Server replies 401 Unauthorized with:
    WWW-Authenticate: Bearer resource_metadata="https://ascend-gateway-v5.ascendgtm.workers.dev/.well-known/oauth-protected-resource",
    scope="gtm:read gtm:write"
  3. Client fetches the resource_metadata doc, learns which auth server(s) handle this resource.
  4. Client fetches /.well-known/oauth-authorization-server on each candidate AS, picks one, reads authorization_endpoint, token_endpoint, code_challenge_methods_supported, registration_endpoint, client_id_metadata_document_supported.
  5. Client registers: prefer pre-registered static creds > CIMD > DCR.
  6. Client generates PKCE verifier/challenge, opens browser with auth URL (including resource= param), catches callback, exchanges code + verifier + resource for access token.
  7. All subsequent MCP calls send Authorization: Bearer <access-token>. Tokens MUST NOT appear in URL query strings.

Scope strategy

  • Scopes MUST follow least-privilege; clients use the scope= in the initial 401 WWW-Authenticate header, or fall back to scopes_supported from the PRM doc.
  • Step-up: runtime tools that need more permission return 403 Forbidden with error="insufficient_scope" and a fresh scope= challenge. Client re-authorizes, retries.

Token binding (critical — this is where many servers get owned)

  • V5 MUST validate the token audience matches its canonical URI (RFC 8707).
  • V5 MUST NOT pass its inbound client token through to downstream APIs (HubSpot, Salesforce, etc.). That’s “token passthrough” and is a known confused-deputy vuln. V5 already does this right (per-tenant stored tokens for each upstream); keep it that way.

Cloudflare OAuthProvider wrapper — the canonical implementation

The @cloudflare/workers-oauth-provider package is a Worker-layer middleware that handles:

  • /.well-known/oauth-authorization-server auto-publication
  • /authorize, /token, /register (DCR) endpoints
  • PKCE verification
  • Token storage in the KV namespace bound as OAUTH_KV
  • Delegation to a user-supplied defaultHandler (GitHub/Google/Auth0/etc upstream IdP)

Reference implementation: github.com/cloudflare/ai/tree/main/demos/remote-mcp-authless. Canonical guide: developers.cloudflare.com/agents/guides/remote-mcp-server/.

Recommendation for V5

  • Wrap current Hono app in OAuthProvider pinning Google as the upstream IdP (every Ascend client already has Google Workspace).
  • Serve PRM + ASM well-known docs from the root Worker.
  • Scope set: gtm:read, gtm:write, gtm:admin, plus per-domain scopes (crm:read, ads:write, etc.) matching the V5 domain handlers.
  • Keep a static pre-registered client_id for claude.ai/desktop/code (published in our setup docs); accept DCR + CIMD for everyone else.

3. Anthropic MCP Registry — live enumeration

Endpoint: https://api.anthropic.com/mcp-registry/v0/servers?version=latest&visibility=commercial&limit=100 (paginate via nextCursor). 216 commercial servers on 2026-04-24.

Each server record carries two metadata blocks:

  • _meta."io.modelcontextprotocol.registry/official" — upstream official-registry fields (status, publishedAt, isLatest)
  • _meta."com.anthropic.api/mcp-registry" — Anthropic-specific: displayName, iconUrl, claudeCodeCopyText, worksWith (which Anthropic surfaces: claude, claude-api, claude-code, claude-desktop), toolNames (exhaustive tool manifest), visibility array (currently always ["commercial"]), useCases array.

GTM-relevant hosted MCP servers (filtered to B2B-GTM / marketing-agency use cases)

CategoryServerURLTransportToolsworksWith
CRMApollo.iohttps://mcp.apollo.io/mcpstreamable-http13claude
CRMAttiohttps://mcp.attio.com/mcpstreamable-http23claude, claude-api, claude-code
CRMClosehttps://mcp.close.com/mcpstreamable-http55claude, claude-api
CRMHubSpothttps://mcp.hubspot.com/anthropicstreamable-http5claude, claude-api
CRMZoho CRM(templated)streamable-http41claude, claude-api, claude-code
Analytics/BIAmplitudehttps://mcp.amplitude.com/mcpstreamable-http14claude, claude-code
Analytics/BIMixpanelhttps://mcp.mixpanel.com/mcpstreamable-http20claude, claude-api, claude-code
Analytics/BIPostHoghttps://mcp.posthog.com/mcpstreamable-http57claude, claude-code
Analytics/BIWindsor.aihttps://mcp.windsor.aistreamable-http5claude, claude-api, claude-code
Analytics/BIOmni Analyticshttps://callbacks.omniapp.co/callback/mcpstreamable-http3claude, claude-api, claude-code
SEO/Content intelAhrefshttps://api.ahrefs.com/mcp/mcpstreamable-http61claude, claude-api, claude-code
SEO/Content intelExahttps://mcp.exa.ai/mcpstreamable-http2claude, claude-api, claude-code
SEO/Content intelTavilyhttps://mcp.tavily.com/mcpstreamable-http5claude, claude-api, claude-code
Email/LifecycleKlaviyohttps://mcp.klaviyo.com/mcp?include-mcp-app=truestreamable-http27claude, claude-api, claude-code
Email/LifecycleIntuit Mailchimphttps://ai-inc.mailchimp.com/claude/mcp/v2streamable-http12claude, claude-api
Content/CMSNotionhttps://mcp.notion.com/mcpstreamable-http13claude, claude-api, claude-code
Content/CMSSanityhttps://mcp.sanity.iostreamable-http31claude, claude-api, claude-code
Content/CMSWebflowhttps://mcp.webflow.com/mcpstreamable-http19claude, claude-api, claude-code
Content/CMSWordPress.comhttps://public-api.wordpress.com/wpcom/v2/mcp/v1streamable-http16claude, claude-api, claude-code
DesignCanvahttps://mcp.canva.com/mcpstreamable-http17claude, claude-api, claude-code
DesignFigmahttps://mcp.figma.com/mcpstreamable-http9claude, claude-api, claude-code
DesignLucidhttps://mcp.lucid.app/mcpstreamable-http11claude, claude-api, claude-code
Data/WarehouseSupabasehttps://mcp.supabase.com/mcpstreamable-http32claude, claude-api, claude-code
Data/WarehousePlanetScalehttps://mcp.pscale.dev/mcp/planetscalestreamable-http15claude, claude-code, claude-desktop
Data/WarehouseSnowflake(templated)streamable-http2claude, claude-api, claude-code
Data/WarehouseDatabricks(templated)streamable-http3claude, claude-api, claude-code
Data/WarehouseGoogle BigQueryhttps://bigquery.googleapis.com/mcpstreamable-http5claude, claude-api, claude-code
Data/WarehouseAirtablehttps://mcp.airtable.com/mcpstreamable-http12claude, claude-api
PM/DocsLinearhttps://mcp.linear.app/mcpstreamable-http22claude, claude-api, claude-code
PM/DocsAtlassian Rovohttps://mcp.atlassian.com/v1/mcpstreamable-http31claude, claude-code
PM/DocsAsanahttps://mcp.asana.com/v2/mcpstreamable-http15claude, claude-code
PM/Docsmonday.comhttps://mcp.monday.com/mcpstreamable-http20claude, claude-api, claude-code
PM/DocsClickUphttps://mcp.clickup.com/mcpstreamable-http35claude, claude-api, claude-code
Payments/BillingStripehttps://mcp.stripe.comstreamable-http23claude, claude-api, claude-code
Payments/BillingIntuit QuickBookshttps://ai-inc.quickbooks.intuit.com/v1/mcpstreamable-http11claude, claude-api
StorageBoxhttps://mcp.box.comstreamable-http20claude, claude-api, claude-code
CommsSlackhttps://mcp.slack.com/mcpstreamable-http11claude, claude-api, claude-code
CommsIntercomhttps://mcp.intercom.com/mcpstreamable-http6claude, claude-api, claude-code
CommsZoomhttps://mcp.zoom.us/mcp/zoom/streamablestreamable-http5claude, claude-api
CommsZoomInfohttps://mcp.zoominfo.com/mcpstreamable-http5claude, claude-api, claude-code
CommsGranolahttps://mcp.granola.ai/mcpstreamable-http4claude, claude-api, claude-code

Absent / missing today (vendors V5 currently proxies via its own handlers, but NO hosted MCP exists for):

  • Google Ads — no official MCP as of 2026-04-24 registry snapshot. V5’s google_ads_query tool is the only clean access path.
  • Meta Ads, LinkedIn Ads, TikTok Ads, Microsoft Ads — same. V5 remains the canonical proxy.
  • Salesforce — not in Anthropic commercial registry today (Salesforce’s slack-by-salesforce Slack MCP is listed, but no Salesforce-CRM MCP). V5’s salesforce_crm + salesforce_query remain essential.
  • GA4, Google Search Console, Gmail, Google Calendar — no Google-branded MCPs in the commercial registry. V5’s ga4_report, gsc_performance, gmail, google_calendar tools are still the only paved path.
  • SEMrush — no hosted MCP. V5’s semrush_research is it.
  • Gong — no hosted MCP. V5 owns this.
  • DealCloud, AWS Bedrock/SES/Textract — V5-only.

Implication: V5 is not being made redundant by the hosted-MCP ecosystem — it fills a clear gap for ad platforms, Google services (GA4/GSC), Salesforce, SEMrush, Gong, DealCloud, and AWS primitives. These 10+ gaps are V5’s moat.


4. Private Registry Entries — What’s actually possible in April 2026

The reality check

  • The Anthropic registry API accepts only visibility=commercial queries today. The visibility field is an ARRAY on the server record but only "commercial" appears in live data. There is no documented public submission API for visibility: private.
  • Anthropic’s registry is a mirror + curation layer over the community-driven official registry at registry.modelcontextprotocol.io / github.com/modelcontextprotocol/registry (Anthropic, PulseMCP, GitHub, Stacklok maintainers). Namespaces are reverse-DNS (io.github.user/server, net.ascendgtm/gateway), authenticated via GitHub OAuth, DNS challenge, or HTTP challenge.
  • Industry practice (per Gentoro, dxrf.com): Enterprises don’t publish private servers in the public registry — they run private mirrors or internal portals. Cloudflare MCP Server Portals is the sanctioned way to do this (see §6, §7).

Paved paths for “private MCP my org auto-discovers”

PathWhat it isWhen to use
Cloudflare MCP Server PortalsOne HTTPS URL per tenant fronting N upstream MCPs; Cloudflare Access enforces SSO + per-tool policy. Your team configures this URL once in Claude Code / claude.ai.Best fit for Ascend tenant isolation.
claude mcp add --transport http one-linerDistribute the raw mcp.ascendgtm.net/mcp URL + setup instructions; clients auto-OAuth via your PRM/ASM docs.Works today with zero registry involvement.
Self-hosted mirror of registry.modelcontextprotocol.ioFork the registry, host internally, configure Claude Code’s registry URL via settings (feature exists for enterprise).Heavyweight — only if we ship many private MCPs to many internal teams.
Anthropic visibility: commercial listingShip the V5 gateway as a publicly listed MCP in the Anthropic registry. Requires submission flow (maintainer-approved).Future — when V5 is multi-tenant SaaS-ready.

Recommendation for V5

  • Do not wait for private visibility. The CF Portal + one-liner pattern is what Cloudflare, Intercom, Klaviyo, and the 211 other commercial servers use: publish a stable URL, document the claude mcp add command, let OAuth handle identity.
  • Plan for public Anthropic listing once V5 is ready for public commercial use. Name: net.ascendgtm/gateway. Verification: DNS challenge on ascendgtm.net.
  • Stand up a Cloudflare MCP Portal per paying client tenant — each client gets their own URL like https://<client>.mcp.ascendgtm.net/mcp fronting (a) V5 gateway, (b) their own HubSpot/Attio/Klaviyo MCPs federated behind Access.

5. MCP Server Best Practices 2026

Synthesis from modelcontextprotocol.io/specification/2025-11-25, Cloudflare’s enterprise reference architecture, and registry observations.

Auth

  • OAuth 2.1 + RFC 9728 PRM + RFC 8707 resource indicators. Non-negotiable.
  • Short-lived access tokens. Rotate refresh tokens on every use (OAuth 2.1 §4.3.1 requires this for public clients).
  • Validate audience claim. Never accept a token whose aud isn’t your canonical server URI.
  • No token passthrough. Server acts as an OAuth client to upstream APIs with separate tokens bound to user identity.

Tool design (per Cloudflare’s MCP on CF overview)

  • “Create focused tools targeting specific user goals rather than wrapping entire API schemas.” A tool called crm_find_decision_maker > 15 tools wrapping HubSpot endpoints.
  • Write detailed JSON Schema descriptions — that’s the prompt the model sees.
  • Keep tool count per server bounded (Anthropic context-window reality: 50+ tools per server forces Code Mode or progressive disclosure).
  • Ship evaluation tests. Anthropic’s registry implicitly enforces this: servers with broken tools get de-listed.

Batch operations

  • No MCP-native batch primitive in spec 2025-11-25. Pattern: implement a batch_execute tool (V5 already has this) that takes an array of tool-call specs and returns parallel results. Ascend V5’s batch_execute is ahead of the ecosystem here.
  • Tasks primitive (SEP-1686) provides async call-now / fetch-later semantics — this is the emerging alternative. Agents WG is closing retry/expiry gaps in the next spec cycle.

Streaming responses

  • Streamable HTTP supports server→client SSE events per request. Use for long-running tool outputs (Claude invocations, large query results). Event IDs enable resume.
  • Not yet standardized: partial-result streaming as first-class. Roadmap item.

Rate limiting

  • Per-session: use MCP-Session-Id as the rate-limit key.
  • Per-client: OAuth client_id (or CIMD URL).
  • Per-user: sub claim from validated access token.
  • Return 429 with Retry-After header — not in MCP spec but standard HTTP.

Versioning

  • MCP-Protocol-Version header on every HTTP request. Reject unsupported versions with 400.
  • Server version field in the registry record is YOUR server’s version, independent of spec version.
  • Keep semver on tool signatures — breaking changes require new tool names.

Deprecation

  • Anthropic registry uses the status field (active, deprecated, deleted) under _meta."io.modelcontextprotocol.registry/official".status.
  • In-band deprecation: include a warning field in tool result _meta or a notifications/deprecation (not yet standardized; emerging).

Security

  • Origin header validation (DNS rebinding).
  • HTTPS only (except localhost dev).
  • No form-mode elicitation for secrets — URL mode only (§9).
  • SSRF protection when fetching CIMD metadata docs.
  • Per-tenant data isolation: session state MUST be bound to OAuth sub, not just session ID.

6. Cloudflare Workers as MCP Server Host — canonical pattern

Source: developers.cloudflare.com/agents/guides/remote-mcp-server/, agents/model-context-protocol/transport/, remote-mcp-authless demo.

Package inventory

PackagePurpose
agentsCloudflare Agents SDK — provides McpAgent DO class, createMcpHandler() stateless factory
@cloudflare/workers-oauth-providerOAuth 2.1 AS implementation; wraps your Worker fetch handler
@modelcontextprotocol/sdkOfficial MCP TypeScript SDK — server + transport primitives
@modelcontextprotocol/inspectorDev tool for testing MCP servers
mcp-remoteLocal proxy for bridging stdio clients to remote Streamable HTTP (legacy bridge)

Implementation pattern options

  1. createMcpHandler() — stateless, no DOs. Fine for 0-state tools. Cannot do Elicitation state.
  2. McpAgent (Durable Object-backed) — stateful. REQUIRED for Elicitation URL mode (server must persist elicitationId → user binding), sessions, resumption.
  3. Raw StreamableHTTPServerTransport — direct SDK; maximum control. What Hono-based servers pick if they want fine-grained routing.

wrangler.jsonc example (canonical shape)

{
"name": "ascend-gateway-v5",
"main": "src/index.ts",
"compatibility_date": "2026-04-01",
"compatibility_flags": ["nodejs_compat"],
"kv_namespaces": [
{ "binding": "OAUTH_KV", "id": "<namespace-id>" }
],
"durable_objects": {
"bindings": [
{ "name": "MCP_AGENT", "class_name": "AscendMcpAgent" }
]
},
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["AscendMcpAgent"] }
],
"d1_databases": [
{ "binding": "AUDIT_DB", "database_name": "ascend-audit", "database_id": "<id>" }
],
"analytics_engine_datasets": [
{ "binding": "TELEMETRY", "dataset": "ascend_mcp_events" }
],
"ai": { "binding": "AI" },
"observability": { "enabled": true }
}

Worker entry — OAuthProvider wrap

import { OAuthProvider } from "@cloudflare/workers-oauth-provider";
import { McpAgent } from "agents/mcp";
import { Hono } from "hono";
export class AscendMcpAgent extends McpAgent<Env, State> {
server = new McpServer({ name: "ascend-gateway", version: "5.2.0" });
async init() {
this.server.tool("hubspot_crm", { /* input schema */ }, async (args) => { ... });
// ...28 tools
}
}
const app = new Hono<{ Bindings: Env }>();
app.get("/health", (c) => c.json({ ok: true }));
// ...existing Hono routes...
export default new OAuthProvider({
apiRoute: "/mcp",
apiHandler: AscendMcpAgent.mount("/mcp"),
defaultHandler: app, // non-/mcp traffic hits Hono
authorizeEndpoint: "/authorize",
tokenEndpoint: "/token",
clientRegistrationEndpoint: "/register",
});

Session state + audit

  • KV OAUTH_KV: OAuthProvider stores client registrations, auth codes, access/refresh tokens, PKCE state. Already bound in wrangler.
  • KV hot-path tenant config: reuse V5’s existing tenant KV namespace.
  • DO state: per-session MCP state lives in AscendMcpAgent instance; idFromName(userId) guarantees one DO per authenticated user.
  • D1 AUDIT_DB: write one row per tool call: {ts, user_sub, client_id, session_id, tool, args_hash, latency_ms, status}. Queryable by agents (via a gateway_audit_query tool) and by Logpush.
  • Analytics Engine TELEMETRY: high-cardinality event firehose — sampled, cheap, queryable via Workers Analytics API.

Authless vs authenticated

  • Authless (isAuthless: true in registry) = no OAuth. Only use for public read-only utilities (tldraw, mermaid, GraphOS docs). NOT appropriate for V5.
  • Authenticated = OAuth 2.1 + PRM + PKCE. V5 must go here.

Recommendation for V5

  • Wrap current Hono app with OAuthProvider as above.
  • Promote McpAgent DO in front of Hono for tool routing (keeps existing Hono handlers as the “resolver” layer).
  • Bind D1 ascend-audit for per-call provenance (this is also the answer to §8 observability).
  • Compatibility date: 2026-04-01 or later for the latest OAuthProvider features.

7. Federation / MCP Gateway Patterns

What the ecosystem has converged on (Q1-Q2 2026)

Per Hey It Works — MCP Aggregation Gateway Proxy State of Ecosystem Q1 2026 and Cloudflare’s enterprise MCP blog:

  • No OSS tool yet provides the full matrix of: 3-level hierarchy, 1:many endpoint-namespace mapping, nested federation, per-client tool visibility, self-host simplicity.
  • The production-proven pattern is MCP Server Portals (Cloudflare One): central HTTPS URL fronting N upstream MCPs, Cloudflare Access identity, per-tool policies, audit via Access logs + optional Gateway HTTP logging + Logpush.

Cloudflare MCP Server Portals — how it works

Source: developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/mcp-portals/

  1. Register each upstream MCP under Zero Trust → Access → AI controls → MCP servers (supports authless or OAuth-protected).
  2. Create a portal at https://<sub>.<domain>/mcp with custom domain.
  3. Policy: bind Access identity (Google/Microsoft/Okta SSO) + per-server tool allowlists.
  4. Clients configure one URL. Access mediates OAuth to each upstream server separately (or uses admin credentials when Require user auth is off).
  5. Audit: Access logs every tool call; optional Gateway inspection adds DLP + HTTP logging; Logpush exports everything.

Code Mode — token compression (huge for agent contexts)

Per CF enterprise blog: enabling ?codemode=search_and_execute on the portal URL collapses all upstream MCP tools into just 2 tools:

  • portal_codemode_search — returns the tool definitions (on-demand, paginated)
  • portal_codemode_execute — invokes any upstream tool by name

Cloudflare’s internal portal (13 MCPs, 182 tools across Backstage, GitLab, Jira, Sentry, Elasticsearch, Prometheus, Google Workspace, more) reports 94% token reduction vs eager tool loading.

Pattern for Ascend

V5 has 28 tools today. Individual clients (e.g. Point Field Partners, Kahuna) will plug in 3-8 more hosted MCPs each (HubSpot, Attio, Klaviyo, Linear, etc.). Total tool count per tenant converges on 50-80. Without Code Mode, that’s ~15k tokens of tool definitions alone — before a single user message.

Recommendation:

  1. V5 stays a single MCP server. Don’t fragment it into 6 per-domain MCPs — domain handlers are an implementation detail, not a federation boundary.
  2. Front each tenant with a Cloudflare MCP Server Portal fronting: (a) V5 gateway, (b) tenant’s hosted 3rd-party MCPs. Turn on Code Mode when tool count exceeds ~30.
  3. Audit flows into Access logs → Logpush → D1 + R2. This gives us a queryable history of every tool call per user per tenant, end-to-end.
  4. Progressive tool disclosure — use MCP tools/list_changed notifications to hide tools the user doesn’t have scope for. Spec-native way to do this.

8. Telemetry + Observability for MCP

Primitives available on Cloudflare (by purpose)

PrimitivePurposeURL
Workers Logs + Tail WorkersRequest-level structured logs; streamed to a logging Workerworkers/observability/logs/workers-logs
Analytics EngineHigh-cardinality event firehose; SQL-queryable; cheap; 90d retentionanalytics/analytics-engine
Workers Analytics Engine + SQL APIQuery historical events; dashboardssame
D1 Audit TableTool-call-grain provenance: ts, user, client, tool, args_hash, result_hash, latency, statusV5-owned
AI GatewayWraps LLM + MCP traffic; rate limiting, caching, logging, per-user token quotasai-gateway
LogpushExport Access + Gateway + Workers logs to R2 / Splunk / Datadog / S3logpush
OpenTelemetryWorkers has OTEL support via Baselime / Axiom / Honeycomb integrations; full W3C trace context propagationworkers/observability/third-party-tools

On every tool call, emit:

  1. Analytics Engine event{blob: tool_name, user_sub, tenant, client_id, session_id, status, error_code, tags: [...]}, double: [latency_ms, input_bytes, output_bytes]. Queryable firehose.
  2. D1 audit row — authoritative record for compliance + agent-queryable history.
  3. Workers Log line — structured JSON line. Tailed by Logpush → R2. Agent can query via the tail worker.
  4. AI Gateway pass-through (optional) — if V5 is routing calls to Claude/Bedrock, wrap those in AI Gateway for free caching + per-user token quotas.
  5. OpenTelemetry trace (optional for paying-tier tenants) — emit spans with mcp.tool.name, mcp.client.id, mcp.user.sub attributes, propagate W3C traceparent to downstream APIs.

Recommendation for V5

  • Bind Analytics Engine (TELEMETRY dataset) + D1 (ascend-audit) today.
  • Expose a V5 tool gateway_audit_query that lets agents query their own call history — closes the “what did I just do?” gap.
  • Defer full OpenTelemetry until we have a customer who pays for it; Analytics Engine + Logpush is enough for 2026 Q2 needs.
  • Front user-facing LLM calls with AI Gateway; this gives us retries, rate limits, and cache for free.

9. MCP Elicitation Spec — the “AskUserQuestion” primitive

Source: modelcontextprotocol.io/specification/2025-11-25/client/elicitation

Two modes

ModePurposeServer MUST/MUST NOT
Form modeStructured non-sensitive data from user (name, date, enum choice)MUST NOT request passwords, API keys, tokens, payment creds. Data passes through client.
URL modeRedirect user to external HTTPS URL for sensitive flows (secret entry, OAuth consent, payment)MUST use for anything sensitive. Data does NOT pass through client. Client never sees the secret.

Form mode — schema is restricted

  • Flat objects only. No nested structures. No arrays-of-objects (except enum multi-select).
  • Primitive types: string (with format: email|uri|date|date-time), number, integer, boolean, enum.
  • Single-select + multi-select enums supported, with optional titles via oneOf/anyOf.
  • Optional default values. Clients SHOULD pre-populate.

URL mode — the OAuth-to-third-party-service primitive

This is the clean answer to “how does an MCP server initiate a 3rd-party OAuth flow without leaking tokens through the client?”:

  1. Server generates elicitationId (UUID), stores binding to user sub.
  2. Server sends elicitation/create with mode: "url", url: "https://mcp.example.com/connect?elicitationId=...", message.
  3. Client shows the URL to the user, gets consent, opens it in a secure browser (NOT WebView — use SFSafariViewController on iOS per spec).
  4. User completes OAuth at the 3rd-party AS; 3rd-party redirects back to the MCP server.
  5. Server verifies the authorized user matches the elicitationId’s stored sub (phishing defense — critical).
  6. Server stores 3rd-party tokens bound to user identity.
  7. Server MAY send notifications/elicitation/complete with the elicitationId so the client can retry automatically.

URLElicitationRequiredError (code -32042)

New error returned from a tool call when the tool needs an elicitation first:

{"jsonrpc":"2.0","id":2,"error":{"code":-32042,"message":"Authorization required",
"data":{"elicitations":[{"mode":"url","elicitationId":"...","url":"https://...","message":"..."}]}}}

Client presents the URL, waits for completion, retries original tool call.

Client capability declaration

Clients declare in init:

{"capabilities": {"elicitation": {"form": {}, "url": {}}}}

Servers MUST NOT send modes the client doesn’t support.

Recommendation for V5

  • This is the OAuth-onboarding primitive Ascend has been missing. Today, users manually hand us API keys or run OAuth flows in browser tabs. With Elicitation URL mode, tool calls that need a secret can prompt “click here to connect HubSpot” inline in the chat, complete the flow out-of-band, and auto-retry.
  • Implement for: HubSpot OAuth, Salesforce OAuth, Google OAuth (GA4/GSC/Gmail/Ads), Klaviyo API key entry, SEMrush key entry.
  • Requires stateful DO-backed McpAgent (elicitation state can’t be session-id-only per spec security requirements).
  • Standard URLElicitationRequiredError path: tool call → error → client opens URL → user completes → notification → client retries. This replaces today’s manual onboarding flow entirely.

10. Upcoming MCP Features — Roadmap April 2026

Source: modelcontextprotocol.io/development/roadmap (last updated 2026-03-05).

Priority Areas (Working Group deliverables this cycle)

  1. Transport Evolution & Scalability — next-gen Streamable HTTP: stateless across multiple server instances, load-balancer-correct behavior, session migration. MCP Server Cards.well-known metadata for discovery without connecting. Ownership: Transports WG + Server Card WG. (No new official transports this cycle — community should use custom transports.)
  2. Agent CommunicationSEP-1686 Tasks primitive is shipped; Agents WG is closing retry semantics + expiry policies.
  3. Governance Maturation — Contributor Ladder SEP, delegation model for WGs, charter templates. SEP-1302, SEP-2085 already landed.
  4. Enterprise Readiness — audit trails + observability, enterprise-managed auth (Cross-App Access), gateway/proxy patterns, configuration portability. Likely to land as extensions rather than core spec changes. New Enterprise WG forming.

On the Horizon (community-driven, lower priority)

  • Triggers / Event-Driven Updates — webhooks / server-push notifications with ordering guarantees (replaces polling and SSE-hold patterns).
  • Result Type Improvements — streamed tool results (incremental output) + reference-based results (pull large payloads on-demand).
  • Security & Authorization — finer-grained least-privilege scopes, SEP-1932 DPoP, SEP-1933 Workload Identity Federation.
  • Extensions Ecosystem — maturing ext-auth and ext-apps tracks; possibly a Skills primitive for composed capabilities; first-class extension support in the registry.

Validation investments

  • Conformance Test Suites (automated spec adherence per-feature).
  • SDK Tiers (SEP-1730).
  • Reference implementations for every new feature.

Watch list for V5

  • Tasks primitive — when retry/expiry semantics land, rebuild V5’s batch_execute on Tasks for proper async semantics.
  • Server Cards — prepare /.well-known/mcp-server-card output so V5 auto-indexes in the next wave of registries.
  • Streamed results — when shipped, swap V5’s long tool call responses to streaming (e.g. Google Ads full-account pulls).

Top-5 Adoption Priorities for Ascend V5

Ranked by impact × ease, mapped to the execution-plan / LEDGER format.

Priority 1 — Ship Streamable HTTP /mcp endpoint with OAuth 2.1

  • What: mount /mcp on V5 Worker via McpAgent DO; wrap Worker in OAuthProvider; publish /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server; Google as upstream IdP.
  • Why first: This is table stakes. Every commercial vendor in the registry has it. Without it V5 is not a first-class MCP in 2026.
  • Blockers: none — @cloudflare/workers-oauth-provider + agents SDK are production-ready. Existing Hono tool handlers remain untouched.
  • Done criterion: claude mcp add --transport http ascend https://ascend-gateway-v5.ascendgtm.workers.dev/mcp walks a new user through Google OAuth and lands all 28 tools in Claude Code / claude.ai / Claude Desktop. All three worksWith values.
  • Migration path: parallel to current bearer-token flow for 1 release; deprecate bearer-token path after 2 weeks with no usage.

Priority 2 — Stand up a Cloudflare MCP Server Portal per tenant

  • What: in Cloudflare Zero Trust, create one MCP Portal per paying client. Register V5 + client’s hosted MCPs (HubSpot, Attio, Klaviyo, Linear, etc.) behind Access SSO (Google Workspace binding).
  • Why: identity, audit, federation, progressive disclosure. Solves the “how do I show this client only their tools” problem without code changes on V5 side.
  • Blockers: Cloudflare One plan tier for production. Need pricing clarity.
  • Done criterion: Kahuna and Point Field Partners each have dedicated URLs https://kahuna.mcp.ascendgtm.net/mcp and https://pointfield.mcp.ascendgtm.net/mcp. Each tenant user logs in once via Access, gets a curated tool set.
  • Code Mode: enable ?codemode=search_and_execute on any portal where tool count > 30 for token compression.

Priority 3 — Implement Elicitation URL mode for third-party OAuth onboarding

  • What: replace manual secret-entry and OAuth flows with in-band elicitation/create (URL mode) + URLElicitationRequiredError (-32042). First targets: HubSpot, Salesforce, Google (GA4/GSC/Gmail/Ads), Klaviyo, SEMrush.
  • Why: 10x better onboarding UX. Turns “here’s a 12-step guide to connect HubSpot” into “click this link in chat.”
  • Blockers: requires DO-backed McpAgent (Priority 1 covers this). Requires /connect?elicitationId=... verification page to prevent the phishing-vector described in spec §Phishing.
  • Done criterion: new user says “pull my HubSpot deals” to Claude with no prior setup; Claude shows a consent link, user clicks, OAuths HubSpot, tool call auto-retries and returns deals.

Priority 4 — Wire D1 audit + Analytics Engine telemetry for every tool call

  • What: D1 table tool_calls(ts, user_sub, tenant, client_id, session_id, tool, args_hash, status, latency_ms, error_code). Analytics Engine firehose for high-cardinality events. Expose agent-queryable gateway_audit_query tool.
  • Why: enterprise readiness (matches roadmap priority 4). Gives agents memory of their own actions. Unblocks compliance questions from PE-portfolio clients.
  • Blockers: D1 migration + one-time ETL from current logs (if any).
  • Done criterion: agent can call gateway_audit_query({tool: "google_ads_query", since: "24h"}) and get full call history; Logpush exports to R2 for external SIEM if client requires.

Priority 5 — Submit V5 to the Anthropic MCP Registry as net.ascendgtm/gateway

  • What: prepare registry submission. Namespace via DNS challenge on ascendgtm.net. Craft displayName, iconUrl, oneLiner, useCases, worksWith: [claude, claude-api, claude-code, claude-desktop]. Complete tool descriptions.
  • Why: discoverability. Once listed, V5 becomes auto-suggested in Claude’s connector directory. Marketing value for Ascend.
  • Blockers: need stable pricing + ToS page. Need public-usable plan for non-paying-tenant use (or gate DCR to waitlist).
  • Done criterion: V5 appears in GET https://api.anthropic.com/mcp-registry/v0/servers?version=latest with slug ascend-gtm-gateway. Anyone can claude mcp add --transport http ascend-gtm https://mcp.ascendgtm.net/mcp.

Appendix A — Full citation list

Primary spec sources

RFCs referenced

Anthropic registry

Cloudflare

Vendor MCPs referenced in Section 3 (all from live Anthropic registry enumeration 2026-04-24)

  • Apollo, Attio, Close, HubSpot, Zoho CRM (CRM)
  • Amplitude, Mixpanel, PostHog, Windsor.ai, Omni Analytics (Analytics)
  • Ahrefs, Exa, Tavily (SEO/Search)
  • Klaviyo, Mailchimp (Email/Lifecycle)
  • Notion, Sanity, Webflow, WordPress.com (CMS)
  • Canva, Figma, Lucid (Design)
  • Supabase, PlanetScale, Snowflake, Databricks, BigQuery, Airtable (Data)
  • Linear, Atlassian Rovo, Asana, monday.com, ClickUp (PM)
  • Stripe, QuickBooks (Payments)
  • Box (Storage)
  • Slack, Intercom, Zoom, ZoomInfo, Granola (Comms)

Ecosystem analysis

SEPs mentioned


End of research brief. No commits, no pushes. Findings ready for planning session.