servicenow-docs
README.md
# servicenow-docs-mcp
MCP server that searches a local clone of [ServiceNow/ServiceNowDocs](https://github.com/ServiceNow/ServiceNowDocs) via two tools:
- `search_docs(query, max_results?)` — keyword/regex search across all docs, returns file + line + snippet.
- `read_doc(path)` — reads the full content of a doc by the relative path returned from `search_docs`.
No vector DB, no API keys. Search runs against a flat pre-built text index using `grep`, so it works fully offline.
See [ARCHITECTURE.md](ARCHITECTURE.md) for how it works, why grep over a vector DB, measured benchmarks, and known limitations.
## Prerequisites
- Node.js 18+
- `grep` (preinstalled on macOS/Linux)
- A local clone of the docs repo
## Setup
```bash
# 1. Clone the docs repo, as a sibling of this project's parent dir
# (default DOCS_ROOT expects ../ServiceNowDocs/markdown relative to this folder)
git clone --depth 1 https://github.com/ServiceNow/ServiceNowDocs.git
# 2. Install dependencies
cd servicenow-docs-mcp
npm install
# 3. Build the search index (one-time; re-run after pulling doc updates)
node build-index.mjs
```
Directory layout expected:
```
ServiceNowKB/
├── ServiceNowDocs/ # cloned docs repo
│ └── markdown/ # DOCS_ROOT
└── servicenow-docs-mcp/ # this project
├── index.js
├── build-index.mjs
└── index.txt # generated by build-index.mjs
```
To point at docs cloned elsewhere, set `DOCS_ROOT` when running the server or the index builder:
```bash
DOCS_ROOT=/path/to/ServiceNowDocs/markdown node build-index.mjs
```
### Why the index build step
`search_docs` doesn't grep the 49k individual markdown files directly — walking that many small files takes minutes. `build-index.mjs` flattens every file into one `index.txt` (`path:line:content` per line), so a query becomes a single-file grep (~1-5s). Rebuild it whenever the docs repo is updated:
```bash
cd ServiceNowDocs && git pull
cd ../servicenow-docs-mcp && node build-index.mjs
```
## Register with a Claude environment
### Claude Code (any project)
Add to that project's `.mcp.json` (create it if missing) — path is relative to wherever you run Claude Code, adjust as needed:
```json
{
"mcpServers": {
"servicenow-docs": {
"command": "node",
"args": ["/absolute/path/to/servicenow-docs-mcp/index.js"]
}
}
}
```
Restart the Claude Code session in that directory to load it — MCP servers are only picked up at session start.
### Claude Desktop
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"servicenow-docs": {
"command": "node",
"args": ["/absolute/path/to/servicenow-docs-mcp/index.js"]
}
}
}
```
Restart Claude Desktop to load it.
## Usage
Once loaded, just ask questions naturally — Claude calls `search_docs` / `read_doc` on its own. Example:
> "Is there a system property to debug OAuth issues?"
To search directly yourself, call the tool with a keyword or grep-style regex:
```
search_docs(query: "oauth.*debug", max_results: 15)
```
Regex special characters are passed through to `grep -i`, so patterns like `glide\.oauth\..*` work.
## Token usage per query
Measured directly from `index.txt` (per-file content size, single pass over all 48,975 indexed files — not sampled), converted at ~4 characters/token.
**File size distribution (this corpus):**
| Percentile | Size | Approx tokens |
|---|---|---|
| min | 348 B | ~90 |
| median | 3.3 KB | ~825 |
| average | 5.4 KB | ~1,360 |
| p90 | 8.9 KB | ~2,215 |
| p99 | 31.4 KB | ~7,850 |
| max (outlier) | 2.2 MB | ~557,000 |
**Per-call cost:**
| Call | What's returned | Approx tokens |
|---|---|---|
| `search_docs`, small (5 results) | 5× `{file, line, snippet≤300 chars}` JSON | ~500 |
| `search_docs`, default (20 results) | 20× same | ~1,900 |
| `search_docs`, max (50 results) | 50× same | ~4,800 |
| `read_doc`, median file (3.3 KB) | full markdown file | ~825 |
| `read_doc`, p90 file (8.9 KB) | full markdown file | ~2,215 |
| `read_doc`, p99 file (31 KB) | full markdown file | ~7,850 |
A typical question costs one `search_docs` call plus one or two `read_doc` calls — so roughly **1,500-10,000 tokens** for most docs, more if it lands on a p99+ outlier page. Lower `max_results` or read fewer docs to cut cost; the server has no chunking/summarization, so `read_doc` always returns the entire file.
## Response token usage with caveman mode
This session ran with the [caveman](https://github.com/anthropics/claude-code) skill active (terse, no filler). Measured on an actual response from this session (the "how this works / best practices" answer above) against a normal-verbosity rewrite of the same content:
| Version | Characters | Approx tokens |
|---|---|---|
| Normal mode (rewritten, same content) | 5,223 | ~1,306 |
| Caveman mode (actual) | 1,762 | ~441 |
| **Savings** | **66%** | **66%** |
Matches caveman's own documented ~65% output-token reduction. Applies to Claude's *response* text only — doesn't change `search_docs`/`read_doc` tool-result token cost above, which is fixed by doc size regardless of response style.
## Troubleshooting
- **No results for an obvious term**: rebuild the index — it may predate a `git pull`.
- **`Search index missing` error**: run `node build-index.mjs` (step 3 above).
- **Server doesn't appear after editing `.mcp.json` / Desktop config**: restart the Claude session — config is only read at startup, not hot-reloaded.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues