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:
@@ -88,6 +88,12 @@ 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 codex --yes
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Multiple clients can be installed in one pass. This is the best path if you use both Claude Code and Codex:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx -y ra-h-mcp-server@latest setup --client claude-code,codex --yes
|
||||||
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
- `--yes` lets the installer write supported client config automatically.
|
- `--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`.
|
- Codex uses TOML config, so the installer writes `CODEX_HOME/config.toml` or `~/.codex/config.toml`.
|
||||||
@@ -165,8 +171,10 @@ SQLITE_DB_PATH="$HOME/Desktop/ra-h_os-demo-data/rah.sqlite" npm run setup:local
|
|||||||
npm run dev
|
npm run dev
|
||||||
|
|
||||||
npx -y ra-h-mcp-server@latest setup \
|
npx -y ra-h-mcp-server@latest setup \
|
||||||
--client claude-code \
|
--client claude-code,codex \
|
||||||
--yes \
|
--yes \
|
||||||
|
--install-rules \
|
||||||
|
--target "$HOME/Desktop/ra-h_os-demo" \
|
||||||
--db "$HOME/Desktop/ra-h_os-demo-data/rah.sqlite"
|
--db "$HOME/Desktop/ra-h_os-demo-data/rah.sqlite"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -231,7 +239,7 @@ You are helping build a thoughtful graph of atomic units of context.
|
|||||||
Or install that guidance into the repo memory file:
|
Or install that guidance into the repo memory file:
|
||||||
|
|
||||||
```bash
|
```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
|
||||||
```
|
```
|
||||||
|
|
||||||
Available tools:
|
Available tools:
|
||||||
|
|||||||
@@ -15,12 +15,13 @@ Other useful commands:
|
|||||||
```bash
|
```bash
|
||||||
npx -y ra-h-mcp-server@latest setup --client cursor --yes
|
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 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 init-db
|
||||||
npx -y ra-h-mcp-server@latest doctor
|
npx -y ra-h-mcp-server@latest doctor
|
||||||
npx -y ra-h-mcp-server@latest print-config --client claude-code
|
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:
|
Important contract:
|
||||||
- `@latest` is the default user-facing install path
|
- `@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:
|
Install or refresh that guidance without replacing the rest of the memory file:
|
||||||
|
|
||||||
```bash
|
```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.
|
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.
|
||||||
|
|||||||
@@ -34,18 +34,19 @@ function usage() {
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
ra-h-mcp-server Start MCP stdio server
|
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 doctor Verify package, DB, and schema
|
||||||
ra-h-mcp-server init-db Create/verify the RA-H SQLite DB
|
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 print-config --client <name>
|
||||||
ra-h-mcp-server install-rules --client <name> [--target <path>]
|
ra-h-mcp-server install-rules --client <name> [--target <path>]
|
||||||
|
|
||||||
Options:
|
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
|
--db <path> Override DB path for this command
|
||||||
--scope <scope> user or project (default: user)
|
--scope <scope> user or project (default: user)
|
||||||
--pin current Use this package version instead of @latest in generated config
|
--pin current Use this package version instead of @latest in generated config
|
||||||
--yes Write supported config files without prompting
|
--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
|
--print-only Print config/rules without writing files
|
||||||
--target <path> Directory for project rule files
|
--target <path> Directory for project rule files
|
||||||
`);
|
`);
|
||||||
@@ -59,6 +60,7 @@ function parseArgs(argv) {
|
|||||||
scope: 'user',
|
scope: 'user',
|
||||||
pin: null,
|
pin: null,
|
||||||
yes: false,
|
yes: false,
|
||||||
|
installRules: false,
|
||||||
printOnly: false,
|
printOnly: false,
|
||||||
target: null,
|
target: null,
|
||||||
};
|
};
|
||||||
@@ -69,12 +71,14 @@ function parseArgs(argv) {
|
|||||||
args.help = true;
|
args.help = true;
|
||||||
} else if (arg === '--yes' || arg === '-y') {
|
} else if (arg === '--yes' || arg === '-y') {
|
||||||
args.yes = true;
|
args.yes = true;
|
||||||
|
} else if (arg === '--install-rules' || arg === '--rules') {
|
||||||
|
args.installRules = true;
|
||||||
} else if (arg === '--print-only' || arg === '--dry-run') {
|
} else if (arg === '--print-only' || arg === '--dry-run') {
|
||||||
args.printOnly = true;
|
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];
|
const value = argv[i + 1];
|
||||||
if (!value || value.startsWith('--')) fail(`${arg} requires a value`);
|
if (!value || value.startsWith('--')) fail(`${arg} requires a value`);
|
||||||
args[arg.slice(2)] = value;
|
args[arg === '--clients' ? 'client' : arg.slice(2)] = value;
|
||||||
i += 1;
|
i += 1;
|
||||||
} else {
|
} else {
|
||||||
args._.push(arg);
|
args._.push(arg);
|
||||||
@@ -337,12 +341,23 @@ function formatSnippet(snippet) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateClient(client) {
|
function validateClient(client) {
|
||||||
if (!client) fail('Missing --client');
|
|
||||||
if (!SUPPORTED_CLIENTS.has(client)) {
|
if (!SUPPORTED_CLIENTS.has(client)) {
|
||||||
fail(`Unsupported client "${client}". Supported: ${Array.from(SUPPORTED_CLIENTS).join(', ')}`);
|
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() {
|
function rulesSnippet() {
|
||||||
return `## RA-H Graph Memory
|
return `## RA-H Graph Memory
|
||||||
|
|
||||||
@@ -409,18 +424,20 @@ function commandDoctor(args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function commandPrintConfig(args) {
|
function commandPrintConfig(args) {
|
||||||
validateClient(args.client);
|
const clients = resolveClients(args.client);
|
||||||
const dbPath = resolveDbPath(args);
|
const dbPath = resolveDbPath(args);
|
||||||
const config = clientConfig(args.client, args, dbPath);
|
clients.forEach((client) => {
|
||||||
|
const config = clientConfig(client, args, dbPath);
|
||||||
|
if (clients.length > 1) log(`Config for ${client}:`);
|
||||||
console.log(formatSnippet(config.snippet));
|
console.log(formatSnippet(config.snippet));
|
||||||
if (config.note) log(config.note);
|
if (config.note) log(config.note);
|
||||||
if (config.path) log(`Target path: ${config.path}`);
|
if (config.path) log(`Target path: ${config.path}`);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function commandInstallRules(args) {
|
function installRulesForClient(client, args) {
|
||||||
validateClient(args.client);
|
|
||||||
const content = rulesSnippet();
|
const content = rulesSnippet();
|
||||||
const targetPath = rulesTarget(args.client, args.target);
|
const targetPath = rulesTarget(client, args.target);
|
||||||
|
|
||||||
if (args.printOnly || !args.yes) {
|
if (args.printOnly || !args.yes) {
|
||||||
console.log(content);
|
console.log(content);
|
||||||
@@ -435,13 +452,13 @@ function commandInstallRules(args) {
|
|||||||
log(`Updated rules in ${targetPath}`);
|
log(`Updated rules in ${targetPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function commandSetup(args) {
|
function commandInstallRules(args) {
|
||||||
validateClient(args.client);
|
resolveClients(args.client).forEach((client) => installRulesForClient(client, args));
|
||||||
const dbPath = resolveDbPath(args);
|
}
|
||||||
initDb(dbPath);
|
|
||||||
log(`Database ready at ${dbPath}`);
|
|
||||||
|
|
||||||
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));
|
console.log(formatSnippet(config.snippet));
|
||||||
if (config.path) log(`MCP config target: ${config.path}`);
|
if (config.path) log(`MCP config target: ${config.path}`);
|
||||||
if (config.note) log(config.note);
|
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.');
|
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(`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);
|
commandDoctor(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "ra-h-mcp-server",
|
"name": "ra-h-mcp-server",
|
||||||
"version": "3.2.2",
|
"version": "3.2.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ra-h-mcp-server",
|
"name": "ra-h-mcp-server",
|
||||||
"version": "3.2.2",
|
"version": "3.2.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.0.0",
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ra-h-mcp-server",
|
"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.",
|
"description": "Connect Claude Code/Desktop to your RA-H knowledge base. Direct SQLite access to an existing RA-H database.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
+6
-3
@@ -15,9 +15,10 @@ Other clients:
|
|||||||
```bash
|
```bash
|
||||||
npx -y ra-h-mcp-server@latest setup --client cursor --yes
|
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 codex --yes
|
||||||
|
npx -y ra-h-mcp-server@latest setup --client claude-code,codex --yes
|
||||||
```
|
```
|
||||||
|
|
||||||
`--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`.
|
||||||
|
|
||||||
Verify the install:
|
Verify the install:
|
||||||
|
|
||||||
@@ -31,8 +32,10 @@ For demo isolation or a separate DB:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx -y ra-h-mcp-server@latest setup \
|
npx -y ra-h-mcp-server@latest setup \
|
||||||
--client claude-code \
|
--client claude-code,codex \
|
||||||
--yes \
|
--yes \
|
||||||
|
--install-rules \
|
||||||
|
--target "$HOME/Desktop/ra-h_os-demo" \
|
||||||
--db "$HOME/Desktop/ra-h_os-demo-data/rah.sqlite"
|
--db "$HOME/Desktop/ra-h_os-demo-data/rah.sqlite"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -147,5 +150,5 @@ You are helping build a thoughtful graph of atomic units of context.
|
|||||||
The installer can add or refresh this section without replacing the rest of the memory file:
|
The installer can add or refresh this section without replacing the rest of the memory file:
|
||||||
|
|
||||||
```bash
|
```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
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user