Skip to content

Python SDK/docs spell custom MCP server keys install_cmd/run_cmd but the gateway only accepts installCmd/runCmd #1899

Description

@aimable100

Summary: the Python SDK's type hints and the docs spell the custom MCP server config keys install_cmd / run_cmd, but the sandbox's mcp-gateway only recognizes installCmd / runCmd. The SDK passes the dict through verbatim, so the documented Python form is silently ignored and the server never starts.

Versions: e2b 2.50.0 (Python), mcp-gateway template on 2026-09-19.

Repro

from e2b import AsyncSandbox

sbx = await AsyncSandbox.create(
    mcp={
        "github/tenuo-ai/tenuo-demos": {
            "install_cmd": "echo hi > /tmp/installed.txt",
            "run_cmd": "cat",
        }
    }
)

Inside the sandbox, /var/log/mcp-gateway/gateway.log:

{"level":"warn","caller":"gateway/tools.go:81","msg":"GitHub server missing run command","component":"TOOLS","server":"github/tenuo-ai/tenuo-demos"}

/tmp/installed.txt is never created and tools/list does not include the server. With the same dict spelled installCmd / runCmd, the gateway clones the repo, runs the install command, and advertises the tool as github/tenuo-ai/tenuo-demos-<tool>.

Where the snake_case spelling comes from

  • e2b/sandbox/sandbox_api.py: GitHubMcpServerConfig declares run_cmd: str and install_cmd: NotRequired[str].
  • Docs, "Custom MCP servers" page, Python example: "install_cmd": "npm install", "run_cmd": "sudo npx ...".

The SDK starts the gateway with mcp-gateway --config <json.dumps(mcp)> (see Sandbox.create), i.e. the keys reach the gateway exactly as typed.

Suggested fix (either):

  1. Have the SDK map install_cmd → installCmd and run_cmd → runCmd before serializing, the way other snake_case fields are mapped to wire names; or
  2. Change the type hints and the Python docs to the camelCase keys the gateway accepts.

Option 1 keeps the documented Python form working. A gateway-side warning that names the offending key would also have made this a two-minute find instead of a log dive.

Activity

  1. added
    bugSomething isn't working
    sdkImprovements or additions to SDKs
    docsImprovements or additions to documentation
    on Sep 26, 2026
  2. devin-ai-integration commented on Sep 26, 2026

    @devin-ai-integration
    Contributor

    Triage

    • type: bug
    • priority: high
    • complexity: small
    • affected_area: python-sdk/sandbox_api (GitHubMcpServerConfig) + docs "Custom MCP servers" Python example
    • actionable: yes
    • needs_clarification: false
    • summary: The Python SDK types and docs use install_cmd/run_cmd for custom GitHub MCP server config, but the dict is serialized verbatim to mcp-gateway --config, which only accepts installCmd/runCmd, so the documented Python form is silently ignored; fix by mapping keys to camelCase before serialization (preferred, keeps documented form working) or by aligning the type hints and docs with the gateway's keys.

    Written by Devin

  3. Kylinny commented on Sep 26, 2026

    @Kylinny

    Thanks for the thorough report and repro, @aimable100 — the gateway log line makes the root cause very clear.

    I'd like to take this on. My plan is option 1 from the issue: have the SDK map install_cmd/run_cmd to the installCmd/runCmd wire names before serializing (matching how the other snake_case fields are mapped), so the documented Python form keeps working, and align the docs example. I'll apply the equivalent change across the JS SDK and the sync/async Python SDKs per the repo's contribution notes.

    If a maintainer would rather go with option 2 (camelCase keys everywhere), happy to adjust.

  4. Kylinny commented on Sep 26, 2026

    @Kylinny

    Thank you again for the report, @aimable100 — your repro made this straightforward to confirm and fix.

    I've opened a PR implementing option 1 from the issue: the Python SDK now maps the documented run_cmd / install_cmd keys to the runCmd / installCmd wire names the gateway expects, in both the sync and async create() paths: #1901

    The mapping only touches github/owner/repo entries, leaves base server entries and unknown keys (like envs) alone, and the caller's dict is never mutated. Happy to adjust if you'd prefer option 2 (camelCase type hints/docs) instead.

  5. linear-code commented on Sep 27, 2026

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    MCP ServerbugSomething isn't workingdocsImprovements or additions to documentationsdkImprovements or additions to SDKs

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions