@evantahler/mcpx 0.16.1 → 0.16.3
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/.claude/skills/mcpx.md +8 -3
- package/.cursor/rules/mcpx.mdc +8 -3
- package/README.md +17 -7
- package/package.json +1 -1
- package/src/commands/exec.ts +92 -8
package/.claude/skills/mcpx.md
CHANGED
|
@@ -25,7 +25,8 @@ This shows parameters, types, required fields, and the full JSON Schema.
|
|
|
25
25
|
## 3. Execute the tool
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
mcpx exec <
|
|
28
|
+
mcpx exec <tool> '<json args>' # server auto-resolved if unambiguous
|
|
29
|
+
mcpx exec <server> <tool> '<json args>' # explicit server (required if tool name exists on multiple servers)
|
|
29
30
|
mcpx exec <server> <tool> -f params.json
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -52,7 +53,10 @@ mcpx search "send a message"
|
|
|
52
53
|
# See what parameters Slack_SendMessage needs
|
|
53
54
|
mcpx info arcade Slack_SendMessage
|
|
54
55
|
|
|
55
|
-
# Send a message
|
|
56
|
+
# Send a message (server optional if tool name is unique)
|
|
57
|
+
mcpx exec Slack_SendMessage '{"channel":"#general","message":"hello"}'
|
|
58
|
+
|
|
59
|
+
# Or explicitly specify the server
|
|
56
60
|
mcpx exec arcade Slack_SendMessage '{"channel":"#general","message":"hello"}'
|
|
57
61
|
|
|
58
62
|
# Chain commands — search repos and read the first result
|
|
@@ -118,7 +122,8 @@ mcpx deauth <server> # remove stored auth
|
|
|
118
122
|
| `mcpx info <server>` | Server overview (version, capabilities, tools) |
|
|
119
123
|
| `mcpx info <server> <tool>` | Show tool schema |
|
|
120
124
|
| `mcpx exec <server>` | List tools for a server |
|
|
121
|
-
| `mcpx exec <
|
|
125
|
+
| `mcpx exec <tool> '<json>'` | Execute tool (server auto-resolved) |
|
|
126
|
+
| `mcpx exec <server> <tool> '<json>'` | Execute tool (explicit server) |
|
|
122
127
|
| `mcpx exec <server> <tool> -f file` | Execute with args from file |
|
|
123
128
|
| `mcpx search "<query>"` | Search tools (keyword + semantic) |
|
|
124
129
|
| `mcpx search -k "<pattern>"` | Keyword/glob search only |
|
package/.cursor/rules/mcpx.mdc
CHANGED
|
@@ -25,7 +25,8 @@ This shows parameters, types, required fields, and the full JSON Schema.
|
|
|
25
25
|
## 3. Execute the tool
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
mcpx exec <
|
|
28
|
+
mcpx exec <tool> '<json args>' # server auto-resolved if unambiguous
|
|
29
|
+
mcpx exec <server> <tool> '<json args>' # explicit server (required if tool name exists on multiple servers)
|
|
29
30
|
mcpx exec <server> <tool> -f params.json
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -52,7 +53,10 @@ mcpx search "send a message"
|
|
|
52
53
|
# See what parameters Slack_SendMessage needs
|
|
53
54
|
mcpx info arcade Slack_SendMessage
|
|
54
55
|
|
|
55
|
-
# Send a message
|
|
56
|
+
# Send a message (server optional if tool name is unique)
|
|
57
|
+
mcpx exec Slack_SendMessage '{"channel":"#general","message":"hello"}'
|
|
58
|
+
|
|
59
|
+
# Or explicitly specify the server
|
|
56
60
|
mcpx exec arcade Slack_SendMessage '{"channel":"#general","message":"hello"}'
|
|
57
61
|
|
|
58
62
|
# Chain commands — search repos and read the first result
|
|
@@ -118,7 +122,8 @@ mcpx deauth <server> # remove stored auth
|
|
|
118
122
|
| `mcpx info <server>` | Server overview (version, capabilities, tools) |
|
|
119
123
|
| `mcpx info <server> <tool>` | Show tool schema |
|
|
120
124
|
| `mcpx exec <server>` | List tools for a server |
|
|
121
|
-
| `mcpx exec <
|
|
125
|
+
| `mcpx exec <tool> '<json>'` | Execute tool (server auto-resolved) |
|
|
126
|
+
| `mcpx exec <server> <tool> '<json>'` | Execute tool (explicit server) |
|
|
122
127
|
| `mcpx exec <server> <tool> -f file` | Execute with args from file |
|
|
123
128
|
| `mcpx search "<query>"` | Search tools (keyword + semantic) |
|
|
124
129
|
| `mcpx search -k "<pattern>"` | Keyword/glob search only |
|
package/README.md
CHANGED
|
@@ -12,14 +12,19 @@ Two audiences:
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
# Via bun
|
|
15
|
+
# Via bun (all platforms)
|
|
16
16
|
bun install -g @evantahler/mcpx
|
|
17
17
|
|
|
18
|
-
# Via curl
|
|
18
|
+
# Via curl (macOS/Linux)
|
|
19
19
|
curl -fsSL https://raw.githubusercontent.com/evantahler/mcpx/main/install.sh | bash
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
```powershell
|
|
23
|
+
# Via PowerShell (Windows)
|
|
24
|
+
irm https://raw.githubusercontent.com/evantahler/mcpx/main/install.ps1 | iex
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The curl/PowerShell installers download a pre-built binary — no runtime needed. The bun install method requires [Bun](https://bun.sh). Binaries for all platforms are also available on the [GitHub Releases](https://github.com/evantahler/mcpx/releases) page.
|
|
23
28
|
|
|
24
29
|
## Quick Start
|
|
25
30
|
|
|
@@ -42,6 +47,9 @@ mcpx info github search_repositories
|
|
|
42
47
|
# Execute a tool
|
|
43
48
|
mcpx exec github search_repositories '{"query": "mcp server"}'
|
|
44
49
|
|
|
50
|
+
# Execute a tool without specifying the server (auto-resolved)
|
|
51
|
+
mcpx exec search_repositories '{"query": "mcp server"}'
|
|
52
|
+
|
|
45
53
|
# Search tools — combines keyword and semantic matching
|
|
46
54
|
mcpx search "post a ticket to linear"
|
|
47
55
|
|
|
@@ -70,6 +78,7 @@ mcpx search -n 5 "manage pull requests"
|
|
|
70
78
|
| `mcpx index` | Build/rebuild the search index |
|
|
71
79
|
| `mcpx index -i` | Show index status |
|
|
72
80
|
| `mcpx exec <server> <tool> [json]` | Validate inputs locally, then execute tool |
|
|
81
|
+
| `mcpx exec <tool> [json]` | Execute tool (server auto-resolved if unambiguous) |
|
|
73
82
|
| `mcpx exec <server> <tool> -f file` | Read tool args from a JSON file |
|
|
74
83
|
| `mcpx exec <server>` | List available tools for a server |
|
|
75
84
|
| `mcpx auth <server>` | Authenticate with an HTTP MCP server (OAuth) |
|
|
@@ -89,8 +98,8 @@ mcpx search -n 5 "manage pull requests"
|
|
|
89
98
|
| `mcpx prompt` | List all prompts across all servers |
|
|
90
99
|
| `mcpx prompt <server>` | List prompts for a server |
|
|
91
100
|
| `mcpx prompt <server> <name> [json]` | Get a specific prompt |
|
|
92
|
-
| `mcpx exec
|
|
93
|
-
| `mcpx exec
|
|
101
|
+
| `mcpx exec [server] <tool> --no-wait` | Execute as async task, return task handle immediately |
|
|
102
|
+
| `mcpx exec [server] <tool> --ttl <ms>` | Set task TTL in milliseconds (default: 60000) |
|
|
94
103
|
| `mcpx task list <server>` | List tasks on a server |
|
|
95
104
|
| `mcpx task get <server> <taskId>` | Get task status |
|
|
96
105
|
| `mcpx task result <server> <taskId>` | Retrieve completed task result |
|
|
@@ -579,7 +588,7 @@ Then in any Claude Code session, the agent can use `/mcpx` or the skill triggers
|
|
|
579
588
|
|
|
580
589
|
1. **Search first** — `mcpx search "<intent>"` to find relevant tools
|
|
581
590
|
2. **Inspect** — `mcpx info <server> <tool>` to get the schema before calling
|
|
582
|
-
3. **Execute** — `mcpx exec <server> <tool> '<json>'`
|
|
591
|
+
3. **Execute** — `mcpx exec <tool> '<json>'` to execute (or `mcpx exec <server> <tool> '<json>'` if the tool name is ambiguous)
|
|
583
592
|
|
|
584
593
|
This keeps tool schemas out of the system prompt entirely. The agent discovers what it needs on-demand, saving tokens and context window space.
|
|
585
594
|
|
|
@@ -614,7 +623,8 @@ To discover tools:
|
|
|
614
623
|
mcpx info <server> <tool> # tool schema
|
|
615
624
|
|
|
616
625
|
To execute tools:
|
|
617
|
-
mcpx exec <
|
|
626
|
+
mcpx exec <tool> '<json args>' # server auto-resolved
|
|
627
|
+
mcpx exec <server> <tool> '<json args>' # explicit server
|
|
618
628
|
mcpx exec <server> <tool> -f params.json
|
|
619
629
|
|
|
620
630
|
Always search before executing — don't assume tool names.
|
package/package.json
CHANGED
package/src/commands/exec.ts
CHANGED
|
@@ -11,27 +11,108 @@ import { logger } from "../output/logger.ts";
|
|
|
11
11
|
import { validateToolInput } from "../validation/schema.ts";
|
|
12
12
|
import { parseJsonArgs, readStdin } from "../lib/input.ts";
|
|
13
13
|
import { DEFAULTS } from "../constants.ts";
|
|
14
|
+
import type { ServerManager } from "../client/manager.ts";
|
|
15
|
+
|
|
16
|
+
type ResolvedArgs =
|
|
17
|
+
| { mode: "list-tools"; server: string }
|
|
18
|
+
| { mode: "call-tool"; server: string; tool: string; argsStr: string | undefined };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the positional args into either list-tools or call-tool mode.
|
|
22
|
+
* Supports both `exec <server> <tool> [args]` and `exec <tool> [args]`.
|
|
23
|
+
*/
|
|
24
|
+
async function resolveExecArgs(
|
|
25
|
+
manager: ServerManager,
|
|
26
|
+
first: string,
|
|
27
|
+
second: string | undefined,
|
|
28
|
+
third: string | undefined,
|
|
29
|
+
): Promise<ResolvedArgs> {
|
|
30
|
+
const serverNames = manager.getServerNames();
|
|
31
|
+
const isServer = serverNames.includes(first);
|
|
32
|
+
|
|
33
|
+
if (isServer) {
|
|
34
|
+
// Traditional form: exec <server> [tool] [args]
|
|
35
|
+
if (!second) {
|
|
36
|
+
return { mode: "list-tools", server: first };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Validate the tool exists on the specified server
|
|
40
|
+
const serverTools = await manager.listTools(first);
|
|
41
|
+
const toolExists = serverTools.some((t) => t.name === second);
|
|
42
|
+
|
|
43
|
+
if (!toolExists) {
|
|
44
|
+
const { tools } = await manager.getAllTools();
|
|
45
|
+
const matches = tools.filter((t) => t.tool.name === second);
|
|
46
|
+
|
|
47
|
+
if (matches.length === 1) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`Tool "${second}" not found on server "${first}". Did you mean:\n mcpx exec ${matches[0]!.server} ${second}`,
|
|
50
|
+
);
|
|
51
|
+
} else if (matches.length > 1) {
|
|
52
|
+
const servers = matches.map((m) => m.server).join(", ");
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Tool "${second}" not found on server "${first}". Found on: ${servers}\nUsage: mcpx exec <server> ${second} [args]`,
|
|
55
|
+
);
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Tool "${second}" not found on server "${first}". Run "mcpx search ${second}" to find similar tools.`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { mode: "call-tool", server: first, tool: second, argsStr: third };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Not a server name — treat first as a tool name
|
|
67
|
+
const toolName = first;
|
|
68
|
+
const { tools } = await manager.getAllTools();
|
|
69
|
+
const matches = tools.filter((t) => t.tool.name === toolName);
|
|
70
|
+
|
|
71
|
+
if (matches.length === 0) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Unknown server or tool "${first}". Run "mcpx search ${first}" to find similar tools.`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (matches.length > 1) {
|
|
78
|
+
const servers = matches.map((m) => m.server).join(", ");
|
|
79
|
+
throw new Error(
|
|
80
|
+
`Ambiguous tool "${toolName}" — found on multiple servers: ${servers}\nSpecify the server: mcpx exec <server> ${toolName} [args]`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return { mode: "call-tool", server: matches[0]!.server, tool: toolName, argsStr: second };
|
|
85
|
+
}
|
|
14
86
|
|
|
15
87
|
export function registerExecCommand(program: Command) {
|
|
16
88
|
program
|
|
17
|
-
.command("exec <
|
|
18
|
-
.description("execute a tool (
|
|
89
|
+
.command("exec <first> [second] [third]")
|
|
90
|
+
.description("execute a tool (server is optional if tool name is unambiguous)")
|
|
19
91
|
.option("-f, --file <path>", "read JSON args from a file")
|
|
20
92
|
.option("--no-wait", "return task handle immediately without waiting for completion")
|
|
21
93
|
.option("--ttl <ms>", "task TTL in milliseconds", String(DEFAULTS.TASK_TTL_MS))
|
|
22
94
|
.action(
|
|
23
95
|
async (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
96
|
+
first: string,
|
|
97
|
+
second: string | undefined,
|
|
98
|
+
third: string | undefined,
|
|
27
99
|
options: { file?: string; wait: boolean; ttl: string },
|
|
28
100
|
) => {
|
|
29
101
|
const { manager, formatOptions } = await getContext(program);
|
|
30
102
|
|
|
31
|
-
|
|
103
|
+
let resolved: ResolvedArgs;
|
|
104
|
+
try {
|
|
105
|
+
resolved = await resolveExecArgs(manager, first, second, third);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.error(formatError(String(err), formatOptions));
|
|
108
|
+
await manager.close();
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (resolved.mode === "list-tools") {
|
|
32
113
|
try {
|
|
33
|
-
const tools = await manager.listTools(server);
|
|
34
|
-
console.log(formatServerTools(server, tools, formatOptions));
|
|
114
|
+
const tools = await manager.listTools(resolved.server);
|
|
115
|
+
console.log(formatServerTools(resolved.server, tools, formatOptions));
|
|
35
116
|
} catch (err) {
|
|
36
117
|
console.error(formatError(String(err), formatOptions));
|
|
37
118
|
process.exit(1);
|
|
@@ -40,6 +121,9 @@ export function registerExecCommand(program: Command) {
|
|
|
40
121
|
}
|
|
41
122
|
return;
|
|
42
123
|
}
|
|
124
|
+
|
|
125
|
+
const { server, tool, argsStr } = resolved;
|
|
126
|
+
|
|
43
127
|
try {
|
|
44
128
|
// Error if both --file and positional arg provided
|
|
45
129
|
if (options.file && argsStr) {
|