Conduit
README.md
# Conduit
An MCP (Model Context Protocol) server that exposes real GitHub
repository and issue data to any MCP client — Claude Desktop, or any
other tool that speaks the protocol — through five standardized tools
instead of a one-off integration per client.
## Why MCP instead of a custom API wrapper
Without MCP, "let an LLM query GitHub" means writing a bespoke
integration for every client that wants it. MCP standardizes that: build
the server once, and Claude Desktop, a custom agent, or any other
MCP-speaking client can call it the same way. This server is the data
side of that — read-only access to public repos and issues, with a
caching layer in front of it.
## Tools exposed
| Tool | What it does |
|---|---|
| `list_repos(username, limit)` | A user's public repos, most recently updated first |
| `get_repo(owner, repo)` | Description, language, stars, open issue count |
| `list_issues(owner, repo, state, limit)` | Issues only — pull requests filtered out |
| `get_issue(owner, repo, number)` | Full issue detail, including body text |
| `get_readme(owner, repo)` | Raw README content |
Every tool response is slimmed down to the fields that matter
(`conduit/github_client.py`) — GitHub's raw API payloads run 60+ keys
deep, and handing an LLM all of it burns context for no reason.
## Caching, and why it's not optional here
GitHub's unauthenticated REST API allows 60 requests/hour per IP. An
LLM calling tools in a loop can burn through that in minutes. Every tool
response is cached in SQLite (`conduit/cache.py`) with a configurable
TTL (`CACHE_TTL_SECONDS`, default 300s) — a real engineering constraint
this project actually has to handle, not a hypothetical one.
## Two real bugs this caught while being built
1. **A run stuck silently on a closed event loop.** The first working
version created its `httpx.AsyncClient` once at import time and
reused it for every tool call. On Windows, that client's connection
pool would occasionally try to close a pooled connection whose
cleanup got scheduled against an event loop that had already moved
on — `RuntimeError: Event loop is closed`, with no indication of why.
Fixed two ways: the client is now built lazily on first real use
instead of at import time, and connection keep-alive pooling is
disabled (`httpx.Limits(max_keepalive_connections=0)`) so there's no
pooled connection left around to race on.
2. **Tests failing on GitHub's rate limit looked like code failures.**
Unauthenticated testing hit the 60/hour cap partway through
development, and those failures were indistinguishable from real bugs
in the output. `tests/conftest.py` now catches a 403 rate-limit
response and skips with a clear reason instead of failing red for a
constraint that has nothing to do with correctness.
## Using it with Claude Desktop
Add to your Claude Desktop MCP config (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"conduit": {
"command": "python",
"args": ["-m", "conduit"],
"cwd": "/path/to/conduit",
"env": { "GITHUB_TOKEN": "your-token-here" }
}
}
}
```
`GITHUB_TOKEN` is optional — public data works without one, just at the
lower rate limit.
## Running it
```
pip install -r requirements.txt
python -m conduit
```
Runs over stdio, which is what Claude Desktop (and most MCP clients)
expect — there's no web dashboard for this one, since an MCP server's
actual interface is the protocol itself, not a UI.
## Running the tests
```
pytest -q
```
12 tests. `test_cache.py` is fully offline. `test_github_client.py` and
`test_server.py` hit the real GitHub API on purpose — a mocked response
would never have caught either bug above — and skip cleanly if the rate
limit is already exhausted rather than failing.
## Known limitations
- Read-only by design. A version that could open issues or comment would
be a legitimately different, higher-risk project — this one doesn't
attempt it.
- No auth beyond an optional personal access token — fine for public
repos, not built for private repo access control.
- The SQLite cache is unbounded — nothing evicts old entries beyond TTL
expiry. Fine at the scale one developer's tool calls generate; not
designed for high-volume multi-user deployment.
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues