gopher

One MCP server that fetches, caches and digests context for Claude.

Point it at a GitHub repository and get back a digest small enough to read. Keep facts across conversations in two plain files you can open yourself. Everything runs locally over stdio.

claude mcp add gopher --scope user -- \
  uv run --no-sync --project /path/to/gopher gopher
GitHub

[ WHAT IT DOES ]

Three things, one install

fetch

A whole repository as markdown: directory tree plus the files that matter. Binaries, lock files and node_modules filtered out, the rest ranked.

cache

Memory that survives the conversation, stored as a structured context.json and an append-only diary.md. Both readable without this tool.

digest

Reads a transcript, pulls the facts out with a local Ollama model, and merges them into the cache. Nothing leaves the machine.

[ THE DIGEST ]

Most of a repository is not worth sending

Early versions returned a fixed ten files, which is not a size limit at all: ten files is anywhere from four thousand characters to two hundred thousand. Anything past the client's ceiling was rejected whole, so the caller got nothing rather than something trimmed.

Before

27,194  test/ecosystem/airflow/pyproject.toml
32,048  test/ecosystem/home-assistant/...
27,517  test/ecosystem/pandas/pyproject.toml
 9,156  test/ecosystem/jupyterlab/...
 8,757  test/ecosystem/black/pyproject.toml

228,310characters, mostly the wrong files

After

Cargo.toml
README.md
pyproject.toml
Dockerfile
crates/uv-build/src/main.rs

39,168characters, within budget

There is a total character budget now, spent on the tree first and then files in priority order until it runs out. Ranking accounts for where a file sits, not just what it is called, so a root manifest beats a vendored copy of the same filename buried in a test fixture.

RepositoryBeforeAfter
astral-sh/uv207,86739,321
modelcontextprotocol/python-sdk217,48839,224
punkpeye/awesome-mcp-servers228,31039,168
sktime/sktime-mcp103,78639,231

[ MEMORY ]

Memory you can only read in full is a file

Facts accumulate across conversations. Reading them back used to mean returning the entire store every time, which works until it does not. Search over key paths and values, or read one section by its dot path.

Reading the whole store

read_context()

13,484characters returned

Finding one fact

search_context("memory")

202characters returned

{
  "query": "memory",
  "matches": [
    {
      "key": "projects.precedent.note",
      "value": "agentic memory hackathon, submitted",
      "matched": "value"
    }
  ],
  "shown": 1,
  "total": 1
}

Substring matching over keys and values, no embeddings and no index. On a store of a few hundred facts that is enough, and something cleverer can wait for evidence that it is not.

[ QUICK START ]

Running it

  1. Clone and install
    git clone https://github.com/pyarchana/gopher.git
    cd gopher
    uv venv
    uv pip install -e ".[dev]"
  2. Optional: a GitHub token, for a higher rate limit
    cp .env.example .env
    # add GITHUB_TOKEN=... to that file
  3. Register it with Claude Code
    claude mcp add gopher --scope user -- \
      uv run --no-sync --project /absolute/path/to/gopher gopher
  4. Or with Claude Desktop, in claude_desktop_config.json
    {
      "mcpServers": {
        "gopher": {
          "command": "uv",
          "args": ["run", "--no-sync", "--project",
                   "/absolute/path/to/gopher", "gopher"]
        }
      }
    }

The digest tools additionally want Ollama running locally with llama3.2 pulled. Fetch and cache work without it.

[ TOOLS ]

Ten tools

ToolWhat it does
fetch_github_repo(repo_url)Markdown digest of a public repo, tree plus top files
search_context(query, limit)Find facts whose key or value contains the query
read_context(prefix)The whole store, or just one section
update_context(key, value)Set a value at a dot path
delete_context_key(key)Delete a key by dot path
log_diary(entry, tag)Append a timestamped markdown entry
read_diary(last_n)Read back the last N entries
digest_transcript(transcript_path)Extract facts from a transcript, merge into the store
summarize_only(transcript_path)Same extraction, returns JSON without writing
read_digest_log()Full contents of the digest log

[ CONFIGURATION ]

Four environment variables

Read from a .env file in the project root, which the server loads on startup.

VariableDefaultPurpose
GITHUB_TOKENnoneRaises the GitHub rate limit from 60 to 5,000 an hour
GOPHER_DATA_DIR./dataWhere the store, diary and digest log live
GOPHER_DIGEST_BUDGET40000Maximum characters one repo digest may return
GOPHER_OLLAMA_MODELllama3.2Model used for fact extraction