DEV Community

Cover image for Healthy containers hid a blocked MCP marketplace connection
Alex
Alex

Posted on Originally published at modelmarket.dev

Healthy containers hid a blocked MCP marketplace connection

All three ARGUS assistants had been running without our marketplace's MCP tools for days. Their containers kept reporting healthy. The assistants could carry on with other capabilities, so the missing connection did not stop the processes or turn their liveness checks red.

The refusal came from WARDEN, the guard inside ARGUS. It compares an MCP server's tool definitions with an approved snapshot before exposing them to the model. Descriptions enter the model's context; changing their instructions matters even if a tool keeps its name. This time the guard reported TOOL_DEF_DRIFT for the hub we operate ourselves.

We had appended the live price and exact free-trial allowance to each paid direct tool's description. It was convenient for a model reading tools/list: the cost was right there. It also made a routine tariff edit rewrite a security-pinned definition. Updating a number could cost the assistant access to the entire hub until someone reviewed it again.

Prices out of the pinned description

The November 6 repair moved those amounts out. A paid direct tool now says, "Paid per call; market_search shows the current price." A free tool says "Free." When a trial is enabled, its description mentions a few free calls without inserting the exact quota.

The catalog search results retain price_per_call_usd and max_price_usd; an external check after deployment confirmed both on modelmarket.dev. Exact trial settings and remaining calls stay in runtime settings and result data. Switching between free and paid, or enabling and disabling the trial phrase, still changes the description.

The useful invariant is narrower: different positive prices and numeric trial allowances must produce identical definitions for the same available tools.

The integration regression has a listed(price, allowance) helper. It seeds a weather capability, replaces the trial setting, then calls both tools/list and market_search through the test client. These are its essential assertions:

before, search_before = listed(0.001, 5)
after_price, search_after = listed(0.05, 5)
after_trial, _ = listed(0.001, 9)

assert before == after_price == after_trial
assert "0.001" in search_before and "0.05" in search_after
Enter fullscreen mode Exit fullscreen mode

Replacing the setting matters. The trial module reads its allowance at import, so changing an environment variable later would leave the test exercising the old value. An independent review caught that mistake in the first test.

Five local gateway fixtures: positive price and allowance changes preserve the definition; free versus paid and disabling the trial phrase change it.

Local renderer fixtures, compared as complete definition objects. These are chosen inputs, not production rates. The integration regression also checks the catalog response.

On November 8, both targeted gateway tests passed locally. To repeat them in the hub checkout's Python environment:

python -m pytest tests/test_mcp_stranger_path.py \
  -k 'price_or_trial_change or direct_tool_text'
Enter fullscreen mode Exit fullscreen mode

Six hubs received the November 6 update. Five had direct tools with amounts in their descriptions; afterward, those five served the same 13-tool list despite their different prices. The sixth, with no direct tools, kept eight. Two consecutive external reads matched at every endpoint, and all six health checks returned HTTP 200. The MCP server version and packaged card moved from 1.2.0 to 1.3.0 together.

Availability can still change the list. Direct tools disappear when their routes are unsellable, including when a peer has been stale for more than six hours.

Blocked in the health response

The MCP host sets a connection's quarantined flag before refreshing its tool list. If the read fails, the server state becomes failed. If WARDEN refuses the definitions, it becomes blocked. Only an accepted refresh clears quarantine. The path assembling tools for the model contains this check:

if (conn.quarantined || !conn.verdict.allow) continue;
Enter fullscreen mode Exit fullscreen mode

An already wrapped tool call also refreshes before invoking the server. This lets the host refuse changes that arrive during operation, including from servers that omit a tool-list-change notification.

The operator needs that state outside the connection code. ARGUS now retains connected, blocked, or failed for every MCP server it was asked to use. These fields were recorded in the public health response after the rollout:

{
  "status": "ok",
  "mcp": {
    "connected": 0,
    "blocked": 1,
    "failed": 0
  }
}
Enter fullscreen mode Exit fullscreen mode

The top-level ok remains the liveness value our monitor expects. Counts alongside it expose integration state; an authenticated status endpoint gives per-server details and WARDEN codes.

ARGUS process liveness remains ok while the MCP admission path can be connected, blocked after definition drift, or failed after a read error; all three instances remained blocked after the November 6 rollout.

Implementation paths, with the November 6 incident state highlighted. The connected branch shows a possible outcome, not a recorded recovery.

Independent review caught a gap here too: the first implementation reported startup blocks but missed later ones. Runtime blocks now produce error-level logs and queued owner notifications when Telegram is enabled. Repeated notifications are limited to once per server per hour; the log keeps the events. Notes written before the bot has an owner wait for one.

Messages use cleaned identifiers and reason codes. Configured servers can be named; remote-catalog servers are counted without copying their catalog-chosen identifiers into the owner's message. Approval commands appear only for pin problems they can resolve. The nine targeted MCP state, notification, and HTTP tests passed locally on November 8.

At the close of the November 6 rollout, all three assistants still had one blocked MCP server. The repair had changed the definitions once more. Each needed a human to review the new snapshot at an interactive terminal, approve it, and restart ARGUS before the marketplace tools could return.

We build WARDEN and ARGUS and operate AIMarket. AI assistance was used for this article, its technical figures, and implementation work; the checks described here are recorded separately from the incident.

Top comments (0)