{
  "markdown": "# ChromaDB Remote MCP Server\n\n[![MCP](https://img.shields.io/badge/MCP-Streamable%20HTTP-blue)](https://modelcontextprotocol.io)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![MseeP.ai](https://img.shields.io/badge/MseeP.ai-Audited-4c1)](https://mseep.ai/app/meloncafe-chromadb-remote-mcp)\n[![codecov](https://codecov.io/gh/meloncafe/chromadb-remote-mcp/graph/badge.svg?token=0abUQsve4y)](https://codecov.io/gh/meloncafe/chromadb-remote-mcp)\n[![DeepSource](https://app.deepsource.com/gh/meloncafe/chromadb-remote-mcp.svg/?label=Active+Issues&show_trend=true&token=Mzfb6tMnlzBIxaJO9CsYO3e8)](https://app.deepsource.com/gh/meloncafe/chromadb-remote-mcp/)\n\nA **Streamable HTTP** MCP (Model Context Protocol) server that provides remote access to ChromaDB for AI assistants like Claude. Enables semantic search and vector database operations from mobile devices and remote locations.\n\n> **Note**: This project uses MCP Streamable HTTP (2025-03-26 spec). SSE transport is deprecated.\n\n[한국어 문서](README.ko.md)\n\n---\n\n## Cross-Platform AI Memory Server\n\n**Compatible with ALL major AI platforms:**\n\n- Claude (Desktop, Mobile, Code)\n- Gemini (CLI, Code Assist)\n- Cursor, Cline, Windsurf, VS Code Copilot\n- and use Remote MCP with any other MCP-compatible client\n\n## Features\n\nRemote MCP server that enables all Claude clients (Desktop, Code, Mobile) to access the same self-hosted ChromaDB instance.\n\n- **Shared Memory Across Devices** - All Claude clients use the same ChromaDB instance\n- **Self-Hosted & Private** - Your data stays on your infrastructure\n- **Remote Access** - Connect from anywhere via Tailscale or public internet\n- **Complete ChromaDB Support** - All CRUD operations via MCP tools\n- **REST API Proxy** - Direct ChromaDB access for Python/JavaScript\n- **Unified Authentication** - Single token protects both MCP and REST API endpoints\n- **Easy Deployment** - One-command installation with Docker\n\n---\n\n## Architecture\n\n### Overview\n\n```\n┌──────────────────────────────┐      ┌──────────────┐\n│   Claude Desktop + Mobile    │      │  Claude Code │\n│  (Custom Connector - synced) │      │  (CLI setup) │\n└──────────────┬───────────────┘      └──────┬───────┘\n               │                             │\n               │     MCP Remote Connector    │\n               └─────────────┬───────────────┘\n                             │ HTTPS\n                   ┌─────────▼──────────┐\n                   │   Remote MCP       │\n                   │   Server (Node.js) │\n                   │                    │\n                   │ • Auth Gateway     │\n                   │ • MCP Protocol     │\n                   │ • REST API Proxy   │\n                   └─────────┬──────────┘\n                             │\n                   ┌─────────▼──────────┐\n                   │     ChromaDB       │\n                   │ (Vector Database)  │\n                   │                    │\n                   │ • Embeddings       │\n                   │ • Collections      │\n                   │ • Semantic Search  │\n                   └────────────────────┘\n\n```\n\n**How Clients Connect:**\n\n- **Claude Desktop + Mobile**: Set up once using custom connector in Claude Desktop, and it automatically syncs to the mobile app. Both share the same connection automatically.\n- **Claude Code**: Requires separate setup using `claude mcp add` CLI command.\n\nAll clients access the same self-hosted ChromaDB through this remote MCP server. Vector embeddings and semantic search results persist across all platforms.\n\n### API Endpoints\n\n| Path            | Purpose           | Client                     | Authentication |\n| --------------- | ----------------- | -------------------------- | -------------- |\n| `/mcp`          | MCP Protocol      | Claude Desktop/Code/Mobile | ✅             |\n| `/api/v2/*`     | ChromaDB REST API | Python                     | ✅             |\n| `/docs`         | Swagger UI        | Browser (API docs)         | ✅             |\n| `/openapi.json` | OpenAPI Spec      | API tools                  | ✅             |\n| `/health`       | Health check      | Monitoring                 | ❌             |\n\n### How It Works\n\n1. **Claude Desktop/Mobile**: Add MCP server via custom connector (syncs automatically between devices)\n2. **Claude Code**: Add MCP server using `claude mcp add` CLI command\n3. **Remote MCP Server** authenticates requests and translates MCP protocol to ChromaDB operations\n4. **ChromaDB** stores and retrieves vector embeddings for semantic search\n5. **Python** can also access ChromaDB directly via the proxied REST API\n\n**Benefits:**\n\n- Same vector database across all clients\n- Desktop and mobile share connection automatically\n- Self-hosted and private\n- Persistent memory across app restarts\n- Single source of truth for embeddings\n\n---\n\n## Quick Start\n\n### One-Command Installation\n\n```bash\ncurl -fsSL https://raw.githubusercontent.com/meloncafe/chromadb-remote-mcp/release/scripts/install.sh | bash\n```\n\nThis will:\n\n1. Download `docker-compose.yml` and `.env.example`\n2. Auto-detect Docker Compose command (`docker-compose` or `docker compose`)\n3. Auto-generate a secure authentication token (optional)\n4. Configure ChromaDB data storage location (Docker volume, local directory, or custom path)\n5. Pull Docker images\n6. Display your authentication token and connection URL\n\n### Manual Installation\n\n#### Option 1: Docker (Recommended - Pre-built Image)\n\n```bash\n# Download configuration files\nmkdir chromadb-remote-mcp && cd chromadb-remote-mcp\ncurl -O https://raw.githubusercontent.com/meloncafe/chromadb-remote-mcp/release/docker-compose.yml\ncurl -O https://raw.githubusercontent.com/meloncafe/chromadb-remote-mcp/release/.env.example\n\n# Configure environment\ncp .env.example .env\n# Edit .env and set:\n#   - MCP_AUTH_TOKEN (see token generation below)\n#   - PORT (default: 8080)\n#   - CHROMA_DATA_PATH (default: chroma-data)\n\n# Start services\ndocker compose up -d\n# or: docker-compose up -d (for older versions)\n\n# Check health\ncurl http://localhost:8080/health\n\n# View logs\ndocker compose logs -f\n```\n\n#### Option 2: Build from Source\n\n```bash\n# Clone repository\ngit clone https://github.com/meloncafe/chromadb-remote-mcp.git\ncd chromadb-remote-mcp\n\n# Configure environment\ncp .env.example .env\n# Edit .env with your configuration\n\n# Start with docker-compose (builds image from source)\ndocker compose -f docker-compose.dev.yml up -d\n# or: docker-compose -f docker-compose.dev.yml up -d (for older versions)\n```\n\n#### Option 3: Local Development\n\n```bash\n# Clone and install\ngit clone https://github.com/meloncafe/chromadb-remote-mcp.git\ncd chromadb-remote-mcp\nyarn install\n\n# Configure environment\ncp .env.example .env\n# Edit .env file\n\n# Build and run\nyarn build\nyarn start\n```\n\n### Generate Secure Token\n\nFor production use, generate a secure token for `MCP_AUTH_TOKEN` in `.env`:\n\n```bash\n# Method 1: Node.js (Recommended)\nnode -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"\n\n# Method 2: OpenSSL\nopenssl rand -base64 32 | tr '+/' '-_' | tr -d '='\n```\n\nCopy the generated token and paste it into your `.env` file:\n\n```env\nMCP_AUTH_TOKEN=your-generated-token-here\n```\n\n### Server Endpoints\n\n- MCP: `http://localhost:8080/mcp` (via Caddy proxy)\n- Health: `http://localhost:8080/health`\n- ChromaDB API: `http://localhost:8080/api/v2/*`\n- Swagger UI: `http://localhost:8080/docs`\n\n---\n\n## Configuration\n\n### Environment Variables (.env file)\n\nAll configuration is done through the `.env` file. Copy `.env.example` to `.env` and customize:\n\n```bash\ncp .env.example .env\n```\n\n| Variable            | Description                                                          | Default            | Required                    |\n| ------------------- | -------------------------------------------------------------------- | ------------------ | --------------------------- |\n| `PORT`              | External port (Caddy reverse proxy)                                  | `8080`             | No                          |\n| `CHROMA_DATA_PATH`  | ChromaDB data storage path (volume name, `./data`, or absolute path) | `chroma-data`      | No                          |\n| `CHROMA_HOST`       | ChromaDB host (internal)                                             | `chromadb`         | No                          |\n| `CHROMA_PORT`       | ChromaDB port (internal)                                             | `8000`             | No                          |\n| `CHROMA_TENANT`     | ChromaDB tenant                                                      | `default_tenant`   | No                          |\n| `CHROMA_DATABASE`   | ChromaDB database                                                    | `default_database` | No                          |\n| `MCP_AUTH_TOKEN`    | Authentication token for MCP and REST API                            | -                  | **Yes** (for public access) |\n| `CHROMA_AUTH_TOKEN` | ChromaDB auth token (if ChromaDB requires auth)                      | -                  | No                          |\n| `RATE_LIMIT_MAX`    | Max requests per IP per 15 minutes                                   | `100`              | No                          |\n| `ALLOWED_ORIGINS`   | Comma-separated list of allowed origins (DNS rebinding protection)   | -                  | No                          |\n\n### Authentication\n\n**IMPORTANT:** For public internet access (Tailscale Funnel, Cloudflare Tunnel, etc.), you **must** set `MCP_AUTH_TOKEN` in your `.env` file.\n\nGenerate a secure token:\n\n```bash\n# Method 1: Node.js (Recommended - from .env.example)\nnode -e \"console.log(require('crypto').randomBytes(32).toString('base64url'))\"\n\n# Method 2: OpenSSL\nopenssl rand -base64 32 | tr '+/' '-_' | tr -d '='\n```\n\nEdit your `.env` file:\n\n```env\nMCP_AUTH_TOKEN=your-generated-token-here\n```\n\nThen restart the services:\n\n```bash\ndocker compose restart\n# or: docker-compose restart\n```\n\n**Supported authentication methods (v2.0.0):**\n\n1. **`Authorization: Bearer TOKEN`** — only supported way to send `MCP_AUTH_TOKEN`.\n\n   - Recommended for service-to-service callers (API clients, scripts, MCP relays).\n   - Compliant with MCP specification.\n   - Example: `curl -H \"Authorization: Bearer YOUR_TOKEN\" https://your-server.com/mcp`\n\n2. **OAuth 2.1 / OpenID Connect** — recommended for human users.\n\n   - Set `OIDC_ISSUERS` (comma-separated issuer URLs) or `OIDC_PRESET=google,github,microsoft`.\n   - Set `OIDC_AUDIENCE` to the resource identifier (typically your MCP server's public URL).\n   - The server publishes RFC 9728 Protected Resource Metadata at `/.well-known/oauth-protected-resource`.\n   - 401 responses include `WWW-Authenticate: Bearer error=\"...\", resource_metadata=\"...\"` per RFC 6750.\n\n> **Removed in v2.0.0:** `X-Chroma-Token` header and `?apiKey=` / `?token=` / `?api_key=` query-parameter auth are no longer accepted. Clients that previously used those paths must migrate to `Authorization: Bearer`. The `ALLOW_QUERY_AUTH` env var is ignored.\n\n### Origin Header Validation (DNS Rebinding Protection)\n\nThe server validates the `Origin` header for browser requests to prevent DNS rebinding attacks. This security feature is enabled by default and protects your local MCP server from malicious websites.\n\n**Default allowed origins (always permitted):**\n\n- **Localhost variants**: `localhost`, `127.0.0.1`, `[::1]`\n- **Claude.ai domains**: `https://claude.ai`, `https://api.anthropic.com`\n\n**Configure additional allowed origins:**\n\nIf you need to allow additional web applications or custom domains, add them to `ALLOWED_ORIGINS` in your `.env` file:\n\n```env\n# Add additional custom domains (Claude.ai is already allowed by default)\nALLOWED_ORIGINS=https://myapp.com,https://yourdomain.com\n```\n\n**When to configure ALLOWED_ORIGINS:**\n\n- ✅ Using Claude Desktop Custom Connector → **No configuration needed** (allowed by default)\n- ✅ Accessing from custom web applications → Add your application's domain\n- ✅ Using Swagger UI remotely → Add your server's domain\n- ❌ Using Claude Code CLI → Not needed (no Origin header)\n- ❌ Using Python/JavaScript clients → Not needed (no Origin header)\n- ❌ Local development only → Not needed (localhost is allowed by default)\n\n**Example configurations:**\n\n```env\n# For custom web application\nALLOWED_ORIGINS=https://myapp.com,https://app.mycompany.com\n\n# Multiple custom domains (comma-separated, spaces are trimmed)\nALLOWED_ORIGINS=https://myapp.com, https://api.example.com, https://dashboard.mycompany.com\n\n# Leave empty if you only need Claude.ai and localhost\nALLOWED_ORIGINS=\n```\n\n**Note:** Claude.ai domains (`https://claude.ai`, `https://api.anthropic.com`) and localhost are always allowed, even if `ALLOWED_ORIGINS` is empty. Server-to-server requests (without Origin header) are always permitted.\n\n### Data Storage Configuration\n\nChromaDB data can be stored in three ways:\n\n1. **Docker volume (default)**: `CHROMA_DATA_PATH=chroma-data`\n\n   - Managed by Docker\n   - Survives container restarts\n   - Use `docker volume ls` and `docker volume inspect chroma-data` to locate\n\n2. **Local directory**: `CHROMA_DATA_PATH=./data`\n\n   - Easy to backup and access\n   - Stored in installation directory\n\n3. **Custom path**: `CHROMA_DATA_PATH=/path/to/data`\n   - Must be an absolute path\n   - Useful for mounting external storage\n\nAfter changing `CHROMA_DATA_PATH`, restart the services:\n\n```bash\ndocker compose restart\n```\n\n---\n\n## Connecting Claude\n\n### Claude Desktop + Mobile\n\n**Method 1: Custom Connector (Recommended - Pro/Team/Enterprise)**\n\n1. Open Claude Desktop → Settings → Integrations → Custom Connector\n2. Click \"Add Custom Server\"\n3. Enter:\n   - **Name**: `ChromaDB`\n   - **URL**: `https://your-server.com/mcp` (set `Authorization: Bearer YOUR_TOKEN` in the connector's header config)\n\n> **Note**: Custom connector automatically syncs to the mobile app. Authentication is mandatory for remote access.\n\n**Method 2: mcp-remote Wrapper (Free/Pro Users)**\n\nIf you don't have access to Custom Connectors, use the `mcp-remote` package as a workaround:\n\n**Configuration file location:**\n\n- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`\n- Windows: `%APPDATA%\\Claude\\claude_desktop_config.json`\n\n**Add to configuration file:**\n\n```json\n{\n  \"mcpServers\": {\n    \"chromadb\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-remote\", \"https://your-server.com/mcp\", \"--header\", \"Authorization: Bearer YOUR_TOKEN\"]\n    }\n  }\n}\n```\n\nRestart Claude Desktop after editing the file.\n\n> **Important**: Remote MCP servers cannot be configured directly in `claude_desktop_config.json` using `streamableHttp` transport. You must either use Custom Connectors or the `mcp-remote` wrapper package.\n\n### Claude Code\n\n**CLI Command:**\n\n```bash\n# Without authentication\nclaude mcp add --transport http chromadb https://your-server.com/mcp\n\n# With authentication (Query Parameter - Recommended)\nclaude mcp add --transport http chromadb https://your-server.com/mcp \\\n  --header \"Authorization: Bearer YOUR_TOKEN\"\n\n# With authentication (Header)\nclaude mcp add --transport http chromadb https://your-server.com/mcp \\\n  --header \"Authorization: Bearer YOUR_TOKEN\"\n\n# Verify\nclaude mcp list\n```\n\n---\n\n## Available Tools (v2.2.0)\n\nThe MCP server provides these tools for Claude. v2.2.0 expands coverage to 30 tools across collection / document / search / fork / client-info / admin / destructive groups.\n\n### Collection Management\n\n- `chroma_list_collections` - List all collections (with `limit` / `offset`)\n- `chroma_create_collection` - Create a new collection (`configuration` / `schema` optional)\n- `chroma_get_or_create_collection` - Idempotent create-or-get (v2.2.0)\n- `chroma_modify_collection` - Rename / change metadata or configuration (v2.2.0)\n- `chroma_delete_collection` - Delete a collection\n- `chroma_get_collection_info` - Get collection metadata\n- `chroma_get_collection_count` - Get document count (`read_level` optional)\n- `chroma_count_collections` - Total collection count (v2.2.0)\n- `chroma_peek_collection` - Preview collection contents\n\n### Document Operations\n\n- `chroma_add_documents` - Add documents (with `uris` for multi-modal)\n- `chroma_upsert_documents` - Idempotent insert-or-update (v2.2.0)\n- `chroma_query_documents` - Semantic search (with `query_uris` / `ids` pre-filter)\n- `chroma_get_documents` - Retrieve documents (`read_level` optional)\n- `chroma_update_documents` - Update existing documents (with `embeddings` / `uris`)\n- `chroma_delete_documents` - Delete by `ids` and/or `where` / `where_document` filter\n\n### Server Info (v2.2.0)\n\n- `chroma_heartbeat` - Server heartbeat (nanosecond timestamp)\n- `chroma_get_server_version` - Server version string\n- `chroma_get_max_batch_size` - Max batch size (for client-side splitting)\n- `chroma_get_user_identity` - Current tenant + databases\n\n### Distributed/Cloud-only — opt-in (`CHROMA_DISTRIBUTED_TOOLS_ENABLED=true`)\n\nThese 4 tools require ChromaDB's **distributed executor** (the executor is the chromadb-server-internal frontend layer, not an algorithmic distribution requirement). The single-node open-source server (`chromadb/chroma:latest` docker) ships with the **local executor**, which has these methods hard-coded as `unimplemented` in [`rust/frontend/src/executor/local.rs`](https://github.com/chroma-core/chroma/blob/main/rust/frontend/src/executor/local.rs) and [`rust/types/src/api_types.rs`](https://github.com/chroma-core/chroma/blob/main/rust/types/src/api_types.rs). To use them you need either Chroma Cloud (`CloudClient`) or a self-hosted distributed Chroma deployment (Kubernetes multi-component: frontend + query executor + WAL + compactor + object storage).\n\nHidden by default so single-node deployments don't waste LLM context on tools that always return `\"not implemented for local executor\"` / `\"unsupported for local chroma\"`.\n\n- `chroma_search` - Hybrid dense + sparse search (RRF). The algorithm itself works on a single node; chromadb open-source simply hasn't implemented the `search()` endpoint in the local executor.\n- `chroma_fork_collection` - Zero-copy fork (segment-level operation on object storage — architecturally requires the distributed compactor/storage stack).\n- `chroma_get_fork_count` - Fork metadata lookup (depends on the distributed metadata store).\n- `chroma_get_indexing_status` - WAL offset + compactor index progress (requires the distributed WAL/compactor services).\n\n### Admin — opt-in (`CHROMA_ADMIN_TOOLS_ENABLED=true`)\n\n- `chroma_admin_create_database` / `chroma_admin_get_database` / `chroma_admin_list_databases`\n- `chroma_admin_create_tenant` / `chroma_admin_get_tenant`\n\n### Destructive — opt-in (`CHROMA_ALLOW_DESTRUCTIVE_OPS=true`)\n\nCalls emit a `[DESTRUCTIVE]` audit line.\n\n- `chroma_reset_database` - Reset entire database (irreversible)\n- `chroma_admin_delete_database` - Delete a database (requires both flags)\n\n---\n\n## Using ChromaDB from Python\n\nThe MCP server proxies all ChromaDB REST API endpoints, allowing direct access from Python clients.\n\n### Python Example\n\n```python\nimport chromadb\n\n# HTTPS (Tailscale Funnel, public deployment)\nclient = chromadb.HttpClient(\n    host=\"your-server.com\",\n    port=443,\n    ssl=True,\n    headers={\n        \"Authorization\": \"Bearer YOUR_TOKEN\"\n    }\n)\n\n# Local development (HTTP)\nclient = chromadb.HttpClient(\n    host=\"localhost\",\n    port=8080,\n    ssl=False,\n    headers={\n        \"Authorization\": \"Bearer YOUR_TOKEN\"\n    }\n)\n\n# Usage\ncollection = client.create_collection(\"my_collection\")\ncollection.add(\n    documents=[\"Document 1\", \"Document 2\"],\n    ids=[\"id1\", \"id2\"]\n)\nresults = collection.query(query_texts=[\"query\"], n_results=2)\n```\n\nAlternative authentication:\n\n```python\nfrom chromadb.config import Settings\n\nclient = chromadb.HttpClient(\n    host=\"your-server.com\",\n    port=443,\n    ssl=True,\n    settings=Settings(\n        chroma_client_auth_provider=\"chromadb.auth.token_authn.TokenAuthClientProvider\",\n        chroma_client_auth_credentials=\"YOUR_TOKEN\"\n    )\n)\n```\n\n### API Documentation\n\nVisit `https://your-server.com/docs` for Swagger UI documentation of all ChromaDB REST API endpoints.\n\n---\n\n## Deployment\n\n### Option 1: Tailscale VPN (Recommended)\n\n**Secure access within your Tailscale network:**\n\n```bash\n# Start services\ndocker compose up -d\n\n# Enable Tailscale Serve (HTTPS with automatic certificates)\ntailscale serve https / http://127.0.0.1:8080\n\n# Check status\ntailscale serve status\n```\n\nYour server is now accessible at `https://your-machine.tailXXXXX.ts.net` to all devices in your Tailnet.\n\n**Advantages:**\n\n- Automatic HTTPS certificates\n- No public internet exposure\n- Encrypted VPN tunnel\n- Authentication optional (VPN provides security layer)\n\n### Option 2: Tailscale Funnel (Public Internet)\n\n**To use Claude Desktop UI Custom Connector or share publicly:**\n\n```bash\n# Enable Funnel (allows public internet access)\ntailscale funnel 8080 on\ntailscale serve https / http://127.0.0.1:8080\n\n# Verify Funnel is active\ntailscale serve status  # Should show \"Funnel on\"\n```\n\n> **Warning**: This exposes your server to the public internet. **Authentication is mandatory!** Set `MCP_AUTH_TOKEN` in your environment.\n\n**Disable Funnel:**\n\n```bash\ntailscale funnel 8080 off\n```\n\n### Option 3: Cloudflare Tunnel\n\n```bash\n# Install cloudflared\ncurl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared\nchmod +x cloudflared\n\n# Authenticate\n./cloudflared tunnel login\n\n# Create tunnel\n./cloudflared tunnel create chroma-mcp\n\n# Run tunnel\n./cloudflared tunnel --url http://localhost:3000\n```\n\n### Option 4: Nginx Reverse Proxy\n\n```nginx\nserver {\n    listen 80;\n    server_name your-domain.com;\n\n    location / {\n        proxy_pass http://localhost:3000;\n        proxy_http_version 1.1;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n```\n\n---\n\n## Security\n\n### Code Quality & Security Analysis\n\nThis project follows strict security practices and has resolved all security issues identified by static analysis:\n\n- ✅ **Zero Active Issues**: All OWASP and CWE security findings have been resolved\n- 🔒 **Static Analysis**: Continuous monitoring with [DeepSource](https://app.deepsource.com/report/1328a083-a457-4598-b56f-e64dafdbcc28)\n- 🛡️ **Security Standards**: Compliant with OWASP Top 10 and Node.js security best practices\n- 📊 **Automated Scanning**: Dependabot, CodeQL, and container vulnerability scanning\n\nFor detailed security information, see [Security Policy](SECURITY.md).\n\n### Security Recommendations\n\n1. **Enable Authentication for Public Access**\n\n   - Set `MCP_AUTH_TOKEN` when using Tailscale Funnel or public internet\n   - Generate strong tokens: `openssl rand -base64 32 | tr '+/' '-_' | tr -d '='`\n   - Rotate tokens regularly\n\n2. **Use HTTPS**\n\n   - Tailscale provides automatic HTTPS certificates\n   - Use reverse proxy (Nginx/Caddy) with Let's Encrypt for other deployments\n\n3. **Prefer VPN Over Public Internet**\n\n   - Tailscale Serve (VPN-only) is more secure than Funnel (public)\n   - Authentication is optional within VPN but mandatory for public access\n\n4. **Monitor Access**\n\n   ```bash\n   # Check for unauthorized access attempts\n   docker compose logs mcp-server | grep \"Unauthorized\"\n   ```\n\n5. **Network Isolation**\n   - Keep ChromaDB on private network\n   - Only expose MCP server to public internet\n\n---\n\n## Testing\n\n### Local Testing\n\n```bash\n# Health check\ncurl http://localhost:3000/health\n\n# MCP tools list\ncurl -X POST http://localhost:3000/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\"}'\n\n# ChromaDB heartbeat\ncurl http://localhost:3000/api/v2/heartbeat\n```\n\n### Remote Testing (with authentication)\n\n```bash\n# MCP endpoint (Bearer token)\ncurl -X POST https://your-server.com/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}'\n\n# MCP endpoint (Bearer token)\ncurl -X POST \"https://your-server.com/mcp\" \\\n  -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}'\n\n# ChromaDB REST API\ncurl https://your-server.com/api/v2/heartbeat \\\n  -H \"Authorization: Bearer YOUR_TOKEN\"\n\n# Swagger UI (browser)\nhttps://your-server.com/docs  # send Authorization: Bearer YOUR_TOKEN header\n```\n\n---\n\n## Troubleshooting\n\n### ChromaDB Connection Failed\n\n```bash\n# Check if ChromaDB is running\ncurl http://localhost:8000/api/v2/heartbeat\n\n# Start ChromaDB with Docker\n# WARNING: ChromaDB has no built-in authentication — do not publish on routable interface.\n# Bind to loopback only (127.0.0.1:8000:8000). Use MCP server as the authenticated gateway.\ndocker run -d -p 127.0.0.1:8000:8000 chromadb/chroma:1.5.9\n\n# Check MCP server logs\ndocker compose logs mcp-server\n```\n\n### MCP Server Not Responding\n\n```bash\n# Check logs\ndocker compose logs mcp-server\n\n# Check port conflicts\nlsof -i :3000\n\n# Restart services\ndocker compose restart\n```\n\n### Claude Desktop Connection Issues\n\n1. Restart Claude Desktop\n2. Verify URL includes `/mcp` path\n3. Confirm transport type is `streamableHttp` (not `sse`)\n4. Check authentication token if enabled\n5. For Custom Connector: Ensure Tailscale Funnel is active\n\n### TLS Handshake Timeout on Local Network\n\nIf you're connecting from the same local network as the server and using Tailscale Funnel HTTPS:\n\n**Problem**: TLS handshake fails with timeout when accessing `https://your-server.ts.net` from the same network.\n\n**Root cause**: Tailscale Funnel has issues with TLS termination when clients on the same LAN try to connect via the public Funnel domain.\n\n**Solution**: Use direct local network connection instead of Tailscale HTTPS:\n\n```bash\n# Remove existing configuration\nclaude mcp remove chromadb\n\n# Add with local IP address\nclaude mcp add chromadb --transport http \\\n  http://192.168.x.x:8080/mcp \\\n  --header \"Authorization: Bearer YOUR_TOKEN\"\n\n# Or use hostname if DNS resolves\nclaude mcp add chromadb --transport http \\\n  http://server-hostname:8080/mcp \\\n  --header \"Authorization: Bearer YOUR_TOKEN\"\n```\n\n**Verification**:\n```bash\n# Test local network connection\ncurl http://192.168.x.x:8080/health\n\n# Should return: {\"status\":\"ok\",\"service\":\"chroma-remote-mcp\",...}\n```\n\n**Note**: External clients should continue using Tailscale Funnel HTTPS. This issue only affects clients on the same LAN as the server.\n\n### Authentication Errors (401)\n\n```bash\n# Verify MCP_AUTH_TOKEN is set\ndocker compose exec mcp-server env | grep MCP_AUTH_TOKEN\n\n# Test without token (should fail with 401)\ncurl -X POST https://your-server.com/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}'\n\n# Test with correct token (should succeed)\ncurl -X POST https://your-server.com/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer YOUR_TOKEN\" \\\n  -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}'\n```\n\n---\n\n## Development\n\n### Building from Source\n\n```bash\n# Clone repository\ngit clone https://github.com/meloncafe/chromadb-remote-mcp.git\ncd chromadb-remote-mcp\n\n# Install dependencies\nyarn install\n\n# Development mode (auto-reload)\nyarn dev\n\n# Build TypeScript\nyarn build\n\n# Type check\nyarn type-check\n```\n\n### Testing\n\nThe project includes integration tests with Docker-based E2E validation:\n\n```bash\n# Run all tests (starts services, runs tests, cleans up)\nyarn test\n\n# Run tests and keep containers running for debugging\nyarn test:keep\n\n# Manual test script with options\n./scripts/test.sh --help\n```\n\n**Integration Test Coverage:**\n\n- ✅ Health check endpoint\n- ✅ Authentication (`Authorization: Bearer` MCP_AUTH_TOKEN; OAuth 2.1 / OIDC multi-provider)\n- ✅ MCP protocol (tools/list, tools/call)\n- ✅ ChromaDB REST API proxy\n- ✅ Collection CRUD operations\n- ✅ Rate limiting\n- ✅ Unauthorized access handling\n\n**Unit Tests:**\n\n```bash\n# Run unit tests\nyarn test:unit\n\n# Run with watch mode\nyarn test:unit:watch\n\n# Run with coverage\nyarn test:unit:coverage\n\n# Run all tests (unit + integration)\nyarn test:all\n```\n\n**Unit Test Coverage:**\n\n- ✅ Authentication utilities (timing-safe comparison, buffer operations)\n- ✅ Input validation (collection names, document IDs, metadata)\n- ✅ Data processing (response formatting, JSON serialization)\n- ✅ Error message formatting\n\nSee `__tests__/README.md` for detailed testing strategy.\n\n### Code Quality & Coverage\n\nThis project uses [Codecov](https://codecov.io/gh/meloncafe/chromadb-remote-mcp) for code coverage tracking and test analytics.\n\n### Docker Development\n\n#### Local Build and Test\n\n```bash\n# Build for local testing (single platform, loads to Docker)\nyarn docker:build:local\n\n# Or with script directly\n./scripts/build.sh --platform linux/amd64 --load\n\n# Test the built image\ndocker run -p 3000:3000 \\\n  -e MCP_AUTH_TOKEN=test123 \\\n  devsaurus/chromadb-remote-mcp:latest\n```\n\n#### Multi-Platform Build\n\n```bash\n# Build for all platforms (amd64, arm64)\nyarn docker:build\n\n# Build with custom version\n./scripts/build.sh --version 1.2.3\n\n# Build with custom repository\n./scripts/build.sh --repo myuser/my-mcp --version dev\n```\n\n#### Push to Docker Hub\n\n```bash\n# Push latest tag\nyarn docker:push\n\n# Push specific version\nVERSION=1.2.3 yarn docker:push\n\n# Or with script directly\n./scripts/build.sh --version 1.2.3 --push\n\n# With custom repository\nDOCKER_REPO=myuser/my-mcp ./scripts/build.sh --version 1.2.3 --push\n```\n\n**Environment Variables for Docker Scripts:**\n\n```bash\nexport DOCKER_REPO=myuser/my-mcp       # Docker repository\nexport VERSION=1.2.3                    # Image version tag\nexport DOCKER_USERNAME=myuser           # For push authentication\nexport DOCKER_PASSWORD=mytoken          # Docker Hub token\n```\n\n### Development Scripts\n\nAll development scripts are located in `scripts/`:\n\n| Script       | Purpose                      | Usage                       |\n| ------------ | ---------------------------- | --------------------------- |\n| `build.sh`   | Build and push Docker images | `./scripts/build.sh --help` |\n| `test.sh`    | Run integration tests        | `./scripts/test.sh --help`  |\n| `install.sh` | One-command installation     | `curl ... \\| bash`          |\n\n**Quick Development Workflow:**\n\n```bash\n# 1. Make code changes\nvim src/index.ts\n\n# 2. Test locally\nyarn dev\n\n# 3. Run integration tests\nyarn test\n\n# 4. Build Docker image\nyarn docker:build:local\n\n# 5. Test Docker image\ndocker-compose up\n\n# 6. If all good, build multi-platform and push\n./scripts/build.sh --version 1.2.3 --push\n```\n\n### Project Structure\n\n```\nchromadb-remote-mcp/\n├── .github/\n│   ├── ISSUE_TEMPLATE/       # GitHub issue templates\n│   └── workflows/            # GitHub Actions (publish-release, security-scan, chromadb-version-check.yml)\n├── scripts/\n│   ├── build.sh             # Docker build and push script (multi-platform)\n│   ├── test.sh              # Integration test runner\n│   └── install.sh           # One-command installation\n├── src/\n│   ├── index.ts             # Main server entry point\n│   ├── chroma-tools.ts      # MCP tool definitions and handlers\n│   └── types.ts             # TypeScript type definitions\n├── docker-compose.yml       # Production (prebuilt image)\n├── docker-compose.dev.yml   # Development (builds from source)\n├── Dockerfile               # MCP server Docker image\n├── .env.example             # Environment variables template\n├── package.json             # Node.js dependencies\n├── tsconfig.json            # TypeScript configuration\n├── SECURITY.md              # Security policy\n├── CONTRIBUTING.md          # Contribution guidelines\n├── CODE_OF_CONDUCT.md       # Code of conduct\n├── CHANGELOG.md             # Version history\n└── LICENSE                  # MIT license\n```\n\n---\n\n## v2.2.3 Release Notes — CVE-2026-45829 Security Hardening\n\n> **⚠️ Breaking changes** — operators upgrading from v2.2.2 or earlier must read this section.\n\n### ChromaDB image version pinned (R4)\n\nAll `docker-compose*.yml` files now pin `chromadb/chroma` to version `1.5.9@sha256:...`.\nVersions `1.0.0–1.5.8` are vulnerable to **CVE-2026-45829 (ChromaToast, CVSS 10.0)** —\na pre-auth RCE via malicious embedding-function configuration. Do not downgrade the pin.\n\nA CI workflow (`.github/workflows/chromadb-version-check.yml`) fails the build if any\ndocker-compose file references a version in the vulnerable range.\n\n### Dev fail-open removed (R1, breaking)\n\nPreviously, starting the server without `MCP_AUTH_TOKEN` or `OIDC_ISSUERS`/`OIDC_PRESET`\nwould succeed silently in non-production environments. This behaviour is **removed**.\n\nThe server now **refuses to start** unless at least one auth method is configured or\n`ALLOW_INSECURE_NO_AUTH=true` is explicitly set.\n\n**Migration:**\n- Production: set `MCP_AUTH_TOKEN` or configure OIDC.\n- Local dev: add `ALLOW_INSECURE_NO_AUTH=true` to your `.env`.\n\n### ChromaDB REST catch-all proxy is now OFF by default (R3, breaking)\n\nThe pass-through REST proxy (previously always mounted) is now **disabled unless**\n`CHROMA_REST_PROXY_ENABLED=true` is set. When disabled, all `/api/*` requests return 404.\n\nWhen enabled, the proxy enforces:\n- DNS-rebind protection (`validateOriginHeader`) — `Origin: evil.example` → 403\n- Authentication (always required; `ALLOW_INSECURE_NO_AUTH` does **not** bypass the proxy)\n- Path filter: collection create/modify/delete and embedding-function endpoints are blocked (403)\n- Body sanitize: `configuration.embedding_function` in POST/PUT/PATCH body → 400\n\n**Migration:** If you relied on direct `/api/v2/*` REST passthrough, set\n`CHROMA_REST_PROXY_ENABLED=true` and ensure authentication is configured.\n\n---\n\n## Contributing\n\nContributions are welcome! Please feel free to submit issues and pull requests.\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n---\n\n## License\n\n[MIT License](LICENSE)\n\n---\n\n## Resources\n\n- [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18/)\n- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)\n- [ChromaDB Documentation](https://docs.trychroma.com/)\n- [Tailscale Serve](https://tailscale.com/kb/1242/tailscale-serve/)\n- [Tailscale Funnel](https://tailscale.com/kb/1223/funnel)\n- [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/)\n\n---\n\n## Support\n\nIf you encounter any issues or have questions, please [open an issue](https://github.com/meloncafe/chromadb-remote-mcp/issues).\n\n---\n\n## v2.0.0 Configuration\n\n> v2.0 introduces collection metadata schema v2, OAuth 2.1 OIDC, configurable embedding providers, and an optional reranker. See [MIGRATION.md](./MIGRATION.md) for the upgrade guide.\n\n### Environment variables\n\n| Variable | Purpose |\n|----------|---------|\n| `EMBEDDING_PROVIDER` | `chromadb-default` (English-only, default) / `external` / `openai_compatible` / `gemini` / `voyage` |\n| `EMBEDDING_MODEL` | Provider-specific model id. Stored in collection metadata. |\n| `EMBEDDING_DIMENSIONS` | Vector dimensions. Required for external mode; Gemini accepts 768/1536/3072. |\n| `EMBEDDING_API_BASE` | OpenAI-compatible endpoint base URL (Ollama / TEI / Voyage / Together / vLLM). |\n| `EMBEDDING_API_KEY` | Bearer key for `openai_compatible` or `voyage` providers. |\n| `GEMINI_API_KEY` | Google AI Studio API key for the `gemini` provider. |\n| `CONFIDENCE_THRESHOLD` | Default `min_score` (0-1). Tool argument has priority. |\n| `RERANKER_API_BASE` | OpenAI-compatible `/rerank` endpoint. Reranker is fail-soft. |\n| `RERANKER_API_KEY` | Optional bearer key for the reranker. |\n| `RERANKER_MODEL` | Reranker model id (default `bge-reranker-v2-m3`). |\n| `OIDC_ISSUERS` | Comma-separated OIDC issuer URLs. |\n| `OIDC_PRESET` | Convenience preset names: `google,github,microsoft`. |\n| `OIDC_AUDIENCE` | Expected `aud` claim. |\n| `OIDC_SCOPES` | Comma-separated scopes for the Protected Resource Metadata. |\n| `OIDC_LOG_SUB_MODE` | `full` for raw `sub`, otherwise SHA-256 first 12 chars (default). |\n| `MCP_AUTH_TOKEN` | **Service-to-service / CI / internal scripts only.** Use OAuth for human users. Coexists with OIDC — either method accepts. |\n| `LEGACY_COLLECTION_COMPAT` | `true` to allow read-only access to legacy v1 collections. Writes are still rejected. |\n\n### Recommended embedding + reranker combinations\n\nVerified locally on Korean RAG workloads (2026-05). Pick by priority:\n\n| Priority | Embedding | Reranker | Why |\n|----------|-----------|----------|-----|\n| Accuracy first (recommended) | `gemini` / `gemini-embedding-001` / 1536d | `cohere` / `rerank-multilingual-v3.0` | Gemini emits asymmetric query↔document vectors (`RETRIEVAL_QUERY`/`RETRIEVAL_DOCUMENT`, self-distance ≈ 0.21 in our test); Cohere reorders short KR question↔answer pairs cleanly. |\n| Cost-balanced | `voyage` / `voyage-3` / 1024d | `cohere` / `rerank-multilingual-v3.0` | Voyage embeddings are ~1/2.5 the cost of Gemini and still asymmetric (`input_type` query/document, self-distance ≈ 0.56). |\n| Minimum embedding cost | `openai_compatible` / `text-embedding-3-small` / 1536d | `cohere` / `rerank-multilingual-v3.0` | Cheapest hosted embedding; symmetric vectors are weaker on short KR queries, so the reranker is essential. |\n| Self-hosted / offline | `openai_compatible` (Ollama / TEI / vLLM) | TEI `bge-reranker-v2-m3` or similar | No external API; latency depends on local hardware. |\n\nNotes from the verification run:\n\n- Voyage `rerank-2` did NOT reorder the short KR question↔answer pair used in this test — keep Cohere as the rerank default for KR until your own corpus shows otherwise.\n- The reranker layer is fail-soft: leave `RERANKER_API_BASE` unset to disable reranking without code changes.\n- Set `CONFIDENCE_THRESHOLD` (or per-call `min_score`) to drop low-similarity hits; the server emits `confidence_gate: \"no_confident_match\"` when every result is filtered.\n\n### Docker Compose snippet (Gemini + Google OAuth)\n\n```yaml\nservices:\n  mcp-server:\n    image: devsaurus/chromadb-remote-mcp:2.0.0\n    environment:\n      EMBEDDING_PROVIDER: gemini\n      EMBEDDING_MODEL: gemini-embedding-001\n      EMBEDDING_DIMENSIONS: \"1536\"\n      GEMINI_API_KEY: ${GEMINI_API_KEY}\n      OIDC_PRESET: google\n      OIDC_AUDIENCE: ${OIDC_AUDIENCE}  # e.g. your client_id\n      CONFIDENCE_THRESHOLD: \"0.55\"\n      RERANKER_API_BASE: \"http://desktop-gpu.tail-xxxx.ts.net:8001\"\n      RERANKER_MODEL: bge-reranker-v2-m3\n```\n\n### OAuth flow\n\n1. Configure your IdP (Google / GitHub / Microsoft) to issue tokens for an audience that matches `OIDC_AUDIENCE`.\n2. Set `OIDC_PRESET=google` (or `OIDC_ISSUERS=...` for custom IdPs) and `OIDC_AUDIENCE=...`.\n3. Clients send `Authorization: Bearer <token>` to `/mcp`.\n4. 401 responses include `WWW-Authenticate: Bearer error=\"...\", resource_metadata=\"<base>/.well-known/oauth-protected-resource\"` per RFC 9728.\n5. `MCP_AUTH_TOKEN` remains valid alongside OAuth — recommended for non-interactive workloads.\n\n### Reading legacy v1 collections\n\nSet `LEGACY_COLLECTION_COMPAT=true` to allow read-only access. Writes (`chroma_add_documents` / `update` / `delete`) on v1 collections still return `Error: Cannot write to legacy v1 collection`. See [MIGRATION.md](./MIGRATION.md).\n",
  "bytes": 39531,
  "sha": "0c0035cd240142a1c1945d23f9697749911612bd927f76dfc546a3f05676c0ca",
  "repo_slug": "meloncafe/chromadb-remote-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_meloncafe_chromadb_remote_mcp_2c085b77/readme"
}