repopack MCP server

repopack MCP server

Serves local and remote repositories over MCP, enabling AI models to search and retrieve code snippets, file contents, and repository structures on demand.

Category
访问服务器

README

repopack

Give an LLM the context of a repository — without sending the whole project over the network. Two ways to do it, one tool, zero runtime dependencies (standard library only):

  • pack — squeeze a repository (any language) into a single JSON file you can hand to a model, under a token ceiling you choose.
  • serve — index your repositories locally and expose them over MCP, so a model pulls only the snippets a task needs, from all your projects, through one server.

Install

pip install -e .           # or just run: python -m repopack ...

Python 3.9+. No runtime dependencies — that is the point: it works on a bare interpreter.

Pack a repository

# Full pack of the current repository (any path works too)
repopack pack . -o context.json --gzip

# Incremental: only what changed since the last pack
repopack pack . --delta -o delta.json --gzip

# Restore on the other end
repopack unpack context.json -d ./restored

--budget is a ceiling on the whole pack, in estimated tokens — tree, stats and the per-file metadata count against it too, not just file content. The command prints how much of the budget went to content and how much to structure.

How the context is organized

What is left after the structural floor is spent in three layers, in priority order (README/docs → configs/manifests → entrypoints → the rest, boosted by git churn):

  1. full — complete content, secrets redacted, up to ~72% of what is left
  2. skeleton — imports, signatures and docstrings: the interface survives, the bodies do not. Measured on this codebase it keeps ~22% of the original characters (13–29% per file), so treat it as "a fifth of the cost", not a tenth
  3. metadata — path, size, sha256, language (content: null plus a reason)

Besides the files, a pack carries tree (every path), stats, git.recent_commits, todos (harvested TODO/FIXME/HACK) and, in --delta mode, deleted_since_last_pack.

Byte-identical content is packed once. The other copies become pointers (content: null, reason: duplicate_of, plus the path that carries it) — their paths stay in the tree, and unpack resolves the pointer so every file is still restored. The highest ranked copy is the one that keeps the content. Measured on a repository with a SKILL.md copied into three places: duplicated content went from 0.89% to 0.00%, 8,982 characters freed for unique context.

In a large repository the structural floor alone can exceed a small budget — 4,212 files cost about 252k tokens in tree and metadata. When that happens the pack is generated without file content and the tool says so on stderr, naming the floor, instead of quietly returning 26× what you asked for. Trimming the tree to fit would hide which files exist, which is exactly what the pack promises to list.

Serve your repositories over MCP

One server, many repositories — local and remote — instead of one MCP per project.

repopack repo add .                                  # a local repository
repopack repo add https://github.com/user/project    # or a remote (shallow clone, cached)
repopack index                                       # ingest; the second run is a no-op
repopack serve                                       # MCP at http://127.0.0.1:7777/mcp

Running repopack serve inside a git repository registers it automatically (--no-auto-register opts out).

Tools exposed

Tool What it does
search_context(query, repo?, budget_tokens?) Finds snippets across every repository (or one, via repo). Each snippet comes back with its repository, path:lines and how old that index is. Respects a token ceiling (default 4000) and reports how many relevant snippets did not fit.
get_file(repo, path, start?, end?) Reads a file, optionally a line range.
get_tree(repo?) Known paths.
list_repos() What is being served, when each was indexed, and the last refresh error.
repo_stats() Repositories, files, chunks, symbols, and how many are failing to refresh.

Protocol: revision 2026-07-28 (stateless), with a compatibility shim for clients from the initialize era (2025-03-26 … 2025-11-25).

Automatic refresh

Remotes are refreshed in the background (--refresh-interval, default 900s, jittered; --no-auto-refresh opts out). When a fetch fails the server keeps serving, and the failure shows up in list_repos and repo_stats — not only in the log. Stale context served as if it were fresh is the failure mode that visibility exists to prevent.

Security

  • Binds to 127.0.0.1 by default. Leaving loopback (--host 0.0.0.0) requires a bearer token: if you do not pass one, the server generates it and prints it once. There is no "network without authentication" mode — the index holds the source code of every repository in the workspace.
  • The Origin header is validated on every request (403 when invalid), against DNS rebinding.
  • Redaction happens at ingest, not on the way out: what is secret never enters the database, so there is no path for it to leave. .env and its variants stay out by default (--include-env opts in, and still redacts).
  • get_file never serves anything outside the repository root — the same guard unpack uses.
  • Credentials for private repositories come from your environment (ssh-agent, git helper); repopack stores no token and never prompts for a password (GIT_TERMINAL_PROMPT=0).
  • Secrets are redacted by pattern (API keys, GitHub/Slack tokens, JWT, private keys, .env variables, URLs with credentials) plus a Shannon-entropy sweep.
  • unpack audits each restored file against the sha256 the pack carries, and warns on mismatch. The check is conclusive only for content that is intact by definition — complete, not truncated, not redacted — because a redacted file diverges from its hash by construction.

Redaction is a net, not a guarantee: it covers variable names containing SECRET/TOKEN/KEY/PASSWORD and known key formats, so a line like MY_PLAIN=value123 gets through. That is why the default for .env is to not pack the content at all, rather than trusting the filter.

What this is not

  • unpack is not a backup tool. It restores only content that is intact, refuses degraded content by default (naming each file and why), and never overwrites an existing file unless you pass --force. A pack is a context artifact, not a copy of your repository.
  • skeleton mode is lossy by design. It keeps imports, signatures and docstrings and drops function bodies. Restoring a skeleton over a real file destroys the original and gives back something that will not even parse — which is why it takes --force and says so.
  • Skeleton extraction is regex, not a real AST. It is good enough to keep an interface readable and wrong often enough that you should not treat it as a parser. Real AST extraction via tree-sitter is on the roadmap, behind the [ast] extra.

Honest limitations

  • Search is lexical (BM25), with no embeddings. It is strong with identifiers (_enforce_ceiling comes back first) and weaker with conceptual questions — "where is the budget enforced?" may return the test file before the implementation. The architecture already fuses rankings with RRF so a graph ranker and an optional dense one can plug in, but they do not exist yet.
  • Cross-repo search gets noisy with many repositories and a generic query. What holds today is the token ceiling and per-repo attribution; diversification (MMR) is future work.
  • IDF is measured across the whole workspace. A term that is common in one project and rare in the others weighs differently than it would in an isolated search.
  • The local gate (scripts/check.sh) runs on the Python versions installed on your machine, which is usually fewer than the matrix. The full 3.9–3.14 matrix is exercised by CI on every push; the three lists (classifiers, CI matrix, local gate) are cross-checked by the test suite, so they cannot drift apart in silence.

The index — and the --delta state — live in ~/.cache/repopack/ (honors XDG_CACHE_HOME; REPOPACK_HOME overrides), never inside the repositories you scan. A --delta state left in a repository root by an older version is still read, so your delta survives the upgrade; it is not deleted, because removing a file from someone's repository is more invasive than having created it.

Configuration

Create a .repopackrc (JSON) at the repository root — see .repopackrc.example. Precedence: defaults < .repopackrc < CLI flags.

Roadmap

  • Real AST extraction via tree-sitter ([ast] extra)
  • Dependency-graph ranking (PageRank/PPR over imports and git co-change), entering through the RRF fusion the search already uses
  • Diversification (MMR) and near-duplicate suppression across repositories
  • Optional dense ranker behind a [rag] extra, for conceptual queries
  • Task-conditioned packing (--task)

Contributing

See CONTRIBUTING.md. The one rule that is not negotiable: zero runtime dependencies.

License

MIT — see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选