ADR-0013: One command model for CLI and MCP
Status: Accepted — 2026-09-24. The owner directed the architecture to include CLI and agent use of trusted installed extensions. Acceptance defines the design below; no product MCP adapter or new conformance evidence is claimed.
English | Русский
Context and problem
Section titled “Context and problem”Rukh is an embeddable core for a named CLI with trusted installable extensions. The same installed capability should be useful to a person and an agent without maintaining a second command implementation, server or access model. A small team needs a useful local configuration; organizational identity and permission services remain optional integrations.
CLI text, process exit and a command’s typed outcome are insufficient as a general machine result. An agent also needs declared inputs, predictable results, explicit interaction requirements and safe behavior when a listed tool becomes obsolete. A tool list is neither permission to execute nor a lease on a mutable implementation. This decision refines ADR-0002, 0003, 0006, 0007 and 0010; task authority, enforcement and effects are defined by ADR-0014–0016.
Decision drivers
Section titled “Decision drivers”- One command identity, declaration, semantic validator and execution path for CLI and MCP.
- Generate tools from verified installed and active extensions; publication and access remain owner-controlled.
- No required server, account, database, agent planner or model provider.
- No execution, runtime preparation, contextual description or sign-in while listing tools.
- Bounded results, current authorization and no silent rerouting or repetition of effects.
Considered options
Section titled “Considered options”| Option | Benefit | Limitation |
|---|---|---|
| Make an agent run arbitrary CLI text | Compatible with many programs | Loses typed contracts and makes shell access the effective tool |
| Give each extension its own MCP server | Full independent integration | Duplicates discovery, credentials, policy, versions and supervision |
| Project one admitted command model into CLI and MCP | Shared contracts and a local baseline | Requires explicit result, publication and adapter rules |
Decision
Section titled “Decision”Keep the installed command model authoritative. CLI and MCP are trusted presentations of that model; both enter the same invocation core. MCP is an optional owner-selected facility of the branded application, not a different extension kind or an extension’s privileged registration mechanism.
The selected view shows one command implementation with two entry paths.
Declaration and compatibility
Section titled “Declaration and compatibility”ADR-0006’s rukh.extension/2 preserves the command identity and input grammar of version 1, and adds the fields below. A version 1 descriptor remains usable for its supported CLI mapping. It is not silently given a machine contract; its publisher must produce a version 2 release, or the owner must declare a separate trusted built-in wrapper. An old validator rejects version 2 as unsupported instead of ignoring new requirements.
| Field | Meaning |
|---|---|
commands[].execute.result | kind: text or kind: structured; structured results require schema as defined below. Required for a machine-eligible command |
commands[].machine | eligible boolean, interaction (none, optional, required) and requiredFeatures containing exact registered feature IDs/versions; omission is equivalent to eligible: false |
| Execution requirements | Existing action/resource requests, plus the required task, effect and confinement contracts from ADR-0014–0016. Declarations request support and access; they do not grant either |
The machine baseline requires declared inputs, a supported managed mapping, foreground lifetime and the versioned command-result/1 feature. A group is not a tool. Direct aliases retain one canonical command and do not create more tools. Passthrough programs remain supported by CLI but are not automatically exported as raw shell/argv tools. An owner-built wrapper needs its own declared typed contract, actual guarantees and permission checks; renaming an opaque program does not make its effects restricted.
For CLI, the existing lexical parser produces typed values. For MCP, object properties are stable parameter IDs, not flag spellings or positional strings. The adapter rejects unknown properties and inappropriate JSON types, records explicitly supplied IDs, applies declared defaults and exact-number normalization, and invokes the same semantic validator. It never constructs a shell command or serializes values to argv and parses them again. null does not mean omitted; integer limits, finite binary64 values, exact decimal strings, list order and Unicode rules remain ADR-0006’s rules. Input schemas use JSON Schema 2020-12 projections with a closed root object. The core remains authoritative even when the client has already validated a schema.
A bounded command result
Section titled “A bounded command result”command-result/1 extends the managed completion report with commandResult. It contains exactly {kind: text, text} or {kind: structured, data} according to the declared result. It is required for succeeded, optional for command_failed, and validated whenever present. It is absent from describe, whose structured help contract remains separate. Invalid or missing required result data is execution_failed, subject to an earlier accepted cancellation. Completion, exit and cleanup must still agree; receiving valid data is not final success.
The result schema is a bounded subset of JSON Schema 2020-12: type is one of object, array, string, boolean, integer, number, null; objects have explicit properties, optional required, and additionalProperties: false; arrays have one items schema and finite maxItems. Applicable minimum, maximum, minLength, maxLength, minItems, enum, title and description are permitted. No references, remote resolution, recursive schemas, regular expressions, code, coercion, defaults or schema composition are accepted in publisher result schemas. Unknown keywords fail descriptor validation. Each schema explicitly selects one type, and constraints must apply to that type. required contains unique declared properties; enum is a nonempty unique set of same-type values satisfying the other constraints; length/cardinality counters are nonnegative integers and lower bounds cannot exceed upper bounds. Numeric and Unicode rules match ADR-0006. Finite payload limits apply even when a scalar schema has no smaller bound.
Selected unmeasured defaults are 64 KiB per result schema, 128 KiB UTF-8 for commandResult, depth 16 and 2,048 total members/elements for either schema or result, and 100 ms elapsed validation per result. Smaller owner or executor limits prevail; the complete IPC envelope must still fit ADR-0003. Exceeding a limit fails rather than truncating a successful result. Validation work is bounded independently of publisher callbacks, because no such callbacks run.
The completion report and command result are validated on receipt, before the final invocation outcome is committed. Validated result data is published or consumed by the caller only after required finalization. Neither stdout nor stderr is guessed to be JSON or promoted into this result. CLI byte streams retain their existing behavior. The MCP baseline gives the invocation closed stdin and no terminal; stdout/stderr are separately drained to owner-controlled, bounded diagnostics, never the host’s MCP stdout and never implicitly returned as agent context. When diagnostic capacity is exhausted the host continues draining and counts drops. A command needing pipeline input must instead expose a declared parameter or admitted resource operation to be machine-compatible.
Owner-controlled exposure and current admission
Section titled “Owner-controlled exposure and current admission”An export exists only when its release is trusted, installed, active and compatible, its declaration is machine-eligible, and the approved profile permits exposure. Built-ins follow equivalent declaration and admission rules. Eligibility does not imply installation, exposure, visibility or execution permission.
The baseline owner profile selects explicit command identities or owner-approved package sets, allowed task presets, resource limits and the enforcement profile from ADR-0015. Ordinary extension metadata cannot export core administration. Sign-in administration, trust/source changes, installation, task issuance, delegation administration, approval and permission editing are excluded from general agent presets; exposure of an owner-built administrative operation requires its separate authority and explicit profile. An ordinary task cannot broaden itself through those operations.
Tool descriptions and MCP annotations are bounded publisher information, not instructions for the host, evidence of safe effects or authorization. Rukh does not claim that an agent will ignore malicious prose. Mandatory controls are independently derived from trusted owner rules and admitted operation implementations. Permission checks apply to each call and each protected operation, even if the tool was listed moments earlier. An unavailable required visibility check returns an explicit list error, not a successful empty or partial list concealing that failure. Static descriptions exclude secret profile configuration and another caller’s data.
The adapter binds taskAuthorityRef, subject and actor through the approved host binding defined by ADR-0014. It does not accept identity claims or new rights inside tool arguments or clientInfo. A local stdio endpoint has one immutable approved launch binding and task preset; reconfiguration cannot mutate that binding. A remote endpoint validates request authority. Choosing an existing task reference is not authority to issue one. An agent application outside Rukh may have other tools; Rukh’s limits do not cover those independent paths.
Atomic projection and obsolete names
Section titled “Atomic projection and obsolete names”ADR-0007 publishes one activeProfileRevision containing configuration and commandGeneration. The candidate now validates both CLI routes and its eligible MCP projection before this same commit. The projection can be derived and cached by that revision; it has no independently mutable active pointer. A policy-only change publishes a new profile and revalidates exports. Current subject-specific visibility is a query over this projection, not a permission grant saved in it.
Each MCP name is slug__fingerprint: a readable ASCII slug of at most 48 characters from [a-z0-9_-], followed by the complete 64-character lowercase SHA-256 hex fingerprint. The fingerprint hashes the RFC 8785 canonical JSON array ["rukh.mcp-tool/1", endpointNamespace, activeProfileRevision, canonicalCommandIdentity]. The endpoint namespace is owner-assigned and stable; the profile already binds the exact release/build, descriptor, mapping, mounts and approved projection settings. Names fit MCP’s 128-character bound. The title can remain readable. Duplicate names or inconsistent identity mappings reject publication; no suffix from installation order is allowed. This conservative choice changes names on any profile revision, including policy-only changes.
tools/call resolves that exact name in the current projection and pins its profile, release and declaration through the existing capture/reference transaction. Unknown, obsolete or currently invisible names return the same safe unknown-tool response without revealing hidden metadata. A removed name never invokes the new release by a similar slug. Existing admitted invocations retain their pinned release while further actions still pass ADR-0012. A current name is not proof that the caller may use it.
MCP version 2026-07-28 is the initial selected adapter contract. Its list must not depend on connection history; the adapter uses the current profile and request-authorized visibility, never a session-specific frozen tool set. Listing uses deterministic name order and static metadata only. Pagination cursors are bounded, integrity-protected references to profile revision, visibility binding and position; any changed binding invalidates the cursor and requires a fresh list. Baseline pages contain at most 32 tools and 1 MiB of encoded data. Tool metadata must fit one page. MCP tools.
Use ttlMs: 0 and cacheScope: private for baseline discovery and tool lists. Notifications may advise subscribed clients of publication changes, but neither notification delivery nor cache freshness participates in authorization. Advertise listChanged only when its subscription path is implemented. MCP caching.
MCP host, results and stopping
Section titled “MCP host, results and stopping”The local baseline is an owner-selected mode of the named CLI using stdio. It needs no listener, daemon, login, database service or separate extension servers. The outer protocol uses MCP framing; the inner child-process channel remains ADR-0003’s private framing and authentication. They are never connected directly. Protocol stdout belongs exclusively to the host. Startup messages, diagnostics and child output cannot corrupt it. MCP stdio.
An HTTP adapter is optional and must implement the selected MCP authorization, origin and request-validation contracts. It authenticates every request, derives actor/subject/task bindings through trusted integrations and does not forward its incoming bearer credential to extension code or downstream services. It is never enabled merely because local MCP is enabled. OIDC/OAuth integrations and custom organizational bindings remain ADR-0005/0011 responsibilities. MCP authorization.
The selected MCP version uses explicit per-request protocol/context and server/discover, not an implicit protocol session. Supporting an earlier version requires a separately validated adapter mapping; it cannot change Rukh’s rights or invocation semantics. Tool calls produce resultType: complete only after the invocation has finalized. The generated outputSchema is a closed core-owned envelope containing invocationId, outcome, optional reason, optional validated commandResult, and optional safe effect references from ADR-0016. The generated schema describes the commandResult wrapper ({kind: text, text} or {kind: structured, data}); the publisher-declared structured schema applies to its data member. The wrapper is required for success. The same envelope is serialized into a text content block for clients consuming text. Publisher output cannot supply or overwrite core outcome/identity fields.
| Observation | MCP boundary behavior |
|---|---|
| Invalid protocol message or unknown/obsolete tool | Standard JSON-RPC protocol error; no command dispatch |
| Known command fails semantic input, support or current access checks | Complete tool result with isError: true, rejected and safe diagnostic; no extension code |
Finalized succeeded | Complete tool result with isError: false and validated result |
command_failed or execution_failed | Complete tool result with isError: true; preserve outcome and uncertainty, without recommending an automatic retry |
| Internal deadline or owner cancellation while a response channel remains valid | Complete error tool result with cancelled and its stable cause; no fabricated success |
| Client cancellation accepted before final commitment | Stop through ADR-0004, retain the internal final outcome, and send no tool response after accepting transport cancellation |
A stdio cancellation notification applies only to that client’s pending request. HTTP request-stream disconnection cancels its invocation; complete local transport loss closes the host’s owned scopes. Unknown cancellation IDs do not affect other clients. The first accepted cause remains authoritative, and all stopping uses the existing non-renewable budget. A committed result stays committed even if its delivery is lost; delivery loss cannot establish rollback. Request IDs correlate delivery and are not effect IDs. MCP cancellation.
The baseline is noninteractive: required dialogs/views make a command unavailable through this adapter; optional interaction returns the normal unavailable operation result. No terminal is opened and no answer is invented. Multi round-trip input_required, requestState and inputResponses continuation is not supported in this profile; unsolicited continuation input is rejected before dispatch. Supporting it requires an explicit continuation contract preserving one invocation/effect identity. A new request ID is never permission to rerun effects. Human approval, if required, comes from ADR-0016’s trusted path, not from an agent’s answer to its own prompt.
The host uses ADR-0010’s finite concurrency and handle limits with no implicit execution queue. Additional selected unmeasured transport defaults are 1 MiB per incoming message, 2 MiB per encoded response, 32 queued outgoing messages or 4 MiB, a 2-second partial-read/blocked-write deadline from the first byte/start of write with no progress-based extension and a 30-second discovery/list deadline. The outer parser also rejects invalid UTF-8, duplicate keys, invalid Unicode and numeric values, and bounds structure to depth 32 and 4,096 members/elements before dispatch. Semantic invocation data retains the smaller ADR-0006 limits. Owners may tighten compatible limits. Each call must have a finite invocation deadline under its approved task preset; progress does not renew it. Congestion cannot block cancellation or lifecycle admission; if control cannot progress, close the transport and finalize owned work.
This accepted sequence keeps list publication, final admission and completion separate.
Consequences
Section titled “Consequences”- Teams implement one command and may expose it through both interfaces without operating additional servers.
- Existing CLI extensions continue to work; machine exposure requires a deliberate versioned declaration and owner profile.
- Fingerprinted names and conservative cache settings trade some prompt-cache stability for explicit stale-binding rejection.
- Text result mode supports simple extensions. Structured results improve interoperability without making terminal text a protocol.
- Local host-trusted operation and stricter confinement profiles remain distinct. MCP annotations and successful parsing do not establish isolation, safe semantics or human intent.
- A planner, model runtime, remote gateway, background job service, sampling, arbitrary MCP Apps and data-flow tracking are not implied by this decision.
Confirmation
Section titled “Confirmation”The owner accepted this architectural direction on 2026-09-24. The following checks remain unrun implementation requirements:
- One extension has equivalent CLI/MCP semantic inputs and identity, including omitted/defaulted values, explicit IDs, lists, Unicode and exact numbers. Passthrough, unknown fields and unsupported machine requirements cannot bypass admission.
- Listing never starts code or prepares environments. Exposure and task rules apply to built-ins and extensions; descriptions, client names and tool annotations cannot grant rights.
- CLI and MCP publish from one profile transaction. Concurrent updates, policy changes, pagination, stale tool names and visibility changes never redirect a call or leak another caller’s list.
- Result-schema mismatch, size/validation limits, noisy streams and unsupported interaction preserve defined outcomes and do not corrupt MCP stdout. Text and structured modes obey the same finalization rules.
- Concurrent requests, cancellation, host loss, response loss and deadlines preserve one outcome, bounded cleanup and ADR-0016’s effect guarantees without replay. Strict profiles reject unavailable enforcement.
- Demonstrate a local static-catalog CLI with no enterprise services. Separately validate each supported protocol version and any optional HTTP adapter, including caller binding, audience checks and absence of token passthrough.
The older ADR-0002/0003 reference checks do not cover this feature or a production MCP adapter. Rendered diagrams and valid documentation establish no product conformance.
- ADR-0002: Core–extension interaction, ADR-0003: Process IPC.
- ADR-0006: Extension descriptor, ADR-0007: Loading and updates, ADR-0010: Core composition.
- ADR-0014: Task authority and delegation, ADR-0015: Execution confinement, ADR-0016: Effect commitment and reconciliation.
- RFC 8785: JSON Canonicalization Scheme.