feat(cli): scaffold @claudemesh/cli MCP client package (stubs)

The user-facing tool. Two invocation modes:
  - `claudemesh mcp`           → MCP server (stdio), consumed by Claude Code
  - `claudemesh <subcommand>`  → human CLI

Layout:
  apps/cli/
  ├── package.json       bin: { claudemesh: ./src/index.ts }
  ├── README.md          install + usage
  └── src/
      ├── index.ts       dispatcher (mcp | install | join | list | leave | --help)
      ├── env.ts         CLAUDEMESH_BROKER_URL, CONFIG_DIR, DEBUG
      ├── mcp/
      │   ├── server.ts  MCP stdio server with 5 tools
      │   ├── tools.ts   tool schemas (send_message, list_peers,
      │   │              check_messages, set_summary, set_status)
      │   └── types.ts
      ├── ws/client.ts   broker connection (stub for 15b)
      ├── state/config.ts ~/.claudemesh/config.json (joined meshes + keys)
      └── commands/
          ├── install.ts print `claude mcp add ...` instruction
          ├── join.ts    parse ic://join/... (stub, Step 17)
          ├── list.ts    show joined meshes
          └── leave.ts   remove mesh from local config

Tool stubs return "not connected, run `claudemesh join <invite-link>`"
errors until 15b wires the WS client.

Verified:
- `bun src/index.ts --help` → prints usage
- `bun src/index.ts install` → prints MCP add command with resolved path
- `bun src/index.ts list` → "No meshes joined yet"
- `bun src/index.ts mcp` (via stdin) → returns tools/list with all 5 tools

Deps: @modelcontextprotocol/sdk, ws, libsodium-wrappers, zod.
Lockfile regenerated in the same commit per claudemesh-3's flag —
avoids breaking Coolify's --frozen-lockfile deploys.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 22:23:12 +01:00
parent c6674e971a
commit 8931296e82
16 changed files with 1048 additions and 2 deletions

81
apps/cli/src/mcp/tools.ts Normal file
View File

@@ -0,0 +1,81 @@
/**
* MCP tool definitions exposed to Claude Code.
*
* Mirror the claude-intercom tool surface: send_message, list_peers,
* check_messages, set_summary, set_status. Tools return "not
* connected" errors until 15b wires the WS client.
*/
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
export const TOOLS: Tool[] = [
{
name: "send_message",
description:
"Send a message to a peer in one of your joined meshes. `to` is a peer display name, hex pubkey, or `#channel`. `priority` controls delivery: `now` bypasses busy gates, `next` waits for idle (default), `low` is pull-only.",
inputSchema: {
type: "object",
properties: {
to: {
type: "string",
description: "Peer name, pubkey, or #channel",
},
message: { type: "string", description: "Message text" },
priority: {
type: "string",
enum: ["now", "next", "low"],
description: "Delivery priority (default: next)",
},
},
required: ["to", "message"],
},
},
{
name: "list_peers",
description:
"List peers across all joined meshes. Shows name, mesh, status (idle/working/dnd), and current summary.",
inputSchema: {
type: "object",
properties: {
mesh_slug: {
type: "string",
description: "Only list peers in this mesh (optional)",
},
},
},
},
{
name: "check_messages",
description:
"Pull any undelivered messages from the broker. Normally messages arrive via push; use this to drain the queue after being offline.",
inputSchema: { type: "object", properties: {} },
},
{
name: "set_summary",
description:
"Set a 12 sentence summary of what you're working on. Visible to other peers.",
inputSchema: {
type: "object",
properties: {
summary: { type: "string", description: "1-2 sentence summary" },
},
required: ["summary"],
},
},
{
name: "set_status",
description:
"Manually override your status. `dnd` blocks everything except `now`-priority messages.",
inputSchema: {
type: "object",
properties: {
status: {
type: "string",
enum: ["idle", "working", "dnd"],
description: "Your status",
},
},
required: ["status"],
},
},
];