fix: wire STDIO attach for docker-image MCP servers #49

Merged
michal merged 1 commits from feat/k8s-operator into main 2026-04-12 21:27:15 +00:00
Owner

Summary

  • Commit 1bd5087 added attachInteractive to the orchestrator but never called it — sendViaPersistentAttach was promised in the commit message and missing from the diff. Docker-image STDIO servers like gitea-mcp ended up with a bogus node dist/index.js workaround that silently failed on every exec, leaving Claude with empty tool lists.
  • PersistentStdioClient now takes a StdioMode discriminated union ({kind:'exec'} | {kind:'attach'}). mcp-proxy-service dispatches: command → exec, packageName → runtime exec, dockerImage-only → attach to PID 1. Error serialization no longer drops non-Error objects as [object Object].
  • templates/gitea.yaml: dropped the workaround command — image CMD runs as PID 1 and mcpd attaches.

Test plan

  • pnpm --filter mcpd exec vitest run — 634/634 passing (incl. 4 new persistent-stdio tests covering both modes)
  • gitea via proxy (attach mode): 49 tools surface, real tools/call round-trip reaches Gitea
  • aws-docs (exec via packageName): 4 tools, no regression
  • docmost (exec via command, kept as-is): 11 tools, no regression

Required follow-up (separate repo)

mcpd's k8s Role needs pods/attach alongside pods/exec. Updated in kubernetes-deployment/deployments/mcpctl/server.ts and kubectl-patched on the live cluster; the Pulumi apply will pick it up.

## Summary - Commit 1bd5087 added `attachInteractive` to the orchestrator but never called it — `sendViaPersistentAttach` was promised in the commit message and missing from the diff. Docker-image STDIO servers like gitea-mcp ended up with a bogus `node dist/index.js` workaround that silently failed on every exec, leaving Claude with empty tool lists. - `PersistentStdioClient` now takes a `StdioMode` discriminated union (`{kind:'exec'}` | `{kind:'attach'}`). `mcp-proxy-service` dispatches: command → exec, packageName → runtime exec, dockerImage-only → attach to PID 1. Error serialization no longer drops non-Error objects as `[object Object]`. - `templates/gitea.yaml`: dropped the workaround command — image CMD runs as PID 1 and mcpd attaches. ## Test plan - [x] `pnpm --filter mcpd exec vitest run` — 634/634 passing (incl. 4 new persistent-stdio tests covering both modes) - [x] gitea via proxy (attach mode): 49 tools surface, real `tools/call` round-trip reaches Gitea - [x] aws-docs (exec via packageName): 4 tools, no regression - [x] docmost (exec via command, kept as-is): 11 tools, no regression ## Required follow-up (separate repo) mcpd's k8s Role needs `pods/attach` alongside `pods/exec`. Updated in `kubernetes-deployment/deployments/mcpctl/server.ts` and kubectl-patched on the live cluster; the Pulumi apply will pick it up.
michal added 1 commit 2026-04-12 21:26:46 +00:00
fix: actually wire STDIO attach for docker-image MCP servers
All checks were successful
CI/CD / typecheck (pull_request) Successful in 52s
CI/CD / lint (pull_request) Successful in 1m43s
CI/CD / test (pull_request) Successful in 1m2s
CI/CD / build (pull_request) Successful in 1m45s
CI/CD / publish-rpm (pull_request) Has been skipped
CI/CD / publish-deb (pull_request) Has been skipped
CI/CD / smoke (pull_request) Successful in 9m51s
9ff2dcc3d9
Commit 1bd5087 added attachInteractive to the orchestrator interface
but never hooked it up in mcp-proxy-service — sendViaPersistentAttach
was promised in the commit message but missing from the diff. Servers
with a distroless image whose entrypoint IS the MCP server (gitea-mcp)
ended up needing a bogus `command: [node, dist/index.js]` workaround
that silently failed on every exec, leaving clients with empty tool
lists.

Changes:
- PersistentStdioClient: take a StdioMode discriminated union. Exec
  mode runs a command via execInteractive; attach mode talks to PID 1
  via attachInteractive.
- mcp-proxy-service: dispatch by config — command → exec; packageName
  → exec via runtime runner; dockerImage-only → attach. Error
  serialization no longer drops non-Error objects as "[object Object]".
- templates/gitea.yaml: remove the command workaround; the image CMD
  runs as PID 1 and mcpd attaches.
- Add unit tests covering both modes and the unsupported-orchestrator
  paths.

Also required (separate repo): mcpd's k8s Role needed pods/attach
added alongside pods/exec; updated in kubernetes-deployment/…/mcpctl/server.ts
and kubectl-patched on the live cluster.

Verified end-to-end against mcpctl.ad.itaz.eu:
- gitea (attach): 49 tools listed, real tools/call round-trip.
- aws-docs (exec via packageName): 4 tools, no regression.
- docmost (exec via command): 11 tools, no regression.
- mcpd suite: 634/634 passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
michal merged commit c968d76e00 into main 2026-04-12 21:27:15 +00:00
michal deleted branch feat/k8s-operator 2026-04-12 21:27:15 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: michal/mcpctl#49