CodeDocs Vault

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:

  1. Scaffolds content into a .ok/ directory (OK_DIR = '.ok', packages/core/src/constants/ok-dir.ts:3), with .ok/config.yml as the project marker.
  2. Detects installed harnessesdetectInstalledEditors probes each target's detectPath and keeps the ones whose directory exists (init.ts:1088-1096, isEditorTargetAvailable at :364-371). No flags needed: if ~/.codex/ exists, Codex gets wired.
  3. 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 right topLevelKey, with atomic writes.
  4. 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).
  5. Installs the project skill — drops SKILL.md into each harness's skill directory (init.ts:702-708, write-project-skill.ts:57-91), sourced from the bundled packages/server/assets/skills/project/SKILL.md. See 05_skills.md.
  6. 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:

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_integration06_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)

  1. 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.
  2. Adapter-as-data. EditorMcpTarget reduces "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.
  3. 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.
  4. Bidirectional handoff with telemetry. "Open with AI" + HandoffStatsLine is a dispatch primitive with built-in observability — you can see which agent got which task and whether the handoff succeeded.
  5. Detect, don't ask. Probing ~/.codex/, ~/.cursor/, ~/.claude/ to auto-wire what's installed is a better onboarding than a checklist.