mcp-catalogue
by Aidan-Kay
README.md
# mcp-catalogue
> Browse-first MCP middleware — an LLM-friendly catalogue for discovering and invoking MCP tools across multiple services.
AI agents (Open WebUI, n8n, etc.) use this as a single MCP endpoint to **browse**, **inspect**, and **call** tools from many upstream MCP servers — without flooding their context with every tool schema upfront.
## How It Works
Instead of connecting every MCP server directly (and loading all their tool schemas at session start), agents connect to **one** catalogue server and discover tools on demand:
```
browse_services → [{id: "todoist", name: "Todoist"}, {id: "outlook", ...}]
browse_tools("todoist") → ["todoist__get-task", "todoist__create-task", ...]
get_schemas(["todoist__get-task"]) → [full input schema]
call_tool("todoist__get-task", {id: "123"}) → result
```
## Quick Start
### Prerequisites
- Node.js 22+
- An MCP server to point it at (or use the included smoke test)
### Install & Run
```bash
# Install dependencies
npm install
# Copy a config (or create your own)
cp mcp-catalogue.dev.yaml mcp-catalogue.yaml
# Start in dev mode (with hot reload)
npm run dev
# Or with a custom config path and verbose logging
npx tsx src/index.ts --config ./mcp-catalogue.dev.yaml --verbose
```
### Verify It's Running
```bash
# Health check
curl http://localhost:8050/health
# Quick smoke test
node smoke-test.mjs
```
## Configuration
Create a `mcp-catalogue.yaml` file:
```yaml
port: 8050
auth:
enabled: false # Set to true and provide a token in production
token: ""
allowedOrigins: # Optional — restrict CORS to these origins when auth is on
- https://openwebui.local
connectors:
httpReuseIdleTimeoutSeconds: 300 # Reap idle upstream HTTP sessions after N seconds
sources:
- id: todoist
name: Todoist
description: Task and project management
transport: http
url: http://todoist-mcp:8081/mcp
filter: ["*"] # Glob patterns — only index matching tools
- id: outlook
name: Outlook
description: Email and calendar
transport: stdio
command: npx
args: ["-y", "@softeria/ms-365-mcp-server"]
env:
API_KEY: "your-key"
```
### Config Reference
| Field | Description |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `port` | HTTP port for the MCP endpoint (default: 8050) |
| `auth.enabled` | Require `Authorization: Bearer <token>` header |
| `auth.token` | Static bearer token (override via `MCP_CATALOGUE_AUTH_TOKEN` env var) |
| `auth.allowedOrigins` | Optional list of origins allowed via CORS when auth is enabled. If omitted, the request `Origin` is reflected back |
| `connectors.httpReuseIdleTimeoutSeconds` | Idle timeout before a cached upstream HTTP session is reaped (default: 300) |
| `sources[].id` | Unique identifier for the source (used in namespaced tool names) |
| `sources[].transport` | `"http"` for Streamable HTTP, `"stdio"` for subprocess |
| `sources[].url` | Upstream MCP server URL (required for HTTP transport) |
| `sources[].command` | Executable to spawn (required for stdio transport) |
| `sources[].filter` | Optional glob patterns to curate which tools are indexed |
Sample configs are included:
- [`mcp-catalogue.dev.yaml`](./mcp-catalogue.dev.yaml) — LAN IPs for local development
- [`mcp-catalogue.prod.yaml`](./mcp-catalogue.prod.yaml) — container-internal DNS for Docker
## Docker
```bash
# Build
npm run docker:build
# Run
docker run -d \
--name mcp-catalogue \
-p 8050:8050 \
-v ./mcp-catalogue.yaml:/app/mcp-catalogue.yaml \
-e MCP_CATALOGUE_AUTH_TOKEN=your-token \
mcp-catalogue
```
Or use the provided Dockerfile directly:
```bash
docker build -t mcp-catalogue .
```
## MCP Tools
The catalogue exposes these tools to connected AI agents:
| Tool | What it does |
| ----------------- | ---------------------------------------------------------------------- |
| `browse_services` | List all available upstream services with descriptions and tool counts |
| `browse_tools` | List all tools for a specific service (namespaced names) |
| `get_schemas` | Get full input schemas for one or more tools in bulk |
| `call_tool` | Call a tool on an upstream service (passes through the result) |
| `index` | Diagnostic — shows index summary, source availability, and error info |
## Architecture
```
AI Agent ──Streamable HTTP──▶ mcp-catalogue ──HTTP/stdio──▶ todoist, outlook, ...
│
In-memory index
Session management
```
- **Transport**: MCP Streamable HTTP (2025-11-05)
- **Auth**: Optional bearer token, with optional CORS origin allowlist
- **Health**: `GET /health` endpoint for monitoring (Uptime Kuma, etc.)
- **HTTP connection reuse**: keep-alive sessions per source, reaped after an idle timeout
## Project Structure
```
src/
index.ts Entry point with CLI args
config.ts YAML loader with Zod validation
types.ts Shared types and interfaces
logger.ts Structured logger
namespace.ts Tool name namespacing (<sourceId>__<toolName>)
indexer.ts Startup index — fetches tools/list from all sources
catalogue-server.ts MCP server — tool definitions and request handling
sources/
http-source.ts HTTP transport client (Streamable HTTP)
stdio-source.ts Stdio transport client (subprocess, JSON-RPC)
```
## Scripts
| Command | Description |
| ---------------------- | ----------------------------------- |
| `npm run dev` | Run with hot reload via `tsx watch` |
| `npm start` | Run without watch |
| `npm run build` | Compile TypeScript to `dist/` |
| `npm run docker:build` | Build Docker image |
| `npm run docker:run` | Run Docker container |
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing