@crush-protocol/mcp-client 0.1.14 → 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/INSTRUCTIONS.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  You have access to **Crush Protocol MCP**, an AI-native quantitative trading platform. Use these tools to help users research markets, build strategies, run backtests, and manage live trading.
4
4
 
5
+ Authentication note: remote MCP requests use OAuth Bearer access tokens issued by the Crush OAuth server. In interactive clients, users should only need the MCP URL; OAuth Authorization Code + PKCE is handled by the client and browser.
6
+
5
7
  ## Tool Categories
6
8
 
7
9
  ### 🔍 Signal Discovery
@@ -19,7 +21,6 @@ You have access to **Crush Protocol MCP**, an AI-native quantitative trading pla
19
21
  | `get_available_tokens` | List tradable tokens, optionally filter by platform. |
20
22
  | `validate_expression` | Validate and compile an AST expression before using it in a backtest. |
21
23
  | `create_backtest` | Submit a backtest. Returns immediately with status `PENDING`. |
22
- | `get_backtest_result` | Poll a backtest by ID. Returns status, summary, portfolio history, and trades. |
23
24
  | `list_backtests` | List user's backtests with optional status filter (`PENDING`, `RUNNING`, `COMPLETED`, `FAILED`). |
24
25
 
25
26
  ### 📈 Market Data (ClickHouse)
@@ -90,7 +91,7 @@ You have access to **Crush Protocol MCP**, an AI-native quantitative trading pla
90
91
  4. get_available_tokens → Find the token to trade
91
92
  5. validate_expression → Validate the entry/exit expression AST
92
93
  6. create_backtest → Submit the backtest (returns backtestId)
93
- 7. get_backtest_result Poll until status is COMPLETED or FAILED
94
+ 7. list_backtests Refresh the user's backtests and inspect the created record until status is COMPLETED or FAILED
94
95
  └─ If COMPLETED: present summary (totalReturn, sharpeRatio, maxDrawdown, winRate, tradeCount)
95
96
  └─ If FAILED: show error, suggest fixes, iterate
96
97
  ```
@@ -119,7 +120,7 @@ You have access to **Crush Protocol MCP**, an AI-native quantitative trading pla
119
120
 
120
121
  ## Important Rules
121
122
 
122
- 1. **Backtest is async**: `create_backtest` returns immediately. You MUST poll `get_backtest_result` until status is `COMPLETED` or `FAILED`. Typical wait: 30s–3min.
123
+ 1. **Backtest is async**: `create_backtest` returns immediately. Refresh with `list_backtests` until status is `COMPLETED` or `FAILED`. Typical wait: 30s–3min.
123
124
  2. **Data row caps**: `fetch_ohlcv` and `fetch_indicator` have a 5000 row limit. For larger datasets, use `get_connection_config` and process locally.
124
125
  3. **Always validate first**: Call `validate_expression` before using an expression in `create_backtest` to catch errors early.
125
126
  4. **Signal discovery order**: Always call `get_signal_metadata` → `get_signals_by_category` before building expressions. Don't guess signal IDs.
package/README.md CHANGED
@@ -1,150 +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:
19
-
20
- ```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.
23
- ```
16
+ Default MCP endpoint:
24
17
 
25
18
  ```txt
26
- List my last 10 completed backtests and summarize the best performing strategy.
19
+ https://crush-mcp-ats.dev.xexlab.com/mcp
27
20
  ```
28
21
 
29
- The AI agent calls the Crush Protocol MCP tools directly — no tab-switching, no manual data entry.
30
-
31
- ---
32
-
33
- ## Authentication
22
+ ## Standard MCP Host Integration
34
23
 
35
- Crush Protocol uses **Device Code Flow** — a secure, browser-based login that works the same way GitHub CLI and Docker CLI authenticate.
24
+ This is the recommended way to use the package.
36
25
 
37
- ### How it works
26
+ Your MCP host launches `@crush-protocol/mcp-client` locally, and the package connects to the hosted Crush MCP endpoint.
38
27
 
39
- 1. Run the login command in your terminal
40
- 2. A browser window opens automatically
41
- 3. Complete wallet authentication in the browser (Privy + wallet signature)
42
- 4. The terminal receives the authorization and saves it locally
28
+ ### Cursor
43
29
 
44
- No manual token copy-pasting required.
30
+ Official one-click install:
45
31
 
46
- > 🔐 Your credentials are stored locally and never shared with third parties.
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)
47
33
 
48
- ---
34
+ Manual config:
49
35
 
50
- ## Installation
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
+ }
45
+ ```
51
46
 
52
47
  ### Claude Code
53
48
 
54
49
  ```sh
55
50
  claude mcp add --scope user crush-protocol \
56
51
  -- npx -y @crush-protocol/mcp-client \
57
- --url https://mcp.crush-protocol.com/mcp
52
+ --url https://crush-mcp-ats.dev.xexlab.com/mcp
58
53
  ```
59
54
 
60
- ### Cursor
55
+ ### Codex
56
+
57
+ Add to `~/.codex/config.toml`:
58
+
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
61
67
 
62
- Add to `~/.cursor/mcp.json`:
68
+ Add to `~/.gemini/settings.json`:
63
69
 
64
70
  ```json
65
71
  {
66
- "mcpServers": {
67
- "crush-protocol": {
68
- "command": "npx",
69
- "args": ["-y", "@crush-protocol/mcp-client"],
70
- "env": {
71
- "CRUSH_MCP_SERVER_URL": "https://mcp.crush-protocol.com/mcp"
72
- }
73
- }
72
+ "mcpServers": {
73
+ "crush-protocol": {
74
+ "command": "npx",
75
+ "args": ["-y", "@crush-protocol/mcp-client", "--url", "https://crush-mcp-ats.dev.xexlab.com/mcp"]
74
76
  }
77
+ }
75
78
  }
76
79
  ```
77
80
 
78
- ### Windsurf
81
+ ### OpenCode
79
82
 
80
- Add to your MCP configuration:
83
+ Add to `~/.config/opencode/opencode.json`:
81
84
 
82
85
  ```json
83
86
  {
84
- "mcpServers": {
85
- "crush-protocol": {
86
- "command": "npx",
87
- "args": ["-y", "@crush-protocol/mcp-client"],
88
- "env": {
89
- "CRUSH_MCP_SERVER_URL": "https://mcp.crush-protocol.com/mcp"
90
- }
91
- }
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
92
93
  }
94
+ }
93
95
  }
94
96
  ```
95
97
 
96
- > On first use, the client will prompt you to log in via your browser.
98
+ ## Quick Setup Helper
97
99
 
98
- ---
100
+ If you do not want to edit config files manually, the CLI can write the MCP configuration for you.
99
101
 
100
- ## Available Tools
102
+ ```sh
103
+ npx -y @crush-protocol/mcp-client setup --all
104
+ ```
101
105
 
102
- | Category | Tools |
103
- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
104
- | **Signal Discovery** | `get_signal_metadata`, `get_signals_by_category` |
105
- | **Backtest** | `get_backtest_config_schema`, `get_available_tokens`, `validate_expression`, `create_backtest`, `get_backtest_result`, `list_backtests` |
106
- | **Market Data** | `list_tables`, `list_tokens`, `list_indicators`, `list_timeframes`, `get_data_range`, `check_query_size`, `fetch_ohlcv`, `fetch_indicator`, `get_connection_config` |
107
- | **Custom Indicators** | `save_custom_indicator`, `list_custom_indicators`, `get_custom_indicator`, `delete_custom_indicator` |
108
- | **Strategy Management** | `create_strategy`, `list_strategies`, `get_strategy`, `update_strategy`, `delete_strategy`, `toggle_strategy`, `get_strategy_logs` |
109
- | **Trading** | `place_order`, `get_positions`, `get_account_info`, `get_portfolio` |
110
- | **Market Intelligence** | `search_tokens`, `get_token_info`, `get_trending_tokens`, `get_alpha_feed`, `get_token_feed`, `fetch_news` |
106
+ Single target setup:
111
107
 
112
- > 📖 See [INSTRUCTIONS.md](./INSTRUCTIONS.md) for detailed tool descriptions, workflows, and AI system prompt template.
108
+ ```sh
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
114
+ ```
113
115
 
114
- ---
116
+ ## What Users Get
115
117
 
116
- ## Environment Variables
118
+ With this package, an MCP client can call Crush tools such as:
117
119
 
118
- | Variable | Description |
119
- | ---------------------- | --------------------------------------------------------------- |
120
- | `CRUSH_MCP_SERVER_URL` | MCP server URL (default: `https://mcp.crush-protocol.com/mcp`) |
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`
121
129
 
122
- ---
130
+ Detailed tool guidance is in [INSTRUCTIONS.md](./INSTRUCTIONS.md).
123
131
 
124
- ## SDK Usage (Advanced)
132
+ ## Authentication
133
+
134
+ Crush MCP uses OAuth 2.1 Authorization Code + PKCE.
135
+
136
+ Default behavior:
137
+
138
+ - you provide only the MCP server URL
139
+ - the client completes browser OAuth when needed
140
+ - tokens are stored locally for reuse
141
+
142
+ Optional token overrides are also supported:
143
+
144
+ - `--token <access-token>`
145
+ - `CRUSH_OAUTH_ACCESS_TOKEN=<access-token>`
146
+
147
+ Priority order:
148
+
149
+ 1. `--token`
150
+ 2. `CRUSH_OAUTH_ACCESS_TOKEN`
151
+ 3. automatic OAuth with local token cache
152
+
153
+ Optional pre-login:
154
+
155
+ ```sh
156
+ npx -y @crush-protocol/mcp-client login --url https://crush-mcp-ats.dev.xexlab.com/mcp
157
+ ```
158
+
159
+ ## CLI / SDK Usage
160
+
161
+ Use this when you want to use Crush from Node.js directly.
125
162
 
126
- For programmatic access:
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
+ ```
127
172
 
128
- ```typescript
129
- import { RemoteMcpClient, BacktestClient } from '@crush-protocol/mcp-client'
173
+ SDK example:
130
174
 
131
- const mcp = new RemoteMcpClient({
132
- serverUrl: 'https://mcp.crush-protocol.com/mcp',
133
- })
134
- await mcp.connect()
175
+ ```ts
176
+ import { OAuthRemoteMcpClient, BacktestClient } from "@crush-protocol/mcp-client";
135
177
 
136
- const backtest = new BacktestClient(mcp)
137
- const bt = await backtest.createBacktest({
138
- config: {
139
- /* ... */
140
- },
141
- })
142
- const result = await backtest.getResult({ backtestId: bt.backtestId })
178
+ const mcp = new OAuthRemoteMcpClient({
179
+ serverUrl: "https://crush-mcp-ats.dev.xexlab.com/mcp",
180
+ });
143
181
 
144
- await mcp.close()
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();
145
190
  ```
146
191
 
147
- ---
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 |
148
198
 
149
199
  ## License
150
200
 
@@ -1,22 +1,22 @@
1
- import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
- import { RemoteMcpClient } from "../mcp/remoteClient.js";
1
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
2
  import { BacktestClient } from "../backtest/backtestClient.js";
3
+ import { OAuthRemoteMcpClient } from "../mcp/oauthRemoteClient.js";
4
4
  /**
5
5
  * E2E 测试:验证 MCP client 能成功连接服务端并调用工具。
6
6
  *
7
7
  * 前置条件(通过环境变量或 .env.e2e 配置):
8
- * CRUSH_MCP_SERVER_URL — 指向运行中的 MCP server(默认 http://localhost:3333/mcp)
9
- * CRUSH_MCP_TOKEN — 有效的 mcp_xxx token
8
+ * CRUSH_MCP_SERVER_URL — 指向运行中的 MCP server(默认 https://crush-mcp-ats.dev.xexlab.com/mcp)
9
+ * CRUSH_OAUTH_ACCESS_TOKEN — 有效的 OAuth access token
10
10
  */
11
- const serverUrl = process.env.CRUSH_MCP_SERVER_URL ?? "http://localhost:3333/mcp";
12
- const token = process.env.CRUSH_MCP_TOKEN ?? "";
11
+ const serverUrl = process.env.CRUSH_MCP_SERVER_URL ?? "https://crush-mcp-ats.dev.xexlab.com/mcp";
12
+ const token = process.env.CRUSH_OAUTH_ACCESS_TOKEN ?? "";
13
13
  // 没有 token 时跳过所有 e2e 测试
14
14
  const describeE2E = token ? describe : describe.skip;
15
15
  describeE2E("MCP Client E2E", () => {
16
16
  let mcp;
17
17
  let backtest;
18
18
  beforeAll(async () => {
19
- mcp = new RemoteMcpClient({ serverUrl, token });
19
+ mcp = new OAuthRemoteMcpClient({ serverUrl, token, oauth: { openBrowser: false } });
20
20
  await mcp.connect();
21
21
  backtest = new BacktestClient(mcp);
22
22
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,45 @@
1
+ import { mkdtemp, readFile } from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { InteractiveOAuthProvider } from "../mcp/oauthProvider.js";
6
+ const tempDirs = [];
7
+ const createProvider = async () => {
8
+ const storageDir = await mkdtemp(path.join(os.tmpdir(), "crush-mcp-oauth-"));
9
+ tempDirs.push(storageDir);
10
+ const provider = new InteractiveOAuthProvider({
11
+ serverUrl: "https://example.com/mcp",
12
+ storageDir,
13
+ openBrowser: false,
14
+ });
15
+ return { provider, storageDir };
16
+ };
17
+ describe("InteractiveOAuthProvider", () => {
18
+ afterEach(async () => {
19
+ await Promise.all(tempDirs
20
+ .splice(0)
21
+ .map((dir) => import("node:fs/promises").then((fs) => fs.rm(dir, { recursive: true, force: true }))));
22
+ });
23
+ it("persists client info, tokens, and code verifier", async () => {
24
+ const { provider, storageDir } = await createProvider();
25
+ await provider.saveClientInformation({ client_id: "client_123" });
26
+ await provider.saveTokens({ access_token: "atk_123", token_type: "Bearer", refresh_token: "rt_123" });
27
+ await provider.saveCodeVerifier("verifier-123");
28
+ expect(await provider.clientInformation()).toEqual({ client_id: "client_123" });
29
+ expect(await provider.tokens()).toEqual({ access_token: "atk_123", token_type: "Bearer", refresh_token: "rt_123" });
30
+ expect(await provider.codeVerifier()).toBe("verifier-123");
31
+ const files = await import("node:fs/promises").then((fs) => fs.readdir(storageDir));
32
+ expect(files.length).toBe(1);
33
+ const raw = await readFile(path.join(storageDir, files[0]), "utf8");
34
+ expect(raw).toContain("client_123");
35
+ expect(raw).toContain("atk_123");
36
+ });
37
+ it("invalidates token state without removing client registration", async () => {
38
+ const { provider } = await createProvider();
39
+ await provider.saveClientInformation({ client_id: "client_123" });
40
+ await provider.saveTokens({ access_token: "atk_123", token_type: "Bearer", refresh_token: "rt_123" });
41
+ await provider.invalidateCredentials?.("tokens");
42
+ expect(await provider.clientInformation()).toEqual({ client_id: "client_123" });
43
+ expect(await provider.tokens()).toBeUndefined();
44
+ });
45
+ });
@@ -1,5 +1,5 @@
1
1
  import { type BacktestStatus, type Platform } from "@crush-protocol/mcp-contracts";
2
- import type { RemoteMcpClient } from "../mcp/remoteClient.js";
2
+ import type { McpClientLike } from "../mcp/types.js";
3
3
  export type GetAvailableTokensInput = {
4
4
  platform?: Platform;
5
5
  };
@@ -11,9 +11,6 @@ export type CreateBacktestInput = {
11
11
  config: Record<string, unknown>;
12
12
  backtestId?: string;
13
13
  };
14
- export type GetBacktestResultInput = {
15
- backtestId: string;
16
- };
17
14
  export type ListBacktestsInput = {
18
15
  status?: BacktestStatus;
19
16
  limit?: number;
@@ -81,7 +78,7 @@ export type ListBacktestsResult = {
81
78
  */
82
79
  export declare class BacktestClient {
83
80
  private readonly mcp;
84
- constructor(mcp: RemoteMcpClient);
81
+ constructor(mcp: McpClientLike);
85
82
  /**
86
83
  * Return supported backtest configuration schema.
87
84
  */
@@ -103,10 +100,6 @@ export declare class BacktestClient {
103
100
  * Create a new backtest or update an existing one.
104
101
  */
105
102
  createBacktest(input: CreateBacktestInput): Promise<BacktestRecord>;
106
- /**
107
- * Get a backtest by ID with summary, portfolio history and trades.
108
- */
109
- getResult(input: GetBacktestResultInput): Promise<BacktestRecord>;
110
103
  /**
111
104
  * List backtests for the current user with optional status filter and pagination.
112
105
  */
@@ -1,4 +1,4 @@
1
- import { BacktestTools, } from "@crush-protocol/mcp-contracts";
1
+ import { BacktestTools } from "@crush-protocol/mcp-contracts";
2
2
  const extractContent = (result) => {
3
3
  // Compatibility path: some SDK result types expose `toolResult` directly.
4
4
  if ("toolResult" in result && result.toolResult !== undefined) {
@@ -53,13 +53,6 @@ export class BacktestClient {
53
53
  const result = await this.mcp.callTool(BacktestTools.CREATE_BACKTEST, input);
54
54
  return extractContent(result);
55
55
  }
56
- /**
57
- * Get a backtest by ID with summary, portfolio history and trades.
58
- */
59
- async getResult(input) {
60
- const result = await this.mcp.callTool(BacktestTools.GET_BACKTEST_RESULT, input);
61
- return extractContent(result);
62
- }
63
56
  /**
64
57
  * List backtests for the current user with optional status filter and pagination.
65
58
  */