MCP (Model Context Protocol)
MCP lets Minerva Agent connect to external tool servers so the agent can use tools that live outside Minerva itself — GitHub, databases, file systems, browser stacks, internal APIs, and more.
If you have ever wanted Minerva to use a tool that already exists somewhere else, MCP is usually the cleanest way to do it.
The mcpServers block in your ~/.claude.json maps to mcp_servers in Minerva's config.yaml — and minerva import-agent claude-code migrates it (along with skills and instructions) automatically. See Import from Other Agents.
What MCP gives you
- Access to external tool ecosystems without writing a native Minerva 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 Minerva to see
Connected by default
Minerva ships with two MCP servers already configured:
| Server | What it gives the agent |
|---|---|
deepwiki | Ask questions about any public GitHub repo — how a subnet's miner works, what its validator scores, what hardware its README assumes. |
context7 | Version-specific library documentation and code samples for the stack you actually run. |
Both are vendor-hosted, read-only and accept anonymous requests, so they need no account, API key or OAuth. They are the only servers Minerva connects on your behalf; everything else in the catalog is opt-in.
Between them they add 13 tools: five of their own, plus the resource and prompt helpers Minerva generates for every server. With Tool Search on (the default) these are deferred, so they cost a line each in the deferred listing rather than a full schema in every request.
Both are configured lazy: true, so they are not dialled while a session starts. The first run connects once to learn their tools and caches that; afterwards Minerva registers them from the cache and only touches the network when the agent actually calls one.
If you would rather not use them, turn either off in ~/.minerva/config.yaml:
mcp_servers:
deepwiki:
enabled: false
Your config deep-merges over the defaults, so this disables the server without having to restate its URL. Disabling both costs nothing at all — Minerva skips the MCP layer entirely, exactly as if you had configured no servers. minerva mcp remove <name> does the same thing for you; a shipped default is disabled rather than deleted, because a deleted one would reappear from the defaults on the next start.
Their connect_timeout is 10 seconds rather than the usual 60, so a machine with restricted outbound access fails fast instead of stalling; a server that cannot be reached is skipped and backs off on its own.
Quick start
-
MCP support ships with the standard install — no extra step needed.
-
Add an MCP server to
~/.minerva/config.yaml:
mcp_servers:
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
- Start Minerva:
minerva chat
- Ask Minerva to use the MCP-backed capability.
For example:
List the files in /home/user/projects and summarize the repo structure.
Minerva will discover the MCP server's tools and use them like any other tool.
Catalog: one-click install for Minerva-approved MCPs
Minerva ships a curated catalog of MCP servers that the Minerva maintainers have reviewed and merged. Apart from the two connected by default, nothing is installed until you ask — install only what you actually want.
minerva mcp # interactive picker (default)
minerva mcp catalog # plain-text list, scriptable
minerva mcp install globalping # install a catalog entry by name
The picker shows each entry with its current status:
context7 enabled Up-to-date, version-specific library docs and code examples.
deepwiki enabled Ask questions about any public GitHub repo (Devin's DeepWiki).
globalping available Ping, traceroute, DNS, and HTTP tests from global probes.
hugging_face available Models, datasets, Spaces, and papers from the Hugging Face Hub.
Hit Enter on a row to install (and walk through any required credentials),
enable, disable, or uninstall. Catalog entries are stored under
optional-mcps/ in the Minerva repo — presence in that directory means
Minerva approval. There is no community submission tier; entries are added by
merging a PR.
Catalog entries can require:
- API key — Minerva prompts at install time and writes the value to
~/.minerva/.env. Non-secret values (base URLs) go to the same file. - OAuth (remote MCP) — written as
auth: oauthin your config; the MCP client opens a browser on first connection.
Tool selection at install time
After credentials are configured, Minerva probes the MCP server to list every tool it exposes and presents a checklist:
Select tools for 'hugging_face' (SPACE toggle, ENTER confirm)
[x] hf_whoami Inspect the current Hugging Face authentication context
[x] hub_repo_search Search Hugging Face repositories
[x] hub_repo_details Get details for one or more Hugging Face repos
[x] hf_fs Browse files in models, datasets and Spaces
The pre-checked rows come from:
- Your prior selection if you've installed this entry before (reinstalls preserve what you had — the manifest's defaults don't override it)
- The manifest's
tools.default_enabledif the entry declares one (some catalog entries pre-prune mutating or rarely-useful tools) - Everything if neither applies
An entry with a very large auto-generated surface (hundreds or thousands of
OpenAPI endpoint tools) can instead declare tools.default_excluded — a
curated block-list of names and glob patterns. Installing one of these skips
the checklist entirely and writes tools.exclude; everything not matched
stays enabled, including tools the server adds later. Edit
mcp_servers.<name>.tools.exclude in config.yaml to re-enable a family.
Submit the checklist with ENTER. Only the checked tools end up in
mcp_servers.<name>.tools.include. If you select everything, no filter is
written (cleanest config shape, identical behavior).
If the probe fails (server unreachable, OAuth not yet completed,
backing service not running), the install still succeeds: the manifest's
tools.default_enabled is applied directly (if declared), or no filter is
written (if not). Re-run minerva mcp configure <name> once the server is
reachable to refine.
Trust model
Installing a catalog entry runs whatever the manifest specifies — git clone,
the entry's bootstrap commands (pip install, npm install, etc.), and
ultimately the MCP server's own code. Manifests are gated by PR review into
the Minerva repo, so the maintainers have reviewed each entry before it shipped —
but you should still read the manifest before installing, especially the
source: field's repository, the install.bootstrap: commands, and any
transport.command: invocation.
Manifests live at optional-mcps/<name>/manifest.yaml in the install
directory. The picker also prints the manifest's source: URL at install
time so you can quickly verify the upstream repo.
Manifest version compatibility
Manifests pin a manifest_version. The catalog is forward-compatible: if a
PR adds an entry with a newer manifest_version than your installed Minerva
understands, the picker will surface a warning (⚠ '<name>' requires a newer Minerva) for that entry instead of silently hiding it. Run minerva update
to install the latest Minerva when you see that.
Runtime ${ENV_VAR} substitution
Inside an entry's transport.command, transport.args, transport.url,
and headers, ${VAR} placeholders are resolved at server-connect time
from environment variables (which include everything in ~/.minerva/.env).
This is useful when a catalog entry wants to reference a value the user
configured elsewhere — e.g. ${HOME}/foo or ${MY_PROVIDER_TOKEN}.
Cursor-style context variables are also substituted (case-sensitive):
${userHome} (home directory), ${workspaceFolder} (session workspace
root), ${workspaceFolderBasename}, and ${pathSeparator} / ${/}
(the OS path separator). See the
MCP config reference for details.
Note this is distinct from ${INSTALL_DIR} in catalog manifests, which is
substituted at install-time with the path the catalog cloned the entry's
repo into.
Updating tool selection later
minerva mcp configure hugging_face
Reopens the same checklist with your current selection pre-checked. Use this when you want more tools enabled, or when the server has added new tools that you want to opt into.
Updating the catalog manifest
MCPs are never auto-updated. Re-run minerva mcp install <name> to refresh
after a Minerva update if a manifest version changed.
Catalog entries are added as new manifests under optional-mcps/ in the
source tree.
Suggestion metadata (suggest:)
A manifest may declare an optional suggest: block with keywords: and/or
hosts: lists. UI surfaces (currently the Desktop app's composer) use it to
offer a one-click "Add <server>" pill when your draft mentions one of the
keywords as a completed word, or contains a pasted link whose hostname ends
with one of the host suffixes. It is purely advisory — installs still flow
through the same validated catalog/config paths — and every entry in the
current catalog declares it.
GitHub is deliberately not in the catalog: its hosted MCP requires each
client to bring its own OAuth app (generic dynamic client registration is
rejected), and Minerva's bundled github skill driving the gh CLI is a
more capable integration. On Desktop, GitHub mentions instead offer the
github skill (its references/auth.md covers installing gh and
gh auth login) when gh isn't signed in yet.
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, andenv
HTTP servers
HTTP MCP servers are remote endpoints Minerva 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 Minerva spawning a local subprocess for that integration
For a server that accepts a bearer token, use the hidden credential prompt:
minerva mcp add remote_api --url https://mcp.example.com/mcp --auth header
minerva mcp test remote_api
Confirm that authentication is required, then enter the token. Minerva stores it
in the active profile's .env as MCP_REMOTE_API_API_KEY and writes an
Authorization: Bearer ${MCP_REMOTE_API_API_KEY} reference to config.yaml.
Cancelling or leaving the token empty stops the add operation. --auth header
uses the Authorization bearer header; a server requiring another header name
can use the headers mapping in config.yaml with an environment reference.
Ctrl-C or EOF at a confirmation cancels it; only pressing Enter accepts the
displayed default.
Connection, authentication, and invalid-configuration failures return a nonzero
exit status from the MCP management command. A failed add can optionally save
a disabled entry for repair, but still returns a failure status. Start a new
chat session after successfully adding a server to use its tools.
OAuth-authenticated HTTP servers
Many hosted MCP servers authenticate with OAuth 2.1 instead of a static bearer token; the catalog installs globalping and hugging_face this way. Set auth: oauth and Minerva handles discovery, client identification, PKCE, token exchange, refresh, and step-up auth via the MCP Python SDK.
Minerva identifies itself with a Client ID Metadata Document on servers that support one, and falls back to Dynamic Client Registration on those that don't. Both are automatic; there is nothing to configure.
mcp_servers:
example:
url: "https://mcp.example.com/mcp"
auth: oauth
minerva mcp add example --url https://mcp.example.com/mcp --auth oauth adds the same server from the command line.
On first connect, Minerva prints an authorize URL, opens your browser when possible, and waits for the OAuth callback on a local loopback port. Tokens are cached at ~/.minerva/mcp-tokens/<server>.json with 0o600 perms; subsequent runs reuse them silently until refresh fails.
Remote / headless hosts. When Minerva runs on a different machine than your browser, the loopback callback can't reach your laptop. Ways to complete the flow:
- Minerva Desktop (automatic): when you run the OAuth sign-in from the Desktop app's MCP setup UI against a remote backend, Desktop hosts the callback listener on your machine and relays the authorization back to the gateway automatically — no tunnel, paste, or proxy needed. Requires both the Desktop app and the backend to be up to date.
- Paste-back (no setup): on an interactive terminal Minerva 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@hostin a separate terminal, then let the redirect flow normally. - Proxied callback (
redirect_uri): when a public HTTPS endpoint forwards to the host (e.g. a Tailscale Funnel or reverse proxy pointed at the callback port), setoauth.redirect_uriand the browser redirect reaches Minerva on its own — no tunnel or paste needed:
mcp_servers:
myserver:
url: "https://mcp.example.com/mcp"
auth: oauth
oauth:
redirect_port: 8765 # fixed port for the proxy to target
redirect_uri: "https://oauth.example.ts.net/callback"
For fully headless gateways (messaging bot, no interactive terminal at all), the mcp-oauth-remote-gateway skill walks the agent through completing the flow manually and writing tokens where Minerva expects them.
Pitfall — WAF rejects 127.0.0.1 redirect URIs. A few providers front their authorization server with a WAF that 403s any authorize request whose query string contains a literal 127.0.0.1 (Reclaim.ai's AWS API Gateway is a known example — every attempt returns {"message":"Forbidden"} before reaching the OAuth app). Set oauth.redirect_host: localhost to use http://localhost:<port>/callback instead; the callback listener still binds 127.0.0.1 either way.
See OAuth over SSH / Remote Hosts for the remote-host walkthrough (Desktop relay, paste-back, SSH tunnel). Servers that reject automatic registration are covered by the Google Drive pitfall below; pre-registered client_id/client_secret, scope and the other per-server OAuth keys are in the MCP config reference.
Pitfall — providers that don't support automatic registration (e.g. Google Drive). Some servers reject the dynamic client registration step (RFC 7591) that bare auth: oauth relies on — Google's official Drive server (https://drivemcp.googleapis.com/mcp/v1) returns a 400 Bad Request, so no OAuth client is created and no token is acquired. The symptom is subtle: these servers also serve tools/list without auth, so minerva mcp login can list the tools and look like it worked, but every real tool call later times out. minerva mcp login now detects this (it checks that a token actually landed on disk) and tells you to supply your own OAuth client. Create one in the provider's console and add it to config:
mcp_servers:
googledrive:
url: "https://drivemcp.googleapis.com/mcp/v1"
auth: oauth
oauth:
client_id: "<your-oauth-client-id>"
client_secret: "<your-oauth-client-secret>"
Then run minerva mcp login googledrive — with the pre-registered client, Minerva skips registration and runs the normal browser authorization flow.
Pitfall — config auto-reload race. When you edit ~/.minerva/config.yaml from inside a running Minerva 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 minerva mcp login <server> from a fresh terminal — it waits the full 5 minutes for you to complete auth.
mTLS / client certificates
Remote HTTP MCP servers that require mutual TLS (client-certificate authentication) are supported via client_cert / client_key. Minerva passes the resolved certificate to the underlying HTTP client for the TLS handshake.
client_cert accepts three shapes:
- A single combined PEM path — one file holding both the certificate and the private key:
mcp_servers:
internal_api:
url: "https://mcp.internal.example.com/mcp"
client_cert: "~/.certs/mcp-client.pem"
- A
[cert, key]2-tuple — certificate and key in separate files (equivalent to settingclient_cert+client_key):
mcp_servers:
internal_api:
url: "https://mcp.internal.example.com/mcp"
client_cert: ["~/.certs/mcp-client.crt", "~/.certs/mcp-client.key"]
- A
[cert, key, password]3-tuple — when the private key is encrypted, the third element is the key passphrase:
mcp_servers:
internal_api:
url: "https://mcp.internal.example.com/mcp"
client_cert: ["~/.certs/mcp-client.crt", "~/.certs/mcp-client.key", "${MCP_KEY_PASSWORD}"]
You can also keep the cert and key fully separate via client_cert (combined PEM) plus an explicit client_key. Paths support ~ expansion; a missing file raises a clear, server-scoped error rather than an opaque TLS handshake failure.
Per-user identity header
Remote HTTP/SSE MCP servers that key behavior on a caller identity (per-user rate limits, audit trails, multi-tenant routing) can be sent an identity header on every request via identity_header:
mcp_servers:
team_api:
url: "https://mcp.team.example.com/mcp"
identity_header:
name: "X-User-Id"
value_from: "static" # "static" (default) or "profile"
value: "alice" # required for static
value_from: staticsends the literalvaluefrom config.yaml.value_from: profilesends the active Minerva profile name, resolved once at connect time — useful when multiple profiles on one machine talk to the same server and it needs to tell them apart.
An explicit entry in the server's headers mapping with the same name (any casing) always wins; the identity header never overrides your own header config. Invalid identity_header blocks are warned about and ignored — they never block the server from connecting. On stdio servers the key is ignored with a warning (stdio transports have no headers).
Basic configuration reference
Minerva reads MCP config from ~/.minerva/config.yaml under mcp_servers.
Common keys
| Key | Type | Meaning |
|---|---|---|
command | string | Executable for a stdio MCP server |
args | list | Arguments for the stdio server |
env | mapping | Environment variables passed to the stdio server |
url | string | HTTP MCP endpoint |
headers | mapping | HTTP headers for remote servers |
client_cert | string | list | Client certificate for mTLS — a combined PEM path, or [cert, key] / [cert, key, password] |
client_key | string | Client private-key PEM path (when separate from client_cert) |
identity_header | mapping | Optional per-user identity header for HTTP/SSE servers — {name, value_from: static|profile, value} |
timeout | number | Tool call timeout |
connect_timeout | number | Initial connection timeout (also bounds the MCP initialize handshake) |
idle_timeout_seconds | number | Recycle a stdio server after this many seconds without a tool call (0 = never, default). The server restarts transparently on the next tool call. |
max_lifetime_seconds | number | Recycle a stdio server after this total age (0 = never, default). Restarts transparently on next use. |
enabled | bool | If false, Minerva skips the server entirely |
supports_parallel_tool_calls | bool | If true, tools from this server may run concurrently |
tools | mapping | Per-server tool filtering and utility policy |
Minimal stdio example
mcp_servers:
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
Recycling memory-heavy stdio servers
Browser-based MCP servers (e.g. @playwright/mcp) keep a full Chromium
resident after their first tool call — hundreds of MB that never get
released. Opt in to automatic recycling and the server is torn down after
the idle/lifetime limit, then restarted transparently the next time one of
its tools is called (its tools stay registered the whole time):
mcp_servers:
playwright:
command: "npx"
args: ["-y", "@playwright/mcp@latest", "--headless"]
idle_timeout_seconds: 900 # recycle after 15 min without a tool call
max_lifetime_seconds: 86400 # and at least once a day regardless
Minimal HTTP example
mcp_servers:
company_api:
url: "https://mcp.internal.example.com"
headers:
Authorization: "Bearer ***"
How Minerva registers MCP tools
Minerva prefixes MCP tools so they do not collide with built-in names:
mcp__<server_name>__<tool_name>
Examples:
| Server | MCP tool | Registered name |
|---|---|---|
filesystem | read_file | mcp__filesystem__read_file |
deepwiki | ask_question | mcp__deepwiki__ask_question |
context7 | resolve-library-id | mcp__context7__resolve_library_id |
my-api | query.data | mcp__my_api__query_data |
In practice, you usually do not need to call the prefixed name manually — Minerva sees the tool and chooses it during normal reasoning.
Tool-result sanitization and _meta
Two behaviors apply to every MCP tool result before the model sees it:
- Invisible Unicode TAG characters are stripped. Characters in the U+E0000–U+E007F range render as nothing in terminals and chat UIs but are fully visible to the model — a classic prompt-injection smuggling channel for a malicious or compromised server. Minerva strips them from tool results, resource content, and tool descriptions. Legitimate emoji tag sequences (regional flags like 🏴) are preserved.
- Vendor
_metais surfaced; protocol-reserved keys are not. When a server attaches a_metamapping to a tool result (vendor namespaces likecom.example/handoff), Minerva passes it through to the model alongside the result content. Keys under protocol-reserved prefixes — amodelcontextprotocolormcplabel followed by another label, e.g.modelcontextprotocol.io/...ortools.mcp.com/...— are dropped, matching the MCP spec's key-name rules. If nothing model-facing remains, the_metafield is omitted entirely.
MCP utility tools
When supported, Minerva also registers utility tools around MCP resources and prompts:
list_resourcesread_resourcelist_promptsget_prompt
These are registered per server with the same prefix pattern, for example:
mcp__deepwiki__list_resourcesmcp__deepwiki__get_prompt
These four helpers are why the two default servers add 13 tools between them rather than their five native ones.
Important
These utility tools are now capability-aware:
- Minerva only registers resource utilities if the MCP session actually supports resource operations
- Minerva 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 Minerva, 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, Minerva 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.
Entries in include/exclude may also be glob patterns (*, ?, [...],
matched case-sensitively): include: ["*_dns_*"] registers every tool whose
name contains _dns_. Plain entries without metacharacters stay exact-match.
Globs are the practical way to filter servers that expose thousands of
auto-generated endpoint tools by product family.
Blacklist server tools
mcp_servers:
billing:
url: "https://mcp.billing.example.com/mcp"
tools:
exclude: [delete_customer]
All server tools are registered except the excluded ones.
Glob patterns
Both lists accept fnmatch-style globs alongside exact names — essential for huge flat surfaces, such as a server that exposes one tool per endpoint of a large REST API (thousands of tools), where excluding product areas one endpoint at a time is impractical:
mcp_servers:
big_api:
url: "https://mcp.example.com/mcp"
auth: oauth
tools:
exclude: ["*_audit_logs_*", "*_billing_*", "*_legacy_*"]
Entries without glob metacharacters (*, ?, [) match exactly — docs
excludes only the tool named docs, never docs_search.
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 Minerva-added utility wrappers:
mcp_servers:
docs:
url: "https://mcp.docs.example.com"
tools:
prompts: false
resources: false
That means:
tools.resources: falsedisableslist_resourcesandread_resourcetools.prompts: falsedisableslist_promptsandget_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
billing:
url: "https://mcp.billing.example.com/mcp"
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, Minerva does not create an empty runtime MCP toolset for that server.
That keeps the tool list clean.
Runtime behavior
Discovery time
Minerva discovers MCP servers at startup and registers their tools into the normal tool registry.
Dynamic Tool Discovery
MCP servers can notify Minerva when their available tools change at runtime by sending a notifications/tools/list_changed notification. When Minerva 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. It is also the explicit way to re-probe availability-gated tools (Docker, OAuth…): a session's tool set is otherwise frozen, so a credential or daemon that appears mid-session is only picked up on /reload-mcp, /new, or context compaction. 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, Minerva 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.
Hugging Face Hub limited to search and repo details
mcp_servers:
hugging_face:
url: "https://huggingface.co/mcp"
auth: oauth
tools:
include: [hub_repo_search, hub_repo_details]
minerva mcp install hugging_face writes an equivalent entry if you tick only those two tools in its checklist.
Use it like:
Search the Hub for small instruction-tuned models and compare the details of the top three.
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 ~/.minerva/minerva-agent && uv pip install -e ".[mcp]"
node --version
npx --version
Then verify your config and restart Minerva.
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 Minerva now only registers those wrappers when both are true:
- your config allows them
- 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, Minerva 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.).
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 Minerva via the sampling/createMessage protocol. This allows an MCP server to ask Minerva 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
MCP Elicitation Support
MCP servers can ask the user for structured input mid-tool-call via the elicitation/create protocol (mcp Python SDK ≥ 1.11.0). Minerva routes form-mode elicitations through its existing approval surface — an interactive prompt in the CLI/TUI, or approval buttons on gateway platforms like Telegram and Slack — so the request reaches you wherever the session lives. URL-mode elicitations (where a server points you at an external URL) are declined as unsupported.
Elicitation is enabled by default per server. Configure it under the elicitation key:
mcp_servers:
my_server:
command: "my-mcp-server"
elicitation:
enabled: true # default: true
timeout: 300 # seconds to wait for your answer (default: 300)
The 5-minute default timeout mirrors the gateway approval default so users on async surfaces have time to respond before the server gives up. Per-server metrics (requests, accepted, declined, errors) are tracked on the handler.
Running Minerva as an MCP server
In addition to connecting to MCP servers, Minerva can also be an MCP server. This lets other MCP-capable agents (Claude Code, Cursor, Codex, or any MCP client) use Minerva'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 Minerva
- You want a single MCP server that bridges to all of Minerva's connected messaging platforms at once
- You already have a running Minerva gateway with connected platforms
Quick start
minerva mcp serve
This starts a stdio MCP server. The MCP client (not you) manages the process lifecycle.
MCP client configuration
Add Minerva to your MCP client config. For example, in Claude Code's ~/.claude/claude_desktop_config.json:
{
"mcpServers": {
"minerva": {
"command": "minerva",
"args": ["mcp", "serve"]
}
}
}
Or if you installed Minerva in a specific location:
{
"mcpServers": {
"minerva": {
"command": "/home/user/.minerva/minerva-agent/venv/bin/minerva",
"args": ["mcp", "serve"]
}
}
}
Available tools
The MCP server exposes 10 tools:
| Tool | Description |
|---|---|
conversations_list | List active messaging conversations. Filter by platform or search by name. |
conversation_get | Get detailed info about one conversation by session key. |
messages_read | Read recent message history for a conversation. |
attachments_fetch | Extract non-text attachments (images, media) from a specific message. |
events_poll | Poll for new conversation events since a cursor position. |
events_wait | Long-poll / block until the next event arrives (near-real-time). |
messages_send | Send a message through a platform (e.g. telegram:123456, discord:#general). |
channels_list | List available messaging targets across all platforms. |
permissions_list_open | List pending approval requests observed during this bridge session. |
permissions_respond | Allow or deny a pending approval request. |
Event system
The MCP server includes a live event bridge that polls Minerva'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
minerva mcp serve # Normal mode
minerva mcp serve --verbose # Debug logging on stderr
How it works
The MCP server reads conversation data directly from Minerva's session store — ~/.minerva/state.db is the primary source, with sessions.json kept only as a legacy fallback. A background thread polls the database for new messages and maintains an in-memory event queue. For sending messages, it uses the same internal send engine (tools/send_message_tool.py) that powers cron delivery and the minerva send CLI.
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
minerva mcp serveexposes 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 Minerva, which already speaks both stdio and HTTP (url+headersundermcp_servers:inconfig.yaml; see HTTP servers above). - Event polling at ~200ms intervals via mtime-optimized DB polling (skips work when files are unchanged)
- No
claude/channelpush notification protocol yet - Text-only sends (no media/attachment sending through
messages_send)