# ctx

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/vanshulgoyal101/ctx  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/ctx

## Description
Turn a GitHub repo or docs site into agent-ready context: pack it or search it, over MCP.

## Claude Desktop Quick Installation
Heuristic fallback — verify the package name and runner against the repository README before running it. Uses `npx` (confidence: low):

```json
"mcpServers": {
  "ctx": {
    "command": "npx",
    "args": ["-y","ctx"]
  }
}
```

## Documentation & README

# ctx.vanshul.com

[![MCP Registry](https://img.shields.io/badge/MCP_Registry-io.github.vanshulgoyal101%2Fctx-a78bfa)](https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.vanshulgoyal101/ctx)
[![Endpoint](https://img.shields.io/badge/endpoint-ctx.vanshul.com%2Fmcp-34d399)](https://ctx.vanshul.com/mcp)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue)](./LICENSE)

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/`](https://github.com/vanshulgoyal101/ctx/blob/HEAD/../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: [...] }
```

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

```json
{ "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`:
  ```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:
  ```json
  { "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

```sh
# Pack a repo, capped to 8000 tokens
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}}}'

# Search a repo for just the relevant passages
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

```sh
cd ctx
npm install
npm run typecheck
npm test           # vitest — full suite
npm run dev        # local worker at http://localhost:8787  (POST /mcp)
npm run deploy     # wrangler 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:

```sh
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

- [docs/architecture.md](https://github.com/vanshulgoyal101/ctx/blob/HEAD/docs/architecture.md) — modules, pipeline, tar parsing, limits
- [docs/tools.md](https://github.com/vanshulgoyal101/ctx/blob/HEAD/docs/tools.md) — full tool & JSON-RPC API reference
- [docs/deployment.md](https://github.com/vanshulgoyal101/ctx/blob/HEAD/docs/deployment.md) — Cloudflare Worker + custom-domain deploy

## License

[MIT](https://github.com/vanshulgoyal101/ctx/blob/HEAD/LICENSE) © Vanshul Goyal

