MCP server
Doc type: Reference · Applies to: POSE 7.x (current stable)
pose serve-mcp exposes a read-heavy governance view of a POSE instance to
MCP-capable agents. Transports: stdio
(--stdio, ideal for agent runtimes) and Streamable HTTP (POSE_MCP_ADDR,
default :8790).
The golden catalog contains 61 tools: 58 project-scoped POSE governance tools and 3 optional Conductor run reporters. The count is a release contract, not a hand-maintained marketing number.
Configuration
| Env var | Purpose |
|---|---|
POSE_PROJECT_ROOT |
Repository root of the default project (must contain .pose/) |
POSE_DEFAULT_PROJECT_ID |
Default project id (derived from the root dir name if empty) |
POSE_PROJECT_ROOTS |
JSON map of additional project_id → root entries |
POSE_MCP_TOKEN |
Bearer token for HTTP transport (empty = dev, auth off) |
POSE_MCP_OPA_URL / POSE_MCP_OPA_PATH |
OPA policy endpoint (empty = allow-all dev mode; failures deny) |
POSE_MCP_REQUIRE_PRINCIPAL |
Deny anonymous tools/call even without OPA |
POSE_MCP_IDENTITY_SECRET |
Verifies run-bound execution identities |
POSE_MCP_STRICT_PROJECT_SELECTION |
Non-empty = fail closed on empty project_id when more than one project is registered (see below) |
POSE_USAGE_DISABLED |
1/true disables local usage event collection; aggregate reads remain available for existing events |
POSE_USAGE_DIR |
Optional absolute local state directory override, useful for a persistent container mount |
The installer seeds .mcp.json when absent. It invokes the native binary
directly and records the installed project's root and project id in the server
environment; no wrapper or second executable is generated.
Worked examples
A single repository with one default root:
{
"POSE_DEFAULT_PROJECT_ID": "project_acme",
"POSE_PROJECT_ROOT": "/path/to/acme",
"POSE_PROJECT_ROOTS": "{\"project_acme\":\"/path/to/acme\"}"
}
Several independent repositories on one server, with explicit selection required:
{
"POSE_DEFAULT_PROJECT_ID": "project_acme",
"POSE_PROJECT_ROOT": "/path/to/acme",
"POSE_PROJECT_ROOTS": "{\"project_beta\":\"/path/to/beta\"}",
"POSE_MCP_STRICT_PROJECT_SELECTION": "1"
}
POSE_PROJECT_ROOTS is an allowlist of project_id → root. The registered
projects are independent: the map grants access, it does not create a
subproject or parent relationship between them. Pass project_id on every call
once more than one root is registered.
Prefer the stdio transport for local agents and let the client start
pose serve-mcp --stdio from .mcp.json; do not keep a manual daemon running
alongside it. Reserve the HTTP transport for an explicitly configured shared
server.
The stdio server exits cleanly on SIGTERM or an interrupt, so a client stopping
it does not leave it running. Most tools answer by running the pose CLI, and
the server resolves that executable once, at start: a server started before
pose update keeps working and runs the updated binary at the same path. It
serves the updated tool catalog only after the client restarts it.
In Codex, mirror the same overlay in .codex/config.toml. Reuse the global
server name to override it at project scope, and neutralize inherited
multi-project values when the repository operates on its own.
Observability
pose serve-mcp can emit OpenTelemetry traces, metrics and correlated logs
for every tools/call (spec pose-otel-observability). Off by default —
POSE stays fully offline unless both of the following are set:
| Env var | Purpose |
|---|---|
POSE_OTEL_ENABLED |
Must be 1/true — POSE's own opt-in gate |
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP/HTTP collector endpoint — no default is baked into the binary |
OTEL_EXPORTER_OTLP_INSECURE |
true to skip TLS (local/dev collectors) |
OTEL_EXPORTER_OTLP_HEADERS |
key1=value1,key2=value2 — e.g. a collector auth header |
OTEL_TRACES_SAMPLER_ARG |
Trace sample ratio, 0.0–1.0 (default 1.0) |
OTEL_METRIC_EXPORT_INTERVAL |
Metric export interval in milliseconds (default 15000) |
Every span and metric carries only the tool name and its catalog risk
class (read/gate/governance-write/external-side-effect) — never an argument, path,
repo name or user id. Metrics: pose.mcp.tool.call.duration (histogram),
pose.mcp.policy.denial.count (counter), pose.mcp.tool.call.inflight
(current concurrency). Logs are structured JSON on stderr, correlated to
the active span's trace_id/span_id, with paths and secret-shaped
content redacted before being written. A misconfigured or unreachable
collector never blocks server startup or a tool call — export failures
are logged and swallowed, bounded by the shutdown timeout.
Tools
| Tool | Returns |
|---|---|
pose_list_specs / pose_get_spec |
Spec inventory (lifecycle frontmatter) / full spec |
pose_requirement_trace |
Bidirectional requirement↔evidence trace of one spec (dispositions, refs, missing/orphans) |
pose_capability_state |
Current capability assessment: mechanisms with scores/targets, typed evidence, gaps, evidence-resolution issues and age |
pose_capability_stale |
Mechanisms currently marked assessment-stale: pending reassessment demands (since, trigger, components hit), cleared by pose assess snapshot |
pose_docs_state |
Docs-governance check: manifest presence/profile/roots plus the live pose docs-check result (declared/undeclared/stale by doc_type, per-doc issues, open review-pending marks) |
pose_capability_history |
Append-only assessment snapshots (score vectors), supersede-aware and paginated |
pose_spec_amendments |
Append-only amendment history of one spec plus unacknowledged changes, the versioned R/A/D contract-node projection and whether contract_nodes_version is adopted |
pose_spec_readiness |
Is a spec eligible? Resolves local and authorized qualified depends_on refs (specs, milestones, roadmaps); phases: true adds per-phase clear/restricted/partially-restricted/unknown beside the legacy ready |
pose_mcp_context |
Active server identity, transport, selection mode, policy-filtered logical project IDs and an optional project-resolution probe; never host paths |
pose_action_open |
Open a material request (the MCP equivalent of pose action open): preview unless apply is true; risk class governance-write — it appends to the request journal under .pose/actions/, never touches the network, and cannot resolve (use pose_action_resolve) |
pose_action_resolve |
Record a principal's answer only with the principal's proof — an SSH signature by a key registered to it (pose identity add) or a trusted issuer's claim; without apply it returns the exact statement to sign and the ssh-keygen -Y sign -n pose-action-answer command; a call without a proof is refused under any identity assurance; risk class governance-write |
pose_action_requests |
Material requests to a person or external system with question, options, consequences, recipient, targets, per-phase effects, request digest, journal and derived state; read-only — pose_action_open or pose action open opens them, pose_action_resolve (signed answers) or pose action resolve resolves them |
pose_obligations |
Everything still owed, projected read-only from readiness, closeout, review judgment, start reconciliation and open follow-ups: stable ids, qualified refs, reason codes, conditions, recipients and per-phase effects, with a snapshot and per-producer coverage (an empty list with incomplete coverage is not "nothing owed"); attention groups the ids for an actor. Same domain function as pose state --attention |
pose_project_state |
Current project state in one call: curated + derived sections (specs/roadmaps, follow-ups, capabilities, decisions/knowledge, validation evidence, architecture), staleness and tamper detection, plus effective_governance computed live (optional governance_scope) |
pose_start_status |
Read-only atomic start view: digest-bound start preview (readiness, dependencies, declared obligations, R/A/D baseline) and status (phase, node origins, reconciliation needs) |
pose_closeout_state |
Hierarchical review state, child blockers, next governed action and terminal closeout for a typed scope; optional include_plan: true adds the CLI execution plan for a spec, preserving Git/input errors |
pose_review_plan |
Deterministic component-aware review plan: mapping provenance, selected profiles, criteria, safe native-tool argv, evidence expectations, independence and blockers |
pose_review_bundle |
Read-only sealed review subject and attestation state: semantic/patch/tree identity, freshness, supersession delta and next governed action |
pose_review_prepare |
Read-only auto-attest preparation of a sealed bundle: a draft attestation answering what automation may answer from sealed evidence, and the judgment criteria still pending; writes nothing — record with pose_review_attest inside a trusted issuer's envelope |
pose_review_attest |
Record a review attestation only inside a trusted issuer's ReviewAttestationEnvelope (the proof pose review attest --envelope verifies); without apply it completes a draft for a sealed bundle as recording would and returns the exact bytes to sign with the blockers verify would report, or verifies an envelope without storing it; a call that applies without an envelope is refused under any identity assurance; risk class governance-write |
pose_federated_roadmap_acceptance |
Recompute an authorized cross-project roadmap snapshot with selected revisions, source closeout/review evidence, trust pins, ownership checks and blockers |
pose_spec_transfer_status |
Read only one authorized project's path-free journal phase and source/destination identities for a spec authority transfer |
pose_delivery_integrity |
Artifact claims, Git-observed change sets, reverse path provenance and stable findings from one project-scoped graph |
pose_surface_assurance |
Typed delivery targets, production entrypoints, current evidence, explainable composition paths and roadmap criteria from the same graph |
pose_list_roadmaps / pose_get_roadmap |
Governed roadmaps and their milestone DAGs |
pose_get_changelog |
User-facing changelog fragments |
pose_release_status |
Pending queue and evidence-backed immutable release lifecycle |
pose_get_followups |
Aggregated follow-up backlog |
pose_check / pose_lint_spec / pose_skills_check |
Run the deterministic gates |
pose_suggest |
Canonical trail per task type |
pose_get_workflow / pose_get_rules / pose_get_skill |
Operating procedure content |
pose_list_knowledge / pose_get_knowledge |
Operational memory |
pose_list_reports / pose_get_report |
Validation evidence |
pose_component_discover |
Deep discovery audit of a component: LOC count (prod vs test), debt markers (TODO/FIXME/stub/panic), languages and submodules |
pose_list_assessments / pose_get_assessment |
Component assessment markdown inventory (.pose/assessments/) / full assessment markdown content |
pose_integration_check / pose_get_integration_matrix |
Cross-component integration assessment (Protobuf, Kafka, REST, MCP) and provider vs consumer matrix |
pose_tech_debt_check / pose_get_tech_debt_report |
Codebase technical debt audit (TODO, FIXME, stub, panic) with file links and recommended POSE backlog actions |
pose_insights |
Deterministic outcome aggregates by workflow, task or context |
pose_governance_stats |
Separate preparation, judgment, intervention, freshness and coverage dimensions without a quality score |
pose_design_delta |
Bounded structural observations from a canonical review subject; explicit unknown/unsupported coverage, no architecture score |
pose_usage |
Privacy-bounded local CLI/MCP usage, semantic outcomes, finding lifecycle and latency; supports since_days, tool and surface filters |
pose_extension_list |
List installed extensions (id, version, kind, digest, signature status) |
pose_validate_request |
Resolve an immutable, digest-pinned validation plan (no execution) |
pose_validate_approve |
Approve/reject a plan, bound to its digest, requiring an Execution Identity |
pose_validate_submit |
Hand an approved plan to the configured Harness executor |
pose_validate_status |
Read a validation request's current state and plan |
pose_validate_cancel |
Cancel a non-terminal validation request |
Every tool above is classified read (repository-owned governance state only)
except pose_check and pose_lint_spec, classified gate (deterministic
local gates — no writes, no network). The advertised catalog is a release-gated
public contract frozen by a golden fixture
(pose-mcp/internal/mcpserver/testdata/tool-catalog.golden.json); removals or
incompatible schema changes require an ADR and a release note.
pose_usage reads the same project-local aggregate as pose usage --json and
does not count its own query. Collection occurs automatically for project-backed
tool calls and is best-effort: a storage failure never changes the MCP result.
Only bounded outcomes, counts, duration and HMAC fingerprints are persisted;
arguments, tool output, paths, repository/project names, principals, run IDs,
source content and raw finding IDs are excluded. No usage event is transmitted
over the network.
Human confirmation of a finding as valid, wont-fix or false-positive is
recorded explicitly with pose usage adjudicate. pose_usage includes those
per-tool totals and unmatched verdicts separately from automatic counts. See
Analytics and delivery metrics.
Optional tools
Three external-side-effect tools report externally observed runs to a
Harne8 Conductor control plane. They are always advertised in tools/list,
but calls only succeed when the reporter is activated via CONDUCTOR_URL,
CONDUCTOR_RUN_TOKEN and CONDUCTOR_PROJECT_ID; without activation they
return an isError result with configuration guidance.
| Tool | Effect |
|---|---|
conductor_run_open |
Open an observed external run (returns run_id, task_id) |
conductor_run_event |
Append a progress/checkpoint event to an open run |
conductor_run_close |
Close a run with its outcome and cost |
Project scope contract
Every pose_* tool advertises the same project_id schema. A default is
convenience only, never a silent guess. Project-backed tools resolve the
project from, in order, the project_id argument, then the
X-MCP-Project/X-Project-Id header, then the configured default root.
pose_mcp_context is deliberately evaluated before store resolution so it can
diagnose an absent, ambiguous or stale selection; its project IDs are filtered
through the caller's policy and Execution Identity.
Resolution failures are distinct, structured errors (isError: true,
structuredContent.error_code) that never include the resolved filesystem
root — only the caller-supplied logical identifier:
error_code |
Meaning | structuredContent |
|---|---|---|
project_unknown |
project_id does not resolve to any registered root, even after a rescan |
project_id, authorized available_project_ids, reconnect remediation |
project_ambiguous |
project_id was omitted and the server cannot pick one unambiguously |
reason: no-default or multi-project-implicit |
A third case — the resolved project exists but policy denies it — surfaces
through the existing JSON-RPC error -32004 with decision.Metadata()
(policy denied), not through structuredContent.
Connection verification
Treat static configuration and the connected process as separate states:
- Run
pose doctor --jsonto validate the local.mcp.json. Itsmcp.configfinding reportsdiagnostic_scope: static-configurationandconnection_checked: false; it never claims to inspect a client-owned stdio process. - Call
pose_mcp_contextbefore the first governed read. Passproject_idexplicitly to probe the repository expected by the current task. - Restart or reconnect the MCP client after changing
.mcp.json, switching repositories, or upgrading the binary. Callpose_mcp_contextagain and require a new/expected server instance and aresolvedproject probe. - Enable
POSE_MCP_STRICT_PROJECT_SELECTIONwhen more than one project is registered. Do not continue afterproject_unknownorproject_ambiguous; use the returned logical IDs and remediation.
The context response contains process metadata and authorized logical IDs only. It never returns project roots, configuration contents or environment values.
Two limits are worth knowing. Project IDs are filtered through the same policy
as any other call, so with no OPA endpoint configured — the default dev-mode
posture for every tool — discovery returns every registered project; configure
a policy endpoint when registry membership is itself confidential. And one
discovery call evaluates at most 64 projects; a larger registry sets
available_project_ids_truncated: true rather than looking complete.
Compatibility / deprecation window: with POSE_MCP_STRICT_PROJECT_SELECTION
unset (default), an empty project_id always resolves to the configured
default root, even once a deployment has onboarded more than one project —
existing single-project stdio ergonomics are exactly unchanged. Setting the
variable makes that same omission fail closed with
project_ambiguous/multi-project-implicit whenever more than one project
is registered; a genuinely single-project deployment is never affected by
the flag. Multi-project operators should plan to adopt it as project count
grows; it is expected to become the default in a future release.
Pagination and catalog stability
pose_list_specs, pose_list_roadmaps, pose_list_knowledge and
pose_list_reports accept optional cursor/limit arguments and return an
additive next_cursor field (empty when exhausted). Cursors are opaque,
versioned position tokens over each list's fixed deterministic order (spec
slug, roadmap slug, knowledge slug, or generated_at descending) — never
parse or construct one client-side; a malformed or wrong-version cursor is a
tool error, not silently coerced to page 1. Omitting both arguments returns
every item in a single page — the exact response shape from before
pagination existed.
The tool catalog itself never changes within one server process: tools/list
is a pure function of the binary and is byte-identical across calls in the
same session, so capabilities.tools.listChanged: false is verified, not
aspirational. A catalog change only happens across a release (a new
pose-mcp binary, a new serverInfo.version) — clients should reconnect
(re-initialize) after observing that version change; POSE does not (and,
given a static per-process catalog, has no reason to) emit
notifications/tools/list_changed events.
Resources and prompts are deliberately not implemented. Every governed
read POSE exposes — specs, roadmaps, knowledge, reports, workflows, rules,
skills — is already served through typed, schema-validated, project-scoped,
policy-gated tools. A generic MCP resources primitive would let a client
address arbitrary repository content by URI, which is exactly "expose
repository files wholesale" — explicitly out of scope. A generic prompts
primitive risks encoding procedure outside the reviewable
.pose/workflows/*.md files it is meant to expose, which is "turn prompts
into hidden policy" — also out of scope. capabilities therefore advertises
only tools; a tools-only client sees no unimplemented primitive to
misconfigure.
Safe validation orchestration
pose validate stays a local, unrestricted CLI command. Agents that need to
request validation through MCP go through a separate, deliberately narrow
state machine instead of an unsafe direct-execution passthrough:
pose_validate_request → pending_approval
│ (plan digest pins matrix + git HEAD + filters)
▼
pose_validate_approve → approved | rejected
│ (requires a bound Execution Identity; digest must match exactly)
▼
pose_validate_submit → submitted (idempotent; requires a Harness executor)
- The plan is immutable and digest-pinned.
pose_validate_requesthashes the exact validation matrix bytes plus git HEAD and the requested filters;pose_validate_approvemust echo that exactplan_digest, or the call is rejected as plan substitution — the request cannot be silently widened between resolution and approval. - Approval is never anonymous.
pose_validate_approverequires a valid, unexpiredX-MCP-Execution-Identitytoken (ADR-007) regardless of the server's default policy mode — a deployment running OPA in dev/allow-all still cannot approve orchestrated validation anonymously. Because the identity header only exists on the HTTP transport, orchestration approval is an HTTP-transport operation; stdio deployments inherit trust from the spawning client for localpose validate, which is unaffected by any of this. - pose-mcp never executes the plan.
pose_validate_submithands the approved, digest-pinned plan to a pluggableHarnessExecutor— wired withServer.WithHarnessExecutor, the same optional-dependency pattern asWithReporterfor Conductor. Without one configured, submission returns a clear configuration error; nothing runs. Submission is idempotent: resubmitting an already-submitted request returns the sameexecution_idwithout invoking the executor again. - State is local and in-process, not "centrally persisted" — a
production deployment centralizes run state in Conductor and plugs
execution in via
HarnessExecutor, the same relationship the Conductor run reporter tools already have to the Conductor board.pose_validate_statusreads a request's current state;pose_validate_cancelmarks a non-terminal request cancelled (a submitted request's cancellation reaching a running Harness execution is the executor's own responsibility, not pose-mcp's).
Security posture
- Default deny on OPA errors; policy decisions are audited
(
policy.decided/policy.violationstructured logs). - Shared-domain tools run in-process; CLI-backed tools invoke the current native executable. Every argument is validated and shell text is never evaluated.
- Multi-replica deployments need the Redis cursor store (enterprise hardening track); single-node dev needs nothing beyond the binary.
Qualified artifact references (6.x development contract)
Use one identity per artifact: project ID, kind and local slug. Bind stable IDs
with POSE_PROJECT_ROOTS; use POSE_DEFAULT_PROJECT_ID to select the current
project. The directory-derived default is legacy convenience and is not a stable
identity after a checkout rename. Explicit bindings select a checkout over the
scanned/default mapping. Duplicate JSON keys, conflicting implicit bindings and
multiple IDs for the current root without explicit selection fail diagnostically.
Registered roots may be nested or sibling checkouts; filesystem ancestry itself
never grants registration or authorization.
The common grammar accepts:
workorspec:workfor a local spec;roadmap:programandmilestone:program/firstfor local typed artifacts;xref:proj.engine/workfor the legacy external spec spelling;xref:proj.engine/spec:work,xref:proj.engine/roadmap:programandxref:proj.engine/milestone:program/firstfor qualified typed references.
lint-spec --ready-check checks structural readiness, including reference
syntax. pose check validates dependency existence and cycles;
pose_spec_readiness additionally requires satisfied dependency lifecycle states.
A missing, unauthorized, unavailable or ambiguous external source cannot fall
back to a local artifact with the same slug. MCP reevaluates the caller's policy
for each target project before looking it up, including transitive dependencies.
Resolution is bounded to 64 dependency levels and 256 identities; references
longer than 1024 bytes and traversal syntax are rejected.
portfolio-projection uses the same Store reader as lookup, including flat,
dated, folder and split specs. Its additive schema 2 includes canonical identity,
resolution state, source Git revision when available, and a SHA-256 content digest.
Tombstones remain supported. freshness_basis: mtime-advisory-not-evidence
clarifies that the existing stale flag is a filesystem heuristic: timestamps,
source digests and done status do not prove review or federated acceptance.
Independent Git repositories report their own revision; an ancestor HEAD is
never substituted for an unversioned source. Source digests describe the observed
working tree, which may differ from that revision.
Adopt required qualified-reference metadata explicitly, after validating the
consumer binary: set review policy schema_version: 3 together with
qualified_artifact_refs_version: 1. Schema 1/2 policies keep their prior
behavior. Existing schema 1/2 engines refuse schema 3 at their review/governance gates
instead of silently approving the new contract. Legacy read-only commands may
still expose their older projection and are not an adoption gate. An unknown reference-contract version is rejected. pose update does
not automatically perform this adoption; it belongs to the consumer adoption
spec. Retain sealed historical policies and bundles unchanged. Roll back the
consumer and adopted metadata together only after removing dependent new usage;
never weaken a required dependency to make an older binary pass.
Resolution and readiness are read-only lifecycle observations. Federated review, pinned evidence acceptance, spec transfer and automatic agent routing are separate implementation scopes; this resolver does not declare those capabilities ready.
Development branch behavior
Unreleased; reviewed 2026-10-20. The published catalog baseline above is v7.4.1. Pending changes strengthen shared readers and writers without treating the presence of a tool name as proof of the newer behavior:
pose_closeout_stateaccepts optionalinclude_plan: truefor the spec's execution plan; this belongs to the Git/dispatch work still in progress.- Interrupted or cancelled gates report
execution_inconclusivewith cause and partial output, rather than claiming a completed failing gate. - Shared readers disclose incomplete source coverage and authenticate persisted proof before reporting verified identity or satisfied action obligations.
pose_review_attestshares domain approval preflight with CLI and Store; invalid or stale approvals returnreview_approval_refusedwithout creating records.- Published index generations give readers one coherent set; unavailable inputs are disclosed instead of silently selecting a mixture.
See Releases and development for the pending fragment/spec boundary. Keep CLI and MCP binaries compatible with the same adopted contracts.