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
- 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 from2024-11-05is formally deprecated — new servers MUST NOT implement it, though backward-compat mode is allowed. (transports spec) - Empirical registry proof of the shift: of the 216 commercial servers in Anthropic’s registry (live, enumerated today), 211 use
streamable-http, 4 still usesse(Plaid, WordPress preview, Buildkite, one other), 1 has no transport listed (broken/stale). SSE is a rounding error in 2026. - OAuth 2.1 + RFC 9728 Protected Resource Metadata is the standard auth floor. MCP clients MUST implement
resourceindicators (RFC 8707), PKCE with S256, and parseWWW-Authenticateresource_metadatachallenges. Client-ID Metadata Documents (CIMD) are now the preferred registration mode over full DCR. (authorization spec) - 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/mcpendpoint away from being a first-class remote MCP. The registry proves every serious vendor is converging onhttps://mcp.<vendor>/mcpas the canonical path. - Cloudflare has shipped the gateway/federation layer natively — MCP 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) - 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)
- Private-visibility registration in Anthropic’s registry is not a user-facing feature today — the
visibilityfield on each server record is["commercial"]in the public feed; no public API accepts avisibility: privatesubmission. 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. - Recommended 2026 Ascend architecture:
- V5 Gateway exposes a native Streamable HTTP MCP endpoint at
/mcpwith OAuth 2.1 (via@cloudflare/workers-oauth-provider), gated per-client viaMcpAgentDurable 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/gatewayonce we’re ready for commercial listing; until then, distribute theclaude mcp add --transport http ascend https://ascend-gateway-v5.ascendgtm.workers.dev/mcpone-liner to each user, which is what every commercial vendor in the registry currently does.
- V5 Gateway exposes a native Streamable HTTP MCP endpoint at
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 referencesschema/2025-11-25/schema.tsin the repo. - Defined transports (exhaustive):
stdioandStreamable 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) ortext/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-25header validation — reject mismatches with 400. - Optional session state: issue
MCP-Session-IdonInitializeResult, require it on subsequent requests, return 404 to terminate. - Support
Last-Event-IDresumption. Event IDs MUST be globally unique per-session and encode the originating stream. - Security mandate: validate
Originheader 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:
| Transport | Count (of 216 commercial servers) |
|---|---|
streamable-http | 211 |
sse | 4 |
| (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
/mcpviacreateMcpHandler()(stateless) orMcpAgentDO-backed (stateful, needed for Elicitation state). - Add
MCP-Protocol-Versionheader 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)
| Requirement | RFC | What it means for V5 |
|---|---|---|
| OAuth 2.1 (draft-ietf-oauth-v2-1-13) | draft-ietf-oauth-v2-1-13 | No implicit grant, no ROPC. Auth code + PKCE only. |
| PKCE with S256 | OAuth 2.1 §7.5.2 | Required; clients MUST refuse servers that don’t advertise code_challenge_methods_supported |
| RFC 9728 Protected Resource Metadata | RFC 9728 | V5 MUST serve /.well-known/oauth-protected-resource |
| RFC 8707 Resource Indicators | RFC 8707 | Clients MUST send resource=https://ascend-gateway-v5.ascendgtm.workers.dev/mcp in auth+token requests |
| RFC 7591 DCR (optional, legacy) | RFC 7591 | Included for back-compat; CIMD is preferred |
| Client ID Metadata Documents | draft-ietf-oauth-client-id-metadata-document-00 | Modern pattern — client hosts metadata at HTTPS URL, URL = client_id |
| RFC 8414 Auth Server Metadata | RFC 8414 | Serve /.well-known/oauth-authorization-server |
Discovery flow (what happens on an unauthenticated call)
- Client calls
POST https://.../mcpwithout a token. - Server replies
401 Unauthorizedwith:WWW-Authenticate: Bearer resource_metadata="https://ascend-gateway-v5.ascendgtm.workers.dev/.well-known/oauth-protected-resource",scope="gtm:read gtm:write" - Client fetches the resource_metadata doc, learns which auth server(s) handle this resource.
- Client fetches
/.well-known/oauth-authorization-serveron each candidate AS, picks one, readsauthorization_endpoint,token_endpoint,code_challenge_methods_supported,registration_endpoint,client_id_metadata_document_supported. - Client registers: prefer pre-registered static creds > CIMD > DCR.
- Client generates PKCE verifier/challenge, opens browser with auth URL (including
resource=param), catches callback, exchanges code + verifier + resource for access token. - 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 401WWW-Authenticateheader, or fall back toscopes_supportedfrom the PRM doc. - Step-up: runtime tools that need more permission return
403 Forbiddenwitherror="insufficient_scope"and a freshscope=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-serverauto-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
OAuthProviderpinning 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),visibilityarray (currently always["commercial"]),useCasesarray.
GTM-relevant hosted MCP servers (filtered to B2B-GTM / marketing-agency use cases)
| Category | Server | URL | Transport | Tools | worksWith |
|---|---|---|---|---|---|
| CRM | Apollo.io | https://mcp.apollo.io/mcp | streamable-http | 13 | claude |
| CRM | Attio | https://mcp.attio.com/mcp | streamable-http | 23 | claude, claude-api, claude-code |
| CRM | Close | https://mcp.close.com/mcp | streamable-http | 55 | claude, claude-api |
| CRM | HubSpot | https://mcp.hubspot.com/anthropic | streamable-http | 5 | claude, claude-api |
| CRM | Zoho CRM | (templated) | streamable-http | 41 | claude, claude-api, claude-code |
| Analytics/BI | Amplitude | https://mcp.amplitude.com/mcp | streamable-http | 14 | claude, claude-code |
| Analytics/BI | Mixpanel | https://mcp.mixpanel.com/mcp | streamable-http | 20 | claude, claude-api, claude-code |
| Analytics/BI | PostHog | https://mcp.posthog.com/mcp | streamable-http | 57 | claude, claude-code |
| Analytics/BI | Windsor.ai | https://mcp.windsor.ai | streamable-http | 5 | claude, claude-api, claude-code |
| Analytics/BI | Omni Analytics | https://callbacks.omniapp.co/callback/mcp | streamable-http | 3 | claude, claude-api, claude-code |
| SEO/Content intel | Ahrefs | https://api.ahrefs.com/mcp/mcp | streamable-http | 61 | claude, claude-api, claude-code |
| SEO/Content intel | Exa | https://mcp.exa.ai/mcp | streamable-http | 2 | claude, claude-api, claude-code |
| SEO/Content intel | Tavily | https://mcp.tavily.com/mcp | streamable-http | 5 | claude, claude-api, claude-code |
| Email/Lifecycle | Klaviyo | https://mcp.klaviyo.com/mcp?include-mcp-app=true | streamable-http | 27 | claude, claude-api, claude-code |
| Email/Lifecycle | Intuit Mailchimp | https://ai-inc.mailchimp.com/claude/mcp/v2 | streamable-http | 12 | claude, claude-api |
| Content/CMS | Notion | https://mcp.notion.com/mcp | streamable-http | 13 | claude, claude-api, claude-code |
| Content/CMS | Sanity | https://mcp.sanity.io | streamable-http | 31 | claude, claude-api, claude-code |
| Content/CMS | Webflow | https://mcp.webflow.com/mcp | streamable-http | 19 | claude, claude-api, claude-code |
| Content/CMS | WordPress.com | https://public-api.wordpress.com/wpcom/v2/mcp/v1 | streamable-http | 16 | claude, claude-api, claude-code |
| Design | Canva | https://mcp.canva.com/mcp | streamable-http | 17 | claude, claude-api, claude-code |
| Design | Figma | https://mcp.figma.com/mcp | streamable-http | 9 | claude, claude-api, claude-code |
| Design | Lucid | https://mcp.lucid.app/mcp | streamable-http | 11 | claude, claude-api, claude-code |
| Data/Warehouse | Supabase | https://mcp.supabase.com/mcp | streamable-http | 32 | claude, claude-api, claude-code |
| Data/Warehouse | PlanetScale | https://mcp.pscale.dev/mcp/planetscale | streamable-http | 15 | claude, claude-code, claude-desktop |
| Data/Warehouse | Snowflake | (templated) | streamable-http | 2 | claude, claude-api, claude-code |
| Data/Warehouse | Databricks | (templated) | streamable-http | 3 | claude, claude-api, claude-code |
| Data/Warehouse | Google BigQuery | https://bigquery.googleapis.com/mcp | streamable-http | 5 | claude, claude-api, claude-code |
| Data/Warehouse | Airtable | https://mcp.airtable.com/mcp | streamable-http | 12 | claude, claude-api |
| PM/Docs | Linear | https://mcp.linear.app/mcp | streamable-http | 22 | claude, claude-api, claude-code |
| PM/Docs | Atlassian Rovo | https://mcp.atlassian.com/v1/mcp | streamable-http | 31 | claude, claude-code |
| PM/Docs | Asana | https://mcp.asana.com/v2/mcp | streamable-http | 15 | claude, claude-code |
| PM/Docs | monday.com | https://mcp.monday.com/mcp | streamable-http | 20 | claude, claude-api, claude-code |
| PM/Docs | ClickUp | https://mcp.clickup.com/mcp | streamable-http | 35 | claude, claude-api, claude-code |
| Payments/Billing | Stripe | https://mcp.stripe.com | streamable-http | 23 | claude, claude-api, claude-code |
| Payments/Billing | Intuit QuickBooks | https://ai-inc.quickbooks.intuit.com/v1/mcp | streamable-http | 11 | claude, claude-api |
| Storage | Box | https://mcp.box.com | streamable-http | 20 | claude, claude-api, claude-code |
| Comms | Slack | https://mcp.slack.com/mcp | streamable-http | 11 | claude, claude-api, claude-code |
| Comms | Intercom | https://mcp.intercom.com/mcp | streamable-http | 6 | claude, claude-api, claude-code |
| Comms | Zoom | https://mcp.zoom.us/mcp/zoom/streamable | streamable-http | 5 | claude, claude-api |
| Comms | ZoomInfo | https://mcp.zoominfo.com/mcp | streamable-http | 5 | claude, claude-api, claude-code |
| Comms | Granola | https://mcp.granola.ai/mcp | streamable-http | 4 | claude, 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_querytool 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-salesforceSlack MCP is listed, but no Salesforce-CRM MCP). V5’ssalesforce_crm+salesforce_queryremain essential. - GA4, Google Search Console, Gmail, Google Calendar — no Google-branded MCPs in the commercial registry. V5’s
ga4_report,gsc_performance,gmail,google_calendartools are still the only paved path. - SEMrush — no hosted MCP. V5’s
semrush_researchis 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=commercialqueries today. Thevisibilityfield is an ARRAY on the server record but only"commercial"appears in live data. There is no documented public submission API forvisibility: 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”
| Path | What it is | When to use |
|---|---|---|
| Cloudflare MCP Server Portals | One 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-liner | Distribute 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.io | Fork 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 listing | Ship 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 addcommand, let OAuth handle identity. - Plan for public Anthropic listing once V5 is ready for public commercial use. Name:
net.ascendgtm/gateway. Verification: DNS challenge onascendgtm.net. - Stand up a Cloudflare MCP Portal per paying client tenant — each client gets their own URL like
https://<client>.mcp.ascendgtm.net/mcpfronting (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
audisn’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_executetool (V5 already has this) that takes an array of tool-call specs and returns parallel results. Ascend V5’sbatch_executeis 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-Idas the rate-limit key. - Per-client: OAuth
client_id(or CIMD URL). - Per-user:
subclaim from validated access token. - Return 429 with
Retry-Afterheader — not in MCP spec but standard HTTP.
Versioning
MCP-Protocol-Versionheader on every HTTP request. Reject unsupported versions with 400.- Server
versionfield 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
statusfield (active,deprecated,deleted) under_meta."io.modelcontextprotocol.registry/official".status. - In-band deprecation: include a warning field in tool result
_metaor anotifications/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
| Package | Purpose |
|---|---|
agents | Cloudflare Agents SDK — provides McpAgent DO class, createMcpHandler() stateless factory |
@cloudflare/workers-oauth-provider | OAuth 2.1 AS implementation; wraps your Worker fetch handler |
@modelcontextprotocol/sdk | Official MCP TypeScript SDK — server + transport primitives |
@modelcontextprotocol/inspector | Dev tool for testing MCP servers |
mcp-remote | Local proxy for bridging stdio clients to remote Streamable HTTP (legacy bridge) |
Implementation pattern options
createMcpHandler()— stateless, no DOs. Fine for 0-state tools. Cannot do Elicitation state.McpAgent(Durable Object-backed) — stateful. REQUIRED for Elicitation URL mode (server must persistelicitationId→ user binding), sessions, resumption.- 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
AscendMcpAgentinstance;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 agateway_audit_querytool) and by Logpush. - Analytics Engine
TELEMETRY: high-cardinality event firehose — sampled, cheap, queryable via Workers Analytics API.
Authless vs authenticated
- Authless (
isAuthless: truein 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
OAuthProvideras above. - Promote
McpAgentDO in front of Hono for tool routing (keeps existing Hono handlers as the “resolver” layer). - Bind D1
ascend-auditfor per-call provenance (this is also the answer to §8 observability). - Compatibility date:
2026-04-01or 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/
- Register each upstream MCP under Zero Trust → Access → AI controls → MCP servers (supports authless or OAuth-protected).
- Create a portal at
https://<sub>.<domain>/mcpwith custom domain. - Policy: bind Access identity (Google/Microsoft/Okta SSO) + per-server tool allowlists.
- Clients configure one URL. Access mediates OAuth to each upstream server separately (or uses admin credentials when
Require user authis off). - 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:
- 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.
- 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.
- 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.
- Progressive tool disclosure — use MCP
tools/list_changednotifications 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)
| Primitive | Purpose | URL |
|---|---|---|
| Workers Logs + Tail Workers | Request-level structured logs; streamed to a logging Worker | workers/observability/logs/workers-logs |
| Analytics Engine | High-cardinality event firehose; SQL-queryable; cheap; 90d retention | analytics/analytics-engine |
| Workers Analytics Engine + SQL API | Query historical events; dashboards | same |
| D1 Audit Table | Tool-call-grain provenance: ts, user, client, tool, args_hash, result_hash, latency, status | V5-owned |
| AI Gateway | Wraps LLM + MCP traffic; rate limiting, caching, logging, per-user token quotas | ai-gateway |
| Logpush | Export Access + Gateway + Workers logs to R2 / Splunk / Datadog / S3 | logpush |
| OpenTelemetry | Workers has OTEL support via Baselime / Axiom / Honeycomb integrations; full W3C trace context propagation | workers/observability/third-party-tools |
Best-practice MCP tool-call telemetry (recommended V5 pattern)
On every tool call, emit:
- 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. - D1 audit row — authoritative record for compliance + agent-queryable history.
- Workers Log line — structured JSON line. Tailed by Logpush → R2. Agent can query via the tail worker.
- 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.
- OpenTelemetry trace (optional for paying-tier tenants) — emit spans with
mcp.tool.name,mcp.client.id,mcp.user.subattributes, propagate W3C traceparent to downstream APIs.
Recommendation for V5
- Bind Analytics Engine (
TELEMETRYdataset) + D1 (ascend-audit) today. - Expose a V5 tool
gateway_audit_querythat 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
| Mode | Purpose | Server MUST/MUST NOT |
|---|---|---|
| Form mode | Structured non-sensitive data from user (name, date, enum choice) | MUST NOT request passwords, API keys, tokens, payment creds. Data passes through client. |
| URL mode | Redirect 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
defaultvalues. 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?”:
- Server generates
elicitationId(UUID), stores binding to usersub. - Server sends
elicitation/createwithmode: "url",url: "https://mcp.example.com/connect?elicitationId=...",message. - Client shows the URL to the user, gets consent, opens it in a secure browser (NOT WebView — use
SFSafariViewControlleron iOS per spec). - User completes OAuth at the 3rd-party AS; 3rd-party redirects back to the MCP server.
- Server verifies the authorized user matches the
elicitationId’s storedsub(phishing defense — critical). - Server stores 3rd-party tokens bound to user identity.
- Server MAY send
notifications/elicitation/completewith theelicitationIdso 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)
- Transport Evolution & Scalability — next-gen Streamable HTTP: stateless across multiple server instances, load-balancer-correct behavior, session migration. MCP Server Cards —
.well-knownmetadata for discovery without connecting. Ownership: Transports WG + Server Card WG. (No new official transports this cycle — community should use custom transports.) - Agent Communication — SEP-1686 Tasks primitive is shipped; Agents WG is closing retry semantics + expiry policies.
- Governance Maturation — Contributor Ladder SEP, delegation model for WGs, charter templates. SEP-1302, SEP-2085 already landed.
- 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-authandext-appstracks; 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_executeon Tasks for proper async semantics. - Server Cards — prepare
/.well-known/mcp-server-cardoutput 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
/mcpon V5 Worker viaMcpAgentDO; wrap Worker inOAuthProvider; publish/.well-known/oauth-protected-resourceand/.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+agentsSDK are production-ready. Existing Hono tool handlers remain untouched. - Done criterion:
claude mcp add --transport http ascend https://ascend-gateway-v5.ascendgtm.workers.dev/mcpwalks 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/mcpandhttps://pointfield.mcp.ascendgtm.net/mcp. Each tenant user logs in once via Access, gets a curated tool set. - Code Mode: enable
?codemode=search_and_executeon 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-queryablegateway_audit_querytool. - 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. CraftdisplayName,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=latestwith slugascend-gtm-gateway. Anyone canclaude mcp add --transport http ascend-gtm https://mcp.ascendgtm.net/mcp.
Appendix A — Full citation list
Primary spec sources
- MCP spec latest (2025-11-25)
- Architecture
- Base protocol
- Transports
- Authorization
- Security best practices
- Elicitation (client feature)
- Roadmap
RFCs referenced
- OAuth 2.1 draft-ietf-oauth-v2-1-13
- RFC 8414 — OAuth 2.0 Authorization Server Metadata
- RFC 7591 — OAuth 2.0 Dynamic Client Registration
- RFC 9728 — OAuth 2.0 Protected Resource Metadata
- RFC 8707 — Resource Indicators for OAuth 2.0
- Client ID Metadata Documents draft-ietf-oauth-client-id-metadata-document-00
Anthropic registry
- Registry API root — servers list
- Claude Code MCP docs
- Official community registry — github.com/modelcontextprotocol/registry
- Official registry — registry.modelcontextprotocol.io
Cloudflare
- Agents — MCP overview
- Agents — Remote MCP server guide
- Agents — MCP transport
- Agents — McpAgent API
- Agents — createMcpHandler API
- Cloudflare One — MCP Server Portals
- Enterprise MCP reference architecture blog
- remote-mcp-authless demo
- workers-oauth-provider package
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
- Hey It Works — MCP Aggregation/Gateway/Proxy Q1 2026 state of ecosystem
- Gentoro — What Is Anthropic’s New MCP Registry
- Anthropic — Donating MCP & establishing the Agentic AI Foundation
- dxrf.com — Locking Down MCP: Registry Setup and Enforcement
- Blog — Introducing the MCP Registry (Sep 2025)
SEPs mentioned
- SEP-1302 — Working Groups governance
- SEP-1686 — Tasks primitive
- SEP-1730 — SDK tiers
- SEP-1932 — DPoP
- SEP-1933 — Workload Identity Federation
- SEP-2085 — Succession and amendment
- SEP-2133 — Experimental extensions
End of research brief. No commits, no pushes. Findings ready for planning session.