HOVEL // chapter 16
Chapter 16 Part 03 / Operator Experience

MCP and Agent Operation

hovel mcp is the agent-facing front end for Hovel. The current source-backed slice is an MCP server with stdio by default, optional streamable HTTP, typed catalog/workspace/chain tools, installed-payload tools, provider command tools, and a destructive throw starter that routes through the same daemon-backed throw path as the human CLI. The product goal remains operator parity: every capability available to a human operator should be available to an AI agent through the same daemon-owned application services, guardrails, plan review, confirmation records, structured events, and audit trail.

MCP does not get a weaker private API and does not get a privileged bypass. It is one operator entity among others. A Hovel MCP instance attaches to hoveld the same way a human CLI or future TUI attaches: it has an entity identity, an active operation, an active chain, a log cursor, and policy-governed permissions.

Implementation Posture

The Go implementation uses the official github.com/modelcontextprotocol/go-sdk. The adapter lives in internal/adapters/mcp and is exposed by the mono-binary as:

hovel mcp --workspace .hovel
hovel mcp --workspace .hovel --config lab.yaml
hovel mcp --transport http --workspace .hovel
task mcp

The default transport is MCP stdio. Stdio keeps the local trust boundary tight: the MCP client launches hovel mcp as a subprocess, Hovel writes MCP JSON-RPC only to stdout, and diagnostic logs go to stderr. --transport http serves streamable HTTP on localhost by default; use --http-addr to pin the listen address. task mcp builds and installs or links the example modules for the dev workspace; direct launches can pass --config to select the same module search paths and indexes as other front ends.

hovel mcp attaches to hoveld over the stable local daemon RPC, registers as an MCP operator entity, heartbeats while connected, and detaches on shutdown. The current local-first model keeps MCP in the adapter process instead of exposing MCP directly from hoveld. Direct daemon-hosted MCP is a future team or remote-deployment shape once authorization, session binding, and remote operator identity are source-backed. The daemon RPC methods used by MCP are documented in Daemon RPC Contract.

Demo Harness

The source tree includes a mock Codex-style agent harness at //tools/demo/mcpagent:mcpagent. It uses the official MCP Go SDK, lists the available tools, calls the identity/entity/operation/snapshot tools, then calls hovel_throw_start and renders a terminal agent panel from real structured MCP responses. The harness intentionally avoids an OS shell-command shortcut so the demo exercises the Hovel MCP surface a model client would use.

The generated VHS demos use real tmux sessions. hovel mcp and hovel cli stay visible while the mock agent runs on the right with a prompt area and chat log. The MCP pane and agent pane communicate over named pipes. The CLI pane is placed on the same operation and chain before the mock agent starts, so the human-facing surface shows the relevant throw logs while the agent throws the exploit through the visible MCP server.

Tmux demo with hovel mcp, hovel cli, and a mock Codex-style MCP agent throwing the mock exploit.
Throw mock exploit through MCP
Tmux demo with hovel mcp, hovel cli, a shell pane, and a mock Codex-style MCP agent operating the Squatter payload and showing transferred files.
Exploit Squatter payload commands and file transfer through MCP

Entity Model

An entity is a live actor attached to hoveld. Entity records are daemon-owned and should include:

id
kind: cli | tui | mcp | rest | one_shot | service | unknown
display_name
agent: true | false
operation
active_chain
connected_at
last_seen_at
capabilities
policy_tags

For launch-key decisions, only healthy live attachments count. A stale socket, a dead subprocess, or a completed one-shot client must not block a throw forever. Each MCP process represents one operator entity; multiple MCP clients mean multiple operator entities.

The daemon attachment lifecycle is represented by explicit RPC calls: AttachEntity, HeartbeatEntity, DetachEntity, and ListEntities. Heartbeats update last_seen_at; omitted operation or chain fields keep the previous value, while explicit empty values clear the active operation or chain.

Entity identity is not an authentication system by itself. Stdio-local MCP can start with process-local identity and daemon-issued attachment IDs. HTTP or remote MCP must bind entity identity to authenticated principal state rather than trusting a client-supplied session ID.

MCP Surface

The MCP server exposes typed domain tools first. hovel_command_run remains as a pragmatic escape hatch for command-mode registry verbs without typed tools; it runs Hovel commands through the daemon-backed operator session, not through an OS shell.

The current MCP slice exposes:

hovel_operator_identity
hovel_operator_list_entities
hovel_operation_list
hovel_workspace_snapshot
hovel_catalog_snapshot
hovel_module_search
hovel_module_inspect
hovel_chain_suggest
hovel_launch_key_policy
hovel_chain_apply
hovel_command_run
hovel_throw_plan
hovel_throw_confirm
hovel_throw_start
hovel_installed_payload_list
hovel_session_capabilities
hovel_session_call
hovel_payload_cmd
hovel_payload_capabilities
hovel_payload_call

Agents should start with hovel_catalog_snapshot and hovel_workspace_snapshot. The catalog snapshot returns the effective config source, loaded module IDs, payload-provider modules, package sources, checksums, and load errors. The workspace snapshot returns operations, chains, steps, config, target config, installed payload records, and provider readiness.

hovel_chain_apply is the normal setup tool. It creates or selects the operation and chain, adds missing modules, adds targets, sets chain and target config, fills safe defaults such as target.host=<target> and module-declared required defaults, and validates. A representative MS17-010-to-Squatter TCP bind apply call uses modules ms17-010-survey@v0.1.0, ms17-010-exploit@v1.0.0, and squatter@v0.1.0; target 192.168.122.142; chain config operator.confirmed_lab=true; and optional chain config squatter.bind_port=9100. The Squatter provider step is explicit, but Hovel derives the MS17-010 payload file and target-side payload config during planning and execution.

After hovel_throw_start, the throw output includes installed payload readiness records with handles such as p1, provider readiness, provider errors, and next typed actions. Agents should call hovel_installed_payload_list when they need the current inventory, hovel_payload_capabilities before invoking provider-owned payload actions, and hovel_payload_call for provider-neutral command execution. hovel_payload_cmd, hovel_payload_command_list, and hovel_payload_command_call remain compatibility aliases for older payload command flows.

Tool names should stay simple MCP identifiers with a hovel_ prefix. The full parity surface should grow through domain verbs:

hovel_operation_list
hovel_operation_use
hovel_chain_create
hovel_chain_use
hovel_chain_add_step
hovel_chain_inspect
hovel_chain_validate
hovel_chain_config_set
hovel_target_add
hovel_target_config_set
hovel_throw_plan
hovel_throw_confirm
hovel_throw_start
hovel_throw_status
hovel_throw_logs
hovel_artifact_list
hovel_artifact_inspect
hovel_installed_payload_list
hovel_payload_connect
hovel_payload_cleanup
hovel_session_list
hovel_session_read
hovel_session_write
hovel_session_close

Tool outputs should use MCP structured content and output schemas wherever practical. Each output should echo the resolved operation and chain, include stable IDs, and include machine-readable next steps:

{
  "operation": "redteam-lab",
  "chain": "ssh-memory",
  "status": "blocked",
  "blockingIssues": [],
  "missingRequirements": [],
  "nextActions": []
}

Tool execution errors are normal tool results with isError semantics, not protocol failures, when the model can recover by changing arguments or workflow. Error payloads should include code, message, recoverable, details, and suggestedFix.

MCP tool annotations should describe read-only, destructive, idempotent, and open-world behavior, but real enforcement lives in Hovel policy, throw planning, dangerous-module gates, launch-key approval, and audit records.

Resources

Read-heavy context belongs in MCP resources using hovel:// URIs. Resources are not implemented in the first slice. These resources are not filesystem paths and do not need to map to local files:

hovel://workspace/current
hovel://operation/current
hovel://operation/{operation}
hovel://chain/{operation}/{chain}
hovel://chain/{operation}/{chain}/validation
hovel://module/{module_id}
hovel://throw-plan/{plan_id}
hovel://throw/{throw_id}
hovel://throw/{throw_id}/logs
hovel://artifact/{artifact_id}
hovel://payload/{payload_handle}
hovel://session/{session_id}
hovel://session/{session_id}/transcript

Resources must validate URIs and enforce the same access policy as tools. Sensitive resource data should not rely on MCP client behavior for protection; Hovel decides what can be read.

Prompts

Prompts are optional user-controlled workflow aids and are not implemented in the first slice. They should not encode hidden policy. Useful initial prompts include:

plan_chain
configure_chain
review_throw_plan
operate_session
cleanup_payloads

Prompts may embed Hovel resources so the model can reason from current state. Module-authored agent hints may appear in prompt output only with explicit provenance labels.

Throw Workflow

MCP throwing is a multi-step protocol:

  1. throw.plan creates or refreshes a persisted plan and returns the exact plan hash, risk summary, reviewed configuration, and required approvers.
  2. The client or model reviews hovel://throw-plan/<plan_id>.
  3. throw.confirm records this entity's approval of that exact plan hash.
  4. throw.start starts execution only if policy permits execution and all confirmation gates are satisfied.
  5. throw.status, throw.logs, and throw.result expose progress and outcome.

throw.start must never synthesize an implicit plan or implicit confirmation. now_bypass is available only when daemon policy permits it for the entity kind, and it still records a confirmation method of now_bypass.

Launch-Key Policy

Launch-key mode is an optional hoveld policy that requires peer operator approval before a throw starts. It is intended to let an AI agent build, configure, validate, and propose a throw while a human operator reviews and approves the exact plan before execution.

Recommended policy semantics:

  1. The approval set is operation-scoped, not workspace-global.
  2. Required approvers are snapshotted when the pending throw is created.
  3. Every required live entity in that operation must approve the same plan hash.
  4. Approvals bind to the plan hash and policy flags such as dangerous-module allowance and now_bypass.
  5. If an approver disconnects before approval, the throw remains blocked until they reconnect, the pending throw is cancelled, or a configured timeout expires.
  6. A policy event records the required approver set, approvals, cancellation, timeout, and start decision.

The daemon launch-key lifecycle is represented by explicit RPC calls: CreatePendingThrow, ConfirmPendingThrow, RequirePendingThrowReady, and CancelPendingThrow. These calls snapshot the daemon's live entity records at pending-throw creation and expose machine-readable required and missing approver IDs for CLI, MCP, and future REST clients.

In CLI, throw may act as approve-and-start when the caller is the final required approver. In MCP and REST, explicit throw.confirm and throw.start calls keep the state machine easier for models and clients to inspect.

Authority Policy

The default MCP authority policy should be useful but conservative:

  1. Allowed by default: inspect, configure, validate, plan, list artifacts, read logs, read session transcripts.
  2. Requires explicit hoveld configuration: throw.start, dangerous modules, now_bypass, session writes, payload reconnect, payload cleanup, and destructive service control.
  3. Policy denials return structured recovery guidance rather than pretending the tool does not exist.

Agent Context

The module-facing concept is agent context, not MCP context. MCP is one source of agent operation; future REST, batch, or TUI automation may also provide agent context. Modules can ignore the feature completely.

When no agent context exists, SDKs expose no object:

if ctx.Agent == nil {
    // Human or non-agent execution; default module behavior.
}
if ctx.agent is None:
    # Human or non-agent execution; default module behavior.

Adding agent context must have no behavior effect for non-MCP and non-agent sessions. Existing modules remain valid. The base Module interface does not gain required methods.

Agent context may be passed to execute, step.prepare, step.execute, payload-provider reconnect and cleanup calls, and any future execution-phase provider call. It is not required for handshake, catalog discovery, or schema reporting.

The first source-backed SDK slice exposes optional agent context in Go, Python, and Rust module contexts, plus opt-in agentHints on module results and step results. The host preserves runtime hints from direct module execution and chain-step execution.

Recommended shape:

{
  "schema": "hovel.agent_context.v1",
  "entity": {
    "id": "entity-mcp-1",
    "kind": "mcp",
    "displayName": "codex",
    "agent": true
  },
  "operation": "redteam-lab",
  "chain": "ssh-memory",
  "planId": "plan-abcd",
  "planHash": "abcd...",
  "approvalState": "pending",
  "phase": "execute",
  "resources": [
    "hovel://throw-plan/plan-abcd"
  ]
}

Modules may provide optional agent hints as structured records. Hints are untrusted third-party content and are never injected as system or developer instructions. Hovel may expose them as MCP resources, prompt content, review material, or result metadata with clear provenance.

{
  "schema": "hovel.agent_hint.v1",
  "phase": "session",
  "audience": "assistant",
  "risk": "medium",
  "appliesTo": {
    "sessionId": "session-1",
    "target": "router-01"
  },
  "text": "Prefer 'whoami' and 'hostname' before changing target state.",
  "provenance": {
    "moduleId": "mock-exploit-session@v0.0.0-example"
  }
}

Agent Hints

Hints can appear in static module metadata, requirement descriptions, step contracts, prepare results, execute results, evidence, or session metadata. They should be phase-scoped: discover, configure, plan, review, execute, session, or cleanup. Runtime hints may bind to target IDs, run IDs, session IDs, payload handles, artifact IDs, or capability IDs.

Any hint that can affect execution choices, session operations, cleanup, payload behavior, or target-side behavior must be persisted with provenance and visible in review or audit material. Static guidance that only helps the model understand a module does not need to affect the plan hash. Runtime or reviewed instructions that change the operator-visible footprint must be covered by the reviewed plan hash.

Modules cannot directly force user or model interactions. They may emit suggested actions or needs_input evidence; Hovel and its front ends decide whether and how to ask the user.

Versioning

MCP-facing contracts and module agent-context contracts are versioned separately:

hovel.mcp.v1
hovel.agent_context.v1
hovel.agent_hint.v1

Write tools reject unknown fields to catch model mistakes. Read payloads may add fields over time. Schemas should be published with the other Hovel JSON schemas once the shapes become source-backed.

Rollout Plan

  1. Document the design and add pure domain tests for connected entities and launch-key approval.
  2. Add daemon attachment APIs for entity attach, detach, heartbeat, and listing, plus daemon launch-key APIs backed by an application-level launch-key coordinator.
  3. Add typed throw plan/confirm/start application APIs that CLI and MCP can share, backed by launch-key policy decisions and policy/audit events. The first source-backed slice is the shared throw-control service and tests; wiring existing CLI execution to it remains follow-on work.
  4. Add stdio hovel mcp with read-only resources and inspection tools. The first source-backed slice is stdio hovel mcp with typed read-only operator and workspace tools; resources remain follow-on work.
  5. Add chain and target mutation tools with structured validation feedback.
  6. Add throw planning and explicit confirmation tools.
  7. Add policy-gated throw.start.
  8. Add session read/write tools, with writes policy-gated.
  9. Add optional agent context and agent hints to SDKs and module RPC. The first source-backed slice covers SDK contexts/results and host pass-through; persistence and MCP resource presentation remain follow-on work.
  10. Add Streamable HTTP only after local stdio behavior and daemon authority policy are stable.