@apify/mcpc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/.claude/settings.local.json +36 -0
  2. package/.eslintrc.json +44 -0
  3. package/.idea/codeStyles/Project.xml +60 -0
  4. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +11 -0
  6. package/.idea/prettier.xml +6 -0
  7. package/.idea/vcs.xml +6 -0
  8. package/.idea/workspace.xml +572 -0
  9. package/LICENSE +201 -0
  10. package/PHASE1-SUMMARY.md +269 -0
  11. package/PUBLISHING.md +111 -0
  12. package/README.md +1056 -0
  13. package/TESTING.md +212 -0
  14. package/TODOs.md +94 -0
  15. package/bin/mcpc +9 -0
  16. package/bin/mcpc-bridge +9 -0
  17. package/dist/bridge/index.d.ts +3 -0
  18. package/dist/bridge/index.d.ts.map +1 -0
  19. package/dist/bridge/index.js +587 -0
  20. package/dist/bridge/index.js.map +1 -0
  21. package/dist/cli/commands/auth.d.ts +9 -0
  22. package/dist/cli/commands/auth.d.ts.map +1 -0
  23. package/dist/cli/commands/auth.js +81 -0
  24. package/dist/cli/commands/auth.js.map +1 -0
  25. package/dist/cli/commands/clean.d.ts +11 -0
  26. package/dist/cli/commands/clean.d.ts.map +1 -0
  27. package/dist/cli/commands/clean.js +224 -0
  28. package/dist/cli/commands/clean.js.map +1 -0
  29. package/dist/cli/commands/index.d.ts +8 -0
  30. package/dist/cli/commands/index.d.ts.map +1 -0
  31. package/dist/cli/commands/index.js +8 -0
  32. package/dist/cli/commands/index.js.map +1 -0
  33. package/dist/cli/commands/logging.d.ts +3 -0
  34. package/dist/cli/commands/logging.d.ts.map +1 -0
  35. package/dist/cli/commands/logging.js +22 -0
  36. package/dist/cli/commands/logging.js.map +1 -0
  37. package/dist/cli/commands/prompts.d.ts +6 -0
  38. package/dist/cli/commands/prompts.d.ts.map +1 -0
  39. package/dist/cli/commands/prompts.js +27 -0
  40. package/dist/cli/commands/prompts.js.map +1 -0
  41. package/dist/cli/commands/resources.d.ts +11 -0
  42. package/dist/cli/commands/resources.d.ts.map +1 -0
  43. package/dist/cli/commands/resources.js +70 -0
  44. package/dist/cli/commands/resources.js.map +1 -0
  45. package/dist/cli/commands/sessions.d.ts +28 -0
  46. package/dist/cli/commands/sessions.d.ts.map +1 -0
  47. package/dist/cli/commands/sessions.js +356 -0
  48. package/dist/cli/commands/sessions.js.map +1 -0
  49. package/dist/cli/commands/tools.d.ts +8 -0
  50. package/dist/cli/commands/tools.d.ts.map +1 -0
  51. package/dist/cli/commands/tools.js +54 -0
  52. package/dist/cli/commands/tools.js.map +1 -0
  53. package/dist/cli/commands/utilities.d.ts +3 -0
  54. package/dist/cli/commands/utilities.d.ts.map +1 -0
  55. package/dist/cli/commands/utilities.js +20 -0
  56. package/dist/cli/commands/utilities.js.map +1 -0
  57. package/dist/cli/helpers.d.ts +18 -0
  58. package/dist/cli/helpers.d.ts.map +1 -0
  59. package/dist/cli/helpers.js +165 -0
  60. package/dist/cli/helpers.js.map +1 -0
  61. package/dist/cli/index.d.ts +3 -0
  62. package/dist/cli/index.d.ts.map +1 -0
  63. package/dist/cli/index.js +326 -0
  64. package/dist/cli/index.js.map +1 -0
  65. package/dist/cli/output.d.ts +18 -0
  66. package/dist/cli/output.d.ts.map +1 -0
  67. package/dist/cli/output.js +221 -0
  68. package/dist/cli/output.js.map +1 -0
  69. package/dist/cli/parser.d.ts +19 -0
  70. package/dist/cli/parser.d.ts.map +1 -0
  71. package/dist/cli/parser.js +168 -0
  72. package/dist/cli/parser.js.map +1 -0
  73. package/dist/cli/shell-parser.d.ts +5 -0
  74. package/dist/cli/shell-parser.d.ts.map +1 -0
  75. package/dist/cli/shell-parser.js +38 -0
  76. package/dist/cli/shell-parser.js.map +1 -0
  77. package/dist/cli/shell.d.ts +2 -0
  78. package/dist/cli/shell.d.ts.map +1 -0
  79. package/dist/cli/shell.js +277 -0
  80. package/dist/cli/shell.js.map +1 -0
  81. package/dist/cli/tool-result.d.ts +2 -0
  82. package/dist/cli/tool-result.d.ts.map +1 -0
  83. package/dist/cli/tool-result.js +19 -0
  84. package/dist/cli/tool-result.js.map +1 -0
  85. package/dist/core/factory.d.ts +19 -0
  86. package/dist/core/factory.d.ts.map +1 -0
  87. package/dist/core/factory.js +55 -0
  88. package/dist/core/factory.js.map +1 -0
  89. package/dist/core/index.d.ts +4 -0
  90. package/dist/core/index.d.ts.map +1 -0
  91. package/dist/core/index.js +4 -0
  92. package/dist/core/index.js.map +1 -0
  93. package/dist/core/mcp-client.d.ts +30 -0
  94. package/dist/core/mcp-client.d.ts.map +1 -0
  95. package/dist/core/mcp-client.js +215 -0
  96. package/dist/core/mcp-client.js.map +1 -0
  97. package/dist/core/transports.d.ts +12 -0
  98. package/dist/core/transports.d.ts.map +1 -0
  99. package/dist/core/transports.js +65 -0
  100. package/dist/core/transports.js.map +1 -0
  101. package/dist/lib/auth/auth-profiles.d.ts +7 -0
  102. package/dist/lib/auth/auth-profiles.d.ts.map +1 -0
  103. package/dist/lib/auth/auth-profiles.js +105 -0
  104. package/dist/lib/auth/auth-profiles.js.map +1 -0
  105. package/dist/lib/auth/keychain.d.ts +22 -0
  106. package/dist/lib/auth/keychain.d.ts.map +1 -0
  107. package/dist/lib/auth/keychain.js +92 -0
  108. package/dist/lib/auth/keychain.js.map +1 -0
  109. package/dist/lib/auth/oauth-flow.d.ts +7 -0
  110. package/dist/lib/auth/oauth-flow.d.ts.map +1 -0
  111. package/dist/lib/auth/oauth-flow.js +236 -0
  112. package/dist/lib/auth/oauth-flow.js.map +1 -0
  113. package/dist/lib/auth/oauth-provider.d.ts +24 -0
  114. package/dist/lib/auth/oauth-provider.d.ts.map +1 -0
  115. package/dist/lib/auth/oauth-provider.js +144 -0
  116. package/dist/lib/auth/oauth-provider.js.map +1 -0
  117. package/dist/lib/auth/oauth-token-manager.d.ts +25 -0
  118. package/dist/lib/auth/oauth-token-manager.d.ts.map +1 -0
  119. package/dist/lib/auth/oauth-token-manager.js +70 -0
  120. package/dist/lib/auth/oauth-token-manager.js.map +1 -0
  121. package/dist/lib/auth/oauth-utils.d.ts +14 -0
  122. package/dist/lib/auth/oauth-utils.d.ts.map +1 -0
  123. package/dist/lib/auth/oauth-utils.js +68 -0
  124. package/dist/lib/auth/oauth-utils.js.map +1 -0
  125. package/dist/lib/auth/token-refresh.d.ts +2 -0
  126. package/dist/lib/auth/token-refresh.d.ts.map +1 -0
  127. package/dist/lib/auth/token-refresh.js +70 -0
  128. package/dist/lib/auth/token-refresh.js.map +1 -0
  129. package/dist/lib/bridge-client.d.ts +23 -0
  130. package/dist/lib/bridge-client.d.ts.map +1 -0
  131. package/dist/lib/bridge-client.js +181 -0
  132. package/dist/lib/bridge-client.js.map +1 -0
  133. package/dist/lib/bridge-manager.d.ts +17 -0
  134. package/dist/lib/bridge-manager.d.ts.map +1 -0
  135. package/dist/lib/bridge-manager.js +240 -0
  136. package/dist/lib/bridge-manager.js.map +1 -0
  137. package/dist/lib/config.d.ts +6 -0
  138. package/dist/lib/config.d.ts.map +1 -0
  139. package/dist/lib/config.js +116 -0
  140. package/dist/lib/config.js.map +1 -0
  141. package/dist/lib/errors.d.ts +23 -0
  142. package/dist/lib/errors.d.ts.map +1 -0
  143. package/dist/lib/errors.js +81 -0
  144. package/dist/lib/errors.js.map +1 -0
  145. package/dist/lib/file-lock.d.ts +2 -0
  146. package/dist/lib/file-lock.d.ts.map +1 -0
  147. package/dist/lib/file-lock.js +46 -0
  148. package/dist/lib/file-lock.js.map +1 -0
  149. package/dist/lib/file-logger.d.ts +19 -0
  150. package/dist/lib/file-logger.d.ts.map +1 -0
  151. package/dist/lib/file-logger.js +126 -0
  152. package/dist/lib/file-logger.js.map +1 -0
  153. package/dist/lib/index.d.ts +6 -0
  154. package/dist/lib/index.d.ts.map +1 -0
  155. package/dist/lib/index.js +6 -0
  156. package/dist/lib/index.js.map +1 -0
  157. package/dist/lib/logger.d.ts +24 -0
  158. package/dist/lib/logger.d.ts.map +1 -0
  159. package/dist/lib/logger.js +189 -0
  160. package/dist/lib/logger.js.map +1 -0
  161. package/dist/lib/session-client.d.ts +28 -0
  162. package/dist/lib/session-client.d.ts.map +1 -0
  163. package/dist/lib/session-client.js +104 -0
  164. package/dist/lib/session-client.js.map +1 -0
  165. package/dist/lib/sessions.d.ts +9 -0
  166. package/dist/lib/sessions.d.ts.map +1 -0
  167. package/dist/lib/sessions.js +116 -0
  168. package/dist/lib/sessions.js.map +1 -0
  169. package/dist/lib/types.d.ts +117 -0
  170. package/dist/lib/types.d.ts.map +1 -0
  171. package/dist/lib/types.js +2 -0
  172. package/dist/lib/types.js.map +1 -0
  173. package/dist/lib/utils.d.ts +29 -0
  174. package/dist/lib/utils.d.ts.map +1 -0
  175. package/dist/lib/utils.js +173 -0
  176. package/dist/lib/utils.js.map +1 -0
  177. package/package.json +71 -0
  178. package/tsconfig.test.json +11 -0
package/README.md ADDED
@@ -0,0 +1,1056 @@
1
+ # mcpc: an MCP command-line client
2
+
3
+ `mcpc` is a universal command-line client for the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/),
4
+ which maps MCP to intuitive CLI commands for shell access, scripts, and AI coding agents.
5
+
6
+ `mcpc` can connect to any MCP server over Streamable HTTP or stdio transports,
7
+ securely login via OAuth credentials and store credentials,
8
+ and keep long-term sessions to multiple servers in parallel.
9
+ It supports all major MCP features, including tools, resources, prompts, asynchronous tasks, and notifications.
10
+
11
+ `mcpc` is handy for manual testing of MCP servers, scripting,
12
+ and AI coding agents to use MCP in ["code mode"](https://www.anthropic.com/engineering/code-execution-with-mcp),
13
+ for better accuracy and lower token compared to traditional tool function calling.
14
+ After all, UNIX-compatible shell script is THE most universal coding language, for both people and LLMs.
15
+
16
+ Note that `mcpc` is deterministic and does not use any LLM on its own; that's for the higher layer to do.
17
+
18
+ ## Features
19
+
20
+ - 🔌 **Universal MCP client** - Works with any MCP server over Streamable HTTP or stdio.
21
+ - 🔄 **Persistent sessions** - Keep multiple server connections alive simultaneously.
22
+ - 🚀 **Zero setup** - Connect to remote servers instantly with just a URL.
23
+ - 🔧 **Full protocol support** - Tools, resources, prompts, sampling, dynamic discovery, and async notifications.
24
+ - 📊 **`--json` output** - Easy integration with `jq`, scripts, and other CLI tools.
25
+ - 🤖 **AI-friendly** - Designed for code generation and automated workflows.
26
+ - 🔒 **Secure** - OS keychain integration for credentials, encrypted auth storage.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ npm install -g @apify/mcpc
32
+ ```
33
+
34
+ ## Quickstart
35
+
36
+ ```bash
37
+ # List all active sessions and saved authentication profiles
38
+ mcpc
39
+
40
+ # Use a local server package referenced by MCP config file
41
+ mcpc --config ~/.vscode/mcp.json filesystem tools-list
42
+
43
+ # Login to OAuth-enabled MCP server and save authentication for future use
44
+ mcpc mcp.apify.com login
45
+
46
+ # Show information about a remote MCP server and open interactive shell
47
+ mcpc mcp.apify.com
48
+ mcpc mcp.apify.com shell
49
+
50
+ # Use JSON mode for scripting
51
+ mcpc --json mcp.apify.com tools-list
52
+
53
+ # Create a persistent session (or reconnect if it exists but bridge is dead)
54
+ mcpc mcp.apify.com session @test
55
+ mcpc @test tools-call search-actors --args query="web crawler"
56
+ mcpc @test shell
57
+ ```
58
+
59
+ ## Usage
60
+
61
+ ```bash
62
+ mcpc [--json] [--config <file>] [-H|--header "K: V"] [-v|--verbose]
63
+ [--schema <file>]
64
+ [--schema-mode <mode>] [--timeout <seconds>] [--insecure]
65
+ [--clean|--clean=sessions,logs,profiles,all]
66
+ <target> <command...>
67
+
68
+ # Lists all active sessions and saved authentication profiles
69
+ mcpc
70
+
71
+ # Shows server or session info, instructions, and capabilities
72
+ mcpc <target>
73
+
74
+ # MCP commands
75
+ mcpc <target> tools
76
+ mcpc <target> tools-list
77
+ mcpc <target> tools-schema <tool-name>
78
+ mcpc <target> tools-call <tool-name> [--args key=val key2:=json ...] [--args-file <file>]
79
+
80
+ mcpc <target> prompts
81
+ mcpc <target> prompts-list
82
+ mcpc <target> prompts-get <prompt-name> [--args key=val key2:=json ...] [--args-file <file>]
83
+
84
+ mcpc <target> resources
85
+ mcpc <target> resources-list
86
+ mcpc <target> resources-read <uri> [-o <file>] [--max-size <bytes>]
87
+ mcpc <target> resources-subscribe <uri> # TODO
88
+ mcpc <target> resources-unsubscribe <uri>
89
+ mcpc <target> resources-templates-list
90
+
91
+ mcpc <target> logging-set-level <level>
92
+
93
+ # Interactive MCP shell
94
+ mcpc <target> shell
95
+
96
+ # Persistent sessions
97
+ mcpc <server> session @<session-name> [--profile <name>]
98
+ mcpc @<session-name> <command...>
99
+ mcpc @<session-name> close
100
+
101
+ # Saved OAuth profiles for remote MCP servers
102
+ mcpc <server> login [--profile <name>]
103
+ mcpc <server> logout [--profile <name>]
104
+ ```
105
+
106
+ where `<target>` can be one of (in this order of precedence):
107
+
108
+ - **Named session** prefixed with `@` (e.g. `@apify`) - persisted connection via bridge process
109
+ - **Named entry** in a config file, when used with `--config` (e.g. `filesystem`) - local or remote server
110
+ - **Remote MCP endpoint** URL (e.g. `mcp.apify.com` or `https://mcp.apify.com`) - direct HTTP connection
111
+
112
+ For local MCP servers (stdio transport), use a config file to specify the command, arguments, and environment variables. See [Configuration](#configuration) below.
113
+
114
+ `mcpc` automatically selects the transport protocol:
115
+ - HTTP/HTTPS URLs use the MCP Streamable HTTP transport (current standard; HTTP with SSE is not supported)
116
+ - Config file entries use the transport specified in the config (stdio for local servers, HTTP for remote)
117
+
118
+ ### MCP command arguments
119
+
120
+ `mcpc` supports multiple ways to pass arguments to `tools-call` and `prompts-get` commands:
121
+
122
+ ```bash
123
+ # Inline JSON object (most convenient)
124
+ ... --args '{"query":"hello","count":10}'
125
+
126
+ # String values (default) - use = for strings
127
+ ... --args name=value query="hello world"
128
+
129
+ # JSON literals - use := for JSON types
130
+ ... --args count:=123 enabled:=true value:=null
131
+ ... --args config:='{"key":"value"}' items:='[1,2,3]'
132
+
133
+ # Mixed strings and JSON
134
+ ... --args query="search term" limit:=10 verbose:=true
135
+
136
+ # Load all arguments from JSON file
137
+ ... --args-file tool-arguments.json
138
+
139
+ # Read from stdin (automatic when piped, no flag needed)
140
+ echo '{"query":"hello","count":10}' | mcpc @server tools-call my-tool
141
+ ```
142
+
143
+ **Rules:**
144
+ - Use only one method: `--args` (inline JSON or key=value pairs), `--args-file`, or stdin (piped input)
145
+ - Inline JSON: If first argument starts with `{` or `[`, it's parsed as JSON object/array
146
+ - Key=value pairs: After `--args`, all `key=value` or `key:=json` pairs are consumed until next flag
147
+ - `=` assigns as string, `:=` parses as JSON
148
+ - Stdin is automatically detected when input is piped (not interactive terminal)
149
+
150
+ ## Global flags
151
+
152
+ - `--json` - Input and output in JSON format for scripting
153
+ - `--config <file>` - Use MCP config JSON file (e.g., `.vscode/mcp.json`)
154
+ - `-H, --header "Key: Value"` - Add HTTP header (can be repeated)
155
+ - `-v, --verbose` - Enable verbose logging (shows protocol details)
156
+ - `--timeout <seconds>` - Request timeout in seconds (default: 300)
157
+ - `--schema <file>` - Validate against expected tool/prompt schema
158
+ - `--schema-mode <mode>` - Schema validation mode: `strict`, `compatible`, or `ignore` (default: `compatible`)
159
+ - `--insecure` - Disable SSL certificate validation (not recommended)
160
+
161
+ ## Authentication
162
+
163
+ `mcpc` supports all standard [authentication methods](https://modelcontextprotocol.io/specification/latest/basic/authorization) for MCP servers,
164
+ including the `WWW-Authenticate` discovery mechanism and OAuth 2.1 with PKCE.
165
+
166
+ ### No authentication
167
+
168
+ For local servers (stdio) or remote servers (Streamable HTTP) which do not require credentials,
169
+ `mcpc` can be used without authentication:
170
+
171
+ ```bash
172
+ # Remote server which enables anonymous access
173
+ mcpc https://mcp.apify.com\?tools=docs tools-list
174
+ ```
175
+
176
+ ### Bearer token authentication
177
+
178
+ For remote servers that require a bearer token (but not OAuth), use the `--header` flag.
179
+ All headers are stored securely in the OS keychain for the session, but **not** saved as a reusable authentication profile:
180
+
181
+ ```bash
182
+ # One-time command with bearer token
183
+ mcpc --header "Authorization: Bearer ${APIFY_TOKEN}" https://mcp.apify.com tools-list
184
+
185
+ # Create session with bearer token (saved to keychain for this session only)
186
+ mcpc --header "Authorization: Bearer ${APIFY_TOKEN}" https://mcp.apify.com session @apify
187
+
188
+ # Use the session (token loaded from keychain automatically)
189
+ mcpc @apify tools-list
190
+ ```
191
+
192
+ ### OAuth authentication
193
+
194
+ For OAuth-enabled remote MCP servers, `mcpc` implements the full OAuth 2.1 flow with PKCE, including:
195
+ - `WWW-Authenticate` header discovery
196
+ - Authorization server metadata discovery (RFC 8414)
197
+ - Client ID metadata documents (SEP-991)
198
+ - Dynamic client registration (RFC 7591)
199
+ - Automatic token refresh
200
+
201
+ The OAuth authentication is **always** initiated by the user calling the `login` command,
202
+ which opens a web browser with login screen. `mcpc` doesn't open web browser in any other case.
203
+
204
+ `mcpc` uses OS keychain to securely store OAuth authentication tokens.
205
+
206
+ #### Authentication profiles
207
+
208
+ For OAuth-enabled servers, `mcpc` uses **authentication profiles** - reusable credentials that can be shared across multiple sessions.
209
+ This allows you to:
210
+ - Authenticate once, create multiple sessions
211
+ - Use different accounts (profiles) with the same server
212
+ - Manage credentials independently from sessions
213
+
214
+ **Key concepts:**
215
+ - **Authentication profile**: Named set of OAuth credentials for a specific server (stored in `~/.mcpc/auth-profiles.json` + OS keychain)
216
+ - **Session**: Active connection to a server that may reference an authentication profile (stored in `~/.mcpc/sessions.json`)
217
+ - **Default profile**: When `--profile` is not specified, `mcpc` uses the authentication profile named `default`
218
+
219
+ **Example:**
220
+
221
+ ```bash
222
+ # Login to server and save 'default' authentication profile for future use
223
+ mcpc https://mcp.apify.com login
224
+
225
+ # Use named authentication profile instead of 'default'
226
+ mcpc https://mcp.apify.com login --profile personal
227
+
228
+ # Re-authenticate existing profile (e.g., to refresh or change scopes)
229
+ mcpc https://mcp.apify.com login --profile personal
230
+
231
+ # Delete an authentication profile
232
+ mcpc https://mcp.apify.com logout --profile personal
233
+
234
+ ```
235
+
236
+ #### Authentication behavior
237
+
238
+ `mcpc` automatically handles authentication based on whether you specify a profile:
239
+
240
+ **When `--profile <name>` is specified:**
241
+
242
+ 1. **Profile exists for the server**: Use its stored credentials
243
+ - If authentication succeeds → Continue with command/session
244
+ - If authentication fails (expired/invalid) → Fail with an error
245
+ 2. **Profile doesn't exist**: Fail with an error
246
+
247
+ **When no `--profile` is specified:**
248
+
249
+ 1. **`default` profile exists for the server**: Use its stored credentials
250
+ - If authentication succeeds → Continue with command/session
251
+ - If authentication fails (expired/invalid) → Fail with an error
252
+ 2. **`default` profile doesn't exist**: Attempt unauthenticated connection
253
+ - If server accepts (no auth required) → Continue without creating profile
254
+ - If server rejects with 401 + `WWW-Authenticate` → Fail with an error
255
+
256
+ On failure, the error message includes instructions on how to login and save the profile, so the users know what to do.
257
+
258
+ **This flow ensures:**
259
+ - You only authenticate when necessary
260
+ - Credentials are never silently mixed up (personal → work) or downgraded (authenticated → unauthenticated)
261
+ - You can mix authenticated sessions (with named profiles) and public access on the same server
262
+
263
+ **Examples:**
264
+
265
+ ```bash
266
+ # With specific profile - always authenticated:
267
+ # - Uses 'personal' if it exists
268
+ # - Fails if it doesn't exist
269
+ mcpc https://mcp.apify.com session @apify1 --profile personal
270
+
271
+ # Without profile - opportunistic authentication:
272
+ # - Uses 'default' if it exists
273
+ # - Tries unauthenticated if 'default' doesn't exist
274
+ # - Fails if the server requires authentication
275
+ mcpc https://mcp.apify.com session @apify2
276
+
277
+ # Public server - no authentication needed:
278
+ mcpc https://mcp.apify.com\?tools=docs tools-list
279
+ ```
280
+
281
+ #### Multiple accounts for the same server
282
+
283
+ Authentication profiles enable using multiple accounts with the same MCP server:
284
+
285
+ ```bash
286
+ # Authenticate with personal account
287
+ mcpc https://mcp.apify.com login --profile personal
288
+
289
+ # Authenticate with work account
290
+ mcpc https://mcp.apify.com login --profile work
291
+
292
+ # Create sessions using the two different credentials
293
+ mcpc https://mcp.apify.com session @apify-personal --profile personal
294
+ mcpc https://mcp.apify.com session @apify-work --profile work
295
+
296
+ # Both sessions work independently
297
+ mcpc @apify-personal tools-list # Uses personal account
298
+ mcpc @apify-work tools-list # Uses work account
299
+ ```
300
+
301
+ ### Authentication precedence
302
+
303
+ When multiple authentication methods are available, `mcpc` uses this precedence order:
304
+
305
+ 1. **Command-line `--header` flag** (highest priority) - Always used if provided
306
+ 2. **Session's stored credentials** - Bearer tokens or OAuth tokens from profile
307
+ 3. **Config file headers** - Headers from `--config` file for the server
308
+ 4. **No authentication** - Attempts unauthenticated connection
309
+
310
+ **Example:**
311
+ ```bash
312
+ # Config file has: "headers": {"Authorization": "Bearer ${TOKEN1}"}
313
+ # Session uses profile with different OAuth token
314
+ # Command provides: --header "Authorization: Bearer ${TOKEN2}"
315
+ # Result: Uses TOKEN2 (command-line flag wins)
316
+ ```
317
+
318
+ ### Authentication profiles storage format
319
+
320
+ By default, authentication profiles are stored in the `~/.mcpc/auth-profiles.json` file with the following structure:
321
+
322
+ ```json
323
+ {
324
+ "profiles": {
325
+ "https://mcp.apify.com": {
326
+ "personal": {
327
+ "name": "personal",
328
+ "serverUrl": "https://mcp.apify.com",
329
+ "authType": "oauth",
330
+ "oauthIssuer": "https://auth.apify.com",
331
+ "scopes": ["tools:read", "tools:write", "resources:read"],
332
+ "createdAt": "2025-12-14T10:00:00Z",
333
+ "authenticatedAt": "2025-12-14T10:00:00Z"
334
+ },
335
+ "work": {
336
+ "name": "work",
337
+ "serverUrl": "https://mcp.apify.com",
338
+ "authType": "oauth",
339
+ "oauthIssuer": "https://auth.apify.com",
340
+ "scopes": ["tools:read"],
341
+ "createdAt": "2025-12-10T15:30:00Z",
342
+ "authenticatedAt": "2025-12-10T15:30:00Z"
343
+ }
344
+ }
345
+ }
346
+ }
347
+ ```
348
+
349
+ **OS Keychain entries:**
350
+ - OAuth tokens: `mcpc:auth:https://mcp.apify.com:personal:tokens`
351
+ - OAuth client info: `mcpc:auth:https://mcp.apify.com:personal:client`
352
+ - HTTP headers (per-session): `mcpc:session:apify:headers`
353
+
354
+ ## Sessions
355
+
356
+ MCP is a [stateful protocol](https://modelcontextprotocol.io/specification/latest/basic/lifecycle): clients and servers perform an initialization handshake
357
+ to negotiate protocol version and capabilities, then communicate within a persistent session.
358
+ Each session maintains:
359
+ - Negotiated protocol version and capabilities (which tools/resources/prompts/notifications are supported)
360
+ - For Streamable HTTP transport: persistent connection with bidirectional streaming, with automatic reconnection
361
+ - For stdio transport: persistent bidirectional pipe to subprocess
362
+
363
+ Instead of forcing every command to reconnect and reinitialize (which is slow and loses state),
364
+ `mcpc` uses a lightweight **bridge process** per session that:
365
+
366
+ - Maintains the MCP session (protocol version, capabilities, connection state)
367
+ - For Streamable HTTP: Manages persistent connections with automatic reconnection and resumption
368
+ - Multiplexes multiple concurrent requests (up to 10 concurrent, 100 queued)
369
+ - Enables piping data between multiple MCP servers simultaneously
370
+
371
+ `mcpc` saves its state to `~/.mcpc/` directory (unless overridden by `MCPC_HOME_DIR`), in the following files:
372
+
373
+ - `~/.mcpc/sessions.json` - Active sessions with references to authentication profiles (file-locked for concurrent access)
374
+ - `~/.mcpc/auth-profiles.json` - Authentication profiles (OAuth metadata, scopes, expiry)
375
+ - `~/.mcpc/bridges/` - Unix domain socket files for each bridge process
376
+ - `~/.mcpc/logs/bridge-*.log` - Log files for each bridge process
377
+ - OS keychain - Sensitive credentials (OAuth tokens, bearer tokens, client secrets)
378
+
379
+ ### Managing sessions
380
+
381
+ ```bash
382
+ # Create a persistent session (with default authentication profile, if available)
383
+ mcpc https://mcp.apify.com session @apify
384
+
385
+ # Create session with specific authentication profile
386
+ mcpc https://mcp.apify.com session @apify --profile personal
387
+
388
+ # List all active sessions and saved authentication profiles
389
+ mcpc
390
+
391
+ # Active sessions:
392
+ # @apify → https://mcp.apify.com (http, profile: personal)
393
+ #
394
+ # Saved authentication profiles:
395
+ # https://mcp.apify.com
396
+ # • personal (authenticated: 2 days ago)
397
+ # • work (authenticated: 1 week ago)
398
+
399
+ # Use the session
400
+ mcpc @apify tools-list
401
+ mcpc @apify shell
402
+
403
+ # Close the session (terminates bridge process, but keeps authentication profile)
404
+ mcpc @apify close
405
+ ```
406
+
407
+ ### Piping between sessions
408
+
409
+ ```bash
410
+ mcpc --json @apify tools-call search-actors --args query="tiktok scraper" \
411
+ | jq '.data.results[0]' \
412
+ | mcpc @playwright tools-call run-browser
413
+ ```
414
+
415
+ ### Scripting
416
+
417
+ `mcpc` is designed to be easily usable in (AI-generated) scripts. To ensure consistency
418
+ of your scripts with the current MCP server interface, you can use `--schema <file>` argument
419
+ to pass `mcpc` the expected schema. If the MCP server's current schema is incompatible,
420
+ the command returns an error.
421
+
422
+ ```bash
423
+ # Save tool schema for future validation
424
+ mcpc --json @apify tools-schema search-actors > search-actors-schema.json
425
+
426
+ # Use schema to ensure compatibility (fails if schema changed)
427
+ mcpc @apify tools-call search-actors \
428
+ --schema search-actors-schema.json \
429
+ --schema-mode strict \
430
+ --args query="tiktok scraper"
431
+ ```
432
+
433
+ **Schema validation modes using the `--schema-mode` parameter:**
434
+ - `strict` - Exact schema match required (all fields, types must be identical)
435
+ - `compatible` (default) - Backwards compatible (new optional fields OK, required fields and types must match)
436
+ - `ignore` - Skip schema validation
437
+
438
+ ### Session failover
439
+
440
+ `mcpc` bridge process attempts to keep sessions alive by sending periodic ping messages to the MCP server.
441
+ But even then, the session can fail for a number of reasons:
442
+
443
+ - Network disconnects
444
+ - Server drops the session for inactivity or other reasons
445
+ - Bridge process crashes
446
+
447
+ Here's how `mcpc` handles these situations:
448
+
449
+ - If the bridge process is running, it will automatically try to reconnect to the server if the connection fails
450
+ and establish the keep-alive pings.
451
+ - If the server response indicates the `MCP-Session-Id` is no longer valid or authentication permanently failed (HTTP error 401 or 402),
452
+ the bridge process will flag the session as **expired** in `~/.mcpc/sessions.json` and terminate.
453
+ - If the bridge process crashes, `mcpc` attempts to restart it next time you use the specific session.
454
+
455
+ Note that `mcpc` never automatically removes sessions from the list, but rather flags the session as **expired**,
456
+ and any attempts to use it will fail.
457
+ To remove the session from the list, you need to explicitly close it:
458
+
459
+ ```bash
460
+ mcpc @apify close
461
+ ```
462
+
463
+ or reconnect it using the `session` command (if the session exists but bridge is dead, it will be automatically reconnected):
464
+
465
+ ```bash
466
+ mcpc https://mcp.apify.com session @apify
467
+ ```
468
+
469
+ ## Logging
470
+
471
+ The background bridge process logs to `~/.mcpc/bridges/mcpc-@<session-name>.log`.
472
+ The main `mcpc` process doesn't save log files, but you can use `--verbose` flag to print all logs to stderr.
473
+
474
+ MCP servers can be instructed to adjust their [logging level](https://modelcontextprotocol.io/specification/latest/server/utilities/logging)
475
+ using the `logging/setLevel` command:
476
+
477
+ ```bash
478
+ # Set server log level to debug for detailed output
479
+ mcpc @apify logging-set-level debug
480
+
481
+ # Reduce server logging to only errors
482
+ mcpc @apify logging-set-level error
483
+ ```
484
+
485
+ **Available log levels** (from most to least verbose):
486
+ - `debug` - Detailed debugging information
487
+ - `info` - General informational messages
488
+ - `notice` - Normal but significant events
489
+ - `warning` - Warning messages
490
+ - `error` - Error messages
491
+ - `critical` - Critical conditions
492
+ - `alert` - Action must be taken immediately
493
+ - `emergency` - System is unusable
494
+
495
+ **Note:** This sets the logging level on the **server side**. The actual log output depends on the server's implementation.
496
+
497
+
498
+ ## Configuration
499
+
500
+ Configuration can be provided via file, environment variables, or command-line flags.
501
+
502
+ **Precedence** (highest to lowest):
503
+ 1. Command-line flags (including `--config` option)
504
+ 2. Environment variables
505
+ 3. Built-in defaults
506
+
507
+ ### MCP config JSON file
508
+
509
+ `mcpc` supports the ["standard"](https://gofastmcp.com/integrations/mcp-json-configuration)
510
+ MCP server JSON config file, compatible with Claude Desktop, VS Code, and other MCP clients.
511
+ You can point to an existing config file with `--config`:
512
+
513
+ ```bash
514
+ # One-shot command to an MCP server configured in Visual Studio Code
515
+ mcpc --config .vscode/mcp.json apify tools-list
516
+
517
+ # Open a session to a server specified in the custom config file
518
+ mcpc --config .vscode/mcp.json apify session @my-apify
519
+ ```
520
+
521
+ **Example MCP config JSON file:**
522
+
523
+ ```json
524
+ {
525
+ "mcpServers": {
526
+ "apify": {
527
+ "url": "https://mcp.apify.com",
528
+ "headers": {
529
+ "Authorization": "Bearer ${APIFY_TOKEN}"
530
+ }
531
+ },
532
+ "filesystem": {
533
+ "command": "npx",
534
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
535
+ "env": {
536
+ "DEBUG": "mcp:*"
537
+ }
538
+ },
539
+ "local-package": {
540
+ "command": "node",
541
+ "args": ["/path/to/server.js"]
542
+ }
543
+ }
544
+ }
545
+ ```
546
+
547
+ **Server configuration properties:**
548
+
549
+ For **HTTP/HTTPS servers:**
550
+ - `url` (required) - MCP server endpoint URL
551
+ - `headers` (optional) - HTTP headers to include with requests
552
+ - `timeout` (optional) - Request timeout in seconds
553
+
554
+ For **stdio servers:**
555
+ - `command` (required) - Command to execute (e.g., `node`, `npx`, `python`)
556
+ - `args` (optional) - Array of command arguments
557
+ - `env` (optional) - Environment variables for the process
558
+
559
+ **Using servers from config file:**
560
+
561
+ When `--config` is provided, you can reference servers by name:
562
+
563
+ ```bash
564
+ # With config file, use server names directly
565
+ mcpc --config .vscode/mcp.json filesystem resources-list
566
+
567
+ # Create a named session from server in config
568
+ mcpc --config .vscode/mcp.json filesystem session @fs
569
+ mcpc @fs tools-call search
570
+ ```
571
+
572
+ **Environment variable substitution:**
573
+
574
+ Config files support environment variable substitution using `${VAR_NAME}` syntax:
575
+
576
+ ```json
577
+ {
578
+ "mcpServers": {
579
+ "secure-server": {
580
+ "url": "https://mcp.apify.com",
581
+ "headers": {
582
+ "Authorization": "Bearer ${API_TOKEN}",
583
+ "X-User-ID": "${USER_ID}"
584
+ }
585
+ }
586
+ }
587
+ }
588
+ ```
589
+
590
+ ### Environment variables
591
+
592
+ - `MCPC_HOME_DIR` - Directory for session and authentication profiles data (default is `~/.mcpc`)
593
+ - `MCPC_VERBOSE` - Enable verbose logging (set to `1`, `true`, or `yes`, case-insensitive)
594
+ - `MCPC_JSON` - Enable JSON output (set to `1`, `true`, or `yes`, case-insensitive)
595
+
596
+ ## MCP protocol notes
597
+
598
+ **Protocol initialization:**
599
+ - `mcpc` follows the MCP initialization handshake: sends `initialize` request with protocol version and capabilities, receives server capabilities and instructions, then sends `initialized` notification
600
+ - Protocol version negotiation: client proposes latest supported version (currently `2025-11-25`), server responds with version to use
601
+
602
+ **Transport handling:**
603
+ - **Streamable HTTP**: `mcpc` supports only the Streamable HTTP transport (the current standard). The deprecated HTTP with SSE transport is not supported. The bridge manages persistent HTTP connections with bidirectional streaming for server-to-client communication, with automatic reconnection using exponential backoff (1s → 30s max)
604
+ - Includes `MCP-Protocol-Version` header on all HTTP requests (per MCP spec)
605
+ - Handles `MCP-Session-Id` for stateful server sessions
606
+ - During reconnection, new requests are queued (fails after 3 minutes of disconnection)
607
+ - **Stdio**: Direct bidirectional JSON-RPC communication over standard input/output
608
+
609
+ **Protocol features:**
610
+ - `mcpc` supports all MCP primitives in both Streamable HTTP and stdio transports:
611
+ - **Instructions**: Fetches and stores MCP server-provided `instructions`
612
+ - **Tools**: Executable functions with JSON Schema-validated arguments.
613
+ - **Resources**: Data sources identified by URIs (e.g., `file:///path/to/file`, `https://example.com/data`), with optional subscriptions for change notifications
614
+ - **Prompts**: Reusable message templates with customizable arguments
615
+ - **Completion**: Provides access to Completion API for tools and resources, and offers completions in shell mode
616
+ - Supports server logging settings (`logging/setLevel`) and messages (`notifications/message`), and prints them to stderr or stdout based on verbosity level.
617
+ - Handles server notifications: progress tracking, logging, and change notifications (`notifications/tools/list_changed`, `notifications/resources/list_changed`, `notifications/prompts/list_changed`)
618
+ - Request multiplexing: supports up to 10 concurrent requests, queues up to 100 additional requests
619
+ - Pagination: List operations automatically fetch all pages when the server returns paginated results
620
+ - Pings: `mcpc` periodically issues the MCP `ping` request to keep the connection alive
621
+ - Sampling is not supported as `mcpc` has no access to an LLM
622
+
623
+ ## Output format
624
+
625
+ ### Human-readable (default)
626
+
627
+ Default output is formatted for human and AI readability with plain text, colors, and Markdown-like formatting.
628
+
629
+ ### JSON mode (`--json`)
630
+
631
+ In JSON mode, `mcpc` always emits only a single JSON object to enable scripting.
632
+ For MCP commands, the object is always consistent with the MCP protocol specification.
633
+ On success, the JSON object is printed to stdout, otherwise to stderr.
634
+
635
+ ## Security
636
+
637
+ MCP enables arbitrary tool execution and data access; treat servers like you treat shells:
638
+
639
+ * Use least-privilege tokens/headers
640
+ * Prefer trusted endpoints
641
+ * Audit what tools do before running them
642
+ * Review server permissions in interactive mode
643
+
644
+ ### Credential storage
645
+
646
+ **OS keychain integration:**
647
+ - OAuth refresh tokens are stored in the OS keychain (access tokens are kept in memory only)
648
+ - OAuth client credentials (client_id, client_secret from dynamic registration) are stored in the keychain
649
+ - All HTTP headers from `--header` flags are stored per-session in the keychain (as JSON)
650
+ - The `~/.mcpc/auth-profiles.json` file only contains metadata (server URL, scopes, timestamps) - never tokens
651
+
652
+ **Keychain entries:**
653
+ - OAuth tokens: `mcpc:auth:<serverUrl>:<profileName>:oauth-tokens`
654
+ - OAuth client: `mcpc:auth:<serverUrl>:<profileName>:oauth-client`
655
+ - HTTP headers: `mcpc:session:<sessionName>:headers`
656
+
657
+ ### Bridge process authentication
658
+
659
+ Background bridge processes need access to credentials for making authenticated requests. To maintain security while allowing token refresh:
660
+
661
+ **For OAuth profiles:**
662
+ 1. **CLI retrieves refresh token** from OS keychain when creating or restarting a session
663
+ 2. **CLI sends refresh token to bridge** via Unix socket IPC (not command line arguments)
664
+ 3. **Bridge stores refresh token in memory only** - never written to disk
665
+ 4. **Bridge refreshes access tokens** periodically using the refresh token
666
+ 5. **Access tokens are kept in bridge memory** - never persisted to disk
667
+
668
+ **For HTTP headers (from `--header` flags):**
669
+ 1. **All headers are treated as potentially sensitive** - not just `Authorization`
670
+ 2. **CLI stores all headers in OS keychain** per-session (as JSON)
671
+ 3. **CLI sends headers to bridge** via Unix socket IPC (not command line arguments)
672
+ 4. **Bridge stores headers in memory only** - never written to disk
673
+ 5. **Headers are deleted from keychain** when session is closed
674
+ 6. **On bridge crash/restart**, CLI retrieves headers from keychain and resends via IPC
675
+
676
+ This architecture ensures:
677
+ - Credentials are never stored in plaintext on disk
678
+ - Headers are not visible in process arguments (`ps aux`)
679
+ - Bridge processes don't need direct keychain access (which may require user interaction)
680
+ - Credentials are securely transmitted via Unix socket (local IPC only)
681
+ - Failover works correctly - headers are preserved across bridge restarts
682
+
683
+ ### File permissions
684
+
685
+ - `~/.mcpc/sessions.json` is set to `0600` (user-only read/write)
686
+ - `~/.mcpc/auth-profiles.json` is set to `0600` (user-only read/write)
687
+ - Bridge sockets in `~/.mcpc/bridges/` are created with `0700` permissions
688
+ - Log files in `~/.mcpc/logs/` are created with `0600` permissions
689
+
690
+ ### Network security
691
+
692
+ - HTTPS enforced for remote servers (HTTP auto-upgraded)
693
+ - Certificate validation enabled (use `--insecure` to disable, not recommended)
694
+ - `Origin` header validation to prevent DNS rebinding attacks
695
+ - Local servers bind to localhost (127.0.0.1) only
696
+ - No credentials logged even in verbose mode
697
+
698
+ ## Error handling
699
+
700
+ `mcpc` provides clear error messages for common issues:
701
+
702
+ - **Connection failures**: Displays transport-level errors with retry suggestions
703
+ - **Session timeouts**: Automatically attempts to reconnect or prompts for session recreation
704
+ - **Invalid commands**: Shows available commands and correct syntax
705
+ - **Tool execution errors**: Returns server error messages with context
706
+ - **Bridge crashes**: Detects and cleans up orphaned processes, offers restart
707
+
708
+ Use `--verbose` to print detailed debugging information to stderr (includes JSON-RPC messages, streaming events, and protocol negotiation).
709
+
710
+ ### Exit codes
711
+
712
+ - `0` - Success
713
+ - `1` - Client error (invalid arguments, command not found, etc.)
714
+ - `2` - Server error (tool execution failed, resource not found, etc.)
715
+ - `3` - Network error (connection failed, timeout, etc.)
716
+ - `4` - Authentication error (invalid credentials, forbidden, etc.)
717
+
718
+ ### Retry strategy
719
+
720
+ - **Network errors**: Automatic retry with exponential backoff (3 attempts)
721
+ - **Stream reconnection**: Starts at 1s, doubles to max 30s
722
+ - **Bridge restart**: Automatic on crash detection (recreates session on next command)
723
+ - **Timeouts**: Configurable per-request timeout (default: 5 minutes)
724
+
725
+ ## Interactive shell
726
+
727
+ The interactive shell provides a REPL-style interface for MCP servers:
728
+
729
+ ```bash
730
+ mcpc @apify shell
731
+ ```
732
+
733
+ **Features:**
734
+ - Command history (saved to `~/.mcpc/history`, last 1000 commands)
735
+ - Tab completion for commands, tool names, and resource URIs
736
+ - Multi-line editing with arrow keys
737
+ - Prompt shows session name: `mcpc(@apify)> `
738
+
739
+ **Shell-specific commands:**
740
+ - `help` - Show available commands
741
+ - `exit` or `quit` or Ctrl+D - Exit shell
742
+ - Ctrl+C - Cancel current operation
743
+
744
+ **Example session:**
745
+ ```
746
+ $ mcpc @apify shell
747
+ Connected to apify (https://mcp.apify.com)
748
+ MCP version: 2025-11-25
749
+
750
+ mcpc(@apify)> tools-list
751
+ Available tools:
752
+ - search-actors
753
+ - get-actor
754
+ - run-actor
755
+
756
+ mcpc(@apify)> tools-call search-actors --args query="tiktok scraper"
757
+ [results...]
758
+
759
+ mcpc(@apify)> exit
760
+ ```
761
+
762
+ ## Implementation status
763
+
764
+ **Note:** This README describes the target architecture. `mcpc` is under active development and not all features are currently implemented.
765
+
766
+ ### What's implemented
767
+
768
+ **✅ Core functionality:**
769
+ - MCP protocol client (wrapper around official SDK)
770
+ - CLI structure with Commander.js
771
+ - All MCP command handlers fully functional
772
+ - Output formatting (human-readable and JSON modes)
773
+ - Argument parsing (inline JSON, key=value, key:=json, `--args-file`)
774
+ - Error handling with exit codes
775
+ - Verbose logging
776
+ - Bridge process with persistent sessions
777
+ - Unix socket IPC between CLI and bridge
778
+ - Session management with file locking
779
+ - Environment variables (MCPC_HOME_DIR, MCPC_VERBOSE, MCPC_JSON)
780
+ - Caching with TTL and notification-based invalidation
781
+ - Server notification handling (`list_changed` events)
782
+ - Per-session bridge logs with rotation
783
+ - Interactive shell: REPL features (history, tab completion)
784
+ - Config file: Full stdio transport support for local packages
785
+ - **Authentication**: OAuth profiles, keychain storage (structure exists, flow not complete)
786
+ - **Error recovery**: Bridge crash recovery, automatic reconnection
787
+
788
+ ## Implementation details
789
+
790
+ ### Design principles
791
+
792
+ - Delightful for humans and AI agents alike (interactive + scripting)
793
+ - One clear way to do things (orthogonal commands, no surprises, saving tokens)
794
+ - Do not ask for user input (except `shell` and `login`)
795
+ - Be forgiving, always help users make forward progress (great errors + guidance)
796
+ - JSON strictly consistency with the MCP specification
797
+ - Minimal and portable (few deps, cross-platform)
798
+ - No slop!
799
+
800
+ ### Architecture overview
801
+
802
+ ```
803
+ TODO: improve interaction diagram
804
+ mcpc ──> cli ├──> bridge (UNIX socket) ──> MCP server (stdio/HTTP)
805
+ ├──> MCP server (stdio/HTTP)
806
+
807
+ mcpc (single package)
808
+ ├── src/
809
+ │ ├── core/ # Core MCP protocol implementation
810
+ │ ├── bridge/ # Bridge process logic
811
+ │ ├── cli/ # CLI interface
812
+ │ └── lib/ # Shared utilities
813
+ ├── bin/
814
+ │ ├── mcpc # Main CLI executable
815
+ │ └── mcpc-bridge # Bridge process executable
816
+ ```
817
+
818
+
819
+ ### Core module (runtime-agnostic)
820
+
821
+ Implemented with minimal dependencies to support both Node.js (≥18.0.0) and Bun (≥1.0.0).
822
+
823
+ **Core responsibilities:**
824
+ - Transport selection and initialization (Streamable HTTP vs stdio)
825
+ - MCP protocol implementation and version negotiation
826
+ - Session state machine management
827
+ - Streamable HTTP connection management (reconnection with exponential backoff)
828
+ - Request/response correlation (JSON-RPC style with request IDs)
829
+ - Multiplexing concurrent requests (up to 10 concurrent)
830
+ - Event emitter for async notifications
831
+
832
+ **Key dependencies:**
833
+ - Native `fetch` API (available in Node.js 18+ and Bun)
834
+ - Native process APIs for stdio transport
835
+ - Minimal: UUID generation, event emitter abstraction
836
+
837
+ ### Bridge process
838
+
839
+ Implemented as a separate executable (`mcpc-bridge`) that maintains persistent connections.
840
+
841
+ **Bridge responsibilities:**
842
+ - Session persistence (reads/writes `~/.mcpc/sessions.json` with file locking)
843
+ - Process lifecycle management for local package servers
844
+ - Stdio framing and protocol handling
845
+ - Unix domain socket server for CLI communication
846
+ - Heartbeat mechanism for health monitoring
847
+ - Orphaned process cleanup on startup
848
+
849
+ **IPC protocol:**
850
+ - Unix domain sockets (located in `~/.mcpc/bridges/<session-name>.sock`)
851
+ - Named pipes on Windows
852
+ - JSON-RPC style messages over socket
853
+ - Control messages: init, request, cancel, close, health-check
854
+
855
+ **Bridge discovery:**
856
+ - CLI reads `~/.mcpc/sessions.json` to find socket path and PID
857
+ - Validates bridge is alive (connect to socket + health-check)
858
+ - Auto-restarts crashed bridges (detected via socket connection failure)
859
+ - Cleanup: removes stale socket files for dead processes
860
+
861
+ **Concurrency safety:**
862
+ - `~/.mcpc/sessions.json` protected with file locking (`proper-lockfile` package)
863
+ - Atomic writes (write to temp file, then rename)
864
+ - Lock timeout: 5 seconds (fails if can't acquire lock)
865
+
866
+ ### CLI executable
867
+
868
+ The main `mcpc` command provides the user interface.
869
+
870
+ **CLI responsibilities:**
871
+ - Argument parsing (using `minimist` or similar)
872
+ - Output formatting (human-readable vs `--json`)
873
+ - Bridge lifecycle: start/connect/stop
874
+ - Communication with bridge via socket
875
+ - Interactive shell (REPL using `@inquirer/prompts`)
876
+ - Configuration file loading (standard MCP JSON format)
877
+ - Credential management (OS keychain via `keytar` package)
878
+
879
+ **Shell implementation:**
880
+ - Built on `@inquirer/prompts` for input handling
881
+ - Command history using `~/.mcpc/history`
882
+ - Tab completion using inquirer autocomplete and MCP completion API
883
+ - Graceful exit handling (cleanup on Ctrl+C/Ctrl+D)
884
+
885
+ ### Session lifecycle
886
+
887
+ 1. User: `mcpc https://mcp.apify.com session @apify`
888
+ 2. CLI: Atomically creates session entry in `~/.mcpc/sessions.json`
889
+ 3. CLI: Spawns bridge process (`mcpc-bridge`)
890
+ 4. Bridge: Creates Unix socket at `~/.mcpc/bridges/apify.sock`
891
+ 5. Bridge: Performs MCP initialization handshake with server:
892
+ - Sends initialize request with protocol version and capabilities
893
+ - Receives server info, version, and capabilities
894
+ - Sends initialized notification to activate session
895
+ 6. Bridge: Updates session in `~/.mcpc/sessions.json` (adds PID, socket path, protocol version)
896
+ 7. CLI: Confirms session created
897
+
898
+ Later...
899
+
900
+ 8. User: mcpc @apify tools-list
901
+ 9. CLI: Reads `~/.mcpc/sessions.json`, finds socket path
902
+ 10. CLI: Connects to bridge socket
903
+ 11. CLI: Sends `tools/list` JSON-RPC request via socket
904
+ 12. Bridge: Forwards to MCP server via Streamable HTTP
905
+ 13. Bridge: Returns response via socket
906
+ 14. CLI: Formats and displays to user
907
+
908
+
909
+ ### Error recovery
910
+
911
+ **Bridge crashes:**
912
+ 1. CLI detects socket connection failure
913
+ 2. Reads `~/.mcpc/sessions.json` for last known config
914
+ 3. Spawns new bridge process
915
+ 4. Bridge re-initializes connection to MCP server
916
+ 5. Continues request
917
+
918
+ **Network failures:**
919
+ 1. Bridge detects connection error
920
+ 2. Begins exponential backoff reconnection
921
+ 3. Queues incoming requests (up to 100, max 3min)
922
+ 4. On reconnect: drains queue
923
+ 5. On timeout: fails queued requests with network error
924
+
925
+ **Orphaned processes:**
926
+ 1. On startup, CLI scans `~/.mcpc/bridges/` directory
927
+ 2. For each socket file, attempts connection
928
+ 3. If connection fails, reads PID from sessions.json
929
+ 4. Checks if process exists (via `kill -0` or similar)
930
+ 5. If dead: removes socket file and session entry
931
+ 6. If alive but unresponsive: kills process, removes entries
932
+
933
+ ## Testing strategy
934
+
935
+ **Unit tests:**
936
+ - Core protocol implementation (mocked transports)
937
+ - Argument parsing and validation
938
+ - Output formatting (human and JSON modes)
939
+
940
+ **Integration tests:**
941
+ - Mock MCP server (simple Streamable HTTP + stdio servers)
942
+ - Bridge lifecycle (start, connect, restart, cleanup)
943
+ - Session management with file locking
944
+ - Stream reconnection logic
945
+
946
+ **E2E tests:**
947
+ - Real MCP server implementations
948
+ - Cross-runtime (Node.js and Bun)
949
+ - Interactive shell workflows
950
+
951
+ **Test utilities:**
952
+ - `examples/test-server/` - Reference MCP server for testing
953
+ - `test/mock-keychain.ts` - Mock OS keychain for testing
954
+
955
+ ## Troubleshooting
956
+
957
+ ### Common issues
958
+
959
+ **"Cannot connect to bridge"**
960
+ - Bridge may have crashed. Try: `mcpc <server> session @<session-name>`
961
+ - Check bridge is running: `ps aux | grep -e 'mcpc-bridge' -e '[m]cpc/dist/bridge'`
962
+ - Check socket exists: `ls ~/.mcpc/bridges/`
963
+
964
+ **"Session not found"**
965
+ - Session may have expired. Create new session: `mcpc <target> session @<session-name>`
966
+ - List existing sessions: `mcpc`
967
+
968
+ **"Authentication failed"**
969
+ - List saved profiles: `mcpc`
970
+ - Re-authenticate: `mcpc <server> login [--profile <name>]`
971
+ - For bearer tokens: provide `--header "Authorization: Bearer ${TOKEN}"` again
972
+
973
+ ### Debug mode
974
+
975
+ Enable detailed logging with `--verbose`:
976
+
977
+ ```bash
978
+ mcpc --verbose @apify tools-list
979
+ ```
980
+
981
+ This shows:
982
+ - Protocol negotiation details
983
+ - JSON-RPC request/response messages
984
+ - Streaming events and reconnection attempts
985
+ - Bridge communication (socket messages)
986
+ - File locking operations
987
+ - Prints server log messages with severity `debug`, `info`, and `notice` to standard output
988
+
989
+ ### Logs
990
+
991
+ Bridge processes log to:
992
+ - `~/.mcpc/logs/bridge-<session-name>.log`
993
+
994
+ Log rotation: Keep last 10MB per session, max 5 files.
995
+
996
+ ## Contributing
997
+
998
+ Contributions are welcome!
999
+
1000
+ ### Development setup
1001
+
1002
+ ```bash
1003
+ # Clone repository
1004
+ git clone https://github.com/apify/mcpc.git
1005
+ cd mcpc
1006
+
1007
+ # Install dependencies
1008
+ npm install
1009
+
1010
+ # Run tests
1011
+ npm test
1012
+
1013
+ # Build
1014
+ npm run build
1015
+
1016
+ # Test locally
1017
+ npm link
1018
+ mcpc --help
1019
+ ```
1020
+
1021
+ ### Release process
1022
+
1023
+ ```bash
1024
+ # Run tests
1025
+ npm test
1026
+
1027
+ # Build
1028
+ npm run build
1029
+
1030
+ # Bump version
1031
+ npm version patch|minor|major
1032
+
1033
+ # Publish
1034
+ npm publish
1035
+
1036
+ # Push tags
1037
+ git push --tags
1038
+ ```
1039
+
1040
+ Please open an issue or pull request on [GitHub](https://github.com/apify/mcpc).
1041
+
1042
+
1043
+ ### References
1044
+
1045
+ - [Official MCP documentation](https://modelcontextprotocol.io/llms.txt)
1046
+ - [Official TypeScript SDK for MCP servers and clients](https://www.npmjs.com/package/@modelcontextprotocol/sdk)
1047
+ - [MCP Inspector](https://github.com/modelcontextprotocol/inspector) - CLI client implementation for reference
1048
+
1049
+ ## Authors
1050
+
1051
+ Built by [Jan Curn](https://x.com/jancurn), [Apify](https://apify.com), and contributors welcome.
1052
+
1053
+ ## License
1054
+
1055
+ Apache-2.0 - see [LICENSE](./LICENSE) for details.
1056
+