mcpc is under active development and some things might not work 100% yet. You have been warned.
Contributions are welcome!
- Delightful for humans and AI agents alike (interactive + scripting)
- Avoid unnecessary interaction loops, provide sufficient context, yet be concise (save tokens)
- One clear way to do things (orthogonal commands, no surprises)
- Do not ask for user input (except
shellandlogin, no unexpected OAuth flows) - Be forgiving, always help users make progress (great errors + guidance)
- Be consistent with the MCP specification, with
--jsonstrictly - Minimal and portable (few deps, cross-platform)
- Keep backwards compatibility to the maximum extent possible
- No slop!
When writing examples, tests, README snippets, or help text that reference a remote MCP server,
please use mcp.apify.com rather than placeholders like mcp.example.com or arbitrary third-party
servers. The motivation is purely practical: mcp.apify.com is a real, publicly available MCP
server that works out of the box, so readers can copy-paste examples and run them unchanged.
Placeholders like mcp.example.com don't resolve to anything, which forces every reader to
substitute a URL before they can try an example.
This is a soft convention for documentation consistency, not a license condition — mcpc is distributed under Apache 2.0 and you are free to use it with any MCP server.
# Clone repository
git clone https://github.com/apify/mcpc.git
cd mcpc
# Install dependencies
npm install
# Run tests
npm test
# Build
npm run build
# Test locally
npm link
mcpc --helpSee test/README.md for details on running unit and E2E tests.
npm test # Run all tests (unit + e2e)
npm run test:unit # Run unit tests only
npm run test:e2e # Run e2e tests only
npm run test:coverage # Run all tests with coverageE2E tests require mcpc to be built first:
npm run build
npm linkSome E2E tests connect to a real remote MCP server and require OAuth authentication profiles. Without these profiles, the affected tests will be skipped or fail.
To set them up, create a free Apify account (you can use the same account for both profiles), then run:
mcpc login mcp.apify.com --profile e2e-test1
mcpc login mcp.apify.com --profile e2e-test2The test runner does not take any destructive actions.
Use the release script to publish a new version of the @apify/mcpc package on NPM:
npm run release # patch version bump (0.1.2 → 0.1.3)
npm run release:minor # minor version bump (0.1.2 → 0.2.0)
npm run release:major # major version bump (0.1.2 → 1.0.0)The script automatically:
- Ensures you're on
mainbranch - Ensures working directory is clean (no uncommitted changes)
- Ensures branch is up-to-date with remote
- Runs lint, build, and tests
- Bumps the version in package.json
- Creates a git commit and annotated tag (
v{version}) - Pushes the commit and tag to origin
- Publishes to npm
After publishing, create a GitHub release at the provided link.
mcpc (single package)
├── src/
│ ├── core/ # Core MCP protocol implementation
│ ├── bridge/ # Bridge process logic
│ ├── cli/ # CLI interface
│ └── lib/ # Shared utilities
├── bin/
│ ├── mcpc # Main CLI executable
│ └── mcpc-bridge # Bridge process executable
└── test/
└── e2e/
└── server/ # Test MCP server for E2E tests
Implemented with minimal dependencies to support both Node.js (≥18.0.0) and Bun (≥1.0.0).
Core responsibilities:
- Transport selection and initialization (Streamable HTTP vs stdio)
- MCP protocol implementation and version negotiation
- Session state machine management
- Streamable HTTP connection management (reconnection with exponential backoff)
- Request/response correlation (JSON-RPC style with request IDs)
- Multiplexing concurrent requests (up to 10 concurrent)
- Event emitter for async notifications
Key dependencies:
- Native
fetchAPI (available in Node.js 18+ and Bun) - Native process APIs for stdio transport
- Minimal: UUID generation, event emitter abstraction
Implemented as a separate executable (mcpc-bridge) that maintains persistent connections.
Bridge responsibilities:
- Session persistence (reads/writes
~/.mcpc/sessions.jsonwith file locking) - Process lifecycle management for local package servers
- Stdio framing and protocol handling
- Unix domain socket server for CLI communication
- Heartbeat mechanism for health monitoring
- Orphaned process cleanup on startup
IPC protocol:
- Unix domain sockets (located in
~/.mcpc/bridges/<session-name>.sock) - Named pipes on Windows
- JSON-RPC style messages over socket
- Control messages: init, request, cancel, close, health-check
Bridge discovery:
- CLI reads
~/.mcpc/sessions.jsonto find socket path and PID - Validates bridge is alive (connect to socket + health-check)
- Auto-restarts crashed bridges (detected via socket connection failure)
- Cleanup: removes stale socket files for dead processes
Concurrency safety:
~/.mcpc/sessions.jsonprotected with file locking (proper-lockfilepackage)- Atomic writes (write to temp file, then rename)
- Lock timeout: 5 seconds (fails if can't acquire lock)
The main mcpc command provides the user interface.
CLI responsibilities:
- Argument parsing using Commander.js
- Output formatting (human-readable vs
--json) - Bridge lifecycle: start/connect/stop
- Communication with bridge via socket
- Interactive shell (REPL using Node.js
readline) - Configuration file loading (standard MCP JSON format)
- Credential management (OS keychain via
keytarpackage)
Shell implementation:
- Built on Node.js
readlinemodule for input handling with history support - Command history using
~/.mcpc/history(last 1000 commands) - Real-time notification display during shell sessions
- Graceful exit handling (cleanup on Ctrl+C/Ctrl+D)
- User:
mcpc https://mcp.apify.com session @apify - CLI: Atomically creates session entry in
~/.mcpc/sessions.json - CLI: Spawns bridge process (
mcpc-bridge) - Bridge: Creates Unix socket at
~/.mcpc/bridges/apify.sock - Bridge: Performs MCP initialization handshake with server:
- Sends initialize request with protocol version and capabilities
- Receives server info, version, and capabilities
- Sends initialized notification to activate session
- Bridge: Updates session in
~/.mcpc/sessions.json(adds PID, socket path, protocol version) - CLI: Confirms session created
- User: mcpc @apify tools-list
- CLI: Reads
~/.mcpc/sessions.json, finds socket path - CLI: Connects to bridge socket
- CLI: Sends
tools/listJSON-RPC request via socket - Bridge: Forwards to MCP server via Streamable HTTP
- Bridge: Returns response via socket
- CLI: Formats and displays to user
Bridge crashes:
- CLI detects socket connection failure
- Reads
~/.mcpc/sessions.jsonfor last known config - Spawns new bridge process
- Bridge re-initializes connection to MCP server
- Continues request
Network failures:
- Bridge detects connection error
- Begins exponential backoff reconnection
- Queues incoming requests (up to 100, max 3min)
- On reconnect: drains queue
- On timeout: fails queued requests with network error
Orphaned processes:
- On startup, CLI scans
~/.mcpc/bridges/directory - For each socket file, attempts connection
- If connection fails, reads PID from sessions.json
- Checks if process exists (via
kill -0or similar) - If dead: removes socket file and session entry
- If alive but unresponsive: kills process, removes entries
- Official MCP documentation
- Official TypeScript SDK for MCP servers and clients
- MCP Inspector - CLI client implementation for reference
Please open an issue or pull request on GitHub.