Skip to content

MCP HTTP handlers read an empty registry on cold locations #54192

Open

Description

@mathew-cf

Description

MCP HTTP handlers can read the registry before config plugins register servers for a cold directory. On macOS, the first opencode mcp list says No MCP servers configured; the same command then lists the configured servers. This is a cold-location race, distinct from the persistent warm-service symptoms in #50710.

Source regression tests also reproduce cold connect, disconnect, and remove returning 404 for a configured server. Waiting for plugin activation makes those requests succeed. Proposed fix: #54191.

Plugins

The source reproduction uses built-in config plugins only.

OpenCode version

Installed 2.0.21; reproduced with current v2 source using Bun 1.4.2.

Steps to reproduce

  1. Configure an MCP server in the global config.
  2. Create a new directory and change into it.
  3. Run opencode mcp list: it may report no configured servers.
  4. Repeat from the same directory after initialization: the server appears.

Expected: the first request waits for config registration and lists the configured server.

The regression in #54191 uses an in-memory server and a disabled local MCP server, so it needs no remote authentication. Without the readiness wait, the first /api/mcp response is HTTP 200 with data: [].

Screenshot and/or share link

Reproduction and regression tests: #54191.

Operating System

macOS 26.5, Apple Silicon.

Activity

  1. opencode-agent commented on Oct 9, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I can reproduce this.

    I set up an isolated home whose global config has one disabled local MCP server, then ran opencode mcp list twice in each of three new directories:

    • 2.0.21: five of the six calls printed No MCP servers configured.
    • 2.0.26 (the latest release): the first call in every new directory printed No MCP servers configured. The second call listed ○ demo disabled in two of the three directories, so even a repeat call can still come back empty.

    I couldn't run current v2 source here, so the latest check used the 2.0.26 release. On the latest v2 commit (d2905b8), the MCP HTTP handlers still don't wait for plugins to finish loading before reading the registry, so I'd expect the same result from source. I only tested mcp list, not the connect, disconnect and remove 404s.

    The script below reproduces it.

    Related: #50710, a Windows report where the service registers no MCP servers at all and the problem doesn't go away on a later run, and your PR #54191, which adds the wait to mcp.list.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Reproduce: first `opencode mcp list` in a cold directory reports no servers.
    # Usage: OC="opencode" bash repro.sh   (or OC="bun ... packages/cli/src/index.ts")
    set -u
    OC=${OC:-opencode}
    ROOT=$(mktemp -d /tmp/opencode/mcp-cold-XXXX)
    export HOME=$ROOT/home XDG_CONFIG_HOME=$ROOT/home/.config XDG_DATA_HOME=$ROOT/home/.local/share \
           XDG_STATE_HOME=$ROOT/home/.local/state XDG_CACHE_HOME=$ROOT/home/.cache
    mkdir -p "$XDG_CONFIG_HOME/opencode"
    cat > "$XDG_CONFIG_HOME/opencode/opencode.json" <<'J'
    { "$schema": "https://opencode.ai/config.json",
      "mcp": { "demo": { "type": "local", "command": ["true"], "enabled": false } } }
    J
    for i in 1 2 3; do
      DIR=$ROOT/project-$i; mkdir -p "$DIR"; cd "$DIR"
      echo "== project-$i, run 1"; $OC mcp list 2>&1
      echo "== project-$i, run 2"; $OC mcp list 2>&1
    done
    $OC service stop >/dev/null 2>&1 || true

    Run it: bash repro.sh

  2. joaquin90jnp-blip commented on Oct 17, 2026

    @joaquin90jnp-blip

    Confirming this still reproduces on 2.0.32 (reported as the latest release by
    https://opencode.ai/update/api/latest/cli/npm), Linux x86_64, standalone binary
    install, no plugins configured. So it is not fixed by anything released since the
    PR here went up.

    Minimal repro (deterministic)

    $ mkdir -p /tmp/mcp-repro && cd /tmp/mcp-repro
    $ opencode mcp list      # first invocation in a directory that was never a location
    No MCP servers configured
    $ opencode mcp list      # ~1s later, same command, same directory
    ✓ context7  connected

    Back-to-back invocations always print the empty message; adding a ~1s sleep before
    the second one makes it succeed reliably. Repeating opencode service restart
    resets every location to the cold state, so the first two invocations are empty
    again and the third recovers:

    $ opencode service restart
    $ opencode mcp list
    No MCP servers configured
    $ opencode mcp list
    No MCP servers configured
    $ opencode mcp list
    ○ context7  pending

    Server log ordering

    The service log shows the CLI completes before the location is booted and before the
    MCP client connects, which matches the "reads the registry before the servers are
    registered" diagnosis:

    level=INFO message="cli starting" args="[\"mcp\",\"list\"]" role=cli
    level=INFO message="cli starting" args="[\"mcp\",\"list\"]" role=cli
    level=INFO message="location services booted" directory=/tmp/opencode/e3 durationMs=30 role=server
    level=INFO message="mcp connected" server=context7 tools=2 directory=/tmp/opencode/e3 role=server
    level=INFO message="cli starting" args="[\"mcp\",\"list\"]" role=cli   # now prints ✓ connected
    

    Extra data points

    • It is not a config discovery problem: opencode api get "/api/config?location[directory]=<dir>" resolves the same cold directory
      correctly, returning the global document and the project document, both with the
      server declared. Only the MCP read is early.
    • It happens with a remote server too (context7 over
      https://mcp.context7.com/mcp, no OAuth/API key needed), not only with local
      stdio servers, and both with servers declared only globally and with a project
      opencode.json for that directory.
    • Related, same ordering issue with user-visible impact: restarting the service
      while a session is running temporarily removes the MCP tools from that running
      session until the location boots again (tools come back on their own once
      mcp connected is logged), which looks like First prompt after start has no MCP tools: tool list does not wait for pending MCP connections #51812.
  3. falsifyer commented on Oct 22, 2026

    @falsifyer

    Confirming this on V2, with the timing measured instead of "run it twice".

    Environment: opencode 2.1.1 (V2, local install under ~/.opencode/bin),
    Debian 13, x86_64. Five MCP servers configured: four local stdio, one remote
    Streamable HTTP. No plugins.

    Server side. Fresh directory, GET /api/mcp?location[directory]=<fresh>,
    polled every 250 ms:

    0 ms   n=0  []                    <- empty registry
    

    282 ms n=5 [pending]
    564 ms n=5 [connected, pending]
    ~1.4 s n=5 [connected] <- stable afterwards

    The first request for a cold location is not "sometimes" empty: it is empty
    every time, and the next request 282 ms later already returns the catalog. The
    registry is populated asynchronously and the handler answers before it exists.

    CLI side. Same cold directory, three consecutive opencode mcp list:

    1 -> No MCP servers configured
    2 -> No MCP servers configured
    3 -> ○ pending x5   (then connected)
    

    Impact. No MCP servers configured is indistinguishable from a genuinely
    empty configuration. It cost me a full misdiagnosis — config shape, service
    environment and credential were all checked before measuring the second call.
    Since #55074 was closed as a duplicate of this one, the user-facing message
    still ships; when the readiness fix lands it would help to say in the release
    note that the first mcp list in a new directory no longer reports an empty
    catalog.

    Happy to test #54191 on Debian 13 against this configuration if a build is
    available.

  4. dskanakoglou commented on Oct 22, 2026

    @dskanakoglou

    Still happens on 2.1.3 (macOS, npm install). In a project folder with four MCP servers configured, the first opencode mcp list prints No MCP servers configured; the second and third list all four. I run a workshop where one step checks the connection with opencode mcp list, so participants would get the wrong message on their first try. We now tell them to check with /mcps in the TUI instead, which shows the servers correctly.

  5. lawzava commented on Nov 3, 2026

    @lawzava

    Still reproduces on 2.2.5 (latest release), Linux x86_64, ~/.opencode/bin install, shared background service. In my runs the empty result does not clear on its own: three consecutive mcp list calls and --standalone all printed it, until something requested the location through the API.

    Minimal repro in a fresh directory whose opencode.json defines one local server:

    { "mcp": { "servers": { "repro-local": { "type": "local", "command": ["npx", "-y", "@modelcontextprotocol/server-everything"] } } } }
    $ opencode mcp list               # tries 1, 2 and 3
    No MCP servers configured
    $ opencode mcp list --standalone
    No MCP servers configured
    $ opencode api get "/api/mcp?location[directory]=<dir>"    # run from another directory
    {"data":[..., {"name":"repro-local","status":{"status":"pending"}}]}
    $ opencode mcp list               # back in <dir>
    ○ repro-local  pending

    The same thing happened in 10 of 11 project directories with project-scoped servers. Once a location had been loaded through GET /api/mcp?location[directory]=..., mcp list worked there as well.

  6. yxlimo commented on Nov 4, 2026

    @yxlimo

    Still reproduces on 2.2.5 (current latest on npm), so nothing released since the reports above has fixed it. Same cold-state behaviour on both the CLI and the HTTP handler.

    Environment: opencode 2.2.5 (npm @opencode/cli-linux-x64), Linux x86_64, two servers configured in ~/.config/opencode/opencode.json — one remote Streamable HTTP (keenable, auth header resolved from a file) and one local stdio (mcp-nixos). One plugin enabled.

    Server side, GET /api/mcp polled every 500 ms on a fresh opencode serve:

       95 ms  n=0  {"location":{...},"data":[]}
      618 ms  n=0  {"location":{...},"data":[]}
     1139 ms  n=2  {"data":[{"name":"keenable","status":{"status":"connected"}},
                            {"name":"nixos","status":{"status":"connected"}}]}

    CLI side, same instance, opencode mcp list --server http://127.0.0.1:<port>: empty at t=0, and by the time I queried again the API already reported both servers connected — i.e. the CLI gives up earlier than the registry takes to fill (~1.1 s here), not because the config is unreadable.

    One extra data point that makes the false negative especially misleading: opencode mcp add <name> --url … writes the entry, and the very next opencode mcp list in that same directory still prints No MCP servers configured while the config file on disk contains exactly the shape the docs specify.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions