{"id":"52dc9b85-6843-47d7-9ab6-347b63ea56c9","slug":"climateclaw","kind":"mcp","name":"climateclaw","summary":"","owner":"freva-org","repoUrl":"https://github.com/freva-org/climateclaw","license":"AGPL-3.0","licenseTier":"strong-copyleft","trust":"community","stars":4,"match":null,"fewStars":true,"format":"mcp","createdAt":"2025-09-05T11:00:38.000Z","addedAt":"2026-10-21T15:52:02.395Z","logoUrl":"https://avatars.githubusercontent.com/u/218064798?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["utilities"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-21T15:53:32.563Z","sample":false,"url":"https://conduits.dev/tools/climateclaw","language":"Python","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# ClimateClaw\r\nClimateClaw is a Python service for building AI-assisted climate-data workflows. It provides the API, conversation handling, model prompting, persistent thread storage, and tool orchestration needed to support interactive work with climate data.\r\n\r\nThe project integrates LiteLLM-native prompting, MongoDB-backed conversation state, and MCP-based tool execution for retrieval, code execution, and domain-specific automation.\r\n\r\n## Highlights\r\n- FastAPI app with strict auth parity to the production Rust service (`/api/chatbot/*`)\r\n- Streaming responses via LiteLLM/OpenAI-compatible SSE (`application/x-ndjson`) with code + image variants\r\n- Persistent conversation threads in MongoDB and JSONL files (`threads/`), plus per-user scratch space (`cache/`)\r\n- MCP manager that wires the backend to dedicated tool servers\r\n- Docker compose stack that includes LiteLLM, the backend, and both MCP servers\r\n- Comprehensive pytest suite covering auth, prompting, storage, litellm client helpers, and route matrices\r\n- Web-search MCP server for ICON model + DKRZ/HPC docs with cancellable OpenAI Web Search calls\r\n\r\n## Quick Start (deployment)\r\n\r\n### Requirements\r\n- `podman` or `docker`\r\n- Credentials & headers for the Freva auth services\r\n\r\n### Configure environment\r\nCreate `.env` (used by FastAPI, Docker, and MCP servers). See `.env.example` for guidance.\r\n\r\n### Full stack via Docker Compose\r\n```bash\r\n./prod.sh up -d --build\r\n```\r\nServices that start:\r\n- `climateclaw`: FastAPI app (debugpy toggle via `DEBUG=true` for remote debugging session)\r\n- `code-server`: MCP server running the sandboxed Jupyter kernel and exposing `code_interpreter`\r\n- `web-search-server`: MCP server doing web search via OpenAI API and exposing `web_search`\r\n- `rag-server`: MCP server exposing `get_context_from_resources`\r\n- `litellm`: LiteLLM proxy that reads `litellm_config.yaml`\r\n\r\nBind mounts expose `/work`, logs, threads, and shared `cache` to other Freva services.\r\n\r\n### vLLM inference with Apptainer\r\n\r\nThe Docker Compose stack delegates local-model inference to an OpenAI-compatible\r\nvLLM endpoint. To run that endpoint on a GPU host with Apptainer, copy and\r\nconfigure the example environment file:\r\n\r\n```bash\r\ncp apptainer/.env.example apptainer/.env\r\n```\r\n\r\nSet `APPTAINER_BASE_DIR`, `VLLM_OCI_IMAGE`, `VLLM_MODEL`, GPU and serving\r\nsettings, and `VLLM_API_KEY` in `apptainer/.env`. Then pull the configured\r\nimage and start vLLM:\r\n\r\n```bash\r\n./apptainer/vllm/run.sh pull\r\n./apptainer/vllm/run.sh up\r\n```\r\n\r\nUse `./apptainer/vllm/run.sh status`, `logs`, and `down` to manage the service.\r\nThe runner uses `--nv`, persists Apptainer, Hugging Face, and vLLM caches below\r\n`APPTAINER_BASE_DIR`, and supports additional vLLM options through\r\n`EXTRA_VLLM_ARGS`.\r\n\r\nConfigure the main `.env` so LiteLLM can reach the service:\r\n\r\n```bash\r\nCLIMATECLAW_VLLM_MODEL_ID=\"<model-id>\"\r\nCLIMATECLAW_VLLM_API_BASE=\"http://<vllm-host>:8000/v1\"\r\nCLIMATECLAW_VLLM_API_KEY=\"<vllm-api-key>\"\r\n```\r\n\r\n`<vllm-host>` must be reachable from the LiteLLM container. This separation\r\nalso lets future deployments replace the locally managed Apptainer service with\r\ndedicated LLM-inference infrastructure without changing ClimateClaw's client\r\ninterface.\r\n\r\n## Quick Start (local dev)\r\n\r\n### Requirements\r\n- `podman` or `docker`\r\n\r\n### Configure environment\r\nCreate `.env` (used by FastAPI, Docker, and MCP servers). See `.env.example` for guidance.\r\n\r\n### Start docker containers in DEV mode\r\n```bash\r\n./dev.sh up -d --build\r\n```\r\n\r\n## Repository Layout\r\n| Path | Purpose |\r\n| --- | --- |\r\n| `src/climateclaw/app.py` | FastAPI entrypoint, CORS policy, router registration, app lifespan hooks |\r\n| `src/climateclaw/api/chatbot/*` | HTTP handlers for chat operations (`availablechatbots`, `streamresponse`, `getthread`, etc.) |\r\n| `src/climateclaw/services/streaming/` | LiteLLM client, orchestrator, stream variant definitions, heartbeat helpers |\r\n| `src/climateclaw/services/storage/` | MongoDB + disk-backed persistence (`threads/` JSONL, `cache/` scratch space) |\r\n| `src/climateclaw/services/mcp/` | MCP manager and MCP client |\r\n| `src/climateclaw/services/authentication/` | Authentication: DEV mode auth surpassing OIDC requirements |\r\n| `src/climateclaw/core/` | Settings, prompt assembly, logging, startup checks, available-model parsing |\r\n| `src/climateclaw/tools/` | MCP servers, auth helpers, header gate middleware |\r\n| `prompt_library/` | Baseline system prompts, summary prompts, and few-shot examples (JSONL) |\r\n| `resources/` | Documentation corpora used by the RAG tool (`stableclimgen` seed content) |\r\n| `docker/` | Dockerfiles for base, climateclaw and MCP servers |\r\n| `scripts/` | Dev utilities (`dev_chat.py`, `dev_script.py`, `check_kernel_env.py`) |\r\n| `tests/` | Pytest suite covering auth, prompting, streaming, storage, and endpoints |\r\n| `litellm_config.yaml` | Source of truth for model catalog (consumed by `available_chatbots()`) |\r\n\r\nGenerated artifacts that persist across runs:\r\n- `threads/` (JSONL transcript per thread id)\r\n- `cache/{user_id}/{thread_id}` (LLM-created files, plots, etc.)\r\n- `logs/` (when mounted in Docker)\r\n\r\n## Architecture at a Glance\r\n1. **FastAPI layer** enforces auth via `AuthRequired` (Bearer tokens validated against `x-freva-rest-url`), derives stable UUIDv5 pseudonymous user IDs from usernames, and validates per-request headers.\r\n2. **LiteLLM proxy** (`CLIMATECLAW_LITE_LLM_ADDRESS`) provides OpenAI-compatible chat + embeddings endpoints; completions stream into `StreamVariant` classes that normalize assistant text, code blocks, tool hints, images, and server hints.\r\n3. **Persistence** uses MongoDB for storing threads and user feedback.\r\n4. **MCP Manager** (`src/climateclaw/services/mcp/mcp_manager.py`) connects to tool servers listed in `CLIMATECLAW_AVAILABLE_MCP_SERVERS`, discovers tools, exposes OpenAI function schemas to LiteLLM, and routes tool invocations with per-thread session ids.\r\n5. **MCP servers** run as separate ASGI apps (dockerized). Requests flow through `header_gate` so required headers (`mongodb-uri`, `working-dir`) become ContextVars before code executes.\r\n6. **Prompting** loads baseline templates + few-shot examples per model and replays thread history (minus prompts, meta) to LiteLLM, matching the Rust semantics.\r\n\r\n## API Surface\r\n\r\n| Method | Path | Description | Notes |\r\n| --- | --- | --- | --- |\r\n| `GET` | `/api/chatbot/ping` | Static ping stub | Placeholder |\r\n| `GET` | `/api/chatbot/docs` | Docs payload stub | Placeholder |\r\n| `GET` | `/api/chatbot/help` | Help payload stub | Placeholder |\r\n| `GET` | `/api/chatbot/availablechatbots` | Returns model names from `litellm_config.yaml` | Requires auth |\r\n| `GET` | `/api/chatbot/newthread` | Generates a fresh `thread_id` | Requires auth |\r\n| `POST` | `/api/chatbot/getthread` | Fetches thread contents omitting prompts + redundant StreamEnd variants | Requires auth |\r\n| `POST` | `/api/chatbot/getuserthreads` | Returns recent threads for authenticated user | JSON body: `num_threads`, `page` |\r\n| `POST` | `/api/chatbot/streamresponse` | Starts an SSE stream of `StreamVariant` JSON payloads | Query params: `thread_id`, `input` (required), `chatbot` |\r\n| `POST` | `/api/chatbot/stop` | Initiates stopping of an active conversation | JSON body: `thread_id`; requires auth |\r\n\r\n### Streaming contract\r\n- Response type: `application/x-ndjson`\r\n- Each `data:` line is a JSON object with `variant` discriminators (`Assistant`, `Code`, `CodeOutput`, `CodeError`, `Image`, `ServerHint`, `StreamEnd`, etc.).\r\n- Code tool calls stream incremental chunks while LiteLLM emits `tool_calls`. When the MCP tool resolves, results are converted back into JSON events and appended to Mongo/disk storage.\r\n- The first chunk is a `ServerHint` carrying the `thread_id`; conversation variants are stored in-memory during streaming and flushed to MongoDB at the end, ensuring replay safety.\r\n- Clients can call `/api/chatbot/stop?thread_id=...` to move a conversation into `STOPPING`; the streaming loop exits and cancels in-flight MCP requests (code, rag, web-search) via the shared `ActiveRequest` registry.\r\n\r\n## Persistence, Prompts, and Assets\r\n- **MongoDB (`mongodb_storage.py`)**: canonical record for threads. Each document stores a UUIDv5 pseudonymous `user_id`, `thread_id`, ISO timestamp, topic (summarized via LiteLLM), and serialized `StreamVariant` list.\r\n- **`cache/` scratch**: `create_dir_at_cache()` ensures each user/thread has a writable directory for generated files (plots, CSVs). Entries are sanitized if user IDs contain unsupported characters.\r\n- **Prompt library**: `prompt_library/baseline` contains `starting_prompt.txt`, `summary_prompt.txt`, and `examples.jsonl`. GPT-5 models currently fall back to baseline prompts (warning logged). Customize by adding new prompt sets and updating `_resolve_baseline_dir()` / `_resolve_gpt5_dir_or_placeholder()`.\r\n- **Resources**: `resources/stableclimgen` seeds the RAG MCP server. Drop additional corpora per library folder and list them in `CLIMATECLAW_AVAILABLE_LIBRARIES` inside `src/climateclaw/tools/rag/server.py`.\r\n\r\n## MCP Tooling\r\n- **Code interpreter** (`src/climateclaw/tools/code/server.py`): spins up per-session Jupyter kernels, sanitizes input, enforces configurable timeouts, and injects Freva config via environment variables. Outputs include stdout/stderr, display data, and structured errors.\r\n- **Web search server** (`src/climateclaw/tools/web_search/server.py`): calls OpenAI Web Search (`gpt-4.1`) constrained to ICON model + DKRZ/HPC docs. Honors request cancellation.\r\n- **RAG server** (`src/climateclaw/tools/rag/server.py`): indexes documentation with custom loaders + splitters, stores embeddings in MongoDB (`embeddings`), and surfaces a single tool `get_context_from_resources`. LiteLLM requests embed queries through the same proxy (`CLIMATECLAW_LITE_LLM_ADDRESS`).\r\n- **Header gate** (`src/climateclaw/tools/header_gate.py`): wraps each MCP ASGI app so critical headers become ContextVars and requests fail fast when missing/invalid (e.g., missing Mongo URI yields SSE-friendly JSON-RPC errors).\r\n- **Manager** (`src/climateclaw/services/mcp/mcp_manager.py`): caches clients, discovers tool schemas, exports OpenAI function definitions, and pins MCP session ids to thread ids for deterministic tool contexts.\r\n\r\n## Development Workflow\r\n- **Spin up dev stack**: `./dev.sh up -d --build` (FastAPI, rag, code, web-search, LiteLLM). Use `./dev.sh up --build` to tail the app. Local Ollama models require an Ollama server running on the host at port `11434` (for example, `ollama serve`).\n- **Unit/functional tests**: `uv run pytest` or focus, e.g. `uv run pytest tests/test_auth.py -k bearer`.\r\n- **Integration: code interpreter**: `CLIMATECLAW_CODE_SERVER_URL=http://localhost:8051 uv run pytest tests/full_integration_tests/test_code_interpreter.py -m integration`.\r\n- **Integration: web-search**: `CLIMATECLAW_WEB_SEARCH_SERVER_URL=http://localhost:8052 uv run pytest tests/full_integration_tests/test_web_search.py -m integration`.\r\n- **Interactive chat**: `uv run python scripts/dev_chat.py` starts a REPL that exercises the same orchestrator logic, persisting outputs to disk and optionally pointing at local MCP servers.\r\n\r\n## Scaling & HAProxy\r\n- **Prod scaling**: `./prod.sh up -d --build` generates `docker-compose.scaled.yml` + `haproxy.cfg` via `gen_compose.py`, then launches HAProxy in front of replicas.\r\n- **Dev scaling**: `./dev.sh --scale up -d` produces `docker-compose.dev.scaled.yml` and matching HAProxy config.\r\n- **Replica knobs**: set `CLIMATECLAW_BACKEND_REPLICAS`, `CLIMATECLAW_LITELLM_REPLICAS`, and `CLIMATECLAW_{RAG|CODE|WEB_SEARCH}_REPLICAS` (default 1). Only MCP servers listed in `CLIMATECLAW_AVAILABLE_MCP_SERVERS` are scaled.\r\n- **Sticky routing**: HAProxy pins backend and MCP tool traffic by `hdr(X-Freva-Thread-Id)`; LiteLLM stays round-robin.\r\n- **Ports**: HAProxy binds `CLIMATECLAW_TARGET_PORT` for the backend and `4000` for litellm; MCP frontends bind their configured ports (e.g., 8050/8051/8052) while container instances stay internal.\r\n\r\n## Troubleshooting\r\n- **Auth failures**: verify headers include both `Authorization` and `x-freva-rest-url`. Inspect FastAPI logs for the exact HTTP status.\r\n- **Missing models**: ensure `litellm_config.yaml` is readable and contains `model_name` keys. `available_chatbots()` aborts the process if it cannot find any entries.\r\n- **MCP issues**: backend logs warn but continue when tool discovery fails; LiteLLM will simply not emit tool calls. Use `settings.AVAILABLE_MCP_SERVERS` to enable/disable targets explicitly.\r\n- **File access**: Make sure `/work` is mounted read-only where expected.\r\n\r\n## License\r\n\r\nCopyright (C) 2025, freva-org\r\n\r\nThis project is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.\r\n\r\nSee the [LICENSE](./LICENSE) file for details.\r\n"}