Skip to content

Telemetry

Foam collects anonymous usage data to help understand how Foam is used and to prioritize improvements. This page describes what is sent, what is never sent, and how to opt out.

This applies to every Foam component:

  • Note content, titles, file names, or tag names
  • Wikilink targets, search queries, or any text you typed
  • Folder paths or any user-defined string
  • Free-text configuration values
  • Command arguments, CLI flag values, or MCP tool arguments
  • Stack traces or error messages (only the error class name is sent)
  • Anything that could identify you personally

Every event includes:

  • Which component sent it (vscode, cli, mcp)
  • The version of that component and of @foam/core
  • Your OS family — CLI/MCP send os.platform (darwin, linux, win32) and Node version; VS Code’s library reports OS and version via its own common.* properties (see Common properties)
  • An anonymous identifier — CLI and MCP share an installation UUID generated locally on first run. VS Code uses the pseudonymous machine ID provided by Microsoft’s @vscode/extension-telemetry library (common.vscodemachineid); we cannot disable that and we never link it to the CLI/MCP ID.

At a high level:

  • VS Code extension sends a small set of session events: that Foam loaded, which commands you ran, your enum/boolean settings, and bucketed workspace size (e.g. “201-500 notes”). No file names, paths, or note titles.
  • CLI sends one event per invocation: which command ran, how long it took (bucketed: 0-10ms, 11-50ms, 51-300ms, 301-1000ms, 1-5s, 5-30s, 30s+), and bucketed workspace size.
  • MCP sends one event per tool call the LLM makes: which tool was called, how long it took, and whether it succeeded. Tool arguments and returned content are never sent.

The full event list is at the bottom of this page.

Foam respects VS Code’s global telemetry setting. Open Settings, search for telemetry.telemetryLevel, and set it to off.

On the first run, the CLI prints a notice describing what is collected and asks you to confirm. The default is enabled — pressing Enter accepts it. Your choice is saved in ~/.config/foam/config.json and not asked again.

If the CLI is run non-interactively (CI, piped input, MCP launched by Claude Desktop / Cursor, etc.), it cannot prompt — it prints a disclosure to stderr and defaults to enabled for that session only. No choice is persisted, so the next interactive CLI run will still ask. To make the choice durable in a non-interactive context, set FOAM_TELEMETRY=0 or FOAM_TELEMETRY=1 in the environment, or edit ~/.config/foam/config.json directly.

You can change your choice at any time:

  • Set FOAM_TELEMETRY=0 in your environment, or
  • Edit ~/.config/foam/config.json and set "telemetry.enabled": false

MCP runs as a child of the CLI (foam mcp ..., typically configured in Claude Desktop / Cursor / mcp.json). It inherits the CLI’s opt-out — set FOAM_TELEMETRY=0 in the MCP server’s environment, or edit ~/.config/foam/config.json.

All events are sent to a single Azure Application Insights resource owned by the Foam project. Connection strings used to send data are not secrets — they only permit writing events, never reading them. Aggregated, anonymized usage data may be shared with the Foam community to inform roadmap discussions. Raw event data is not shared.

The properties below are attached to every event.

PropertyComponentsExampleDescription
foam.componentCLI, MCP, VS Codecli, mcp, vscodeWhich Foam component emitted the event
foam.versionCLI, MCP, VS Code0.26.0Version of that component
foam.coreVersionCLI, MCP, VS Code0.4.1Version of @foam/core
node.versionCLI, MCP22.5.0Node runtime version (CLI/MCP only — VS Code reports it via common.platformversion)
os.platformCLI, MCPdarwin, linux, win32Operating system family (CLI/MCP only — VS Code reports it via common.os)
installationIdCLI, MCP(random UUID)Anonymous installation ID shared across CLI and MCP. Sent on the App Insights envelope as the ai.user.id tag, not as an event property. Omitted on the cli.first-run event.

VS Code events additionally carry the common.* properties added by Microsoft’s @vscode/extension-telemetry library — these are not controlled by Foam:

PropertyDescription
common.extnameExtension name (foam.foam-vscode)
common.extversionExtension version
common.vscodemachineidPseudonymous machine identifier generated by VS Code. Distinct from Foam’s installationId; we cannot disable it.
common.vscodesessionidPer-session identifier generated by VS Code
common.vscodeversionVS Code version
common.vscodecommithashVS Code commit hash
common.osOS running VS Code
common.platformversionOS/platform version
common.productVS Code host (desktop, github.dev, codespaces, …)
common.uikindweb or desktop
common.remotenameType of remote connection if any (ssh, wsl, dev-container, or other)
common.nodeArchNode architecture (e.g. arm64, x64, or web)

You can disable VS Code’s telemetry — and therefore everything Foam sends from VS Code — via the global telemetry.telemetryLevel setting (off). The CLI and MCP opt-out is separate and is described above.

  • Duration: <10ms, <50ms, <500ms, <5s, <30s, 30s+
  • Workspace size (note count): 0, 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-2000, 2001-5000, 5001-10000, 10000+
EventWhen it firesProperties
vscode.session-startedOnce per extension activationnone
vscode.session-with-commandFirst Foam command in a sessionnone
vscode.session-with-noteFirst markdown file opened in a sessionnone
vscode.commandEvery Foam command invocationcommand (identifier only — no arguments)
vscode.config-snapshotOnce per sessionselected enum/boolean settings — e.g. graph.onStartup, completion.linkFormat, links.hover.enable, graph.viewsConfigured (count, not names). Free-text settings are never included.
vscode.workspace-statsOnce per sessionnoteCount and attachmentCount (both bucketed), hasTemplates, hasDailyNoteTemplate
vscode.errorAn unhandled error in a Foam featurecontext (feature name), errorType (error class name only — no message, no stack)
EventWhen it firesProperties
cli.command-invokedOnce per CLI invocation that runs a known command, after it completes. Skipped for --help, --version, foam config, and unknown commands (typos) — those never emit telemetry.command, durationBucket, exitCode. When the command threw an error caught by the dispatcher, also errorType (error class name only — no message, no stack) and errorContext (where it was caught — dispatch). Some commands attach extra properties — e.g. note create and daily --create attach template-type (one of default, daily-note, custom) and template-format (md / js). Both are omitted when no template was applied (e.g. a note create that fell back to the minimal # title body). daily additionally attaches mode: create when a new daily note was written this invocation, open for read-only lookups and for --create against a note that already existed (nothing was written).
cli.first-runAt most twice per installation: once for the strongest consent outcome we’ve recorded so far. Anonymous — carries no installationId, no os.platform, no node.version. An install that runs headlessly first (e.g. MCP) fires once with default_on; if the same install later answers an interactive CLI prompt, a second event fires with granted / declined (the “upgrade”). Subsequent runs of either kind are deduped via state.json.consent: granted / declined (from an interactive prompt), or default_on (non-interactive first encounter)
EventWhen it firesProperties
mcp.session-startedOnce per server startclient (e.g. claude-desktop, cursor, omitted if not exposed); noteCount and attachmentCount (both bucketed); mode (read or read-writeread-write is set when the CLI is started with --allow-writes; the default is read)
mcp.session-with-toolFirst tool call in a sessionnone
mcp.tool-invokedEvery tool call (sent unsampled)tool (name only — arguments never recorded), durationBucket, outcome (success / error)
mcp.errorAn unhandled error during MCP dispatch. Rare in practice: tool handlers are wrapped to convert throws into structured isError: true results, which surface as outcome: 'error' on mcp.tool-invoked. mcp.error is the safety net for anything that escapes that wrapping.context, tool, errorType
Published with Foam