@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.
Files changed (106) hide show
  1. package/.claude/settings.local.json +18 -0
  2. package/.claude/skills/mcpx.md +165 -0
  3. package/.claude/worktrees/elastic-jennings/.claude/settings.local.json +18 -0
  4. package/.claude/worktrees/elastic-jennings/.claude/skills/mcpcli.md +93 -0
  5. package/.claude/worktrees/elastic-jennings/.github/workflows/auto-release.yml +117 -0
  6. package/.claude/worktrees/elastic-jennings/.github/workflows/ci.yml +18 -0
  7. package/.claude/worktrees/elastic-jennings/.prettierignore +4 -0
  8. package/.claude/worktrees/elastic-jennings/.prettierrc +7 -0
  9. package/.claude/worktrees/elastic-jennings/CLAUDE.md +19 -0
  10. package/.claude/worktrees/elastic-jennings/LICENSE +21 -0
  11. package/.claude/worktrees/elastic-jennings/README.md +487 -0
  12. package/.claude/worktrees/elastic-jennings/bun.lock +381 -0
  13. package/.claude/worktrees/elastic-jennings/install.sh +55 -0
  14. package/.claude/worktrees/elastic-jennings/package.json +56 -0
  15. package/.claude/worktrees/elastic-jennings/src/cli.ts +39 -0
  16. package/.claude/worktrees/elastic-jennings/src/client/http.ts +100 -0
  17. package/.claude/worktrees/elastic-jennings/src/client/manager.ts +266 -0
  18. package/.claude/worktrees/elastic-jennings/src/client/oauth.ts +299 -0
  19. package/.claude/worktrees/elastic-jennings/src/client/stdio.ts +12 -0
  20. package/.claude/worktrees/elastic-jennings/src/commands/add.ts +155 -0
  21. package/.claude/worktrees/elastic-jennings/src/commands/auth.ts +114 -0
  22. package/.claude/worktrees/elastic-jennings/src/commands/exec.ts +91 -0
  23. package/.claude/worktrees/elastic-jennings/src/commands/index.ts +62 -0
  24. package/.claude/worktrees/elastic-jennings/src/commands/info.ts +38 -0
  25. package/.claude/worktrees/elastic-jennings/src/commands/list.ts +30 -0
  26. package/.claude/worktrees/elastic-jennings/src/commands/remove.ts +67 -0
  27. package/.claude/worktrees/elastic-jennings/src/commands/search.ts +45 -0
  28. package/.claude/worktrees/elastic-jennings/src/commands/skill.ts +70 -0
  29. package/.claude/worktrees/elastic-jennings/src/config/env.ts +41 -0
  30. package/.claude/worktrees/elastic-jennings/src/config/loader.ts +156 -0
  31. package/.claude/worktrees/elastic-jennings/src/config/schemas.ts +137 -0
  32. package/.claude/worktrees/elastic-jennings/src/context.ts +53 -0
  33. package/.claude/worktrees/elastic-jennings/src/output/formatter.ts +316 -0
  34. package/.claude/worktrees/elastic-jennings/src/output/logger.ts +114 -0
  35. package/.claude/worktrees/elastic-jennings/src/search/index.ts +69 -0
  36. package/.claude/worktrees/elastic-jennings/src/search/indexer.ts +92 -0
  37. package/.claude/worktrees/elastic-jennings/src/search/keyword.ts +86 -0
  38. package/.claude/worktrees/elastic-jennings/src/search/semantic.ts +75 -0
  39. package/.claude/worktrees/elastic-jennings/src/search/staleness.ts +8 -0
  40. package/.claude/worktrees/elastic-jennings/src/validation/schema.ts +77 -0
  41. package/.claude/worktrees/elastic-jennings/test/cli.test.ts +51 -0
  42. package/.claude/worktrees/elastic-jennings/test/client/manager.test.ts +249 -0
  43. package/.claude/worktrees/elastic-jennings/test/client/oauth.test.ts +328 -0
  44. package/.claude/worktrees/elastic-jennings/test/commands/add-remove.test.ts +253 -0
  45. package/.claude/worktrees/elastic-jennings/test/commands/exec.test.ts +105 -0
  46. package/.claude/worktrees/elastic-jennings/test/commands/info.test.ts +48 -0
  47. package/.claude/worktrees/elastic-jennings/test/commands/list.test.ts +39 -0
  48. package/.claude/worktrees/elastic-jennings/test/commands/skill.test.ts +98 -0
  49. package/.claude/worktrees/elastic-jennings/test/config/env.test.ts +61 -0
  50. package/.claude/worktrees/elastic-jennings/test/config/loader.test.ts +139 -0
  51. package/.claude/worktrees/elastic-jennings/test/fixtures/.keep +0 -0
  52. package/.claude/worktrees/elastic-jennings/test/fixtures/auth.json +10 -0
  53. package/.claude/worktrees/elastic-jennings/test/fixtures/mock-config/.keep +0 -0
  54. package/.claude/worktrees/elastic-jennings/test/fixtures/mock-config/servers.json +8 -0
  55. package/.claude/worktrees/elastic-jennings/test/fixtures/mock-server.ts +113 -0
  56. package/.claude/worktrees/elastic-jennings/test/fixtures/search.json +15 -0
  57. package/.claude/worktrees/elastic-jennings/test/fixtures/servers.json +18 -0
  58. package/.claude/worktrees/elastic-jennings/test/integration/stdio-server.test.ts +149 -0
  59. package/.claude/worktrees/elastic-jennings/test/output/formatter.test.ts +54 -0
  60. package/.claude/worktrees/elastic-jennings/test/output/logger.test.ts +89 -0
  61. package/.claude/worktrees/elastic-jennings/test/search/indexer.test.ts +32 -0
  62. package/.claude/worktrees/elastic-jennings/test/search/keyword.test.ts +80 -0
  63. package/.claude/worktrees/elastic-jennings/test/search/semantic.test.ts +32 -0
  64. package/.claude/worktrees/elastic-jennings/test/validation/schema.test.ts +113 -0
  65. package/.claude/worktrees/elastic-jennings/tsconfig.json +29 -0
  66. package/.cursor/rules/mcpx.mdc +165 -0
  67. package/LICENSE +21 -0
  68. package/README.md +627 -0
  69. package/package.json +58 -0
  70. package/src/cli.ts +72 -0
  71. package/src/client/browser.ts +24 -0
  72. package/src/client/debug-fetch.ts +81 -0
  73. package/src/client/elicitation.ts +368 -0
  74. package/src/client/http.ts +25 -0
  75. package/src/client/manager.ts +566 -0
  76. package/src/client/oauth.ts +314 -0
  77. package/src/client/sse.ts +17 -0
  78. package/src/client/stdio.ts +12 -0
  79. package/src/client/trace.ts +184 -0
  80. package/src/commands/add.ts +179 -0
  81. package/src/commands/auth.ts +114 -0
  82. package/src/commands/exec.ts +156 -0
  83. package/src/commands/index.ts +62 -0
  84. package/src/commands/info.ts +63 -0
  85. package/src/commands/list.ts +64 -0
  86. package/src/commands/ping.ts +69 -0
  87. package/src/commands/prompt.ts +60 -0
  88. package/src/commands/remove.ts +67 -0
  89. package/src/commands/resource.ts +46 -0
  90. package/src/commands/search.ts +49 -0
  91. package/src/commands/servers.ts +66 -0
  92. package/src/commands/skill.ts +112 -0
  93. package/src/commands/task.ts +82 -0
  94. package/src/config/env.ts +41 -0
  95. package/src/config/loader.ts +156 -0
  96. package/src/config/schemas.ts +152 -0
  97. package/src/context.ts +62 -0
  98. package/src/lib/input.ts +36 -0
  99. package/src/output/formatter.ts +884 -0
  100. package/src/output/logger.ts +173 -0
  101. package/src/search/index.ts +69 -0
  102. package/src/search/indexer.ts +92 -0
  103. package/src/search/keyword.ts +86 -0
  104. package/src/search/semantic.ts +75 -0
  105. package/src/search/staleness.ts +8 -0
  106. package/src/validation/schema.ts +103 -0
package/README.md ADDED
@@ -0,0 +1,627 @@
1
+ # mcpx
2
+
3
+ A command-line interface for MCP servers. **curl for MCP.**
4
+
5
+ Two audiences:
6
+
7
+ 1. **AI/LLM agents** that prefer shelling out over maintaining persistent MCP connections — better for token management, progressive tool discovery, and sharing a single pool of MCP servers across multiple agents on one machine
8
+ 2. **MCP developers** who need a fast way to discover, debug, and test their servers from the terminal
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ # Via bun
14
+ bun install -g @evantahler/mcpx
15
+
16
+ # Via curl
17
+ curl -fsSL https://raw.githubusercontent.com/evantahler/mcpx/main/install.sh | bash
18
+ ```
19
+
20
+ The curl installer downloads a pre-built binary (macOS/Linux) — no runtime needed. The bun install method requires [Bun](https://bun.sh). Windows `.exe` binaries are available on the [GitHub Releases](https://github.com/evantahler/mcpx/releases) page.
21
+
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ # Add the GitHub MCP server
26
+ mcpx add github --url https://mcp.github.com
27
+
28
+ # List all servers and their tools
29
+ mcpx
30
+
31
+ # List with descriptions
32
+ mcpx -d
33
+
34
+ # Inspect a server
35
+ mcpx info github
36
+
37
+ # Inspect a specific tool
38
+ mcpx info github search_repositories
39
+
40
+ # Execute a tool
41
+ mcpx exec github search_repositories '{"query": "mcp server"}'
42
+
43
+ # Search tools — combines keyword and semantic matching
44
+ mcpx search "post a ticket to linear"
45
+
46
+ # Search with only keyword/glob matching (fast, no embeddings)
47
+ mcpx search -k "*file*"
48
+
49
+ # Search with only semantic matching
50
+ mcpx search -q "manage pull requests"
51
+
52
+ # Limit the number of results (default: 10)
53
+ mcpx search -n 5 "manage pull requests"
54
+ ```
55
+
56
+ ## Commands
57
+
58
+ | Command | Description |
59
+ | -------------------------------------- | ------------------------------------------------------ |
60
+ | `mcpx` | List all configured servers and tools |
61
+ | `mcpx servers` | List configured servers (name, type, detail) |
62
+ | `mcpx info <server>` | Server overview (version, capabilities, tools, counts) |
63
+ | `mcpx info <server> <tool>` | Show tool schema |
64
+ | `mcpx search <query>` | Search tools (keyword + semantic) |
65
+ | `mcpx search -k <pattern>` | Keyword/glob search only |
66
+ | `mcpx search -q <query>` | Semantic search only |
67
+ | `mcpx search -n <number> <query>` | Limit number of results (default: 10) |
68
+ | `mcpx index` | Build/rebuild the search index |
69
+ | `mcpx index -i` | Show index status |
70
+ | `mcpx exec <server> <tool> [json]` | Validate inputs locally, then execute tool |
71
+ | `mcpx exec <server> <tool> -f file` | Read tool args from a JSON file |
72
+ | `mcpx exec <server>` | List available tools for a server |
73
+ | `mcpx auth <server>` | Authenticate with an HTTP MCP server (OAuth) |
74
+ | `mcpx auth <server> -s` | Check auth status and token TTL |
75
+ | `mcpx auth <server> -r` | Force token refresh |
76
+ | `mcpx deauth <server>` | Remove stored authentication for a server |
77
+ | `mcpx add <name> --command <cmd>` | Add a stdio MCP server to your config |
78
+ | `mcpx add <name> --url <url>` | Add an HTTP MCP server to your config |
79
+ | `mcpx remove <name>` | Remove an MCP server from your config |
80
+ | `mcpx ping` | Check connectivity to all configured servers |
81
+ | `mcpx ping <server> [server2...]` | Check connectivity to specific server(s) |
82
+ | `mcpx skill install --claude` | Install the mcpx skill for Claude Code |
83
+ | `mcpx skill install --cursor` | Install the mcpx rule for Cursor |
84
+ | `mcpx resource` | List all resources across all servers |
85
+ | `mcpx resource <server>` | List resources for a server |
86
+ | `mcpx resource <server> <uri>` | Read a specific resource |
87
+ | `mcpx prompt` | List all prompts across all servers |
88
+ | `mcpx prompt <server>` | List prompts for a server |
89
+ | `mcpx prompt <server> <name> [json]` | Get a specific prompt |
90
+ | `mcpx exec <server> <tool> --no-wait` | Execute as async task, return task handle immediately |
91
+ | `mcpx exec <server> <tool> --ttl <ms>` | Set task TTL in milliseconds (default: 60000) |
92
+ | `mcpx task list <server>` | List tasks on a server |
93
+ | `mcpx task get <server> <taskId>` | Get task status |
94
+ | `mcpx task result <server> <taskId>` | Retrieve completed task result |
95
+ | `mcpx task cancel <server> <taskId>` | Cancel a running task |
96
+
97
+ ## Options
98
+
99
+ | Flag | Purpose |
100
+ | ------------------------- | -------------------------------------------------------- |
101
+ | `-h, --help` | Show help |
102
+ | `-V, --version` | Show version |
103
+ | `-d, --with-descriptions` | Include tool descriptions in list output |
104
+ | `-c, --config <path>` | Specify config file location |
105
+ | `-v, --verbose` | Show HTTP details and JSON-RPC protocol messages |
106
+ | `-S, --show-secrets` | Show full auth tokens in verbose output (unmasked) |
107
+ | `-j, --json` | Force JSON output (default when piped) |
108
+ | `-N, --no-interactive` | Decline server elicitation requests (for scripted usage) |
109
+ | `-l, --log-level <level>` | Minimum server log level to display (default: `warning`) |
110
+
111
+ Server log messages (`notifications/message`) are displayed on stderr with level-appropriate coloring. Valid levels (in ascending severity): `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency`. When a server declares logging capability, mcpx sends `logging/setLevel` to request messages at the configured threshold and above.
112
+
113
+ ## Managing Servers
114
+
115
+ Add and remove servers from the CLI — no manual JSON editing required.
116
+
117
+ ```bash
118
+ # Add a stdio server
119
+ mcpx add filesystem --command npx --args "-y,@modelcontextprotocol/server-filesystem,/tmp"
120
+
121
+ # Add an HTTP server with headers
122
+ mcpx add my-api --url https://api.example.com/mcp --header "Authorization:Bearer tok123"
123
+
124
+ # Add with tool filtering
125
+ mcpx add github --url https://mcp.github.com --allowed-tools "search_*,get_*"
126
+
127
+ # Add a legacy SSE server (explicit transport)
128
+ mcpx add legacy-api --url https://api.example.com/sse --transport sse
129
+
130
+ # Add with environment variables
131
+ mcpx add my-server --command node --args "server.js" --env "API_KEY=sk-123,DEBUG=true"
132
+
133
+ # Overwrite an existing server
134
+ mcpx add filesystem --command echo --force
135
+
136
+ # Remove a server (also cleans up auth.json)
137
+ mcpx remove filesystem
138
+
139
+ # Remove but keep stored auth credentials
140
+ mcpx remove my-api --keep-auth
141
+
142
+ # Preview what would be removed
143
+ mcpx remove my-api --dry-run
144
+ ```
145
+
146
+ **`add` options:**
147
+
148
+ | Flag | Purpose |
149
+ | -------------------------- | -------------------------------------- |
150
+ | `--command <cmd>` | Command to run (stdio server) |
151
+ | `--args <a1,a2,...>` | Comma-separated arguments |
152
+ | `--env <KEY=VAL,...>` | Comma-separated environment variables |
153
+ | `--cwd <dir>` | Working directory for the command |
154
+ | `--url <url>` | Server URL (HTTP server) |
155
+ | `--header <Key:Value>` | HTTP header (repeatable) |
156
+ | `--transport <type>` | Transport: `sse` or `streamable-http` |
157
+ | `--allowed-tools <t1,t2>` | Comma-separated allowed tool patterns |
158
+ | `--disabled-tools <t1,t2>` | Comma-separated disabled tool patterns |
159
+ | `-f, --force` | Overwrite if server already exists |
160
+ | `--no-auth` | Skip automatic OAuth after adding |
161
+ | `--no-index` | Skip rebuilding the search index |
162
+
163
+ **`remove` options:**
164
+
165
+ | Flag | Purpose |
166
+ | ------------- | ------------------------------------------------- |
167
+ | `--keep-auth` | Don't remove stored auth credentials |
168
+ | `--dry-run` | Show what would be removed without changing files |
169
+
170
+ ## Configuration
171
+
172
+ Config lives in `~/.mcpx/` (or the current directory). Three files:
173
+
174
+ ### `servers.json` — MCP Server Definitions
175
+
176
+ Standard MCP server config format. Supports both stdio and HTTP servers.
177
+
178
+ ```json
179
+ {
180
+ "mcpServers": {
181
+ "filesystem": {
182
+ "command": "npx",
183
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
184
+ "env": { "API_KEY": "${API_KEY}" },
185
+ "allowedTools": ["read_file", "list_directory"],
186
+ "disabledTools": ["delete_file"]
187
+ },
188
+ "github": {
189
+ "url": "https://mcp.github.com"
190
+ },
191
+ "internal-api": {
192
+ "url": "https://mcp.internal.example.com",
193
+ "headers": { "Authorization": "Bearer ${TOKEN}" }
194
+ },
195
+ "legacy-sse": {
196
+ "url": "https://legacy.example.com/sse",
197
+ "transport": "sse"
198
+ }
199
+ }
200
+ }
201
+ ```
202
+
203
+ **Stdio servers** — `command` + `args`, spawned as child processes
204
+ **HTTP servers** — `url`, with optional static `headers` for pre-shared tokens. OAuth is auto-discovered at connection time via `.well-known/oauth-authorization-server` — no config needed. By default, mcpx tries Streamable HTTP first and automatically falls back to legacy SSE if the server doesn't support it. Set `"transport": "sse"` or `"transport": "streamable-http"` to skip auto-detection.
205
+
206
+ Environment variables are interpolated via `${VAR_NAME}` syntax. Set `MCP_STRICT_ENV=false` to warn instead of error on missing variables.
207
+
208
+ **Tool filtering:**
209
+
210
+ - `allowedTools` — glob patterns for tools to expose (whitelist)
211
+ - `disabledTools` — glob patterns for tools to hide (blacklist, takes precedence)
212
+
213
+ ### `auth.json` — OAuth Token Storage (managed automatically)
214
+
215
+ Stores OAuth tokens for HTTP MCP servers. You don't edit this directly — managed automatically.
216
+
217
+ ```json
218
+ {
219
+ "github": {
220
+ "access_token": "gho_xxxx",
221
+ "refresh_token": "ghr_xxxx",
222
+ "expires_at": "2026-03-03T12:00:00Z",
223
+ "token_type": "bearer",
224
+ "scope": "repo,read:org"
225
+ },
226
+ "linear": {
227
+ "access_token": "lin_xxxx",
228
+ "refresh_token": "lin_ref_xxxx",
229
+ "expires_at": "2026-03-04T08:30:00Z",
230
+ "token_type": "bearer"
231
+ }
232
+ }
233
+ ```
234
+
235
+ Tokens are automatically refreshed when expired (if a refresh token is available). Any command that connects to a server (`exec`, `info`, `search`, listing) will refresh tokens transparently. `mcpx auth <server> --status` shows current token state and TTL.
236
+
237
+ ### `search.json` — Semantic Search Index (managed automatically)
238
+
239
+ Contains every discovered tool with metadata for semantic search. Built and updated automatically — any command that connects to a server will detect new/changed tools and re-index them in the background.
240
+
241
+ ```json
242
+ {
243
+ "version": 1,
244
+ "indexed_at": "2026-03-03T10:00:00Z",
245
+ "embedding_model": "Xenova/all-MiniLM-L6-v2",
246
+ "tools": [
247
+ {
248
+ "server": "linear",
249
+ "tool": "createIssue",
250
+ "description": "Create a new issue in Linear",
251
+ "input_schema": { "...": "..." },
252
+ "scenarios": ["Create a new issue in Linear", "create issue"],
253
+ "keywords": ["create", "issue"],
254
+ "embedding": [0.012, -0.034, "..."]
255
+ }
256
+ ]
257
+ }
258
+ ```
259
+
260
+ Each tool gets:
261
+
262
+ - **scenarios** — the tool description plus a keyword phrase derived from the tool name
263
+ - **keywords** — terms extracted by splitting the tool name on `_`, `-`, and camelCase boundaries
264
+ - **embedding** — 384-dim vector for cosine similarity search
265
+
266
+ Scenarios and keywords are extracted heuristically from tool names and descriptions. Embeddings are generated in-process using `Xenova/all-MiniLM-L6-v2` (~23MB ONNX model, downloaded on first run). No API keys needed.
267
+
268
+ ## Config Resolution Order
269
+
270
+ 1. `MCP_CONFIG_PATH` environment variable
271
+ 2. `-c / --config` flag
272
+ 3. `./servers.json` (current directory)
273
+ 4. `~/.mcpx/servers.json`
274
+
275
+ ## Environment Variables
276
+
277
+ | Variable | Purpose | Default |
278
+ | ----------------- | --------------------------- | ---------- |
279
+ | `MCP_CONFIG_PATH` | Config directory path | `~/.mcpx/` |
280
+ | `MCP_DEBUG` | Enable debug output | `false` |
281
+ | `MCP_TIMEOUT` | Request timeout (seconds) | `1800` |
282
+ | `MCP_CONCURRENCY` | Parallel server connections | `5` |
283
+ | `MCP_MAX_RETRIES` | Retry attempts | `3` |
284
+ | `MCP_STRICT_ENV` | Error on missing `${VAR}` | `true` |
285
+
286
+ ## OAuth Flow
287
+
288
+ For HTTP MCP servers that require OAuth:
289
+
290
+ ```bash
291
+ # Start the OAuth flow — opens browser for authorization
292
+ mcpx auth github
293
+
294
+ # Check token status
295
+ mcpx auth github -s
296
+ # => github: authenticated (expires in 47m)
297
+
298
+ # Force re-authentication
299
+ mcpx auth github -r
300
+
301
+ # Authenticate without rebuilding the search index
302
+ mcpx auth github --no-index
303
+ ```
304
+
305
+ The OAuth flow:
306
+
307
+ 1. Discovers the server's OAuth metadata via `/.well-known/oauth-authorization-server`
308
+ 2. Starts a local callback server on a random port
309
+ 3. Opens the browser for user authorization
310
+ 4. Exchanges the authorization code for tokens
311
+ 5. Stores tokens in `auth.json`
312
+ 6. Automatically refreshes tokens before they expire on any subsequent command
313
+
314
+ ## Search
315
+
316
+ `mcpx search` is a single command that combines keyword matching and semantic vector search. By default, both strategies run and results are merged.
317
+
318
+ ```bash
319
+ # Combined search (default) — keyword hits + semantic matches, merged and ranked
320
+ mcpx search "send a message to slack"
321
+ # => slack/postMessage (0.94) Post a message to a channel
322
+ # => slack/sendDirectMessage (0.87) Send a DM to a user
323
+ # => teams/sendMessage (0.72) Send a Teams message
324
+
325
+ # Keyword only — fast glob match against tool names, descriptions, and keywords
326
+ mcpx search -k "*pull*request*"
327
+ # => github/createPullRequest
328
+ # => github/getPullRequest
329
+ # => github/mergePullRequest
330
+
331
+ # Semantic only — vector similarity against intent
332
+ mcpx search -q "review someone's code changes"
333
+ # => github/submitPullRequestReview (0.91) Submit a PR review
334
+ # => github/getPullRequest (0.85) Get PR details
335
+ # => github/listPullRequestCommits (0.78) List commits in a PR
336
+ ```
337
+
338
+ The combined search pipeline:
339
+
340
+ 1. **Keyword match** — glob/substring against tool names, descriptions, and indexed keywords
341
+ 2. **Semantic match** — embed the query, cosine similarity against tool embeddings
342
+ 3. **Merge & rank** — combine both result sets, deduplicate, sort by score
343
+ 4. **Return** — top results with similarity scores
344
+
345
+ The index updates incrementally — only new or changed tools are re-indexed. The first run indexes everything; subsequent runs are fast.
346
+
347
+ ## Tasks (Async Tool Execution)
348
+
349
+ MCP servers can declare support for [tasks](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks) — long-running operations that return a task handle instead of blocking until completion. When a tool supports tasks (`execution.taskSupport: "optional"` or `"required"`), mcpx automatically uses task-augmented execution.
350
+
351
+ ```bash
352
+ # Default: wait for the task to complete, showing progress updates
353
+ mcpx exec my-server long_running_tool '{"input": "data"}'
354
+
355
+ # Return immediately with a task handle (useful for scripting)
356
+ mcpx exec my-server long_running_tool '{"input": "data"}' --no-wait
357
+ # => Task created: task-abc123 (status: working)
358
+
359
+ # Check task status
360
+ mcpx task get my-server task-abc123
361
+
362
+ # Retrieve the result once complete
363
+ mcpx task result my-server task-abc123
364
+
365
+ # List all tasks on a server
366
+ mcpx task list my-server
367
+
368
+ # Cancel a running task
369
+ mcpx task cancel my-server task-abc123
370
+ ```
371
+
372
+ For tools that don't support tasks, `exec` works exactly as before — no changes needed.
373
+
374
+ ## Elicitation (Server-Requested User Input)
375
+
376
+ MCP servers can request user input mid-operation via [elicitation](https://modelcontextprotocol.io/specification/draft/client/elicitation). mcpx supports both modes:
377
+
378
+ - **Form mode**: The server sends a JSON schema describing input fields (strings, numbers, booleans, enums, multi-select). mcpx renders prompts in the terminal and validates input before returning it.
379
+ - **URL mode**: The server sends a URL for the user to visit (e.g., for authentication or payment flows). mcpx opens it in the default browser.
380
+
381
+ ```bash
382
+ # Interactive — prompts appear in the terminal
383
+ mcpx exec my-server deploy_tool '{"target": "staging"}'
384
+ # Server requests input: Confirm deployment
385
+ # *Confirm [y/n]: y
386
+
387
+ # Non-interactive — decline all elicitation (for scripts/CI)
388
+ mcpx exec my-server deploy_tool '{"target": "staging"}' --no-interactive
389
+
390
+ # JSON mode — elicitation requests are written to stdout as JSON,
391
+ # and responses are read from stdin (for programmatic handling)
392
+ echo '{"action":"accept","content":{"confirm":true}}' | \
393
+ mcpx exec my-server deploy_tool '{"target": "staging"}' --json
394
+ ```
395
+
396
+ ## Debugging with Verbose Mode
397
+
398
+ `-v` shows both HTTP request/response details (like `curl -v`) and JSON-RPC protocol messages exchanged with the server. All debug output goes to stderr so piping to `jq` still works.
399
+
400
+ ### JSON-RPC Protocol Tracing
401
+
402
+ Verbose mode traces every JSON-RPC message at the transport layer — requests, responses, and notifications — for both stdio and HTTP servers:
403
+
404
+ ```bash
405
+ mcpx -v exec mock echo '{"message":"hello"}'
406
+
407
+ # → initialize (id: 0)
408
+ # ← initialize (id: 0) [45ms] — mock-server v1.0
409
+ # → notifications/initialized
410
+ # → tools/call (id: 1)
411
+ # ← tools/call (id: 1) [12ms] — ok
412
+ ```
413
+
414
+ With `--json`, trace output is NDJSON on stderr (one JSON object per message):
415
+
416
+ ```bash
417
+ mcpx -v -j exec mock echo '{"message":"hello"}' 2>trace.jsonl
418
+ ```
419
+
420
+ ### HTTP Traffic
421
+
422
+ For HTTP/SSE servers, verbose mode also shows raw HTTP headers and timing:
423
+
424
+ ```bash
425
+ mcpx -v exec arcade Gmail_WhoAmI
426
+
427
+ # > POST https://api.arcade.dev/mcp/evan-coding
428
+ # > authorization: Bearer eyJhbGci...
429
+ # > content-type: application/json
430
+ # > accept: application/json, text/event-stream
431
+ # >
432
+ # {
433
+ # "method": "tools/call",
434
+ # "params": {
435
+ # "name": "Gmail_WhoAmI",
436
+ # "arguments": {}
437
+ # }
438
+ # }
439
+ # < 200 OK (142ms)
440
+ # < content-type: application/json
441
+ # < x-request-id: abc123
442
+ # <
443
+ # { "content": [ ... ] }
444
+
445
+ # Debug on stderr, clean JSON on stdout
446
+ mcpx -v exec arcade Gmail_WhoAmI | jq .
447
+
448
+ # Show full auth tokens (unmasked)
449
+ mcpx -v -S exec arcade Gmail_WhoAmI
450
+ ```
451
+
452
+ The `>` / `<` convention matches curl — `>` for request, `<` for response. The `→` / `←` arrows show JSON-RPC protocol messages with method names, IDs, round-trip timing, and result summaries.
453
+
454
+ ## Input Validation
455
+
456
+ `mcpx exec` validates tool arguments locally before sending them to the server. MCP tools advertise a JSON Schema for their inputs — mcpx uses this to catch errors fast, without a round-trip.
457
+
458
+ ```bash
459
+ # Missing required field — caught locally
460
+ mcpx exec github create_issue '{"title": "bug"}'
461
+ # => error: missing required field "repo" (github/create_issue)
462
+
463
+ # Wrong type — caught locally
464
+ mcpx exec github create_issue '{"repo": "foo", "title": 123}'
465
+ # => error: "title" must be a string, got number (github/create_issue)
466
+
467
+ # Valid — sent to server
468
+ mcpx exec github create_issue '{"repo": "foo", "title": "bug"}'
469
+ # => { ... }
470
+ ```
471
+
472
+ Validation covers:
473
+
474
+ - **Required fields** — errors before sending if any are missing
475
+ - **Type checking** — string, number, boolean, array, object
476
+ - **Enum values** — rejects values not in the allowed set
477
+ - **Nested objects** — validates recursively
478
+
479
+ If a tool's `inputSchema` is unavailable (some servers don't provide one), execution proceeds without local validation.
480
+
481
+ ## Shell Output & Piping
482
+
483
+ Output is human-friendly by default, JSON when piped:
484
+
485
+ ```bash
486
+ # Human-readable
487
+ mcpx info github
488
+
489
+ # JSON (piped)
490
+ mcpx info github | jq '.tools[].name'
491
+
492
+ # Force JSON
493
+ mcpx info github --json
494
+ ```
495
+
496
+ Tool results are always JSON, designed for chaining:
497
+
498
+ ```bash
499
+ # Search repos and read the first result
500
+ mcpx exec github search_repositories '{"query":"mcp"}' \
501
+ | jq -r '.content[0].text | fromjson | .items[0].full_name' \
502
+ | xargs -I {} mcpx exec github get_file_contents '{"owner":"{}","path":"README.md"}'
503
+
504
+ # Conditional execution
505
+ mcpx exec filesystem list_directory '{"path":"."}' \
506
+ | jq -e '.content[0].text | contains("package.json")' \
507
+ && mcpx exec filesystem read_file '{"path":"./package.json"}'
508
+ ```
509
+
510
+ Stdin and file input work for tool arguments:
511
+
512
+ ```bash
513
+ # Pipe JSON directly
514
+ echo '{"path":"./README.md"}' | mcpx exec filesystem read_file
515
+
516
+ # Pipe from a file
517
+ cat params.json | mcpx exec server tool
518
+
519
+ # Shell redirect from a file
520
+ mcpx exec server tool < params.json
521
+
522
+ # Read args from a file with --file flag
523
+ mcpx exec filesystem read_file -f params.json
524
+ ```
525
+
526
+ ## Agent Integration
527
+
528
+ ### Claude Code Skill
529
+
530
+ mcpx ships a Claude Code skill at `.claude/skills/mcpx.md` that teaches Claude Code how to discover and use MCP tools. Install it:
531
+
532
+ ```bash
533
+ # Install to the current project (.claude/skills/mcpx.md)
534
+ mcpx skill install --claude
535
+
536
+ # Install globally (~/.claude/skills/mcpx.md)
537
+ mcpx skill install --claude --global
538
+
539
+ # Install to both locations
540
+ mcpx skill install --claude --global --project
541
+
542
+ # Overwrite an existing skill file
543
+ mcpx skill install --claude --force
544
+ ```
545
+
546
+ Then in any Claude Code session, the agent can use `/mcpx` or the skill triggers automatically when the agent needs to interact with external services. The skill instructs the agent to:
547
+
548
+ 1. **Search first** — `mcpx search "<intent>"` to find relevant tools
549
+ 2. **Inspect** — `mcpx info <server> <tool>` to get the schema before calling
550
+ 3. **Execute** — `mcpx exec <server> <tool> '<json>'` to execute
551
+
552
+ This keeps tool schemas out of the system prompt entirely. The agent discovers what it needs on-demand, saving tokens and context window space.
553
+
554
+ ### Cursor Rule
555
+
556
+ mcpx ships a Cursor rule at `.cursor/rules/mcpx.mdc` that teaches Cursor how to discover and use MCP tools. Install it:
557
+
558
+ ```bash
559
+ # Install to the current project (.cursor/rules/mcpx.mdc)
560
+ mcpx skill install --cursor
561
+
562
+ # Install globally (~/.cursor/rules/mcpx.mdc)
563
+ mcpx skill install --cursor --global
564
+
565
+ # Install both Claude and Cursor at once
566
+ mcpx skill install --claude --cursor
567
+
568
+ # Overwrite an existing rule file
569
+ mcpx skill install --cursor --force
570
+ ```
571
+
572
+ ### Raw System Prompt (other agents)
573
+
574
+ For non-Claude-Code agents, add this to the system prompt:
575
+
576
+ ```
577
+ You have access to MCP tools via the `mcpx` CLI.
578
+
579
+ To discover tools:
580
+ mcpx search "<what you want to do>" # combined keyword + semantic
581
+ mcpx search -k "<pattern>" # keyword/glob only
582
+ mcpx info <server> <tool> # tool schema
583
+
584
+ To execute tools:
585
+ mcpx exec <server> <tool> '<json args>'
586
+ mcpx exec <server> <tool> -f params.json
587
+
588
+ Always search before executing — don't assume tool names.
589
+ ```
590
+
591
+ ## Development
592
+
593
+ ```bash
594
+ # Install dependencies
595
+ bun install
596
+
597
+ # Run in development
598
+ bun run dev
599
+
600
+ # Run tests
601
+ bun test
602
+
603
+ # Build single binary
604
+ bun run build
605
+
606
+ # Lint
607
+ bun lint
608
+ ```
609
+
610
+ ## Tech Stack
611
+
612
+ | Layer | Choice |
613
+ | ----------- | ----------------------------------------------------- |
614
+ | Runtime | Bun |
615
+ | Language | TypeScript |
616
+ | MCP Client | `@modelcontextprotocol/sdk` |
617
+ | CLI Parsing | `commander` |
618
+ | Validation | `ajv` (JSON Schema) |
619
+ | Embeddings | `@huggingface/transformers` (Xenova/all-MiniLM-L6-v2) |
620
+
621
+ ## Inspiration
622
+
623
+ Inspired by [mcp-cli](https://github.com/philschmid/mcp-cli) by Phil Schmid, which nails the core DX of a shell-friendly MCP client. mcpx extends that foundation with OAuth support for HTTP servers and semantic tool search.
624
+
625
+ ## License
626
+
627
+ MIT
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@evantahler/mcpx",
3
+ "version": "0.15.0",
4
+ "description": "A command-line interface for MCP servers. curl for MCP.",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcpx": "./src/cli.ts"
8
+ },
9
+ "files": [
10
+ "src",
11
+ ".claude",
12
+ ".cursor",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "dev": "bun run src/cli.ts",
18
+ "test": "bun test",
19
+ "test:e2e": "bun test test/integration/remote-server.test.ts",
20
+ "lint": "prettier --check .",
21
+ "format": "prettier --write .",
22
+ "build": "bun build --compile --minify --sourcemap ./src/cli.ts --outfile dist/mcpx"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/evantahler/mcpx.git"
30
+ },
31
+ "keywords": [
32
+ "mcp",
33
+ "cli",
34
+ "model-context-protocol",
35
+ "ai",
36
+ "llm",
37
+ "tools"
38
+ ],
39
+ "author": "Evan Tahler",
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "@huggingface/transformers": "^3.8.1",
43
+ "@modelcontextprotocol/sdk": "^1.27.1",
44
+ "ajv": "^8.18.0",
45
+ "ansis": "^4.2.0",
46
+ "commander": "^14.0.3",
47
+ "nanospinner": "^1.2.2",
48
+ "picomatch": "^4.0.3"
49
+ },
50
+ "devDependencies": {
51
+ "@types/bun": "latest",
52
+ "@types/picomatch": "^4.0.2",
53
+ "prettier": "^3.8.1"
54
+ },
55
+ "peerDependencies": {
56
+ "typescript": "^5"
57
+ }
58
+ }