langchain-docs-local-mcp
by xmkoh
README.md
# langchain-docs-local-mcp
An offline MCP server that provides LangChain and LangGraph documentation search — a drop-in replacement for the cloud-hosted `mcp__langchain-docs__` server for airgapped environments.
## Why
In airgapped environments the cloud MCP server for LangChain docs is unreachable. This project bundles the documentation locally and serves it via a local MCP server using BM25 full-text search — no network access, no ML model downloads, no Docker required at runtime.
## Tools exposed
| Tool | Description |
|------|-------------|
| `search_docs_by_lang_chain` | BM25 search across bundled LangChain/LangGraph docs |
| `query_docs_filesystem_docs_by_lang_chain` | Shell-like access to doc files (`cat`, `head`, `ls`, `find`, `rg`) |
These names match the original cloud MCP server exactly for drop-in compatibility.
---
## Local quick start (stdio)
```bash
# 1. Clone and install
git clone https://github.com/xmkoh/langchain-docs-local-mcp
cd langchain-docs-local-mcp
pip install -r requirements.txt
# 2. Build the search index from the bundled docs
python scripts/build_index.py
# 3. Test
pip install pytest
pytest tests/
# 4. Wire up Claude Code — edit the path, then restart Claude Code
cp .claude/settings.json /path/to/your/project/.claude/settings.json
```
`.claude/settings.json` (stdio):
```json
{
"mcpServers": {
"langchain-docs": {
"type": "stdio",
"command": "python",
"args": ["server.py"],
"cwd": "/absolute/path/to/langchain-docs-local-mcp"
}
}
}
```
The tools appear in Claude Code as:
- `mcp__langchain-docs__search_docs_by_lang_chain`
- `mcp__langchain-docs__query_docs_filesystem_docs_by_lang_chain`
---
## Kubernetes / OpenShift deployment
### 1. Build and push the image
The image bakes the docs and pre-built index in at build time — no volumes or init containers needed.
```bash
docker build -t registry.example.com/langchain-docs-local-mcp:0.1.0 .
docker push registry.example.com/langchain-docs-local-mcp:0.1.0
```
The image defaults to **SSE transport** on port 8000. Switch to streamable-http via the `MCP_TRANSPORT` env var or `--transport` flag.
### 2. Deploy with Helm
```bash
# OpenShift — with a public Route (TLS edge termination)
helm install langchain-docs ./helm/langchain-docs-mcp \
--namespace my-namespace \
--set image.repository=registry.example.com/langchain-docs-local-mcp \
--set image.tag=0.1.0 \
--set route.enabled=true
# Plain Kubernetes — with an Ingress instead
helm install langchain-docs ./helm/langchain-docs-mcp \
--namespace my-namespace \
--set image.repository=registry.example.com/langchain-docs-local-mcp \
--set image.tag=0.1.0 \
--set ingress.enabled=true \
--set ingress.hosts[0].host=langchain-docs-mcp.example.com \
--set ingress.hosts[0].paths[0].path=/ \
--set ingress.hosts[0].paths[0].pathType=Prefix
```
After install, Helm prints the Claude Code config snippet automatically (via `NOTES.txt`).
### 3. Get the Route URL (OpenShift)
```bash
oc get route langchain-docs-langchain-docs-mcp -n my-namespace \
-o jsonpath='{.spec.host}'
```
### 4. Wire up Claude Code (network transport)
**SSE** (default, `mcpTransport: sse`):
```json
{
"mcpServers": {
"langchain-docs": {
"type": "sse",
"url": "https://<ROUTE_HOST>/sse"
}
}
}
```
**Streamable HTTP** (`--set mcpTransport=streamable-http`):
```json
{
"mcpServers": {
"langchain-docs": {
"type": "http",
"url": "https://<ROUTE_HOST>/mcp"
}
}
}
```
**Port-forward** (no Route/Ingress, in-cluster access only):
```bash
kubectl port-forward svc/langchain-docs-langchain-docs-mcp 8000:8000 -n my-namespace
```
```json
{
"mcpServers": {
"langchain-docs": {
"type": "sse",
"url": "http://localhost:8000/sse"
}
}
}
```
### OpenShift security notes
The image is built to work with the default **restricted** SCC — no `anyuid` or elevated permissions required:
- Files are owned `1001:0` with `chmod g=u` so OpenShift's arbitrary UID (always GID 0) can read them
- `runAsNonRoot: true`, `allowPrivilegeEscalation: false`, all capabilities dropped
- `readOnlyRootFilesystem: true` with an `/tmp` emptyDir for Python's tempfile
### Key Helm values
| Value | Default | Description |
|-------|---------|-------------|
| `image.repository` | `langchain-docs-local-mcp` | Image to deploy |
| `image.tag` | chart `appVersion` | Image tag |
| `mcpTransport` | `sse` | `sse` or `streamable-http` |
| `route.enabled` | `false` | Create an OpenShift Route |
| `route.tls.enabled` | `true` | TLS on the Route |
| `route.tls.termination` | `edge` | `edge`, `passthrough`, or `reencrypt` |
| `ingress.enabled` | `false` | Create a Kubernetes Ingress |
| `autoscaling.enabled` | `false` | Enable HorizontalPodAutoscaler |
| `replicaCount` | `1` | Number of replicas |
---
## Updating the documentation
Documentation is bundled at image build time. To update after an upstream LangChain/LangGraph release:
```bash
# On a machine with internet access:
pip install httpx
python scripts/fetch_docs.py # re-downloads from GitHub
python scripts/build_index.py # rebuilds index/bm25_index.pkl
git add docs/ index/
git commit -m "Update docs"
git push
# Then rebuild and push a new image:
docker build -t registry.example.com/langchain-docs-local-mcp:0.2.0 .
docker push registry.example.com/langchain-docs-local-mcp:0.2.0
helm upgrade langchain-docs ./helm/langchain-docs-mcp --set image.tag=0.2.0
```
`fetch_docs.py` prints a warning for any URL that fails — this happens when LangChain reorganizes their docs. Update the `FETCH_TARGETS` list in `scripts/fetch_docs.py` with the new GitHub raw URL.
## Adding your own documentation
Drop any `.md` file anywhere under `docs/` and rebuild the index and image:
```bash
python scripts/build_index.py
docker build -t registry.example.com/langchain-docs-local-mcp:custom .
```
## Offline pip installs (fully airgapped)
```bash
# On a networked machine — bundle wheels alongside the repo:
pip download -r requirements.txt -d wheels/
git add wheels/ && git commit -m "Add offline wheels"
# On the airgapped machine:
pip install --no-index --find-links wheels/ -r requirements.txt
```
## Documentation included
The repository ships with hand-curated documentation for:
- **LangGraph**: `BaseStore`, `InMemoryStore`, `MemorySaver`, `StateGraph`, checkpointers, persistence, human-in-the-loop
- **Deep Agents**: architecture overview, memory patterns (short-term vs long-term), namespace conventions
- **LangChain**: `@tool`, `StructuredTool`, `BaseTool`, `create_react_agent`, `AgentExecutor`, streaming
See [`docs/README.md`](docs/README.md) for full coverage.
## Architecture
```
server.py FastMCP server — stdio, SSE, or streamable-http transport
search.py BM25 search engine (rank_bm25), loaded once at startup
Dockerfile Builds a self-contained image with docs + index baked in
helm/
langchain-docs-mcp/ Helm chart for Kubernetes / OpenShift
templates/
deployment.yaml Pod with restricted SCC security context
service.yaml ClusterIP on port 8000
route.yaml OpenShift Route (route.enabled=true)
ingress.yaml Kubernetes Ingress (ingress.enabled=true)
hpa.yaml HorizontalPodAutoscaler (autoscaling.enabled=true)
docs/ Bundled markdown documentation
index/
bm25_index.pkl Pre-built search index (committed to git)
scripts/
build_index.py Rebuild the index from docs/
fetch_docs.py Download docs from GitHub (run once, online)
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing