@crush-protocol/mcp-client 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,245 +1,200 @@
1
- # Crush Protocol MCP Server
1
+ # Crush Protocol MCP Client
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@crush-protocol/mcp-client)](https://www.npmjs.com/package/@crush-protocol/mcp-client)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
4
 
6
- **AI-powered quantitative trading tools for Claude Code and other MCP clients.**
5
+ `@crush-protocol/mcp-client` is the npm entrypoint for Crush Protocol MCP.
7
6
 
8
- Run backtests, validate trading strategies, and query market data directly from your AI coding assistant.
7
+ Crush Protocol is an AI-native quantitative trading product. It lets an MCP host connect to Crush and use trading-focused tools for:
9
8
 
10
- ## Without Crush Protocol MCP
9
+ - strategy research
10
+ - backtest creation and result retrieval
11
+ - live strategy management
12
+ - market data and signal discovery
11
13
 
12
- - Manually switch between your IDE and trading dashboards
13
- - ❌ Copy-paste backtest configs and wait for results separately
14
- - ❌ No way for your AI agent to iterate on strategies automatically
14
+ This package connects your MCP host to the Crush MCP server over Streamable HTTP and handles OAuth automatically.
15
15
 
16
- ## With Crush Protocol MCP
17
-
18
- Your AI agent can **create, run, and analyze backtests** in a single conversation:
16
+ Default MCP endpoint:
19
17
 
20
18
  ```txt
21
- Run a backtest on ETHUSDT on Hyperliquid using a 4h timeframe,
22
- entry when RSI < 30, exit when RSI > 70. Use crush protocol mcp.
19
+ https://crush-mcp-ats.dev.xexlab.com/mcp
23
20
  ```
24
21
 
25
- ```txt
26
- List my last 10 completed backtests and summarize the best performing strategy.
27
- ```
28
-
29
- The AI agent calls the Crush Protocol MCP tools directly — no tab-switching, no manual data entry.
30
-
31
- ---
32
-
33
- ## Authentication
34
-
35
- Crush Protocol MCP uses **OAuth 2.1 Authorization Code + PKCE**.
36
-
37
- For a standards-based MCP experience, configure only the MCP server URL. The client can complete browser authorization automatically and persist tokens locally.
38
-
39
- This means the published CLI and the URL-only MCP setup now follow the same auth model:
40
-
41
- - `npx -y @crush-protocol/mcp-client --url <mcp-url>` uses automatic OAuth on first use
42
- - host integrations such as Claude Code / Cursor / Windsurf can also provide only the MCP URL
43
- - both paths ultimately use the same OAuth access tokens against `/mcp`
44
-
45
- Non-interactive OAuth overrides are still available for scripts and already-published client workflows:
46
-
47
- 1. `crush-mcp-client login --url <mcp-url>` to pre-authorize locally
48
- 2. `--token <access-token>` for debugging
49
- 3. `CRUSH_OAUTH_ACCESS_TOKEN=<access-token>` for non-interactive automation
50
-
51
- ---
52
-
53
- ## CLI Usage
54
-
55
- The CLI is now an OAuth-aware MCP client, not a separate legacy auth path.
56
-
57
- ### Common commands
22
+ ## Standard MCP Host Integration
58
23
 
59
- ```sh
60
- # Show help
61
- npx -y @crush-protocol/mcp-client help
62
-
63
- # Pre-authorize locally (optional)
64
- npx -y @crush-protocol/mcp-client login --url https://mcp.crush-protocol.com/mcp
65
-
66
- # List tools
67
- npx -y @crush-protocol/mcp-client tools:list --url https://mcp.crush-protocol.com/mcp
68
-
69
- # Ping MCP server
70
- npx -y @crush-protocol/mcp-client ping --url https://mcp.crush-protocol.com/mcp
24
+ This is the recommended way to use the package.
71
25
 
72
- # Call a tool directly
73
- npx -y @crush-protocol/mcp-client tool:call \
74
- --url https://mcp.crush-protocol.com/mcp \
75
- --name get_backtest_config_schema
76
- ```
77
-
78
- ### Backtest commands
79
-
80
- ```sh
81
- # Read backtest schema
82
- npx -y @crush-protocol/mcp-client backtest:schema --url https://mcp.crush-protocol.com/mcp
26
+ Your MCP host launches `@crush-protocol/mcp-client` locally, and the package connects to the hosted Crush MCP endpoint.
83
27
 
84
- # List available tokens
85
- npx -y @crush-protocol/mcp-client backtest:tokens --url https://mcp.crush-protocol.com/mcp
28
+ ### Cursor
86
29
 
87
- # List backtests
88
- npx -y @crush-protocol/mcp-client backtest:list --url https://mcp.crush-protocol.com/mcp --limit 10
89
- ```
30
+ Official one-click install:
90
31
 
91
- ### How auth behaves
32
+ [![Install in Cursor](cursor://anysphere.cursor-deeplink/mcp/install?name=crush-protocol&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjcnVzaC1wcm90b2NvbC9tY3AtY2xpZW50IiwiLS11cmwiLCJodHRwczovL2NydXNoLW1jcC1hdHMuZGV2LnhleGxhYi5jb20vbWNwIl19)](cursor://anysphere.cursor-deeplink/mcp/install?name=crush-protocol&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjcnVzaC1wcm90b2NvbC9tY3AtY2xpZW50IiwiLS11cmwiLCJodHRwczovL2NydXNoLW1jcC1hdHMuZGV2LnhleGxhYi5jb20vbWNwIl19)
92
33
 
93
- ```sh
94
- # Recommended: URL only, browser OAuth happens automatically if needed
95
- npx -y @crush-protocol/mcp-client tools:list --url https://mcp.crush-protocol.com/mcp
34
+ Manual config:
96
35
 
97
- # Optional: inject a pre-issued OAuth access token
98
- npx -y @crush-protocol/mcp-client tools:list \
99
- --url https://mcp.crush-protocol.com/mcp \
100
- --token <oauth-access-token>
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "crush-protocol": {
40
+ "command": "npx",
41
+ "args": ["-y", "@crush-protocol/mcp-client", "--url", "https://crush-mcp-ats.dev.xexlab.com/mcp"]
42
+ }
43
+ }
44
+ }
101
45
  ```
102
46
 
103
- Behavior priority is:
104
-
105
- 1. `--token`
106
- 2. `CRUSH_OAUTH_ACCESS_TOKEN`
107
- 3. automatic OAuth with local token cache
108
-
109
- So the CLI remains compatible with script usage, but the default interactive path is the same URL-only OAuth flow used by MCP hosts.
110
-
111
- ---
112
-
113
- ## Installation
114
-
115
47
  ### Claude Code
116
48
 
117
49
  ```sh
118
50
  claude mcp add --scope user crush-protocol \
119
51
  -- npx -y @crush-protocol/mcp-client \
120
- --url https://mcp.crush-protocol.com/mcp
52
+ --url https://crush-mcp-ats.dev.xexlab.com/mcp
121
53
  ```
122
54
 
123
- On first use, Claude-compatible hosts should open the browser and complete OAuth automatically.
55
+ ### Codex
124
56
 
125
- ### Cursor
57
+ Add to `~/.codex/config.toml`:
126
58
 
127
- Add to `~/.cursor/mcp.json`:
59
+ ```toml
60
+ [mcp_servers.crush-protocol]
61
+ command = "npx"
62
+ args = ["-y", "@crush-protocol/mcp-client", "--url", "https://crush-mcp-ats.dev.xexlab.com/mcp"]
63
+ startup_timeout_ms = 20000
64
+ ```
65
+
66
+ ### Gemini CLI
67
+
68
+ Add to `~/.gemini/settings.json`:
128
69
 
129
70
  ```json
130
71
  {
131
- "mcpServers": {
132
- "crush-protocol": {
133
- "command": "npx",
134
- "args": ["-y", "@crush-protocol/mcp-client"],
135
- "env": {
136
- "CRUSH_MCP_SERVER_URL": "https://mcp.crush-protocol.com/mcp"
137
- }
138
- }
72
+ "mcpServers": {
73
+ "crush-protocol": {
74
+ "command": "npx",
75
+ "args": ["-y", "@crush-protocol/mcp-client", "--url", "https://crush-mcp-ats.dev.xexlab.com/mcp"]
139
76
  }
77
+ }
140
78
  }
141
79
  ```
142
80
 
143
- ### Windsurf
81
+ ### OpenCode
144
82
 
145
- Add to your MCP configuration:
83
+ Add to `~/.config/opencode/opencode.json`:
146
84
 
147
85
  ```json
148
86
  {
149
- "mcpServers": {
150
- "crush-protocol": {
151
- "command": "npx",
152
- "args": ["-y", "@crush-protocol/mcp-client"],
153
- "env": {
154
- "CRUSH_MCP_SERVER_URL": "https://mcp.crush-protocol.com/mcp"
155
- }
156
- }
87
+ "$schema": "https://opencode.ai/config.json",
88
+ "mcp": {
89
+ "crush-protocol": {
90
+ "type": "local",
91
+ "command": ["npx", "-y", "@crush-protocol/mcp-client", "--url", "https://crush-mcp-ats.dev.xexlab.com/mcp"],
92
+ "enabled": true
157
93
  }
94
+ }
158
95
  }
159
96
  ```
160
97
 
161
- > On first use, the client will prompt you to log in via your browser and persist OAuth tokens under `~/.crush-mcp/`.
98
+ ## Quick Setup Helper
162
99
 
163
- ### Host Integration Notes
100
+ If you do not want to edit config files manually, the CLI can write the MCP configuration for you.
164
101
 
165
- For Scheme C hosts, the recommended integration is:
166
-
167
- 1. Configure only the MCP URL
168
- 2. Let the client attempt `/mcp`
169
- 3. When the server returns `401` with `WWW-Authenticate`, start OAuth discovery
170
- 4. Complete Authorization Code + PKCE in the browser
171
- 5. Persist tokens locally and reconnect
102
+ ```sh
103
+ npx -y @crush-protocol/mcp-client setup --all
104
+ ```
172
105
 
173
- If a host cannot yet complete this flow automatically, run:
106
+ Single target setup:
174
107
 
175
108
  ```sh
176
- crush-mcp-client login --url https://mcp.crush-protocol.com/mcp
109
+ npx -y @crush-protocol/mcp-client setup --cursor
110
+ npx -y @crush-protocol/mcp-client setup --claude
111
+ npx -y @crush-protocol/mcp-client setup --codex
112
+ npx -y @crush-protocol/mcp-client setup --gemini
113
+ npx -y @crush-protocol/mcp-client setup --opencode
177
114
  ```
178
115
 
179
- This pre-authorizes the local client cache without requiring manual token copying.
116
+ ## What Users Get
117
+
118
+ With this package, an MCP client can call Crush tools such as:
180
119
 
181
- ### Unified Production Architecture
120
+ - `get_backtest_config_schema`
121
+ - `get_available_tokens`
122
+ - `create_backtest`
123
+ - `get_backtest_result`
124
+ - `list_backtests`
125
+ - `create_strategy`
126
+ - `list_strategies`
127
+ - `toggle_strategy`
128
+ - `get_strategy_logs`
182
129
 
183
- Crush MCP now uses one production auth path for remote access:
130
+ Detailed tool guidance is in [INSTRUCTIONS.md](./INSTRUCTIONS.md).
184
131
 
185
- 1. The host knows only the MCP URL
186
- 2. The MCP server returns OAuth discovery hints when auth is required
187
- 3. The client dynamically registers, runs Authorization Code + PKCE, and persists credentials locally
188
- 4. Authorization codes, access tokens, and refresh tokens are all managed server-side under the OAuth data model
132
+ ## Authentication
189
133
 
190
- There is no separate legacy MCP token service in the production path.
134
+ Crush MCP uses OAuth 2.1 Authorization Code + PKCE.
191
135
 
192
- ---
136
+ Default behavior:
193
137
 
194
- ## Available Tools
138
+ - you provide only the MCP server URL
139
+ - the client completes browser OAuth when needed
140
+ - tokens are stored locally for reuse
195
141
 
196
- | Category | Tools |
197
- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
198
- | **Signal Discovery** | `get_signal_metadata`, `get_signals_by_category` |
199
- | **Backtest** | `get_backtest_config_schema`, `get_available_tokens`, `validate_expression`, `create_backtest`, `list_backtests` |
200
- | **Market Data** | `list_tables`, `list_tokens`, `list_indicators`, `list_timeframes`, `get_data_range`, `check_query_size`, `fetch_ohlcv`, `fetch_indicator`, `get_connection_config` |
201
- | **Custom Indicators** | `save_custom_indicator`, `list_custom_indicators`, `get_custom_indicator`, `delete_custom_indicator` |
202
- | **Strategy Management** | `create_strategy`, `list_strategies`, `get_strategy`, `update_strategy`, `delete_strategy`, `toggle_strategy`, `get_strategy_logs` |
203
- | **Trading** | `place_order`, `get_positions`, `get_account_info`, `get_portfolio` |
204
- | **Market Intelligence** | `search_tokens`, `get_token_info`, `get_trending_tokens`, `get_alpha_feed`, `get_token_feed`, `fetch_news` |
142
+ Optional token overrides are also supported:
205
143
 
206
- > 📖 See [INSTRUCTIONS.md](./INSTRUCTIONS.md) for detailed tool descriptions, workflows, and AI system prompt template.
144
+ - `--token <access-token>`
145
+ - `CRUSH_OAUTH_ACCESS_TOKEN=<access-token>`
207
146
 
208
- ---
147
+ Priority order:
209
148
 
210
- ## Environment Variables
149
+ 1. `--token`
150
+ 2. `CRUSH_OAUTH_ACCESS_TOKEN`
151
+ 3. automatic OAuth with local token cache
211
152
 
212
- | Variable | Description |
213
- | --------------------------- | --------------------------------------------------------------- |
214
- | `CRUSH_MCP_SERVER_URL` | MCP server URL (default: `https://mcp.crush-protocol.com/mcp`) |
215
- | `CRUSH_OAUTH_ACCESS_TOKEN` | Optional override for a pre-issued OAuth access token |
153
+ Optional pre-login:
216
154
 
217
- ---
155
+ ```sh
156
+ npx -y @crush-protocol/mcp-client login --url https://crush-mcp-ats.dev.xexlab.com/mcp
157
+ ```
218
158
 
219
- ## SDK Usage (Advanced)
159
+ ## CLI / SDK Usage
220
160
 
221
- For programmatic access:
161
+ Use this when you want to use Crush from Node.js directly.
222
162
 
223
- ```typescript
224
- import { OAuthRemoteMcpClient, BacktestClient } from '@crush-protocol/mcp-client'
163
+ CLI examples:
164
+
165
+ ```sh
166
+ npx -y @crush-protocol/mcp-client help
167
+ npx -y @crush-protocol/mcp-client tools:list --url https://crush-mcp-ats.dev.xexlab.com/mcp
168
+ npx -y @crush-protocol/mcp-client ping --url https://crush-mcp-ats.dev.xexlab.com/mcp
169
+ npx -y @crush-protocol/mcp-client backtest:schema --url https://crush-mcp-ats.dev.xexlab.com/mcp
170
+ npx -y @crush-protocol/mcp-client backtest:list --url https://crush-mcp-ats.dev.xexlab.com/mcp --limit 10
171
+ ```
172
+
173
+ SDK example:
174
+
175
+ ```ts
176
+ import { OAuthRemoteMcpClient, BacktestClient } from "@crush-protocol/mcp-client";
225
177
 
226
178
  const mcp = new OAuthRemoteMcpClient({
227
- serverUrl: 'https://mcp.crush-protocol.com/mcp',
228
- })
229
- await mcp.connect()
230
-
231
- const backtest = new BacktestClient(mcp)
232
- const bt = await backtest.createBacktest({
233
- config: {
234
- /* ... */
235
- },
236
- })
237
- const result = await backtest.list({ limit: 10 })
238
-
239
- await mcp.close()
179
+ serverUrl: "https://crush-mcp-ats.dev.xexlab.com/mcp",
180
+ });
181
+
182
+ await mcp.connect();
183
+
184
+ const backtests = new BacktestClient(mcp);
185
+ const result = await backtests.list({ limit: 10 });
186
+
187
+ console.log(result);
188
+
189
+ await mcp.close();
240
190
  ```
241
191
 
242
- ---
192
+ ## Environment Variables
193
+
194
+ | Variable | Description |
195
+ | --- | --- |
196
+ | `CRUSH_MCP_SERVER_URL` | MCP server URL. Default: `https://crush-mcp-ats.dev.xexlab.com/mcp` |
197
+ | `CRUSH_OAUTH_ACCESS_TOKEN` | Optional OAuth access token override |
243
198
 
244
199
  ## License
245
200
 
@@ -5,10 +5,10 @@ import { OAuthRemoteMcpClient } from "../mcp/oauthRemoteClient.js";
5
5
  * E2E 测试:验证 MCP client 能成功连接服务端并调用工具。
6
6
  *
7
7
  * 前置条件(通过环境变量或 .env.e2e 配置):
8
- * CRUSH_MCP_SERVER_URL — 指向运行中的 MCP server(默认 http://localhost:3333/mcp)
8
+ * CRUSH_MCP_SERVER_URL — 指向运行中的 MCP server(默认 https://crush-mcp-ats.dev.xexlab.com/mcp)
9
9
  * CRUSH_OAUTH_ACCESS_TOKEN — 有效的 OAuth access token
10
10
  */
11
- const serverUrl = process.env.CRUSH_MCP_SERVER_URL ?? "http://localhost:3333/mcp";
11
+ const serverUrl = process.env.CRUSH_MCP_SERVER_URL ?? "https://crush-mcp-ats.dev.xexlab.com/mcp";
12
12
  const token = process.env.CRUSH_OAUTH_ACCESS_TOKEN ?? "";
13
13
  // 没有 token 时跳过所有 e2e 测试
14
14
  const describeE2E = token ? describe : describe.skip;
package/dist/cli.js CHANGED
@@ -4,8 +4,10 @@ import { BacktestClient } from "./backtest/backtestClient.js";
4
4
  import { ClickHouseDirectClient } from "./clickhouse/directClient.js";
5
5
  import { OAuthRemoteMcpClient } from "./mcp/oauthRemoteClient.js";
6
6
  import { RemoteMcpClient } from "./mcp/remoteClient.js";
7
+ import { installClientConfig } from "./setup/setupClients.js";
8
+ const DEFAULT_MCP_SERVER_URL = "https://crush-mcp-ats.dev.xexlab.com/mcp";
7
9
  const printUsage = () => {
8
- console.log(`\ncrush-mcp-client\n\nGeneral:\n login [--url URL]\n tools:list [--url URL] [--token TOKEN]\n tool:call --name TOOL_NAME [--args JSON] [--url URL] [--token TOKEN]\n ping [--url URL] [--token TOKEN]\n\nBacktest:\n backtest:schema [--url URL] [--token TOKEN]\n backtest:tokens [--platform PLATFORM] [--url URL] [--token TOKEN]\n backtest:validate --expression JSON [--data-source kline|factors] [--url URL] [--token TOKEN]\n backtest:create --config JSON [--backtest-id ID] [--url URL] [--token TOKEN]\n backtest:list [--status STATUS] [--limit N] [--offset N] [--url URL] [--token TOKEN]\n\nClickHouse:\n clickhouse:list-tables [--ch-host HOST --ch-port PORT --ch-user USER --ch-password PASS --ch-database DB]\n clickhouse:query --sql SQL [--ch-host HOST --ch-port PORT --ch-user USER --ch-password PASS --ch-database DB --ch-row-cap N]\n\nAuth:\n --token TOKEN overrides auto OAuth (for CI/scripts).\n Without --token, OAuth flow runs automatically (browser login on first use).\n\nEnv fallback:\n CRUSH_MCP_SERVER_URL, CRUSH_OAUTH_ACCESS_TOKEN\n CH_HOST, CH_PORT, CH_USER, CH_PASSWORD, CH_DATABASE, CH_ROW_CAP\n`);
10
+ console.log(`\ncrush-mcp-client\n\nGeneral:\n login [--url URL]\n setup [--cursor] [--claude] [--codex] [--gemini] [--opencode] [--all] [--scope user|project] [--url URL]\n tools:list [--url URL] [--token TOKEN]\n tool:call --name TOOL_NAME [--args JSON] [--url URL] [--token TOKEN]\n ping [--url URL] [--token TOKEN]\n\nBacktest:\n backtest:schema [--url URL] [--token TOKEN]\n backtest:tokens [--platform PLATFORM] [--url URL] [--token TOKEN]\n backtest:validate --expression JSON [--data-source kline|factors] [--url URL] [--token TOKEN]\n backtest:create --config JSON [--backtest-id ID] [--url URL] [--token TOKEN]\n backtest:list [--status STATUS] [--limit N] [--offset N] [--url URL] [--token TOKEN]\n\nClickHouse:\n clickhouse:list-tables [--ch-host HOST --ch-port PORT --ch-user USER --ch-password PASS --ch-database DB]\n clickhouse:query --sql SQL [--ch-host HOST --ch-port PORT --ch-user USER --ch-password PASS --ch-database DB --ch-row-cap N]\n\nAuth:\n --token TOKEN uses a provided OAuth access token.\n Without --token, OAuth runs automatically in the browser when needed.\n\nEnv:\n CRUSH_MCP_SERVER_URL, CRUSH_OAUTH_ACCESS_TOKEN\n CH_HOST, CH_PORT, CH_USER, CH_PASSWORD, CH_DATABASE, CH_ROW_CAP\n`);
9
11
  };
10
12
  const parseFlags = (args) => {
11
13
  const flags = {};
@@ -32,7 +34,14 @@ const requireString = (value, message) => {
32
34
  };
33
35
  const getServerUrl = (flags) => typeof flags.url === "string"
34
36
  ? flags.url
35
- : (process.env.CRUSH_MCP_SERVER_URL ?? "https://mcp.crush-protocol.com/mcp");
37
+ : (process.env.CRUSH_MCP_SERVER_URL ?? DEFAULT_MCP_SERVER_URL);
38
+ const getSetupTargets = (flags) => {
39
+ const explicitTargets = ["cursor", "claude", "codex", "gemini", "opencode"].filter((target) => flags[target] === true);
40
+ if (flags.all === true) {
41
+ return ["cursor", "claude", "codex", "gemini", "opencode"];
42
+ }
43
+ return explicitTargets;
44
+ };
36
45
  /**
37
46
  * 创建 MCP 客户端(统一认证入口)
38
47
  *
@@ -118,6 +127,23 @@ const run = async () => {
118
127
  }
119
128
  return;
120
129
  }
130
+ case "setup": {
131
+ const targets = getSetupTargets(flags);
132
+ if (targets.length === 0) {
133
+ throw new Error("Specify at least one setup target: --cursor, --claude, --codex, --gemini, --opencode, or --all");
134
+ }
135
+ const rawScope = typeof flags.scope === "string" ? flags.scope : "user";
136
+ if (rawScope !== "user" && rawScope !== "project") {
137
+ throw new Error("Invalid --scope. Expected 'user' or 'project'.");
138
+ }
139
+ const scope = rawScope;
140
+ const serverUrl = getServerUrl(flags);
141
+ for (const target of targets) {
142
+ const location = installClientConfig(target, serverUrl, scope);
143
+ console.log(`[setup] ${target}: configured (${location})`);
144
+ }
145
+ return;
146
+ }
121
147
  case "tools:list": {
122
148
  const client = await connectClient(flags);
123
149
  try {
@@ -0,0 +1,3 @@
1
+ export type SetupTarget = "cursor" | "claude" | "codex" | "gemini" | "opencode";
2
+ export type SetupScope = "user" | "project";
3
+ export declare const installClientConfig: (target: SetupTarget, serverUrl: string, scope: SetupScope) => string;
@@ -0,0 +1,119 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ const SERVER_NAME = "crush-protocol";
6
+ const PACKAGE_NAME = "@crush-protocol/mcp-client";
7
+ const ensureDir = (filePath) => {
8
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
9
+ };
10
+ const readJson = (filePath) => {
11
+ if (!fs.existsSync(filePath))
12
+ return {};
13
+ try {
14
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
15
+ }
16
+ catch (error) {
17
+ throw new Error(`Failed to parse JSON config at ${filePath}: ${error.message}`);
18
+ }
19
+ };
20
+ const writeJson = (filePath, value) => {
21
+ ensureDir(filePath);
22
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, "utf8");
23
+ };
24
+ const createNpxConfig = (serverUrl) => ({
25
+ command: "npx",
26
+ args: ["-y", PACKAGE_NAME, "--url", serverUrl],
27
+ });
28
+ const getCursorConfigPath = (scope) => scope === "project"
29
+ ? path.join(process.cwd(), ".cursor", "mcp.json")
30
+ : path.join(os.homedir(), ".cursor", "mcp.json");
31
+ const getGeminiConfigPath = () => path.join(os.homedir(), ".gemini", "settings.json");
32
+ const getOpenCodeConfigPath = (scope) => scope === "project"
33
+ ? path.join(process.cwd(), "opencode.json")
34
+ : path.join(os.homedir(), ".config", "opencode", "opencode.json");
35
+ const getCodexConfigPath = () => path.join(os.homedir(), ".codex", "config.toml");
36
+ const installCursor = (serverUrl, scope) => {
37
+ const filePath = getCursorConfigPath(scope);
38
+ const config = readJson(filePath);
39
+ const mcpServers = (config.mcpServers ?? {});
40
+ mcpServers[SERVER_NAME] = createNpxConfig(serverUrl);
41
+ config.mcpServers = mcpServers;
42
+ writeJson(filePath, config);
43
+ return filePath;
44
+ };
45
+ const installGemini = (serverUrl) => {
46
+ const filePath = getGeminiConfigPath();
47
+ const config = readJson(filePath);
48
+ const mcpServers = (config.mcpServers ?? {});
49
+ mcpServers[SERVER_NAME] = createNpxConfig(serverUrl);
50
+ config.mcpServers = mcpServers;
51
+ writeJson(filePath, config);
52
+ return filePath;
53
+ };
54
+ const installOpenCode = (serverUrl, scope) => {
55
+ const filePath = getOpenCodeConfigPath(scope);
56
+ const config = readJson(filePath);
57
+ const mcp = (config.mcp ?? {});
58
+ if (typeof config.$schema !== "string") {
59
+ config.$schema = "https://opencode.ai/config.json";
60
+ }
61
+ mcp[SERVER_NAME] = {
62
+ type: "local",
63
+ command: ["npx", "-y", PACKAGE_NAME, "--url", serverUrl],
64
+ enabled: true,
65
+ };
66
+ config.mcp = mcp;
67
+ writeJson(filePath, config);
68
+ return filePath;
69
+ };
70
+ const installCodex = (serverUrl) => {
71
+ const filePath = getCodexConfigPath();
72
+ const section = `[mcp_servers.${SERVER_NAME}]
73
+ command = "npx"
74
+ args = ["-y", "${PACKAGE_NAME}", "--url", "${serverUrl}"]
75
+ startup_timeout_ms = 20000
76
+ `;
77
+ ensureDir(filePath);
78
+ const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
79
+ if (!existing.includes(`[mcp_servers.${SERVER_NAME}]`)) {
80
+ const next = existing.trim().length > 0 ? `${existing.trimEnd()}\n\n${section}` : section;
81
+ fs.writeFileSync(filePath, next, "utf8");
82
+ }
83
+ return filePath;
84
+ };
85
+ const installClaude = (serverUrl, scope) => {
86
+ const configJson = JSON.stringify({
87
+ type: "stdio",
88
+ command: "npx",
89
+ args: ["-y", PACKAGE_NAME, "--url", serverUrl],
90
+ });
91
+ const result = spawnSync("claude", ["mcp", "add-json", "--scope", scope, SERVER_NAME, configJson], {
92
+ stdio: "pipe",
93
+ encoding: "utf8",
94
+ });
95
+ if (result.error) {
96
+ throw new Error(`Failed to run Claude CLI: ${result.error.message}`);
97
+ }
98
+ if (result.status !== 0) {
99
+ const output = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
100
+ throw new Error(output || "Claude CLI returned a non-zero exit code.");
101
+ }
102
+ return "claude-managed-config";
103
+ };
104
+ export const installClientConfig = (target, serverUrl, scope) => {
105
+ switch (target) {
106
+ case "cursor":
107
+ return installCursor(serverUrl, scope);
108
+ case "claude":
109
+ return installClaude(serverUrl, scope);
110
+ case "codex":
111
+ return installCodex(serverUrl);
112
+ case "gemini":
113
+ return installGemini(serverUrl);
114
+ case "opencode":
115
+ return installOpenCode(serverUrl, scope);
116
+ default:
117
+ throw new Error(`Unsupported setup target: ${target}`);
118
+ }
119
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crush-protocol/mcp-client",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Crush MCP npm client package (remote Streamable HTTP + optional ClickHouse direct)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,7 @@
26
26
  "@modelcontextprotocol/sdk": "^1.26.0",
27
27
  "dotenv": "^17.2.1",
28
28
  "zod": "^3.25.76",
29
- "@crush-protocol/mcp-contracts": "0.1.1"
29
+ "@crush-protocol/mcp-contracts": "0.1.2"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^24.3.0",