Skip to content

Feature: MCP server for mex — let agents call check/sync/log as native tools #81

Description

@rudi193-cmd

The problem

Right now agents have to shell out to use mex:

```

agent has to run this as a subprocess

mex check --json
mex log "decided to use postgres" --type decision
```

That breaks the tool-call flow in Claude Code, Cursor, and any agent that routes through MCP. The agent loses structured output, can't compose mex calls with other tools, and the whole thing degrades gracefully to "run this bash command and paste the output."

An MCP server fixes this: mex becomes a first-class tool the agent calls directly, same as any other MCP tool.


What the tools would look like

```typescript
// agent calls these as native MCP tools — no subprocess, structured output
mex_check() // → drift report (score, errors, warnings, stale files)
mex_sync() // → targeted prompts for the AI to fix flagged files
mex_log(message, type, files) // → confirms appended event
mex_timeline(limit, since) // → recent events as structured JSON
mex_heartbeat() // → health check results
mex_read_file(path) // → scaffold file contents (ROUTER.md, context/architecture.md, etc.)
```

The mex_read_file tool is the one agents would use most — at session start, load ROUTER.md, then load only the context files relevant to the current task. No filesystem access required.


Two implementation paths

Option A — Thin wrapper (standalone package, ships fast)

A small MCP server that shells out to the mex CLI. No architectural changes to mex itself.

```typescript
// packages/mex-mcp/src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { execa } from "execa";
import { readFile } from "node:fs/promises";
import { z } from "zod";

const server = new McpServer({ name: "mex", version: "1.0.0" });

server.tool("mex_check", "Run drift detection against the mex scaffold", {}, async () => {
const { stdout } = await execa("mex", ["check", "--json"]);
return { content: [{ type: "text", text: stdout }] };
});

server.tool("mex_sync", "Build targeted prompts to fix scaffold drift", {}, async () => {
const { stdout } = await execa("mex", ["sync", "--dry-run"]);
return { content: [{ type: "text", text: stdout }] };
});

server.tool(
"mex_log",
"Append a decision, note, risk, or todo to the mex event log",
{ message: z.string(), type: z.enum(["decision", "note", "risk", "todo"]).optional() },
async ({ message, type }) => {
const args = ["log", message, ...(type ? ["--type", type] : [])];
const { stdout } = await execa("mex", args);
return { content: [{ type: "text", text: stdout }] };
}
);

server.tool(
"mex_read_file",
"Read a scaffold file by path relative to .mex/ (e.g. ROUTER.md, context/architecture.md)",
{ path: z.string() },
async ({ path: filePath }) => {
const content = await readFile(filePath, "utf8");
return { content: [{ type: "text", text: content }] };
}
);

await server.connect(new StdioServerTransport());
```

Agent config (Claude Code .mcp.json):
```json
{
"mcpServers": {
"mex": {
"command": "npx",
"args": ["mex-mcp"]
}
}
}
```


Option B — Embedded API (cleaner long-term)

Extract mex's core functions as an importable API, then build the MCP server on top of that instead of shelling out. This is what production-grade agent platforms do — the MCP server is the library.

```typescript
// src/api.ts — export the core functions directly
export async function checkScaffold(opts?: CheckOptions): Promise { ... }
export async function syncScaffold(opts?: SyncOptions): Promise { ... }
export async function logEvent(message: string, opts?: LogOptions): Promise { ... }
export async function readTimeline(opts?: TimelineOptions): Promise<Event[]> { ... }

// packages/mex-mcp/src/index.ts — thin MCP adapter
import { checkScaffold, syncScaffold, logEvent } from "mex-agent/api";
// no subprocesses, no JSON parse/stringify overhead, typed end-to-end
```

Option B is more work but means the MCP server and CLI both run the same code — no drift between what the CLI reports and what the MCP server returns.


Prior art

We've shipped something in this space on our own agent platform — a persistent MCP server that exposes memory, KB search, and scaffolding tools across sessions. Happy to share patterns, code samples, or implementation notes from that if useful. The main lessons:

  1. Structured JSON output from every tool — agents compose tool calls; free-text responses force another LLM call to parse them
  2. A read_file tool beats filesystem access — agents load only the context they need, scaffolded by path
  3. Session-start boot sequencemex_check at session start + mex_read_file("ROUTER.md") as a two-call boot is all agents need to orient themselves

Questions for the maintainer

  • Is Option A (subprocess wrapper) acceptable as a first contribution, or would you prefer the API extraction in Option B?
  • Is packages/mex-mcp the right location, or a separate mex-mcp npm package?
  • Any constraints on the MCP SDK version or transport (stdio vs HTTP)?

Happy to open a draft PR for either path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions