Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>Lens by corteXNew to Visual Studio Code? Get it now.
Lens by corteX

Lens by corteX

corteX R&D

|
2 installs
| (1) | Free
Lens observability for VS Code, Cursor, Windsurf and other VS Code-family editors. Ships token, cost, and tool-call telemetry into the Lens dashboard so teams see SavingsCounter, Brain Advisor whisper, and Claude-Code-in-terminal de-duplication in real time.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

lens by cortex

Cortex Lens is a paid commercial product. This extension is the client component and requires an active Cortex Lens tenant to operate. See LICENSE for terms.

extension for VS Code, Cursor, Windsurf and other VS Code-family editors that ships token, cost, and tool-call telemetry into the multi-tenant cortex-lens dashboard. it lives next to connectors/claude-code-hook and connectors/lens-cli and uses the same env contract so a workspace can adopt all three with one token.

status: 0.3.3, published as questo.lens-cursor-extension, with detected-fork attribution, encrypted-keychain tenant token, typed heartbeat, identity attribution, session boundaries, and the no-paste device connect (Lens: connect this editor with a code pairs the editor through the dashboard approval page and stores the granted token in the encrypted keychain).

what it looks like

once activated, the extension surfaces a status bar widget at the bottom-right of the editor window showing live session savings (lens: $0.00 saved). click the widget to open the configured dashboard at <lens.baseUrl> in the browser, where the SavingsCounter, Brain Advisor whisper card, and Improvements drill-down chart aggregate every tool call captured by this extension and its sibling connectors.

screenshots: three banner-style placeholder mockups live in images/: status-bar.png (1200x300, the bottom-of-IDE widget), dashboard-preview.png (1200x600, the Lens dashboard top nav with the savings counter), and command-palette.png (1200x400, the lens: commands the extension registers). these are composed placeholders, not real captures from a running install; they ship with the Marketplace listing and are replaced with real screenshots once the first design-partner is on the dashboard.

install

pnpm --filter lens-cursor-extension build
pnpm --filter lens-cursor-extension package
cursor --install-extension lens-cursor-extension-0.3.3.vsix

VS Code and Windsurf accept the same vsix via their own --install-extension flag, or via the Extensions panel "Install from VSIX" picker.

getting connected

connect with a code (recommended, no token paste)

Lens: connect this editor with a code is the one-command path and needs nothing typed. run it from the command palette: the extension shows a short pairing code, a signed-in developer approves it in the dashboard, and the extension collects the granted token over the poll channel and writes it straight into the encrypted SecretStorage keychain. the token never appears in the notification and never touches settings.json.

  • zero typing: the pairing runs against the hosted dashboard (https://lens.questo.media) by default, so a fresh install connects with one command. a self-hosted deployment is reached by setting lens.baseUrl (or LENS_BASE_URL) before connecting; either overrides the default.
  • self-configuring: on approval the extension persists the deployment-advertised ingest origin as lens.baseUrl and the approving developer's email as lens.seatEmail, so capture reports to the right place and attributes the right seat with no manually-keyed detail.

settings

every value is optional. adjust them in Settings > Extensions > Lens by corteX (the panel label is identical across VS Code, Cursor, and Windsurf) or in your workspace .vscode/settings.json:

setting required purpose
lens.baseUrl no lens dashboard and ingest origin. the extension posts events to <lens.baseUrl>/api/ingest/ide. when blank it reads LENS_BASE_URL, then falls back to the hosted default https://lens.questo.media. device connect fills it from the deployment-advertised ingest origin.
lens.tenantToken no per-tenant bearer token of shape <tenant_uuid>:<secret>. device connect provides this for you; set it by hand only for the fallback path below. sent only on the authorization header; migrates into the encrypted SecretStorage keychain on first run. falls back to LENS_TENANT_TOKEN.
lens.seatEmail no work email of the developer who approved device connect, filled automatically on connect and used to attribute this seat when no lens.developerId is set. honored only from account-scope settings; a workspace value a repo could commit is ignored. not a secret; sent in the event body, never as a credential.
lens.enabled no toggle capture on or off without uninstalling. default true.
lens.developerId no organization-managed seat id or work email used to attribute activity in the per-developer dashboard. honored only when it arrives through an organization-managed settings policy; hand-typed values in personal or workspace settings are ignored (no environment fallback), and captured activity is otherwise attributed automatically from the seat email or the git author email. not a secret; sent in the event body, never as a credential.

fallback: paste a token

device connect is the primary path and needs no token handling. the manual token path remains for headless installs, CI, and org-managed rollouts where a policy distributes the values centrally.

token placement warning: a token written into workspace .vscode/settings.json is committed with the repository unless that file is git-ignored. put lens.tenantToken in editor settings at the account scope rather than the workspace scope (or set LENS_TENANT_TOKEN in the environment) and keep workspace settings for non-secrets such as lens.baseUrl.

self-serve without editing settings.json

a lone developer without an org policy can configure the extension entirely from the shell, because both values fall back to the environment (LENS_BASE_URL and LENS_TENANT_TOKEN). the claude-code-hook installer writes both into one locked env file. from a checkout of the cortex-lens repository:

python -m pip install ./connectors/claude-code-hook
LENS_TENANT_TOKEN='<tenant_id>:<token_secret>' python -m lens_code_hook.install --endpoint https://<your-lens-host>/api/ingest/ide
set -a; . ~/.cortex/lens-code-hook.env; set +a

on Windows, dot-source the generated loader instead of the third line: . $env:USERPROFILE\.cortex\lens-code-hook.ps1.

then launch the editor from that same shell (for example cursor ., code ., or windsurf .). the extension picks up LENS_BASE_URL and LENS_TENANT_TOKEN from the inherited environment, so you never hand-edit editor settings, and the token never touches a committable .vscode/settings.json.

honest limitation: an editor launched from the dock or Start menu does not inherit shell environment. either launch the editor from the sourced shell or set the values in editor settings at the account scope. the settings panel remains the path for org-managed rollouts, where an organization policy distributes lens.baseUrl and the developer id centrally.

what it captures

  • mcp tool calls forwarded from the host editor's internal mcp layer (pre and post phases). args are redacted client-side via lib/redact for obvious credentials; the receiver applies the full removal catalog server-side.
  • status bar widget bottom-right showing current session savings. click opens the configured dashboard.
  • claude-code-in-terminal detector: polls integrated terminals every 30 seconds. when claude code is running in the same workspace, the de-dup adapter in packages/adapters uses this signal to drop duplicate events.
  • editor activity (file opens, selection changes) and the detected IDE fork (vscode | cursor | windsurf | vscodium | unknown) ride on every event so the dashboard can attribute spend to the right editor and the right developer.

heartbeat wire contract (one-sided)

besides events, the extension posts a periodic typed heartbeat to <lens.baseUrl>/api/ingest/heartbeat so the dashboard install-coverage panel can tell which machines are alive (the server upserts one row per tenant, machine, and ide fork). that wire contract is exercised one-sided today, which matters before extending either end:

  • the client (src/lib/heartbeat.ts) sends the seven-key payload and reads only the http response status. the response body, which carries the server's E-11 state-machine classification (heartbeat_state, transitioned, inserted, tuple, last_seen_at), is discarded.
  • the operator dashboard is the only shipped consumer of heartbeat state. the in-ide pulse the server route comment describes (surfacing heartbeat_state in the status bar) is unshipped client work, not a wired path.
  • consequence: the server may extend its response body without breaking deployed extensions, but a client change that starts reading the body must treat every field as optional so older lens deployments stay compatible.

commands

id description
lens.openDashboard open <lens.baseUrl> in the default browser
lens.toggleEnabled pause or resume capture for the current workspace
lens.viewSavings quick-pick recent session savings
lens.showUsage open the per-developer usage panel
lens.requestLimitIncrease queue a policy limit-increase request for operator review
lens.connectWithCode pair this editor with the dashboard with zero typing: shows a short code, waits for approval at /connect/device, then stores the granted token in the encrypted keychain and persists the deployment-advertised ingest origin as lens.baseUrl and the approver email as lens.seatEmail

privacy

  • the tenant token never lands in logs, stdout, stderr, or the post body. only the authorization header carries it.
  • arg values matching common credential patterns are replaced with [redacted] before they leave the workstation. server-side, the removal catalog applies again over the message text and tool args.
  • the extension only reads files the host editor already has open or terminals already running. it does not read arbitrary disk.

carry-forwards (future scope)

the following are documented as out of scope for the current ship and land in follow-on waves:

  1. completion telemetry: cursor tab token usage. requires hooking cursor's internal completion api (still in flux as of 2026-05).
  2. inline chat capture: the host editor's inline chat panel events. depends on the editor exposing a public event api.
  3. real mcp transport binding: this build still uses a no-op mcp source pending a stable event stream from each editor fork.

layout

connectors/cursor-extension/
  package.json
  tsconfig.json
  vitest.config.ts
  README.md
  src/
    extension.ts          activate/deactivate, status bar wire, command registration
    index.ts              public re-exports for tests
    status-bar.ts         LensStatusBar widget
    mcp-proxy.ts          MCPProxy + MCPEventSource interface
    claude-code-detector.ts  ClaudeCodeDetector poll
    lib/
      redact.ts           client-side credential redactor stub
      ingest-client.ts    batched HTTP transport
  tests/
    vscode-mock.ts        vscode runtime mock for vitest
    extension.test.ts     4 cases
    mcp-proxy.test.ts     3 cases
    claude-code-detector.test.ts  4 cases
    status-bar.test.ts    2 cases
    ingest-client.test.ts 4 cases

related

  • connectors/claude-code-hook ships hook events for claude code itself (python).
  • connectors/lens-cli wraps coding-agent cli invocations from the outside (node).
  • packages/adapters/src/claude-code-to-agent-trace.ts is the receiver-side adapter for the hook source. the cursor adapter lands in pc-2.
  • apps/enterprise/app/api/ingest/ide/route.ts is the receiver this extension posts to (the ide path).

verifying that lens is actually loaded inside the host editor

cursor 3.5.x and some windsurf builds occasionally hide side-loaded vsix entries from the search filter inside the extensions panel even when the extension is fully activated and running. the activation log is the source of truth, not the panel search box. follow this procedure to verify in under one minute. vs code stable surfaces the panel entry directly, so step 4 is mostly a cursor and windsurf concern.

1. confirm activation in the log

open the most recent log session under %APPDATA%/Cursor/logs/<TIMESTAMP>/window1/exthost/exthost.log (cursor), %APPDATA%/Code/logs/... (vs code), or %APPDATA%/Windsurf/logs/... (windsurf), and search for questo.lens-cursor-extension. you should see two lines per editor restart:

ExtensionService#_doActivateExtension questo.lens-cursor-extension, startup: false, activationEvent: 'onStartupFinished'
Extension activated success: questo.lens-cursor-extension - 10ms (code loading: 8ms, activate call: 2ms, activate resolved: 0ms)

if those two lines are present, the extension is loaded and running. visibility in the extensions panel is a separate ui concern and does not gate functionality.

2. confirm the status bar widget

after activation, the extension calls vscode.window.createStatusBarItem(...).show(). look at the bottom-right of the host editor status bar. you will see one of two texts:

  • lens: $0.00 saved when capture is enabled
  • lens: paused when capture is paused

if the widget is visible, lens is alive end-to-end. clicking the widget runs lens.openDashboard.

3. confirm command registration

open the command palette with ctrl+shift+p and type lens:. three commands should appear:

  • Lens: open dashboard
  • Lens: toggle capture
  • Lens: view recent session savings

if the palette finds them, the extension is registered correctly. command palette lookup is independent of the extensions panel filter and is the second most reliable signal after the activation log.

4. if the widget and palette both show but the panel does not

this is the known cursor 3.5.x ui state: side-loaded vsix entries can disappear from the installed-tab search filter when the editor's publisher registry sync fails. functionality is unaffected. options:

  • ignore it: the extension is running. the panel entry is only needed to uninstall via the gui.
  • restart the editor with --reload-extensions: forces a rebuild of the panel state from ~/.cursor/extensions/extensions.json or the equivalent vs code or windsurf path.
  • uninstall via cli if needed: cursor --uninstall-extension questo.lens-cursor-extension (substitute code or windsurf for the host editor). the entry comes back on the next reinstall in some cases.

5. if the activation log does not show the two lines

then the extension truly failed to load. likely causes and fixes, in order of probability:

cause how to detect fix
engines mismatch log line Cannot activate ... engines.vscode '^A.B.C' incompatible with cursor edit package.json in the install dir to lower engines.vscode (current shipped value: ^1.85.0).
out/extension.js syntax error log line with stack trace at activate call rebuild via pnpm --filter @cortex/lens build and replace the out/ dir.
publisher rejected extension absent from ~/.cursor/extensions/extensions.json after install publisher in package.json is questo. if the host editor rejects it, change to local or omit the publisher, then repackage with vsce package --no-dependencies and reinstall.
extension registry file corrupted extensions.json is missing the questo.lens-cursor-extension entry but the install dir exists delete the install dir, then cursor --install-extension <path-to-vsix> (substitute code or windsurf for the host editor).

6. quick repackage and reinstall procedure

if you need to rebuild and reinstall from source after editing the manifest:

cd C:/Intel/questo/projects/cortex-lens/connectors/cursor-extension
pnpm --filter lens-cursor-extension build
pnpm --filter lens-cursor-extension package
cursor --install-extension lens-cursor-extension-0.3.3.vsix

then fully quit the editor (taskkill any leftover Cursor.exe, Code.exe, or Windsurf.exe) and reopen. re-verify steps 1 through 3 above.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft