Skip to content

Settings

⚙ Settings holds persisted preferences, plus management panels for custom nodes and on-disk state. Everything persisted lives in ~/.config/threadle/settings.json.

SettingKeyValuesDefault
Appearanceappearancesystem · light · darksystem
Default file openereditor.modevscode · vscode-insiders · cursor · zed · commandvscode
Custom opener commandeditor.commandargv string, e.g. emacsclient -nunset
Claude Code billingclaudeBillingsubscription · apisubscription
Show Workflows addon examplesshowExamplestrue · falsetrue
MCP client enabledmcpClientEnabledtrue · falsetrue
MCP servers disabled for clientmcpDisabledServersstring[] of server ids[]
MCP publish allowlistmcpPublishAllowliststring[] of graph ids, or null[] (none). null = all

Editor / billing save together via Save. Appearance, examples, and MCP toggles write immediately.


Three chips: system · light · dark.

ValueEffect
systemFollows the OS color scheme (prefers-color-scheme)
lightCool zinc surfaces, dark text
darkLifted near-black surfaces (default technical theme)

Applied immediately (and mirrored in localStorage so the next paint does not flash). The docs site header theme control defaults to System (auto) and offers System · Light · Dark. Tokens match the app.


Every “open in editor” button in threadle. File lists, custom-node folders, internals paths. Routes through this setting.

URL-scheme editors are opened client-side by navigating to <scheme>://file<path>:

ModeURL built
vscodevscode://file/Users/you/project
vscode-insidersvscode-insiders://file/…
cursorcursor://file/…
zedzed://file/…

Choosing custom command… reveals a command field and switches to a different path entirely: the browser POSTs the absolute path to /api/open, and the server spawns command <path> detached, with no shell. Because there is no terminal attached, plain vim or nano will not work. Use a GUI or client flavor:

Terminal window
emacsclient -n
gvim
nvim-qt
subl

The command is split on whitespace. argv[0] becomes the binary and the path is appended as the final argument. Buttons elsewhere in the UI label themselves with the binary’s basename (emacsclient), falling back to open.


Splits tracked cost (list price of the tokens. What an API would have billed) from actual spend (what you really paid).

OptionMeaning
subscription (Pro / Max)Claude Code sessions report $0 actual spend. The tracked figure is hypothetical
API key (pay per token)An API key pays list price, so actual spend equals tracked cost

opencode always reports what its own keys were billed, independent of this setting. Cursor token counts come from CLI usage or a chars÷4 estimate, with no list price available in public storage.

When threadle can read a plan from ~/.claude.json, the card adds a detection line. Detected from ~/.claude.json: Max · subscription.

List prices themselves are not configured here. They ship bundled (see Costs & billing). Full accounting model: Costs & billing.


Viewer Settings → Addons lists optional packages. Workflows shows whether the addon API is mounted, opens threadle-workflows (/addon/workflows), and hosts the examples toggle below.

A two-button segmented control (off / on) with no Save button. Toggling writes immediately. When on, the recipes and examples tables appear in the Workflows addon list. See Recipes and Examples.


Manages ~/.config/threadle/nodes/. Three ways in:

ActionWhat it does
+ Create nodeScaffolds a new node folder from a name, then opens the new file in your configured editor
⇣ ImportClones a git URL or copies a local path. Then validates the descriptor
⌸ Browse…Uploads a folder from disk. Skips .git and node_modules, and aborts if any file exceeds 2 MB. Then validates

The install table shows every package on disk with a status column:

StatusMeaning
enabledValid. In the palette and runnable
disabledValid. Hidden from the palette / runner. Folder kept. Toggle with enable
errorFailed validation. Error text under the name. Fix node.json or remove the folder

A summary line counts installed · enabled · disabled · errors. Per-row actions: enable / disable (valid nodes) and open folder in your editor. Disable state lives in nodes/.disabled.json and does not rewrite the author’s node.json. There is no in-app delete. Remove the folder on disk.

Nothing executes on import. Metadata is validated (sandboxed for bare class files), and node code runs only when you wire the node and press Run. See Custom nodes. For curated utilities from the threadle repo (jq, diff, …), see the stdlib pack.


Preferences plus a live discovery table for Model Context Protocol. Keys live in settings.json. The server list is scanned on demand.

PieceMeaning
MCP client off/onWhen off, canvas ◈ MCP tool discovery and calls refuse
publish allowlistGraph ids exposed by threadle mcp as wf_*. One per line or comma-separated. Empty / unset = none. null = all
host .mcp.json snippetCopy into Claude Code / Cursor so the host runs threadle mcp
⟳ Refresh serversRe-scans project configs (walk-up), ~/.config/threadle/mcp/servers.json, and host configs (Cursor / Claude / opencode / Antigravity / Codex where present)
⌘ registryOpens ~/.config/threadle/mcp/
server tableId, transport (stdio · streamable-http · sse), endpoint, source. threadle badge. disable / enable for the canvas client

Spawned stdio servers get the custom-node env allowlist plus each server’s env from disk. Not a full copy of the threadle process. HTTP/SSE remotes use optional headers. Nested recursion into threadle mcp is refused. Full how-to: MCP.


Live view of the threadle server process, refreshed every 5 s while the view is open. Agent-less workflows. Custom nodes. Run here as child processes, so this is the hardware bill.

pid · node version · uptime · cpu (server) · rss · heap used · worker threads (libuv pool) · custom-node children running · agent jobs running · cores · load 1/5/15m · system memory.


Every file and directory threadle writes, with size and file count, and an open in editor button per row. The list is a server-side whitelist. Workflows, favorites, and settings are shown for transparency but can never be bulk-cleared through the API. They render kept instead of a clear button.

Clearing is two-click: the first click arms the button (confirm, or really delete? for destructive items) and it disarms itself after 4 seconds. Already-empty items are disabled.

Full layout: Files & config.


{
"appearance": "system",
"editor": {
"mode": "command",
"command": "emacsclient -n"
},
"claudeBilling": "subscription",
"showExamples": true,
"mcpClientEnabled": true,
"mcpDisabledServers": [],
"mcpPublishAllowlist": []
}

Unknown keys are dropped on save. A missing or unreadable file falls back to the defaults in the table above. A claudeBilling value other than "api" reads as subscription, showExamples / mcpClientEnabled are true unless explicitly false, mcpPublishAllowlist of [] publishes nothing and null means publish all workflows, and an unknown appearance reads as system.