meteor-wormhole
A cosmic bridge connecting Meteor methods to AI agents through MCP
meteor-wormhole is a server-only Meteor 3 package that exposes your Meteor.methods as MCP (Model Context Protocol) tools so AI agents (Claude, GPT, Copilot, etc.) can discover and invoke them. It also optionally exposes them as REST endpoints with an auto-generated OpenAPI 3.1 spec and Swagger UI.
Features
- All-In Mode — Automatically expose every Meteor method as an MCP tool
- Opt-In Mode — Selectively expose methods with rich metadata and schemas
- Streamable HTTP Transport — MCP server embedded in your Meteor app via
WebApp - REST API — Optional REST endpoints for every exposed method (
POST /api/<method>) - OpenAPI 3.1 Spec — Auto-generated from your method registry
- Swagger UI — Built-in API docs browser at
/api/docs - API Key Authentication — Optional bearer token auth for MCP and REST endpoints
- Input & Output Schemas — Define JSON Schema for method parameters and return values
- Smart Defaults — Auto-excludes internal Meteor, DDP, and Accounts methods
Requirements
- Meteor 3.4+
- Server-only (this package does not run on the client)
Install
meteor add wreiske:meteor-wormhole
Quick Start
1import { Wormhole } from 'meteor/wreiske:meteor-wormhole'; 2 3// Expose all Meteor methods as MCP tools (default mode) 4Wormhole.init();
That's it. Every method defined via Meteor.methods() is now available to MCP clients at http://localhost:3000/mcp.
Configuration
Wormhole.init(options)
Initialize the MCP bridge and optionally the REST API. Must be called once at server startup.
1Wormhole.init({ 2 mode: 'all', 3 path: '/mcp', 4 name: 'my-app', 5 version: '1.0.0', 6 apiKey: 'my-secret-key', 7 exclude: [/^admin\./, 'internalMethod'], 8 rest: { 9 enabled: true, 10 path: '/api', 11 docs: true, 12 }, 13});
Options
| Option | Type | Default | Description |
|---|---|---|---|
mode | 'all' | 'opt-in' | 'all' | 'all' auto-registers all methods. 'opt-in' requires explicit Wormhole.expose() calls. |
path | string | '/mcp' | HTTP path where the MCP server listens. |
name | string | 'meteor-wormhole' | Display name for the MCP server. |
version | string | '1.0.0' | Semantic version of the MCP server. |
apiKey | string | null | null | Bearer token for authentication. If set, all requests must include Authorization: Bearer <apiKey>. |
exclude | (string | RegExp)[] | [] | Method name patterns to exclude in 'all' mode. Supports exact strings and RegExp. |
rest | object | boolean | false | REST API configuration (see below). Pass true to enable with defaults. |
REST Options (options.rest)
| Option | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Enable the REST API (opt-in). |
path | string | '/api' | Base path for REST endpoints. |
docs | boolean | true | Serve Swagger UI at <path>/docs. |
apiKey | string | null | (inherits from parent) | Override the API key for REST only. |
Default Exclusions (All Mode)
In 'all' mode, the following methods are always excluded automatically:
- Methods starting with
/(DDP internal) - Methods starting with
_(private convention) - Accounts methods:
login,logout,getNewToken,removeOtherTokens,configureLoginService,changePassword,forgotPassword,resetPassword,verifyEmail,createUser,ATRemoveToken,ATCreateUserServer
Use the exclude option to add your own patterns on top of these defaults.
Exposing Methods
Wormhole.expose(methodName, options)
Explicitly register a Meteor method as an MCP tool. Required in 'opt-in' mode, but also works in 'all' mode to enrich auto-registered methods with descriptions and schemas.
1Wormhole.expose('todos.add', { 2 description: 'Add a new todo item to the list', 3 inputSchema: { 4 type: 'object', 5 properties: { 6 title: { type: 'string', description: 'The todo title' }, 7 priority: { 8 type: 'number', 9 description: 'Priority level (1=low, 5=high)', 10 }, 11 }, 12 required: ['title'], 13 }, 14 outputSchema: { 15 type: 'object', 16 properties: { 17 _id: { type: 'string' }, 18 title: { type: 'string' }, 19 done: { type: 'boolean' }, 20 }, 21 }, 22});
Parameters
| Parameter | Type | Description |
|---|---|---|
methodName | string | The exact Meteor method name (e.g., 'todos.add'). |
options.description | string | Human-readable description. Used in MCP tool listings and OpenAPI spec. |
options.inputSchema | object | JSON Schema defining expected input parameters. |
options.outputSchema | object | JSON Schema for the return value. Used in OpenAPI spec. REST wraps this in { result: <value> }. |
Wormhole.unexpose(methodName)
Remove a method from MCP/REST exposure.
1Wormhole.unexpose('todos.add'); // returns true if it was registered
Tool Name Mapping
Method names are sanitized for MCP and REST: special characters (., -, /) are replaced with underscores.
| Meteor Method | MCP Tool / REST Route |
|---|---|
todos.add | todos_add |
user-service.getUser | user_service_getUser |
MCP Transport
The MCP server uses Streamable HTTP transport, mounted at the configured path (default /mcp).
Connecting an MCP Client
Configure your MCP client (Claude Desktop, VS Code, etc.) to connect:
1{ 2 "mcpServers": { 3 "my-meteor-app": { 4 "type": "streamablehttp", 5 "url": "http://localhost:3000/mcp" 6 } 7 } 8}
With API key authentication:
1{ 2 "mcpServers": { 3 "my-meteor-app": { 4 "type": "streamablehttp", 5 "url": "http://localhost:3000/mcp", 6 "headers": { 7 "Authorization": "Bearer my-secret-key" 8 } 9 } 10 } 11}
How It Works
- POST
/mcp— Send JSON-RPC 2.0 requests. First request creates a session; subsequent requests include themcp-session-idheader. - GET
/mcp— Long-poll/stream events for an existing session. - DELETE
/mcp— Close a session.
Tool Invocation
When an MCP client calls a tool:
- With
inputSchema: The entire params object is passed as a single argument to the Meteor method. - Without
inputSchema: Theargsarray is spread as positional arguments.
Errors from Meteor methods are returned as MCP error content:
1{ 2 "content": [{ "type": "text", "text": "not-found: Todo not found" }], 3 "isError": true 4}
REST API
Enable the REST API to call exposed methods via standard HTTP.
1Wormhole.init({ 2 mode: 'all', 3 rest: { enabled: true }, 4});
Endpoints
| Method | Path | Description | Auth Required |
|---|---|---|---|
POST | /api/<tool_name> | Invoke a method | Yes (if apiKey set) |
GET | /api/ | List all endpoints | Yes (if apiKey set) |
GET | /api/openapi.json | OpenAPI 3.1 spec | No |
GET | /api/docs | Swagger UI | No |
Calling a Method
curl -X POST http://localhost:3000/api/todos_add \ -H 'Content-Type: application/json' \ -d '{"title": "Buy milk", "priority": 3}'
Success response:
1{ 2 "result": { "_id": "abc123", "title": "Buy milk", "done": false } 3}
Error response:
1{ 2 "error": "not-found", 3 "reason": "Todo not found", 4 "message": "Todo not found [not-found]" 5}
Request Limits
- Max body size: 1 MB
- Content-Type: Must be
application/json
Authentication
When apiKey is set, all MCP and REST requests (except Swagger UI and OpenAPI spec) require a bearer token:
Authorization: Bearer <your-api-key>
Unauthorized requests receive a 401 response.
Additional API
Wormhole.registry
Read-only access to the internal method registry.
1Wormhole.registry.names(); // ['todos.add', 'todos.list', ...] 2Wormhole.registry.size(); // 5 3Wormhole.registry.has('todos.add'); // true 4Wormhole.registry.get('todos.add'); 5// { description: '...', inputSchema: {...}, outputSchema: {...}, registeredAt: 1709... }
Wormhole.initialized
boolean — Whether Wormhole.init() has been called.
Wormhole.options
Returns a copy of the resolved configuration options.
generateOpenApiSpec(registry, options)
Generate an OpenAPI 3.1.0 spec object from a registry. Exported for advanced use cases.
1import { generateOpenApiSpec } from 'meteor/wreiske:meteor-wormhole'; 2 3const spec = generateOpenApiSpec(Wormhole.registry, { 4 name: 'My API', 5 version: '2.0.0', 6 restPath: '/api', 7 apiKey: 'secret', 8 description: 'My custom API', 9});
Full Example
1import { Meteor } from 'meteor/meteor'; 2import { Wormhole } from 'meteor/wreiske:meteor-wormhole'; 3 4// Initialize with all features 5Wormhole.init({ 6 mode: 'all', 7 path: '/mcp', 8 name: 'my-app', 9 version: '1.0.0', 10 apiKey: process.env.MCP_API_KEY || null, 11 exclude: [/^admin\./], 12 rest: { 13 enabled: true, 14 path: '/api', 15 docs: true, 16 }, 17}); 18 19// Enrich specific methods with schemas 20Wormhole.expose('todos.add', { 21 description: 'Add a new todo item', 22 inputSchema: { 23 type: 'object', 24 properties: { 25 title: { type: 'string' }, 26 }, 27 required: ['title'], 28 }, 29 outputSchema: { 30 type: 'object', 31 properties: { 32 _id: { type: 'string' }, 33 title: { type: 'string' }, 34 done: { type: 'boolean' }, 35 }, 36 }, 37}); 38 39// Define the actual Meteor method 40Meteor.methods({ 41 'todos.add'({ title }) { 42 const todoId = TodosCollection.insertAsync({ title, done: false }); 43 return TodosCollection.findOneAsync(todoId); 44 }, 45});
Now available:
- MCP:
http://localhost:3000/mcp— AI agents can discover and calltodos_add - REST:
POST http://localhost:3000/api/todos_add— HTTP clients can call the method - Docs:
http://localhost:3000/api/docs— Interactive Swagger UI - Spec:
http://localhost:3000/api/openapi.json— OpenAPI 3.1 spec
Testing
# Run package unit tests (Tinytest) cd apps/test-app && meteor test-packages ../../packages/meteor-wormhole # Run MCP integration test client meteor npm run test:client
License
MIT