feat: support one-command multi-client MCP setup

- Allow comma-separated clients for setup, print-config, and install-rules
- Add --install-rules to setup so MCP config and agent memory install together
- Document the Claude Code plus Codex one-command demo path

Generated with Claude Code
This commit is contained in:
“BeeRad”
2026-04-21 09:24:31 +10:00
parent e4f51148fa
commit ad07fcb868
6 changed files with 74 additions and 31 deletions
+3 -2
View File
@@ -15,12 +15,13 @@ Other useful commands:
```bash
npx -y ra-h-mcp-server@latest setup --client cursor --yes
npx -y ra-h-mcp-server@latest setup --client codex --yes
npx -y ra-h-mcp-server@latest setup --client claude-code,codex --yes --install-rules --target .
npx -y ra-h-mcp-server@latest init-db
npx -y ra-h-mcp-server@latest doctor
npx -y ra-h-mcp-server@latest print-config --client claude-code
```
`--yes` lets the installer write supported client config automatically. Codex uses TOML config, so the installer writes `CODEX_HOME/config.toml` or `~/.codex/config.toml`.
`--client` accepts a single client or comma-separated clients. `--yes` lets the installer write supported client config automatically. Codex uses TOML config, so the installer writes `CODEX_HOME/config.toml` or `~/.codex/config.toml`.
Important contract:
- `@latest` is the default user-facing install path
@@ -96,7 +97,7 @@ Retrieve relevant RA-H context before substantive work, search before creating,
Install or refresh that guidance without replacing the rest of the memory file:
```bash
npx -y ra-h-mcp-server@latest install-rules --client codex --target . --yes
npx -y ra-h-mcp-server@latest install-rules --client claude-code,codex --target . --yes
```
RA-H should still work well without this line. The MCP tools, server instructions, skills, and docs are meant to carry the core behavior on their own.
+52 -21
View File
@@ -34,18 +34,19 @@ function usage() {
Usage:
ra-h-mcp-server Start MCP stdio server
ra-h-mcp-server setup --client <name> Configure MCP for an agent
ra-h-mcp-server setup --client <name> Configure MCP for one or more agents
ra-h-mcp-server doctor Verify package, DB, and schema
ra-h-mcp-server init-db Create/verify the RA-H SQLite DB
ra-h-mcp-server print-config --client <name>
ra-h-mcp-server install-rules --client <name> [--target <path>]
Options:
--client <name> claude-code, claude-desktop, cursor, codex, opencode, vscode, windsurf, aider
--client <name[,name]> claude-code, claude-desktop, cursor, codex, opencode, vscode, windsurf, aider
--db <path> Override DB path for this command
--scope <scope> user or project (default: user)
--pin current Use this package version instead of @latest in generated config
--yes Write supported config files without prompting
--install-rules Also install RA-H graph behavior into each client's repo memory file
--print-only Print config/rules without writing files
--target <path> Directory for project rule files
`);
@@ -59,6 +60,7 @@ function parseArgs(argv) {
scope: 'user',
pin: null,
yes: false,
installRules: false,
printOnly: false,
target: null,
};
@@ -69,12 +71,14 @@ function parseArgs(argv) {
args.help = true;
} else if (arg === '--yes' || arg === '-y') {
args.yes = true;
} else if (arg === '--install-rules' || arg === '--rules') {
args.installRules = true;
} else if (arg === '--print-only' || arg === '--dry-run') {
args.printOnly = true;
} else if (['--client', '--db', '--scope', '--pin', '--target'].includes(arg)) {
} else if (['--client', '--clients', '--db', '--scope', '--pin', '--target'].includes(arg)) {
const value = argv[i + 1];
if (!value || value.startsWith('--')) fail(`${arg} requires a value`);
args[arg.slice(2)] = value;
args[arg === '--clients' ? 'client' : arg.slice(2)] = value;
i += 1;
} else {
args._.push(arg);
@@ -337,12 +341,23 @@ function formatSnippet(snippet) {
}
function validateClient(client) {
if (!client) fail('Missing --client');
if (!SUPPORTED_CLIENTS.has(client)) {
fail(`Unsupported client "${client}". Supported: ${Array.from(SUPPORTED_CLIENTS).join(', ')}`);
}
}
function resolveClients(rawClient) {
if (!rawClient) fail('Missing --client');
const clients = rawClient
.split(',')
.map((client) => client.trim())
.filter(Boolean);
if (clients.length === 0) fail('Missing --client');
clients.forEach(validateClient);
return [...new Set(clients)];
}
function rulesSnippet() {
return `## RA-H Graph Memory
@@ -409,18 +424,20 @@ function commandDoctor(args) {
}
function commandPrintConfig(args) {
validateClient(args.client);
const clients = resolveClients(args.client);
const dbPath = resolveDbPath(args);
const config = clientConfig(args.client, args, dbPath);
console.log(formatSnippet(config.snippet));
if (config.note) log(config.note);
if (config.path) log(`Target path: ${config.path}`);
clients.forEach((client) => {
const config = clientConfig(client, args, dbPath);
if (clients.length > 1) log(`Config for ${client}:`);
console.log(formatSnippet(config.snippet));
if (config.note) log(config.note);
if (config.path) log(`Target path: ${config.path}`);
});
}
function commandInstallRules(args) {
validateClient(args.client);
function installRulesForClient(client, args) {
const content = rulesSnippet();
const targetPath = rulesTarget(args.client, args.target);
const targetPath = rulesTarget(client, args.target);
if (args.printOnly || !args.yes) {
console.log(content);
@@ -435,13 +452,13 @@ function commandInstallRules(args) {
log(`Updated rules in ${targetPath}`);
}
function commandSetup(args) {
validateClient(args.client);
const dbPath = resolveDbPath(args);
initDb(dbPath);
log(`Database ready at ${dbPath}`);
function commandInstallRules(args) {
resolveClients(args.client).forEach((client) => installRulesForClient(client, args));
}
const config = clientConfig(args.client, args, dbPath);
function setupClient(client, args, dbPath) {
const config = clientConfig(client, args, dbPath);
log(`Configuring ${client}`);
console.log(formatSnippet(config.snippet));
if (config.path) log(`MCP config target: ${config.path}`);
if (config.note) log(config.note);
@@ -460,9 +477,23 @@ function commandSetup(args) {
log('Automatic config writing is not available for this client; copy the printed config.');
}
const rulePath = rulesTarget(args.client, args.target);
if (args.installRules) {
installRulesForClient(client, args);
return;
}
const rulePath = rulesTarget(client, args.target);
log(`Recommended rules target: ${rulePath}`);
log(`Install rules with: npx -y ${packageSpec(args)} install-rules --client ${args.client} --target <repo> --yes`);
log(`Install rules with: npx -y ${packageSpec(args)} install-rules --client ${client} --target <repo> --yes`);
}
function commandSetup(args) {
const clients = resolveClients(args.client);
const dbPath = resolveDbPath(args);
initDb(dbPath);
log(`Database ready at ${dbPath}`);
clients.forEach((client) => setupClient(client, args, dbPath));
commandDoctor(args);
}
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "ra-h-mcp-server",
"version": "3.2.2",
"version": "3.2.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "ra-h-mcp-server",
"version": "3.2.2",
"version": "3.2.3",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ra-h-mcp-server",
"version": "3.2.2",
"version": "3.2.3",
"description": "Connect Claude Code/Desktop to your RA-H knowledge base. Direct SQLite access to an existing RA-H database.",
"main": "index.js",
"bin": {