Skip to content

feat(mcp): serve over a host-supplied transport - #716

Merged
steipete merged 5 commits into
openclaw:mainfrom
semyoren:feat/mcp-embedded-transport
Sep 13, 2026
Merged

feat(mcp): serve over a host-supplied transport#716
steipete merged 5 commits into
openclaw:mainfrom
semyoren:feat/mcp-embedded-transport

Conversation

@semyoren

@semyoren semyoren commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Allow applications embedding PeekabooCore to serve MCP over a connection they own with PeekabooMCPServer.serve(transport: any Transport). The supplied transport and existing stdio path share startup, completion waiting, SDK disconnection, and tool-context cleanup. Built-in HTTP/SSE transports remain unimplemented.

The SDK receive loop does not own incoming tool tasks, so Peekaboo now owns accepted calls explicitly. Shutdown closes admission, cancels and awaits startup, disconnects the transport once, and cancels/drains accepted calls before releasing their context. Competing EOF/cancellation/error paths await one shutdown task. A serving-generation task-local prevents delayed old calls from entering a new lifetime; concurrent serve calls refuse before connecting a second transport. Hosts must provide cooperative transports and tool services, and use one server per connection.

The server retains its isolated snapshot namespace. Tests seed the actual server owner and verify cleanup on completion/startup failure. The wire-decoder fixture uses a deterministic permission provider. In-memory test peers connect before clients initialize because the SDK otherwise drops early messages.

Validation:

  • 33 tests across PeekabooMCPServerTests, PeekabooMCPHostTransportTests, and EOFDrainingTransportTests passed. Held-call tests verify cancellation and drain; held-startup and held-disconnect tests verify ordering and exactly one disconnect. The earlier disconnect and accepted-call regressions failed before their repairs.
  • CLI build, SwiftFormat (0/1827 files require formatting), SwiftLint (zero violations), and docs lint passed.
  • Isolated Codex review of the complete candidate, including the pinned SDK implementation: no actionable P0–P2 findings after lifecycle repairs.
  • Native proof: a signed external SwiftPM consumer linked this checkout and SDK 0.12.1. A loopback-only NWListener accepted a real TCP connection, then passed NetworkTransport into the new API. A separate client initialized, listed all 24 tools, and disconnected; both processes exited 0. Accepted connections disable SDK reconnection so peer disconnect ends the serving lifetime.
  • The signed built CLI separately returned all 24 tools after stdin EOF, then exited 0, preserving final-response draining. Both native proofs were repeated after the final shutdown repair.
CLIENT initialized=peekaboo-mcp tools=24
HOST transport completed; tool-context teardown confirmed
STDIO tools=24 reply_after_input_eof=true exit=0

Unreleased includes contributor credit. Exact-head hosted checks must pass before merge.

Co-authored-by: Semyon Kotliarenko semako.ua@gmail.com

@clawsweeper

clawsweeper Bot commented Sep 12, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes MCP server lifecycle, cancellation, and tool-call admission—embedding hosts must use cooperative transports and one server per connection, but stdio path is refactored through the same code with added test coverage.

Overview
Adds PeekabooMCPServer.serve(transport: any Transport) so apps embedding PeekabooCore can run MCP on a connection they own (e.g. TCP/NetworkTransport), with the same wait-until-complete → disconnect → release tool context behavior as built-in stdio. Stdio serve(transport:port:) now funnels through shared run logic.

Lifecycle and shutdown are tightened for embedding: a serving generation (@TaskLocal + instance UUID) gates tool calls; shutdown stops accepting calls, cancels and drains in-flight tool tasks, coordinates startup cancel vs single server.stop(), and rejects concurrent serve on one server instance.

New PeekabooMCPHostTransportTests cover shutdown drain, single disconnect, concurrent-serve refusal, startup ordering on cancel, startup-failure snapshot cleanup, and happy-path completion; existing wire tests connect in-memory transports before the client and use a deterministic permissions stub. docs/MCP.md documents host transport contracts; CHANGELOG credits #716.

Reviewed by Cursor Bugbot for commit 2569466. Bugbot is set up for automated code reviews on this repo. Configure here.

Add `PeekabooMCPServer.serve(transport: any Transport)` for hosts that embed the
server instead of spawning the CLI — an application that links `PeekabooCore`
and speaks MCP over a connection it owns. The built-in stdio path and the new
entry point share one lifecycle: the server runs until the transport completes
and the tool context is released on the way out, on success and on failure
alike. `serve(transport: TransportType, port:)` keeps its behaviour, including
teardown on the not-implemented transports.
@semyoren
semyoren force-pushed the feat/mcp-embedded-transport branch from 528347c to d8ae61b Compare September 12, 2026 19:09
@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 12, 2026
@clawsweeper

clawsweeper Bot commented Sep 12, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed September 12, 2026, 9:45 PM ET / September 13, 2026, 01:45 UTC (Revision 6).

ClawSweeper review

What this changes

Adds a host-supplied MCP transport entry point with shared stdio lifecycle handling, cancellation and cleanup safeguards, regression tests, and embedding documentation.

Merge readiness

Ready for maintainer review

This PR supplies a useful embedding capability absent from current main and v4.3.4. The lifecycle implementation, focused regressions, and recorded native proof support keeping it as a landing candidate; no blocking defect was found.

Priority: P2
Reviewed head: 2569466cfac9c23e1ccfaaeb111de2237fd33338

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused additive API with coherent lifecycle handling, relevant native proof, and substantial regression coverage.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (terminal): The captured native TCP consumer exercises the new serve entry point through initialization, tool listing, peer disconnect, and confirmed teardown; separate native stdio output verifies final-response draining after EOF. Cancellation races are supported by focused tests rather than claimed as native observations.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The captured native TCP consumer exercises the new serve entry point through initialization, tool listing, peer disconnect, and confirmed teardown; separate native stdio output verifies final-response draining after EOF. Cancellation races are supported by focused tests rather than claimed as native observations.
Evidence reviewed 8 items Introduced lifecycle implementation: The complete server source uses one shutdown task, closes admission before capturing active calls, waits for startup before SDK disconnection, and drains accepted calls before releasing their context.
Current-main and release necessity: Current main exposes only the TransportType-based public serve method; arbitrary transports are accessible only through an internal testing helper. The v4.3.4 source has the same public limitation. No merged replacement was established.
Verified SDK lifecycle contract: The changed source imports MCP and directly calls Server.start, stop, and waitUntilCompleted; Peekaboo's Package.resolved pins SDK 0.12.1. At that revision, start creates inheriting tasks for reception and request dispatch, stop disconnects the connection without draining incoming handlers, and waitUntilCompleted awaits only the receive task. This supports the task-local generation and explicit call ownership. The SDK tree contained no AGENTS.md.
Findings None None.
Security None None.

How this fits together

Peekaboo’s MCP server exposes desktop automation tools to external clients. The changed lifecycle accepts either standard input/output or a host-owned connection, dispatches tool requests, and releases connection-scoped resources when serving ends.

flowchart TD
 A[Standard input and output] --> C[MCP server lifecycle]
 B[Host-owned connection] --> C
 C --> D[Tool-call admission]
 D --> E[Desktop automation tools]
 C --> F[Completion or cancellation]
 F --> G[Disconnect and drain calls]
 G --> H[Release tool context]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and test growth Production +107 net lines; tests +329 net lines Production growth supports explicit lifecycle ownership, with most added code devoted to focused regression coverage.

Technical review

Best possible solution:

Keep the additive embedding entry point on the shared lifecycle, preserving stdio response draining and the documented host-owned connection policy.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a feature request; source confirms the public embedding entry point is absent from main, and the supplied native transcript demonstrates the added transport lifecycle.

Is this the best way to solve the issue?

Yes. An additive overload using the SDK transport contract and a shared cleanup path avoids duplicating the MCP server or adding a competing built-in listener.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 6916848ab2fa.

Labels

Label justifications:

  • P2: This is a bounded embedding improvement with shared MCP lifecycle coverage and no demonstrated urgent user regression.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The captured native TCP consumer exercises the new serve entry point through initialization, tool listing, peer disconnect, and confirmed teardown; separate native stdio output verifies final-response draining after EOF. Cancellation races are supported by focused tests rather than claimed as native observations.
  • proof: sufficient: Contributor real behavior proof is sufficient. The captured native TCP consumer exercises the new serve entry point through initialization, tool listing, peer disconnect, and confirmed teardown; separate native stdio output verifies final-response draining after EOF. Cancellation races are supported by focused tests rather than claimed as native observations.

Evidence

What I checked:

  • Introduced lifecycle implementation: The complete server source uses one shutdown task, closes admission before capturing active calls, waits for startup before SDK disconnection, and drains accepted calls before releasing their context. (Core/PeekabooCore/Sources/PeekabooAgentRuntime/MCP/Server/PeekabooMCPServer.swift:258, 2569466cfac9)
  • Current-main and release necessity: Current main exposes only the TransportType-based public serve method; arbitrary transports are accessible only through an internal testing helper. The v4.3.4 source has the same public limitation. No merged replacement was established. (Core/PeekabooCore/Sources/PeekabooAgentRuntime/MCP/Server/PeekabooMCPServer.swift, 6916848ab2fa)
  • Verified SDK lifecycle contract: The changed source imports MCP and directly calls Server.start, stop, and waitUntilCompleted; Peekaboo's Package.resolved pins SDK 0.12.1. At that revision, start creates inheriting tasks for reception and request dispatch, stop disconnects the connection without draining incoming handlers, and waitUntilCompleted awaits only the receive task. This supports the task-local generation and explicit call ownership. The SDK tree contained no AGENTS.md. (Sources/MCP/Server/Server.swift:205, a0ae212ebf6e)
  • Focused regression coverage: Read all six new host-transport test functions, covering accepted-call cancellation and drain, single disconnection, concurrent-serve refusal, startup cancellation ordering, failed-startup owner cleanup, and completion disconnection. The existing server suite also gains a client initialization/listing/completion test. Tests were inspected, not executed during this read-only review. (Core/PeekabooCore/Tests/PeekabooTests/MCP/PeekabooMCPHostTransportTests.swift:9, 2569466cfac9)
  • Captured native behavior proof: The supplied complete PR body records a signed external SwiftPM consumer using SDK 0.12.1, a loopback NWListener and real NetworkTransport, a separate client initializing and listing 24 tools, and confirmed host teardown with both processes exiting successfully. A separate signed CLI run returned 24 tools after stdin EOF and exited successfully. Both were reported repeated after the final shutdown repair. These are transport lifecycle and EOF observations, not native cancellation proof; held-call cancellation coverage is supplemental test evidence. Captured context sourceRevision: 6b3a7b8b26dcf29cb56dc09f75188d94259085398173da2267e19bd1fbe36f6f. (2569466cfac9)
  • Embedding contract and existing stdio behavior: Documentation assigns connection policy to the host, requires cooperative transports and services, specifies one server per connection, and disables SDK reconnection for accepted NetworkTransport connections. The stdio path retains EOFDrainingTransport, which waits for response completion before ending reception. (docs/MCP.md:60, 2569466cfac9)

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (5 earlier review cycles)
  • reviewed 2026-09-12T19:13:30.723Z sha d8ae61b :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-12T19:27:24.605Z sha d8ae61b :: needs maintainer review before merge. :: none
  • reviewed 2026-09-12T22:56:19.909Z sha 71ffab4 :: needs maintainer review before merge. :: none
  • reviewed 2026-09-12T23:21:36.669Z sha ad4b686 :: needs maintainer review before merge. :: none
  • reviewed 2026-09-13T01:10:51.251Z sha 0bdf2f9 :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Sep 12, 2026
steipete and others added 4 commits September 12, 2026 15:50
Reconcile the contributor transport entry point with current main, prove server-owned cleanup on success and startup failure, and isolate wire-decoder permission fixtures.

Co-authored-by: Semyon Kotliarenko <semako.ua@gmail.com>
SDK completion only awaits the receive loop and retains its transport. Stop the SDK session before releasing tool context on both completion and startup failure, with disconnect regressions and live TCP/stdio EOF proof.
Co-authored-by: Semyon Kotliarenko <semako.ua@gmail.com>
@steipete
steipete merged commit 8ecf8a9 into openclaw:main Sep 13, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants