Skip to content

GLM-5.2 Zread MCP: When Repository Context Goes Stale

Independent research — not an official Z.ai publication.Identity and provider disclosure

GLM-5.2 Zread MCP workflow from a public GitHub repository through search_doc, get_repo_structure and read_file, an application freshness and error gate, and bounded untrusted model input

Original repository trust-boundary diagram. The sanitized evidence image below is rendered from the archived live result.

A coding agent can lose minutes opening GitHub pages, scanning a tree and copying files into context. Z.ai’s Zread MCP compresses those steps into repository search, tree inspection and file reading. That makes it adjacent to web retrieval, but not interchangeable with it: repository indexing has a branch and freshness problem that ordinary page reading does not solve for you.

The critical question is therefore not only “did the MCP connect?” It is “did it return the current file, did the requested operation succeed semantically, and what may the model do next?” We tested those boundaries with a digest-pinned, non-root Docker probe on July 29, 2026. The target was Z.ai’s own public GLM repository so its current README gave us release-specific controls rather than subjective answer grading.

  1. Give Zread one job
  2. Inspect the ten-request run
  3. Copy an OpenCode configuration
  4. Prove the tool can run
  5. Read the live MCP contract
  6. Gate repository freshness
  7. Detect semantic failures
  8. Bound GLM-5.2 ingestion
  9. Separate quotas and billing
  10. Constrain the repository trust boundary
  11. Troubleshoot by failed contract
  12. Promote only with acceptance evidence
  13. Resolve operational questions
  14. Review data and sources

Use Zread after the application knows a public GitHub owner/repo and wants repository-specific context. Keep the surrounding products separate:

Product surface Starting input Current documented access Endpoint Model ID in this object?
Zread Remote MCP Public owner/repo, query or relative file GLM Coding Plan https://api.z.ai/api/mcp/zread/mcp No
Web Search A web query General API, Chat tool or Coding Plan MCP Route-dependent Only in Chat
Web Reader A selected URL General Reader API or Coding Plan MCP Route-dependent No
GLM-5.2 Chat Validated text and instructions Coding Plan or pay-as-you-go model route Route-dependent Yes: glm-5.2

The dedicated Z.ai Zread guide calls it an exclusive Coding Plan server and documents only public, supported repositories. It does not publish a pay-as-you-go Zread endpoint. A separate metered GLM-5.2 request can consume validated output, but that does not turn the MCP call into general API traffic.

Use GLM-5.2 Web Search when the agent does not know the source URL. Use the Web Reader test for a chosen documentation page. Use Zread when the unit of work is a repository tree, repository documentation or a specific file. For a custom function that your own application executes, follow the complete tool-calling loop instead.

The final probe made one current-source GET, seven MCP exchanges and two GLM-5.2 Chat Completions. All ten observed statuses were 200:

Observation HTTP Client time Measured result
GitHub raw README control 200 0.717 s 11,776 characters; 104 lines; four of four current markers
MCP initialize 200 0.735 s Protocol 2024-11-05; server zread-server
Initialized notification 200 0.653 s Session accepted
MCP tools/list 200 0.580 s Three tool schemas
get_repo_structure 200 1.702 s 577 characters; 23 lines
read_file README.md 200 2.131 s 10,219 characters; 140 lines
search_doc 200 3.274 s 5,813 characters; 186 lines
Missing-file control 200 1.917 s Business error 1015 in text; isError: false
GLM-5.2 over Zread text 200 2.704 s Valid JSON; 3,093 total tokens
GLM-5.2 over GitHub control 200 2.264 s Valid JSON; 3,600 total tokens
Sanitized GLM-5.2 Zread MCP test showing ten HTTP 200 observations, three live tools, 0.5469 current-source line recall, zero of four current markers, a hidden business error and two valid model controls

Actual evidence view generated from the sanitized result archive. It is not a Z.ai dashboard and contains no credential, Authorization header, response ID, MCP session value, cookie or model reasoning.

This is not a latency benchmark. The rows perform different work, and each time is one client-side observation rather than a percentile, service-level objective or availability claim. The useful evidence is contractual: connection, schema discovery, all three tool shapes, returned content, one controlled failure and source-specific validation.

Connect OpenCode without committing the key

Section titled “Connect OpenCode without committing the key”

OpenCode’s current Remote MCP documentation supports environment expansion inside headers. Put the Coding Plan key in the shell or your secret manager:

Load a Coding Plan key without printing it
export ZHIPU_API_KEY="your-coding-plan-key"
test -n "$ZHIPU_API_KEY" && echo "ZHIPU_API_KEY is set"

Then merge this mcp block into the project’s opencode.json:

opencode.json — Zread Remote MCP
{
"$schema": "https://opencode.ai/config.json",
"model": "zai-coding-plan/glm-5.2",
"mcp": {
"zread": {
"type": "remote",
"url": "https://api.z.ai/api/mcp/zread/mcp",
"oauth": false,
"headers": {
"Authorization": "Bearer {env:ZHIPU_API_KEY}"
},
"enabled": true,
"timeout": 15000
}
}
}

The MCP block has no model field. zai-coding-plan/glm-5.2 selects the agent model elsewhere in OpenCode; zread only adds repository tools. If the current provider IDs, endpoint or key source are unfamiliar, start with the separately tested GLM-5.2 OpenCode setup and then add this MCP block.

The 15-second tool-fetch timeout is an application choice, not a Z.ai guarantee. OpenCode currently documents a five-second default, while our tools/list observation took 0.580 seconds. A bounded increase gives transient setup work room without allowing an indefinite hang. Set a separate longer task timeout for large repository calls rather than using one unlimited timer for every phase.

Do not paste a real key into JSON, a screenshot, a shell history example or a bug report. A placeholder copied literally should fail; that is safer than a tutorial that normalizes committed credentials.

Verify a tool call, not just a config file

Section titled “Verify a tool call, not just a config file”

First ask OpenCode to show MCP status:

Inspect registered MCP servers
opencode mcp list

A green zread entry proves that the config parsed and tool discovery succeeded. It does not prove the intended repository can be read or that the returned file is current. Run an explicit, checkable task:

Bounded acceptance prompt
Use only the zread MCP.
1. Call get_repo_structure for zai-org/GLM-5.
2. Call read_file for README.md.
3. Report the first Markdown heading and whether the exact strings
GLM-5.2, IndexShare, 2.9, and 1M are present.
Do not edit files or call a shell.

Record the client version, selected model, repository, tool names, status, elapsed time, returned heading and marker result. Configuration screenshots are setup evidence; a completed tool response is integration evidence. A freshness comparison is stronger still.

Our archived test used a client-independent Streamable HTTP MCP sequence inside Docker rather than treating OpenCode’s UI as the protocol oracle. It requested MCP 2025-03-26; the server returned 2024-11-05. That negotiation worked in this run. A future client that drops the older version could fail even if the same JSON still looks correct.

Treat the live tool schema as a versioned contract

Section titled “Treat the live tool schema as a versioned contract”

tools/list returned exactly these shapes:

search_doc(repo_name, query, language?)
read_file(repo_name, file_path)
get_repo_structure(repo_name, dir_path?)

repo_name was required everywhere. query was required for search, file_path for file reading, and dir_path was optional for structure. All three schemas set additionalProperties: false. Send zai-org/GLM-5, not a full GitHub URL, and send repository-relative paths rather than absolute filesystem paths.

Each of our four tool results put one JSON-encoded string inside the MCP text item. The probe decoded the outer MCP object, then unwrapped the value only when a second JSON parse yielded a string. Without that step, a multiline README appeared as one line full of escaped \n sequences. Do not blindly parse arbitrary objects as text: accept the documented content type, cap the body and make any compatibility unwrap narrowly typed and observable.

Schemas and response shapes can change independently of model weights. Keep a small canary that records server name, negotiated protocol, tool names, required fields and response shape. Fail closed if a formerly read-only tool becomes write-capable or the client introduces automatic execution you did not authorize.

Compare an indexed snapshot with the current origin

Section titled “Compare an indexed snapshot with the current origin”

The public GLM repository was a useful control because its current README began with GLM-5.2 & GLM-5.1 & GLM-5. It named a solid 1M context and IndexShare reuse across every four sparse attention layers, with a stated 2.9× per-token FLOPs reduction at that context.

Zread’s read_file result began with GLM-5.1 & GLM-5 and linked to a /blob/master/README.md source. The comparison was:

Release marker Current GitHub README Zread read_file
GLM-5.2 Present Missing
IndexShare Present Missing
2.9 Present Missing
1M Present Missing

Across unique nontrivial lines, 35 of the current source’s 64 lines matched exactly, giving 0.5469 source-line recall for this one file comparison. That is not semantic recall and not whole-repository coverage. The result shows one indexed snapshot lagging one current origin on one date; it does not prove every Zread repository is stale.

Turn freshness into a task-specific contract. For release notes, require the release name or commit. For an API migration, require the new endpoint and a version heading. For security work, use the exact advisory or commit as the source of truth. If the marker is absent, fetch the current origin through an approved path or stop for review. Do not ask GLM-5.2 to invent the missing delta.

Detect an application error inside HTTP 200

Section titled “Detect an application error inside HTTP 200”

The deliberate read_file call for __glm52_probe_missing__.md returned:

Observed sanitized tool text
code: 1015, message: tool execute error,
error: `__glm52_probe_missing__.md` does not exist

Transport status was 200 and MCP result.isError was false. A caller that checks only those two fields can pass an error string downstream as repository evidence.

A narrow validator can reject the observed shape while preserving telemetry:

Layer transport, envelope and application checks
def accept_zread_text(http_status, is_error, text, required_markers=()):
if http_status != 200:
raise RuntimeError(f"Zread transport failed: HTTP {http_status}")
if is_error is True:
raise RuntimeError("Zread MCP marked the tool result as an error")
if text.startswith("code: 1015") or "tool execute error" in text:
raise RuntimeError("Zread returned an application-level tool error")
if not text.strip():
raise RuntimeError("Zread returned empty text")
missing = [marker for marker in required_markers if marker not in text]
if missing:
raise RuntimeError(f"Repository freshness markers missing: {missing}")
return text

The text match is a fixture for the error we observed, not a complete Z.ai error taxonomy. Preserve sanitized codes, inspect new shapes and prefer a documented structured error field if the server adds one.

Bound untrusted repository data before synthesis

Section titled “Bound untrusted repository data before synthesis”

Public code and documentation are external data. They can contain stale instructions, malicious comments or prose that tells an agent to reveal secrets and run commands. The OWASP prompt-injection guidance recommends separating instructions from remote content, validating tool parameters and applying least privilege.

Our probe released at most 24,000 characters inside explicit untrusted-data delimiters. The first request left thinking at its default and set max_tokens: 300. It returned 200, consumed all 300 completion tokens and did not yield parseable JSON. The corrected controls made output behavior explicit:

Relevant GLM-5.2 request controls
{
"model": "glm-5.2",
"thinking": { "type": "disabled" },
"max_tokens": 500,
"response_format": { "type": "json_object" }
}

Both corrected calls produced valid JSON. Over the Zread snapshot, GLM-5.2 reported GLM-5, DeepSeek Sparse Attention and null for the current IndexShare fields. Over the current GitHub control, it reported GLM-5.2, IndexShare, four shared layers, 2.9 and 1M. The model followed the supplied evidence; it did not recover facts absent from stale context.

That A/B test is the practical reason to validate retrieval before synthesis. If a reasoning-heavy task needs thinking, allocate a sufficient output budget and validate the final structured object. See the GLM-5.2 reasoning-effort map for the difference between selecting a reasoning path and merely capping generation.

Separate Coding Plan MCP quota from model tokens

Section titled “Separate Coding Plan MCP quota from model tokens”

The current Coding Plan overview says Zread, Web Search and Web Reader share one monthly MCP pool:

Plan Combined Search + Reader + Zread calls
Lite 100/month
Pro 1,000/month
Max 4,000/month

Those calls are separate from the five-hour and weekly model prompt quotas. The overview listed Coding Plan from $18/month when checked, while model multipliers and promotions varied by time. Recheck the live terms rather than embedding those numbers in an annual budget.

The Coding Plan FAQ says exhausted supported plan quota does not spill into general API balance. The current public pricing table listed GLM-5.2 at $1.40/M input and $4.40/M output, but it did not publish a pay-as-you-go Zread price. Our two final model extractions calculate to $0.0097662 in model tokens; that figure excludes the subscription and MCP pool.

For the broader choice, compare Coding Plan, pay-as-you-go API and self-hosting and model token scenarios in the GLM-5.2 cost calculator. Do not use either page to fill in an unpublished Zread per-call price.

Keep read-only retrieval from becoming unbounded agency

Section titled “Keep read-only retrieval from becoming unbounded agency”

The official MCP security practices stress server trust, explicit consent and least privilege. Zread’s three observed tools are read-oriented, but their content enters an agent that may also possess shell, file-write, browser or deployment tools. A malicious README can therefore influence a higher-privilege next action.

Use layered controls:

  • allowlist repository owners or exact repositories after validating the owner/repo syntax;
  • reject absolute paths, backslashes, nulls and . or .. traversal segments before a file request;
  • cap returned bytes, lines and tool calls per task;
  • require a release marker, commit or direct-origin check when freshness affects the answer;
  • wrap tool output as untrusted data and keep trusted instructions outside that boundary;
  • require human approval before writes, credential access, package installation, publication or deployment;
  • log sanitized tool name, arguments, timing, content hash and validation result—not keys, session values or full private context.

Our syntax fixture intentionally accepted localhost/repo as a valid string shape but labelled it as still needing a policy allowlist. Syntax is not authorization. Likewise, “public repository” does not mean “safe instructions.”

Troubleshoot Zread by the contract that failed

Section titled “Troubleshoot Zread by the contract that failed”
Symptom Check first Avoid concluding
401 or invalid token Coding Plan key, activation, header format, entitlement and nonempty env expansion The repository is unsupported
MCP appears but has no tools tools/list, client timeout, negotiated protocol and current server URL The JSON file alone proves connectivity
Repository access fails Exact owner/repo, public visibility and support on zread.ai Every public repository is indexed
File “succeeds” with error prose HTTP, isError, returned business code and nonempty-content contract HTTP 200 means the file exists
Release details are old Current default-branch file, commit/release marker and Zread result hash GLM-5.2 will infer the missing update
Multiline content looks escaped Whether the text item contains one JSON-encoded string layer Backslash sequences are real source lines
Structured model output truncates Thinking mode, output cap, finish reason and JSON parse Raising context alone fixes output budgeting
Goose setup is unclear Current Z.ai client section and linked issue One contradictory documentation line settles support

The last row matters because the dedicated Zread page currently lists Goose among supported clients and later says it is not supported. Treat that as a documentation conflict, check the linked issue and perform a live tool call before adopting it. Do not silently generalize our protocol test into a client-support guarantee.

If the failure occurs only in a large project, retry from an empty diagnostic directory with the minimal config and one public repository. OpenCode merges configuration sources; a project override can replace the header, timeout or MCP block you thought you were testing.

Promote Zread only after an acceptance sequence

Section titled “Promote Zread only after an acceptance sequence”

Use a sequence that proves each boundary:

  1. Record the coding client version, selected glm-5.2 route and current first-party documentation date.
  2. Confirm opencode mcp list discovers exactly the expected read tools.
  3. Call get_repo_structure for one allowlisted public repository.
  4. Call read_file for a known file and reject empty, error-shaped or oversized output.
  5. Compare a release marker, commit or hash with the current origin when the task needs current facts.
  6. Send only the bounded, validated content to GLM-5.2 as untrusted data.
  7. Validate the model’s schema and source claims before any consequential tool action.
  8. Monitor transport, MCP-envelope, application-error, freshness and quota failures as separate metrics.

The reader-facing JSON preserves the tool schemas, timings, hashes, marker comparison, missing-file result, model answers, token arithmetic and local guard fixtures. The repository bundle adds the standard-library probe, pinned container runner, first failed attempt, visual sources and limitations.

No model ID appears in the MCP server object. Zread exposes repository tools. Select zai-coding-plan/glm-5.2 in OpenCode’s model configuration; the MCP endpoint remains https://api.z.ai/api/mcp/zread/mcp.

The current first-party page describes Zread as exclusive to GLM Coding Plan users and does not document a general metered Zread route. A pay-as-you-go key can fund a separate GLM-5.2 model request after your application validates content.

Does the stale README make Zread unusable?

Section titled “Does the stale README make Zread unusable?”

No. Structure discovery and broad repository orientation can remain useful. The observation means release-sensitive facts need an origin freshness check. It is one repository, file, account and date—not a global defect rate.

The current troubleshooting guide says to confirm that the repository is open source/public and supported. Do not send private repository identifiers or assume private access without a separate first-party product contract.

The JSON-RPC exchange itself completed, while the returned text carried application error 1015. Validate transport, MCP envelope and the application-level result separately.

No freshness SLA was found in the checked documentation. Our search result also omitted three current GLM-5.2 markers. Require a date, release, commit or direct-origin control when currency changes the decision.

Evidence date: July 29, 2026 (Asia/Hong_Kong). Recheck endpoint, client support, quotas, plan pricing, model multipliers and repository index state before production use.