Skip to content

feat(execd): add embedded MCP proxy endpoint#1111

Open
Pangjiping wants to merge 5 commits into
mainfrom
feat/execd-mcp-proxy
Open

feat(execd): add embedded MCP proxy endpoint#1111
Pangjiping wants to merge 5 commits into
mainfrom
feat/execd-mcp-proxy

Conversation

@Pangjiping

Copy link
Copy Markdown
Collaborator

Summary

  • Add /mcpproxy endpoint to execd that acts as an MCP gateway, proxying and aggregating multiple upstream MCP servers (stdio subprocess or HTTP) through a single MCP streamable-http endpoint
  • Upstream MCP servers are managed dynamically via REST API at /mcpproxy/upstreams (register, list, get, remove)
  • Tool aggregation with conflict detection — tool name collision across upstreams returns error on registration
  • New package execd/pkg/mcpproxy/ with stdio/HTTP upstream transports, JSON-RPC 2.0 routing, and session management
  • Windows compatible via platform-specific process management files (process.go / process_windows.go)

API Endpoints

Method Path Description
POST /mcpproxy MCP JSON-RPC entry point (streamable-http)
GET /mcpproxy SSE notification stream
DELETE /mcpproxy Session termination
POST /mcpproxy/upstreams Register upstream MCP server
GET /mcpproxy/upstreams List all upstreams
GET /mcpproxy/upstreams/:name Get upstream detail
DELETE /mcpproxy/upstreams/:name Remove upstream

Test plan

  • Unit tests for JSON-RPC types, normalizeID, request/response construction
  • Unit tests for Manager: session lifecycle, initialize, ping, tools/list aggregation, tools/call routing, conflict detection, upstream CRUD, close cleanup
  • Controller tests: HTTP layer initialize + session header, missing/unknown session, tools/list, delete session, ping, invalid JSON, upstream validation, notification handling
  • Smoke test (tests/smoke_mcpproxy.py) with inline mock stdio MCP server — full end-to-end flow
  • Cross-platform build verified: GOOS=windows go build
  • CI workflow updated: MCP proxy smoke test added to execd-test.yml smoke job (ubuntu + windows)
  • All existing tests pass

Add /mcpproxy endpoint to execd that acts as an MCP gateway, proxying
and aggregating multiple upstream MCP servers (stdio subprocess or HTTP)
through a single streamable-http endpoint. Upstream servers are managed
dynamically via REST API at /mcpproxy/upstreams.

New package execd/pkg/mcpproxy with stdio/HTTP upstream transports,
tool aggregation with conflict detection, session management, and
JSON-RPC 2.0 routing. Includes unit tests, controller tests, and
a smoke test using an inline mock MCP server. Windows compatible
via platform-specific process management files.
Comment thread components/execd/tests/smoke_mcpproxy.py Fixed
Comment thread components/execd/tests/smoke_mcpproxy.py Fixed
@Pangjiping Pangjiping marked this pull request as draft June 22, 2026 04:03

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e1e3eceff7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread components/execd/pkg/mcpproxy/http_upstream.go
Comment thread components/execd/pkg/mcpproxy/manager.go Outdated
Comment thread components/execd/pkg/mcpproxy/upstream.go
Comment thread components/execd/pkg/web/router.go
Comment thread components/execd/pkg/mcpproxy/manager.go Outdated
Comment thread components/execd/pkg/mcpproxy/manager.go Outdated
Comment thread components/execd/pkg/mcpproxy/upstream.go
Comment thread components/execd/pkg/web/controller/mcp.go
Comment thread components/execd/pkg/mcpproxy/http_upstream.go
@Pangjiping Pangjiping added feature New feature or request component/execd labels Jun 22, 2026
@Pangjiping Pangjiping marked this pull request as ready for review June 22, 2026 06:39
- Remove unused json import in smoke test
- Add stderr warning to empty except clause
- Fix data race on httpUpstream.sessionID with RWMutex
- Move upstream Initialize/Tools outside manager lock to avoid
  blocking all proxy traffic during slow upstream registration
- Handle all JSON-RPC notifications generically via IsNotification()
  instead of only notifications/initialized
- Support SSE (text/event-stream) responses from HTTP upstreams
- Set listChanged capability to false (no notifications sent yet)
- Preserve tool annotations field in aggregated tools/list
- Follow tools/list pagination (nextCursor) in both transports

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1361f7b09b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread components/execd/pkg/mcpproxy/manager.go Outdated
Comment thread components/execd/pkg/web/controller/mcp.go
Comment thread components/execd/pkg/mcpproxy/http_upstream.go
- Release manager lock before closing upstreams in RemoveUpstream
  to avoid blocking proxy traffic during slow upstream shutdown
- Validate upstream names: alphanumeric, dot, hyphen, underscore,
  1-64 chars, must start with alphanumeric
- Cache empty tool lists using toolsFetched bool flag instead of
  nil check to avoid re-fetching on every rebuildToolList

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 98bfa04cbc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +147 to +148
if resp.ID != nil {
return &resp, nil

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep reading until the matching SSE response

For an HTTP upstream that sends a valid Streamable HTTP SSE response containing a server JSON-RPC request before the final response, this returns the first message with any id instead of the response for requestID. The current fresh evidence is that if resp.ID != nil still ignores requestID, so Initialize/Tools/CallTool can treat an upstream request as the result and either fail parsing or forward the wrong payload; compare the response id to the request id and continue past non-response messages.

Useful? React with 👍 / 👎.

// When ID is nil the message is a notification (no response expected).
type Request struct {
JSONRPC string `json:"jsonrpc"`
ID any `json:"id,omitempty"`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve JSON-RPC ids without float rounding

When a JSON-RPC client sends an integer id outside float64's exact range, encoding/json decodes this any field as a float64, so the proxy echoes a rounded id (for example, 9007199254740993 becomes 9007199254740992). Clients match responses by exact id and will ignore the rounded response; keep ids as json.RawMessage/json.Number or decode with UseNumber so they round-trip unchanged.

Useful? React with 👍 / 👎.

Comment on lines +124 to +126
key := normalizeID(resp.ID)
if ch, ok := s.pending.LoadAndDelete(key); ok {
ch.(chan *Response) <- &resp

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Distinguish stdio requests from responses

For a stdio upstream that sends a server JSON-RPC request with an id while the proxy is waiting for initialize, tools/list, or tools/call, this loop unmarshals it as a Response and routes solely by id. If the upstream request id collides with a pending proxy id, the pending call is satisfied with a non-response object and can fail parsing or return the wrong result; parse the envelope and only deliver actual responses to pending.

Useful? React with 👍 / 👎.

Comment thread components/execd/main.go
}

controller.InitCodeRunner()
controller.InitMCPProxy()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Close MCP upstreams when execd exits

When a stdio upstream is registered, it is started in its own process group so it will not receive terminal or SIGTERM signals sent to execd's process group, but this initialization has no matching shutdown path that calls Manager.Close(). If execd is stopped or restarted before each upstream is explicitly deleted, those MCP server subprocesses can keep running as orphans; wire the manager close path into main's shutdown handling.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/execd feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant