@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
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
|
+
}
|