Browser-based chatbot interface for DERIVA. Wraps an LLM and the deriva-mcp-core MCP server behind a web frontend, giving end users a natural-language interface to query and manage DERIVA catalogs without needing a desktop MCP client.
- Standard web login via Credenza (OAuth 2.0 + PKCE -- no token pasting)
- Streaming responses via Server-Sent Events; press ESC to stop generation mid-stream
- Full tool-calling loop: the LLM invokes DERIVA tools transparently; tool calls are shown in collapsible blocks in the UI
- Three operating tiers (auto-detected from config, or forced via
DERIVA_CHATBOT_MODE):- LLM -- full tool-calling loop via any LiteLLM-supported provider (Anthropic, OpenAI, Ollama, Azure, etc.)
- RAG-only -- no API key required; answers directly from indexed documentation and catalog data via the MCP server's RAG subsystem
- Local model -- full tool-calling via a local Ollama instance
- Two catalog modes:
- Default-catalog -- anchored to a specific catalog; schema context injected automatically on the first turn
- General-purpose -- user or LLM specifies hostname and catalog ID
- Conversation history preserved server-side across the browser session and across Credenza token re-authentication
- Multiple session storage backends: memory (default), Redis/Valkey, PostgreSQL, SQLite
- Centered chat column (65 rem) with right-aligned user messages -- layout consistent with modern chat UIs; user message alignment is configurable
- Inline hyperlinks rendered as clickable numbered reference bubbles with a collapsed "References" section after each block -- no raw blue hyperlinks in responses
- Auto-resizing message input; no Send button (Enter to send, ESC to stop)
- Montserrat font, 1.6 line-height
- Fully configurable branding: header color, input area color, chat background, response card style, and logo URL
- Python 3.11+
- A running deriva-mcp-core instance reachable over HTTP
- A Credenza instance for authentication (optional -- omit for anonymous/public deployments)
- An LLM API key (Anthropic, OpenAI, etc.) or a local Ollama (or similar) instance -- not required for RAG-only mode
pip install deriva-mcp-ui
# or with uv:
uv add deriva-mcp-uiWith optional backends:
pip install "deriva-mcp-ui[redis]" # Redis / Valkey
pip install "deriva-mcp-ui[sqlite]" # SQLite
pip install "deriva-mcp-ui[postgresql]" # PostgreSQLAll configuration is via environment variables with the DERIVA_CHATBOT_ prefix.
| Variable | Description |
|---|---|
DERIVA_CHATBOT_MCP_URL |
Base URL of the deriva-mcp-core server |
The following are required only when Credenza authentication is enabled
(i.e., when DERIVA_CHATBOT_CREDENZA_URL is set):
| Variable | Description |
|---|---|
DERIVA_CHATBOT_CREDENZA_URL |
Base URL of the Credenza instance |
DERIVA_CHATBOT_CLIENT_ID |
OAuth client ID registered in Credenza |
DERIVA_CHATBOT_MCP_RESOURCE |
Resource URI for the MCP server (must match MCP server config) |
DERIVA_CHATBOT_PUBLIC_URL |
Public HTTPS URL of this service (used as OAuth redirect base) |
| Variable | Default | Description |
|---|---|---|
DERIVA_CHATBOT_LLM_API_KEY |
API key for the configured provider (Anthropic, OpenAI, etc.) | |
DERIVA_CHATBOT_LLM_MODEL |
claude-haiku-4-5 |
Model identifier (e.g. claude-haiku-4-5, gpt-4o-mini, ollama/llama3) |
DERIVA_CHATBOT_LLM_PROVIDER |
Provider name; auto-detected from model string if omitted | |
DERIVA_CHATBOT_LLM_API_BASE |
Custom API base URL (required for Ollama and self-hosted endpoints) | |
DERIVA_CHATBOT_MODE |
auto |
auto, llm, or rag_only; forces operating tier |
Set both variables to anchor the chatbot to a specific catalog:
| Variable | Description |
|---|---|
DERIVA_CHATBOT_DEFAULT_HOSTNAME |
DERIVA server hostname |
DERIVA_CHATBOT_DEFAULT_CATALOG_ID |
Catalog ID or alias |
DERIVA_CHATBOT_DEFAULT_CATALOG_LABEL |
Display name shown in the UI header (optional) |
| Variable | Default | Description |
|---|---|---|
DERIVA_CHATBOT_ALLOW_ANONYMOUS |
false |
When true, unauthenticated users get an anonymous session even when Credenza is configured |
DERIVA_CHATBOT_ALLOW_RAG_TOGGLE |
false |
When true, users in LLM/local tier can switch to RAG-only mode per session from the UI |
| Variable | Default | Description |
|---|---|---|
DERIVA_CHATBOT_HEADER_TITLE |
DERIVA Data Assistant |
Text shown in the header bar |
DERIVA_CHATBOT_HEADER_LOGO_URL |
static/deriva-logo.png |
Logo URL; must be static/<filename> or https:// (no other schemes) |
DERIVA_CHATBOT_HEADER_BG_COLOR |
#1e3a5f |
CSS color for the header bar background |
DERIVA_CHATBOT_INPUT_AREA_BG_COLOR |
#1e3a5f |
CSS color for the input area background and textarea border accent |
DERIVA_CHATBOT_CHAT_BG_COLOR |
#f5f5f5 |
CSS color for the chat thread area background |
DERIVA_CHATBOT_CODE_THEME |
vs2015 |
highlight.js theme name for code block syntax highlighting (e.g. github, vs) |
DERIVA_CHATBOT_SHOW_RESPONSE_CARDS |
false |
When true, assistant responses are rendered in styled card bubbles |
DERIVA_CHATBOT_CHAT_ALIGN_LEFT |
false |
When true, user messages are left-aligned (default: right-aligned, matching ChatGPT) |
| Variable | Default | Description |
|---|---|---|
DERIVA_CHATBOT_MAX_HISTORY_TURNS |
10 |
Conversation turns retained per session |
DERIVA_CHATBOT_MAX_MESSAGE_LENGTH |
10000 |
Maximum user message length in characters |
DERIVA_CHATBOT_SESSION_TTL |
28800 |
Server-side session TTL in seconds (default 8h) |
DERIVA_CHATBOT_HISTORY_TTL |
604800 |
History-only TTL in seconds; history survives session expiry (7 days) |
DERIVA_CHATBOT_STORAGE_BACKEND |
memory |
Session backend: memory, redis, valkey, postgresql, sqlite |
DERIVA_CHATBOT_STORAGE_BACKEND_URL |
Connection URL for the selected backend | |
DERIVA_CHATBOT_DEBUG |
false |
Enable debug logging |
| Variable | Default | Description |
|---|---|---|
DERIVA_CHATBOT_HOSTNAME_MAP |
{} |
JSON object mapping public hostnames to internal ones (e.g. {"localhost":"deriva"} for Docker deployments) |
DERIVA_CHATBOT_SSL_VERIFY |
true |
Set to false to disable TLS certificate verification (useful for self-signed certs in dev/staging) |
When the chatbot runs inside Docker, localhost in DERIVA_CHATBOT_MCP_URL and
DERIVA_CHATBOT_CREDENZA_URL resolves to the container itself. Use
DERIVA_CHATBOT_HOSTNAME_MAP to remap the public hostname to the internal Docker
service name:
DERIVA_CHATBOT_HOSTNAME_MAP = {"localhost": "deriva"}
DERIVA_CHATBOT_SSL_VERIFY = falseThis mirrors the DERIVA_MCP_HOSTNAME_MAP / DERIVA_MCP_SSL_VERIFY pattern used by
deriva-mcp-core. In deriva-docker, both variables are populated automatically by
generate-env.sh for localhost deployments.
Storage URL examples:
redis://localhost:6379/0
postgresql://user:pass@host/dbname
sqlite:///path/to/sessions.db
| Variable | Default | Description |
|---|---|---|
DERIVA_CHATBOT_APP_USE_SYSLOG |
false |
Enable syslog (LOCAL1) for app logs -- leave false under Docker |
DERIVA_CHATBOT_ACCESS_LOGFILE_PATH |
deriva-mcp-ui-access.log |
Path for rotating uvicorn access log file |
DERIVA_CHATBOT_ACCESS_USE_SYSLOG |
false |
Enable syslog (LOCAL2) for access logs |
deriva-mcp-uiThe server listens on 0.0.0.0:8001. Set DERIVA_CHATBOT_DEBUG=true for debug logging.
FROM ghcr.io/informatics-isi-edu/deriva-mcp-ui:latestOr build locally:
docker build -t deriva-mcp-ui .Example docker-compose.yml snippet:
deriva-mcp-ui:
image: deriva-mcp-ui:latest
environment:
DERIVA_CHATBOT_MCP_URL: http://deriva-mcp-core:8000
DERIVA_CHATBOT_CREDENZA_URL: https://auth.example.org
DERIVA_CHATBOT_CLIENT_ID: deriva-mcp-ui
DERIVA_CHATBOT_MCP_RESOURCE: https://mcp.example.org
DERIVA_CHATBOT_PUBLIC_URL: https://example.org/chatbot
DERIVA_CHATBOT_LLM_API_KEY: sk-ant-...
DERIVA_CHATBOT_LLM_MODEL: claude-haiku-4-5
DERIVA_CHATBOT_DEFAULT_HOSTNAME: data.example.org
DERIVA_CHATBOT_DEFAULT_CATALOG_ID: "1"
DERIVA_CHATBOT_DEFAULT_CATALOG_LABEL: "Example Catalog"
DERIVA_CHATBOT_HEADER_BG_COLOR: "#1e3a5f"
DERIVA_CHATBOT_STORAGE_BACKEND: redis
DERIVA_CHATBOT_STORAGE_BACKEND_URL: redis://redis:6379/0Add a deriva-mcp-ui service with these labels:
labels:
- "traefik.enable=true"
- "traefik.http.routers.chatbot.rule=PathPrefix(`/chatbot`)"
- "traefik.http.middlewares.chatbot-strip.stripprefix.prefixes=/chatbot"
- "traefik.http.routers.chatbot.middlewares=chatbot-strip"
- "traefik.http.services.chatbot.loadbalancer.server.port=8001"Access the UI at https://your-host/chatbot or https://your-host/chatbot/. A
<base href> tag is injected automatically from DERIVA_CHATBOT_PUBLIC_URL, so
relative asset URLs resolve correctly regardless of whether the trailing slash is
present.
ProxyPass /chatbot/ http://127.0.0.1:8001/
ProxyPassReverse /chatbot/ http://127.0.0.1:8001/Add an entry to config/client_registry.json:
{
"deriva-chatbot": {
"desc": "DERIVA Chatbot (public, PKCE-only)",
"enabled": true,
"public": true,
"allowed_grant_types": [
"authorization_code"
],
"allowed_redirect_uris": [
"https://your-host.example.org/chatbot/callback"
],
"allowed_resources": [
"urn:deriva:rest:service:all",
"https://your-host.example.org/mcp"
],
"allowed_scopes": [
"openid"
],
"require_consent": true,
"consent_display_name": "DERIVA Chatbot Server",
"consent_labels": {
"https://your-host.example.org/mcp": "Connect to the DERIVA MCP Server at this URL"
},
"max_session_ttl_seconds": 28800
}
}Set DERIVA_CHATBOT_MCP_RESOURCE to the resource URI listed in allowed_resources
(e.g. https://your-host.example.org/mcp). Align DERIVA_CHATBOT_SESSION_TTL with
max_session_ttl_seconds. Conversation history is preserved across token expiry and
re-authentication -- users return to their existing conversation after logging in again.
Browser (HTML + JS)
|
| HTTPS (session cookie)
v
deriva-mcp-ui (FastAPI, port 8001)
| |
| MCP over HTTP | HTTPS
| (bearer token) | (LLM API -- optional)
v v
deriva-mcp-core LLM provider (Anthropic / OpenAI / Ollama / ...)
|
| HTTPS
v
DERIVA (ERMrest, Hatrac)
The UI service is a stateless MCP client: each chat turn opens a fresh HTTP connection to the MCP server, runs the LiteLLM tool-calling loop, streams text back via SSE, then closes. No persistent MCP session is maintained.
deriva-mcp-ui/
+-- pyproject.toml
+-- Dockerfile
+-- docs/
| +-- workplan-deriva-mcp-ui.md
+-- src/
+-- deriva_mcp_ui/
+-- __init__.py
+-- server.py # FastAPI app, route registration, lifespan
+-- config.py # Settings (DERIVA_CHATBOT_* env vars, pydantic-settings)
+-- auth.py # Credenza OAuth client: /login, /callback, /logout routes
+-- chat.py # LiteLLM tool-calling loop, RAG-only path, SSE streaming
+-- mcp_client.py # MCP client: connect, list_tools, call_tool, open_session
+-- audit.py # Structured audit event logging
+-- storage/ # Session store backends
| +-- __init__.py # STORAGE_BACKENDS registry + factory
| +-- base.py # SessionStore protocol + Session dataclass
| +-- memory.py
| +-- redis.py
| +-- valkey.py
| +-- postgresql.py
| +-- sqlite.py
+-- static/
+-- index.html # Chat UI shell (branding injected at request time)
+-- chat.js # SSE client, message rendering, link transform, login state
+-- deriva-logo.png
Branding config values are injected into index.html at request time in the GET /
route (server.py). Placeholders such as {{HEADER_BG_COLOR}} are replaced with
html.escape()-sanitized values. This avoids a build step while still supporting
per-deployment customization.
git clone https://github.com/informatics-isi-edu/deriva-mcp-ui
cd deriva-mcp-ui
uv sync --extra dev
uv run pytestLint:
uv run ruff check src testsThe HTTP server exposes a health endpoint at GET /health that returns {"status": "ok"}
with no authentication required. Suitable for Docker health probes and load balancer checks.
curl http://localhost:8001/health
# {"status":"ok"}deriva-mcp-ui is alpha-quality software. The API and configuration surface are
still evolving and breaking changes may occur between releases without advance notice.
It has been validated end-to-end against live DERIVA deployments, but has not yet seen
broad production use. Use in production environments is at your own risk. Bug reports
and contributions are welcome via the
issue tracker.
Apache 2.0. See LICENSE.