Skip to main content

Exposing harnessd as an MCP Server

The Model Context Protocol (MCP) is a JSON-RPC 2.0 based standard for connecting AI models to tools and data sources. harnessd supports MCP in both directions: it can consume tools from external MCP servers (acting as an MCP client), and it can expose itself as an MCP server so that Claude Desktop and other MCP hosts can drive it directly.

This page covers the second direction — harnessd as an MCP server. There are three distinct surfaces, each suited to a different deployment pattern:

SurfaceTransportBest for
HTTP MCP server (/mcp)HTTP (JSON-RPC POST + SSE GET)Programmatic clients, CI integrations, agents calling agents
stdio MCP server (--mcp)stdio (JSON-RPC over stdin/stdout)Running harnessd directly as an MCP tool inside a host process
harness-mcp proxystdio → HTTP proxyConnecting Claude Desktop to an already-running harnessd instance

These are three separate surfaces with different tool sets and use cases. Connecting Claude Desktop to harnessd --mcp (stdio mode) exposes the full harness tool catalog. Connecting via harness-mcp exposes five task-management tools that drive the harnessd REST API. Choose based on whether you need the full catalog or a curated run-management interface.


HTTP MCP server (/mcp)

When harnessd starts in normal HTTP mode, it mounts an MCP server on the same port as the REST API at the path /mcp. No extra configuration is needed — the endpoint is always available alongside /v1/....

Endpoints

MethodPathPurpose
POST /mcpJSON-RPC 2.0Tool calls, initialize, tools/list
GET /mcpSSE streamJSON-RPC 2.0 notifications for subscribed runs

The MCP server advertises protocol version "2025-11-25" and identifies itself as name = "go-agent-harness", version = "1.0".

The 10 tools

Tools exposed by the HTTP MCP server

ToolRequired argumentsDescription
start_runpromptSubmit a new agent run; returns run_id
get_run_statusrun_idCurrent status and output
list_runsList all known runs
steer_runrun_id, messageInject a guidance message into an active run
submit_user_inputrun_id, inputRespond when a run is paused at waiting_for_user
subscribe_runrun_idRegister for SSE notifications; returns stream_id
list_conversationsPaginated conversation list (default limit 20)
get_conversationconversation_idFull message history for a conversation
search_conversationsqueryFull-text search across conversations
compact_conversationconversation_idTrigger context compaction on a conversation

The harnessd HTTP MCP server is always constructed via mcpserver.NewServer (see runtime_container.go:188), which never sets a ConversationInterface. NewServerWithConversations is not wired into any production code path. As a result, list_conversations, search_conversations, and compact_conversation always return "conversations not available" through the /mcp endpoint — there is no deployment configuration that enables them. The exception is get_conversation, which is backed by runner.ConversationMessages (via mcpRunnerAdapter) and does work.

None of the harnessd MCP server surfaces expose MCP resources (resources/list / resources/read). The clientManagerRegistry used for outbound MCP client calls also returns an empty list for ListResources and an error for ReadResource (mcp_setup.go:49-57).

SSE notifications

When a client calls subscribe_run, the SSE stream from GET /mcp delivers JSON-RPC 2.0 notifications as events arrive. Two notification methods are published:

  • run/event — emitted on non-terminal status changes; includes run_id, event_type: "status_changed", and status.
  • run/completed — emitted when a run reaches a terminal state ("completed" or "failed"); includes run_id, status, cost_usd, and error. Note: cost_usd is currently hardcoded to 0 in the poller (poller.go:119) and does not reflect the run's actual cost — fetch real cost via get_run_status or the REST run object instead.

The SSE keepalive ping interval is controlled by HARNESS_SSE_KEEPALIVE_SECONDS (default: 15 seconds).


stdio MCP server (--mcp)

Running harnessd --mcp starts an MCP server over stdin/stdout instead of HTTP. This mode exposes the full harness tool catalog — both TierCore and TierDeferred tools — as MCP tools. Each tool's description includes tier and tag metadata appended in the format [tier:X tags:Y,Z].

This surface is useful when a host process (another agent, an IDE, or a script) wants to launch harnessd as a subprocess and interact with it directly through stdio JSON-RPC.

Workspace resolution order

The workspace root is resolved from these sources, in priority order:

  1. --mcp-workspace flag
  2. HARNESS_WORKSPACE environment variable
  3. Default: "." (the current directory)

Build and run

# Build
go build ./cmd/harnessd

# Start in stdio MCP mode (workspace defaults to current directory)
./harnessd --mcp

# Start with an explicit workspace
./harnessd --mcp --mcp-workspace /path/to/workspace

In stdio mode harnessd does not listen on any TCP port — all communication happens over stdin/stdout. The process exits when the host closes the stdin pipe.


The harness-mcp proxy

harness-mcp is a standalone binary that bridges a stdio MCP host (such as Claude Desktop) to a harnessd instance that is already running over HTTP. The proxy reads JSON-RPC from stdin, translates each call into a REST request against harnessd, and writes the JSON-RPC response back to stdout.

This is the recommended path for Claude Desktop integration: keep harnessd running as a persistent daemon and configure Claude Desktop to launch harness-mcp as a subprocess.

Architecture

Claude Desktop
│ (stdio JSON-RPC)

harness-mcp (StdioTransport → Dispatcher → HarnessClient)
│ (HTTP REST)

harnessd (running at HARNESS_ADDR)

The proxy advertises name = "harness-mcp", version = "1.0.0" and protocol version "2025-11-25".

The 5 tools

Tools exposed by harness-mcp

ToolRequired argumentsOptional argumentsDescription
start_runpromptmodel, conversation_id, max_steps, max_cost_usdStart a new agent run
get_run_statusrun_idReturns status, messages, cost_usd, and error
wait_for_runrun_idtimeout_seconds (default 300)Polls every 2 seconds until the run reaches completed, failed, or waiting_for_user
continue_runrun_id, promptFetches the previous run's conversation_id and starts a new run in that conversation
list_runsconversation_id, limit (default 20)List runs, optionally filtered by conversation

The proxy makes direct REST calls to harnessd:

  • POST /v1/runs for start_run
  • GET /v1/runs/{runID} for get_run_status and wait_for_run
  • GET /v1/runs?conversation_id=&limit= for list_runs

Build the proxy

go build -o bin/harness-mcp ./cmd/harness-mcp

Configuration

Environment variableDefaultDescription
HARNESS_ADDRhttp://localhost:8080Base URL of the running harnessd instance

Claude Desktop registration

To register harness-mcp with Claude Desktop, edit ~/Library/Application Support/Claude/claude_desktop_config.json and add an entry under mcpServers:

{
"mcpServers": {
"harness": {
"command": "/path/to/bin/harness-mcp",
"env": {
"HARNESS_ADDR": "http://localhost:8080"
}
}
}
}

Replace /path/to/bin/harness-mcp with the absolute path to the binary you built above. Claude Desktop launches harness-mcp as a subprocess when it starts; the proxy connects to harnessd at the address specified by HARNESS_ADDR.

  1. Build harness-mcp

    go build -o bin/harness-mcp ./cmd/harness-mcp

    Note the absolute path to the resulting binary.

  2. Start harnessd

    Start harnessd in a separate terminal or as a background service. The proxy expects it to be reachable at HARNESS_ADDR (default http://localhost:8080).

    OPENAI_API_KEY=sk-... ./harnessd
  3. Edit claude_desktop_config.json

    Add the mcpServers entry shown above. Use the absolute path from step 1.

  4. Restart Claude Desktop

    Quit and reopen Claude Desktop. The "harness" MCP server will appear in the tool list. You can ask Claude to start a run, check run status, or wait for a long-running task to complete.

You can also use harnessd --mcp (stdio mode) directly as the Claude Desktop command without the proxy. The difference is that --mcp mode exposes the full harness tool catalog, while harness-mcp exposes only the five curated run-management tools. The proxy also lets you share one persistent harnessd daemon across multiple clients simultaneously.


Choosing the right surface

Use the HTTP MCP endpoint when:

  • You are integrating from another service or agent over a network connection.
  • You need live SSE notifications via subscribe_run.
  • You want a single harnessd process to serve many concurrent MCP clients.

The endpoint lives at POST /mcp (tool calls) and GET /mcp (SSE) on the same port as the REST API (default 8080). No extra build step is required.


Next steps