Skip to main content
Glama
Aidan-Kay
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                |