Orchestrator MCP server

harn mcp serve exposes a local Harn orchestrator as an MCP server so any MCP client can fire triggers, inspect queues, replay events, and read runtime state without a Harn-specific adapter.

This page is the canonical reference for the orchestrator control-plane MCP server. For the general MCP client/server guide, see MCP, ACP, and A2A integration; for the full protocol routing table, see Protocol support matrix.

The server is aimed at closed-loop agent clients that already know how to speak MCP, including:

  • Cursor Composer
  • Claude Desktop
  • Claude Code
  • LangChain MCP adapters

Quickstart

Hook Harn into Cursor Composer in 3 steps:

  1. Start the server from the workspace that owns your orchestrator manifest.
harn mcp serve --config ./harn.toml --state-dir ./.harn/orchestrator
  1. Point Cursor at the command as a stdio MCP server.
{
  "mcpServers": {
    "harn": {
      "command": "harn",
      "args": ["mcp", "serve", "--config", "/absolute/path/to/harn.toml", "--state-dir", "/absolute/path/to/.harn/orchestrator"]
    }
  }
}
  1. Ask the client to call Harn tools such as harn.trigger.list or harn.orchestrator.inspect.

Example prompts:

  • "List the Harn triggers in this workspace."
  • "Fire the cron-ok trigger with an empty payload."
  • "Show the Harn DLQ and retry the newest entry."
  • "Scan this diff for secrets before I open a PR."

Transports

harn mcp serve supports:

  • stdio for local spawned clients. This is the default.
  • http for remote MCP clients.

HTTP mode exposes:

  • Streamable HTTP POST, GET, and DELETE at --path (default /mcp)
  • Legacy SSE GET at --sse-path (default /sse), marked with Deprecation: true
  • Legacy SSE POST at --messages-path (default /messages), marked with Deprecation: true

Streamable HTTP clients initialize with POST /mcp. The initialize response includes an Mcp-Session-Id header, and clients must echo that header on later POST, GET, and DELETE requests. Requests normally return one application/json JSON-RPC response; clients that only accept text/event-stream receive an SSE stream containing message events. GET /mcp opens the server-to-client SSE stream for notifications, and DELETE /mcp terminates the session.

Example:

harn mcp serve \
  --config ./harn.toml \
  --state-dir ./.harn/orchestrator \
  --transport http \
  --bind 127.0.0.1:8765

Auth

MCP OAuth applies only to HTTP transports. Local stdio servers are launched by the client process and should receive credentials through the launch environment or client-specific secret configuration, not through HTTP WWW-Authenticate, protected-resource metadata, or bearer-token refresh.

For HTTP transports, prefer OAuth resource-server mode. Set HARN_MCP_OAUTH_AUTHORIZATION_SERVERS to a comma-separated list of issuer URLs and configure exactly how the MCP server validates bearer tokens:

  • HARN_MCP_OAUTH_INTROSPECTION_URL for opaque-token introspection, optionally with HARN_MCP_OAUTH_INTROSPECTION_CLIENT_ID, HARN_MCP_OAUTH_INTROSPECTION_CLIENT_SECRET, or HARN_MCP_OAUTH_INTROSPECTION_TOKEN
  • HARN_MCP_OAUTH_JWKS_URL for JWT validation against a JWKS endpoint

Recommended production settings:

  • HARN_MCP_OAUTH_RESOURCE to the canonical MCP resource URI clients pass as the RFC 8707 resource value, for example https://mcp.example.com/mcp
  • HARN_MCP_OAUTH_AUDIENCE when tokens use an audience value different from the resource URI
  • HARN_MCP_OAUTH_ISSUER when tokens should be pinned to one issuer
  • HARN_MCP_OAUTH_SCOPES as a comma- or space-separated list of scopes required for the MCP control plane

When OAuth mode is configured, unauthenticated HTTP requests return 401 with WWW-Authenticate: Bearer resource_metadata="...". The challenge includes scope="..." when HARN_MCP_OAUTH_SCOPES is set. The server publishes RFC 9728 protected resource metadata at both:

  • /.well-known/oauth-protected-resource
  • /.well-known/oauth-protected-resource/<mcp-path>

The metadata includes authorization_servers, the canonical resource, and scopes_supported when scopes are configured. Access tokens must be sent on every HTTP request as Authorization: Bearer <token>. Tokens are rejected when they are inactive, expired, issued for a different audience/resource or issuer, or missing required scopes.

HARN_ORCHESTRATOR_API_KEYS remains available as a legacy compatibility mode. Set it to a comma-separated key list to require API keys.

HTTP clients can authenticate with either:

  • Authorization: Bearer <key>
  • x-api-key: <key>

Legacy clients that do not yet send an Authorization header can authenticate during initialize using a deprecated Harn extension field:

{
  "capabilities": {
    "harn": {
      "apiKey": "test-key"
    }
  }
}

The server prints a warning when this apiKey initialize extension is used. If neither OAuth mode nor HARN_ORCHESTRATOR_API_KEYS is configured, the MCP server runs without auth.

Tool catalog

MCP list endpoints use cursor pagination. tools/list, resources/list, resources/templates/list, prompts/list, and tasks/list return up to 100 entries by default and include nextCursor when more entries are available. Set HARN_MCP_LIST_PAGE_SIZE to a positive integer to change the per-page limit for large local catalogs.

harn.secret_scan

Scans arbitrary text or diffs for high-signal leaked credentials and returns a redacted finding list. Use it before commit or PR-open flows. The server also accepts the legacy alias harn::secret_scan.

Input:

{
  "content": "token = \"ghp_example...\""
}

Returns a JSON array of findings. Each finding includes:

  • detector
  • source
  • title
  • line
  • column_start
  • column_end
  • start_offset
  • end_offset
  • redacted
  • fingerprint

harn.trigger.fire

Dispatch a trigger inline.

Input:

{
  "trigger_id": "cron-ok",
  "payload": {}
}

Returns the dispatch handle summary including event_id and status.

harn.trigger.list

Lists manifest-backed triggers with:

  • trigger_id
  • kind
  • provider
  • when
  • handler
  • version
  • state
  • metrics

harn.trigger.replay

Replays a historical event. Supports as_of to resolve bindings against a historical timestamp when needed.

{
  "event_id": "trigger_evt_123",
  "as_of": "2026-04-19T18:00:00Z"
}

harn.orchestrator.queue

Returns queue counts plus recent head previews for:

  • inbox
  • outbox
  • attempts
  • DLQ

harn.orchestrator.dlq.list

Lists pending dead-letter entries.

harn.orchestrator.dlq.retry

Retries one DLQ entry by id.

{
  "entry_id": "dlq_123"
}

harn.orchestrator.inspect

Returns the dispatcher snapshot, trigger-centric inspect data, persisted orchestrator snapshot, flow-control state, and recent dispatch records.

harn.trust.query

Placeholder trust-graph surface. Today it returns:

{
  "results": []
}

Resources

The server exposes these MCP resources:

  • harn://manifest
  • harn://topic/trigger.inbox
  • harn://topic/trigger.outbox
  • harn://topic/agent.transcript.<id>
  • harn://event/<event_id>
  • harn://dlq/<entry_id>

harn://event/<event_id> includes the recorded trigger event plus related outbox/attempt/DLQ/action-graph trace entries.

resources/templates/list advertises the parameterized forms clients can use after discovering concrete examples:

  • harn://topic/{name}
  • harn://event/{event_id}
  • harn://dlq/{entry_id}

completion/complete supports those resource template arguments. Topic completion includes static trigger topics and discovered agent transcript topics; event and DLQ completion use recorded trigger and pending DLQ ids.

harn://topic/* resources expose recent EventLog entries for subscribable orchestrator topics. resources/subscribe starts a live EventLog watcher for the resource URI and the server sends notifications/resources/updated when a new record is appended. resources/unsubscribe stops delivery for that client connection. Streamable HTTP clients receive these notifications over GET /mcp; stdio and legacy SSE clients receive the same JSON-RPC notification on their existing server-to-client channel.

Prompts

The server exposes .harn.prompt files from the project root and from installed prompt-library packages under .harn/packages/<alias>. TOML front matter can define display metadata and MCP arguments:

---
id = "review"
description = "Review code"
[[arguments]]
name = "code"
description = "Code to review"
required = true
[[arguments]]
name = "language"
required = false
suggestions = ["rust", "typescript", "python"]
---
Review this:
{{ code }}

prompts/get renders the template with the supplied arguments object. completion/complete uses the optional suggestions/completions list on front-matter arguments when a client asks for prompt argument completions. The server advertises tools.listChanged, resources.listChanged, and prompts.listChanged. It emits the corresponding notifications/*/list_changed messages when watched manifest, prompt, lockfile, or package metadata changes.

Protocol support

harn mcp serve negotiates MCP protocol version 2025-11-25. It is a control-plane server for Harn orchestration state, so it supports tools, resources, prompts, completions, logging, MCP tasks, cancellation, progress, and streamable HTTP sessions. It does not expose roots. The orchestrator-mode catalog does not currently issue sampling/createMessage against connected clients (Harn's outbound MCP clients accept inbound sampling — see the client docs).

Method or featureStatus
initialize, notifications/initialized, pingSupported
logging/setLevel, notifications/messageSupported; see Logging notifications
tools/list, tools/callSupported for the Harn tool catalog above
notifications/progress, notifications/cancelledSupported for cancellable work
resources/list, resources/readSupported for manifest, EventLog topic, event, and DLQ resources
resources/templates/listSupported for EventLog topic, trigger event, and DLQ URI patterns
resources/subscribe, resources/unsubscribeSupported for EventLog topic resources; updates emit notifications/resources/updated
prompts/listSupported for .harn.prompt files in the project and prompt-library packages
prompts/getSupported; renders prompt templates with supplied arguments
completion/completeSupported for prompt arguments with front-matter suggestions and orchestrator resource template arguments
elicitation/createSupported on script-driven harn run --serve mcp surfaces via the mcp_elicit(...) builtin (see Elicitation). The orchestrator-mode tool catalog does not currently issue elicitations.
roots/listExplicitly unsupported
sampling/createMessageServer-initiated sampling against the connected client is not emitted by the orchestrator catalog. Harn-as-MCP-client does accept inbound sampling/createMessage (routed to llm_call via the host bridge) — see the client matrix.
tasks/get, tasks/result, tasks/list, tasks/cancelSupported for task-augmented orchestrator tool calls
tools/call with params.taskSupported for tools that advertise optional task execution; rejected with -32602 for tools that advertise execution.taskSupport="forbidden"

Explicitly unsupported methods return a JSON-RPC error with code -32601 and error.data.type = "mcp.unsupportedFeature". Tool calls that request task-augmented execution for a non-taskable tool return -32602 because the request conflicts with the tool's advertised execution metadata.

sampling/createMessage and elicitation/create are client-bound MCP requests. When a client sends either method to a Harn MCP server endpoint, Harn returns an explicit mcp.unsupportedFeature JSON-RPC error instead of treating the request as an ordinary unknown method.

Elicitation

Script-driven MCP servers (those built with mcp_tools(...) / mcp_resource(...) / mcp_prompt(...) and started with harn run --serve mcp or harn serve mcp) can prompt the connected client for structured user input mid-tool-call via the mcp_elicit(...) builtin:

let answer = mcp_elicit({
  message: "Which environment should I deploy to?",
  requestedSchema: {
    type: "object",
    properties: {
      env: { type: "string", enum: ["staging", "production"] },
      confirm: { type: "boolean" }
    },
    required: ["env", "confirm"]
  }
})
// answer is one of:
//   { action: "accept", content: { env: "staging", confirm: true } }
//   { action: "decline" }
//   { action: "cancel" }

The builtin returns the canonical {action, content?} envelope from the MCP elicitation spec. On accept, content is validated against requestedSchema before returning so scripts can rely on its shape. On decline / cancel, content is omitted.

mcp_elicit(...) is only valid while a client connection is active. If called outside a tool / resource / prompt handler — e.g. at pipeline top-level — it raises a structured error rather than hanging.

When Harn is on the client side of an MCP connection (mcp_connect(...) or mcp_call(...)) and a remote server sends an elicitation/create request, Harn dispatches it to the embedder via the HostCallBridge (capability="mcp", operation="elicit"). If no host bridge is wired up, Harn responds with { action: "decline" } so the server can fall back to a sensible default rather than blocking forever.

Progress notifications

Per the MCP progress utility, a client may opt into progress updates by attaching _meta.progressToken to a request. While the matching tool call is in flight, Harn's mcp_report_progress(...) builtin emits notifications/progress notifications carrying that token:

pub fn import_records(rows: list) -> string {
  var i = 0
  for row in rows {
    i = i + 1
    process(row)
    mcp_report_progress(i, {total: len(rows), message: "imported " + to_string(i)})
  }
  return "imported " + to_string(i) + " records"
}

The builtin returns true when a notification was emitted and false when it was dropped — either because the client did not opt in via _meta.progressToken, the call sits outside an MCP tool handler, or the value would not strictly increase per the spec's monotonicity requirement. Scripts can therefore call it unconditionally without a preflight check.

opts is optional; supported keys:

  • total: numeric ceiling so the client can render a progress bar
  • message: human-readable status string
  • token: override the ambient request token (rarely needed; useful only when fanning progress out for nested work)

The orchestrator-mode harn.trigger.fire tool also emits its own milestones (loading runtime, preparing event, firing trigger, trigger complete) so MCP clients can render meaningful progress for long trigger fan-outs without any user-side wiring.

Observability

Every MCP tool call appends an observability.action_graph event and emits a stderr log line with:

  • MCP client identity
  • tool name
  • status
  • trace id

harn.trigger.fire also injects MCP client identity and trace metadata into the synthetic event headers so downstream dispatch traces can be tied back to the calling MCP client.

harn.secret_scan additionally appends audit.secret_scan records with only redacted findings plus stable fingerprints so future trust-graph consumers can reason about scan hygiene without storing raw secret material.

Logging notifications

The server advertises the logging capability and forwards Harn's structured audit and observability streams as MCP notifications/message envelopes. Each notification carries level, logger, and data fields per the MCP logging spec. The data payload is the raw event-log entry: event_id, kind, occurred_at_ms, headers, and payload.

logging/setLevel updates the per-session minimum severity. Subsequent notifications below the requested level are dropped before they hit the wire; the default is info. Levels follow RFC 5424 ordering: debug, info, notice, warning, error, critical, alert, emergency. The server returns -32602 for an unknown or missing level.

The streams the orchestrator currently surfaces:

LoggerSource topicDefault level
harn.audit.secret_scanaudit.secret_scannotice
harn.audit.signature_verifyaudit.signature_verifynotice
harn.connectors.egress.auditconnectors.egress.auditnotice
harn.trigger.operations.audittrigger.operations.auditnotice
harn.trigger.dlqtrigger.dlqwarning
harn.observability.action_graphobservability.action_graphdebug

A producer can override the level for a specific event by setting an explicit severity header on the event-log entry; otherwise the server escalates events whose kind contains error/panic to error and fail/denied/blocked/ rejected/dropped/dlq to warning.