Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/joseluis-dev/harness-ai/llms.txt

Use this file to discover all available pages before exploring further.

The MCP Gateway and CLI Tool Runner are two complementary subsystems that give Harness AI controlled, auditable access to external and local tools. The MCP Gateway connects the harness to external services through the Model Context Protocol — resolving every tool invocation dynamically against the tool_registry rather than a hardcoded list — while the CLI Tool Runner executes deterministic local binaries (PDF converters, OCR engines, ETL scripts) under an allowlist, sandboxed, and audited execution environment. Neither subsystem ever decides policy or issues its own tokens; those responsibilities belong exclusively to Harness Core and the Auth Service.

MCP Gateway

Dynamic Tool Resolution

The MCP Gateway carries no embedded list of MCP servers. Every tool invocation is resolved at runtime against the tool_registry table in harness_auth_db. Adding a new MCP server to the harness means inserting a row — no gateway code changes, no redeployment. The gateway enforces these rules on every invocation:
  • tool_registry.state must be active; disabled or deprecated tools are rejected with 404
  • The environment field must match the running environment
  • required_scopes and per-operation scope values are checked before the token is issued
  • timeout_ms and retry_policy from the registry are applied to every outbound call
  • Payload size and per-user/tool rate limits are enforced independently of the MCP server

Tool Call Flow

1

Agent Requests Tool

The runtime worker requests a tool by technical_name (e.g. mcp-readonly-sql) with an operation name and typed arguments.
2

Registry Lookup

tool_gateway queries tool_registry for the tool’s state, environment, required scopes, risk_level, timeout_ms, and retry_policy. Any mismatch returns an error before a token is ever requested.
3

Token Issuance

The runtime obtains an OAuth 2.1 Client Credentials token from the Auth Service (mcp-auth-service) scoped to the required operation scope. The token is short-lived and carries the risk_level_cap of the requesting client.
4

Tool Invocation

The gateway invokes the MCP server over JSON-RPC 2.0, passing the Authorization: Bearer <token>, X-Harness-Execution-Id, and X-Harness-Actor-Id headers.
POST /mcp/readonly-sql
Authorization: Bearer <token>
X-Harness-Execution-Id: <uuid>
X-Harness-Actor-Id: <actor_id>
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "id": "<uuid>",
  "method": "tools/call",
  "params": {
    "name": "execute_query",
    "arguments": { "sql": "<validated>", "limit": 100 }
  }
}
5

Auth-SDK Validation

The MCP server validates the Bearer token using auth-sdk (local JWKS verification). It checks signature, expiration, scope, and risk_level_cap. No MCP server ever implements its own token validation logic.
6

Risk Level Gate

If risk_level requires human confirmation (e.g. DESTRUCTIVE, EXTERNAL_SIDE_EFFECT), the gateway responds 202 with an approval_id and suspends the execution pending approval. The Phase 9 approval flow handles the continuation.
7

Normalize and Audit

The result is normalized according to the standard result contract. An audit_event is emitted with execution_id, actor_id, args_hash, and decision.

Transport Types

TransportValueDescription
HTTP MCPmcp_httpJSON-RPC 2.0 over HTTP. The MCP server exposes a single endpoint.
stdio MCPmcp_stdioJSON-RPC 2.0 over stdin/stdout for local server processes.
CLIcliLocal binary execution via the CLI Tool Runner (see below). Tool entries with transport=command use this type.
HTTP APIhttp_apiGeneric HTTP integration (non-MCP).
Inbound webhookwebhook_inReceive events from external systems.
Outbound webhookwebhook_outSend signed HMAC events to external systems.

Outbound Headers

All tool calls carry:
HeaderDescription
Authorization: Bearer <token>OAuth 2.1 token from Auth Service. Never in query string.
X-Harness-Execution-IdCorrelates the tool call with the parent execution and audit trail.
X-Harness-Actor-IdResolved by mcp-identity-connector before the request reaches the gateway.
Content-Type: application/jsonAlways JSON.

Error Handling

StatusErrorCondition
401insufficient_scopeToken valid but scope does not cover the operation. Response includes WWW-Authenticate with resource metadata.
403risk_level requires approval or an admin scope is missing.
404tool_registry.state is disabled or deprecated, or environment mismatch.
429Rate limit exceeded per user or per tool.
202approval_idOperation requires human confirmation.
MCP servers must not implement their own OAuth or token validation logic. Every MCP server must use auth-sdk to validate the Bearer token it receives. Implementing custom auth in an MCP server is an anti-pattern that breaks the centralized policy and audit guarantees of the harness. Any PR introducing custom token logic in an MCP server will be rejected.

Seeded MCP Servers

The initial set of MCP servers is seeded into tool_registry via infra/postgres/seed/tool_registry_seed.yaml. They are not enumerated in the gateway code.
mcp-readonly-sql       — read-only SQL queries against institutional databases
mcp-identity-connector — bearer → ActorContext resolution
mcp-document-registry  — document metadata and access control
mcp-expedientes        — case file (expediente) management
mcp-reportes           — report generation
mcp-notificaciones     — notification dispatch

CLI Tool Runner

The CLI Tool Runner handles tools of type cli (transport=command) — deterministic local binaries that should never be invoked with arbitrary arguments or without authorization. It resolves every command from tool_registry, validates authorization with auth-sdk, and executes the binary under a strict sandbox.

Security Rules

The CLI Tool Runner enforces ten invariants before executing any command:
  1. No free-form commands. Only commands registered in tool_registry with type=cli are executable.
  2. No LLM-generated arguments. All arguments are validated through a declarative schema before construction.
  3. Commands are resolved from tool_registry (type=cli, transport=command). A local YAML file is only valid as a seed or bootstrap artifact — runtime truth lives in harness_auth_db.
  4. Parameters are validated by schema.
  5. Every execution has a hard timeout.
  6. Commands that touch the filesystem run in an isolated container.
  7. Network is disabled unless the tool_registry entry explicitly enables it.
  8. stdout and stderr are recorded in summarized form (not verbatim, to prevent accidental data leakage).
  9. Exit code is recorded on every invocation.
  10. File size limits are enforced on inputs and outputs.
A tool call is rejected if auth-sdk cannot validate the Bearer, if the scope is insufficient, if state != active, or if the risk_level exceeds what the calling client’s risk_level_cap permits.

Sandbox Controls

# Example tool_registry seed entry (development reference only)
allowed_tools:
  - id: pdf_to_text
    command: pdftotext
    timeout_seconds: 30
    network: false
    allowed_args:
      input_file:
        type: file
        base_dir: /data/documents/input
      output_file:
        type: file
        base_dir: /data/documents/output

  - id: ocr_pdf
    command: ocrmypdf
    timeout_seconds: 300
    network: false
    allowed_args:
      input_file:
        type: file
        base_dir: /data/documents/input
      output_file:
        type: file
        base_dir: /data/documents/output
      language:
        type: enum
        values: [spa, eng, spa+eng]
transport=command does not mean bypass of authorization. The CLI Tool Runner calls auth-sdk to validate the Bearer and scope on every invocation, exactly as an MCP server would. The only difference is that the “server” is a local binary rather than an HTTP endpoint.

Sandbox Restriction Categories

CategoryControls
execution_restrictionsCPU quota, memory limit, network disabled by default, container isolation for filesystem-touching commands
filesystem_restrictionsRead allowlist, write allowlist. All paths are validated against base_dir before argument construction.
network_restrictionsEgress allowlist. Network is off unless tool_registry.network=true.

Common Use Cases

  • PDF → text extraction (pdftotext)
  • OCR on scanned PDFs (ocrmypdf)
  • Document metadata extraction
  • DOCX → PDF conversion
  • Internal validators and schema checkers
  • ETL scripts with controlled input/output paths
  • Signature verification
  • Scheduled report generation

n8n Webhook Adapter

The n8n Webhook Adapter allows n8n to trigger harness automations or receive harness events without gaining direct control over any internal service. Every inbound webhook is validated (HMAC signature + anti-replay timestamp + schema) before being dispatched as a job to the execution queue.
POST /api/webhooks/n8n/:event
X-Harness-Signature: <hmac-sha256>
X-Harness-Timestamp: <unix_timestamp>
Content-Type: application/json
{
  "event": "document_processing_requested",
  "source": "n8n",
  "correlation_id": "uuid",
  "payload": {
    "document_id": "…",
    "priority": "normal"
  }
}
n8n authenticates as an OAuth 2.1 confidential client. Its client_id and client_secret are provisioned through the Auth Service admin panel — never hardcoded. Outbound events (harness → n8n) use the same HMAC + timestamp scheme so both sides mutually authenticate.
n8n has no permission to invoke tools, agents, or models directly. It can only notify the harness (via inbound webhooks) and receive notifications from the harness (via outbound webhooks). All heavy work runs inside the harness execution pipeline after the event is dispatched to the queue.

Telegram Channel

The Telegram channel is a controlled external input channel, not a shortcut into the harness internals. Every Telegram command follows the mandatory chain:
Telegram → Bot API → signed webhook → Harness Core → policy engine → permissions → execution → audit
No Telegram command may bypass this chain. Direct paths from Telegram to MCPs, CLI tools, LLMs, vLLM, or institutional databases are architecturally prohibited — there is no feature flag, development mode, or bypass that suspends this rule.

Identity Resolution

A Telegram user_id (integer) is not an institutional identity. The channel resolves it through mcp-identity-connector:
mcp-identity-connector.get_user_context(telegram_user_id)
Without a valid institutional mapping, the command is rejected with a generic response (no information about whether the user exists is revealed) and a denied audit event is emitted with reason="unmapped_user".

Allowed Commands

Only these commands are accepted. Any other input is rejected without invoking any tool, agent, or model.
CommandPurpose
/consultarRAG query over a document the user has access to
/resumir <doc_id>Structured summary of an accessible document
/aprobar <approval_id>Approve a pending approval request
/rechazar <approval_id> <motivo>Reject a pending approval request with a reason
/estado <execution_id>Summarized status of one of the user’s executions
/ayudaLists available commands and their syntax
Responses are always summaries and references (≤500 tokens). Full documents, PII outside the user’s scope, internal tokens, stack traces, IP addresses, and internal paths are never returned.

Build docs developers (and LLMs) love