@apify/mcpc 0.1.11-beta.1 → 0.1.11-beta.2

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 (68) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +133 -47
  3. package/dist/bridge/index.js +78 -4
  4. package/dist/bridge/index.js.map +1 -1
  5. package/dist/cli/commands/index.d.ts +1 -0
  6. package/dist/cli/commands/index.d.ts.map +1 -1
  7. package/dist/cli/commands/index.js +1 -0
  8. package/dist/cli/commands/index.js.map +1 -1
  9. package/dist/cli/commands/sessions.d.ts +1 -0
  10. package/dist/cli/commands/sessions.d.ts.map +1 -1
  11. package/dist/cli/commands/sessions.js +15 -0
  12. package/dist/cli/commands/sessions.js.map +1 -1
  13. package/dist/cli/commands/x402.d.ts +2 -0
  14. package/dist/cli/commands/x402.d.ts.map +1 -0
  15. package/dist/cli/commands/x402.js +203 -0
  16. package/dist/cli/commands/x402.js.map +1 -0
  17. package/dist/cli/helpers.d.ts +1 -0
  18. package/dist/cli/helpers.d.ts.map +1 -1
  19. package/dist/cli/helpers.js +12 -0
  20. package/dist/cli/helpers.js.map +1 -1
  21. package/dist/cli/index.js +18 -0
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/parser.d.ts +1 -0
  24. package/dist/cli/parser.d.ts.map +1 -1
  25. package/dist/cli/parser.js +4 -0
  26. package/dist/cli/parser.js.map +1 -1
  27. package/dist/core/factory.d.ts +2 -0
  28. package/dist/core/factory.d.ts.map +1 -1
  29. package/dist/core/factory.js +4 -0
  30. package/dist/core/factory.js.map +1 -1
  31. package/dist/core/transports.d.ts +3 -2
  32. package/dist/core/transports.d.ts.map +1 -1
  33. package/dist/core/transports.js +4 -0
  34. package/dist/core/transports.js.map +1 -1
  35. package/dist/lib/auth/keychain.d.ts.map +1 -1
  36. package/dist/lib/auth/keychain.js +85 -67
  37. package/dist/lib/auth/keychain.js.map +1 -1
  38. package/dist/lib/auth/profiles.d.ts.map +1 -1
  39. package/dist/lib/auth/profiles.js.map +1 -1
  40. package/dist/lib/bridge-client.d.ts +2 -1
  41. package/dist/lib/bridge-client.d.ts.map +1 -1
  42. package/dist/lib/bridge-client.js +6 -0
  43. package/dist/lib/bridge-client.js.map +1 -1
  44. package/dist/lib/bridge-manager.d.ts +1 -0
  45. package/dist/lib/bridge-manager.d.ts.map +1 -1
  46. package/dist/lib/bridge-manager.js +33 -1
  47. package/dist/lib/bridge-manager.js.map +1 -1
  48. package/dist/lib/sessions.d.ts.map +1 -1
  49. package/dist/lib/sessions.js.map +1 -1
  50. package/dist/lib/types.d.ts +16 -1
  51. package/dist/lib/types.d.ts.map +1 -1
  52. package/dist/lib/utils.d.ts +1 -0
  53. package/dist/lib/utils.d.ts.map +1 -1
  54. package/dist/lib/utils.js +3 -0
  55. package/dist/lib/utils.js.map +1 -1
  56. package/dist/lib/wallets.d.ts +5 -0
  57. package/dist/lib/wallets.d.ts.map +1 -0
  58. package/dist/lib/wallets.js +66 -0
  59. package/dist/lib/wallets.js.map +1 -0
  60. package/dist/lib/x402/fetch-middleware.d.ts +9 -0
  61. package/dist/lib/x402/fetch-middleware.d.ts.map +1 -0
  62. package/dist/lib/x402/fetch-middleware.js +133 -0
  63. package/dist/lib/x402/fetch-middleware.js.map +1 -0
  64. package/dist/lib/x402/signer.d.ts +48 -0
  65. package/dist/lib/x402/signer.d.ts.map +1 -0
  66. package/dist/lib/x402/signer.js +139 -0
  67. package/dist/lib/x402/signer.js.map +1 -0
  68. package/package.json +3 -2
package/CHANGELOG.md CHANGED
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Changed
11
+ - OS keychain now falls back to `~/.mcpc/credentials.json` (mode 0600) when no keyring daemon is available (e.g. headless Linux servers, containers)
12
+
10
13
  ## [0.1.10] - 2026-03-01
11
14
 
12
15
  ### Added
package/README.md CHANGED
@@ -10,6 +10,7 @@ After all, UNIX-compatible shell script is THE most universal coding language.
10
10
  ![mcpc screenshot](https://raw.githubusercontent.com/apify/mcpc/main/docs/images/mcpc-demo.gif)
11
11
 
12
12
  **Key features:**
13
+
13
14
  - 🌎 **Compatible** - Works with any MCP server over Streamable HTTP or stdio.
14
15
  - 🔄 **Persistent sessions** - Keep multiple server connections alive simultaneously.
15
16
  - 🔧 **Strong MCP support** - Instructions, tools, resources, prompts, dynamic discovery.
@@ -17,7 +18,7 @@ After all, UNIX-compatible shell script is THE most universal coding language.
17
18
  - ðŸĪ– **AI sandboxing** - MCP proxy server to securely access authenticated sessions from AI-generated code.
18
19
  - 🔒 **Secure** - Full OAuth 2.1 support, OS keychain for credentials storage.
19
20
  - ðŸŠķ **Lightweight** - Minimal dependencies, works on Mac/Win/Linux, doesn't use LLMs on its own.
20
-
21
+ - ðŸ’ļ **[Agentic payments (x402)](#agentic-payments-x402)** - Experimental support for the [x402](https://www.x402.org/) payment protocol, enabling AI agents to pay for MCP tool calls with USDC on [Base](https://www.base.org/).
21
22
 
22
23
  ## Table of contents
23
24
 
@@ -31,6 +32,7 @@ After all, UNIX-compatible shell script is THE most universal coding language.
31
32
  - [Authentication](#authentication)
32
33
  - [MCP proxy](#mcp-proxy)
33
34
  - [AI agents](#ai-agents)
35
+ - [Agentic payments (x402)](#agentic-payments-x402)
34
36
  - [MCP support](#mcp-support)
35
37
  - [Configuration](#configuration)
36
38
  - [Security](#security)
@@ -48,29 +50,29 @@ npm install -g @apify/mcpc
48
50
  ```
49
51
 
50
52
  **Linux users:** `mcpc` uses the OS keychain for secure credential storage via the
51
- [Secret Service API](https://specifications.freedesktop.org/secret-service/). Two things are required:
53
+ [Secret Service API](https://specifications.freedesktop.org/secret-service/).
54
+ On desktop systems (GNOME, KDE) this works out of the box. On headless/server/CI environments
55
+ without a keyring daemon, `mcpc` automatically falls back to a file-based credential store
56
+ (`~/.mcpc/credentials`, mode `0600`).
52
57
 
53
- 1. **`libsecret`** — the shared library (client side):
54
- ```bash
55
- # Debian/Ubuntu
56
- sudo apt-get install libsecret-1-0
58
+ To use the OS keychain on a headless system, install `libsecret` and a secret service daemon:
57
59
 
58
- # Fedora/RHEL/CentOS
59
- sudo dnf install libsecret
60
+ ```bash
61
+ # Debian/Ubuntu
62
+ sudo apt-get install libsecret-1-0 gnome-keyring
60
63
 
61
- # Arch Linux
62
- sudo pacman -S libsecret
63
- ```
64
+ # Fedora/RHEL/CentOS
65
+ sudo dnf install libsecret gnome-keyring
64
66
 
65
- 2. **A running secret service daemon** — on desktop systems (GNOME, KDE) this is already provided
66
- by gnome-keyring or KWallet. On headless/server/CI environments you need to install and start one:
67
- ```bash
68
- # Debian/Ubuntu
69
- sudo apt-get install gnome-keyring
67
+ # Arch Linux
68
+ sudo pacman -S libsecret gnome-keyring
69
+ ```
70
70
 
71
- # Then start it (e.g. in CI):
72
- dbus-run-session -- bash -c "echo -n 'password' | gnome-keyring-daemon --unlock && your-command"
73
- ```
71
+ And then run `mcpc` as follows:
72
+
73
+ ```
74
+ dbus-run-session -- bash -c "echo -n 'password' | gnome-keyring-daemon --unlock && mcpc ..."
75
+ ```
74
76
 
75
77
  ## Quickstart
76
78
 
@@ -117,6 +119,7 @@ Options:
117
119
  --timeout <seconds> Request timeout in seconds (default: 300)
118
120
  --proxy <[host:]port> Start proxy MCP server for session (with "connect" command)
119
121
  --proxy-bearer-token <token> Require authentication for access to proxy server
122
+ --x402 Enable x402 auto-payment using the configured wallet
120
123
  --clean[=types] Clean up mcpc data (types: sessions, logs, profiles, all)
121
124
  -h, --help Display general help
122
125
 
@@ -148,7 +151,14 @@ MCP server commands:
148
151
  resources-templates-list
149
152
  logging-set-level <level>
150
153
  ping
151
-
154
+
155
+ x402 payment commands (no target needed):
156
+ x402 init Create a new x402 wallet
157
+ x402 import <key> Import wallet from private key
158
+ x402 info Show wallet info
159
+ x402 sign -r <base64> Sign payment from PAYMENT-REQUIRED header
160
+ x402 remove Remove the wallet
161
+
152
162
  Run "mcpc" without <target> to show available sessions and profiles.
153
163
  ```
154
164
 
@@ -181,6 +191,7 @@ To connect and interact with an MCP server, you need to specify a `<target>`, wh
181
191
  connects, and enables you to interact with it.
182
192
 
183
193
  **URL handling:**
194
+
184
195
  - URLs without a scheme (e.g. `mcp.apify.com`) default to `https://`
185
196
  - `localhost` and `127.0.0.1` addresses without a scheme default to `http://` (for local dev/proxy servers)
186
197
  - To override the default, specify the scheme explicitly (e.g. `http://example.com`)
@@ -229,6 +240,7 @@ cat args.json | mcpc <target> tools-call <tool-name>
229
240
  ```
230
241
 
231
242
  **Rules:**
243
+
232
244
  - All arguments use `:=` syntax: `key:=value`
233
245
  - Values are auto-parsed: valid JSON becomes that type, otherwise treated as string
234
246
  - `count:=10` → number `10`
@@ -257,6 +269,7 @@ echo "{\"query\": \"${QUERY}\", \"limit\": 10}" | mcpc @server tools-call search
257
269
  ```
258
270
 
259
271
  **Common pitfall:** Don't put spaces around `:=` - it won't work:
272
+
260
273
  ```bash
261
274
  # Wrong - spaces around :=
262
275
  mcpc @server tools-call search query := "hello world"
@@ -329,7 +342,7 @@ Still, sessions can fail due to network disconnects, bridge process crash, or se
329
342
  **Session states:**
330
343
 
331
344
  | State | Meaning |
332
- |------------------|-----------------------------------------------------------------------------------------------|
345
+ | ---------------- | --------------------------------------------------------------------------------------------- |
333
346
  | ðŸŸĒ **`live`** | Bridge process is running; server might or might not be operational |
334
347
  | ðŸŸĄ **`crashed`** | Bridge process crashed or was killed; will auto-restart on next use |
335
348
  | ðŸ”ī **`expired`** | Server rejected the session (auth failed, session ID invalid); requires `close` and reconnect |
@@ -365,7 +378,6 @@ and opens new connection with new `MCP-Session-Id`, by running:
365
378
  mcpc @apify restart
366
379
  ```
367
380
 
368
-
369
381
  ## Authentication
370
382
 
371
383
  `mcpc` supports all standard [MCP authorization methods](https://modelcontextprotocol.io/specification/latest/basic/authorization).
@@ -404,8 +416,8 @@ mcpc @apify tools-list
404
416
 
405
417
  ### OAuth profiles
406
418
 
407
- For OAuth-enabled remote MCP servers, `mcpc` implements the full OAuth 2.1 flow with PKCE,
408
- including `WWW-Authenticate` header discovery, server metadata discovery, client ID metadata documents,
419
+ For OAuth-enabled remote MCP servers, `mcpc` implements the full OAuth 2.1 flow with PKCE,
420
+ including `WWW-Authenticate` header discovery, server metadata discovery, client ID metadata documents,
409
421
  dynamic client registration, and automatic token refresh.
410
422
 
411
423
  The OAuth authentication **always** needs to be initiated by the user calling the `login` command,
@@ -413,11 +425,13 @@ which opens a web browser with login screen. `mcpc` never opens the web browser
413
425
 
414
426
  The OAuth credentials to specific servers are securely stored as **authentication profiles** - reusable
415
427
  credentials that allow you to:
428
+
416
429
  - Authenticate once, use credentials across multiple commands or sessions
417
430
  - Use different accounts (profiles) with the same server
418
431
  - Manage credentials independently from sessions
419
432
 
420
433
  Key concepts:
434
+
421
435
  - **Authentication profile**: Named set of OAuth credentials for a specific server (stored in `~/.mcpc/profiles.json` + OS keychain)
422
436
  - **Session**: Active connection to a server that may reference an authentication profile (stored in `~/.mcpc/sessions.json`)
423
437
  - **Default profile**: When `--profile` is not specified, `mcpc` uses the authentication profile named `default`
@@ -456,7 +470,6 @@ When multiple authentication methods are available, `mcpc` uses this precedence
456
470
  3. **Config file headers** - Headers from `--config` file for the server
457
471
  4. **No authentication** - Attempts unauthenticated connection
458
472
 
459
-
460
473
  `mcpc` automatically handles authentication based on whether you specify a profile:
461
474
 
462
475
  **When `--profile <name>` is specified:**
@@ -478,6 +491,7 @@ When multiple authentication methods are available, `mcpc` uses this precedence
478
491
  On failure, the error message includes instructions on how to login and save the profile, so you know what to do.
479
492
 
480
493
  This flow ensures:
494
+
481
495
  - You only authenticate when necessary
482
496
  - Credentials are never silently mixed up (personal → work) or downgraded (authenticated → unauthenticated)
483
497
  - You can mix authenticated sessions (with named profiles) and public access on the same server
@@ -500,7 +514,6 @@ mcpc mcp.apify.com connect @apify-personal
500
514
  mcpc mcp.apify.com\?tools=docs tools-list
501
515
  ```
502
516
 
503
-
504
517
  ## MCP proxy
505
518
 
506
519
  For stronger isolation, `mcpc` can expose an MCP session under a new local proxy MCP server using the `--proxy` option.
@@ -534,7 +547,7 @@ mcpc localhost:8081 connect @sandboxed2 --header "Authorization: Bearer secret12
534
547
  **Proxy options for `connect` command:**
535
548
 
536
549
  | Option | Description |
537
- |--------------------------------|--------------------------------------------------------------------------------|
550
+ | ------------------------------ | ------------------------------------------------------------------------------ |
538
551
  | `--proxy [host:]port` | Start proxy MCP server. Default host: `127.0.0.1` (localhost only) |
539
552
  | `--proxy-bearer-token <token>` | Requires `Authorization: Bearer <token>` header to access the proxy MCP server |
540
553
 
@@ -565,7 +578,6 @@ mcpc
565
578
  # @relay → https://mcp.apify.com (HTTP, OAuth: default) [proxy: 127.0.0.1:8080]
566
579
  ```
567
580
 
568
-
569
581
  ## AI agents
570
582
 
571
583
  `mcpc` is designed for CLI-enabled AI agents like Claude Code or Codex CLI, supporting both
@@ -635,6 +647,7 @@ mcpc @apify tools-call search-actors --schema expected.json keywords:="test"
635
647
  ```
636
648
 
637
649
  Available schema validation modes (`--schema-mode`):
650
+
638
651
  - `compatible` (default)
639
652
  - Input schema: new optional fields OK, required fields must have the same type.
640
653
  - Output schema: new fields OK, removed required fields cause error.
@@ -668,6 +681,73 @@ To help Claude Code use `mcpc`, you can install this [Claude skill](./docs/claud
668
681
 
669
682
  <!-- TODO: Add also AGENTS.md, GitHub skills etc. -->
670
683
 
684
+ ## Agentic payments (x402)
685
+
686
+ > ⚠ïļ **Experimental.** This feature is under active development and may change.
687
+
688
+ `mcpc` has experimental support for the [x402 payment protocol](https://www.x402.org/),
689
+ which enables AI agents to autonomously pay for MCP tool calls using cryptocurrency.
690
+ When an MCP server charges for a tool call (HTTP 402), `mcpc` automatically signs a USDC payment
691
+ on the [Base](https://base.org/) blockchain and retries the request — no human intervention needed.
692
+
693
+ This is entirely **opt-in**: existing functionality is unaffected unless you explicitly pass the `--x402` flag.
694
+
695
+ ### How it works
696
+
697
+ 1. **Server returns HTTP 402** with a `PAYMENT-REQUIRED` header describing the price and payment details.
698
+ 2. `mcpc` parses the header, signs an [EIP-3009](https://eips.ethereum.org/EIPS/eip-3009) `TransferWithAuthorization` using your local wallet.
699
+ 3. `mcpc` retries the request with a `PAYMENT-SIGNATURE` header containing the signed payment.
700
+ 4. The server verifies the signature and fulfills the request.
701
+
702
+ For tools that advertise pricing in their `_meta.x402` metadata, `mcpc` can **proactively sign** payments
703
+ on the first request, avoiding the 402 round-trip entirely.
704
+
705
+ ### Wallet setup
706
+
707
+ `mcpc` stores a single wallet in `~/.mcpc/wallets.json` (file permissions `0600`).
708
+ You need to create or import a wallet before using x402 payments.
709
+
710
+ ```bash
711
+ # Create a new wallet (generates a random private key)
712
+ mcpc x402 init
713
+
714
+ # Or import an existing wallet from a private key
715
+ mcpc x402 import <private-key>
716
+
717
+ # Show wallet address and creation date
718
+ mcpc x402 info
719
+
720
+ # Remove the wallet
721
+ mcpc x402 remove
722
+ ```
723
+
724
+ After creating a wallet, **fund it with USDC on Base** (mainnet or Sepolia testnet) to enable payments.
725
+
726
+ ### Using x402 with MCP servers
727
+
728
+ Pass the `--x402` flag when connecting to a session or running direct commands:
729
+
730
+ ```bash
731
+ # Create a session with x402 payment support
732
+ mcpc mcp.apify.com connect @apify --x402
733
+
734
+ # The session now automatically handles 402 responses
735
+ mcpc @apify tools-call expensive-tool query:="hello"
736
+
737
+ # Restart a session with x402 enabled
738
+ mcpc @apify restart --x402
739
+ ```
740
+
741
+ When `--x402` is active, a fetch middleware wraps all HTTP requests to the MCP server.
742
+ If any request returns HTTP 402, the middleware transparently signs and retries.
743
+
744
+ ### Supported networks
745
+
746
+ | Network | Status |
747
+ | -------------------- | ------------ |
748
+ | Base Mainnet | ✅ Supported |
749
+ | Base Sepolia testnet | ✅ Supported |
750
+
671
751
  ## MCP support
672
752
 
673
753
  `mcpc` is built on the official [MCP SDK for TypeScript](https://github.com/modelcontextprotocol/typescript-sdk) and supports most [MCP protocol features](https://modelcontextprotocol.io/specification/latest).
@@ -688,6 +768,7 @@ To help Claude Code use `mcpc`, you can install this [Claude skill](./docs/claud
688
768
  ### MCP session
689
769
 
690
770
  The bridge process manages the full MCP session lifecycle:
771
+
691
772
  - Performs initialization handshake (`initialize` → `initialized`)
692
773
  - Negotiates protocol version and capabilities
693
774
  - Fetches server-provided `instructions`
@@ -698,21 +779,21 @@ The bridge process manages the full MCP session lifecycle:
698
779
 
699
780
  ### MCP feature support
700
781
 
701
- | **Feature** | **Status** |
702
- |:---------------------------------------------------|:-----------------------------------|
703
- | 📖 [**Instructions**](#server-instructions) | ✅ Supported |
704
- | 🔧 [**Tools**](#tools) | ✅ Supported |
705
- | 💎 [**Prompts**](#prompts) | ✅ Supported |
706
- | ðŸ“Ķ [**Resources**](#resources) | ✅ Supported |
707
- | 📝 [**Logging**](#server-logs) | ✅ Supported |
708
- | 🔔 [**Notifications**](#list-change-notifications) | ✅ Supported |
709
- | 📄 [**Pagination**](#pagination) | ✅ Supported |
710
- | 🏓 [**Ping**](#ping) | ✅ Supported |
711
- | âģ **Async tasks** | 🚧 Planned |
712
- | 📁 **Roots** | 🚧 Planned |
713
- | ❓ **Elicitation** | 🚧 Planned |
714
- | ðŸ”Ī **Completion** | 🚧 Planned |
715
- | ðŸĪ– **Sampling** | ❌ Not applicable (no LLM access) |
782
+ | **Feature** | **Status** |
783
+ | :------------------------------------------------- | :-------------------------------- |
784
+ | 📖 [**Instructions**](#server-instructions) | ✅ Supported |
785
+ | 🔧 [**Tools**](#tools) | ✅ Supported |
786
+ | 💎 [**Prompts**](#prompts) | ✅ Supported |
787
+ | ðŸ“Ķ [**Resources**](#resources) | ✅ Supported |
788
+ | 📝 [**Logging**](#server-logs) | ✅ Supported |
789
+ | 🔔 [**Notifications**](#list-change-notifications) | ✅ Supported |
790
+ | 📄 [**Pagination**](#pagination) | ✅ Supported |
791
+ | 🏓 [**Ping**](#ping) | ✅ Supported |
792
+ | âģ **Async tasks** | 🚧 Planned |
793
+ | 📁 **Roots** | 🚧 Planned |
794
+ | ❓ **Elicitation** | 🚧 Planned |
795
+ | ðŸ”Ī **Completion** | 🚧 Planned |
796
+ | ðŸĪ– **Sampling** | ❌ Not applicable (no LLM access) |
716
797
 
717
798
  #### Server instructions
718
799
 
@@ -865,6 +946,7 @@ mcpc @apify ping --json
865
946
  You can configure `mcpc` using a config file, environment variables, or command-line flags.
866
947
 
867
948
  **Precedence** (highest to lowest):
949
+
868
950
  1. Command-line flags (including `--config` option)
869
951
  2. Environment variables
870
952
  3. Built-in defaults
@@ -912,11 +994,13 @@ mcpc --config .vscode/mcp.json apify connect @my-apify
912
994
  **Server configuration properties:**
913
995
 
914
996
  For **Streamable HTTP servers:**
997
+
915
998
  - `url` (required) - MCP server endpoint URL
916
999
  - `headers` (optional) - HTTP headers to include with requests
917
1000
  - `timeout` (optional) - Request timeout in seconds
918
1001
 
919
1002
  For **stdio servers:**
1003
+
920
1004
  - `command` (required) - Command to execute (e.g., `node`, `npx`, `python`)
921
1005
  - `args` (optional) - Array of command arguments
922
1006
  - `env` (optional) - Environment variables for the process
@@ -958,6 +1042,7 @@ Config files support environment variable substitution using `${VAR_NAME}` synta
958
1042
 
959
1043
  - `~/.mcpc/sessions.json` - Active sessions with references to authentication profiles (file-locked for concurrent access)
960
1044
  - `~/.mcpc/profiles.json` - Authentication profiles (OAuth metadata, scopes, expiry)
1045
+ - `~/.mcpc/wallets.json` - x402 wallet data (file permissions `0600`)
961
1046
  - `~/.mcpc/bridges/` - Unix domain socket files for each bridge process
962
1047
  - `~/.mcpc/logs/bridge-*.log` - Log files for each bridge process
963
1048
  - OS keychain - Sensitive credentials (OAuth tokens, bearer tokens, client secrets)
@@ -1000,11 +1085,12 @@ MCP enables arbitrary tool execution and data access - treat servers like you tr
1000
1085
  ### Credential protection
1001
1086
 
1002
1087
  | What | How |
1003
- |------------------------|-------------------------------------------------|
1088
+ | ---------------------- | ----------------------------------------------- |
1004
1089
  | **OAuth tokens** | Stored in OS keychain, never on disk |
1005
1090
  | **HTTP headers** | Stored in OS keychain per-session |
1006
1091
  | **Bridge credentials** | Passed via Unix socket IPC, kept in memory only |
1007
1092
  | **Process arguments** | No secrets visible in `ps aux` |
1093
+ | **x402 private key** | Stored in `wallets.json` (`0600` permissions) |
1008
1094
  | **Config files** | Contain only metadata, never tokens |
1009
1095
  | **File permissions** | `0600` (user-only) for all config files |
1010
1096
 
@@ -1055,20 +1141,22 @@ The main `mcpc` process doesn't save log files, but supports [verbose mode](#ver
1055
1141
  ### Troubleshooting
1056
1142
 
1057
1143
  **"Cannot connect to bridge"**
1144
+
1058
1145
  - Bridge may have crashed. Try: `mcpc @<session-name> tools-list` to restart the bridge
1059
1146
  - Check bridge is running: `ps aux | grep -e 'mcpc-bridge' -e '[m]cpc/dist/bridge'`
1060
1147
  - Check socket exists: `ls ~/.mcpc/bridges/`
1061
1148
 
1062
1149
  **"Session not found"**
1150
+
1063
1151
  - List existing sessions: `mcpc`
1064
1152
  - Create new session if expired: `mcpc @<session-name> close` and `mcpc <target> connect @<session-name>`
1065
1153
 
1066
1154
  **"Authentication failed"**
1155
+
1067
1156
  - List saved OAuth profiles: `mcpc`
1068
1157
  - Re-authenticate: `mcpc <server> login [--profile <name>]`
1069
1158
  - For bearer tokens: provide `--header "Authorization: Bearer ${TOKEN}"` again
1070
1159
 
1071
-
1072
1160
  ## Development
1073
1161
 
1074
1162
  The initial version of `mcpc` was developed and [launched by Jan Curn](https://x.com/jancurn/status/2007144080959291756) of [Apify](https://apify.com)
@@ -1099,8 +1187,6 @@ See [CONTRIBUTING](./CONTRIBUTING.md) for development setup, architecture overvi
1099
1187
  - Other
1100
1188
  - https://github.com/TeamSparkAI/mcpGraph
1101
1189
 
1102
-
1103
1190
  ## License
1104
1191
 
1105
1192
  Apache-2.0 - see [LICENSE](./LICENSE) for details.
1106
-
@@ -15,6 +15,7 @@ import { readKeychainProxyBearerToken } from '../lib/auth/keychain.js';
15
15
  import { createRequire } from 'module';
16
16
  const { version: mcpcVersion } = createRequire(import.meta.url)('../../package.json');
17
17
  import { ProxyServer } from './proxy-server.js';
18
+ import { createX402FetchMiddleware } from '../lib/x402/fetch-middleware.js';
18
19
  setGlobalDispatcher(new EnvHttpProxyAgent());
19
20
  const KEEPALIVE_INTERVAL_MS = 30_000;
20
21
  const logger = createLogger('bridge');
@@ -30,8 +31,12 @@ class BridgeProcess {
30
31
  tokenManager = null;
31
32
  authProvider = null;
32
33
  headers = null;
34
+ x402Wallet = null;
35
+ cachedTools = null;
33
36
  authCredentialsReceived = null;
34
37
  authCredentialsResolver = null;
38
+ x402WalletReceived = null;
39
+ x402WalletResolver = null;
35
40
  mcpClientReady;
36
41
  mcpClientReadyResolver;
37
42
  mcpClientReadyRejecter;
@@ -120,6 +125,18 @@ class BridgeProcess {
120
125
  this.authCredentialsResolver = null;
121
126
  }
122
127
  }
128
+ setX402Wallet(credentials) {
129
+ logger.info(`Received x402 wallet: ${credentials.address}`);
130
+ this.x402Wallet = {
131
+ privateKey: credentials.privateKey,
132
+ address: credentials.address,
133
+ };
134
+ logger.debug('x402 wallet stored in memory');
135
+ if (this.x402WalletResolver) {
136
+ this.x402WalletResolver();
137
+ this.x402WalletResolver = null;
138
+ }
139
+ }
123
140
  async updateTransportAuth() {
124
141
  const config = { ...this.options.serverConfig };
125
142
  if (!config.url) {
@@ -177,16 +194,27 @@ class BridgeProcess {
177
194
  this.runOrphanedLogCleanup();
178
195
  try {
179
196
  await this.createSocketServer();
197
+ const ipcWaiters = [];
180
198
  if (this.options.profileName) {
181
199
  logger.debug(`Waiting for auth credentials (profile: ${this.options.profileName})...`);
182
200
  this.authCredentialsReceived = new Promise((resolve) => {
183
201
  this.authCredentialsResolver = resolve;
184
202
  });
203
+ ipcWaiters.push(this.authCredentialsReceived);
204
+ }
205
+ if (this.options.x402) {
206
+ logger.debug('Waiting for x402 wallet...');
207
+ this.x402WalletReceived = new Promise((resolve) => {
208
+ this.x402WalletResolver = resolve;
209
+ });
210
+ ipcWaiters.push(this.x402WalletReceived);
211
+ }
212
+ if (ipcWaiters.length > 0) {
185
213
  const timeout = new Promise((_, reject) => {
186
- setTimeout(() => reject(new Error('Timeout waiting for auth credentials')), 5000);
214
+ setTimeout(() => reject(new Error('Timeout waiting for IPC credentials')), 5000);
187
215
  });
188
- await Promise.race([this.authCredentialsReceived, timeout]);
189
- logger.debug('Auth credentials received, proceeding with MCP connection');
216
+ await Promise.race([Promise.all(ipcWaiters), timeout]);
217
+ logger.debug('IPC credentials received, proceeding with MCP connection');
190
218
  }
191
219
  try {
192
220
  await this.connectToMcp();
@@ -270,9 +298,19 @@ class BridgeProcess {
270
298
  }
271
299
  logger.debug('Building MCP client config...');
272
300
  logger.debug(` this.authProvider is set: ${!!this.authProvider}`);
301
+ logger.debug(` this.x402Wallet is set: ${!!this.x402Wallet}`);
273
302
  if (this.authProvider) {
274
303
  logger.debug(` authProvider type: ${this.authProvider.constructor.name}`);
275
304
  }
305
+ let customFetch;
306
+ if (this.x402Wallet && serverConfig.url) {
307
+ logger.debug('Creating x402 fetch middleware for payment signing');
308
+ const wallet = this.x402Wallet;
309
+ const getToolByName = (name) => {
310
+ return this.cachedTools?.find((t) => t.name === name);
311
+ };
312
+ customFetch = createX402FetchMiddleware(fetch, { wallet, getToolByName });
313
+ }
276
314
  const clientConfig = {
277
315
  clientInfo: { name: 'mcpc', version: mcpcVersion },
278
316
  serverConfig,
@@ -282,11 +320,16 @@ class BridgeProcess {
282
320
  },
283
321
  ...(this.authProvider && { authProvider: this.authProvider }),
284
322
  ...(this.options.mcpSessionId && { mcpSessionId: this.options.mcpSessionId }),
323
+ ...(customFetch && { customFetch }),
285
324
  listChanged: {
286
325
  tools: {
287
326
  autoRefresh: true,
288
327
  onChanged: (error, tools) => {
289
328
  logger.debug('Tools list changed', { error, count: tools?.length });
329
+ if (tools) {
330
+ this.cachedTools = tools;
331
+ logger.debug(`Updated cached tools list (${tools.length} tools)`);
332
+ }
290
333
  this.broadcastNotification('tools/list_changed');
291
334
  this.updateNotificationTimestamp('tools').catch((err) => {
292
335
  logger.warn('Failed to update tools notification timestamp:', err);
@@ -340,6 +383,18 @@ class BridgeProcess {
340
383
  logger.info(`MCP-Session-Id saved for resumption: ${newMcpSessionId}`);
341
384
  }
342
385
  await updateSession(this.options.sessionName, sessionUpdate);
386
+ if (this.x402Wallet) {
387
+ try {
388
+ const toolsResult = await this.client.listTools();
389
+ if (toolsResult.tools) {
390
+ this.cachedTools = toolsResult.tools;
391
+ logger.debug(`Pre-populated tools cache (${this.cachedTools.length} tools) for x402`);
392
+ }
393
+ }
394
+ catch (error) {
395
+ logger.warn('Failed to pre-populate tools cache for x402:', error);
396
+ }
397
+ }
343
398
  }
344
399
  async startProxyServer() {
345
400
  if (!this.options.proxyConfig || !this.client) {
@@ -499,6 +554,21 @@ class BridgeProcess {
499
554
  throw new ClientError('Missing authCredentials in set-auth-credentials message');
500
555
  }
501
556
  break;
557
+ case 'set-x402-wallet':
558
+ if (message.x402Wallet) {
559
+ this.setX402Wallet(message.x402Wallet);
560
+ if (message.id) {
561
+ this.sendResponse(socket, {
562
+ type: 'response',
563
+ id: message.id,
564
+ result: { success: true },
565
+ });
566
+ }
567
+ }
568
+ else {
569
+ throw new ClientError('Missing x402Wallet in set-x402-wallet message');
570
+ }
571
+ break;
502
572
  default:
503
573
  throw new ClientError(`Unknown message type: ${message.type}`);
504
574
  }
@@ -696,7 +766,7 @@ class BridgeProcess {
696
766
  async function main() {
697
767
  const args = process.argv.slice(2);
698
768
  if (args.length < 2) {
699
- console.error('Usage: mcpc-bridge <sessionName> <transportConfigJson> [--verbose] [--profile <name>] [--proxy-host <host>] [--proxy-port <port>] [--mcp-session-id <id>]');
769
+ console.error('Usage: mcpc-bridge <sessionName> <transportConfigJson> [--verbose] [--profile <name>] [--proxy-host <host>] [--proxy-port <port>] [--mcp-session-id <id>] [--x402]');
700
770
  process.exit(1);
701
771
  }
702
772
  const sessionName = args[0];
@@ -722,6 +792,7 @@ async function main() {
722
792
  if (mcpSessionIdIndex !== -1 && args[mcpSessionIdIndex + 1]) {
723
793
  mcpSessionId = args[mcpSessionIdIndex + 1];
724
794
  }
795
+ const x402 = args.includes('--x402');
725
796
  try {
726
797
  const bridgeOptions = {
727
798
  sessionName,
@@ -737,6 +808,9 @@ async function main() {
737
808
  if (mcpSessionId) {
738
809
  bridgeOptions.mcpSessionId = mcpSessionId;
739
810
  }
811
+ if (x402) {
812
+ bridgeOptions.x402 = true;
813
+ }
740
814
  const bridge = new BridgeProcess(bridgeOptions);
741
815
  await bridge.start();
742
816
  }