@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.
- package/.claude/settings.local.json +36 -0
- package/.eslintrc.json +44 -0
- package/.idea/codeStyles/Project.xml +60 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +11 -0
- package/.idea/prettier.xml +6 -0
- package/.idea/vcs.xml +6 -0
- package/.idea/workspace.xml +572 -0
- package/LICENSE +201 -0
- package/PHASE1-SUMMARY.md +269 -0
- package/PUBLISHING.md +111 -0
- package/README.md +1056 -0
- package/TESTING.md +212 -0
- package/TODOs.md +94 -0
- package/bin/mcpc +9 -0
- package/bin/mcpc-bridge +9 -0
- package/dist/bridge/index.d.ts +3 -0
- package/dist/bridge/index.d.ts.map +1 -0
- package/dist/bridge/index.js +587 -0
- package/dist/bridge/index.js.map +1 -0
- package/dist/cli/commands/auth.d.ts +9 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +81 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/clean.d.ts +11 -0
- package/dist/cli/commands/clean.d.ts.map +1 -0
- package/dist/cli/commands/clean.js +224 -0
- package/dist/cli/commands/clean.js.map +1 -0
- package/dist/cli/commands/index.d.ts +8 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +8 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/logging.d.ts +3 -0
- package/dist/cli/commands/logging.d.ts.map +1 -0
- package/dist/cli/commands/logging.js +22 -0
- package/dist/cli/commands/logging.js.map +1 -0
- package/dist/cli/commands/prompts.d.ts +6 -0
- package/dist/cli/commands/prompts.d.ts.map +1 -0
- package/dist/cli/commands/prompts.js +27 -0
- package/dist/cli/commands/prompts.js.map +1 -0
- package/dist/cli/commands/resources.d.ts +11 -0
- package/dist/cli/commands/resources.d.ts.map +1 -0
- package/dist/cli/commands/resources.js +70 -0
- package/dist/cli/commands/resources.js.map +1 -0
- package/dist/cli/commands/sessions.d.ts +28 -0
- package/dist/cli/commands/sessions.d.ts.map +1 -0
- package/dist/cli/commands/sessions.js +356 -0
- package/dist/cli/commands/sessions.js.map +1 -0
- package/dist/cli/commands/tools.d.ts +8 -0
- package/dist/cli/commands/tools.d.ts.map +1 -0
- package/dist/cli/commands/tools.js +54 -0
- package/dist/cli/commands/tools.js.map +1 -0
- package/dist/cli/commands/utilities.d.ts +3 -0
- package/dist/cli/commands/utilities.d.ts.map +1 -0
- package/dist/cli/commands/utilities.js +20 -0
- package/dist/cli/commands/utilities.js.map +1 -0
- package/dist/cli/helpers.d.ts +18 -0
- package/dist/cli/helpers.d.ts.map +1 -0
- package/dist/cli/helpers.js +165 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +326 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.d.ts +18 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +221 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/parser.d.ts +19 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +168 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/cli/shell-parser.d.ts +5 -0
- package/dist/cli/shell-parser.d.ts.map +1 -0
- package/dist/cli/shell-parser.js +38 -0
- package/dist/cli/shell-parser.js.map +1 -0
- package/dist/cli/shell.d.ts +2 -0
- package/dist/cli/shell.d.ts.map +1 -0
- package/dist/cli/shell.js +277 -0
- package/dist/cli/shell.js.map +1 -0
- package/dist/cli/tool-result.d.ts +2 -0
- package/dist/cli/tool-result.d.ts.map +1 -0
- package/dist/cli/tool-result.js +19 -0
- package/dist/cli/tool-result.js.map +1 -0
- package/dist/core/factory.d.ts +19 -0
- package/dist/core/factory.d.ts.map +1 -0
- package/dist/core/factory.js +55 -0
- package/dist/core/factory.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +4 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/mcp-client.d.ts +30 -0
- package/dist/core/mcp-client.d.ts.map +1 -0
- package/dist/core/mcp-client.js +215 -0
- package/dist/core/mcp-client.js.map +1 -0
- package/dist/core/transports.d.ts +12 -0
- package/dist/core/transports.d.ts.map +1 -0
- package/dist/core/transports.js +65 -0
- package/dist/core/transports.js.map +1 -0
- package/dist/lib/auth/auth-profiles.d.ts +7 -0
- package/dist/lib/auth/auth-profiles.d.ts.map +1 -0
- package/dist/lib/auth/auth-profiles.js +105 -0
- package/dist/lib/auth/auth-profiles.js.map +1 -0
- package/dist/lib/auth/keychain.d.ts +22 -0
- package/dist/lib/auth/keychain.d.ts.map +1 -0
- package/dist/lib/auth/keychain.js +92 -0
- package/dist/lib/auth/keychain.js.map +1 -0
- package/dist/lib/auth/oauth-flow.d.ts +7 -0
- package/dist/lib/auth/oauth-flow.d.ts.map +1 -0
- package/dist/lib/auth/oauth-flow.js +236 -0
- package/dist/lib/auth/oauth-flow.js.map +1 -0
- package/dist/lib/auth/oauth-provider.d.ts +24 -0
- package/dist/lib/auth/oauth-provider.d.ts.map +1 -0
- package/dist/lib/auth/oauth-provider.js +144 -0
- package/dist/lib/auth/oauth-provider.js.map +1 -0
- package/dist/lib/auth/oauth-token-manager.d.ts +25 -0
- package/dist/lib/auth/oauth-token-manager.d.ts.map +1 -0
- package/dist/lib/auth/oauth-token-manager.js +70 -0
- package/dist/lib/auth/oauth-token-manager.js.map +1 -0
- package/dist/lib/auth/oauth-utils.d.ts +14 -0
- package/dist/lib/auth/oauth-utils.d.ts.map +1 -0
- package/dist/lib/auth/oauth-utils.js +68 -0
- package/dist/lib/auth/oauth-utils.js.map +1 -0
- package/dist/lib/auth/token-refresh.d.ts +2 -0
- package/dist/lib/auth/token-refresh.d.ts.map +1 -0
- package/dist/lib/auth/token-refresh.js +70 -0
- package/dist/lib/auth/token-refresh.js.map +1 -0
- package/dist/lib/bridge-client.d.ts +23 -0
- package/dist/lib/bridge-client.d.ts.map +1 -0
- package/dist/lib/bridge-client.js +181 -0
- package/dist/lib/bridge-client.js.map +1 -0
- package/dist/lib/bridge-manager.d.ts +17 -0
- package/dist/lib/bridge-manager.d.ts.map +1 -0
- package/dist/lib/bridge-manager.js +240 -0
- package/dist/lib/bridge-manager.js.map +1 -0
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +116 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/errors.d.ts +23 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +81 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/file-lock.d.ts +2 -0
- package/dist/lib/file-lock.d.ts.map +1 -0
- package/dist/lib/file-lock.js +46 -0
- package/dist/lib/file-lock.js.map +1 -0
- package/dist/lib/file-logger.d.ts +19 -0
- package/dist/lib/file-logger.d.ts.map +1 -0
- package/dist/lib/file-logger.js +126 -0
- package/dist/lib/file-logger.js.map +1 -0
- package/dist/lib/index.d.ts +6 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +6 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/logger.d.ts +24 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +189 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/session-client.d.ts +28 -0
- package/dist/lib/session-client.d.ts.map +1 -0
- package/dist/lib/session-client.js +104 -0
- package/dist/lib/session-client.js.map +1 -0
- package/dist/lib/sessions.d.ts +9 -0
- package/dist/lib/sessions.d.ts.map +1 -0
- package/dist/lib/sessions.js +116 -0
- package/dist/lib/sessions.js.map +1 -0
- package/dist/lib/types.d.ts +117 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/utils.d.ts +29 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +173 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +71 -0
- 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
|
+
|