· about 25 minutes · runs on ubuntu-26.04-claude · unrated
An unrated lab. It runs on your machine with no account and no network, and everything about it — the faults, the checks, the hints and the reference solution — is in the repository. An attempt on it is recorded on your profile and never moves a rating: only rated labs do. Rated and unrated labs.
~/bin/health-check asks Claude Code, in ~/ops, how loaded this machine is. It gets the numbers
from the metrics MCP server, which ~/ops/.mcp.json starts with /usr/local/bin/metrics-mcp. The
server's settings are in /etc/metrics-mcp/config.json.
It worked until someone turned on debug logging to chase a slow answer, and tidied the start-up
script so the version shows. Since then the job says it has no way to see the machine's load. The
server itself runs fine when you start it by hand — it even prints its log for you. The job's JSON
result is ~/health.json.
This lab runs in a container, as learner with sudo. claude here is the real Claude Code
2.1.270, talking to a scripted model on this machine; it starts the MCP server for real.
What is expected, and graded:
get_load answers it.This lab runs in a container — Docker or Podman, no VM — so there is no boot to survive: the checks run once, against the state you left.
| Check | Objective |
|---|---|
| 01_the_server_connects | Keep a stdio MCP server's stdout for the protocol alone |
| 02_stdout_carries_only_the_protocol | Keep a stdio MCP server's stdout for the protocol alone |
| 03_the_log_is_still_written | Keep a server's diagnostics when moving them off stdout |
Where the lab's hints send you, level by level, as you ask for them (h, then l opens a journal section in the TUI).
3 questions on the same topic, in the lab's Theory tab. They never affect the lab's grade. Here they are, to answer in place:
A stdio MCP server wants to print its version and debug logs. Where should they go?
stderr or a log file; stdout carries only JSON-RPC messages
On the stdio transport the client reads stdout as newline-delimited JSON-RPC, and the specification says a server must not write anything else there. stderr is the server's own channel, which clients may capture for debugging; a file works too.
https://modelcontextprotocol.io/specification/2026-07-28/basic/transports
What does this print — how many lines of the server's stdout a client can parse as JSON, of the three it wrote?
import json
stdout = 'ready {"jsonrpc":"2.0","id":1,"result":{}}\n' \
'12:00 DEBUG handled initialize\n' \
'{"jsonrpc":"2.0","id":2,"result":{}}\n'
ok = 0
for line in stdout.splitlines():
try:
json.loads(line)
ok += 1
except ValueError:
pass
print(ok)
1
The banner without a newline shares a line with the first response, so that response cannot be parsed and is lost; the log line is not JSON; only the second response survives. Losing the answer to initialize is enough to stop a client connecting at all.
https://modelcontextprotocol.io/specification/2026-07-28/basic/transports · executed in a sandbox
.mcp.json starts a server with `npx some-mcp-server`. Which output can break the protocol?
Anything npx or the package writes to stdout, including install messages
The whole command is the server as far as the client is concerned: a wrapper's echo, a package manager's progress line or a library's print all land on the same stdout the client parses.
https://code.claude.com/docs/en/mcp