@autonomous-ai/harness-mcp 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +47 -2
  2. package/bin.mjs +6 -0
  3. package/index.mjs +99 -0
  4. package/package.json +25 -4
package/README.md CHANGED
@@ -1,3 +1,48 @@
1
- # Temporary Holding Version
1
+ # @autonomous-ai/harness-mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [Harness](https://harness.autonomous.ai/agents) as an MCP server, for clients that install servers from npm.
4
+
5
+ Harness runs coding agents (Claude Code, Codex) in sessions of their own, on this computer or the person's other
6
+ machines, and the person can watch and join every one in the Harness app. With this server an agent can start
7
+ harnesses, wait for them, read their answers and close them:
8
+
9
+ | Tool | What it does |
10
+ |---|---|
11
+ | `harness_whoami` | who you are to Harness, and whether you may start harnesses now: call it first |
12
+ | `harness_list` | the harnesses here: state, who started each, what each is about |
13
+ | `harness_start` | start an agent on a task in a folder or a new project, by name |
14
+ | `harness_wait` | wait until harnesses stop working |
15
+ | `harness_read` | a harness's last ask and its answer |
16
+ | `harness_close` | close a harness you started |
17
+
18
+ An agent may give work to, and close, only the harnesses it started.
19
+
20
+ ## Install
21
+
22
+ Harness itself (macOS or Linux; no account needed for harnesses on this computer):
23
+
24
+ ```
25
+ curl -fsSL https://harness.autonomous.ai/cli/install.sh | bash
26
+ harness start
27
+ ```
28
+
29
+ On Linux the installer installs tmux and curl with apt-get, which asks for your password (or run
30
+ `sudo apt-get install -y tmux curl` first).
31
+
32
+ Then add the server to your client, for example:
33
+
34
+ ```
35
+ claude mcp add harness -- npx -y @autonomous-ai/harness-mcp
36
+ ```
37
+
38
+ ```json
39
+ { "mcpServers": { "harness": { "command": "npx", "args": ["-y", "@autonomous-ai/harness-mcp"] } } }
40
+ ```
41
+
42
+ This package runs `harness mcp` from the installed CLI (or `HARNESS_BIN`). Where Harness is not installed, the
43
+ server offers one tool, `harness_setup`, that says how to install it.
44
+
45
+ Claude Code and Codex can also install the Harness plugin, which brings a skill with it: see
46
+ [harness.autonomous.ai/agents](https://harness.autonomous.ai/agents).
47
+
48
+ MIT licensed. Source: [autonomous-ai/openharness](https://github.com/autonomous-ai/openharness/tree/main/cli/packages/harness-mcp).
package/bin.mjs ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { findHarness, runHarness, serveSetup } from './index.mjs'
3
+
4
+ const harness = findHarness()
5
+ if (harness) process.exitCode = await runHarness(harness)
6
+ else await serveSetup()
package/index.mjs ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * `npx -y @autonomous-ai/harness-mcp`: Harness's MCP server for clients that install servers from npm. Harness is
3
+ * not an npm package (its installer brings its own Node and tmux, cli/scripts/install.sh), so this finds the
4
+ * `harness` command and runs `harness mcp` in its place. Where Harness is not installed it still answers, with one
5
+ * tool that says how to install it: an agent that found Harness in a directory can then ask its person to.
6
+ *
7
+ * No dependencies: it runs before anything is installed.
8
+ */
9
+ import { spawn } from 'node:child_process'
10
+ import { accessSync, constants } from 'node:fs'
11
+ import { homedir } from 'node:os'
12
+ import { delimiter, join } from 'node:path'
13
+ import { createInterface } from 'node:readline'
14
+
15
+ export const INSTALL = 'curl -fsSL https://harness.autonomous.ai/cli/install.sh | bash'
16
+
17
+ /** Where `harness` is: HARNESS_BIN, else the PATH, else where the installer links it (a client may start this with
18
+ * a PATH that leaves out ~/.local/bin). */
19
+ export function findHarness(env = process.env, home = homedir(), executable = isExecutable) {
20
+ const candidates = [
21
+ env.HARNESS_BIN,
22
+ ...(env.PATH ?? '').split(delimiter).filter(Boolean).map((dir) => join(dir, 'harness')),
23
+ join(home, '.local', 'bin', 'harness'),
24
+ ].filter(Boolean)
25
+ return candidates.find((path) => executable(path)) ?? null
26
+ }
27
+
28
+ function isExecutable(path) {
29
+ try { accessSync(path, constants.X_OK); return true } catch { return false }
30
+ }
31
+
32
+ const SETUP_TEXT = (platform) => platform === 'win32'
33
+ ? 'Harness runs on macOS and Linux. On Windows, install it inside WSL, and run this server from there.'
34
+ : [
35
+ 'Harness is not installed on this computer. It runs coding agents (Claude Code, Codex) in sessions of their own that the person can watch, and gives you tools to start, wait for, read and close them.',
36
+ '',
37
+ 'To install it, ask the person, then run:',
38
+ ` ${INSTALL}`,
39
+ ' harness start',
40
+ '',
41
+ 'Then restart this MCP server (or your client) to get the Harness tools, and call harness_whoami: it says whether you may start harnesses.',
42
+ 'Only the person can let you: they sign in (harness login, in a browser) and turn on "Agents can start other agents"',
43
+ '(Settings ▸ Experimental, or harness settings agents-start-agents on in their own terminal). Ask them; until then, do the work yourself.',
44
+ 'More: https://harness.autonomous.ai/agents',
45
+ ].join('\n')
46
+
47
+ /** The answer to one JSON-RPC message while Harness is not installed, or null for a notification. */
48
+ export function setupAnswer(message, platform = process.platform) {
49
+ const id = message?.id
50
+ const isRequest = id !== undefined && id !== null
51
+ const reply = (result) => ({ jsonrpc: '2.0', id, result })
52
+ switch (message?.method) {
53
+ case 'initialize':
54
+ return reply({
55
+ protocolVersion: typeof message.params?.protocolVersion === 'string' ? message.params.protocolVersion : '2025-06-18',
56
+ capabilities: { tools: {} },
57
+ serverInfo: { name: 'harness', title: 'Harness (not installed)', version: '0.1.0' },
58
+ instructions: SETUP_TEXT(platform),
59
+ })
60
+ case 'ping': return reply({})
61
+ case 'tools/list':
62
+ return reply({ tools: [{
63
+ name: 'harness_setup',
64
+ description: 'Harness is not installed here. Call this for how to install it, which gives you tools to start, wait for, read and close other coding agents.',
65
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
66
+ annotations: { readOnlyHint: true },
67
+ }] })
68
+ case 'tools/call':
69
+ if (message.params?.name !== 'harness_setup') return { jsonrpc: '2.0', id, error: { code: -32602, message: `Unknown tool: ${String(message.params?.name)}` } }
70
+ return reply({ content: [{ type: 'text', text: SETUP_TEXT(platform) }] })
71
+ default:
72
+ return isRequest ? { jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${String(message?.method)}` } } : null
73
+ }
74
+ }
75
+
76
+ /** Serve [setupAnswer] on newline-delimited JSON-RPC until the input ends. */
77
+ export async function serveSetup(input = process.stdin, output = process.stdout, platform = process.platform) {
78
+ for await (const line of createInterface({ input, crlfDelay: Infinity })) {
79
+ if (!line.trim()) continue
80
+ let message
81
+ try { message = JSON.parse(line) } catch {
82
+ output.write(`${JSON.stringify({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'Parse error' } })}\n`)
83
+ continue
84
+ }
85
+ const reply = setupAnswer(message, platform)
86
+ if (reply) output.write(`${JSON.stringify(reply)}\n`)
87
+ }
88
+ }
89
+
90
+ /** Run `harness mcp` in this process's place: its stdio, its exit, and the signals a client stops it with. */
91
+ export function runHarness(path, run = spawn) {
92
+ return new Promise((done) => {
93
+ const child = run(path, ['mcp'], { stdio: 'inherit' })
94
+ const forward = (signal) => () => child.kill(signal)
95
+ for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) process.on(signal, forward(signal))
96
+ child.on('error', () => done(1))
97
+ child.on('exit', (code, signal) => done(code ?? (signal ? 1 : 0)))
98
+ })
99
+ }
package/package.json CHANGED
@@ -1,6 +1,27 @@
1
1
  {
2
2
  "name": "@autonomous-ai/harness-mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.0",
4
+ "description": "Harness as an MCP server: start, wait for, read and close Claude Code and Codex agents the person can watch.",
5
+ "mcpName": "ai.autonomous.harness/harness",
6
+ "type": "module",
7
+ "bin": {
8
+ "harness-mcp": "bin.mjs"
9
+ },
10
+ "exports": "./index.mjs",
11
+ "files": [
12
+ "bin.mjs",
13
+ "index.mjs",
14
+ "README.md"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18"
18
+ },
19
+ "keywords": ["mcp", "mcp-server", "agents", "claude-code", "codex", "parallel", "harness"],
20
+ "homepage": "https://harness.autonomous.ai/agents",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/autonomous-ai/openharness.git",
24
+ "directory": "cli/packages/harness-mcp"
25
+ },
26
+ "license": "MIT"
27
+ }