An independent third-party Go CLI client for the public
Context7 documentation HTTP API. It exposes the same
two operations as Upstash's official MCP server
(upstash/context7) —
resolve-library-id and get-library-docs — but as a single static binary
rather than an MCP transport.
Built for environments where adding new MCP servers requires approvals but
running a signed CLI does not. This is not a replacement for, nor a fork
of, Upstash's MCP server; it's an alternative transport for the same
public API. If your client supports MCP, use the upstream
@upstash/context7-mcp directly.
- Locked-down enterprise: MCP server allow-lists block extensions; a CLI binary is fine.
- Mixed OS team: Windows colleagues get a
.exe; macOS and Linux get a static binary. No Python, Node, or container runtime required. - Pipe-friendly: status to stderr, payload to stdout, ANSI only on a TTY.
Plays nicely with
glow,jq,less.
| You have… | Run | Quarantine? |
|---|---|---|
| Go ≥ 1.26.3 | go install github.com/kevin-burns/c7search@latest |
No — recommended for macOS |
brew (when tap is published) |
brew install kevin-burns/tap/c7search |
No — Homebrew clears it |
Just a release .tar.gz |
Extract, move onto $PATH, then on macOS run xattr -d + codesign - (see below) |
Yes — needs the manual step |
The go install path is the simplest on macOS because Go-built
binaries are produced locally and never touch the
com.apple.quarantine xattr — Gatekeeper has nothing to flag and you
skip the xattr / codesign dance entirely.
c7search is a single static binary; pick whichever directory in your
$PATH you prefer:
| Location | Scope | sudo? | When to choose |
|---|---|---|---|
~/.local/bin |
user-only | no | XDG-style; lowest friction. Add to PATH if not already there. |
~/bin |
user-only | no | Classic per-user path. macOS adds it to PATH automatically when present. |
/usr/local/bin |
system-wide | yes | Shared with other CLI tools; survives user-profile resets. |
/opt/homebrew/bin (Apple Silicon) /usr/local/bin (Intel) |
system-wide | depends | If you want to drop alongside Homebrew tools. |
After install, confirm:
which c7search # → /Users/you/.local/bin/c7search (or wherever)
c7search version # → c7search v0.1.0 (...)If which finds nothing, the directory isn't on $PATH. Add to
~/.zshrc (zsh) or ~/.bashrc (bash):
export PATH="$HOME/.local/bin:$PATH"Requires Go ≥ 1.26.3.
go install github.com/kevin-burns/c7search@latest
# binary lands in $(go env GOBIN) or $(go env GOPATH)/binAdd $(go env GOPATH)/bin to your PATH if it isn't already:
echo 'export PATH="$(go env GOPATH)/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
c7search versionNo xattr / codesign step is needed. The binary is built on
your machine, so it never receives the com.apple.quarantine extended
attribute that browsers and curl set on downloaded files.
The most common cause on macOS: the install succeeded, but
$GOPATH/bin isn't on your $PATH. The binary is sitting in
~/go/bin/c7search and your shell can't see it. Three quick checks:
# 1. Where does Go put binaries on your machine?
go env GOBIN GOPATH
# If GOBIN is set, the binary is in $GOBIN.
# If GOBIN is empty, it's in $(go env GOPATH)/bin (typically ~/go/bin).
# 2. Does the binary actually exist?
ls -l "$(go env GOPATH)/bin/c7search"
# Expected: -rwxr-xr-x ... ~/go/bin/c7search
# 3. Is that directory on your PATH?
echo "$PATH" | tr ':' '\n' | grep -E '(/go/bin|GOBIN)'
# If empty, that's your problem — fix below.If the binary exists but isn't on $PATH:
echo 'export PATH="$(go env GOPATH)/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
which c7search # should print ~/go/bin/c7search
c7search versionIf the binary doesn't exist, the install failed silently. Re-run with
-v:
go install -v github.com/kevin-burns/c7search@v0.1.0 2>&1 | tail -20Common failure modes:
- Corporate proxy blocking
proxy.golang.org→GOPROXY=direct go install github.com/kevin-burns/c7search@v0.1.0(slower, bypasses the module proxy). ~/goowned by root from a previoussudo go install→sudo chown -R "$USER" ~/go.
Use this path if you don't have Go installed, or you want exactly the artifact that shipped with a tagged release.
Download the platform archive from the Releases page and verify the SHA256:
shasum -a 256 -c checksums.txt
tar -xzf c7search-darwin-arm64.tar.gz # adjust for your platform
mv c7search ~/.local/bin/ # or wherever (see table above)
chmod +x ~/.local/bin/c7searchBrowsers and curl set the com.apple.quarantine extended attribute on
downloaded files. If you try to run the binary without clearing it,
macOS shows: "c7search" cannot be opened because the developer cannot
be verified.
Recommended (no sudo, user-local install):
# 1. Strip the quarantine xattr
xattr -d com.apple.quarantine ~/.local/bin/c7search
# 2. Ad-hoc sign so future Gatekeeper checks pass
codesign --force --sign - ~/.local/bin/c7search
# 3. Verify
c7search versionSystem-wide install (/usr/local/bin, requires sudo):
sudo xattr -d com.apple.quarantine /usr/local/bin/c7search
sudo codesign --force --sign - /usr/local/bin/c7search
c7search versionIf xattr -d reports No such xattr, the file wasn't quarantined —
skip to the codesign step (or run c7search version directly; it may
already work).
To inspect what extended attributes are present before/after:
xattr -l ~/.local/bin/c7search
# expected after the steps above: (empty output)Why this works: stripping
com.apple.quarantineopts you out of the first-launch Gatekeeper review for that file. The ad-hoccodesign -(the dash means "sign with no developer identity") gives the binary a local signature so subsequent integrity checks pass. This is appropriate for a binary you've SHA256-verified yourself. Once we publish cosign-signed releases (planned for v0.2), the manualcodesignstep won't be needed.
Edge / Chrome / curl mark downloaded .exes with a Mark-of-the-Web
zone identifier. SmartScreen prompts the first time you run them.
GUI: Right-click c7search.exe → Properties → tick Unblock
→ OK.
PowerShell (equivalent, scriptable):
Unblock-File -Path .\c7search.exe
.\c7search.exe versionLinux: no quarantine system; chmod +x and run.
c7search works anonymously against the public Context7 API — no key
required. Anonymous calls are rate-limited (~200 requests per window
per IP); set an API key to lift that ceiling.
Sign in at https://context7.com and copy your key from the dashboard.
Keys start with the prefix ctx7sk- (used by the redactor for safer
display).
| Precedence | Method | When to use |
|---|---|---|
| 1. highest | --api-key ctx7sk-... flag |
one-off override; CI jobs that pass a per-run key |
| 2. | CONTEXT7_API_KEY env var |
normal day-to-day use |
| 3. lowest | none | anonymous tier — works for trivia and casual lookups |
# 1. per-invocation flag (overrides env)
c7search --api-key ctx7sk-XXXX auth status
# 2. current shell session only
export CONTEXT7_API_KEY=ctx7sk-XXXX
c7search auth status
# 3. persistent across sessions — add to ~/.zshrc (or ~/.bashrc)
echo 'export CONTEXT7_API_KEY=ctx7sk-XXXX' >> ~/.zshrc
source ~/.zshrcIf you keep your shell rc file in a public dotfiles repo, source the key from a gitignored sidecar instead of inlining it:
# in ~/.zshrc (committed)
[ -f ~/.zshrc.local ] && source ~/.zshrc.local
# in ~/.zshrc.local (gitignored)
export CONTEXT7_API_KEY=ctx7sk-XXXX$ c7search auth status
key: ctx7sk...XXXX
source: $CONTEXT7_API_KEY
status: okThe key line shows only prefix...last4. If status: invalid key
appears, the call returned 401 — exit code is 3.
--debug produces a structured request log on stderr. Bearer tokens
and URL-embedded credentials are scrubbed by a redactHandler before
the line is written:
c7search --debug resolve fastmcp 2>&1 | grep -i bearer # → emptyIf that command returns anything containing your key, report it as a
security bug — see SECURITY.md. The redactor is
covered by TestRedactHandler_ScrubsBearerAndCreds; a regression
should fail CI before it ships.
The on-disk cache namespaces every entry on sha256(key)[:8] (or
anon when no key is set). An agent that runs anonymously, then sets
a key mid-session and re-runs the same query, will hit the API again
rather than serve stale anonymous-tier results. The key itself is
never written to disk.
c7search resolve "next.js app router"
c7search resolve --library-name fastmcp "tool registration" # v2 search
c7search docs /vercel/next.js --topic routing --tokens 5000
c7search ask "how do I do middleware in next.js"
c7search ask ... --json | jq .
c7search docs ... | glow -
c7search auth status # show key source + connectivity
c7search cache path # print the on-disk cache directory
c7search cache clear # bust the cache
c7search version # build version, commit, date
| Flag | Description |
|---|---|
--api-key |
Override $CONTEXT7_API_KEY for this invocation |
--format |
text | md | json (per-command default) |
--json |
Shorthand for --format json |
--no-cache |
Bypass the on-disk cache for this invocation |
--debug |
Verbose stderr logging (secrets redacted) |
--version |
Print version and exit |
resolve-only flags:
| Flag | Description |
|---|---|
--limit N |
Cap displayed results (0 = unlimited; default 10) |
--library-name |
Route through /api/v2/libs/search; the positional argument becomes the relevance topic. Use this when you know the library by name. |
docs / ask-only flags:
| Flag | Description |
|---|---|
--topic |
Narrow to a topic (semantic query within the library) |
--tokens N |
Approximate token budget for the response (default 5000) |
| Code | Meaning |
|---|---|
| 0 | OK |
| 1 | No results |
| 2 | API error (5xx, rate-limited, 404) |
| 3 | Auth error (401/403) |
| 4 | Usage / bad request |
c7search is built to be a clean substitute for the curl-and-jq recipe in
the Context7 agent skill: same v2 endpoints (/api/v2/libs/search,
/api/v2/context), but with on-disk caching, secret redaction, retries
with bounded backoff, and predictable exit codes.
A ready-to-install Claude skill that wraps this CLI ships at
skills/c7search/SKILL.md. To install it
into a Claude Code session:
# user-scoped (available in every project)
mkdir -p ~/.claude/skills && cp -r skills/c7search ~/.claude/skills/
# or project-scoped (only the current repo)
mkdir -p .claude/skills && cp -r skills/c7search .claude/skills/Then /reload-plugins (or restart the session). The skill auto-activates
when you ask Claude about a library, framework, or specific API.
Two-step (verbose, gives you control):
LIB=$(c7search resolve --library-name fastmcp "tool registration" --json --limit 1 \
| jq -r '.[0].id')
c7search docs "$LIB" --topic "tool registration" --tokens 4000One-shot (only when the library name is unambiguous):
c7search ask "how do I do app router middleware in next.js" --tokens 4000ask runs resolve + docs and prints the resolution announcement
(resolved: /vercel/next.js (trust=9.5, snippets=2352)) to stderr
so you can pipe stdout straight into the model context.
For agents: prefer the two-step recipe.
askuses free-form v1 search and picks the highest-trust hit, which can drift to a different library when your question phrasing matches another well-trusted entry (e.g. "register a tool in fastmcp" can resolve to a Go DI library because "register tool" matches it strongly). Useresolve --library-nameto pin the library, thendocsto fetch — that path uses the v2 endpoints exclusively and has predictable resolution.
| Goal | Use | Why |
|---|---|---|
| Feed docs into a model context | default markdown (docs, ask) |
Context7 returns a markdown-ish format pre-optimized for LLM token efficiency: Source: headers + fenced code blocks + --- separators. JSON wrapping wastes ~10–15% tokens on braces. |
| Filter/sort snippets by language before ingesting | --json on docs |
Exposes per-snippet codeLanguage, codeTitle, codeList so you can jq to e.g. only TypeScript blocks. |
| Pick a library ID from candidates | --json on resolve |
Gives id, trustScore, totalSnippets, stars for ranking. |
| Show docs to a human at a terminal | default text/markdown, optionally piped through glow |
ANSI/colour suppressed automatically when stdout isn't a TTY. |
These are pinned by tests; a change here is a breaking change.
resolve --json→ array of objects with at leastid,title,description,trustScore,totalSnippets,stars.docs --json→ object withlibraryId,format,snippets[]. Each snippet hascodeTitle,codeLanguage,codeList[].language,codeList[].code.- Exit codes (table above) are part of the contract — branch on
$?.
- Trivia / one-API lookups:
--tokens 1500 - Walkthrough of a feature:
--tokens 4000–6000(default 5000) - Whole-library reference:
--tokens 10000+(anonymous tier may rate-limit)
Anonymous works for trivia; expect rate-limiting on bursty workflows. For a key, see API key above. Quick check:
c7search auth status # → "status: ok" or "invalid key" (exit 3)Responses are cached on disk under os.UserCacheDir()/c7search/ —
typically:
- macOS:
~/Library/Caches/c7search/ - Linux:
~/.cache/c7search/(or$XDG_CACHE_HOME/c7search/) - Windows:
%LOCALAPPDATA%\c7search\Cache\
Search results live 6h; docs payloads live 24h. The cache directory is
created with mode 0700 and files with 0600. Run c7search cache clear
to bust everything.
- API keys are never logged. Errors are passed through
redact.RedactURLandredact.RedactBearerbefore being returned. auth statusshows onlyprefix...last4— never the full key.- Every external HTTP call runs under a bounded
context.WithTimeout. - Cache files: mode
0600. Cache dir: mode0700. Defense in depth even though no secrets live there today. - Build pipeline:
CGO_ENABLED=0,-trimpath, SHA256 checksums. Cosign signing planned for v0.2.
See SECURITY.md for the disclosure policy.
make build # build c7search in repo root
make test-race # tests with race detector
make lint # golangci-lint (gosec, errcheck, govet, staticcheck)
make secrets-scan
make vuln # govulncheck
make cover # 70% coverage gate
make fuzz # opt-in fuzz harness for parsersQuality gates also run via pre-commit hooks. The repo uses
prek when available (Rust, faster cold
start) and falls back to the canonical Python pre-commit otherwise:
# install one of the two runners
brew install prek # recommended
# or: pipx install pre-commit # canonical fallback
make precommit-install # wires the git hook
make precommit-run # runs every hook over all filesHooks configured in .pre-commit-config.yaml: go-fmt, module-scoped
go-vet-mod and go-build-mod (Go tooling reasons about packages, not
single files), golangci-lint, trailing-whitespace / end-of-file fixers,
large-file guard, private-key detector, and gitleaks for broad-spectrum
secret scanning. The Go hooks come from the maintained
TekWizely/pre-commit-golang
(the older dnephin/pre-commit-golang was archived in 2023).
This CLI is a thin wrapper around the Context7 public API. Original MCP server: https://github.com/upstash/context7.
MIT — see LICENSE.