Tools (MCP)
Give agents superpowers with MCP servers
Tools let agents interact with the world - search the web, read files, query databases, call APIs. MUXI uses the Model Context Protocol (MCP) for tool integration.
Add Your First Tool
-
Create the MCP config file
mkdir -p mcpCreate
mcp/web-search.afs:schema: "1.0.0" id: web-search description: Brave web search type: command command: npx args: ["-y", "@modelcontextprotocol/server-brave-search"] auth: type: env BRAVE_API_KEY: "${{ secrets.BRAVE_API_KEY }}" -
Set the secret
muxi secrets set BRAVE_API_KEY -
Test it
All agents in the formation automatically have access to MCP servers defined in
mcp/*.afs.muxi dev # Then: "Search for recent AI news"
MCP Server Types
Command-based (Local)
Runs a local process:
# mcp/local-tool.afs
schema: "1.0.0"
id: local-tool
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-brave-search"]
timeout_seconds: 30
auth:
type: env
API_KEY: "${{ secrets.API_KEY }}"
HTTP-based (Remote)
Calls a remote MCP server:
# mcp/remote-tool.afs
schema: "1.0.0"
id: remote-tool
type: http
endpoint: "https://api.example.com/mcp"
timeout_seconds: 30
auth:
type: bearer
token: "${{ secrets.API_TOKEN }}"
MCP File Fields
Required Fields
| Field | Type | Description |
|---|---|---|
schema
| string | Schema version ("1.0.0")
|
id
| string | Unique identifier |
description
| string | What this MCP server does |
type
| enum | command (stdio) or http
|
Command Type Fields
| Field | Type | Description |
|---|---|---|
command
| string | Command to execute (npx, python, node)
|
args
| array | Command arguments |
env
| object | Environment variables for the command |
cwd
| string | Working directory for the command |
HTTP Type Fields
| Field | Type | Description |
|---|---|---|
endpoint
| string | HTTP endpoint URL |
protocol
| enum | http or https
|
method
| enum | GET, POST, PUT, DELETE (default: POST)
|
headers
| object | Additional HTTP headers |
Common Optional Fields
| Field | Type | Default | Description |
|---|---|---|---|
timeout_seconds
| int | - | Request timeout |
retry_attempts
| int | - | Number of retries (0-10) |
connection_ttl
| int | - | Per-server idle TTL override (seconds). See Connection Lifecycle. |
parameters
| object | - | Default tool arguments injected into every call. See Default Parameters. |
Capabilities
capabilities:
tools: ["list_repos", "create_issue"] # Tool names provided
streaming: false # Supports streaming?
async: false # Supports async?
Health Check (HTTP only)
health_check:
enabled: true
endpoint: "/health"
interval_seconds: 60
timeout_seconds: 5
Rate Limiting
rate_limiting:
requests_per_minute: 60
requests_per_hour: 1000
burst_limit: 10
Metadata
metadata:
version: "1.0.0"
author: "Your Team"
homepage: "https://example.com"
documentation: "https://docs.example.com"
tags: ["search", "web"]
Popular MCP Servers
| Server | What It Does | Type |
|---|---|---|
@modelcontextprotocol/server-brave-search
| Web search | command |
@modelcontextprotocol/server-filesystem
| Read/write files | command |
@modelcontextprotocol/server-github
| GitHub API | command |
@modelcontextprotocol/server-postgres
| PostgreSQL | command |
@modelcontextprotocol/server-sqlite
| SQLite | command |
Configuration Examples
Web Search
# mcp/web-search.afs
schema: "1.0.0"
id: web-search
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-brave-search"]
auth:
type: env
BRAVE_API_KEY: "${{ secrets.BRAVE_API_KEY }}"
File System
# mcp/filesystem.afs
schema: "1.0.0"
id: filesystem
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "./documents", "./projects"]
timeout_seconds: 30
Always restrict file access to specific directories in the args.
Database
# mcp/postgres.afs
schema: "1.0.0"
id: database
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-postgres"]
auth:
type: env
DATABASE_URL: "${{ secrets.DATABASE_URL }}"
# mcp/sqlite.afs
schema: "1.0.0"
id: database
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-sqlite", "--db", "./data/app.db"]
GitHub
# mcp/github.afs
schema: "1.0.0"
id: github
type: command
command: npx
args: ["-y", "@modelcontextprotocol/server-github"]
auth:
type: env
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
Formation-Level MCP Settings
Configure global MCP behavior in formation.afs:
mcp:
connection_ttl: 300 # Idle connection TTL in seconds (default: 300)
default_timeout_seconds: 30
max_tool_iterations: 10
max_tool_calls: 50
max_repeated_errors: 3
Connection Lifecycle
MCP connections follow a lazy-persistent lifecycle:
- Discovery: On formation startup, the runtime connects to each MCP server, discovers its tools, then disconnects.
- First tool call: The runtime reconnects and keeps the connection alive.
- Subsequent calls: Reuse the existing connection. Each call resets the idle timer.
- Idle timeout: A background reaper closes connections that have been idle longer than
connection_ttl. - Next call after timeout: The runtime reconnects transparently.
A frequently-used server stays connected indefinitely. A rarely-used server closes after the TTL and reconnects on demand.
Set connection_ttl: 0 to disable keep-alive and use ephemeral connections (connect/execute/disconnect per call).
Per-server override
Override the global TTL for individual servers:
# mcp/ms365.afs
schema: "1.0.0"
id: ms365
type: http
endpoint: "https://ms365.example.com/mcp"
connection_ttl: 600 # Keep this server alive longer (10 minutes)
# mcp/one-off-tool.afs
schema: "1.0.0"
id: one-off-tool
type: http
endpoint: "https://tool.example.com/mcp"
connection_ttl: 0 # Always disconnect after each call
Default Parameters
MCP servers support an optional parameters field: a flat key-value map injected into every tool call on that server. Use this for infrastructure constants that should never be left to LLM inference -- org-level drive IDs, tenant IDs, project keys, etc.
# mcp/ms365.afs
schema: "1.0.0"
id: ms365-mcp
type: http
endpoint: "https://mcp.example.com/ms365"
parameters:
driveId: "${{ secrets.ORG_DRIVE_ID }}"
siteId: "contoso.sharepoint.com,guid1,guid2"
How it works:
- Parameters are injected at execution time, before the LLM planner runs
- If a tool call already provides a value for the same key, the explicit value wins
- Values support
${{ secrets.X }}and${{ user.credentials.X }}interpolation - Must be a flat map with scalar values (strings, numbers, booleans) -- no nested objects
When to use:
- Fixed org-wide identifiers (drive IDs, site IDs, tenant IDs)
- API version strings
- Any value that is constant across all tool calls for a given server
Move constants out of agent prompts. If your agent's system message contains a hardcoded ID just so the LLM can pass it to tools, put it in parameters instead. The LLM never needs to see or propagate infrastructure config.
Works on both formation-level MCP servers (mcp/*.afs) and agent-level MCP servers (mcp_servers in agent .afs files).
Agent-Specific Tools
Prefer per-agent tools. Defining tools per-agent improves Overlord's agent selection and each agent's tool selection. Use global MCP servers only for tools ALL agents need.
Formation-level MCP servers (in mcp/*.afs, declared in mcp.servers) are available to all agents. Agents can reference them by string ID, or define agent-private tools inline:
# agents/researcher.afs
schema: "1.0.0"
id: researcher
name: Researcher
description: Research topics
system_message: |
You are a research specialist.
Your job is to gather accurate information...
mcp_servers:
- web-search # Reference formation-level MCP by ID
# agents/developer.afs
schema: "1.0.0"
id: developer
name: Developer
description: Code assistant
system_message: |
You are a software developer.
Your job is to write, review, and debug code...
mcp_servers:
- filesystem # Reference formation-level MCP by ID
- database # Reference formation-level MCP by ID
Authentication Types
Environment Variables
auth:
type: env
API_KEY: "${{ secrets.API_KEY }}"
DEBUG: "true"
Bearer Token
auth:
type: bearer
token: "${{ secrets.TOKEN }}"
Basic Auth
auth:
type: basic
username: "${{ secrets.USERNAME }}"
password: "${{ secrets.PASSWORD }}"
API Key Header
auth:
type: api_key
header: "X-API-Key"
key: "${{ secrets.API_KEY }}"
Troubleshooting
Tool Not Available
Verify the server is installed:
npx @modelcontextprotocol/server-brave-search --version
Connection Failed
Check server logs:
muxi logs --mcp
Common issues:
- Invalid API key
- Network connectivity
- Server not running
Next Steps
- Secrets - Manage tool credentials
- Agents - Assign tools to agents
- Add Tools Guide - Step-by-step tutorial