From b004e9fbb4f3fed9252f4546fd79bbcd41ea76a9 Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Mon, 29 Jun 2026 01:56:24 +0200 Subject: [PATCH] feat(mcp): `agenthub mcp install` writes .mcp.json (auto-register) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One command, no hand-editing: `agenthub mcp install` writes/merges a project .mcp.json (the standard config Claude Code and other .mcp.json-aware clients auto-discover) with the hub URL filled in — so those agents register automatically. Prints the global-config snippet for Codex/Kimi as fallback. `--print` is a dry run. Bump 0.7.3 -> 0.7.4. Co-Authored-By: Claude Opus 4.8 --- package.json | 2 +- src/cli/index.ts | 11 +++---- src/mcp/install.ts | 74 +++++++++++++++++++++++++++------------------- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 35d0a05..12bc12f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agenthub", - "version": "0.7.3", + "version": "0.7.4", "description": "Local coordination layer for AI coding agents", "type": "module", "main": "./dist/index.js", diff --git a/src/cli/index.ts b/src/cli/index.ts index 64d373b..2f5ce13 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -12,7 +12,7 @@ import { watchEvents } from './commands/watch.js'; import { startAgent } from './commands/start.js'; import { workAgent } from './commands/work.js'; import { startMcpServer } from '../mcp/server.js'; -import { printMcpConfig } from '../mcp/install.js'; +import { installMcp } from '../mcp/install.js'; import { loadConfig, saveConfig } from '../core/config.js'; import { findProjectRoot } from '../core/paths.js'; import { discoverServer } from '../discovery.js'; @@ -116,7 +116,7 @@ async function runRemote(serverUrl: string, fn: () => Promise): Promise', 'AgentHub server URL (env: AGENTHUB_SERVER)'); program @@ -486,10 +486,11 @@ export function createProgram(cwd: string): Command { // ─── mcp ─────────────────────────────────────────────────────────────────── program .command('mcp [action]') - .description('Start the MCP server (stdio). `agenthub mcp install` prints per-CLI registration config.') - .action(async (action?: string) => { + .description('Start the MCP server (stdio). `agenthub mcp install` writes .mcp.json so MCP-aware agents auto-register.') + .option('--print', 'Dry run: print the config instead of writing .mcp.json') + .action(async (action: string | undefined, options: { print?: boolean }) => { if (action === 'install') { - printMcpConfig(cwd); + installMcp(cwd, { print: options.print }); return; } await startMcpServer(cwd); diff --git a/src/mcp/install.ts b/src/mcp/install.ts index aa15dcf..e5acbfd 100644 --- a/src/mcp/install.ts +++ b/src/mcp/install.ts @@ -1,13 +1,17 @@ +import { readFileSync, writeFileSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; import { findProjectRoot } from '../core/paths.js'; import { loadConfig } from '../core/config.js'; /** - * Print ready-to-paste MCP registration config for each agent CLI, with the - * hub URL filled in. The MCP server itself is `agenthub mcp` (stdio) — every - * client just needs to know to spawn it + which hub to talk to. + * Register the AgentHub MCP server with as little friction as possible. + * + * Default: writes/merges a project `.mcp.json` — the standard config that Claude + * Code (and any `.mcp.json`-aware MCP client) auto-discovers, so those agents + * register with zero hand-editing. For CLIs that use a GLOBAL config instead + * (Codex / Kimi) it prints a ready-to-paste snippet. `--print` is a dry run. */ -export function printMcpConfig(cwd: string): void { - const root = findProjectRoot(cwd) ?? cwd; +function resolveUrl(root: string): string { let serverUrl = process.env.AGENTHUB_SERVER || undefined; if (!serverUrl) { try { @@ -16,44 +20,54 @@ export function printMcpConfig(cwd: string): void { /* no project config */ } } - const url = serverUrl ?? 'http://127.0.0.1:3377'; + return serverUrl ?? 'http://127.0.0.1:3377'; +} - const out = (s: string) => process.stdout.write(s + '\n'); +export function installMcp(cwd: string, opts: { print?: boolean } = {}): void { + const root = findProjectRoot(cwd) ?? cwd; + const url = resolveUrl(root); + const serverDef = { command: 'agenthub', args: ['mcp'], env: { AGENTHUB_SERVER: url } }; + const out = (s = '') => process.stdout.write(s + '\n'); - out(''); + out(); out('AgentHub MCP — registration'); out('═══════════════════════════'); out(`Hub URL: ${url}`); - out('(On the Windows machine use the Mac\'s LAN IP, e.g. http://192.168.178.30:3377)'); - out(''); - out('The MCP server is `agenthub mcp` (stdio). Register it once per agent CLI:'); - out(''); - out('── Claude Code ──────────────────────────────────────────────'); - out(` claude mcp add agenthub --env AGENTHUB_SERVER=${url} -- agenthub mcp`); - out(' (or add the .mcp.json block below to the project)'); - out(''); + const mcpJsonPath = join(root, '.mcp.json'); + if (opts.print) { + out(''); + out(`Would write ${mcpJsonPath}:`); + out(JSON.stringify({ mcpServers: { agenthub: serverDef } }, null, 2)); + } else { + let json: { mcpServers?: Record } = { mcpServers: {} }; + if (existsSync(mcpJsonPath)) { + try { + json = JSON.parse(readFileSync(mcpJsonPath, 'utf-8')) as typeof json; + } catch { + json = { mcpServers: {} }; + } + } + if (!json.mcpServers) json.mcpServers = {}; + json.mcpServers.agenthub = serverDef; + writeFileSync(mcpJsonPath, JSON.stringify(json, null, 2) + '\n', 'utf-8'); + out(''); + out(`✓ Wrote ${mcpJsonPath}`); + out(' → Claude Code (and any .mcp.json-aware client) opened here auto-registers it.'); + } - out('── .mcp.json (project root — Claude Code / generic) ─────────'); - out(JSON.stringify( - { mcpServers: { agenthub: { command: 'agenthub', args: ['mcp'], env: { AGENTHUB_SERVER: url } } } }, - null, - 2, - )); out(''); - - out('── Codex CLI (~/.codex/config.toml) ─────────────────────────'); + out('For CLIs that use a GLOBAL config instead of project .mcp.json:'); + out(''); + out('── Codex (~/.codex/config.toml) ──'); out(' [mcp_servers.agenthub]'); out(' command = "agenthub"'); out(' args = ["mcp"]'); out(` env = { AGENTHUB_SERVER = "${url}" }`); out(''); - - out('── Kimi CLI ─────────────────────────────────────────────────'); - out(' Add a stdio MCP server via Kimi\'s MCP config with the same shape:'); - out(' command: agenthub args: ["mcp"] env: { AGENTHUB_SERVER: ' + url + ' }'); + out('── Kimi (its MCP config) ──'); + out(` stdio server — command: agenthub args: ["mcp"] env: AGENTHUB_SERVER=${url}`); out(''); - out('After registering, the agent calls the tools (agenthub_work, agenthub_task_review, …)'); - out('instead of CLI strings. Requires agenthub >= 0.7.0 on each machine.'); + out('On Windows use the Mac LAN IP (e.g. http://192.168.178.30:3377). Requires agenthub >= 0.7.0.'); out(''); }