SciComp Docs Agent
Officialby AaltoSciComp
README.md
# SciComp Docs Assistant
Search and Q&A over [Aalto scicomp-docs](https://github.com/AaltoSciComp/scicomp-docs) (Triton HPC and related guides).
Two ways to use this project:
| Use case | What you get | Needs LLM API key? |
|----------|--------------|-------------------|
| **[Web chat UI](#quick-start-web-chat-ui)** | Browser chat UI with tool-calling loop | **Yes** (Aalto LLM gateway + VPN) |
| **[MCP server](#quick-start-mcp-only)** | Docs search tool for AI agents (Codex, Cursor, Claude, etc.) | **No** |
Both paths share the same Python search stack in `app/doc_tools.py`. The web chat drives fine-grained tools via an LLM; MCP exposes a single one-shot `search_scicomp_docs` tool.
## Prerequisites
- **Python 3.12+** or **Docker**
- **Docs snapshot** in `docs-source/` (git submodule — required for both paths)
- **ripgrep** optional for local dev (`brew install ripgrep`); included in the Docker image
- **LLM API key** only for the [web chat UI](#quick-start-web-chat-ui)
## Clone
```bash
git clone --recurse-submodules https://github.com/AaltoSciComp/scicomp-docs-assistant.git
cd scicomp-docs-assistant
```
If you already cloned without submodules:
```bash
git submodule update --init --recursive
```
After a healthy clone, `/api/health` should report `docs_present: true` and `index_pages` in the **hundreds** (typically 300+). If `index_pages` is **0**, the docs submodule is missing — see [Troubleshooting](#troubleshooting).
---
## Quick start: Web chat UI
Browser-based agent that searches the local docs snapshot and cites `https://scicomp.aalto.fi/`.
**Requires:** Aalto VPN + API key from [llm-gateway.k8s.aalto.fi](https://llm-gateway.k8s.aalto.fi/).
### Docker (recommended)
```bash
cp .env.example .env
# Edit .env and set LLM_API_KEY=your-key-here
docker compose up --build
```
Open **http://localhost:8080**.
### Local Python
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # set LLM_API_KEY
uvicorn app.main:app --reload --port 8080
```
Open **http://localhost:8080**.
### Using another LLM provider
The gateway client is OpenAI-compatible. Set in `.env`:
```bash
LLM_API_KEY=your-provider-key
LLM_BASE_URL=https://your-provider.example.com/v1
LLM_MODEL=your-model-id
```
The web UI and MCP share the same server process; MCP does not use the LLM.
---
## Quick start: MCP only
Expose a **single** docs search tool to any [MCP](https://modelcontextprotocol.io) client (Codex, Cursor, Claude Desktop, custom agents). No LLM key, no VPN.
The tool runs page ranking, keyword search, and excerpt reading internally.
### Option A — HTTP (Docker, recommended)
```bash
cp .env.mcp.example .env # optional; compose works without .env for MCP-only
docker compose up --build
```
MCP endpoint: **http://localhost:8080/mcp/** (trailing slash required)
Verify:
```bash
curl -s http://localhost:8080/api/health | python3 -m json.tool
# expect: "docs_present": true, "index_pages": 300+ (approx), "llm_configured": false
```
### Option B — HTTP (local Python)
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --port 8080
```
MCP endpoint: **http://localhost:8080/mcp/**
### Option C — stdio (local MCP clients)
From the repo root, with dependencies installed:
```bash
source .venv/bin/activate # after pip install -r requirements.txt
python -m app.mcp_stdio
```
**Cursor / VS Code client config** (use your venv Python path):
```json
{
"mcpServers": {
"scicomp-docs": {
"command": "/path/to/scicomp-docs-assistant/.venv/bin/python",
"args": ["-m", "app.mcp_stdio"],
"cwd": "/path/to/scicomp-docs-assistant",
"env": {
"DOCS_ROOT": "/path/to/scicomp-docs-assistant/docs-source"
}
}
}
}
```
### MCP tool
| Tool | Description |
|------|-------------|
| `search_scicomp_docs` | One-shot search: ranked pages + keyword hits + excerpts with published URLs |
Optional argument `path` limits the search (e.g. `triton/ref`, `aalto`).
Ask the agent in natural language (e.g. “How do I request a GPU on Triton?”); it should call `search_scicomp_docs` once and answer from the result.
### Connect from any MCP client (HTTP)
```json
{
"mcpServers": {
"scicomp-docs": {
"url": "http://localhost:8080/mcp/"
}
}
}
```
Clients that only support stdio (e.g. Claude Desktop) can proxy via [mcp-remote](https://www.npmjs.com/package/mcp-remote):
```json
{
"mcpServers": {
"scicomp-docs": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:8080/mcp/"]
}
}
}
```
### Connect from Python
Install the MCP client SDK on the **machine running your script** (`pip install "mcp>=1.19,<2"`):
```python
import asyncio
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async def main():
async with streamablehttp_client("http://localhost:8080/mcp/") as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("search_scicomp_docs", {"query": "GPU sbatch"})
print(result.content[0].text)
asyncio.run(main())
```
> **Security:** `/mcp/` has no authentication. Bind to localhost or put a reverse proxy in front if exposing beyond your machine.
---
## How it works (web chat)
1. You ask a question in the browser UI.
2. The LLM receives a **search skill** (`skills/scicomp-docs-search.md`).
3. The model calls tools: `find_pages` → `get_doc_outline` / `search_docs` → `read_doc`.
4. It answers with citations as **published URLs** on https://scicomp.aalto.fi/.
## Updating the documentation snapshot
```bash
git submodule update --remote docs-source
docker compose up --build # rebuild image so docs-source is refreshed
```
## Configuration
| Variable | Default | Description |
|----------|---------|-------------|
| `LLM_API_KEY` | (empty) | Required for **web chat** only |
| `LLM_BASE_URL` | `https://llm-gateway.k8s.aalto.fi/api/v1` | OpenAI-compatible base URL |
| `LLM_MODEL` | `RedHatAI/gemma-4-31B-it-FP8-Dynamic` | Model id |
| `LLM_TEMPERATURE` | `0.15` | Sampling temperature |
| `LLM_MAX_TOKENS` | `4096` | Max completion tokens |
| `AGENT_MAX_TOOL_ROUNDS` | `18` | Max tool-calling rounds per chat turn |
| `DOCS_ROOT` | `./docs-source` | Path to scicomp-docs tree |
| `DOCS_SITE_BASE_URL` | `https://scicomp.aalto.fi` | Published site for citations |
| `SKILLS_DIR` | `./skills` | Agent skill markdown files (**web chat** only) |
| `HOST` | `0.0.0.0` | Bind address (local uvicorn) |
| `PORT` | `8080` | Listen port (local uvicorn) |
Env templates:
- `.env.example` — full template (web chat + MCP)
- `.env.mcp.example` — minimal MCP-only (`LLM_API_KEY` empty)
## Troubleshooting
| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| `index_pages: 0` or `docs_present: false` | Submodule not initialized | `git submodule update --init --recursive`, then rebuild/restart |
| MCP client can't connect | Wrong port or missing trailing slash | Use `http://localhost:8080/mcp/` (trailing slash required) |
| MCP works but tools return nothing | Empty `docs-source/` | Same as above; check health endpoint |
| Chat returns **503** “LLM_API_KEY is not configured” | Key not set | Add `LLM_API_KEY` to `.env` (not needed for MCP) |
| Chat errors / timeouts from LLM | VPN off or wrong gateway | Connect to Aalto VPN; verify key at llm-gateway.k8s.aalto.fi |
| `docker compose` fails on `.env` | Old Compose version | Upgrade Docker Desktop, or `cp .env.mcp.example .env` |
| Slow local search | ripgrep not installed | `brew install ripgrep` or use Docker |
| stdio MCP: “docs not found” warning | Wrong `cwd` or `DOCS_ROOT` | Run from repo root; set `DOCS_ROOT` in client config |
| Docker build: can't pull `python` image | Docker Hub unreachable or rate-limited | Use a mirror, e.g. `public.ecr.aws/docker/library/python:3.12-slim-bookworm` |
**Health check:**
```bash
curl -s http://localhost:8080/api/health | python3 -m json.tool
```
Healthy MCP + docs: `docs_present: true`, `index_pages` > 0. Web chat additionally needs `llm_configured: true`.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing