Harness Integration — the bridges
This is the doc the study was commissioned for. OpenKnowledge integrates with Claude Code, Codex, Cursor, and OpenCode through one disciplined mechanism. The whole strategy fits in a sentence:
Don't invent a protocol — be the MCP server every harness already knows how to load, and ship a skill that teaches the agent to use you.
Compare this to Omnigent's inject-without-owning:
Omnigent attaches to each agent at whatever messy seam it exposes (tmux paste,
app-server RPC, transcript tailing). OpenKnowledge has it easier because it
integrates in the other direction — it is a tool, not an orchestrator —
so the only per-harness difference is where the MCP config file lives and what
shape its JSON/TOML takes. That difference is captured in one adapter.
1. The EditorMcpTarget adapter
One interface, five implementations. This is the entire bridge layer.
// packages/cli/src/commands/editors.ts:222-234
export interface EditorMcpTarget {
id: EditorId; // 'claude' | 'claude-desktop' | 'cursor' | 'codex' | 'opencode'
label: string;
configPath: (cwd, home?) => string; // user-level config location
format: 'json' | 'toml';
topLevelKey: 'mcpServers' | 'servers' | 'mcp_servers' | 'mcp';
serverName: (cwd) => string;
buildEntry: (cwd, options?) => Record<string, unknown>; // the MCP server entry
scope: 'project' | 'global';
detectPath?: (cwd, home?) => string; // existence ⇒ "this harness is installed"
projectConfigPath?: (cwd) => string; // project-scoped MCP config
projectSkillPath?: (cwd) => string; // where to drop SKILL.md
}The concrete registry is EDITOR_TARGETS (editors.ts:236-300):
| Harness | configPath |
format |
topLevelKey |
project config | skill path |
|---|---|---|---|---|---|
| Claude Code | ~/.claude.json |
json | mcpServers |
.mcp.json |
.claude/skills/open-knowledge/SKILL.md |
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
json | mcpServers |
— | — |
| Cursor | ~/.cursor/mcp.json |
json | mcpServers |
.cursor/mcp.json |
.cursor/skills/open-knowledge/SKILL.md |
| Codex | ~/.codex/config.toml |
toml | mcp_servers |
.codex/config.toml |
.agents/skills/open-knowledge/SKILL.md |
| OpenCode | ~/.config/opencode/opencode.json |
json | mcp |
opencode.json |
.agents/skills/open-knowledge/SKILL.md |
The lesson worth stealing: the messy, per-vendor knowledge (config path, file format, nesting key, skill directory) is data in a struct, not branching code. Adding a sixth harness is appending one ~12-line object, not editing five functions. (This is the outbound twin of Omnigent's adapter: Omnigent normalizes N agents on the way out; OK normalizes N config files on the way in.)
2. What ok init actually writes
runInit (packages/cli/src/commands/init.ts:587-769) does six things:
- Scaffolds content into a
.ok/directory (OK_DIR = '.ok',packages/core/src/constants/ok-dir.ts:3), with.ok/config.ymlas the project marker. - Detects installed harnesses —
detectInstalledEditorsprobes each target'sdetectPathand keeps the ones whose directory exists (init.ts:1088-1096,isEditorTargetAvailableat:364-371). No flags needed: if~/.codex/exists, Codex gets wired. - Writes the MCP server entry into each detected harness's user-level config
(
writeEditorMcpConfig,init.ts:373-505) — JSON or TOML per the adapter, under the righttopLevelKey, with atomic writes. - Writes project-scoped MCP config for the four harnesses that support it
(
.mcp.json,.cursor/mcp.json,.codex/config.toml,opencode.json) so a cloned repo carries its own wiring (init.ts:686-699). - Installs the project skill — drops
SKILL.mdinto each harness's skill directory (init.ts:702-708,write-project-skill.ts:57-91), sourced from the bundledpackages/server/assets/skills/project/SKILL.md. See05_skills.md. - Configures git/sharing — chooses whether the OK config is committed or
kept local via
.git/info/exclude(init.ts:788-827).
The actual entry written for most harnesses is a resilient shell launcher, not a hard-coded path:
// Claude Code → ~/.claude.json (buildManagedServerEntry, editors.ts:86-99)
"open-knowledge": {
"command": "/bin/sh",
"args": ["-l", "-c", "<CHAIN_V1>"] // editors.ts:20-32
}OpenCode gets a structurally different entry (the same idea, OpenCode's schema):
// opencode.json (buildOpenCodeEntry, editors.ts:101-116)
"open-knowledge": { "type": "local", "enabled": true,
"command": ["/bin/sh", "-l", "-c", "<CHAIN_V1>"] }The CHAIN_V1 launcher
CHAIN_V1 is a sh script that tries, in order: the bundled desktop app's
CLI (~/Applications/OpenKnowledge.app/.../bin/ok.sh), then an npx/global
install, then a clear error. One launcher string serves every harness
(editors.ts:20-32). The payoff: the integration survives an outdated bundled
app, a cleared npm cache, or an offline machine — it degrades through the chain
instead of breaking. (This is OK's analogue of Omnigent's
secretless-credential-proxy thinking:
make the integration robust at the seam so nothing downstream has to care.)
3. "Open with AI" — the bidirectional handoff
Wiring is only half of it. The other half is dispatching work from the editor to an agent. From the OK editor you pick Open with AI ▸ Claude / Codex / Cursor on a file, folder, or whole project (sidebar context menu, the macOS File → Open with AI menu, or the bottom Ask AI composer). What happens:
- Claude Code / Codex: OK hands the agent a short prompt asking it to open
the target in OpenKnowledge's web preview, then the agent grounds the rest of
the conversation through MCP (
docs/content/integrations/claude-code.mdx,codex.mdx). - Cursor: a two-step handoff — OK first spawns
cursor <projectDir>to open the workspace (packages/desktop/src/main/ipc-handlers.ts:147-181,resolveCursorSpawnInvocation), then fires acursor://deep-link with the prompt. In the web app the spawn step falls back to a localPOST /api/spawn-cursor(docs/content/integrations/cursor.mdx). - Every handoff is recorded as a
HandoffStatsLinetracking the target (claude-cowork/claude-code/codex/cursor), outcome, scope, and host (packages/desktop/src/shared/ipc-channels.ts:109-124).
So the loop closes: human dispatches from the editor → agent opens the doc and
edits over MCP → edits render live back in the editor (02_harness_integration
→ 06_collaboration_crdt).
4. OpenCode and the fully-local loop
OpenCode is the interesting tail case: it is model-agnostic, so OK documents a
fully local edit-your-knowledge-base loop — point OpenCode at Ollama / LM
Studio / any OpenAI-compatible endpoint in the same opencode.json that holds
the OK MCP entry (docs/content/integrations/opencode.mdx). The one hard
requirement the docs call out: the model must support tool/function calling,
"that's how it drives OpenKnowledge's exec / write / edit tools; a
chat-only model will connect but never call them." A clean reminder that the MCP
surface is only as useful as the agent's ability to call tools.
5. Desktop first-launch wiring
When installed from the macOS DMG, the desktop app re-runs the same wiring on
first launch: a consent dialog offers to wire up every MCP-capable agent it
detects (docs/content/integrations/claude-code.mdx), tracked via
.ok/mcp-status.json (packages/desktop/src/main/mcp-wiring.ts:1-30). Delete
that file to re-trigger the dialog. The desktop path can also rewireClaudeMcp
on demand (08_desktop_runtime.md).
What's worth stealing (for Swisscheese)
- Integrate at the protocol the agent already loads. Swisscheese orchestrates from above; OK shows the from-the-side complement — exposing your own capabilities to Claude Code / Codex as an MCP server they mount is far cheaper than driving them. Worth having both directions in the toolbox.
- Adapter-as-data.
EditorMcpTargetreduces "support harness X" to a struct of {config path, format, key, skill dir}. A code-review meta-harness that has to read each agent's config (to know what tools it can already see) wants exactly this registry. - Resilient launcher chain. One
sh -l -c <chain>that falls through bundled → npx → error means the integration never silently dies. Steal it for however Swisscheese launches worker CLIs. - Bidirectional handoff with telemetry. "Open with AI" +
HandoffStatsLineis a dispatch primitive with built-in observability — you can see which agent got which task and whether the handoff succeeded. - Detect, don't ask. Probing
~/.codex/,~/.cursor/,~/.claude/to auto-wire what's installed is a better onboarding than a checklist.