feat: expose daemon-managed MCP servers via serve#172
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bfe727150c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| export function encodeToolName(server: string, tool: string): string { | ||
| return `${server}${TOOL_SEPARATOR}${tool}`; |
There was a problem hiding this comment.
Escape bridged names before joining them
When two served keep-alive servers can be named with the separator (config keys are arbitrary strings), this encoding can produce duplicate exposed tool names: for example server alpha tool beta__ping and server alpha__beta tool ping both become alpha__beta__ping. Because decodeToolName uses longest-prefix matching, calls to the first tool are routed to alpha__beta instead, so one advertised tool becomes uncallable. Please reject/escape __ in server names or use an unambiguous encoding.
Useful? React with 👍 / 👎.
|
Landed on I could not update this PR branch directly because Proof run locally before landing:
Thanks @zm2231. For future PRs, enabling "Allow edits by maintainers" lets us push small fixups directly to the PR branch. |
Summary
Adds
mcporter serve, a bridge that exposes daemon-managed keep-alive servers as a single MCP endpoint.The mcporter daemon already keeps keep-alive servers warm between calls, but MCP clients such as Claude Code and Codex cannot connect to it directly because it speaks mcporter's private Unix-socket protocol. Without a bridge, each client session still launches its own copy of every configured MCP server.
mcporter servesits between the daemon-backed runtime and any MCP client. Clients register one MCP server, while mcporter handles tool discovery, namespacing, routing, and lifecycle reuse through the daemon.What's Included
src/serve.tsMcpServerbridge.Runtimesurface (listTools/callTool) rather than a rawDaemonClient.server__toolto avoid cross-server name collisions.__cannot collide.src/cli/serve-command.tsmcporter serve.createRuntime+DaemonClient+createKeepAliveRuntime.--http <port>on127.0.0.1by default.--host <host>for explicit non-local binding.--servers a,b,cto restrict exposed keep-alive servers.Review Fixes
--http=instead of treating them as port0.createKeepAliveRuntimewrapper to prove served keep-alive tool listing/calls route through the daemon client and trigger restart/close behavior on fatal daemon errors.alpha+beta__pingandalpha__beta+ping.Tests
Added coverage in:
tests/serve.test.tstests/cli-serve-command.test.tstests/cli-serve-runtime.test.tsValidated with:
./runner pnpm check./runner pnpm docs:list./runner pnpm exec tsx src/cli.ts serve --help./runner pnpm testResult:
Forward Compatibility
If
createManagedRuntime()lands later, onlyserve-command.tsneeds to swap its manual runtime wiring for that helper.serve.tsshould not need changes because it already depends on the runtime interface rather than daemon construction details.