Skip to main content

MCP (Model Context Protocol)

MCP lets Crewlabs Agent connect to external tool servers so the agent can use tools that live outside Crewlabs itself — GitHub, databases, file systems, browser stacks, internal APIs, and more.

If you have ever wanted Crewlabs to use a tool that already exists somewhere else, MCP is usually the cleanest way to do it.

What MCP gives you

  • Access to external tool ecosystems without writing a native Crewlabs tool first
  • Local stdio servers and remote HTTP MCP servers in the same config
  • Automatic tool discovery and registration at startup
  • Utility wrappers for MCP resources and prompts when supported by the server
  • Per-server filtering so you can expose only the MCP tools you actually want Crewlabs to see

Quick start

  1. Install MCP support (already included if you used the standard install script):
cd ~/.crewlabs/crewlabs-agent
uv pip install -e ".[mcp]"
  1. Add an MCP server to ~/.crewlabs/config.yaml:
mcp_servers:
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
  1. Start Crewlabs:
crewlabs chat
  1. Ask Crewlabs to use the MCP-backed capability.

For example:

List the files in /home/user/projects and summarize the repo structure.

Crewlabs will discover the MCP server's tools and use them like any other tool.

Two kinds of MCP servers

Stdio servers

Stdio servers run as local subprocesses and talk over stdin/stdout.

mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "***"

Use stdio servers when:

  • the server is installed locally
  • you want low-latency access to local resources
  • you are following MCP server docs that show command, args, and env

HTTP servers

HTTP MCP servers are remote endpoints Crewlabs connects to directly.

mcp_servers:
remote_api:
url: "https://mcp.example.com/mcp"
headers:
Authorization: "Bearer ***"

Use HTTP servers when:

  • the MCP server is hosted elsewhere
  • your organization exposes internal MCP endpoints
  • you do not want Crewlabs spawning a local subprocess for that integration

OAuth-authenticated HTTP servers

Most hosted MCP servers (Linear, Sentry, Atlassian, Asana, Figma, Stripe, …) require OAuth 2.1 instead of a static bearer token. Set auth: oauth and Crewlabs handles discovery, dynamic client registration, PKCE, token exchange, refresh, and step-up auth via the MCP Python SDK.

mcp_servers:
linear:
url: "https://mcp.linear.app/mcp"
auth: oauth

On first connect, Crewlabs prints an authorize URL, opens your browser when possible, and waits for the OAuth callback on a local loopback port. Tokens are cached at ~/.crewlabs/mcp-tokens/<server>.json with 0o600 perms; subsequent runs reuse them silently until refresh fails.

Remote / headless hosts. When Crewlabs runs on a different machine than your browser, the loopback callback can't reach your laptop. Two ways to complete the flow:

  • Paste-back (no setup): on an interactive terminal Crewlabs prints "Or paste the redirect URL here…" alongside the authorize URL. Open the URL in your browser, approve, copy the full URL the browser ends up on (the redirect will show a connection error — that's expected), paste it at the prompt. Bare ?code=…&state=… query strings work too.
  • SSH port forward: ssh -N -L <port>:127.0.0.1:<port> user@host in a separate terminal, then let the redirect flow normally.

See OAuth over SSH / Remote Hosts for the full walkthrough, including DCR-less servers (e.g. Slack), pre-registered client_id/client_secret, scope customization, and re-auth via crewlabs mcp login <server>.

Pitfall — config auto-reload race. When you edit ~/.crewlabs/config.yaml from inside a running Crewlabs session, the CLI auto-reloads MCP connections with a 30s timeout. That's not enough for an interactive OAuth flow. Add the entry, then run crewlabs mcp login <server> from a fresh terminal — it waits the full 5 minutes for you to complete auth.

Basic configuration reference

Crewlabs reads MCP config from ~/.crewlabs/config.yaml under mcp_servers.

Common keys

KeyTypeMeaning
commandstringExecutable for a stdio MCP server
argslistArguments for the stdio server
envmappingEnvironment variables passed to the stdio server
urlstringHTTP MCP endpoint
headersmappingHTTP headers for remote servers
timeoutnumberTool call timeout
connect_timeoutnumberInitial connection timeout
enabledboolIf false, Crewlabs skips the server entirely
supports_parallel_tool_callsboolIf true, tools from this server may run concurrently
toolsmappingPer-server tool filtering and utility policy

Minimal stdio example

mcp_servers:
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]

Minimal HTTP example

mcp_servers:
company_api:
url: "https://mcp.internal.example.com"
headers:
Authorization: "Bearer ***"

Built-in presets

For well-known MCP servers, crewlabs mcp add accepts a --preset flag that fills in the transport details so you don't have to look up the command and args. The preset only supplies defaults — anything else (env vars, headers, filtering) you pass on the same command line still wins.

PresetWhat it wires up
codexThe Codex CLI's MCP server (codex mcp-server over stdio). Requires the codex CLI on PATH.
# Add Codex CLI as an MCP server in one line
crewlabs mcp add codex --preset codex

That writes the equivalent of:

mcp_servers:
codex:
command: "codex"
args: ["mcp-server"]

You can pick any local name (crewlabs mcp add my-codex --preset codex is fine); the preset only provides the command/args defaults.

How Crewlabs registers MCP tools

Crewlabs prefixes MCP tools so they do not collide with built-in names:

mcp_<server_name>_<tool_name>

Examples:

ServerMCP toolRegistered name
filesystemread_filemcp_filesystem_read_file
githubcreate-issuemcp_github_create_issue
my-apiquery.datamcp_my_api_query_data

In practice, you usually do not need to call the prefixed name manually — Crewlabs sees the tool and chooses it during normal reasoning.

MCP utility tools

When supported, Crewlabs also registers utility tools around MCP resources and prompts:

  • list_resources
  • read_resource
  • list_prompts
  • get_prompt

These are registered per server with the same prefix pattern, for example:

  • mcp_github_list_resources
  • mcp_github_get_prompt

Important

These utility tools are now capability-aware:

  • Crewlabs only registers resource utilities if the MCP session actually supports resource operations
  • Crewlabs only registers prompt utilities if the MCP session actually supports prompt operations

So a server that exposes callable tools but no resources/prompts will not get those extra wrappers.

Per-server filtering

You can control which tools each MCP server contributes to Crewlabs, allowing fine-grained management of your tool namespace.

Disable a server entirely

mcp_servers:
legacy:
url: "https://mcp.legacy.internal"
enabled: false

If enabled: false, Crewlabs skips the server completely and does not even attempt a connection.

Whitelist server tools

mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
tools:
include: [create_issue, list_issues]

Only those MCP server tools are registered.

Blacklist server tools

mcp_servers:
stripe:
url: "https://mcp.stripe.com"
tools:
exclude: [delete_customer]

All server tools are registered except the excluded ones.

Precedence rule

If both are present:

tools:
include: [create_issue]
exclude: [create_issue, delete_issue]

include wins.

Filter utility tools too

You can also separately disable Crewlabs-added utility wrappers:

mcp_servers:
docs:
url: "https://mcp.docs.example.com"
tools:
prompts: false
resources: false

That means:

  • tools.resources: false disables list_resources and read_resource
  • tools.prompts: false disables list_prompts and get_prompt

Full example

mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
tools:
include: [create_issue, list_issues, search_code]
prompts: false

stripe:
url: "https://mcp.stripe.com"
headers:
Authorization: "Bearer ***"
tools:
exclude: [delete_customer]
resources: false

legacy:
url: "https://mcp.legacy.internal"
enabled: false

What happens if everything is filtered out?

If your config filters out all callable tools and disables or omits all supported utilities, Crewlabs does not create an empty runtime MCP toolset for that server.

That keeps the tool list clean.

Runtime behavior

Discovery time

Crewlabs discovers MCP servers at startup and registers their tools into the normal tool registry.

Dynamic Tool Discovery

MCP servers can notify Crewlabs when their available tools change at runtime by sending a notifications/tools/list_changed notification. When Crewlabs receives this notification, it automatically re-fetches the server's tool list and updates the registry — no manual /reload-mcp required.

This is useful for MCP servers whose capabilities change dynamically (e.g. a server that adds tools when a new database schema is loaded, or removes tools when a service goes offline).

The refresh is lock-protected so rapid-fire notifications from the same server don't cause overlapping refreshes. Prompt and resource change notifications (prompts/list_changed, resources/list_changed) are received but not yet acted on.

Reloading

If you change MCP config, use:

/reload-mcp

This reloads MCP servers from config and refreshes the available tool list. For runtime tool changes pushed by the server itself, see Dynamic Tool Discovery above.

Toolsets

Each configured MCP server also creates a runtime toolset when it contributes at least one registered tool:

mcp-<server>

That makes MCP servers easier to reason about at the toolset level.

Security model

Stdio env filtering

For stdio servers, Crewlabs does not blindly pass your full shell environment.

Only explicitly configured env plus a safe baseline are passed through. This reduces accidental secret leakage.

Config-level exposure control

The new filtering support is also a security control:

  • disable dangerous tools you do not want the model to see
  • expose only a minimal whitelist for a sensitive server
  • disable resource/prompt wrappers when you do not want that surface exposed

Example use cases

GitHub server with a minimal issue-management surface

mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
tools:
include: [list_issues, create_issue, update_issue]
prompts: false
resources: false

Use it like:

Show me open issues labeled bug, then draft a new issue for the flaky MCP reconnection behavior.

Stripe server with dangerous actions removed

mcp_servers:
stripe:
url: "https://mcp.stripe.com"
headers:
Authorization: "Bearer ***"
tools:
exclude: [delete_customer, refund_payment]

Use it like:

Look up the last 10 failed payments and summarize common failure reasons.

Filesystem server for a single project root

mcp_servers:
project_fs:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/my-project"]

Use it like:

Inspect the project root and explain the directory layout.

Troubleshooting

MCP server not connecting

Check:

# Verify MCP deps are installed (already included in standard install)
cd ~/.crewlabs/crewlabs-agent && uv pip install -e ".[mcp]"

node --version
npx --version

Then verify your config and restart Crewlabs.

Tools not appearing

Possible causes:

  • the server failed to connect
  • discovery failed
  • your filter config excluded the tools
  • the utility capability does not exist on that server
  • the server is disabled with enabled: false

If you are intentionally filtering, this is expected.

Why didn't resource or prompt utilities appear?

Because Crewlabs now only registers those wrappers when both are true:

  1. your config allows them
  2. the server session actually supports the capability

This is intentional and keeps the tool list honest.

Parallel Tool Calls

By default, MCP tools run sequentially — one at a time. If your MCP server exposes tools that are safe to run concurrently (e.g. read-only queries, independent API calls), you can opt-in to parallel execution:

mcp_servers:
docs:
command: "docs-server"
supports_parallel_tool_calls: true

When supports_parallel_tool_calls is true, Crewlabs may execute multiple tools from that server at the same time within a single tool-call batch, just like it does for built-in read-only tools (web_search, read_file, etc.).

caution

Only enable parallel calls for MCP servers whose tools are safe to run at the same time. If tools read and write shared state, files, databases, or external resources, review the read/write race conditions before enabling this setting.

MCP Sampling Support

MCP servers can request LLM inference from Crewlabs via the sampling/createMessage protocol. This allows an MCP server to ask Crewlabs to generate text on its behalf — useful for servers that need LLM capabilities but don't have their own model access.

Sampling is enabled by default for all MCP servers (when the MCP SDK supports it). Configure it per-server under the sampling key:

mcp_servers:
my_server:
command: "my-mcp-server"
sampling:
enabled: true # Enable sampling (default: true)
model: "openai/gpt-4o" # Override model for sampling requests (optional)
max_tokens_cap: 4096 # Max tokens per sampling response (default: 4096)
timeout: 30 # Timeout in seconds per request (default: 30)
max_rpm: 10 # Rate limit: max requests per minute (default: 10)
max_tool_rounds: 5 # Max tool-use rounds in sampling loops (default: 5)
allowed_models: [] # Allowlist of model names the server may request (empty = any)
log_level: "info" # Audit log level: debug, info, or warning (default: info)

The sampling handler includes a sliding-window rate limiter, per-request timeouts, and tool-loop depth limits to prevent runaway usage. Metrics (request count, errors, tokens used) are tracked per server instance.

To disable sampling for a specific server:

mcp_servers:
untrusted_server:
url: "https://mcp.example.com"
sampling:
enabled: false

Running Crewlabs as an MCP server

In addition to connecting to MCP servers, Crewlabs can also be an MCP server. This lets other MCP-capable agents (Claude Code, Cursor, Codex, or any MCP client) use Crewlabs's messaging capabilities — list conversations, read message history, and send messages across all your connected platforms.

When to use this

  • You want Claude Code, Cursor, or another coding agent to send and read Telegram/Discord/Slack messages through Crewlabs
  • You want a single MCP server that bridges to all of Crewlabs's connected messaging platforms at once
  • You already have a running Crewlabs gateway with connected platforms

Quick start

crewlabs mcp serve

This starts a stdio MCP server. The MCP client (not you) manages the process lifecycle.

MCP client configuration

Add Crewlabs to your MCP client config. For example, in Claude Code's ~/.claude/claude_desktop_config.json:

{
"mcpServers": {
"crewlabs": {
"command": "crewlabs",
"args": ["mcp", "serve"]
}
}
}

Or if you installed Crewlabs in a specific location:

{
"mcpServers": {
"crewlabs": {
"command": "/home/user/.crewlabs/crewlabs-agent/venv/bin/crewlabs",
"args": ["mcp", "serve"]
}
}
}

Available tools

The MCP server exposes 10 tools, matching OpenClaw's channel bridge surface plus a Crewlabs-specific channel browser:

ToolDescription
conversations_listList active messaging conversations. Filter by platform or search by name.
conversation_getGet detailed info about one conversation by session key.
messages_readRead recent message history for a conversation.
attachments_fetchExtract non-text attachments (images, media) from a specific message.
events_pollPoll for new conversation events since a cursor position.
events_waitLong-poll / block until the next event arrives (near-real-time).
messages_sendSend a message through a platform (e.g. telegram:123456, discord:#general).
channels_listList available messaging targets across all platforms.
permissions_list_openList pending approval requests observed during this bridge session.
permissions_respondAllow or deny a pending approval request.

Event system

The MCP server includes a live event bridge that polls Crewlabs's session database for new messages. This gives MCP clients near-real-time awareness of incoming conversations:

# Poll for new events (non-blocking)
events_poll(after_cursor=0)

# Wait for next event (blocks up to timeout)
events_wait(after_cursor=42, timeout_ms=30000)

Event types: message, approval_requested, approval_resolved

The event queue is in-memory and starts when the bridge connects. Older messages are available through messages_read.

Options

crewlabs mcp serve              # Normal mode
crewlabs mcp serve --verbose # Debug logging on stderr

How it works

The MCP server reads conversation data directly from Crewlabs's session store (~/.crewlabs/sessions/sessions.json and the SQLite database). A background thread polls the database for new messages and maintains an in-memory event queue. For sending messages, it uses the same send_message infrastructure as the Crewlabs agent itself.

The gateway does NOT need to be running for read operations (listing conversations, reading history, polling events). It DOES need to be running for send operations, since the platform adapters need active connections.

Current limits

  • The embedded crewlabs mcp serve exposes a stdio-only MCP server today. If you need an HTTP MCP server, run a separate adapter — or, much more commonly, use the MCP client side of Crewlabs, which already speaks both stdio and HTTP (url + headers in mcp_servers.yaml / config.yaml; see HTTP servers above).
  • Event polling at ~200ms intervals via mtime-optimized DB polling (skips work when files are unchanged)
  • No claude/channel push notification protocol yet
  • Text-only sends (no media/attachment sending through messages_send)