Make the extension's own vscode:// deep link clickable in chat output (open past sessions)

Open 💬 0 comments Opened Jul 10, 2026 by rhwinter

Summary

Make the extension's own deep link — vscode://anthropic.claude-code/open?session=<id> — clickable in chat output, so results that reference a past session can be opened in one click instead of copy-pasting a raw URL into a browser.

Ideally scope this to links the extension itself produces (a host-rendered "session reference" affordance), not arbitrary model-emitted markdown, so it doesn't widen the prompt-injection surface.

Motivation

I have a custom slash command that does "session archaeology" — it searches ~/.claude/projects/**/*.jsonl for past sessions matching a topic and returns a ranked list. It would be great to render each result as a clickable link that opens that session. The deep-link route already works: pasting vscode://anthropic.claude-code/open?session=<id> into a browser correctly hands off to VSCode and opens the right session.

But there's no way to make it clickable from chat output today.

What I found (why no markdown form works)

The chat panel renders assistant markdown with react-markdown using its default urlTransform. In webview/index.js that's:

function exe(e){ let t=e.indexOf(":"),i=e.indexOf("?"),n=e.indexOf("#"),o=e.indexOf("/");
  if(t===-1 || (o!==-1&&t>o) || (i!==-1&&t>i) || (n!==-1&&t>n) || nat.test(e.slice(0,t)))
    return e;
  return "" }              // nat = /^(https?|ircs?|mailto|xmpp)$/

Any URL whose scheme isn't http(s)|ircs|mailto|xmpp gets its href blanked. So every form I tried fails — the href is stripped before it can fire:

  • [open](vscode://anthropic.claude-code/open?session=<id>)
  • <vscode://anthropic.claude-code/open?session=<id>> (autolink)
  • bare URL
  • [open](command:vscode.open?<encoded deep link>)

(The deep-link route itself is valid — this is purely the renderer refusing to emit a non-http href.)

This is an extension choice, not a VSCode limitation

VSCode fully supports opening non-http links from a webview:

  • intercept the click and postMessage to the host, which calls vscode.env.openExternal(Uri.parse(...)); or
  • render trusted markdown (MarkdownString with isTrusted) where command: links are allowed — which the extension already does in its Monaco-based renderer (r.href.startsWith("command:") handling exists in the bundle).

The chat view just keeps react-markdown's default allowlist.

Security note / suggested scoping

I understand blanking non-http schemes in model-generated text is a sensible anti-injection default — arbitrary clickable command:/vscode:// links in LLM output would be a real risk. So rather than "allow custom schemes in chat markdown," the safe version is a first-party, host-controlled affordance:

  • allowlist only the extension's own vscode://anthropic.claude-code/open?session=… deep link, and/or
  • expose a structured "session reference" the host renders as a link (so it's not trusting raw model markdown at all).

That keeps arbitrary model-emitted links blocked while making the one first-party link clickable.

Workaround today

Render the URL as inline code so it's one-click-select + copy, then paste into a browser:

vscode://anthropic.claude-code/open?session=<id>

Works, but a real clickable link would be much nicer for tools that surface past sessions.

---
🤖 Authored with Claude Code

View original on GitHub ↗