ctx.vanshul.com

A public Model Context Protocol (MCP) server, running as a Cloudflare
Worker, that turns a GitHub repository or a documentation site into agent-ready
context. Point an AI agent at it and it can pack a whole repo (or crawl a docs
site) into one token-counted blob β or search it and get back only the relevant
passages, each with its file/URL and line.
It's the agent-first companion to mcp/: where mcp reads a single
web page, ctx reads whole repos and doc sites. The repo pipeline fetches,
gunzips and parses the tarball in-process; docs extraction reuses Mozilla
Readability + Turndown.
Endpoint
POST https://ctx.vanshul.com/mcp # JSON-RPC 2.0 (MCP)
GET https://ctx.vanshul.com/health # { ok: true, tools: [...] }
| Tool | Input | Returns |
|---|
pack_repo | { repo, ref?, include?, exclude?, max_tokens? } | The repo as one context blob with ==== path ==== headers + token estimate |
search_context | { repo, query, ref?, include?, exclude?, max_matches?, context_chars? } | Only the passages matching query, each with file, line and score |
list_files | { repo, ref?, include?, exclude? } | JSON: the text files ctx would include, with byte sizes |
get_file | { repo, path, ref? } | The full text of a single file |
pack_docs | { url, depth?, max_pages?, max_tokens? } | A crawled docs site as one context blob (each page β Markdown) |
search_docs | { url, query, depth?, max_pages?, max_matches?, context_chars? } | Only the docs passages matching query, each with page URL, line and score |
repo is owner/repo, owner/repo/ref, or a github.com URL. url (for the
docs tools) is an absolute http(s) docs page to start crawling from.
Connect from an MCP client
{ "mcpServers": { "ctx": { "url": "https://ctx.vanshul.com/mcp" } } }
Stdio-only clients bridge with npx mcp-remote https://ctx.vanshul.com/mcp.
Add it to your client
- Cursor β Settings β MCP β Add new server, or drop this into
~/.cursor/mcp.json:
{ "mcpServers": { "ctx": { "url": "https://ctx.vanshul.com/mcp" } } }
- Claude Desktop β add the same block to
claude_desktop_config.json (Settings β Developer β Edit Config). If your version is stdio-only, use:
{ "mcpServers": { "ctx": { "command": "npx", "args": ["mcp-remote", "https://ctx.vanshul.com/mcp"] } } }
- Continue / VS Code β add
ctx with URL https://ctx.vanshul.com/mcp to your MCP servers config.
Try it with curl
curl -s https://ctx.vanshul.com/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"pack_repo","arguments":{"repo":"sindresorhus/slugify","max_tokens":8000}}}'
curl -s https://ctx.vanshul.com/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search_context","arguments":{"repo":"sindresorhus/slugify","query":"replace separator"}}}'
Layout
ctx/
βββ src/
β βββ worker.ts # entry: routes /mcp, /health, rate limit, CORS
β βββ mcp.ts # JSON-RPC dispatch + the six tool definitions
β βββ github.ts # fetch tarball, gunzip, parse tar, filter files (no deps)
β βββ pack.ts # assemble the context blob + token estimate
β βββ search.ts # ranked passage search over files (file + line)
β βββ docs.ts # crawl a docs site into pages (BFS, same-section)
β βββ extract.ts # HTML -> Markdown / links (Readability + Turndown)
β βββ fetcher.ts # bounded fetch with re-validated redirects (SSRF)
β βββ security.ts # SSRF guard (block private/internal addresses)
βββ public/
β βββ index.html # landing page (served for non-API paths)
β βββ og.png / og.svg
β βββ robots.txt
β βββ sitemap.xml
βββ tests/ # vitest: github (tar parsing), pack, search, mcp, worker
βββ docs/ # architecture, tools/API reference, deployment
βββ wrangler.toml
βββ package.json
βββ tsconfig.json
Develop & deploy
cd ctx
npm install
npm run typecheck
npm test
npm run dev
npm run deploy
How it works
owner/repo β github.com tarball β DecompressionStream('gzip')
β in-process tar parse β drop binaries/lockfiles/build dirs
β pack (concat + token estimate) OR search (ranked passages)
The GitHub URL is always constructed from a fixed owner/repo slug, so the repo
tools have no SSRF surface. The docs tools fetch caller-supplied URLs, so every
URL and redirect hop is re-validated against the SSRF guard. Downloads are
bounded (timeout, size caps, page/file-count caps) and results are cached
per-isolate for a few minutes.
Security & limits
- No SSRF: input is a repo slug, not an arbitrary URL; only github.com is fetched.
- Bounded: 20s download timeout, ~60 MB uncompressed cap, 512 KB/file, β€3000 files, per-IP rate limit.
- Stateless & private: no code stored, no LLM in the loop; public repos by default (private with a token).
Authentication (optional)
Set a GITHUB_TOKEN Worker secret to lift GitHub's rate limit (60 β 5,000/hour) and
read private repos:
wrangler secret put GITHUB_TOKEN
The token is a Worker secret only β never a tool argument β so it can't leak to an agent.
Documentation
License
MIT Β© Vanshul Goyal