AI Ops Hub
# AI Ops Hub
**An MCP server that gives AI assistants safe, sandboxed hands on your machine** — notes, tasks, web pages, and hybrid search (FTS5 + embeddings) over a personal document corpus. Built with TypeScript, SQLite, and a security-first design.
[](https://github.com/Galiusbro/ai-ops-hub/actions/workflows/ci.yml)
[](tsconfig.json)
[](package.json)
[](LICENSE)
[MCP (Model Context Protocol)](https://modelcontextprotocol.io) is the open standard that lets AI clients like Claude Desktop call external tools. This server implements it **twice from one codebase**: over stdio for local clients and over HTTP for remote access.
## What it looks like in practice
Once connected to Claude Desktop, conversations like this just work:
> **You:** Find my notes about the Postgres migration and add a task to finish it by Friday.
>
> **Claude:** → `rag_search("postgres migration")` — 3 matching chunks from your corpus
> → `task_create("Finish Postgres migration", due: "2026-07-31")`
> *"Found your migration notes — the remaining step was the index rebuild. Task created for Friday."*
Every step happens inside the sandbox you configured: Claude can only touch the notes directory you allowed, only fetch from hosts you allowlisted, and only through the tools below.
## Tools
| Tool | What it does |
|---|---|
| `rag_search` | Search the corpus — `keyword` (FTS5), `vector` (embeddings), or `hybrid` (both, fused with Reciprocal Rank Fusion) |
| `rag_add_document` | Add or update a document: auto-chunked, FTS-indexed, embedded when vector search is configured |
| `rag_stats` | Corpus statistics: documents, chunks, embeddings, backend availability |
| `file_read` / `file_write` / `file_list` | Notes access — sandboxed to `NOTES_DIR`, allowlisted extensions only |
| `web_fetch` | Fetch a page from **allowlisted hosts only**, stripped to clean text (cheerio) |
| `task_create` / `task_list` / `task_complete` | Tasks stored as plain, human-editable markdown |
**Hybrid search** is the default when `OPENAI_API_KEY` is set: FTS5 and cosine-similarity results are merged with [Reciprocal Rank Fusion](https://plg.uwaterloo.ca/~gvcormac/cormacksigir09-rrf.pdf) — rank-based fusion that needs no score normalization between bm25 and cosine scales. If the vector backend fails mid-query, hybrid degrades gracefully to keyword results.
## Security model
Local tool access for an LLM is a security problem before it is anything else. The interesting engineering here:
- **Path sandboxing that survives the classic bypasses.** Every path resolves against `NOTES_DIR`; absolute paths, `../` traversal, and the sibling-prefix bypass (`notes` vs `notes-evil` — a bug most naive `startsWith` checks have) are rejected. Extension allowlist is enforced on both read and write.
- **Web fetching is deny-by-default.** `web_fetch` refuses any host not in `WEB_ALLOWED_HOSTS`. Subdomains of allowed hosts pass; lookalikes (`example.com.evil.com`) do not. HTTP(S) only.
- **The protocol channel stays clean.** All logging goes to stderr — on a stdio MCP server, stdout belongs to JSON-RPC and a single stray `console.log` corrupts the stream.
- **Typed failure paths.** The persistence layer returns [neverthrow](https://github.com/supermacro/neverthrow) `Result` types instead of throwing; inputs are validated with [zod](https://zod.dev).
All of this is pinned down by **45 unit tests** targeting exactly these properties — traversal attempts, prefix bypasses, lookalike domains, protocol filtering, rank fusion, and registry dispatch — running in CI on Node 20 and 22.
## Architecture
Both transports consume one `ToolRegistry` — a single source of truth for tool definitions and dispatch, so the stdio and HTTP surfaces can never drift apart.
```mermaid
flowchart LR
CD[Claude Desktop] -- "stdio (JSON-RPC)" --> REG[ToolRegistry<br/>definitions + dispatch]
RC[Remote client] -- "HTTP :3333" --> REG
REG --> FS["FileService<br/>sandboxed notes"]
REG --> WS["WebService<br/>allowlisted fetch"]
REG --> TS["TaskService<br/>markdown store"]
REG --> RAG["RAGService<br/>keyword | vector | hybrid"]
RAG -- "FTS5 (bm25)" --> POOL["ConnectionPool"]
RAG -- "embeddings + cosine" --> VEC["VectorRAGService"]
VEC --> POOL
RAG -- "RRF fusion" --> RAG
POOL --> DB[("SQLite<br/>docs + chunks<br/>chunks_fts + chunk_vecs")]
```
```
src/
server.ts MCP entrypoint (SDK 1.x): wires services into the registry
tools/
registry.ts single source of truth: tool schemas + dispatch
transports/
http-transport.ts thin HTTP facade over the registry: /health, /tools, /call, /status
connectors/
file-service.ts sandboxed file access
web-service.ts allowlisted web fetching + HTML cleaning
task-service.ts markdown-backed task store
rag/
rag-service.ts search facade: keyword / vector / hybrid modes
fusion.ts Reciprocal Rank Fusion (pure, unit-tested)
sqlite-client.ts SQLite persistence: FTS5, chunking, migrations (neverthrow API)
vector-rag-service.ts vector search with OpenAI embeddings
embedding-service.ts embedding generation (text-embedding-3-small)
db/
connection-pool.ts SQLite connection pooling
```
## Quick start
```bash
git clone https://github.com/Galiusbro/ai-ops-hub.git && cd ai-ops-hub
npm install
cp .env.example .env # adjust paths and allowlist
npm run build
npm start # stdio only (for Claude Desktop)
npm run start:http # stdio + HTTP facade on :3333
```
The HTTP facade is opt-in (`--http` flag or `HTTP_ENABLED=1`) so that MCP clients can spawn multiple server instances without port clashes.
### Connect to Claude Desktop
```json
{
"mcpServers": {
"ai-ops-hub": {
"command": "node",
"args": ["/absolute/path/to/dist/server.js"],
"env": {
"NOTES_DIR": "/path/to/your/notes",
"RAG_DB_PATH": "/path/to/your/rag.db"
}
}
}
}
```
### Or talk to it over HTTP
```bash
curl http://localhost:3333/health
curl http://localhost:3333/tools
curl -X POST http://localhost:3333/call \
-H "Content-Type: application/json" \
-d '{"name":"rag_search","arguments":{"query":"postgres migration"}}'
```
### Configuration
| Variable | Default | Purpose |
|---|---|---|
| `NOTES_DIR` | `./notes` | Directory the file tools are sandboxed to |
| `TASKS_FILE` | `./tasks.md` | Markdown file behind the task tools |
| `RAG_DB_PATH` | `./data/rag.db` | SQLite database for the corpus |
| `WEB_ALLOWED_HOSTS` | `example.com,developer.mozilla.org` | Comma-separated allowlist for `web_fetch` |
| `HTTP_PORT` | `3333` | HTTP transport port |
| `OPENAI_API_KEY` | — | Enables vector + hybrid search (embeddings) |
## Development
```bash
npm run dev # run from source (tsx)
npm test # vitest unit suite
npm run type-check # tsc --noEmit
npm run lint
```
## Roadmap
- [x] MCP server over stdio + HTTP
- [x] Sandboxed file / web / task tools
- [x] SQLite FTS5 corpus with trigger-synced index
- [x] Unit tests for the security-critical paths + CI
- [x] Shared tool registry between the two transports
- [x] Vector search wired in: hybrid mode with Reciprocal Rank Fusion
- [x] `@modelcontextprotocol/sdk` 1.x
- [ ] Streamable HTTP transport from the SDK (replace the custom REST facade)
- [ ] Audit logging
- [ ] Local embedding backend as an alternative to OpenAI
## Why this exists
I built this to understand MCP from the inside — the protocol, the transports, and what it actually takes to hand an LLM safe access to a real machine. It grew into a working local-first assistant backend: the FTS5 corpus, the sandboxing, and the test suite are the parts I'd reuse in production.
## License
[MIT](LICENSE)
TDQS
Scored across 7 tools
Each tool has a clearly distinct purpose with no overlap: file operations, RAG operations, task management, and web fetching are all separate domains. The descriptions clearly differentiate them, making misselection unlikely.
Most tools follow a consistent verb_noun pattern (e.g., file_read, task_create, web_fetch), but 'rag_add_document' and 'rag_search' deviate slightly by using 'add' and 'search' as verbs instead of a uniform verb style. The naming is still readable and mostly predictable.
With 7 tools, this server is well-scoped for an AI Ops Hub, covering key areas like file handling, RAG, task management, and web operations. Each tool earns its place without feeling excessive or insufficient.
The tool surface covers core operations for file I/O, RAG, tasks, and web fetching, but there are minor gaps such as missing update/delete for tasks or document management in RAG. Agents can likely work around these with the provided tools.