c7search

command module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 30, 2026 License: MIT Imports: 2 Imported by: 0

README

c7search

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.

Why a CLI

  • 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.

Install

TL;DR — pick one
You have… Run Quarantine?
Go ≥ 1.26.2 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.

Where to install

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.2.

go install github.com/kevin-burns/c7search@latest
# binary lands in $(go env GOBIN) or $(go env GOPATH)/bin

Add $(go env GOPATH)/bin to your PATH if it isn't already:

echo 'export PATH="$(go env GOPATH)/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
c7search version

No 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.

Troubleshooting: c7search: command not found after go install

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 version

If 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 -20

Common failure modes:

  • Corporate proxy blocking proxy.golang.orgGOPROXY=direct go install github.com/kevin-burns/c7search@v0.1.0 (slower, bypasses the module proxy).
  • ~/go owned by root from a previous sudo go installsudo chown -R "$USER" ~/go.
From a release binary

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/c7search
macOS: clear the Gatekeeper quarantine

Browsers 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 version

System-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 version

If 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.quarantine opts you out of the first-launch Gatekeeper review for that file. The ad-hoc codesign - (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 manual codesign step won't be needed.

Windows: clear the SmartScreen mark

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.exeProperties → tick UnblockOK.

PowerShell (equivalent, scriptable):

Unblock-File -Path .\c7search.exe
.\c7search.exe version

Linux: no quarantine system; chmod +x and run.

API key

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.

Get a key

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).

Provide it (three ways, in precedence order)
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 ~/.zshrc

If 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
Verify it works
$ c7search auth status
key:    ctx7sk...XXXX
source: $CONTEXT7_API_KEY
status: ok

The key line shows only prefix...last4. If status: invalid key appears, the call returned 401 — exit code is 3.

Confirm the key never leaks

--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  # → empty

If 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.

Cache and the API key

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.

Commands

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
Flags
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)
Exit codes
Code Meaning
0 OK
1 No results
2 API error (5xx, rate-limited, 404)
3 Auth error (401/403)
4 Usage / bad request

Using c7search from an LLM agent

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.

Quick recipes

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 4000

One-shot (only when the library name is unambiguous):

c7search ask "how do I do app router middleware in next.js" --tokens 4000

ask 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. ask uses 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). Use resolve --library-name to pin the library, then docs to fetch — that path uses the v2 endpoints exclusively and has predictable resolution.

Format guidance for agents
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.
Stable contract for downstream scripts

These are pinned by tests; a change here is a breaking change.

  • resolve --json → array of objects with at least id, title, description, trustScore, totalSnippets, stars.
  • docs --json → object with libraryId, format, snippets[]. Each snippet has codeTitle, codeLanguage, codeList[].language, codeList[].code.
  • Exit codes (table above) are part of the contract — branch on $?.
Token budget guidance
  • 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)
Authenticated vs anonymous

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)

Cache

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.

Security

  • API keys are never logged. Errors are passed through redact.RedactURL and redact.RedactBearer before being returned.
  • auth status shows only prefix...last4 — never the full key.
  • Every external HTTP call runs under a bounded context.WithTimeout.
  • Cache files: mode 0600. Cache dir: mode 0700. 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.

Development

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 parsers

Quality 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 files

Hooks configured in .pre-commit-config.yaml: go fmt, go vet, go-build, golangci-lint, trailing-whitespace / end-of-file fixers, large-file guard, private-key detector, and gitleaks for broad-spectrum secret scanning.

Acknowledgements

This CLI is a thin wrapper around the Context7 public API. Original MCP server: https://github.com/upstash/context7.

License

MIT — see LICENSE.

Documentation

Overview

Command c7search is a CLI for the Context7 documentation API. See `c7search --help` for usage.

Directories

Path Synopsis
internal
cache
Package cache provides a small filesystem cache for search results and docs payloads.
Package cache provides a small filesystem cache for search results and docs payloads.
cli
Package cli wires the cobra command tree.
Package cli wires the cobra command tree.
client
Package client is the typed HTTP wrapper around context7.com/api/v1.
Package client is the typed HTTP wrapper around context7.com/api/v1.
config
Package config (in v0.1) provides only the filesystem-permission warning helper.
Package config (in v0.1) provides only the filesystem-permission warning helper.
output
Package output renders search results and docs to text/markdown/json.
Package output renders search results and docs to text/markdown/json.
redact
Package redact provides allocation-light helpers for stripping secrets out of strings before they hit logs, errors, or stdout.
Package redact provides allocation-light helpers for stripping secrets out of strings before they hit logs, errors, or stdout.
version
Package version exposes build-time metadata stamped into the binary.
Package version exposes build-time metadata stamped into the binary.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL