Rebased and cleaned-up version of PR #113 by @mricharz, resolved against current main (including Codex engine, Gemini thought_signature, and agent manager fixes merged since the original PR). MCP Transport & Client: - StreamableHTTPTransport with session tracking, SSE parsing, timeouts - MCPClient.initialize() sends proper MCP handshake (protocolVersion, capabilities, clientInfo) + notifications/initialized - Fix StdioTransport constructor: command=[command] + args - MCPRequest.to_dict() with notification support (id=None) External MCP Discovery: - _discover_external_mcp supports both url (HTTP) and command (stdio) - Per-server include_tools / exclude_tools filtering - MCP clients persisted on JarvisSystem for runtime lifetime Streaming Tool-Call Support (stream_full): - StreamChunk dataclass in _stubs.py (content, tool_calls, finish_reason, usage) - Default stream_full() on InferenceEngine ABC wraps stream() for backward compat - _OpenAICompatibleEngine.stream_full() with SSE parsing - CloudEngine: _stream_full_openai (OpenAI/OpenRouter/MiniMax/Codex routing) _stream_full_anthropic (event-based → OpenAI delta format) - InstrumentedEngine, MultiEngine: stream_full delegation - GuardrailsEngine: stream_full with post-hoc security scanning (FIXED: original PR bypassed output scanning — now accumulates and scans like stream()) Other improvements: - _prepare_anthropic_messages() extracted to eliminate duplication - Default tool_choice=auto when tools are provided (OpenAI compat engines) - MCP tool injection into managed agent streaming path - Documentation: docs/user-guide/mcp-external-servers.md Tests: ~59 new tests across 8 test files, all passing. Closes PR #113 Co-Authored-By: mricharz <mricharz@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.0 KiB
External MCP Server Integration
OpenJarvis can extend agent capabilities by connecting to external Model Context Protocol (MCP) servers. This allows agents to use tools provided by services like Home Assistant, databases, custom APIs, or any MCP-compatible server -- without writing custom tool code.
How It Works
When OpenJarvis starts, it reads the [tools.mcp] section in config.toml. For each configured server, it:
- Opens a connection using the appropriate transport (Streamable HTTP or stdio).
- Performs the MCP initialize handshake (protocol version negotiation and
initializednotification). - Discovers available tools via
tools/list. - Wraps each discovered tool as a standard
BaseToolso agents can call them like any built-in tool.
If a server is unreachable or returns an error, OpenJarvis logs a warning and continues loading the remaining servers. One broken server does not prevent other tools from being available.
Configuration
External MCP servers are configured in config.toml under [tools.mcp]:
[tools.mcp]
enabled = true
servers = '[{"name": "homeassistant", "url": "http://172.16.3.1:9583/private_abc123"}]'
The servers value is a JSON-encoded string containing an array of server objects. Each object defines one external MCP server.
!!! note The value must be a JSON string (with single-quote TOML delimiters around it), not a native TOML array. This is because the configuration system passes it through as a single string field.
Server Config Schema
Each server object supports the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | No | Human-readable name used in log messages. Defaults to <unnamed>. |
url |
string | No* | URL for Streamable HTTP transport. |
command |
string | No* | Command to launch a stdio-based MCP server. |
args |
list of strings | No | Arguments passed to the stdio command. |
include_tools |
list of strings | No | Whitelist of tool names to import. Only these tools are loaded. |
exclude_tools |
list of strings | No | Blacklist of tool names to skip. All other tools are loaded. |
*Either url or command must be provided. If neither is set, the server is skipped with a warning.
When both include_tools and exclude_tools are specified, the whitelist is applied first, then the blacklist filters the result.
Examples
Home Assistant via Streamable HTTP
Connect to the ha-mcp Home Assistant add-on:
[tools.mcp]
enabled = true
servers = '[{"name": "homeassistant", "url": "http://172.16.3.1:9583/private_abc123"}]'
This discovers all HA tools (entity control, automations, history, etc.) and makes them available to agents.
Stdio Server
Launch a local MCP server as a subprocess:
[tools.mcp]
enabled = true
servers = '[{"name": "myserver", "command": "python", "args": ["-m", "my_mcp_server"]}]'
OpenJarvis starts the process automatically, communicates via JSON-RPC over stdin/stdout, and terminates it on shutdown.
Multiple Servers
[tools.mcp]
enabled = true
servers = '[{"name": "homeassistant", "url": "http://172.16.3.1:9583/private_abc123"}, {"name": "database", "command": "db-mcp-server", "args": ["--db", "postgres://localhost/mydb"]}]'
Tool Filtering
When a server exposes many tools but you only need a few, use include_tools to whitelist:
[tools.mcp]
enabled = true
servers = '[{"name": "ha", "url": "http://172.16.3.1:9583/private_abc123", "include_tools": ["hassTurnOn", "hassTurnOff", "hassGetState"]}]'
To load everything except specific tools, use exclude_tools:
[tools.mcp]
enabled = true
servers = '[{"name": "ha", "url": "http://172.16.3.1:9583/private_abc123", "exclude_tools": ["hassCreateBackup", "hassDeleteBackup"]}]'
Transport Types
Streamable HTTP
Used when the url field is set. The transport sends JSON-RPC requests as HTTP POST to the given URL using httpx. It tracks the Mcp-Session-Id header across requests as required by the MCP Streamable HTTP specification.
When to use: Remote MCP servers, services running as HTTP endpoints (e.g., Home Assistant MCP add-on, cloud-hosted MCP servers).
Connection parameters:
- Connect timeout: 10 seconds
- Request timeout: 60 seconds
Stdio
Used when the command field is set. OpenJarvis spawns the command as a subprocess and communicates via JSON-RPC lines on stdin/stdout.
When to use: Local MCP servers distributed as CLI tools, development/testing, servers that require filesystem access on the same machine.
!!! info "SSETransport alias"
SSETransport is provided as a backward-compatible alias for StreamableHTTPTransport. Both refer to the same implementation.
Error Handling
OpenJarvis handles MCP server failures gracefully:
- Server unreachable: A warning is logged and the server is skipped. All other servers and built-in tools continue to load normally.
- Timeout: HTTP requests time out after 60 seconds. The server is skipped with a warning.
- Invalid config: If the
serversJSON is malformed or a server entry has neitherurlnorcommand, a warning is logged and that entry is skipped. - Tool discovery failure: If
tools/listfails on a server, the error is caught and the server is skipped. - Runtime tool call failure: If a tool call to an external MCP server fails at runtime, it returns a
ToolResultwithsuccess=Falseand the error message.
No single server failure causes OpenJarvis to crash or prevents other tools from working.
Troubleshooting
Server not discovered
- Check that
[tools.mcp]hasenabled = true. - Verify the
serversJSON is valid. A common mistake is using TOML arrays instead of a JSON string. - Check the OpenJarvis logs for warnings like
Failed to discover external MCP tools.
Connection refused / timeout
- Verify the server is running and reachable from the OpenJarvis host:
curl -v http://host:port/. - Check firewall rules between the OpenJarvis container and the MCP server.
- For Docker deployments, ensure both containers are on the same network or use host IPs.
Tools not appearing
- Run with debug logging to see which tools were discovered.
- Check if
include_toolsorexclude_toolsfilters are too restrictive. - Verify the MCP server actually exposes tools via
tools/list(some servers only expose resources or prompts).
Stdio server crashes immediately
- Test the command manually:
python -m my_mcp_servershould start and wait for input on stdin. - Check stderr output in the OpenJarvis logs for error messages from the subprocess.
- Ensure all dependencies for the MCP server are installed in the same environment.