Claude Code mcp_server_errors: fail CI when Sume never loaded

In stream-json runs Claude Code reports a skipped MCP entry in mcp_server_errors. Check it with jq before a CI job spends on Sume, then verify with mcp_health.

4 min readSume
All posts

Read the mcp_server_errors field of the system init event and fail the job if it is not empty. Claude Code's MCP documentation says that in --output-format stream-json runs, a skipped --mcp-config entry is reported there, so scripts can detect that the server never loaded; that needs Claude Code v2.1.219 or later. For a Sume job this matters because a skipped entry does not stop the run: the agent simply finishes without any Sume tools, and the pipeline looks green.

Claude Code's claims here come from its MCP documentation, read on 2026-10-03. Sume's come from MCP OAuth and API keys and MCP tools and gates.

What does mcp_server_errors tell a script?

The documentation names the field and the version; it does not publish the item shape, so treat the contents as opaque text for a human and test only whether the field has entries. The case the docs call out is a config entry Claude Code could not load, for example an entry with a url and no type, or an in-process sdk entry that only an SDK host can register.

It does not cover a server that loaded and then failed to connect or authenticate. Those show up as connection statuses (Failed to connect, Needs authentication) in claude mcp list and /mcp, so a complete gate checks both the config and the live server.

Where each failure shows up; Claude Code behavior from its MCP docs, read 2026-10-03.
FailureWhere Claude Code reports itScript check
Entry skipped (no type, reserved name, sdk type)mcp_server_errors in the system init event, v2.1.219+Fail on a non-empty field
Server added but not signed inNeeds authentication in claude mcp list and /mcpUse an API key in headless runs
Server added, endpoint unreachableFailed to connect, with the HTTP status or code in recent versionsRun claude mcp get <name> before the job

How do I wire the check for Sume?

Pass the server with --mcp-config and an API key header, since a headless run has no browser for the OAuth sign-in. Sume accepts Authorization: Bearer <key> or x-api-key, and an API-key session sees the full hosted tool set. Add --strict-mcp-config so only that file's servers are used; the docs say a strict session uses only the servers you pass with --mcp-config and, from v2.1.246, no longer waits on approval for project-scoped servers.

The jq filter below does not depend on the field's inner shape. It reads every JSON line, and prints and fails if any line carries a non-empty mcp_server_errors. A missing field and an empty array both count as clean.

claude -p "Call mcp_health and report auth_source" \
  --mcp-config ./sume.mcp.json --strict-mcp-config \
  --output-format stream-json --verbose > run.ndjson

jq -e -s 'map(.mcp_server_errors // [] | select(length > 0)) | length == 0' run.ndjson \
  || { echo "Sume MCP entry did not load"; exit 1; }

What should the job check after the server loads?

Have the prompt call mcp_health, which Sume lists as the read-only tool that reports endpoint readiness, auth source and safety posture. With an API key the auth source is not mcp_oauth, so the field also shows which credential the job used. Do not generate media in a gate step: paid and write calls need an idempotency_key, and dry_run previews cost without submitting.

If the job later waits on a render, remember the hold limit. Every remote POST /mcp caller holds at most 55 seconds per jobs_wait call (50 when the timeout is omitted); on wait_slice_expired, call jobs_wait again with the same ids and never resubmit the create. Jobs and results has the batch form with up to 20 ids.

What does a clean pass look like?

The run prints a stream of JSON lines. The init event is the first one, and for a good config its mcp_server_errors is empty or absent, so the jq expression prints true and the step exits zero. A bad entry, such as a url-only block, makes the filter print false and the || branch fail the job with a clear message instead of a silent no-op.

Keep the gate cheap. It makes one read-only call and runs in seconds. Put it before any step that generates media, so a broken config costs a minute and not a wasted job. Pin the Claude Code version in CI as well: the field needs v2.1.219 or later, and an older binary simply will not emit it, which the jq filter would read as clean.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume