feat(core): server→client request/response primitive (#960)#962
Conversation
Ships the JSON-RPC request/response primitive that three queued MCP adoption issues need: roots/list (#880), sampling/createMessage (#876), and elicitation/create (#877). Without it, each would reinvent the same correlation/timeout/abort plumbing. - src/mcp-server.ts: - pendingClientRequests map + monotonic counter; ids prefixed `oc-s2c-` so they cannot collide with client-allocated ids. - clientCapabilities cache populated in handleInitialize. - requestFromClient<T>(method, params?, { timeoutMs?, signal? }): allocates id, registers one-shot resolver, serializes via existing sendResponse path, returns typed Promise. - Response-routing fork at the top of handleMessage — if incoming message has id+(result|error)+no-method, route to pending resolver BEFORE the regular method-required validation. - _stopInternal rejects every pending request with s2c_aborted:connection_closed so dropped connections don't hang Promises forever. - tests/unit/s2c-request.test.ts: 10 cases — round-trip, concurrent independent requests, error mapping, timeout, AbortSignal, pre- aborted signal, stale-response drop, _stopInternal drain, clientCapabilities populated/empty. Failure modes documented in the PR body so downstream consumers can match against stable error messages. Portability-Harness Contract: P1 (inline within tool calls, no orchestration), P3 (no deps, no outbound, no native). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee16ef11d5
ℹ️ 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".
| ...(params ? { params } : {}), | ||
| }; | ||
| try { | ||
| this.sendResponse(request as unknown as MCPResponse); |
There was a problem hiding this comment.
Route server→client RPC to one session
requestFromClient sends its JSON-RPC request through sendResponse, but in HTTP mode HTTPTransport.send() broadcasts every payload to all SSE connections (src/transports/http.ts lines 349-353). Because pending resolvers are keyed only by request id, whichever client responds first can satisfy (or fail) another session’s in-flight call, and clients that don't support the method will still receive it and may trigger timeouts; this appears whenever multiple MCP sessions are connected concurrently.
Useful? React with 👍 / 👎.
Brings the server-to-client request primitive onto the current develop base and resolves the overlap with progress notification plumbing in src/mcp-server.ts. Constraint: PR #962 was marked conflicting against develop and could not merge as submitted. Rejected: Reimplementing progress reporting or changing the s2c request contract | this update only preserves both already-reviewed code paths. Confidence: medium Scope-risk: moderate Directive: Keep requestFromClient response routing ahead of request validation when editing handleMessage. Tested: npm test -- --runInBand tests/unit/s2c-request.test.ts; /Users/jh0927/openchrome/node_modules/.bin/tsc -p tsconfig.json --pretty false Not-tested: Full CI matrix locally; remote CI must verify platform coverage. Co-authored-by: OmX <omx@oh-my-codex.dev>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3af2a26bc0
ℹ️ 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".
| // #960 — reject every in-flight server→client request before the | ||
| // transport tears down so callers don't hang forever on Promises that | ||
| // can never resolve. | ||
| this.rejectAllPendingS2cRequests('s2c_aborted:connection_closed'); |
There was a problem hiding this comment.
Reject pending S2C requests on HTTP session delete
This cleanup only runs during full server shutdown, so in HTTP mode a client DELETE /mcp (which just removes that session/SSE connection in src/transports/http.ts around handleDelete) does not reject any in-flight requestFromClient promise for that session. In that disconnect path, callers wait until timeoutMs instead of getting the documented immediate s2c_aborted:connection_closed, which can stall tool flows for up to 30s per request.
Useful? React with 👍 / 👎.
Routes server-initiated JSON-RPC requests to the owning MCP SSE session, ignores wrong-session responses, drains pending requests on HTTP session delete, and caches initialized capabilities per MCP session. Constraint: PR #962 must be safe for multiple HTTP/SSE clients and tenants, not only stdio or single-client HTTP. Rejected: Broadcasting server-to-client requests over the generic HTTP send path | it allows wrong-client responses and cross-session leakage. Confidence: medium Scope-risk: moderate Directive: Keep requestFromClient response routing session-bound whenever HTTP transport context is available. Tested: npm run build; npm run lint:changed; npx jest --config jest.ci.config.js --runInBand tests/unit/s2c-request.test.ts tests/tools/tabs.test.ts tests/cross-env/cursor-verification.test.ts Not-tested: Full remote CI matrix locally. Co-authored-by: OmX <omx@oh-my-codex.dev>
Routes server-initiated JSON-RPC requests to the owning MCP SSE session, ignores wrong-session responses, drains pending requests on HTTP session delete, and caches initialized capabilities per MCP session. Constraint: PR #962 must be safe for multiple HTTP/SSE clients and tenants, not only stdio or single-client HTTP. Rejected: Broadcasting server-to-client requests over the generic HTTP send path | it allows wrong-client responses and cross-session leakage. Confidence: medium Scope-risk: moderate Directive: Keep requestFromClient response routing session-bound whenever HTTP transport context is available. Tested: npm run build; npm run lint:changed; npx jest --config jest.ci.config.js --runInBand tests/unit/s2c-request.test.ts tests/tools/tabs.test.ts tests/cross-env/cursor-verification.test.ts Not-tested: Full remote CI matrix locally. Co-authored-by: OmX <omx@oh-my-codex.dev>
414a470 to
801bf97
Compare
Resolves the latest develop overlap in tab tests while preserving the session-bound server-to-client request fixes. Constraint: PR #962 became conflicting again after develop advanced. Rejected: Reworking tab behavior in this PR | only merged current test expectations from develop. Confidence: high Scope-risk: narrow Directive: Keep this PR focused on the S2C primitive and its HTTP/session safety. Tested: pending rerun after conflict resolution; prior build/lint and targeted Jest passed before this base refresh. Not-tested: Full remote CI matrix locally. Co-authored-by: OmX <omx@oh-my-codex.dev>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f324ed4d98
ℹ️ 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".
| if (mcpSessionId && transport && typeof transport.sendToSession === 'function') { | ||
| const sent = transport.sendToSession(mcpSessionId, request as unknown as MCPResponse); | ||
| if (!sent) { | ||
| cleanup(); |
There was a problem hiding this comment.
Preserve HTTP session routing in dual-transport mode
When requestFromClient runs during an HTTP request in --transport both, this.transport is still the stdio transport (see src/index.ts where server.start() is called before wiring HTTP). Because stdio has no sendToSession, this branch falls back to sendResponse, which writes the server→client RPC onto stdio instead of the HTTP session’s SSE stream. That makes S2C methods (roots/sampling/elicitation) time out for HTTP clients in dual mode even though the request context has a valid mcpSessionId.
Useful? React with 👍 / 👎.
Clears and unrefs the stop-path cleanup timeout after cleanup wins the race so S2C shutdown tests do not leave a pending timer in CI workers. Constraint: Windows CI failed in the test phase after the S2C tests exercised _stopInternal. Rejected: Removing the shutdown rejection test | it covers the connection-closed S2C contract. Confidence: medium Scope-risk: narrow Directive: Keep shutdown timeout best-effort without leaking handles after successful cleanup. Tested: npm run build; npx jest --config jest.ci.config.js --runInBand tests/unit/s2c-request.test.ts Not-tested: Full Windows CI locally. Co-authored-by: OmX <omx@oh-my-codex.dev>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d750b8a85d
ℹ️ 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".
| mcpSessionId: sessionId, | ||
| }, | ||
| () => this.processBatch(parsed, sessionId, tenantId, signal, principal), |
There was a problem hiding this comment.
Propagate assigned batch session id into request context
When a batch arrives without Mcp-Session-Id, processBatch() may assign a new session id (for initialize), but the async request context is created earlier with mcpSessionId: sessionId still undefined. Any downstream requestFromClient() call in that same batch then misses session affinity and falls back to sendResponse broadcast routing, allowing cross-session delivery/response matching instead of targeting the new session. This is observable in HTTP batch flows that combine initialize with later requests that trigger server→client RPC.
Useful? React with 👍 / 👎.
Constraint: PR #962 must remain mergeable across Windows and current develop tool tiers. Rejected: Updating the JSON fixture contents | the response shape did not change; only line-ending checkout behavior differed. Confidence: high Scope-risk: narrow Directive: Keep fixture comparisons semantic across platform line endings while preserving strict JSON shape checks. Tested: CI=true npx jest --config jest.ci.config.js --runInBand tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts Not-tested: Full CI matrix pending on GitHub runners. Co-authored-by: OmX <omx@oh-my-codex.dev>
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Merge the latest develop into PR #951 and resolve the remaining CI-fixture comments so the DOM and benchmark hardening branch stays conflict-free after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests that #951 had already stabilized. Rejected: Reverting either fixture stabilization | both branches agree on the 45-tool count and CRLF-normalized console baseline. Confidence: high Scope-risk: narrow Directive: Keep exact tool-count assertions documented when Tier 1 tool exposure changes. Tested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite skipped locally by platform guard); git diff --check for touched files. Not-tested: Full GitHub Actions matrix after this refresh. Co-authored-by: OmX <omx@oh-my-codex.dev>
Merge the latest develop into PR #947 and keep the shared CI fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests that #947 had already stabilized. Rejected: Reverting either fixture stabilization | both branches agree on the 45-tool count and CRLF-normalized console baseline. Confidence: high Scope-risk: narrow Directive: Keep includeSnapshot behavior additive and fixture guards explicit as Tier 1 exposure evolves. Tested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite skipped locally by platform guard); git diff --check for touched files. Not-tested: Full GitHub Actions matrix after this refresh. Co-authored-by: OmX <omx@oh-my-codex.dev>
Merge the latest develop into PR #1093 and keep the shared CI fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests that #1093 had already stabilized. Rejected: Reverting either fixture stabilization | both branches agree on the 45-tool count and CRLF-normalized console baseline. Confidence: high Scope-risk: narrow Directive: Keep perception snapshot changes additive while shared CI fixture guards track the current Tier 1 tool surface. Tested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite skipped locally by platform guard); git diff --check for touched files. Not-tested: Full GitHub Actions matrix after this refresh. Co-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #1069 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #1067 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #945 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #944 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #930 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #929 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #928 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #919 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #917 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #915 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts.\nRejected: Rewriting feature code during conflict refresh | the merge conflict was limited to shared CI fixtures.\nConfidence: high\nScope-risk: narrow\nDirective: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #914 and keep the shared cursor/console fixture expectations aligned after the server-to-client primitive landed. Constraint: PR #962 updated the same cross-environment and console fixture tests, leaving this PR blocked by fixture-only conflicts. Rejected: Rewriting schema-lint behavior during conflict refresh | the merge conflict was limited to shared CI fixtures and develop-side transport additions. Confidence: high Scope-risk: narrow Directive: Keep conflict refreshes limited to shared fixture expectations unless feature code conflicts require design review. Tested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite skipped locally by platform guard); git diff --check for touched fixture files. Not-tested: Full GitHub Actions matrix after this refresh. Co-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #911 while preserving the task-ledger branch's non-brittle Tier 1 visibility smoke test. Constraint: PR #962 changed shared transport files and CI fixtures after #911 already had green non-macOS checks. Rejected: Restoring an exact Tier 1 count | task-ledger intentionally allows the Tier 1 surface to grow as core tools graduate. Confidence: high Scope-risk: narrow Directive: Keep task-ledger conflict refreshes focused on compatibility surfaces unless mcp-server registration conflicts require design review. Tested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite skipped locally by platform guard); git diff --check for touched fixture files. Not-tested: Full GitHub Actions matrix after this refresh. Co-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #912 while preserving the branch's non-brittle Tier 1 visibility smoke test. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Restoring an exact Tier 1 count | this branch intentionally allows the Tier 1 surface to grow as core tools graduate.\nConfidence: high\nScope-risk: narrow\nDirective: Keep fixture refreshes semantically faithful to the feature branch instead of overwriting lower-bound guards with develop's exact-count snapshot.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #895 while preserving the branch's non-brittle Tier 1 visibility smoke test. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Restoring an exact Tier 1 count | this branch intentionally allows the Tier 1 surface to grow as core tools graduate.\nConfidence: high\nScope-risk: narrow\nDirective: Keep fixture refreshes semantically faithful to the feature branch instead of overwriting lower-bound guards with develop's exact-count snapshot.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #1066 while preserving the branch's non-brittle Tier 1 visibility smoke test. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Restoring an exact Tier 1 count | this branch intentionally allows the Tier 1 surface to grow as core tools graduate.\nConfidence: high\nScope-risk: narrow\nDirective: Keep fixture refreshes semantically faithful to the feature branch instead of overwriting lower-bound guards with develop's exact-count snapshot.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #1065 while preserving the branch's non-brittle Tier 1 visibility smoke test. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Restoring an exact Tier 1 count | this branch intentionally allows the Tier 1 surface to grow as core tools graduate.\nConfidence: high\nScope-risk: narrow\nDirective: Keep fixture refreshes semantically faithful to the feature branch instead of overwriting lower-bound guards with develop's exact-count snapshot.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #949 while preserving the branch-specific cursor and console fixture expectations. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Replacing branch-specific fixture semantics with develop's exact snapshot | these PRs intentionally alter tool visibility or fixture normalization expectations.\nConfidence: high\nScope-risk: narrow\nDirective: Treat fixture-only conflict refreshes as semantic preservation work; do not rewrite feature behavior while unblocking mergeability.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #938 while preserving the branch-specific cursor and console fixture expectations. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Replacing branch-specific fixture semantics with develop's exact snapshot | these PRs intentionally alter tool visibility or fixture normalization expectations.\nConfidence: high\nScope-risk: narrow\nDirective: Treat fixture-only conflict refreshes as semantic preservation work; do not rewrite feature behavior while unblocking mergeability.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #937 while preserving the branch-specific cursor and console fixture expectations. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Replacing branch-specific fixture semantics with develop's exact snapshot | these PRs intentionally alter tool visibility or fixture normalization expectations.\nConfidence: high\nScope-risk: narrow\nDirective: Treat fixture-only conflict refreshes as semantic preservation work; do not rewrite feature behavior while unblocking mergeability.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #927 while preserving the branch-specific cursor and console fixture expectations. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Replacing branch-specific fixture semantics with develop's exact snapshot | these PRs intentionally alter tool visibility or fixture normalization expectations.\nConfidence: high\nScope-risk: narrow\nDirective: Treat fixture-only conflict refreshes as semantic preservation work; do not rewrite feature behavior while unblocking mergeability.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #925 while preserving the branch-specific cursor and console fixture expectations. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Replacing branch-specific fixture semantics with develop's exact snapshot | these PRs intentionally alter tool visibility or fixture normalization expectations.\nConfidence: high\nScope-risk: narrow\nDirective: Treat fixture-only conflict refreshes as semantic preservation work; do not rewrite feature behavior while unblocking mergeability.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #924 while preserving the branch-specific cursor and console fixture expectations. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Replacing branch-specific fixture semantics with develop's exact snapshot | these PRs intentionally alter tool visibility or fixture normalization expectations.\nConfidence: high\nScope-risk: narrow\nDirective: Treat fixture-only conflict refreshes as semantic preservation work; do not rewrite feature behavior while unblocking mergeability.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #923 while preserving the branch-specific cursor and console fixture expectations. Constraint: PR #962 changed shared transport files and CI fixtures after this branch was opened.\nRejected: Replacing branch-specific fixture semantics with develop's exact snapshot | these PRs intentionally alter tool visibility or fixture normalization expectations.\nConfidence: high\nScope-risk: narrow\nDirective: Treat fixture-only conflict refreshes as semantic preservation work; do not rewrite feature behavior while unblocking mergeability.\nTested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite may skip locally by platform guard); git diff --check for touched fixture files.\nNot-tested: Full GitHub Actions matrix after this refresh.\nCo-authored-by: OmX <omx@oh-my-codex.dev>
Merge latest develop into PR #940 now that the remaining conflict is limited to shared cursor and console fixtures. Constraint: PR #962 updated shared transport files and fixture wording after the progress-notification branch was opened. Rejected: Reworking the progress architecture during conflict refresh | current unmerged paths are fixture-only and the progress code merges cleanly. Confidence: high Scope-risk: narrow Directive: Keep PR #940 conflict refreshes limited to shared fixture expectations unless progress-reporting code conflicts reappear. Tested: npx jest tests/tools/console-capture-regression.test.ts tests/cross-env/cursor-verification.test.ts --runInBand --forceExit (cross-env suite skipped locally by platform guard); git diff --check for touched fixture files. Not-tested: Full GitHub Actions matrix after this refresh. Co-authored-by: OmX <omx@oh-my-codex.dev>
Closes #960.
Summary
Ships the server→client JSON-RPC request/response primitive that three queued MCP adoption issues need to land:
roots/list)elicitation/create)sampling/createMessage)Without this primitive, each of those issues would reinvent the same correlation/timeout/abort plumbing. With it, each becomes a single helper call (
this.requestFromClient(...)) plus their domain-specific logic.Changes
src/mcp-server.ts:pendingClientRequests: Map<id, { resolve, reject, timer, signal? }>+nextS2cRequestIdcounter.clientCapabilitiescache populated inhandleInitialize(capturesroots,sampling,elicitationfrom the client'sinitialize.capabilities).requestFromClient<T>(method, params?, { timeoutMs?, signal? })— allocates idoc-s2c-N(prefixed string so it cannot collide with client-allocated numeric/string ids), serializes via existingsendResponsepath, registers a one-shot resolver, returns a typed Promise.rejectAllPendingS2cRequests(reason)— drains the pending map on shutdown.handleMessage: new response-routing fork BEFORE the regular method-required validation. If the incoming message hasidAND (resultORerror) AND NOmethod, route to the pending resolver; otherwise behavior unchanged._stopInternal: callsrejectAllPendingS2cRequests('s2c_aborted:connection_closed')so dropped connections don't leave Promises hanging forever.tests/unit/s2c-request.test.ts(NEW) — 10 cases covering: round-trip, concurrent independent requests, error → reject mapping, timeout fires in budget, AbortSignal fires fast, pre-aborted signal rejects synchronously, stale-response drop,_stopInternalrejects all in-flight,clientCapabilitiespopulated/empty.Failure modes (callers can rely on these)
Verification
npm run build— cleannpm run lint— 0 errors (3 pre-existing warnings, unrelated)npm test -- tests/unit/s2c-request.test.ts— 10/10 passPost-merge real verification with openchrome
(The bundled smoke client lives only in the test-manual directory; it is not part of the runtime surface.)
Design notes for reviewers
oc-s2c-prevents collision with client-allocated request ids (which are typically integers or short strings). Even if a malicious client deliberately sends a numericid: 1, the server-issued requests use string ids like"oc-s2c-1"— no collision possible.methodfield and the current validation rejects "missing method". Moving the fork above the validation keeps the validation path unchanged for client-initiated traffic.Mapentry; no head-of-line blocking, no shared mutable queue state._stopInternalpath, so every shutdown trigger (SIGTERM, stdin EOF, transport teardown) automatically drains the pending map.Out of scope
The downstream consumers (#880, #877, #876) — each lands as its own PR threading a single
requestFromClientcall onto its domain logic. This PR is the shared primitive only.Portability-Harness Contract compliance
tools/call(or follow-up notifications). No orchestration; no background work.🤖 Generated with Claude Code