@evantahler/mcpx 0.15.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/.claude/settings.local.json +18 -0
- package/.claude/skills/mcpx.md +165 -0
- package/.claude/worktrees/elastic-jennings/.claude/settings.local.json +18 -0
- package/.claude/worktrees/elastic-jennings/.claude/skills/mcpcli.md +93 -0
- package/.claude/worktrees/elastic-jennings/.github/workflows/auto-release.yml +117 -0
- package/.claude/worktrees/elastic-jennings/.github/workflows/ci.yml +18 -0
- package/.claude/worktrees/elastic-jennings/.prettierignore +4 -0
- package/.claude/worktrees/elastic-jennings/.prettierrc +7 -0
- package/.claude/worktrees/elastic-jennings/CLAUDE.md +19 -0
- package/.claude/worktrees/elastic-jennings/LICENSE +21 -0
- package/.claude/worktrees/elastic-jennings/README.md +487 -0
- package/.claude/worktrees/elastic-jennings/bun.lock +381 -0
- package/.claude/worktrees/elastic-jennings/install.sh +55 -0
- package/.claude/worktrees/elastic-jennings/package.json +56 -0
- package/.claude/worktrees/elastic-jennings/src/cli.ts +39 -0
- package/.claude/worktrees/elastic-jennings/src/client/http.ts +100 -0
- package/.claude/worktrees/elastic-jennings/src/client/manager.ts +266 -0
- package/.claude/worktrees/elastic-jennings/src/client/oauth.ts +299 -0
- package/.claude/worktrees/elastic-jennings/src/client/stdio.ts +12 -0
- package/.claude/worktrees/elastic-jennings/src/commands/add.ts +155 -0
- package/.claude/worktrees/elastic-jennings/src/commands/auth.ts +114 -0
- package/.claude/worktrees/elastic-jennings/src/commands/exec.ts +91 -0
- package/.claude/worktrees/elastic-jennings/src/commands/index.ts +62 -0
- package/.claude/worktrees/elastic-jennings/src/commands/info.ts +38 -0
- package/.claude/worktrees/elastic-jennings/src/commands/list.ts +30 -0
- package/.claude/worktrees/elastic-jennings/src/commands/remove.ts +67 -0
- package/.claude/worktrees/elastic-jennings/src/commands/search.ts +45 -0
- package/.claude/worktrees/elastic-jennings/src/commands/skill.ts +70 -0
- package/.claude/worktrees/elastic-jennings/src/config/env.ts +41 -0
- package/.claude/worktrees/elastic-jennings/src/config/loader.ts +156 -0
- package/.claude/worktrees/elastic-jennings/src/config/schemas.ts +137 -0
- package/.claude/worktrees/elastic-jennings/src/context.ts +53 -0
- package/.claude/worktrees/elastic-jennings/src/output/formatter.ts +316 -0
- package/.claude/worktrees/elastic-jennings/src/output/logger.ts +114 -0
- package/.claude/worktrees/elastic-jennings/src/search/index.ts +69 -0
- package/.claude/worktrees/elastic-jennings/src/search/indexer.ts +92 -0
- package/.claude/worktrees/elastic-jennings/src/search/keyword.ts +86 -0
- package/.claude/worktrees/elastic-jennings/src/search/semantic.ts +75 -0
- package/.claude/worktrees/elastic-jennings/src/search/staleness.ts +8 -0
- package/.claude/worktrees/elastic-jennings/src/validation/schema.ts +77 -0
- package/.claude/worktrees/elastic-jennings/test/cli.test.ts +51 -0
- package/.claude/worktrees/elastic-jennings/test/client/manager.test.ts +249 -0
- package/.claude/worktrees/elastic-jennings/test/client/oauth.test.ts +328 -0
- package/.claude/worktrees/elastic-jennings/test/commands/add-remove.test.ts +253 -0
- package/.claude/worktrees/elastic-jennings/test/commands/exec.test.ts +105 -0
- package/.claude/worktrees/elastic-jennings/test/commands/info.test.ts +48 -0
- package/.claude/worktrees/elastic-jennings/test/commands/list.test.ts +39 -0
- package/.claude/worktrees/elastic-jennings/test/commands/skill.test.ts +98 -0
- package/.claude/worktrees/elastic-jennings/test/config/env.test.ts +61 -0
- package/.claude/worktrees/elastic-jennings/test/config/loader.test.ts +139 -0
- package/.claude/worktrees/elastic-jennings/test/fixtures/.keep +0 -0
- package/.claude/worktrees/elastic-jennings/test/fixtures/auth.json +10 -0
- package/.claude/worktrees/elastic-jennings/test/fixtures/mock-config/.keep +0 -0
- package/.claude/worktrees/elastic-jennings/test/fixtures/mock-config/servers.json +8 -0
- package/.claude/worktrees/elastic-jennings/test/fixtures/mock-server.ts +113 -0
- package/.claude/worktrees/elastic-jennings/test/fixtures/search.json +15 -0
- package/.claude/worktrees/elastic-jennings/test/fixtures/servers.json +18 -0
- package/.claude/worktrees/elastic-jennings/test/integration/stdio-server.test.ts +149 -0
- package/.claude/worktrees/elastic-jennings/test/output/formatter.test.ts +54 -0
- package/.claude/worktrees/elastic-jennings/test/output/logger.test.ts +89 -0
- package/.claude/worktrees/elastic-jennings/test/search/indexer.test.ts +32 -0
- package/.claude/worktrees/elastic-jennings/test/search/keyword.test.ts +80 -0
- package/.claude/worktrees/elastic-jennings/test/search/semantic.test.ts +32 -0
- package/.claude/worktrees/elastic-jennings/test/validation/schema.test.ts +113 -0
- package/.claude/worktrees/elastic-jennings/tsconfig.json +29 -0
- package/.cursor/rules/mcpx.mdc +165 -0
- package/LICENSE +21 -0
- package/README.md +627 -0
- package/package.json +58 -0
- package/src/cli.ts +72 -0
- package/src/client/browser.ts +24 -0
- package/src/client/debug-fetch.ts +81 -0
- package/src/client/elicitation.ts +368 -0
- package/src/client/http.ts +25 -0
- package/src/client/manager.ts +566 -0
- package/src/client/oauth.ts +314 -0
- package/src/client/sse.ts +17 -0
- package/src/client/stdio.ts +12 -0
- package/src/client/trace.ts +184 -0
- package/src/commands/add.ts +179 -0
- package/src/commands/auth.ts +114 -0
- package/src/commands/exec.ts +156 -0
- package/src/commands/index.ts +62 -0
- package/src/commands/info.ts +63 -0
- package/src/commands/list.ts +64 -0
- package/src/commands/ping.ts +69 -0
- package/src/commands/prompt.ts +60 -0
- package/src/commands/remove.ts +67 -0
- package/src/commands/resource.ts +46 -0
- package/src/commands/search.ts +49 -0
- package/src/commands/servers.ts +66 -0
- package/src/commands/skill.ts +112 -0
- package/src/commands/task.ts +82 -0
- package/src/config/env.ts +41 -0
- package/src/config/loader.ts +156 -0
- package/src/config/schemas.ts +152 -0
- package/src/context.ts +62 -0
- package/src/lib/input.ts +36 -0
- package/src/output/formatter.ts +884 -0
- package/src/output/logger.ts +173 -0
- package/src/search/index.ts +69 -0
- package/src/search/indexer.ts +92 -0
- package/src/search/keyword.ts +86 -0
- package/src/search/semantic.ts +75 -0
- package/src/search/staleness.ts +8 -0
- package/src/validation/schema.ts +103 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { keywordSearch } from "../../src/search/keyword.ts";
|
|
3
|
+
import type { IndexedTool } from "../../src/config/schemas.ts";
|
|
4
|
+
|
|
5
|
+
const tools: IndexedTool[] = [
|
|
6
|
+
{
|
|
7
|
+
server: "arcade",
|
|
8
|
+
tool: "Gmail_SendEmail",
|
|
9
|
+
description: "Send an email message via Gmail",
|
|
10
|
+
scenarios: ["send an email", "compose a message"],
|
|
11
|
+
keywords: ["gmail", "send", "email"],
|
|
12
|
+
embedding: [],
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
server: "arcade",
|
|
16
|
+
tool: "Slack_SendMessage",
|
|
17
|
+
description: "Send a message to a Slack channel",
|
|
18
|
+
scenarios: ["send a slack message", "post to channel"],
|
|
19
|
+
keywords: ["slack", "send", "message"],
|
|
20
|
+
embedding: [],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
server: "arcade",
|
|
24
|
+
tool: "Github_CreatePullRequest",
|
|
25
|
+
description: "Create a pull request on GitHub",
|
|
26
|
+
scenarios: ["create a PR", "open a pull request"],
|
|
27
|
+
keywords: ["github", "create", "pull", "request"],
|
|
28
|
+
embedding: [],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
describe("keywordSearch", () => {
|
|
33
|
+
test("matches tool name substring", () => {
|
|
34
|
+
const results = keywordSearch("gmail", tools);
|
|
35
|
+
expect(results.length).toBeGreaterThan(0);
|
|
36
|
+
expect(results[0]!.tool).toBe("Gmail_SendEmail");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("matches keywords", () => {
|
|
40
|
+
const results = keywordSearch("send", tools);
|
|
41
|
+
expect(results.length).toBe(2);
|
|
42
|
+
expect(results.map((r) => r.tool)).toContain("Gmail_SendEmail");
|
|
43
|
+
expect(results.map((r) => r.tool)).toContain("Slack_SendMessage");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("matches description text", () => {
|
|
47
|
+
const results = keywordSearch("pull request", tools);
|
|
48
|
+
expect(results.length).toBeGreaterThan(0);
|
|
49
|
+
expect(results[0]!.tool).toBe("Github_CreatePullRequest");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("glob matching on tool name", () => {
|
|
53
|
+
const results = keywordSearch("Gmail_*", tools);
|
|
54
|
+
expect(results.length).toBe(1);
|
|
55
|
+
expect(results[0]!.tool).toBe("Gmail_SendEmail");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("case insensitive", () => {
|
|
59
|
+
const results = keywordSearch("SLACK", tools);
|
|
60
|
+
expect(results.length).toBeGreaterThan(0);
|
|
61
|
+
expect(results[0]!.tool).toBe("Slack_SendMessage");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("returns empty for no match", () => {
|
|
65
|
+
const results = keywordSearch("zzzznotfound", tools);
|
|
66
|
+
expect(results.length).toBe(0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("results sorted by score descending", () => {
|
|
70
|
+
const results = keywordSearch("send", tools);
|
|
71
|
+
for (let i = 1; i < results.length; i++) {
|
|
72
|
+
expect(results[i]!.score).toBeLessThanOrEqual(results[i - 1]!.score);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("multi-word query matches better", () => {
|
|
77
|
+
const results = keywordSearch("send email", tools);
|
|
78
|
+
expect(results[0]!.tool).toBe("Gmail_SendEmail");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { cosineSimilarity } from "../../src/search/semantic.ts";
|
|
3
|
+
|
|
4
|
+
describe("cosineSimilarity", () => {
|
|
5
|
+
test("identical vectors return 1", () => {
|
|
6
|
+
const v = [1, 2, 3];
|
|
7
|
+
expect(cosineSimilarity(v, v)).toBeCloseTo(1.0, 5);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("orthogonal vectors return 0", () => {
|
|
11
|
+
expect(cosineSimilarity([1, 0], [0, 1])).toBeCloseTo(0.0, 5);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("opposite vectors return -1", () => {
|
|
15
|
+
expect(cosineSimilarity([1, 0], [-1, 0])).toBeCloseTo(-1.0, 5);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("empty vectors return 0", () => {
|
|
19
|
+
expect(cosineSimilarity([], [])).toBe(0);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("mismatched lengths return 0", () => {
|
|
23
|
+
expect(cosineSimilarity([1, 2], [1, 2, 3])).toBe(0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("normalized vectors", () => {
|
|
27
|
+
const a = [0.6, 0.8];
|
|
28
|
+
const b = [0.8, 0.6];
|
|
29
|
+
const expected = 0.6 * 0.8 + 0.8 * 0.6; // 0.96
|
|
30
|
+
expect(cosineSimilarity(a, b)).toBeCloseTo(expected, 5);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { validateToolInput } from "../../src/validation/schema.ts";
|
|
3
|
+
import type { Tool } from "../../src/config/schemas.ts";
|
|
4
|
+
|
|
5
|
+
function makeTool(name: string, inputSchema: Record<string, unknown>): Tool {
|
|
6
|
+
return {
|
|
7
|
+
name,
|
|
8
|
+
description: "A test tool",
|
|
9
|
+
inputSchema: inputSchema as Tool["inputSchema"],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("validateToolInput", () => {
|
|
14
|
+
test("passes valid input", () => {
|
|
15
|
+
const tool = makeTool("valid_input", {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: { name: { type: "string" } },
|
|
18
|
+
required: ["name"],
|
|
19
|
+
});
|
|
20
|
+
const result = validateToolInput("s", tool, { name: "hello" });
|
|
21
|
+
expect(result.valid).toBe(true);
|
|
22
|
+
expect(result.errors).toHaveLength(0);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("catches missing required field", () => {
|
|
26
|
+
const tool = makeTool("missing_required", {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: { name: { type: "string" }, age: { type: "number" } },
|
|
29
|
+
required: ["name", "age"],
|
|
30
|
+
});
|
|
31
|
+
const result = validateToolInput("s", tool, { name: "hello" });
|
|
32
|
+
expect(result.valid).toBe(false);
|
|
33
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
34
|
+
expect(result.errors[0]!.message).toContain("age");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("catches wrong type", () => {
|
|
38
|
+
const tool = makeTool("wrong_type", {
|
|
39
|
+
type: "object",
|
|
40
|
+
properties: { count: { type: "number" } },
|
|
41
|
+
});
|
|
42
|
+
const result = validateToolInput("s", tool, { count: "not a number" });
|
|
43
|
+
expect(result.valid).toBe(false);
|
|
44
|
+
expect(result.errors[0]!.message).toContain("number");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("catches invalid enum value", () => {
|
|
48
|
+
const tool = makeTool("bad_enum", {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: { color: { type: "string", enum: ["red", "blue", "green"] } },
|
|
51
|
+
});
|
|
52
|
+
const result = validateToolInput("s", tool, { color: "purple" });
|
|
53
|
+
expect(result.valid).toBe(false);
|
|
54
|
+
expect(result.errors[0]!.message).toContain("one of");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("validates nested objects", () => {
|
|
58
|
+
const tool = makeTool("nested", {
|
|
59
|
+
type: "object",
|
|
60
|
+
properties: {
|
|
61
|
+
user: {
|
|
62
|
+
type: "object",
|
|
63
|
+
properties: { email: { type: "string" } },
|
|
64
|
+
required: ["email"],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const result = validateToolInput("s", tool, { user: {} });
|
|
69
|
+
expect(result.valid).toBe(false);
|
|
70
|
+
expect(result.errors[0]!.message).toContain("email");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("passes when no schema properties defined", () => {
|
|
74
|
+
const tool = makeTool("no_props", { type: "object" });
|
|
75
|
+
const result = validateToolInput("s", tool, { anything: "goes" });
|
|
76
|
+
expect(result.valid).toBe(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("passes with empty input and no required fields", () => {
|
|
80
|
+
const tool = makeTool("optional_only", {
|
|
81
|
+
type: "object",
|
|
82
|
+
properties: { optional: { type: "string" } },
|
|
83
|
+
});
|
|
84
|
+
const result = validateToolInput("s", tool, {});
|
|
85
|
+
expect(result.valid).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("reports multiple errors", () => {
|
|
89
|
+
const tool = makeTool("multi_error", {
|
|
90
|
+
type: "object",
|
|
91
|
+
properties: {
|
|
92
|
+
name: { type: "string" },
|
|
93
|
+
age: { type: "number" },
|
|
94
|
+
},
|
|
95
|
+
required: ["name", "age"],
|
|
96
|
+
});
|
|
97
|
+
const result = validateToolInput("s", tool, {});
|
|
98
|
+
expect(result.valid).toBe(false);
|
|
99
|
+
expect(result.errors.length).toBe(2);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("caches compiled validators", () => {
|
|
103
|
+
const tool = makeTool("cached_tool", {
|
|
104
|
+
type: "object",
|
|
105
|
+
properties: { x: { type: "string" } },
|
|
106
|
+
required: ["x"],
|
|
107
|
+
});
|
|
108
|
+
// Call twice — second should use cache
|
|
109
|
+
validateToolInput("cache_test", tool, { x: "a" });
|
|
110
|
+
const result = validateToolInput("cache_test", tool, {});
|
|
111
|
+
expect(result.valid).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
// Environment setup & latest features
|
|
4
|
+
"lib": ["ESNext"],
|
|
5
|
+
"target": "ESNext",
|
|
6
|
+
"module": "Preserve",
|
|
7
|
+
"moduleDetection": "force",
|
|
8
|
+
"jsx": "react-jsx",
|
|
9
|
+
"allowJs": true,
|
|
10
|
+
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
// Best practices
|
|
18
|
+
"strict": true,
|
|
19
|
+
"skipLibCheck": true,
|
|
20
|
+
"noFallthroughCasesInSwitch": true,
|
|
21
|
+
"noUncheckedIndexedAccess": true,
|
|
22
|
+
"noImplicitOverride": true,
|
|
23
|
+
|
|
24
|
+
// Some stricter flags (disabled by default)
|
|
25
|
+
"noUnusedLocals": false,
|
|
26
|
+
"noUnusedParameters": false,
|
|
27
|
+
"noPropertyAccessFromIndexSignature": false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Discover and use MCP tools via the mcpx CLI
|
|
3
|
+
globs:
|
|
4
|
+
alwaysApply: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# mcpx — MCP Tool Discovery and Execution
|
|
8
|
+
|
|
9
|
+
You have access to external tools via `mcpx`. Use this workflow:
|
|
10
|
+
|
|
11
|
+
## 1. Search for tools
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
mcpx search "<what you want to do>"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 2. Inspect the tool schema
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
mcpx info <server> <tool>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This shows parameters, types, required fields, and the full JSON Schema.
|
|
24
|
+
|
|
25
|
+
## 3. Execute the tool
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
mcpx exec <server> <tool> '<json args>'
|
|
29
|
+
mcpx exec <server> <tool> -f params.json
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Rules
|
|
33
|
+
|
|
34
|
+
- Always search before executing — don't assume tool names exist
|
|
35
|
+
- Always inspect the schema before executing — validate you have the right arguments
|
|
36
|
+
- Use `mcpx search -k` for exact name matching
|
|
37
|
+
- Pipe results through `jq` when you need to extract specific fields
|
|
38
|
+
- Use `-v` for verbose debugging (HTTP details + JSON-RPC protocol messages) if an exec fails unexpectedly
|
|
39
|
+
- Use `-l debug` to see all server log messages, or `-l error` for errors only
|
|
40
|
+
|
|
41
|
+
## Examples
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Find tools related to sending messages
|
|
45
|
+
mcpx search "send a message"
|
|
46
|
+
|
|
47
|
+
# See what parameters Slack_SendMessage needs
|
|
48
|
+
mcpx info arcade Slack_SendMessage
|
|
49
|
+
|
|
50
|
+
# Send a message
|
|
51
|
+
mcpx exec arcade Slack_SendMessage '{"channel":"#general","message":"hello"}'
|
|
52
|
+
|
|
53
|
+
# Chain commands — search repos and read the first result
|
|
54
|
+
mcpx exec github search_repositories '{"query":"mcp"}' \
|
|
55
|
+
| jq -r '.content[0].text | fromjson | .items[0].full_name' \
|
|
56
|
+
| xargs -I {} mcpx exec github get_file_contents '{"owner":"{}","path":"README.md"}'
|
|
57
|
+
|
|
58
|
+
# Read args from stdin
|
|
59
|
+
echo '{"path":"./README.md"}' | mcpx exec filesystem read_file
|
|
60
|
+
|
|
61
|
+
# Pipe from a file
|
|
62
|
+
cat params.json | mcpx exec server tool
|
|
63
|
+
|
|
64
|
+
# Read args from a file with --file flag
|
|
65
|
+
mcpx exec filesystem read_file -f params.json
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 4. Long-running tools (Tasks)
|
|
69
|
+
|
|
70
|
+
Some tools support async execution via MCP Tasks. mcpx auto-detects this and uses task-augmented execution when available.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Default: waits for the task to complete, showing progress
|
|
74
|
+
mcpx exec my-server long_running_tool '{"input": "data"}'
|
|
75
|
+
|
|
76
|
+
# Return immediately with a task handle (for scripting/polling)
|
|
77
|
+
mcpx exec my-server long_running_tool '{"input": "data"}' --no-wait
|
|
78
|
+
|
|
79
|
+
# Check task status
|
|
80
|
+
mcpx task get my-server <taskId>
|
|
81
|
+
|
|
82
|
+
# Retrieve the result once complete
|
|
83
|
+
mcpx task result my-server <taskId>
|
|
84
|
+
|
|
85
|
+
# List all tasks on a server
|
|
86
|
+
mcpx task list my-server
|
|
87
|
+
|
|
88
|
+
# Cancel a running task
|
|
89
|
+
mcpx task cancel my-server <taskId>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
For tools that don't support tasks, `exec` works exactly as before.
|
|
93
|
+
|
|
94
|
+
## 5. Elicitation (Server-Requested Input)
|
|
95
|
+
|
|
96
|
+
Some servers request user input mid-operation (e.g., confirmations, auth flows). mcpx handles this automatically:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Interactive — prompts appear in the terminal
|
|
100
|
+
mcpx exec my-server deploy_tool '{"target": "staging"}'
|
|
101
|
+
# Server requests input: Confirm deployment
|
|
102
|
+
# *Confirm [y/n]: y
|
|
103
|
+
|
|
104
|
+
# Non-interactive — decline all elicitation (for scripts/CI)
|
|
105
|
+
mcpx exec my-server deploy_tool '{"target": "staging"}' --no-interactive
|
|
106
|
+
|
|
107
|
+
# JSON mode — read/write elicitation as JSON via stdin/stdout
|
|
108
|
+
echo '{"action":"accept","content":{"confirm":true}}' | \
|
|
109
|
+
mcpx exec my-server deploy_tool '{"target": "staging"}' --json
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Authentication
|
|
113
|
+
|
|
114
|
+
Some HTTP servers require OAuth. If you see an "Not authenticated" error:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
mcpx auth <server> # authenticate via browser
|
|
118
|
+
mcpx auth <server> -s # check token status and TTL
|
|
119
|
+
mcpx auth <server> -r # force token refresh
|
|
120
|
+
mcpx deauth <server> # remove stored auth
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Available commands
|
|
124
|
+
|
|
125
|
+
| Command | Purpose |
|
|
126
|
+
| -------------------------------------- | --------------------------------- |
|
|
127
|
+
| `mcpx` | List all servers and tools |
|
|
128
|
+
| `mcpx servers` | List servers (name, type, detail) |
|
|
129
|
+
| `mcpx -d` | List with descriptions |
|
|
130
|
+
| `mcpx info <server>` | Server overview (version, capabilities, tools) |
|
|
131
|
+
| `mcpx info <server> <tool>` | Show tool schema |
|
|
132
|
+
| `mcpx exec <server>` | List tools for a server |
|
|
133
|
+
| `mcpx exec <server> <tool> '<json>'` | Execute a tool |
|
|
134
|
+
| `mcpx exec <server> <tool> -f file` | Execute with args from file |
|
|
135
|
+
| `mcpx search "<query>"` | Search tools (keyword + semantic) |
|
|
136
|
+
| `mcpx search -k "<pattern>"` | Keyword/glob search only |
|
|
137
|
+
| `mcpx search -q "<query>"` | Semantic search only |
|
|
138
|
+
| `mcpx search -n <number> "<query>"` | Limit number of results (default: 10) |
|
|
139
|
+
| `mcpx index` | Build/rebuild search index |
|
|
140
|
+
| `mcpx index -i` | Show index status |
|
|
141
|
+
| `mcpx auth <server>` | Authenticate with OAuth |
|
|
142
|
+
| `mcpx auth <server> -s` | Check token status and TTL |
|
|
143
|
+
| `mcpx auth <server> -r` | Force token refresh |
|
|
144
|
+
| `mcpx deauth <server>` | Remove stored authentication |
|
|
145
|
+
| `mcpx ping` | Check connectivity to all servers |
|
|
146
|
+
| `mcpx ping <server> [server2...]` | Check specific server(s) |
|
|
147
|
+
| `mcpx add <name> --command <cmd>` | Add a stdio MCP server |
|
|
148
|
+
| `mcpx add <name> --url <url>` | Add an HTTP MCP server |
|
|
149
|
+
| `mcpx add <name> --url <url> --transport sse` | Add a legacy SSE server |
|
|
150
|
+
| `mcpx remove <name>` | Remove an MCP server |
|
|
151
|
+
| `mcpx skill install --claude` | Install mcpx skill for Claude |
|
|
152
|
+
| `mcpx skill install --cursor` | Install mcpx rule for Cursor |
|
|
153
|
+
| `mcpx resource` | List all resources across servers |
|
|
154
|
+
| `mcpx resource <server>` | List resources for a server |
|
|
155
|
+
| `mcpx resource <server> <uri>` | Read a specific resource |
|
|
156
|
+
| `mcpx prompt` | List all prompts across servers |
|
|
157
|
+
| `mcpx prompt <server>` | List prompts for a server |
|
|
158
|
+
| `mcpx prompt <server> <name> '<json>'` | Get a specific prompt |
|
|
159
|
+
| `mcpx exec <server> <tool> --no-wait` | Execute as async task, return handle |
|
|
160
|
+
| `mcpx exec <server> <tool> --ttl <ms>` | Set task TTL (default: 60000) |
|
|
161
|
+
| `mcpx -N exec <server> <tool> ...` | Decline elicitation (non-interactive) |
|
|
162
|
+
| `mcpx task list <server>` | List tasks on a server |
|
|
163
|
+
| `mcpx task get <server> <taskId>` | Get task status |
|
|
164
|
+
| `mcpx task result <server> <taskId>` | Retrieve completed task result |
|
|
165
|
+
| `mcpx task cancel <server> <taskId>` | Cancel a running task |
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Evan Tahler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|