MCP Cutting-Edge State of the Art — April 2026
MCP Cutting-Edge State of the Art — April 2026
Section titled “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
Section titled “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
Section titled “1. MCP Transport Evolution — SSE is dead, Streamable HTTP is the only choice”Current spec
Section titled “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
Section titled “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)
Section titled “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
Section titled “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
Section titled “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)
Section titled “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)
Section titled “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
Section titled “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)
Section titled “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
Section titled “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
Section titled “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
Section titled “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)
Section titled “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
Section titled “4. Private Registry Entries — What’s actually possible in April 2026”The reality check
Section titled “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”
Section titled “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
Section titled “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
Section titled “5. MCP Server Best Practices 2026”Synthesis from modelcontextprotocol.io/specification/2025-11-25, Cloudflare’s enterprise reference architecture, and registry observations.
- 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)
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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)
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “7. Federation / MCP Gateway Patterns”What the ecosystem has converged on (Q1-Q2 2026)
Section titled “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
Section titled “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)
Section titled “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
Section titled “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
Section titled “8. Telemetry + Observability for MCP”Primitives available on Cloudflare (by purpose)
Section titled “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)
Section titled “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
Section titled “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
Section titled “9. MCP Elicitation Spec — the “AskUserQuestion” primitive”Source: modelcontextprotocol.io/specification/2025-11-25/client/elicitation
Two modes
Section titled “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
Section titled “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
Section titled “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)
Section titled “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
Section titled “Client capability declaration”Clients declare in init:
{"capabilities": {"elicitation": {"form": {}, "url": {}}}}Servers MUST NOT send modes the client doesn’t support.
Recommendation for V5
Section titled “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
Section titled “10. Upcoming MCP Features — Roadmap April 2026”Source: modelcontextprotocol.io/development/roadmap (last updated 2026-03-05).
Priority Areas (Working Group deliverables this cycle)
Section titled “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)
Section titled “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
Section titled “Validation investments”- Conformance Test Suites (automated spec adherence per-feature).
- SDK Tiers (SEP-1730).
- Reference implementations for every new feature.
Watch list for V5
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “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
Section titled “Appendix A — Full citation list”Primary spec sources
Section titled “Primary spec sources”- MCP spec latest (2025-11-25)
- Architecture
- Base protocol
- Transports
- Authorization
- Security best practices
- Elicitation (client feature)
- Roadmap
RFCs referenced
Section titled “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
Section titled “Anthropic registry”- Registry API root — servers list
- Claude Code MCP docs
- Official community registry — github.com/modelcontextprotocol/registry
- Official registry — registry.modelcontextprotocol.io
Cloudflare
Section titled “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)
Section titled “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
Section titled “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
Section titled “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.