@apify/mcpc 0.1.11-beta.4 → 0.1.11-beta.5
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/CHANGELOG.md +43 -35
- package/README.md +62 -26
- package/dist/bridge/index.js +61 -41
- package/dist/bridge/index.js.map +1 -1
- package/dist/cli/commands/sessions.d.ts.map +1 -1
- package/dist/cli/commands/sessions.js +157 -158
- package/dist/cli/commands/sessions.js.map +1 -1
- package/dist/cli/commands/tools.d.ts +1 -1
- package/dist/cli/commands/tools.d.ts.map +1 -1
- package/dist/cli/commands/tools.js +77 -17
- package/dist/cli/commands/tools.js.map +1 -1
- package/dist/cli/commands/x402.d.ts.map +1 -1
- package/dist/cli/commands/x402.js +20 -6
- package/dist/cli/commands/x402.js.map +1 -1
- package/dist/cli/helpers.d.ts +1 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/helpers.js +4 -18
- package/dist/cli/helpers.js.map +1 -1
- package/dist/cli/index.js +45 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +4 -2
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +74 -23
- package/dist/cli/output.js.map +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +0 -2
- package/dist/cli/parser.js.map +1 -1
- package/dist/cli/shell.js +4 -4
- package/dist/cli/shell.js.map +1 -1
- package/dist/core/mcp-client.d.ts +10 -4
- package/dist/core/mcp-client.d.ts.map +1 -1
- package/dist/core/mcp-client.js +75 -7
- package/dist/core/mcp-client.js.map +1 -1
- package/dist/core/transports.d.ts.map +1 -1
- package/dist/core/transports.js +3 -0
- package/dist/core/transports.js.map +1 -1
- package/dist/lib/auth/keychain.d.ts.map +1 -1
- package/dist/lib/auth/keychain.js +40 -15
- package/dist/lib/auth/keychain.js.map +1 -1
- package/dist/lib/auth/oauth-flow.d.ts.map +1 -1
- package/dist/lib/auth/oauth-flow.js +87 -10
- package/dist/lib/auth/oauth-flow.js.map +1 -1
- package/dist/lib/auth/oauth-utils.d.ts.map +1 -1
- package/dist/lib/auth/oauth-utils.js +3 -2
- package/dist/lib/auth/oauth-utils.js.map +1 -1
- package/dist/lib/bridge-client.js +1 -1
- package/dist/lib/bridge-manager.js +1 -1
- package/dist/lib/bridge-manager.js.map +1 -1
- package/dist/lib/proxy.d.ts +6 -0
- package/dist/lib/proxy.d.ts.map +1 -0
- package/dist/lib/proxy.js +13 -0
- package/dist/lib/proxy.js.map +1 -0
- package/dist/lib/session-client.d.ts +6 -3
- package/dist/lib/session-client.d.ts.map +1 -1
- package/dist/lib/session-client.js +13 -6
- package/dist/lib/session-client.js.map +1 -1
- package/dist/lib/types.d.ts +9 -3
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/x402/fetch-middleware.d.ts.map +1 -1
- package/dist/lib/x402/fetch-middleware.js +41 -8
- package/dist/lib/x402/fetch-middleware.js.map +1 -1
- package/docs/TODOs.md +0 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,42 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Changed
|
|
11
|
-
- Revised session states: auth failures (401/403) now show as `unauthorized` (separate from `expired` which is for session ID expiry), with actionable login guidance; new `disconnected` display state surfaces when bridge is alive but server has been unreachable for >2 minutes
|
|
12
|
-
- `DISCONNECTED_THRESHOLD_MS` is now derived from `KEEPALIVE_INTERVAL_MS` (2× ping interval + 5s buffer) via shared constants, eliminating duplicate magic numbers
|
|
13
|
-
|
|
14
|
-
### Added
|
|
15
|
-
|
|
16
|
-
- `--async` flag for `tools-call` to opt-in to async task execution (experimental) with a progress spinner showing elapsed time and server status messages in human mode
|
|
17
|
-
- `--detach` flag for `tools-call` to start an async task and return the task ID immediately without waiting for completion (implies `--async`)
|
|
18
|
-
- New `tasks-list`, `tasks-get`, `tasks-cancel` commands for managing async tasks on the server
|
|
19
|
-
- Task capability and `execution.taskSupport` displayed in `tools-get` and server info
|
|
20
|
-
- E2E test server now includes a `slow-task` tool that supports async task execution
|
|
21
|
-
- E2E tests for async task execution, detached mode, task management (list/get/cancel), and synchronous fallback
|
|
22
|
-
- `[async]` indicator in `tools-list` output for tools that support async task execution
|
|
23
|
-
- `--insecure` global option to skip TLS certificate verification, for MCP servers with self-signed certificates
|
|
24
|
-
- E2E test for `--insecure` flag using a self-signed HTTPS test server wrapper
|
|
25
|
-
- `--client-id` and `--client-secret` options for `mcpc login` command, for servers that don't support dynamic client registration
|
|
26
|
-
- `mcpc close @session`, `mcpc restart @session`, and `mcpc shell @session` command-first syntax as alternatives to `mcpc @session close/restart/shell`
|
|
27
|
-
- E2E tests now run under the Bun runtime (in addition to Node.js); use `./test/e2e/run.sh --runtime bun` or `npm run test:e2e:bun`
|
|
28
|
-
- `--no-profile` option for `connect` command to skip OAuth profile auto-detection and connect anonymously
|
|
29
|
-
|
|
30
10
|
### Fixed
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- Combining `--profile` with `--header "Authorization: ..."` now returns a clear error instead of silently stripping the header
|
|
35
|
-
- `logTarget` no longer prints a misleading `[→ @name (HTTP)]` prefix when a session doesn't exist; only the error message is shown
|
|
36
|
-
- `logging-set-level` JSON output no longer includes a `success` field; output is now `{"level":"<level>"}` consistent with the project's convention of indicating errors via exit codes
|
|
37
|
-
- `--header` / `-H` option is now specific to the `connect` command instead of being shown as a global option in `mcpc --help`
|
|
38
|
-
- Bridge now forwards `logging/message` notifications from the MCP server to connected clients, so `logging-set-level` actually takes effect in interactive shell sessions
|
|
39
|
-
- IPC buffer between CLI and bridge process is now capped at 10 MB; sockets are destroyed if the limit is exceeded, preventing unbounded memory growth
|
|
40
|
-
- `validateOptions()` no longer includes subcommand-specific options (`--full`, `--x402`, `--proxy`, etc.) in global known-options list; misplaced flags now produce clear "Unknown option" errors instead of confusing Commander rejections
|
|
41
|
-
- Sessions requiring authentication now correctly show as `expired` instead of `live` when the server rejects unauthenticated connections
|
|
42
|
-
- Auth errors wrapped in `NetworkError` by bridge IPC are now detected on first health check, avoiding unnecessary bridge restart
|
|
43
|
-
- Fixed flaky E2E invariant check that failed when `lastSeenAt` changed between `--json` and `--json --verbose` calls
|
|
44
|
-
- `--timeout` flag now correctly propagates to MCP requests via session bridge
|
|
45
|
-
- `parseServerArg()` now handles well Windows drive-letter config paths as well as other ambiguous cases
|
|
11
|
+
|
|
12
|
+
- Fixed auth loss when reconnecting an unauthorized session via `mcpc connect` — the `unauthorized` status was not cleared, causing all subsequent operations to fail with "Authentication required by server" even after successful reconnection
|
|
13
|
+
- HTTP proxy support (`HTTP_PROXY`/`HTTPS_PROXY` env vars) now works for MCP server connections, OAuth token refresh, and x402 payment signing — previously the MCP SDK transport and OAuth calls bypassed the global proxy dispatcher
|
|
46
14
|
|
|
47
15
|
### Changed
|
|
48
16
|
|
|
@@ -59,7 +27,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
59
27
|
|
|
60
28
|
Direct one-shot URL access (e.g. `mcpc mcp.apify.com tools-list`) is removed; create a session first with `mcpc connect`.
|
|
61
29
|
|
|
30
|
+
- Renamed `--async` flag to `--task` in `tools-call` for consistency with MCP specification; `--detach` now implies `--task`
|
|
31
|
+
- Revised session states: auth failures (401/403) now show as `unauthorized` (separate from `expired` which is for session ID expiry), with actionable login guidance; new `disconnected` state surfaces when bridge is alive but server has been unreachable for >2 minutes
|
|
32
|
+
- When `--profile` is not specified, only the `default` profile is used; non-default profiles require an explicit `--profile` flag
|
|
62
33
|
- `@napi-rs/keyring` native addon is now loaded lazily: `mcpc` starts and works normally even when `libsecret` (Linux) or the addon itself is missing; a one-time warning is emitted and credentials fall back to `~/.mcpc/credentials.json` (mode 0600)
|
|
34
|
+
- `x402 sign` now takes the PAYMENT-REQUIRED header as a positional argument instead of `-r` flag (e.g. `mcpc x402 sign <base64>`)
|
|
35
|
+
- `tools-list` and `tools-get` now show `[task:optional]`, `[task:required]`, or `[task:forbidden]` instead of `[async]`
|
|
36
|
+
- Tools cache now fetches all pages on startup and on `tools/list_changed` notifications; `tools-get` uses cached list first and only re-fetches if the tool is not found
|
|
37
|
+
- `--header` / `-H` option is now specific to the `connect` command instead of being shown as a global option
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- New `tasks-list`, `tasks-get`, `tasks-cancel` commands for managing async tasks on the server
|
|
42
|
+
- `--task` flag for `tools-call` to opt-in to task execution with a progress spinner showing elapsed time, server status messages, and progress notifications in human mode
|
|
43
|
+
- `--detach` flag for `tools-call` to start a task and return the task ID immediately without waiting for completion (implies `--task`)
|
|
44
|
+
- Press ESC during `--task` execution to detach on the fly — the task continues in the background and the task ID is printed
|
|
45
|
+
- `--insecure` global option to skip TLS certificate verification, for MCP servers with self-signed certificates
|
|
46
|
+
- `--client-id` and `--client-secret` options for `mcpc login` command, for servers that don't support dynamic client registration
|
|
47
|
+
- `--no-profile` option for `connect` command to skip OAuth profile auto-detection and connect anonymously
|
|
48
|
+
- `mcpc close @session`, `mcpc restart @session`, and `mcpc shell @session` command-first syntax as alternatives to `mcpc @session close/restart/shell`
|
|
49
|
+
- `mcpc login` now falls back to accepting a pasted callback URL when the browser cannot be opened (e.g. headless servers, containers)
|
|
50
|
+
- `tools-list` now shows inline parameter signatures (e.g. `read_file(path: string, +4 optional)`) for quick scanning without `--full`
|
|
51
|
+
- `mcpc @session` now shows available tools list from bridge cache (no extra server call)
|
|
52
|
+
- Task capability and `execution.taskSupport` displayed in `tools-get` and server info
|
|
53
|
+
- x402 payments are now also sent via the MCP `_meta["x402/payment"]` field on `tools/call` requests, in addition to the existing HTTP header
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
|
|
57
|
+
- Explicit `--header "Authorization: Bearer ..."` is no longer silently ignored when a default OAuth profile exists for the same server; explicit CLI headers now take precedence over auto-detected profiles
|
|
58
|
+
- Combining `--profile` with `--header "Authorization: ..."` now returns a clear error instead of silently stripping the header
|
|
59
|
+
- Session restart now auto-detects the `default` OAuth profile created after the session was established, fixing the `login` then `restart` flow for unauthorized sessions
|
|
60
|
+
- Sessions requiring authentication now correctly show as `expired` instead of `live` when the server rejects unauthenticated connections
|
|
61
|
+
- Auth errors wrapped in `NetworkError` by bridge IPC are now detected on first health check, avoiding unnecessary bridge restart
|
|
62
|
+
- `--timeout` flag now correctly propagates to MCP requests via session bridge
|
|
63
|
+
- `--task` and `--detach` tool calls now correctly send task creation parameters to the server
|
|
64
|
+
- Bridge now forwards `logging/message` notifications from the MCP server to connected clients, so `logging-set-level` actually takes effect in interactive shell sessions
|
|
65
|
+
- IPC buffer between CLI and bridge process is now capped at 10 MB, preventing unbounded memory growth
|
|
66
|
+
- `parseServerArg()` now handles Windows drive-letter config paths and other ambiguous cases
|
|
67
|
+
- Misplaced subcommand-specific flags (e.g. `--full`, `--proxy`) now produce clear "Unknown option" errors instead of confusing rejections
|
|
68
|
+
- `logging-set-level` JSON output no longer includes a redundant `success` field
|
|
69
|
+
- `logTarget` no longer prints a misleading prefix when a session doesn't exist
|
|
70
|
+
- File lock retries now use randomized exponential backoff to reduce contention
|
|
63
71
|
|
|
64
72
|
## [0.1.10] - 2026-03-01
|
|
65
73
|
|
package/README.md
CHANGED
|
@@ -130,7 +130,7 @@ Commands:
|
|
|
130
130
|
logout <server> Delete an authentication profile for a server
|
|
131
131
|
clean [resources...] Clean up mcpc data (sessions, profiles, logs, all)
|
|
132
132
|
x402 [subcommand] [args...] Configure an x402 payment wallet (EXPERIMENTAL)
|
|
133
|
-
help [command]
|
|
133
|
+
help [command] [subcommand] Show help for a specific command
|
|
134
134
|
|
|
135
135
|
MCP session commands (after connecting):
|
|
136
136
|
<@session> Show MCP server info and capabilities
|
|
@@ -316,29 +316,34 @@ Still, sessions can fail due to network disconnects, bridge process crash, or se
|
|
|
316
316
|
|
|
317
317
|
**Session states:**
|
|
318
318
|
|
|
319
|
-
| State
|
|
320
|
-
|
|
|
321
|
-
| 🟢 **`live`**
|
|
322
|
-
| 🟡 **`
|
|
323
|
-
|
|
|
319
|
+
| State | Meaning |
|
|
320
|
+
| --------------------- | ------------------------------------------------------------------------------------------------- |
|
|
321
|
+
| 🟢 **`live`** | Bridge process running and server responding |
|
|
322
|
+
| 🟡 **`disconnected`** | Bridge process running but server unreachable; auto-recovers when server responds |
|
|
323
|
+
| 🟡 **`crashed`** | Bridge process crashed or was killed; auto-restarts on next use |
|
|
324
|
+
| 🔴 **`unauthorized`** | Server rejected authentication (401/403) or token refresh failed; requires `login` then `restart` |
|
|
325
|
+
| 🔴 **`expired`** | Server rejected session ID (404); requires `restart` |
|
|
324
326
|
|
|
325
327
|
Here's how `mcpc` handles various bridge process and server connection states:
|
|
326
328
|
|
|
327
329
|
- While the **bridge process is running**:
|
|
328
330
|
- If **server positively responds** to pings, the session is marked 🟢 **`live`**, and everything is fine.
|
|
329
|
-
- If **server stops responding**, the
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
the
|
|
333
|
-
|
|
331
|
+
- If **server stops responding**, the session is marked 🟡 **`disconnected`**.
|
|
332
|
+
The bridge will keep trying to reconnect in the background and will return to 🟢 **`live`** once the server responds again.
|
|
333
|
+
- If **server rejects authentication** (HTTP 401 or 403) or token refresh fails,
|
|
334
|
+
the session is marked 🔴 **`unauthorized`**.
|
|
335
|
+
You need to re-authenticate with `mcpc login <server>` and then `mcpc @my-session restart`.
|
|
336
|
+
- If **server rejects the session ID** (HTTP 404), indicating the MCP session is no longer valid,
|
|
337
|
+
the session is marked 🔴 **`expired`**.
|
|
338
|
+
You need to restart the session with `mcpc @my-session restart` to establish a new connection.
|
|
334
339
|
- If the **bridge process crashes**, `mcpc` will mark the session as 🟡 **`crashed`** on first use.
|
|
335
340
|
Next time you run `mcpc @my-session ...`, it will attempt to restart the bridge process.
|
|
336
341
|
- If bridge **restart succeeds**, everything starts again (see above).
|
|
337
342
|
- If bridge **restart fails**, `mcpc @my-session ...` returns error, and session remains marked 🟡 **`crashed`**.
|
|
338
343
|
|
|
339
344
|
Note that `mcpc` never automatically removes sessions from the list.
|
|
340
|
-
Instead, it keeps them flagged as 🟡 **`crashed
|
|
341
|
-
and any future attempts to use them will
|
|
345
|
+
Instead, it keeps them flagged as 🟡 **`crashed`**, 🔴 **`unauthorized`**, or 🔴 **`expired`**,
|
|
346
|
+
and any future attempts to use them will show the appropriate error with recovery instructions.
|
|
342
347
|
|
|
343
348
|
To **remove the session from the list**, you need to explicitly close it:
|
|
344
349
|
|
|
@@ -705,6 +710,35 @@ mcpc x402 remove
|
|
|
705
710
|
|
|
706
711
|
After creating a wallet, **fund it with USDC on Base** (mainnet or Sepolia testnet) to enable payments.
|
|
707
712
|
|
|
713
|
+
### Manual payment signing
|
|
714
|
+
|
|
715
|
+
You can manually sign a payment from a server's `PAYMENT-REQUIRED` header using `x402 sign`.
|
|
716
|
+
This is useful for pre-signing payments or integrating with tools outside of `mcpc`.
|
|
717
|
+
|
|
718
|
+
```bash
|
|
719
|
+
# Sign a payment using the base64-encoded PAYMENT-REQUIRED header
|
|
720
|
+
mcpc x402 sign <base64-payment-required>
|
|
721
|
+
|
|
722
|
+
# Override the amount (in USD, e.g. 2.50 = $2.50)
|
|
723
|
+
mcpc x402 sign <base64-payment-required> --amount 2.50
|
|
724
|
+
|
|
725
|
+
# Override the expiry (in seconds from now)
|
|
726
|
+
mcpc x402 sign <base64-payment-required> --expiry 7200
|
|
727
|
+
|
|
728
|
+
# Combine overrides and use JSON output
|
|
729
|
+
mcpc x402 sign <base64-payment-required> --amount 1.00 --expiry 3600 --json
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
**Options:**
|
|
733
|
+
|
|
734
|
+
| Option | Description |
|
|
735
|
+
| ------------------- | ---------------------------------------------------------------- |
|
|
736
|
+
| `--amount <usd>` | Override the payment amount in USD (e.g. `0.50` for $0.50) |
|
|
737
|
+
| `--expiry <seconds>`| Override the payment expiry in seconds from now (e.g. `3600`) |
|
|
738
|
+
|
|
739
|
+
The command outputs the signed `PAYMENT-SIGNATURE` header value and an MCP config snippet
|
|
740
|
+
that can be used directly with other MCP clients.
|
|
741
|
+
|
|
708
742
|
### Using x402 with MCP servers
|
|
709
743
|
|
|
710
744
|
Pass the `--x402` flag when connecting to a session or running direct commands:
|
|
@@ -1147,23 +1181,25 @@ See [CONTRIBUTING](./CONTRIBUTING.md) for development setup, architecture overvi
|
|
|
1147
1181
|
|
|
1148
1182
|
<!-- Stars and activity as of March 2026. -->
|
|
1149
1183
|
|
|
1150
|
-
| Tool
|
|
1151
|
-
|
|
1152
|
-
| **[apify/mcpc](https://github.com/apify/mcpc)**
|
|
1153
|
-
| [steipete/mcporter](https://github.com/steipete/mcporter)
|
|
1154
|
-
| [IBM/mcp-cli](https://github.com/IBM/mcp-cli)
|
|
1155
|
-
| [f/mcptools](https://github.com/f/mcptools)
|
|
1156
|
-
| [philschmid/mcp-cli](https://github.com/philschmid/mcp-cli)
|
|
1157
|
-
| [adhikasp/mcp-client-cli](https://github.com/adhikasp/mcp-client-cli)
|
|
1158
|
-
| [thellimist/clihub](https://github.com/thellimist/clihub)
|
|
1159
|
-
| [wong2/mcp-cli](https://github.com/wong2/mcp-cli)
|
|
1160
|
-
| [knowsuchagency/mcp2cli](https://github.com/knowsuchagency/mcp2cli)
|
|
1161
|
-
| [mcpshim/mcpshim](https://github.com/mcpshim/mcpshim)
|
|
1162
|
-
| [
|
|
1184
|
+
| Tool | Lang | Stars | Active | Tools | Resources | Prompts | Code mode | Sessions | OAuth | Stdio | HTTP | Tool search | LLM |
|
|
1185
|
+
| ----------------------------------------------------------------------- | ------ | ----: | ------ | ----- | --------- | ------- | --------- | -------- | ----- | ----- | ---- | ----------- | --- |
|
|
1186
|
+
| **[apify/mcpc](https://github.com/apify/mcpc)** | TS | ~350 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | — |
|
|
1187
|
+
| [steipete/mcporter](https://github.com/steipete/mcporter) | TS | ~2.6k | ✅ | ✅ | — | — | ✅ | ✅ | ✅ | ✅ | ✅ | — | — |
|
|
1188
|
+
| [IBM/mcp-cli](https://github.com/IBM/mcp-cli) | Python | ~1.9k | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ |
|
|
1189
|
+
| [f/mcptools](https://github.com/f/mcptools) | Go | ~1.5k | ⚠️ | ✅ | ✅ | ✅ | ✅ | — | — | ✅ | ✅ | — | — |
|
|
1190
|
+
| [philschmid/mcp-cli](https://github.com/philschmid/mcp-cli) | TS | ~950 | ✅ | ✅ | — | — | ✅ | ✅ | — | ✅ | ✅ | ✅ | — |
|
|
1191
|
+
| [adhikasp/mcp-client-cli](https://github.com/adhikasp/mcp-client-cli) | Python | ~670 | ⚠️ | ✅ | ✅ | ✅ | — | — | — | ✅ | — | — | ✅ |
|
|
1192
|
+
| [thellimist/clihub](https://github.com/thellimist/clihub) | Go | ~590 | ✅ | ✅ | — | — | — | — | ✅ | ✅ | ✅ | ✅ | — |
|
|
1193
|
+
| [wong2/mcp-cli](https://github.com/wong2/mcp-cli) | JS | ~420 | ⚠️ | ✅ | ✅ | ✅ | — | — | ✅ | — | ✅ | — | — |
|
|
1194
|
+
| [knowsuchagency/mcp2cli](https://github.com/knowsuchagency/mcp2cli) | Python | ~170 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — |
|
|
1195
|
+
| [mcpshim/mcpshim](https://github.com/mcpshim/mcpshim) | Go | ~46 | ✅ | ✅ | — | — | ✅ | ✅ | ✅ | — | ✅ | ✅ | — |
|
|
1196
|
+
| [evantahler/mcpx](https://github.com/evantahler/mcpx) | TS | ~26 | ✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | — |
|
|
1197
|
+
| [EstebanForge/mcp-cli-ent](https://github.com/EstebanForge/mcp-cli-ent) | Go | ~13 | ✅ | ✅ | — | — | ✅ | ✅ | — | ✅ | ✅ | ✅ | — |
|
|
1163
1198
|
|
|
1164
1199
|
**Legend:** ✅ = supported, ⚠️ = stale (no commits in 3+ months), **LLM** = requires/uses an LLM.
|
|
1165
1200
|
|
|
1166
1201
|
**Notes:**
|
|
1202
|
+
|
|
1167
1203
|
- [thellimist/clihub](https://github.com/thellimist/clihub) is a code generator that compiles MCP tools into standalone CLI binaries, rather than a runtime client ([HN discussion](https://news.ycombinator.com/item?id=47157398)).
|
|
1168
1204
|
- [knowsuchagency/mcp2cli](https://github.com/knowsuchagency/mcp2cli) also supports OpenAPI specs directly and uses a custom TOON encoding for token-efficient tool schemas.
|
|
1169
1205
|
- [IBM/mcp-cli](https://github.com/IBM/mcp-cli) and [mcp-client-cli](https://github.com/adhikasp/mcp-client-cli) integrate an LLM (Ollama, OpenAI, etc.) for chat-style interaction, while the other tools are pure CLI clients.
|
package/dist/bridge/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { initProxy, proxyFetch } from '../lib/proxy.js';
|
|
3
3
|
import { createServer } from 'net';
|
|
4
4
|
import { unlink } from 'fs/promises';
|
|
5
5
|
import { createMcpClient } from '../core/index.js';
|
|
6
6
|
import { KEEPALIVE_INTERVAL_MS } from '../lib/types.js';
|
|
7
7
|
import { createLogger, setVerbose, initFileLogger, closeFileLogger } from '../lib/index.js';
|
|
8
8
|
import { fileExists, getBridgesDir, getSocketPath, ensureDir, cleanupOrphanedLogFiles, isSessionExpiredError, } from '../lib/index.js';
|
|
9
|
-
import { ClientError, NetworkError, isAuthenticationError } from '../lib/index.js';
|
|
9
|
+
import { ClientError, NetworkError, AuthError, isAuthenticationError } from '../lib/index.js';
|
|
10
10
|
import { getSession, loadSessions, updateSession } from '../lib/sessions.js';
|
|
11
11
|
import { OAuthTokenManager } from '../lib/auth/oauth-token-manager.js';
|
|
12
12
|
import { OAuthProvider } from '../lib/auth/oauth-provider.js';
|
|
@@ -33,7 +33,6 @@ class BridgeProcess {
|
|
|
33
33
|
authProvider = null;
|
|
34
34
|
headers = null;
|
|
35
35
|
x402Wallet = null;
|
|
36
|
-
cachedTools = null;
|
|
37
36
|
activeTasks = new Map();
|
|
38
37
|
authCredentialsReceived = null;
|
|
39
38
|
authCredentialsResolver = null;
|
|
@@ -232,8 +231,8 @@ class BridgeProcess {
|
|
|
232
231
|
logger.error('Bridge startup failed, will stay alive briefly for CLI to receive error:', error);
|
|
233
232
|
this.mcpClientReadyRejecter(error);
|
|
234
233
|
const errorMsg = error.message || '';
|
|
235
|
-
if (isSessionExpiredError(errorMsg)
|
|
236
|
-
logger.warn('Session rejected by server (expired
|
|
234
|
+
if (isSessionExpiredError(errorMsg)) {
|
|
235
|
+
logger.warn('Session rejected by server (expired session ID), marking as expired');
|
|
237
236
|
try {
|
|
238
237
|
await updateSession(this.options.sessionName, { status: 'expired' });
|
|
239
238
|
}
|
|
@@ -241,6 +240,15 @@ class BridgeProcess {
|
|
|
241
240
|
logger.error('Failed to mark session as expired:', updateError);
|
|
242
241
|
}
|
|
243
242
|
}
|
|
243
|
+
else if (isAuthenticationError(errorMsg)) {
|
|
244
|
+
logger.warn('Server requires authentication, marking as unauthorized');
|
|
245
|
+
try {
|
|
246
|
+
await updateSession(this.options.sessionName, { status: 'unauthorized' });
|
|
247
|
+
}
|
|
248
|
+
catch (updateError) {
|
|
249
|
+
logger.error('Failed to mark session as unauthorized:', updateError);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
244
252
|
this.setupSignalHandlers();
|
|
245
253
|
setTimeout(() => {
|
|
246
254
|
logger.info('Shutting down after startup failure');
|
|
@@ -313,9 +321,9 @@ class BridgeProcess {
|
|
|
313
321
|
logger.debug('Creating x402 fetch middleware for payment signing');
|
|
314
322
|
const wallet = this.x402Wallet;
|
|
315
323
|
const getToolByName = (name) => {
|
|
316
|
-
return this.
|
|
324
|
+
return this.client?.getCachedTools()?.find((t) => t.name === name);
|
|
317
325
|
};
|
|
318
|
-
customFetch = createX402FetchMiddleware(
|
|
326
|
+
customFetch = createX402FetchMiddleware(proxyFetch, { wallet, getToolByName });
|
|
319
327
|
}
|
|
320
328
|
const clientConfig = {
|
|
321
329
|
clientInfo: { name: 'mcpc', version: mcpcVersion },
|
|
@@ -336,12 +344,13 @@ class BridgeProcess {
|
|
|
336
344
|
...(customFetch && { customFetch }),
|
|
337
345
|
listChanged: {
|
|
338
346
|
tools: {
|
|
339
|
-
autoRefresh:
|
|
340
|
-
onChanged: (
|
|
341
|
-
logger.debug('Tools list changed
|
|
342
|
-
if (
|
|
343
|
-
this.
|
|
344
|
-
|
|
347
|
+
autoRefresh: false,
|
|
348
|
+
onChanged: () => {
|
|
349
|
+
logger.debug('Tools list changed notification received, refreshing all tools...');
|
|
350
|
+
if (this.client) {
|
|
351
|
+
this.client.listAllTools({ refreshCache: true }).catch((err) => {
|
|
352
|
+
logger.warn('Failed to refresh tools cache:', err);
|
|
353
|
+
});
|
|
345
354
|
}
|
|
346
355
|
this.broadcastNotification('tools/list_changed');
|
|
347
356
|
this.updateNotificationTimestamp('tools').catch((err) => {
|
|
@@ -401,17 +410,10 @@ class BridgeProcess {
|
|
|
401
410
|
logger.info(`MCP-Session-Id saved for resumption: ${newMcpSessionId}`);
|
|
402
411
|
}
|
|
403
412
|
await updateSession(this.options.sessionName, sessionUpdate);
|
|
404
|
-
if (
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
this.cachedTools = toolsResult.tools;
|
|
409
|
-
logger.debug(`Pre-populated tools cache (${this.cachedTools.length} tools) for x402`);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
catch (error) {
|
|
413
|
-
logger.warn('Failed to pre-populate tools cache for x402:', error);
|
|
414
|
-
}
|
|
413
|
+
if (serverDetails.capabilities?.tools) {
|
|
414
|
+
await this.client.listAllTools({ refreshCache: true }).catch((err) => {
|
|
415
|
+
logger.warn('Failed to pre-populate tools cache:', err);
|
|
416
|
+
});
|
|
415
417
|
}
|
|
416
418
|
}
|
|
417
419
|
async startProxyServer() {
|
|
@@ -445,9 +447,9 @@ class BridgeProcess {
|
|
|
445
447
|
startKeepalive() {
|
|
446
448
|
logger.debug(`Starting keepalive ping every ${KEEPALIVE_INTERVAL_MS / 1000}s`);
|
|
447
449
|
this.keepaliveInterval = setInterval(() => {
|
|
448
|
-
this.sendKeepalivePing().catch((error) => {
|
|
450
|
+
this.sendKeepalivePing().catch(async (error) => {
|
|
449
451
|
logger.error('Keepalive ping failed:', error);
|
|
450
|
-
this.handlePossibleExpiration(error);
|
|
452
|
+
await this.handlePossibleExpiration(error);
|
|
451
453
|
});
|
|
452
454
|
}, KEEPALIVE_INTERVAL_MS);
|
|
453
455
|
this.keepaliveInterval.unref();
|
|
@@ -471,20 +473,27 @@ class BridgeProcess {
|
|
|
471
473
|
logger.error('Failed to update lastSeenAt:', error);
|
|
472
474
|
}
|
|
473
475
|
}
|
|
474
|
-
handlePossibleExpiration(error) {
|
|
476
|
+
async handlePossibleExpiration(error) {
|
|
477
|
+
let status = null;
|
|
475
478
|
if (isSessionExpiredError(error.message)) {
|
|
476
479
|
logger.warn('Session appears to be expired, marking as expired and shutting down');
|
|
477
|
-
|
|
478
|
-
logger.error('Failed to mark session as expired:', e);
|
|
479
|
-
process.exit(1);
|
|
480
|
-
});
|
|
480
|
+
status = 'expired';
|
|
481
481
|
}
|
|
482
482
|
else if (isAuthenticationError(error.message)) {
|
|
483
483
|
logger.warn('Authentication rejected, marking session as unauthorized and shutting down');
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
484
|
+
status = 'unauthorized';
|
|
485
|
+
}
|
|
486
|
+
if (status) {
|
|
487
|
+
try {
|
|
488
|
+
await updateSession(this.options.sessionName, { status });
|
|
489
|
+
logger.info(`Session ${this.options.sessionName} marked as ${status}`);
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
logger.error('Failed to update session status:', e);
|
|
493
|
+
}
|
|
494
|
+
setTimeout(() => {
|
|
495
|
+
this.shutdown().catch(() => process.exit(1));
|
|
496
|
+
}, 100);
|
|
488
497
|
}
|
|
489
498
|
}
|
|
490
499
|
async markSessionStatusAndExit(status) {
|
|
@@ -641,7 +650,7 @@ class BridgeProcess {
|
|
|
641
650
|
const params = message.params;
|
|
642
651
|
if (params.useTask && this.client.supportsTasksForToolCall()) {
|
|
643
652
|
if (params.detach) {
|
|
644
|
-
const taskUpdate = await this.client.callToolDetached(params.name, params.arguments);
|
|
653
|
+
const taskUpdate = await this.client.callToolDetached(params.name, params.arguments, params._meta);
|
|
645
654
|
this.activeTasks.set(taskUpdate.taskId, {
|
|
646
655
|
taskId: taskUpdate.taskId,
|
|
647
656
|
status: taskUpdate.status,
|
|
@@ -675,7 +684,7 @@ class BridgeProcess {
|
|
|
675
684
|
onUpdate(update);
|
|
676
685
|
}, params.name);
|
|
677
686
|
try {
|
|
678
|
-
result = await this.client.callToolWithTask(params.name, params.arguments, wrappedOnUpdate);
|
|
687
|
+
result = await this.client.callToolWithTask(params.name, params.arguments, wrappedOnUpdate, params._meta);
|
|
679
688
|
}
|
|
680
689
|
finally {
|
|
681
690
|
for (const [tid, task] of this.activeTasks) {
|
|
@@ -693,7 +702,7 @@ class BridgeProcess {
|
|
|
693
702
|
}
|
|
694
703
|
}
|
|
695
704
|
else {
|
|
696
|
-
result = await this.client.callTool(params.name, params.arguments);
|
|
705
|
+
result = await this.client.callTool(params.name, params.arguments, params._meta);
|
|
697
706
|
}
|
|
698
707
|
break;
|
|
699
708
|
}
|
|
@@ -782,6 +791,11 @@ class BridgeProcess {
|
|
|
782
791
|
}
|
|
783
792
|
break;
|
|
784
793
|
}
|
|
794
|
+
case 'listAllTools': {
|
|
795
|
+
const params = message.params;
|
|
796
|
+
result = await this.client.listAllTools(params);
|
|
797
|
+
break;
|
|
798
|
+
}
|
|
785
799
|
default:
|
|
786
800
|
throw new ClientError(`Unknown MCP method: ${message.method}`);
|
|
787
801
|
}
|
|
@@ -795,8 +809,8 @@ class BridgeProcess {
|
|
|
795
809
|
}
|
|
796
810
|
catch (error) {
|
|
797
811
|
logger.error('Failed to forward MCP request to server:', error);
|
|
812
|
+
await this.handlePossibleExpiration(error);
|
|
798
813
|
this.sendError(socket, error, message.id);
|
|
799
|
-
this.handlePossibleExpiration(error);
|
|
800
814
|
}
|
|
801
815
|
}
|
|
802
816
|
async persistActiveTask(taskId, toolName) {
|
|
@@ -848,7 +862,13 @@ class BridgeProcess {
|
|
|
848
862
|
const message = {
|
|
849
863
|
type: 'response',
|
|
850
864
|
error: {
|
|
851
|
-
code: error instanceof ClientError
|
|
865
|
+
code: error instanceof ClientError
|
|
866
|
+
? 1
|
|
867
|
+
: error instanceof NetworkError
|
|
868
|
+
? 3
|
|
869
|
+
: error instanceof AuthError
|
|
870
|
+
? 4
|
|
871
|
+
: 2,
|
|
852
872
|
message: error.message,
|
|
853
873
|
},
|
|
854
874
|
};
|
|
@@ -966,7 +986,7 @@ async function main() {
|
|
|
966
986
|
}
|
|
967
987
|
const x402 = args.includes('--x402');
|
|
968
988
|
const insecure = args.includes('--insecure');
|
|
969
|
-
|
|
989
|
+
initProxy({ insecure });
|
|
970
990
|
try {
|
|
971
991
|
const bridgeOptions = {
|
|
972
992
|
sessionName,
|