ctxpeek (pronounced Context Peek) is a local-first MCP server for fresh, version-pinned repo docs.
ctxpeek demonstrates solid tool design with clear, action-oriented naming (resolve_repo, list_docs, fetch_doc, peek, get_changes, etc.) and comprehensive descriptions (avg ~150 chars, well within 10-1024 range). All 10 tools have input schemas with typed parameters and descriptions. However, output schemas are not explicitly documented in the source code, tool results return markdown text + optional structuredContent, but the shape of structuredContent for each tool is not formally specified. Error handling is present (toolErrorResult with structured errors, retryable classification, suggestions) but lacks recovery guidance in tool descriptions. Parameters are well-described but some lack constraints (e.g., max_line_length, max_matches, context have no min/max bounds stated). No tool annotations (readOnlyHint, destructiveHint, idempotentHint) are visible. All tools are READ_ONLY by design, which is appropriate, but this should be declared via annotations for clarity.
On-disk cache state. Pass `{ repo: 'owner/repo' }` for per-repo breakdown.
CHANGELOG entries between two refs (e.g. v14.0.0 → v15.0.0).
Fetch a doc file at a pinned commit. Supports `lines: [start, end]` and `head_bytes` for partial reads.
Unified diff for one file across two refs (e.g. v14.2.0 vs v15.0.0).
Search a repo's issues/PRs, or read one in full. Search with `query` + filters (`labels`, `author`, `assignee`, `state`, `type`, `since`, `sort`, `order`). Or pass `number` to read that issue/PR's full body plus its first `comments` comments. Opt-in per call (separate rate-limit bucket).
List doc files as a tree. Input: 'owner/repo[@ref][#subpath]'. Omit `@ref` for the default branch.
Output schemas not formally documented. Tools return ToolResult<S> with optional structuredContent, but the shape of S for each tool is not specified in code or descriptions. LLMs cannot plan downstream operations without knowing what fields to expect.
Numeric parameters lack bounds. max_line_length, max_matches, context, and comments parameters have no min/max constraints stated in descriptions or schema. LLMs may pass absurd values (e.g., max_matches=999999) causing performance issues.
No tool annotations present. All tools are READ_ONLY and idempotent, but readOnlyHint and idempotentHint are not declared in tool definitions. This forces LLMs to infer safety properties from descriptions alone.
Inferred effective spec: 2025-06-18+.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 64 | 2025-06-18+ | v2 |
Quick look at a doc file. Default: first `n` lines. Pass `query` to grep within the file (literal, or `regex: true`) and get every match with `context` lines around it — deterministic, scoped to this one file. `max_line_length` caps long lines; `max_matches` caps results. Binary/empty files are reported, not dumped.
Show GitHub rate limits, with automatic local fallback when GitHub is unreachable. Pass `details: true` for local throttler internals.
Find peer libraries via github.com links in the repo's README/llms.txt.
Resolve a library/package name to owner/repo. Manifest-aware: prefers packages already in the cwd lockfile. If `ambiguous: true` or the result looks wrong, re-call with `force_refresh: true` or pick from `alternatives[]`.
Error descriptions lack recovery guidance. toolErrorResult returns structured errors with suggestions array, but tool descriptions do not mention what to do on failure (e.g., 'If rate-limited, call rate_limits() to check reset time').
Pagination not explicitly documented. list_docs and get_issues may return large result sets, but descriptions do not mention pagination limits, offset/cursor parameters, or total counts. This risks context window exhaustion.