WSO2 Docs MCP Server
by iamvirul
README.md
# WSO2 Docs MCP Server
[](https://www.npmjs.com/package/wso2-docs-mcp-server)
[](LICENSE)
>"This is an unofficial community project. Not affiliated with or endorsed by WSO2."
A production-ready **Model Context Protocol (MCP)** server that provides AI assistants (Claude Desktop, Claude Code, Cursor, VS Code) with semantic search over WSO2 documentation via Retrieval-Augmented Generation (RAG).
Under the hood, it uses a blazing-fast dual-ingestion engine:
- **GitHub Native:** Fetches raw Markdown directly from WSO2's public GitHub repositories via the Git Trees API (avoids web-scraping noise and rate limits)
- **Web Crawl Fallback:** For products without dedicated GitHub docs repos (like the WSO2 Library)
## Architecture

## Documentation Sources
| Product | ID | URL |
|---|---|---|
| API Manager | `apim` | https://apim.docs.wso2.com |
| Micro Integrator | `mi` | https://mi.docs.wso2.com/en/4.4.0 |
| Ballerina Integrator | `bi` | https://bi.docs.wso2.com |
| Choreo | `choreo` | https://wso2.com/choreo/docs |
| Identity Server | `is` | https://is.docs.wso2.com/en/latest |
| Ballerina | `ballerina` | https://ballerina.io/learn |
| WSO2 Library | `library` | https://wso2.com/library |
## Prerequisites
- **Node.js** ≥ 20
- **Docker** (for pgvector)
- **Embeddings** - no API key required by default:
- **[Ollama](https://ollama.com)** (recommended) - runs locally, model auto-downloaded on first run
- If Ollama is not running, the server automatically falls back to **HuggingFace ONNX** (in-process, also downloads automatically)
- Cloud providers are also supported: OpenAI, Google Gemini, Voyage AI
---
## Quick Start
Choose the setup path that fits your use case:
- **[Install from npm](#install-from-npm)** - simplest, no cloning required
- **[Clone and build](#clone-and-build)** - for development or contributions
---
### Install from npm
Install the package globally to get the `wso2-docs-mcp-server`, `wso2-docs-crawl`, and `wso2-docs-migrate` commands available system-wide:
```bash
npm install -g wso2-docs-mcp-server
```
> **Prefer no global install?** You can use `npx wso2-docs-mcp-server`, `npx wso2-docs-crawl`, and `npx wso2-docs-migrate` in every step below - just replace the bare command with its `npx` equivalent.
#### 1. Start pgvector
Download the `docker-compose.yml` and start the database:
```bash
curl -O https://raw.githubusercontent.com/iamvirul/wso2-docs-mcp-server/main/docker-compose.yml
docker compose up -d
```
#### 2. Start Ollama (optional but recommended)
[Install Ollama](https://ollama.com) and pull the default embedding model:
```bash
ollama pull nomic-embed-text
ollama serve
```
> **No Ollama?** Skip this step. The server automatically falls back to HuggingFace ONNX - model downloads on first use with no extra setup.
#### 3. Run database migration
```bash
DATABASE_URL="postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs" \
wso2-docs-migrate
```
> Run migration again whenever you change `EMBEDDING_DIMENSIONS` (i.e. switch embedding provider). The script detects and handles dimension changes automatically.
#### 4. Index WSO2 documentation
```bash
# Index all products (first run downloads the embedding model automatically)
DATABASE_URL="postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs" \
wso2-docs-crawl
# Index a single product (faster, great for testing)
DATABASE_URL="postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs" \
wso2-docs-crawl --product ballerina --limit 20
# Force re-index even unchanged pages
DATABASE_URL="postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs" \
wso2-docs-crawl --force
```
Available product IDs: `apim`, `mi`, `bi`, `choreo`, `is`, `ballerina`, `library`
#### 5. Configure your AI client
The MCP server is launched on demand by your AI client - no background process needed.
**Claude Desktop** - edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"wso2-docs": {
"command": "wso2-docs-mcp-server",
"env": {
"DATABASE_URL": "postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs",
"EMBEDDING_PROVIDER": "ollama"
}
}
}
}
```
**Claude Code** - run once in your terminal:
```bash
claude mcp add wso2-docs \
--transport stdio \
-e DATABASE_URL="postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs" \
-e EMBEDDING_PROVIDER="ollama" \
-- wso2-docs-mcp-server
# Verify
claude mcp list
```
**Cursor** - create `.cursor/mcp.json` in your project root:
```json
{
"mcpServers": {
"wso2-docs": {
"command": "wso2-docs-mcp-server",
"env": {
"DATABASE_URL": "postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs",
"EMBEDDING_PROVIDER": "ollama"
}
}
}
}
```
**VS Code** - create `.vscode/mcp.json`:
```json
{
"servers": {
"wso2-docs": {
"type": "stdio",
"command": "wso2-docs-mcp-server",
"env": {
"DATABASE_URL": "postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs",
"EMBEDDING_PROVIDER": "ollama"
}
}
}
}
```
> **Using `npx` instead of global install?** Replace `"command": "wso2-docs-mcp-server"` with `"command": "npx"` and add `"args": ["-y", "wso2-docs-mcp-server"]`.
> **Cloud embedding provider?** Add the key to `env`, e.g. `"EMBEDDING_PROVIDER": "openai", "OPENAI_API_KEY": "sk-..."`.
---
### Clone and build
#### 1. Clone and install
```bash
git clone https://github.com/iamvirul/wso2-docs-mcp-server.git
cd wso2-docs-mcp-server
npm install
```
#### 2. Start Ollama (optional but recommended)
[Install Ollama](https://ollama.com) and start it:
```bash
ollama serve
```
> **No Ollama?** Skip this step. The server detects Ollama is not running and automatically falls back to HuggingFace ONNX inference - the model downloads on first use with no extra setup.
#### 3. Configure environment
```bash
cp .env.example .env
# Defaults work out of the box with Ollama.
# Only edit if using a cloud provider (OpenAI / Gemini / Voyage).
```
#### 4. Start pgvector
```bash
docker compose up -d
# pgAdmin available at http://localhost:5050 (admin@wso2mcp.local / admin)
```
#### 5. Run database migration
```bash
npm run db:migrate
```
> **Note:** Run migration again whenever you change `EMBEDDING_DIMENSIONS` (i.e. switch embedding provider). The script detects and handles dimension changes automatically.
#### 6. Index documentation
```bash
# Index all products
# On first run the embedding model is downloaded automatically (Ollama or HuggingFace)
npm run crawl
# Index a single product (faster, great for testing)
npm run crawl -- --product ballerina --limit 20
# Force re-index even unchanged pages
npm run crawl -- --force
```
#### 7. Build and start the MCP server
```bash
npm run build
npm start
```
For development (no build step):
```bash
npm run dev
```
#### 8. Configure your AI client
> Replace `/ABSOLUTE/PATH/TO/wso2-docs-mcp-server` with your actual clone path.
**Claude Desktop** - edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"wso2-docs": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/wso2-docs-mcp-server/dist/src/index.js"],
"env": {
"DATABASE_URL": "postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs",
"EMBEDDING_PROVIDER": "ollama"
}
}
}
}
```
**Claude Code:**
```bash
claude mcp add wso2-docs \
--transport stdio \
-e DATABASE_URL="postgresql://wso2mcp:wso2mcp@localhost:5432/wso2docs" \
-e EMBEDDING_PROVIDER="ollama" \
-- node "/ABSOLUTE/PATH/TO/wso2-docs-mcp-server/dist/src/index.js"
# Verify
claude mcp list
```
See `config-examples/claude_code.sh` for a convenience script.
**Cursor** - create `.cursor/mcp.json` - see `config-examples/cursor_mcp.json`.
**VS Code** - create `.vscode/mcp.json` - see `config-examples/vscode_mcp.json`.
---
## MCP Tools
| Tool | Description |
|---|---|
| `search_wso2_docs` | Semantic search across all products. Optional `product` and `limit` filters. |
| `get_wso2_guide` | Search within a specific product (`apim`, `mi`, `bi`, `choreo`, `is`, `ballerina`, `library`). |
| `explain_wso2_concept` | Broad concept search across all products, returns 8 top results. |
| `list_wso2_products` | Returns all supported products with IDs and base URLs. |
### Example response
```json
[
{
"title": "Deploying WSO2 API Manager",
"snippet": "WSO2 API Manager can be deployed in various topologies…",
"source_url": "https://apim.docs.wso2.com/en/latest/install-and-setup/...",
"product": "apim",
"section": "Deployment Patterns",
"score": 0.8712
}
]
```
---
## Local Embeddings
The default `EMBEDDING_PROVIDER=ollama` runs entirely on your machine with no API key. The startup sequence is:
```
Is Ollama running?
├── Yes → Is model present?
│ ├── Yes → Ready (instant)
│ └── No → Pull via Ollama (streamed, runs once)
└── No → Download ONNX model from HuggingFace Hub (~250 MB, cached after first run)
and run inference in-process via @huggingface/transformers
```
Both paths use `nomic-embed-text` / `Xenova/nomic-embed-text-v1` by default and produce identical 768-dim vectors, so you can switch between them without re-indexing.
### Hardware acceleration (HuggingFace ONNX fallback)
When Ollama is not available, the server auto-detects the best compute backend:
| Machine | Detection | ONNX dtype | Batch size | Throughput |
|---|---|---|---|---|
| Apple Silicon (M1/M2/M3/M4) | `process.arch === 'arm64'` | `q8` INT8 | 32 | ~9 ms/chunk |
| NVIDIA GPU | `nvidia-smi` probe | `fp32` | 64 | GPU-dependent |
| All others | fallback | `q8` INT8 | 16 | ~10 ms/chunk |
**Why `q8` on Apple Silicon instead of CoreML/Metal?**
CoreML compiles Metal shaders on first use (~20 min cold-start). For the typical chunk sizes produced by this server (6–20 chunks per page), the CPU↔GPU transfer overhead eliminates any inference gain. INT8 quantized inference on ARM NEON SIMD is consistently **~100× faster than fp32 CPU** with zero cold-start cost.
**Benchmark (Apple M-chip, `Xenova/nomic-embed-text-v1`):**
```
fp32 CPU (before): ~1,000 ms/chunk (68 chunks ≈ 68 s of embedding)
q8 ARM NEON: ~9 ms/chunk (68 chunks ≈ 0.6 s of embedding) ← ~100× speedup
```
> **Note:** For small crawls (≤ 10 pages) total wall-clock time is dominated by network I/O
> (HTTPS fetches to docs sites), so the end-to-end improvement is modest. The embedding
> speedup becomes significant at scale - crawling 500+ pages where embedding previously
> accounted for hours of runtime. For best crawl performance, run Ollama (`ollama serve`)
> which parallelises inference natively and has no per-chunk overhead.
---
## Environment Variables
### Core
| Variable | Default | Description |
|---|---|---|
| `DATABASE_URL` | - | PostgreSQL connection string (required) |
| `EMBEDDING_PROVIDER` | `ollama` | `ollama` \| `openai` \| `gemini` \| `voyage` |
| `EMBEDDING_DIMENSIONS` | `768` | Must match model output dimensions |
| `CRAWL_CONCURRENCY` | `5` | Concurrent HTTP requests during crawl |
| `CHUNK_SIZE` | `800` | Approximate tokens per chunk |
| `CHUNK_OVERLAP` | `100` | Overlap tokens between chunks |
| `CACHE_TTL_SECONDS` | `3600` | In-memory query cache TTL |
| `TOP_K_RESULTS` | `10` | Default search result count |
### Ollama (default)
| Variable | Default | Description |
|---|---|---|
| `OLLAMA_BASE_URL` | `http://localhost:11434` | Ollama server URL |
| `OLLAMA_EMBEDDING_MODEL` | `nomic-embed-text` | Model pulled and used via Ollama |
| `HUGGINGFACE_EMBEDDING_MODEL` | `Xenova/nomic-embed-text-v1` | ONNX fallback when Ollama is not running |
### Cloud providers
| Variable | Default | Description |
|---|---|---|
| `OPENAI_API_KEY` | - | Required if `EMBEDDING_PROVIDER=openai` |
| `OPENAI_EMBEDDING_MODEL` | `text-embedding-3-small` | OpenAI model |
| `GEMINI_API_KEY` | - | Required if `EMBEDDING_PROVIDER=gemini` |
| `GEMINI_EMBEDDING_MODEL` | `text-embedding-004` | Gemini model |
| `VOYAGE_API_KEY` | - | Required if `EMBEDDING_PROVIDER=voyage` |
| `VOYAGE_EMBEDDING_MODEL` | `voyage-3` | Voyage model |
### Embedding dimension reference
| Provider | Model | Dimensions |
|---|---|---|
| Ollama / HuggingFace | `nomic-embed-text` / `Xenova/nomic-embed-text-v1` | **768** (default) |
| Ollama / HuggingFace | `mxbai-embed-large` / `Xenova/mxbai-embed-large-v1` | 1024 |
| Ollama / HuggingFace | `all-minilm` / `Xenova/all-MiniLM-L6-v2` | 384 |
| OpenAI | `text-embedding-3-small` | 1536 |
| OpenAI | `text-embedding-3-large` | 3072 |
| Gemini | `text-embedding-004` | 768 |
| Voyage | `voyage-3` | 1024 |
| Voyage | `voyage-3-lite` | 512 |
---
## Scheduled Re-indexing
```bash
# Run a one-off re-index (checks hashes, skips unchanged pages)
npm run reindex
# Or from the project directory using node-cron (runs daily at 2 AM)
DATABASE_URL=... node -e "
const { ReindexJob } = require('./dist/jobs/reindexDocs');
const job = new ReindexJob();
job.initialize().then(() => job.scheduleDaily());
"
```
---
## Project Structure
```
src/
config/ env.ts · constants.ts
vectorstore/ pgvector.ts · schema.sql
ingestion/ crawler.ts · parser.ts · githubFetcher.ts · markdownParser.ts · chunker.ts · embedder.ts
server/ mcpServer.ts · toolRegistry.ts
jobs/ reindexDocs.ts
index.ts
scripts/
crawl.ts CLI ingestion pipeline
migrate.ts Dynamic schema migration
config-examples/ claude_desktop.json · claude_code.sh · cursor_mcp.json · vscode_mcp.json
docker-compose.yml
.env.example
```
---
## Development
```bash
# Type-check
npx tsc --noEmit
# Run crawl with tsx (no build needed)
npm run crawl -- --product ballerina --limit 5
# Run server in dev mode
npm run dev
```
This server cannot be deployed
Maintenance
ActivityActive
ResponsivenessNo issues