Developer setup
For terminal-first users, MCP integrators, and anyone who wants Cotext profiles outside the browser. If you just want to react on AI sites and have your preferences applied, the non-developer guide is what you want.
This page covers:
- Installing the
cotextCLI andcotext-mcpMCP server - Two source modes: a local folder synced by the browser
extension, or a direct API token against
cotext.io - Pushing a profile from the terminal (no extension required)
- Wiring MCP into Claude Code / Codex
- Running the headless daemon for fully-CLI workflows
For the full command reference, see CLI & MCP.
1. Install the CLI
From npm (eventually)
npm install -g cotext
This installs two binaries: cotext (the CLI) and cotext-mcp (the
stdio MCP server).
The npm package isn't published yet — use the from-source path below until it is. Watch the GitHub repo for the release tag.
From source
git clone https://github.com/cotext-io/cotext
cd cotext/packages/cli
npm install
npm run build
npm link
npm link makes the binaries available on your PATH from anywhere.
Verify:
cotext --help
cotext-mcp --help
2. Pick a source
The CLI reads profiles from one of two places:
| Source | When to use | What it needs |
|---|---|---|
| Folder | You also run the browser extension and want the CLI to read the same data | A folder the extension is syncing to |
| Cloud | You don't run the extension; you want to push directly to cotext.io from the terminal | An API token from cotext.io/settings |
Both work the same from the CLI's perspective — every command is
written against a Source abstraction. Switch by setting
environment variables; nothing to reconfigure inside the CLI.
Resolution priority
When multiple sources are configured, the CLI picks the first one it finds, in order:
1. --folder <path> (CLI flag)
2. COTEXT_FOLDER (env)
3. ~/.config/cotext/config.json (saved by `cotext init`)
4. COTEXT_API_TOKEN (cloud fallback)
Folder always wins when both are configured.
3a. Folder source (paired with the extension)
If you're running the browser extension and want the CLI to read its data:
# Make sure the extension is syncing to a folder
# (popup → ".cotext Folder" → Connect folder)
# Point the CLI at the same folder
cotext init # interactive — saves to ~/.config/cotext/config.json
# Verify
cotext status
Expected output:
Source: folder (/Users/you/cotext-sync)
Profiles: 3
Active: code-review
Reading a profile
cotext list # show all profiles
cotext pull # active prompt to stdout
cotext pull --profile code-review # specific profile
cotext cat code-review # full markdown including header
A common pattern — refresh CLAUDE.md from your active profile:
cotext pull > CLAUDE.md
Writing feedback signals
The CLI can write feedback signals into the folder's inbox/
directory; the extension picks them up on next popup open.
cotext signal "be more concise" --type dislike
cotext signal --type dislike --tag too-verbose
cotext signal "this format works" --type like \
--response "$(cat /tmp/last-claude-output.txt)"
See CLI & MCP for the full flag set.
3b. Cloud source (no extension needed)
This is the path for "I never want to open a browser, I just want to push profiles from the terminal."
# 1. Sign in at cotext.io and generate an API token at /settings
# 2. Export it:
export COTEXT_API_TOKEN="ctx_<your-token>"
# 3. Verify the CLI sees the cloud source
cotext status
Expected:
Source: cloud (https://cotext.io)
Profiles: 0
Active: (none yet)
Create a profile from the terminal
cotext profile create "Code review" --starter code-review --activate
This:
- Builds the profile JSON locally from the starter pack
- POSTs it to
https://cotext.io/api/push - Activates it as your current profile
Visible immediately at https://cotext.io/@<your-username>/code-review.
profile create flags
| Flag | Purpose |
|---|---|
--context <text> | Free-form descriptor of what this profile is for. Appears in the synthesized prompt and biases LLM passes. e.g. coding, "code review at a 50-person startup". Default: general. |
--category <slug> | Curated category for the /explore listing on cotext.io. One of: general, coding, writing, research, creative, learning, business, productivity, analysis, support. Defaults to derive from --context. |
--starter <pack> | Seed entries + metrics from a starter pack |
--description <text> | Free-form note kept as customInstructions |
--activate | Make this the active profile after creation |
See CLI & MCP reference for the
difference between --context (free-form, shapes the prompt) and
--category (curated, shapes the public listing).
Starter pack ids: code-review, email-writing, research,
daily-driver, tutor, patient-explainer, strategic,
creative-writing, brainstorm, sprint-planner, data-analyst,
support-helper.
Sending signals against the cloud
Same syntax as folder mode — the CLI routes through /api/signals
instead of dropping into inbox/:
cotext signal "be more concise" --type dislike
Cloud-side signals are stored until a local drainer (the browser
extension or cotext daemon on your machine) picks them up, runs
interpretation locally, and applies the resulting rule.
4. Wire MCP into Claude Code / Codex
The MCP server exposes your active profile to any MCP-aware client.
One-command install
cotext mcp-install
Registers cotext-mcp with both Claude Code (~/.claude.json) and
Codex (~/.codex/config.toml) in one shot. Idempotent — re-running
is a no-op if the entry already exists.
cotext mcp-install --claude # only Claude Code
cotext mcp-install --codex # only Codex
cotext mcp-install --print-only # show the config snippets, don't write
Restart your AI client (Claude Code, Codex) after this.
Teach the agent when to use it
Pipe the canonical snippet into your CLAUDE.md (or AGENTS.md
for Codex):
cotext mcp-prompt >> ~/.claude/CLAUDE.md
# or scoped to a single project:
cotext mcp-prompt >> ./CLAUDE.md
The snippet tells the agent to call get_preferences at session
start and record_signal whenever you express a preference inline
("be more concise", "this format is perfect").
What MCP exposes
| URI | Description |
|---|---|
cotext://preferences/active | Active profile's prompt |
cotext://profiles | JSON summary of all profiles |
cotext://profiles/{slug} | One specific profile |
| Tool | What it does |
|---|---|
get_preferences | Returns the current prompt |
list_profiles | Lists all profiles |
record_signal | Drops a feedback signal into the inbox or /api/signals |
5. Headless mode: cotext daemon
If you want to run Cotext entirely without the browser extension —
on a server, in a headless workflow, or just on principle — the
daemon command does what the extension normally does.
The daemon supports three providers — Ollama (local), Anthropic Claude (cloud), and OpenAI (cloud). Pick whichever fits.
# Default — Ollama running locally
ollama serve &
ollama pull qwen3.5:4b
cotext init
cotext daemon
# Anthropic Claude — no Ollama install needed
export COTEXT_ANTHROPIC_KEY=sk-ant-...
cotext daemon --provider anthropic
# OpenAI
export COTEXT_OPENAI_KEY=sk-...
cotext daemon --provider openai
The daemon polls the folder's inbox/ every 1.5s, interprets each
new signal via the chosen provider, appends rules to the active
profile, runs the fact-extraction pass to pick up user-stated values
(name, role, default language, …), and re-renders the prompt
markdown. Every Nth appended entry (default 3, configurable via
--synth-every) it runs the full library + prose synthesis to
dedupe and rewrite the polished prompt — same output as the
extension.
In another terminal:
cotext signal "be more concise" --type dislike \
--response "$(cat /tmp/claude-output.txt)"
Within seconds, the daemon's stdout shows the new rule landing on
the active profile. After a few signals the synthesis line will
print too, e.g. synthesis: 5 → 4 entries on "daily" (Direct & Decisive).
For users on cloud providers who want tighter spend control:
cotext daemon --synth-every 0 # disable cadence — run cotext synth manually
cotext daemon --synth-every 10 # synthesize every 10 entries
cotext synth # one-off pass, any time
Cloud-only daemon (no folder, no extension)
If you only have COTEXT_API_TOKEN and no folder, the daemon polls
/api/signals/pending against cotext.io instead of a local
inbox/. Same loop — just over HTTP.
Important: don't run the daemon AND the extension at the same time
Both watch the same folder. The race on inbox file deletion is harmless, but two writers to the profile JSON can clobber each other. Pick one mode and stick with it.
Running as a service
The daemon is a foreground process. To run it under launchd (macOS) or systemd (Linux), see the CLI & MCP reference.
Where the CLI fits in
flowchart LR
Ext(["Browser extension"]):::primary
Folder[(".cotext folder<br/><i>*.json · *.md · inbox/</i>")]:::store
CLI["cotext CLI<br/>cotext-mcp"]:::dep
Cloud[("cotext.io")]:::store
Ext -- "writes profiles" --> Folder
CLI -- "reads profiles" --> Folder
CLI -- "writes signals to inbox/" --> Folder
Folder -. "publish (opt-in)" .-> Cloud
CLI -. "fallback when no folder" .-> Cloud
classDef primary fill:#0c111f,stroke:#5A96FF,stroke-width:2px,color:#ffffff
classDef store fill:#050810,stroke:#0038A8,color:#d9d9d9
classDef dep fill:#0a0e1a,stroke:#1f2a45,color:#c0c8db
The folder is the system's interop seam. The browser extension is
the only writer of profile artifacts; the CLI / MCP / local server
only WRITE signals (into inbox/) and READ profiles. This is why
the CLI doesn't need a daemon or socket to coexist with the
extension — the file system IS the protocol.
For the full architecture write-up, see Architecture.
Next steps
- CLI & MCP reference — full command list, all flags, all MCP tools/resources
- Architecture — internals, contracts, data flow
- Self-hosting — running your own
cotext.io - Telemetry — opt-in event schema and receiver recipes