Skip to content

feat: support custom protocol links (cursor://, vscode:) without triggering cross-link validation #2733

@theletterf

Description

@theletterf

Problem

When adding one-click MCP install buttons for Cursor and VS Code to the docs, we discovered that custom protocol URIs like cursor:// and vscode: are treated as cross-repository links by the link validator, producing errors:

  • 'cursor' was not found in the cross link index
  • 'vscode' was not found in the cross link index

This happens because CrossLinkValidator.IsCrossLink() flags any absolute URI whose scheme isn't in ExcludedSchemes, and ValidateExternalUri() only skips validation for http/https/mailto — everything else falls through to ProcessInternalLink, which tries to resolve the URI as a local file path.

What we tried

  1. Adding cursor, vscode, vscode-insiders to ExcludedSchemes and changing ValidateExternalUri to return true for non-http/mailto schemes. This is technically correct but touches core validation logic that affects all links.

  2. Using ela.st short links that redirect to the deep-link URIs. The redirect service sanitizes destination URLs and strips non-http protocols, so this didn't work.

  3. Raw HTML <a> tags with class="btn btn-primary" to bypass the markdown parser entirely. The docs engine rendered them as static text, not clickable links.

  4. Code blocks with a "copy and paste into your browser" instruction. This is what we shipped (PR fix(mcp): fix document tools and expand MCP documentation #2731), but it's a poor UX compared to a real clickable button.

Proposal

It would be useful to have a way to create buttons or links with arbitrary protocol URIs that bypass cross-link validation. Some options:

  • A directive option on the existing {button} directive, e.g. :external: or :raw:, that tells the validator to skip the link entirely.
  • Extending ExcludedSchemes in CrossLinkValidator with a more general approach — either a configurable allowlist in _docset.yml, or treating any URI with a recognized absolute scheme as external by default.
  • A dedicated {deeplink} directive for IDE protocol links that renders as a button but is never validated.

Any of these would let us turn the current "copy this URL" workaround into a proper clickable install button.

Context

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions