• Sign In



  • Categories

    Extension Details



    Readme

    Claude Code Bridge for Nova

    Integrate Claude Code CLI with Nova (by Panic) through the WebSocket MCP protocol — the same protocol used by the official VS Code and JetBrains extensions.

    License: MIT
    Nova: 10+
    Node: 18+
    Sponsor: LCI Education

    Why?

    Claude Code has official IDE integrations for VS Code and JetBrains, but nothing for Nova. If you love Nova's native macOS experience and want Claude Code's full agentic capabilities — context sharing, inline diffs, selection tracking — this extension bridges the gap.

    The approach was pioneered by coder/claudecode.nvim for Neovim. This project brings the same idea to Nova using its JavaScript extension API.

    How It Works

    ┌──────────────────────────────────────────────────┐
    │                   Nova Editor                     │
    │                                                   │
    │  ┌─────────────┐   JSON lines    ┌─────────────┐ │
    │  │  main.js    │◄──────────────►│ ws-server.js │ │
    │  │  (Extension)│  stdin/stdout   │ (Node.js     │ │
    │  │             │                 │  subprocess) │ │
    │  └──────┬──────┘                 └──────┬───────┘ │
    │         │                               │         │
    │    Nova APIs:                     WebSocket MCP   │
    │    • TextEditor                   (RFC 6455)      │
    │    • Workspace                          │         │
    │    • NotificationCenter                 │         │
    │    • FileSystem                         │         │
    └─────────────────────────────────────────┼─────────┘
                                              │
                        ┌─────────────────────┘
                        │
                        ▼
              ┌─────────────────┐
              │  Claude Code CLI │
              │  (claude → /ide) │
              └─────────────────┘
    

    The extension spawns a Node.js subprocess that runs a WebSocket server implementing the MCP (Model Context Protocol). Claude Code CLI discovers this server through a lock file at ~/.claude/ide/<port>.lock — exactly the same mechanism used by the official extensions. All communication uses JSON-RPC 2.0 over WebSocket frames.

    Features

    • Automatic context sharing — Claude Code sees your active file, selection, and workspace structure
    • Selection tracking — Real-time selection broadcasts as you navigate and select code
    • Diff review — Accept or reject Claude's proposed changes via notification prompts; user edits in the proposed-changes tab are preserved on Accept and signalled back to Claude
    • File operations — Claude can open files, save documents, and check for unsaved changes
    • Sidebar tracking — Three live sections: connection status, pending diffs queue with per-item Accept/Reject, and an activity log of file operations and diff outcomes (auto-refreshes every 30s)
    • One-click launch — Open Claude Code in iTerm or Terminal.app with the IDE-bridge env vars pre-set; the bridge connects automatically
    • Secure by default — Localhost-only WebSocket with UUID token authentication

    Requirements

    Dependency Minimum Version
    Nova 10.0
    Node.js 18.0
    Claude Code CLI Latest

    Installation

    From Source

    git clone https://github.com/okapi-ca/claudecode-nova.git
    cp -r claudecode-nova/claudecode-nova.novaextension \
      ~/Library/Application\ Support/Nova/Extensions/
    

    For Development

    git clone https://github.com/okapi-ca/claudecode-nova.git
    ln -s "$(pwd)/claudecode-nova/claudecode-nova.novaextension" \
      ~/Library/Application\ Support/Nova/Extensions/claudecode-nova.novaextension
    

    Then enable Extension Development in Nova: Preferences → General → Extension Development.

    Quick Start

    1. Open a project in Nova
    2. The extension starts automatically (you'll see a notification)
    3. Run the Launch Claude Code command from Extensions → Claude Code Bridge (or the Command Palette).
    4. It opens Claude in your configured terminal (iTerm by default if installed, otherwise Terminal.app — see the claudecode.terminalApp setting) with the workspace cwd and IDE-bridge env vars already set.
    5. The bridge connects automatically; no need to type /ide.
    6. You'll see a "Connected" notification in Nova. Claude now has access to your editor context.

    Manual launch (alternative)

    If you prefer to drive the terminal yourself, set claudecode.terminalApp to clipboard and run:

    cd /your/project
    CLAUDE_CODE_SSE_PORT=<port> ENABLE_IDE_INTEGRATION=true claude
    

    The port is shown in the Show Claude Code Status command. Inside Claude, /ide triggers discovery if the env vars weren't picked up.

    Supported MCP Tools

    These are the tools that Claude Code can invoke through the bridge, matching the protocol used by the official VS Code and JetBrains extensions:

    Tool Status Description
    openFile ✅ Full Open a file with optional line navigation
    openDiff ✅ Full Diff via temp file + accept/reject notification. User edits in the proposed-changes tab are preserved on Accept and signalled back to Claude (see Known Limitations §1 for the side-by-side caveat).
    getCurrentSelection ✅ Full Current editor selection with file path and range
    getLatestSelection ✅ Full Most recently recorded selection
    getOpenEditors ✅ Full List all open editor tabs with metadata
    getWorkspaceFolders ✅ Full Workspace folder paths
    checkDocumentDirty ✅ Full Check for unsaved changes in a file
    saveDocument ✅ Full Save a document
    getDiagnostics ⚠️ Partial Requires LSP extension cooperation (see Limitations)
    closeAllDiffTabs ⚠️ Best-effort Removes our temporary proposed_* files; cannot close Nova editor tabs because Nova has no public tab-management API
    close_tab ❌ Not supported Nova exposes no public API to close an editor tab — see Known Limitations §6. Not advertised in tools/list.
    executeCode ❌ Not supported Nova has no Jupyter kernel integration. Not advertised in tools/list.

    Direct Tool Invocation (debug helper)

    The Claude Code CLI only forwards mcp__ide__getDiagnostics to the model — the other 9 tools registered by ws-server.js are consumed internally by the CLI and not callable from a model conversation. For debugging or scripting, Scripts/call-bridge.js connects to the running bridge directly via the lock file and invokes any tool by name. No npm dependencies.

    SCRIPT="$HOME/Library/Application Support/Nova/Extensions/ca.okapi.claudecode-nova/Scripts/call-bridge.js"
    # (or wherever the extension is installed; for development use the project path)
    
    node "$SCRIPT" --tools                         # list tools advertised by the bridge
    node "$SCRIPT" getOpenEditors                  # call with empty args
    node "$SCRIPT" getCurrentSelection
    node "$SCRIPT" getWorkspaceFolders
    node "$SCRIPT" openFile '{"filePath":"/abs/path","lineNumber":42}'
    node "$SCRIPT" saveDocument '{"filePath":"/abs/path"}'
    

    The script auto-discovers the lock file under ~/.claude/ide/, preferring one whose workspaceFolders matches the current cwd when several Nova instances are running. Output is the unwrapped tool result as pretty-printed JSON; errors go to stderr with a non-zero exit code.

    Sidebar

    The Claude Code sidebar exposes three sections:

    ▼ Claude Code
      ▼ Status
        ● Server Running
        Port: 12345
        Clients: 1
      ▼ Pending Diffs
        ▼ auth.ts                                      5s ago
          ✓  Accept
          ✗  Reject
        ▼ Button.tsx                                   1m ago
          ✓  Accept
          ✗  Reject
      ▼ Activity
        📄  Opened src/parser.ts                       just now
        💾  Saved README.md                            12s ago
        ✓  Accepted diff: components/Button.tsx       1m ago
        ✂️  Sent selection from src/types.ts           2m ago
        ✗  Rejected diff: middleware/cors.ts          5m ago
        ▶ Tool Calls (47)
    
    • Status — connection state, port, client count. Header buttons start/stop the bridge.
    • Pending Diffs — every diff Claude proposes is queued here with file name + age. Double-click Accept or Reject to resolve. Notifications still appear for the first diff (so it gets your attention); the sidebar handles multi-diff overflow. Double-click the parent item to see details with Open / Accept / Reject buttons.
    • Activity — visible-effect events (file opens/saves, selections sent, diff outcomes). Click an item to open the corresponding file (file ops) or see a details dialog (diff ops). The collapsible Tool Calls group at the bottom shows the raw MCP traffic for debugging — including the bookkeeping calls Claude makes constantly (getCurrentSelection, getOpenEditors, …).
    • Buffers are bounded (50 activity events, 100 tool calls). Header Refresh re-renders, Clear empties both buffers. Auto-refresh every 30 s keeps relative timestamps accurate.

    Commands

    Access these from Extensions → Claude Code Bridge or the Command Palette:

    Command Description
    Start Claude Code Bridge Start the WebSocket MCP server
    Stop Claude Code Bridge Stop the server and disconnect clients
    Send Selection to Claude Push the current selection as context
    Add Current File to Claude Send the entire active file as context
    Show Claude Code Status Display connection status and server info
    Launch Claude Code (with IDE integration) Open Claude Code in your terminal of choice (iTerm or Terminal) with the IDE bridge env vars pre-set. Falls back to clipboard for unsupported terminals — see claudecode.terminalApp setting.

    Configuration

    Global Preferences

    Key Default Description
    claudecode.portMin 10000 Minimum port for the WebSocket server
    claudecode.portMax 65535 Maximum port
    claudecode.autoStart true Start the bridge automatically on activation
    claudecode.trackSelection true Broadcast selection changes in real time
    claudecode.nodePath node Path to the Node.js executable
    claudecode.terminalApp auto Where the Launch Claude Code command opens the CLI: auto (iTerm if installed, else Terminal), iTerm, Terminal, or clipboard (just copy the command). Other terminals (Warp, Ghostty, Hyper) fall back to clipboard automatically.

    Per-Project Settings

    Key Default Description
    claudecode.claudeCommand claude Command to launch Claude Code CLI

    Known Limitations

    The following limitations exist due to Nova's extension API boundaries:

    1. Diff viewer — Nova does not expose a native diff API like VS Code's vscode.diff. Proposed changes are shown by opening a temporary file alongside the original, with an accept/reject notification. Edits the user makes in the proposed-changes tab before clicking Accept are preserved (the actual content of the temp file is what gets saved) and signalled back to Claude via userEdited: true in the response. A future version may leverage Nova's built-in Git comparison view for side-by-side rendering.

    2. Diagnostics — Nova does not provide a global API for reading LSP diagnostics from third-party extensions. The getDiagnostics tool currently returns an empty list. Full support would require cooperation with language server extensions or a shared IssueCollection.

    3. No native WebSocket server — Nova's JavaScript runtime does not include WebSocket server or raw TCP socket APIs. The workaround is a Node.js subprocess, which adds a dependency but works reliably.

    4. No HTTP preview integration — Nova's built-in web preview is not accessible through the extension API, so Claude cannot interact with the preview pane.

    5. Selection line numbers — Nova's Range is character-offset based. Line number mapping in selection tracking is approximate. A future version will use TextDocument line-counting methods for precise ranges.

    6. No tab-management API — Nova exposes no method on TextEditor or Workspace to close an editor tab from an extension. As a consequence:

    7. close_tab (tool 11 in claudecode.nvim PROTOCOL.md) is not advertised in our tools/list — Claude will not call it.
    8. closeAllDiffTabs only removes the temporary proposed_* files staged in extension storage; the corresponding tabs in Nova remain open until the user closes them manually (Cmd+W).
    9. Confirmed by the Tabs Sidebar extension, which documents the same limitation.

    10. No Jupyter kernel integrationexecuteCode (tool 12 in PROTOCOL.md) is unsupported. Nova does not expose a notebook runtime, and exposing one would be a separate product. Not advertised in tools/list.

    Architecture

    claudecode-nova.novaextension/
    ├── extension.json          # Extension manifest (commands, sidebar, config)
    ├── Scripts/
    │   ├── main.js             # Nova entry point — editor API bridge
    │   ├── ws-server.js        # WebSocket MCP server (Node.js subprocess)
    │   └── call-bridge.js      # Standalone CLI client for invoking bridge tools
    ├── Images/
    │   ├── claude-icon-small.png
    │   ├── claude-icon-large.png
    │   └── ...@2x variants
    └── README.md
    

    Communication Flow

    1. Nova extension (main.js) spawns ws-server.js as a child process
    2. ws-server.js binds a WebSocket server on 127.0.0.1:<random_port>
    3. A lock file is written to ~/.claude/ide/<port>.lock with connection info
    4. Claude Code CLI discovers the lock file via /ide command
    5. CLI connects to the WebSocket, authenticates with the UUID token
    6. MCP tool calls arrive as JSON-RPC 2.0 requests over WebSocket
    7. ws-server.js forwards them to main.js via stdout JSON lines
    8. main.js executes the tool using Nova APIs and sends the result back via stdin
    9. ws-server.js wraps the result in MCP format and returns it over WebSocket

    Lock File Format

    {
      "port": 12345,
      "authToken": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
      "version": "0.2.0",
      "ideName": "Nova",
      "ideVersion": "1.0.0",
      "workspaceFolders": ["/Users/you/project"],
      "pid": 54321
    }
    

    Security

    • WebSocket server binds to 127.0.0.1 only (no network exposure)
    • Every connection requires the UUID auth token in the x-claude-code-ide-authorization header
    • Lock files are removed on clean shutdown
    • No data leaves your machine — all communication is local IPC

    Roadmap

    v0.2.0 (shipped)

    • [x] One-click launch into iTerm or Terminal.app with IDE env vars pre-set
    • [x] User edits in the proposed-changes tab are preserved on Accept and signalled to Claude (userEdited / finalContent)
    • [x] Sidebar with Pending Diffs queue (per-item Accept/Reject) and Activity log (visible-effect events + raw tool-call group)
    • [ ] Improved diff view with side-by-side file comparison
    • [ ] Diagnostics integration via shared IssueCollection
    • [ ] Accurate line number tracking in selections

    v0.3.0

    • [ ] Warp / Ghostty / Hyper launch support (URL-scheme or wrapper-script approach)
    • [ ] Configurable keyboard shortcuts
    • [ ] Multi-workspace support
    • [ ] File watcher for external changes

    v1.0.0

    • [ ] Full MCP protocol v2 compatibility
    • [ ] Automated test suite
    • [ ] Publication on extensions.panic.com
    • [ ] Proper icon set

    Contributing

    Contributions are welcome! This project exists because the community (notably coder/claudecode.nvim) proved that third-party IDE integrations with Claude Code are fully achievable.

    1. Fork the repository
    2. Create a feature branch (git checkout -b feature/amazing-thing)
    3. Commit your changes (git commit -m 'Add amazing thing')
    4. Push to the branch (git push origin feature/amazing-thing)
    5. Open a Pull Request

    Development Tips

    • Enable Nova's Extension Console: Extensions → Show Extension Console, filter by source
    • The ws-server.js subprocess logs are piped through — check the console for both layers
    • Use claude --ide from an external terminal to test connections
    • The PROTOCOL.md from claudecode.nvim is the definitive protocol reference

    Credits

    • Author: Marc Bourget — CISSP, Principal Director of Cybersecurity
    • Sponsor: LCI Education — An international educational community of 12 higher education institutions across 17 campuses on 5 continents. LCI Education supports open-source initiatives and encouraged the public release of this project.
    • Protocol reverse engineering: coder/claudecode.nvim by Thomas Kosiewski and contributors
    • Nova Extension API: docs.nova.app
    • Claude Code: Anthropic

    Sponsor

    LCI Education
    Proudly supporting open-source development

    LCI Education is an international educational community comprising 12 higher education institutions
    operating across 17 campuses on 5 continents, dedicated to accessible, quality education worldwide.

    License

    MIT — See LICENSE for details.

    Release Notes

    Changelog

    0.3.0 — 2026-04-30

    Added

    • Real line/column numbers in selectionsgetCurrentSelection,
      getLatestSelection, and live selection_update payloads now expose
      0-indexed startLine / endLine / startColumn / endColumn
      computed from the document offset (was hardcoded to 0). The
      selection_sent activity events render as (L42-L58) in the
      sidebar.
    • Git branch in context — current branch is cached via
      git rev-parse --abbrev-ref HEAD (refreshed on bridge start and
      every 5 minutes) and shipped in every selection_update and
      getWorkspaceFolders response. Silently null outside git repos.
    • Diff line stats — every openDiff computes a lightweight
      +N / -M lines delta (multiset line intersection vs. the file on
      disk; detects new files) and surfaces it in the system notification,
      the Pending Diffs sidebar label, the row tooltip, and the details
      dialog.
    • Pending Diffs tooltip preview — hovering a diff row now shows
      the first 5 lines of the proposed content with an overflow marker.
    • Activity log persistenceactivityLog and toolCallLog are
      serialized to globalStoragePath/activity.json (debounced 1s) and
      restored on activate. pendingDiffs are intentionally not persisted
      (their Claude-side requestIds die with the session).
    • claudecode.diffTimeoutMinutes setting (default 30, 0 disables)
      — auto-rejects pending diffs older than the threshold via the
      existing 30s sidebar tick. Stops dead requestIds from piling up
      when Claude crashes mid-flow.
    • Restart Claude Code Bridge command (claudecode.restart) —
      stop + 300ms + start, useful after changing the port range or the
      Node.js path.
    • Default keyboard shortcuts:
    • Cmd+Ctrl+L → Send Selection to Claude (when a selection exists)
    • Cmd+Ctrl+A → Add Current File to Claude
      Avoids Cmd+Shift+L which Nova already uses for "Reveal in Files
      Sidebar".
    • claudecode.claudeArgs per-workspace setting — extra arguments
      appended after the Claude command in Launch Claude Code. Unlocks
      --continue, --model claude-opus-4-7,
      --dangerously-skip-permissions, etc. without code changes.
    • Auto-save before Send Selection to Claude — if the document is
      dirty, it's saved first so Claude's disk-reading tools (Read, Bash)
      see the same content as the buffer.

    Fixed

    • closeAllDiffTabs no longer leaks pending diffs — the tool now
      rejects every still-pending diff via resolveDiff(id, false) before
      sweeping the temp-file directory, freeing Claude-side requestIds
      that would otherwise wait forever. The Nova editor tabs themselves
      still need a manual Cmd+W (no public tab-close API in Nova).

    Notes

    • No protocol changes; ws-server.js is untouched.
    • All payload additions are additive — existing Claude Code clients
      will simply ignore the new fields (gitBranch, startLine,
      endLine, …).

    0.2.1 — 2026-04-29

    Changed

    • Identifier renamed from com.marcbourget.claudecode-nova to
      ca.okapi.claudecode-nova to match the okapi-ca organization
      registered on the Panic Extension Library. Required for marketplace
      publication — Panic ties extensions to a registered organization
      via the reverse-DNS prefix of the identifier, and the com.marcbourget
      prefix had no corresponding org. Organization metadata in the manifest
      was updated from "Marc Bourget" to "okapi-ca" for the same reason.
    • Side-effect on local installs: Nova treats the new identifier as
      a different extension. Anyone who previously installed v0.2.0 from
      source under com.marcbourget.claudecode-nova/ should remove that
      directory and copy the new bundle to ca.okapi.claudecode-nova/,
      otherwise both versions cohabit and the bridge port allocation can
      collide.

    0.2.0 — 2026-04-29

    Added

    • Sidebar activity tracking — two new sections under the Claude Code
      sidebar mirror what the VS Code v2.1.69+ activity panel shows:
    • Pending Diffs — every diff Claude proposes appears with its file
      name and a relative timestamp. Each diff has Accept / Reject child
      items so you can resolve from the sidebar without going through
      the system notification. Notifications are still emitted (3A) so
      the first diff still attracts attention; the sidebar is the
      multi-diff overflow path.
    • Activity — file opens, saves, sends-to-context, file-added-to-
      context, and diff outcomes appear at the top with timestamps that
      auto-refresh every 30s. Below them, a collapsible "Tool Calls (N)"
      group exposes every raw MCP tool invocation for debugging.
    • Click-through on activity items:
    • File operations (opened, saved, added, selection_sent) →
      open the file in Nova
    • Diff events → details dialog with timestamp, length, and Open /
      Accept / Reject buttons
    • Five new commands wired internally for the sidebar (not in the
      Extensions menu): claudecode.activityClick,
      claudecode.activityClear, claudecode.diffAccept,
      claudecode.diffReject, claudecode.diffShowDetails,
      claudecode.sidebarRefresh.
    • "Launch Claude Code" command now opens Claude in a real terminal
      instead of just copying a command to the clipboard. Driven via
      AppleScript so the workspace cwd and the IDE-bridge env vars
      (CLAUDE_CODE_SSE_PORT, ENABLE_IDE_INTEGRATION=true) are pre-set
      — no manual paste, no /ide typing required, the bridge connects
      automatically.
    • New global setting claudecode.terminalApp (enum):
    • auto (default) — iTerm2 if installed, otherwise Terminal.app
    • iTerm — iTerm2, opens a new tab in the current window
    • Terminal — Terminal.app, opens a new window
    • clipboard — keep the v0.1.x copy-to-clipboard behaviour
    • Scripts/call-bridge.js — standalone CLI client for debugging
      and scripting. The Claude Code CLI only forwards
      mcp__ide__getDiagnostics to the model side; the other 9 tools
      registered by ws-server.js are consumed internally by the CLI
      and unreachable from a model conversation. This script connects
      to the running bridge directly via the lock file and invokes any
      tool by name. No npm dependencies — manual WebSocket framing
      mirrors ws-server.js. See README §Direct Tool Invocation.

    Fixed

    • RFC 6455 handshake — the Sec-WebSocket-Accept calculation
      used a transposed magic GUID (…-5AB5DC11CE56 instead of
      …-C5AB0DC85B11), so any RFC-compliant client computed a
      different digest and closed the connection right after the 101.
      Symptom: read ECONNRESET ~5 ms after "Claude Code client
      connected" in the Nova extension console with Claude CLI v2.1.x
      (which uses the ws Node.js library). Without this fix the
      bridge was effectively unusable on recent Claude CLI builds.
    • WebSocket subprotocol echows-server.js now echoes back
      the first offered Sec-WebSocket-Protocol (Claude CLI sends
      mcp). Strict clients reject the connection if a requested
      subprotocol is not selected by the server.
    • Disconnect logging — the close-event hadError flag is now
      surfaced in the extension console so future handshake regressions
      are visible at a glance.

    Changed

    • openDiff was refactored: pendingDiffs is now the source of truth.
      The notification handler and the sidebar Accept/Reject commands both
      call the same resolveDiff(id, accepted) function. Idempotent —
      resolving a diff a second time is a no-op, so race conditions
      between the notification and the sidebar are harmless.

    Notes

    • Other terminals (Warp, Ghostty, Hyper, kitty, Alacritty) lack a
      reliable AppleScript control surface and fall back to the clipboard
      path. Pick clipboard explicitly to silence the auto-detect.
    • If the configured terminal isn't installed at launch time, the
      extension falls back to clipboard automatically and notifies the user.
    • Activity buffers are bounded: 50 visible events, 100 raw tool calls.
      Older entries roll off — there is no persistence across Nova restarts.

    0.1.1 — 2026-04-29

    Fixed

    • openDiff now respects edits the user made in the proposed-changes tab
      before clicking Accept. Previously the original newContent from Claude
      was always written to disk, silently discarding any in-IDE edits.

    Changed

    • openDiff response payload now carries userEdited: true and
      finalContent when the saved file differs from Claude's original proposal.
      This mirrors the v2.1.110 Claude Code CLI behaviour where the model is
      informed of edits the user made before accepting (see CLI changelog
      2026-03 entry).
    • closeAllDiffTabs MCP description corrected to reflect what it actually
      does on Nova: cleans up temporary proposed_* files in extension storage.
      Nova exposes no public API to programmatically close editor tabs, so the
      prior wording ("Close all open diff views") was misleading.

    Investigated, not implemented

    • close_tab and executeCode (added to claudecode.nvim PROTOCOL.md as
      the canonical 12-tool list) are not implementable on Nova:
    • close_tab: Nova has no public tab-management API
      (TextEditor/Workspace expose no close() method, no command, no
      keyboard-shortcut surface for extensions).
    • executeCode: Nova has no Jupyter kernel integration.
      Neither tool is advertised in our tools/list response — Claude will
      not call them on Nova.

    0.1.0 — 2026-02-26

    • Initial release
    • WebSocket MCP server bridge via Node.js subprocess
    • MCP tools: openFile, openDiff, getCurrentSelection, getLatestSelection, getOpenEditors, getWorkspaceFolders, checkDocumentDirty, saveDocument, getDiagnostics, closeAllDiffTabs
    • Real-time selection tracking
    • Sidebar status panel
    • Lock file discovery mechanism for Claude Code CLI