Skip to content

step: mcp-client — invoke a CLI-wrapped MCP server and ingest its JSON output #52

Description

@hartsock

Summary

An mcp-client step type that spawns a CLI process acting as an MCP client, passes a structured tool call as arguments, and ingests the JSON response into the routine report.

Motivation

Many services expose functionality through an MCP server fronted by a CLI wrapper (e.g. mytool mcp, mytool briefing --json). The existing harness step type can spawn the process, but it treats stdout as opaque text. An mcp-client step would understand the MCP tool-call/response envelope, validate the schema, and promote the result into the report's data field as a first-class structured payload.

This enables any MCP-capable CLI to participate in a routine without writing a custom step type — the step type is the adapter.

Proposed design

Step type: mcp-client

Config shape:

[[routines.morning.steps]]
name    = "briefing"
type    = "mcp-client"
command = "mytool"
args    = ["briefing", "--json"]   # CLI args that cause the tool to emit MCP JSON
tool    = "briefing_get"           # optional: MCP tool name to call
params  = { hours = 24 }           # optional: tool input params
timeout = 60
env     = { MY_TOKEN = { file = "~/.keys/mytoken" } }

Two invocation modes:

  1. Subprocess JSON mode (args only): spawn command, capture stdout, parse as MCP CallToolResult. Simple; works with any CLI that emits a valid MCP response body.
  2. stdio MCP mode (tool + params): start the process as a stdio MCP server, send a tools/call request over stdin, read the response, shut down. Full protocol; works with any modulex-mcp-compatible server.

Data contract:
The data field carries the raw content array from the MCP CallToolResult, schema-tagged mcp-client/1.

Behavior:

  • Non-zero exit or malformed JSON: success = false, error set — routine continues (soft failure).
  • isError: true in the MCP response propagates as success = false.
  • Spawned only through ExecGate; command must be in the declared or leash-granted program list.

Alternatives considered

  • harness step: works today for text output; lacks structured data promotion and MCP envelope awareness.
  • Writing a custom Rust step per service: correct but defeats the purpose of a generic adapter.

Acceptance criteria

  • mcp-client step type in modulex-core
  • Subprocess JSON mode (parse MCP CallToolResult from stdout)
  • stdio MCP mode (full tools/call round-trip)
  • data schema pinned with golden file test
  • isError propagation
  • Spawned exclusively via ExecGate
  • modulex doctor reports whether the command is available

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions