Key takeaways

  • All nine syntactically valid candidates reached the registered application handler: HTTPS, public HTTP, loopback HTTP, file:, javascript:, data:, ftp:, credential-bearing HTTPS and a Punycode hostname.
  • The SDK correctly rejected URL mode when the client declared only form mode, and form mode when it declared only URL mode. Capability negotiation answers whether a mode is supported, not whether one destination is safe.
  • The malformed string “not a URL” never reached the handler, but came back as JSON-RPC -32603 with an Invalid URL validation body in this transport path—not the -32602 expectation in the first harness draft.
  • MCP requires full-URL display, explicit consent, no prefetch and secure opening. Those controls do not eliminate the need for a client-owned HTTPS, origin, credential, local-target and Unicode-display policy before consent.
  • Deploy URL elicitation only when the AI host treats the server-supplied URL as untrusted navigation input and can bind the approved origin and user identity through completion.
01

The sharp question: what does “valid URL” prove before an agent asks for consent?

URL elicitation lets an MCP server move an authorization, payment or credential-entry flow out of an AI client. That is an important confidentiality boundary: the model and client should not inspect a third-party secret. But the server also chooses the navigation target. Before an AI host presents that target, does the official SDK’s successful validation mean the link is suitable to open?

The bounded answer in SDK 1.30.0 is no. Validation proved that the payload matched the elicitation union and that its URL string passed the schema’s URL parser. It did not select an allowed scheme, reject local destinations, strip credentials, interpret Unicode risk or establish that the destination belonged to the expected service.

02

Chronology: URL mode moved sensitive interaction outside the model

SEP-1036 opened July 22, 2025 to add an out-of-band mode for sensitive interactions. Its core design separated form elicitation—which returns structured content through MCP—from URL elicitation, where a person interacts with a web origin and the server later sends a completion notification. The proposal closed November 13, and MCP 2025-11-25 incorporated the mode.

The specification commit landed November 25, 2025. The tested TypeScript SDK 1.30.0 release followed July 27, 2026. This September 2 investigation tested the boundary between that release’s protocol validation and the navigation policy an AI host still has to own.

03

The specification protects the interaction, not every possible destination

For URL mode, a client must show the full URL, must not prefetch it, must not open it without explicit user consent and must open it so the client or LLM cannot inspect content or user input. It should highlight the domain and warn on ambiguous or suspicious identifiers such as Punycode. Servers should use HTTPS outside development and must not put credentials, personal data or pre-authenticated protected-resource access into the URL.

These are meaningful requirements. They also distribute responsibility. The server binds the elicitation to the right user and avoids secret-bearing links; the client controls presentation and navigation. The protocol’s SHOULD for HTTPS is not a runtime allowlist, and the TypeScript schema represents url as a string rather than a restricted origin type.

04

The SDK enforces the declared mode before calling application code

The tested client’s getSupportedElicitationModes helper treats URL support as opt-in. A form-only client returned JSON-RPC -32602 when the fixture sent URL mode, and its application handler was never called. A URL-only client did the same for form mode. These controls prevent a server from silently switching to an undeclared interaction surface.

That is a capability boundary, not a trust decision about one URL. Once URL mode is declared and the payload validates, the wrapped request handler delegates to application code. The host must make the policy decision there or in a stricter layer before it asks a person to proceed.

05

The clean fixture exercised the real reverse-request path

The audit created a temporary npm project and installed exact package versions with --ignore-scripts. A synthetic transport answered initialize with protocol version 2025-11-25, then behaved as the MCP server by delivering elicitation/create JSON-RPC requests to a real Client. The client’s outbound result or error came back through the same transport object.

The handler parsed any delivered URL only to record protocol and hostname, returned decline for every request and performed no I/O. This design tested SDK acceptance without risking browser execution, local-file access, network access, credential transmission or a third-party side effect.

06

Nine URL shapes passed schema validation and reached the handler

The accepted set was https://safe.example/connect, http://public.example/connect, http://127.0.0.1:8080/admin, file:///etc/passwd, javascript:alert(1), data:text/html,<h1>hello</h1>, ftp://files.example/archive, https://user:password@safe.example/connect and https://xn--pple-43d.example/connect. All nine produced an application-level decline result rather than a validation error.

This does not show that the SDK opened any of them; it did not. It shows that the SDK’s URL schema is broad enough to represent them and delegates the final choice to the host. “Accepted by the handler” must not be rewritten as “safe,” “reachable” or “allowed by MCP.”

07

Syntactic parsing and navigation authorization answer different questions

The distributed types file uses z.string().url() for URL-mode requests. The platform URL constructor also parsed every accepted candidate in the fixture. That is consistent with a general URL parser: URLs are not limited to HTTPS web origins, and a hostname is absent for several valid non-special forms.

A navigation policy asks a narrower question: may this client present and open this scheme, host, port and path for this server, tenant, user and requested operation? Parsing supplies components for that decision. It cannot make the decision without application context.

08

A blank hostname is not a reassuring domain signal

For file:, javascript: and data:, the fixture recorded an empty hostname. The official example initializes a display label to “unknown domain,” parses with new URL, then replaces the label with parsedUrl.hostname. For these candidates, parsing succeeds and the resulting displayed hostname can be empty rather than causing the function to decline.

The example still prints the full URL and asks for consent, and it is explicitly example code—not a production browser launcher. The practical lesson is that a successful parse followed by hostname display is not a scheme policy. Reject unsupported schemes before constructing consent UI, not after a user has been conditioned to approve a server request.

09

HTTP and loopback need policy even when no prefetch occurs

The public HTTP and 127.0.0.1 candidates reached the handler. Because the fixture never fetched them, this is not an SSRF finding. It does expose a navigation decision an AI host must make: plain HTTP can lose origin confidentiality and integrity, while loopback or private destinations can address local services under the user’s authority.

A sensible production default is HTTPS-only, with explicit development exceptions tied to a local server identity and environment. If private or loopback destinations are genuinely required, constrain scheme, exact host, port, route and launch context instead of accepting any URL merely because it is local.

10

Embedded credentials and Punycode complicate what consent means

The credential-bearing HTTPS candidate parsed with hostname safe.example even though its full authority also contained user:password. The Punycode candidate parsed as xn--pple-43d.example. A hostname-only highlight can therefore omit security-relevant authority syntax or present an ASCII label that a person does not readily interpret.

The MCP specification already says servers must not include sensitive end-user information in URLs and clients should warn on suspicious Punycode. Hosts can make this deterministic: reject username or password components, render the normalized ASCII host and a safe Unicode comparison where appropriate, separate port from host, and never let the server message visually overwrite the client-owned destination display.

11

Malformed syntax failed, but the error code surprised the first harness

The string “not a URL” did not reach the application handler. In this reverse-request path, however, the response used JSON-RPC code -32603 and contained the schema’s Invalid URL details. The first harness draft expected -32602, failed, and forced inspection of the observed payload before the article was written.

The specification’s failure table says a request using a mode not declared by the client should return -32602; the two undeclared-mode controls did. This experiment did not establish what code every malformed elicitation must use, and the article does not label the -32603 result a security defect. Operators should nevertheless test exact error mapping if automation or telemetry depends on it.

12

Consent is a control only when the question is trustworthy

A modal that says “the server wants to open a URL” transfers too much analysis to a person—especially inside an agent workflow where repeated prompts create approval fatigue. Consent should be the final authorization step after deterministic policy has rejected impossible or disallowed targets and explained the expected origin in client-owned language.

Bind the prompt to the requesting MCP server, tool call, user, destination origin, purpose and expiry. Do not let model-generated prose or server-provided message text determine the visual hierarchy. A user should be able to distinguish “GitHub authorization requested by this server” from “arbitrary link supplied while this server was active.”

13

Completion must preserve the approved origin and identity

The specification’s phishing analysis is not solved by URL parsing. A server must prove that the person who completed the out-of-band interaction is the same person for whom the elicitation was created. The recommended pattern sends the user first to a server-controlled connect URL, verifies the browser session against the MCP-authorized subject, and only then starts third-party authorization.

On the client side, retain the elicitation ID, approved normalized origin, requesting server identity, user or tenant, tool-call context and expiration. Treat a completion notification as a state transition from that record—not as proof that any arbitrary browser action was safe or that the intended account was bound.

14

Use a deny-first URL policy before rendering consent

For typical production authorization, accept only https:. Reject credentials in the authority, fragments when they are not required, control characters, opaque origins, file and executable schemes, and hosts that are empty. Normalize once with a standards-compliant parser, then compare structured components rather than raw prefixes. Decide how redirects are constrained at the server-controlled connect endpoint.

Maintain an origin allowlist per MCP server or integration when the expected destinations are knowable. When destinations are dynamic, at minimum enforce public-network policy, organization domain rules and an explicit exception process. OWASP’s redirect guidance independently supports allowlisting untrusted destinations where possible; it does not validate this SDK behavior.

15

Test the opener separately from the protocol parser

This harness stopped before navigation, so it cannot certify Electron shell.openExternal, a native mobile controller, a system browser call or an embedded web view. Those surfaces may handle javascript:, data:, file:, custom schemes, redirects and local addresses differently. A safe parser test is necessary but not sufficient.

Build an opener matrix with no-network fakes and platform-specific integration tests. Cover scheme case normalization, userinfo, encoded delimiters, trailing dots, IPv4 variants, IPv6, private and link-local ranges, IDNs, mixed-script labels, redirect chains, custom handlers, browser isolation and cancellation. Verify that rejected values never become clickable and never generate metadata fetches.

16

What failed, what repeated and what remains unknown

The first run failed because malformed syntax returned -32603 rather than the asserted -32602. After recording that distinction, the final 31-assertion run reproduced nine of nine handler deliveries, one malformed rejection and both capability rejections. The checked-in script recreates its sandbox and source digests on every run.

The experiment did not launch a browser, click a link, follow a redirect, fetch metadata, test a model, run a production MCP server, authenticate a user, complete OAuth or payment, test mobile or desktop shells, inspect another SDK, fuzz URL parsing or demonstrate exploitation. Targeted issue search found no independent reproduction of this exact SDK boundary. The finding is a client-policy boundary, not a claim that MCP or the SDK automatically executes malicious URLs.

17

Decision: deploy URL elicitation only behind a client-owned navigation gate

Deploy when URL mode is explicitly enabled per client, HTTPS and destination policy run before consent UI, the full normalized URL and expected origin are shown in client-owned chrome, no prefetch occurs, the system browser or equivalent isolation prevents client and model inspection, and the server binds completion to the initiating user. Log policy reason, normalized origin, server identity and elicitation ID without logging secrets.

Constrain to supervised flows if destinations are dynamic or identity binding is incomplete. Disable URL mode when the host cannot reject unsafe schemes and local targets, cannot distinguish the requesting server, opens inside a model-observable view, trusts a completion notification without user binding, or allows server prose to obscure the destination. A schema-valid URL is input to the gate—not evidence that the gate passed.

18

Copy-ready MCP URL elicitation gate

Complete this record before an AI host enables URL mode for an MCP server or integration.

Entries stay in this browser tab and are not submitted to AccessAllGPT. Blank responses are copied as [Unresolved].

Server identity, tool, expected user action, why form mode is insufficient and data that must stay outside the model/client.

Client builds, tenants and environments allowed to declare elicitation.url; URL mode disabled by default elsewhere.

Schemes, normalized origins, ports, paths, public/private network policy, IDN policy and development exceptions.

Credentials, empty hosts, opaque origins, executable/local/custom schemes, control characters, fragments and ambiguous encodings.

Client-owned server identity, full normalized URL, highlighted host/port, Punycode warning, purpose and decline/cancel behavior.

System/browser surface, no prefetch or metadata lookup, no embedded model-visible view, opener tests and platform differences.

Initiating MCP subject, browser session subject, connect endpoint, third-party authorization state and mismatch handling.

Elicitation ID, approved origin, expiry, tool-call context, notification handling, duplicate/late completion and cancellation.

Server-controlled connect URL, allowed redirect chain, origin transitions, loop limit and evidence that the final account is correct.

Policy outcome and reason without credentials, query secrets, personal data or browser content; retention and access controls.

Unexpected scheme/host, local target, identity mismatch, opener escape, hidden destination, repeated prompts or unverifiable completion.

Primary sources

  1. Elicitation (protocol version 2025-11-25)Model Context Protocol Specification · Reviewed: Capabilities, URL mode, completion flow, safe URL handling, identifying the user, form security and phishing · Retrieved · Supports: The specification makes URL mode explicit, requires full-URL display and consent, prohibits automatic prefetch and opening without consent, recommends HTTPS outside development, and assigns identity binding to the server.
  2. MCP schema at the 2025-11-25 specification commitModel Context Protocol · Reviewed: Form and URL elicitation parameter types, mode discriminator, message, elicitationId, URL and request union · Retrieved · Supports: The pinned protocol schema represents the URL as a string and separates URL-mode requests from form-mode requestedSchema payloads; it does not define a scheme or destination allowlist in the type.
  3. SEP-1036: URL Mode Elicitation for secure out-of-band interactionsModel Context Protocol proposal archive · Reviewed: Proposal dates, motivation, sensitive-data boundary, URL-mode flow, identity-binding threat and accepted specification work · Retrieved · Supports: The proposal opened July 22, 2025 and closed November 13, 2025; it explains that sensitive authorization should happen outside the MCP client and describes the phishing risk when the wrong user completes a flow.
  4. TypeScript SDK release 1.30.0Model Context Protocol TypeScript SDK · Reviewed: Release identity, tag, publication date, v1.x branch and package changes · Retrieved · Supports: The official release record binds the tested package to SDK 1.30.0, published July 27, 2026 on the v1.x line.
  5. Client elicitation validation at SDK 1.30.0Model Context Protocol TypeScript SDK · Reviewed: Supported-mode helper, backwards-compatible form default and explicit URL-mode capability rule · Retrieved · Supports: The pinned client computes form and URL support separately; URL mode is supported only when the client explicitly declares it.
  6. URL elicitation example at SDK 1.30.0Model Context Protocol TypeScript SDK · Reviewed: URL parsing, hostname display, warning, full URL display, consent, pending completion and navigation instruction · Retrieved · Supports: The official example parses with the platform URL constructor, displays hostname and the full URL, asks for consent, and returns accept; the reviewed function does not impose a protocol or hostname allowlist.
  7. URL StandardWHATWG · Reviewed: Goals, terminology, URL parser, URL representations, schemes and host parsing · Retrieved · Supports: The web standard defines a general URL parser across special and non-special schemes. Successful parsing establishes URL syntax and components, not authorization or destination safety.
  8. Unvalidated Redirects and Forwards Cheat SheetOWASP Cheat Sheet Series · Reviewed: Dangerous URL redirects, safe alternatives, validation, allowlists and user-visible destination guidance · Retrieved · Supports: Independent security guidance recommends avoiding untrusted destination input where possible or validating it against an allowlist; it is a cross-check for navigation policy, not an independent reproduction of this SDK path.

Limitations

This is a bounded local reproduction on Node v24.10.0, Darwin arm64, @modelcontextprotocol/sdk 1.30.0 and Zod 3.25.76 using a synthetic in-process transport, three clients, nine syntactically valid URL strings, one malformed string and two undeclared-mode controls. The handler always returned decline. It did not open a browser, render a clickable link, follow redirects, prefetch, test a model, production client/server, authentication, OAuth, payment, user identity, mobile or desktop shell, another SDK, network policy, URL fuzz corpus, exploit or third-party data. Acceptance by the SDK handler means schema delivery only—not safety, reachability, protocol endorsement or execution. The official URL client is example code. The specification recommends HTTPS outside development and already requires careful client handling; this article identifies the application policy still needed rather than claiming the specification promises an allowlist.

Disclosures

AccessAllGPT created and ran only the synthetic local fixture in the checked-in harness; every candidate was declined and no candidate was fetched or opened. I found no independent public reproduction of the exact SDK 1.30.0 scheme-delivery boundary in a targeted TypeScript SDK issue search. OWASP is cited as independent navigation-policy guidance and WHATWG as the parsing standard, not as reproductions. The MCP project, proposal authors, SDK maintainers, Zod, WHATWG and OWASP did not review this article or provide access, private data, payment or endorsement. AccessAllGPT Research is operated by NeuralArc, is independent, and is not affiliated with OpenAI, Anthropic, the MCP project, Zod, WHATWG or OWASP. Publication-wide relationships are listed on the disclosures page.

Further AccessAllGPT guidance

  1. An MCP Task Timeout Is Not an AI Agent Deadline
  2. MCP’s readOnlyHint Is Not a Permission Boundary
  3. The MCP Token-Passthrough Trap
  4. Where Human Approval Belongs in AI Automation
  5. AccessAllGPT Research methodology
  6. Publication disclosures

Continue the research

Get evidence-led updates for teams making production AI decisions.