MCP (client + serve)

Overview

MCP (Model Context Protocol) lets eVi connect to other tool-providing programs — and lets other AI apps connect to eVi. eVi plays both roles:

Use the client side when you want eVi to do more than its built-in toolset allows, without writing a Python plugin — just point it at an existing MCP server. Use the serve side when you'd rather drive eVi's tools and memory from a desktop AI app you already use.

Everything is local-first: client servers run as child processes on your machine over stdio, and the serve side defaults to stdio (spawned by the client) — nothing touches the network unless you explicitly opt into HTTP.

How it works

Client (consuming external servers)

  1. On startup, eVi reads ~/.evi/mcp.json (plus any mcp.json shipped by installed plugins) into a list of MCPServer launch configs. Missing or malformed files yield an empty list — eVi keeps running.
  2. The consume-side allowlist [tools] mcp_allow is applied (see Setup). Then each enabled server is spawned as a child process over stdio using the mcp Python SDK.
  3. For each server, eVi calls initialize and list_tools, then wraps every discovered tool as a native eVi tool named <server>.<tool> and registers it in eVi's tool registry. (Plugin-supplied servers are namespaced <plugin>:<server>, so they can't collide with your own.)
  4. When the agent calls one of those tools, the call is forwarded to the server through an async-to-sync bridge (one asyncio loop on a daemon thread), so eVi's synchronous tool layer can drive the all-async MCP SDK.
  5. Tool results are flattened to text for the model: text content is concatenated; non-text items (images, etc.) become a [<type> omitted] placeholder; errors are prefixed ERROR:.

Fail-open by design: a server that fails to start is logged once and skipped — the rest of eVi keeps working. On shutdown eVi unregisters the added tools first (so an in-flight agent loop can't reach a closed session), then closes each server session.

Server (evi mcp serve)

eVi builds a standard MCP Server named evi exposing three things, all drawn from eVi's own single sources of truth:

Transports: stdio (the default; what a desktop MCP client spawns) or streamable HTTP (--http) served at the /mcp path, with an optional bearer-token gate (--token) for remote/untrusted clients.

Setup

Client side

1. Define your servers in ~/.evi/mcp.json (on Windows: C:\Users\<you>\.evi\mcp.json; the directory honors the EVI_HOME environment variable if set). The file is a JSON array of server objects:

FieldTypeRequiredDefaultMeaning
namestringyes—Server label; tools register as <name>.<tool>
commandstringstdio only—Executable to launch (e.g. npx, uvx, python)
argsstring[]no[]Arguments passed to the command
envobjectno{}Extra environment variables for the child process
transportstringnostdiostdio (spawn command) or a remote transport: http (streamable-http) / sse
urlstringhttp/sse only—The remote server's endpoint URL
headersobjectno{}Request headers for the remote transport (e.g. Authorization)
enabledboolnotrueSet false to keep a server in the file but skip it

A stdio entry needs command; a remote entry needs url (transport defaults to http when a url is present). Entries missing the required field are skipped silently. Print the exact path any time with evi mcp path.

Remote (HTTP) servers. To connect to a hosted MCP server instead of spawning a local process:

[
  { "name": "linear", "transport": "http", "url": "https://mcp.linear.app/mcp",
    "headers": { "Authorization": "Bearer <token>" } }
]

or add it from the CLI (header values are never echoed back by list-servers):

evi mcp add-http linear https://mcp.linear.app/mcp -H "Authorization=Bearer <token>"
evi mcp add-http legacy https://host/sse --sse        # legacy SSE transport

2. Enable MCP in ~/.evi/config.toml (it is off by default):

[tools]
mcp = true

3. (Optional) Gate which servers load per machine. When [tools] mcp_allow is non-empty, only servers whose name is in the list are loaded — handy if you sync one mcp.json across machines but want different servers active on each:

[tools]
mcp = true
mcp_allow = ["filesystem", "git"]

An empty/unset mcp_allow loads every server in the file (each still honoring its own enabled flag).

4. Install the MCP extra (the mcp SDK is an optional dependency):

pip install 'evi-assistant[mcp]'

If MCP is enabled but the package isn't installed, eVi prints a hint and continues without MCP tools.

Server side (evi mcp serve)

No config keys are required — evi mcp serve works out of the box once the mcp extra is installed. You control the exposed surface with command flags:

Usage

CLI commands (client side)

CommandWhat it does
evi mcp pathPrint the path to your mcp.json
evi mcp list-serversList configured servers from mcp.json with on/off state
evi mcp add <name> <command> [args…]Add a server. Use -- before args that start with a dash; --env KEY=VALUE (repeatable) for secrets; --disabled; --overwrite
evi mcp remove <name>Remove a user server (plugin-supplied plugin:name servers go with evi plugin remove)
evi mcp enable <name> / disable <name>Flip a server's enabled flag without deleting it
evi mcp list-toolsStart the servers and enumerate every tool they expose
# example: the filesystem server, scoped to your projects dir
evi mcp add filesystem npx -- -y @modelcontextprotocol/server-filesystem ~/projects
evi mcp list-tools          # verify it connects + see what it exposes

Once tools.mcp = true and servers are configured, MCP tools are available automatically in normal evi usage (CLI chat, the REPL, and the web UI) — they appear in the registry as <server>.<tool> and the agent calls them like any other tool. evi mcp list-tools warns if tools.mcp is still false.

CLI commands (server side)

CommandWhat it does
evi mcp serveRun eVi as an MCP server over stdio (what a desktop client spawns)
evi mcp serve --http --token <secret>Run as a streamable-HTTP server, bearer-token gated
evi mcp serve-configPrint a ready-to-paste client config snippet (mcpServers block)

Web / Desktop — the MCP panel

Settings → MCP manages servers without touching the file:

Backed by GET /api/mcp and POST /api/mcp[/remove|/toggle]. The allowlist itself stays editable as "MCP server allowlist" under Tools (bound to [tools] mcp_allow). When tools.mcp is enabled and servers are configured, the web server starts the same MCPManager on launch, so MCP tools are available in browser chat too.

Examples

Example 1 — Client: give eVi a filesystem server and a git server

Create ~/.evi/mcp.json:

[
  {
    "name": "filesystem",
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-filesystem", "C:/Users"],
    "env": {},
    "enabled": true
  },
  {
    "name": "git",
    "command": "uvx",
    "args": ["mcp-server-git", "--repository", "C:/evi"]
  }
]

Enable MCP in ~/.evi/config.toml:

[tools]
mcp = true

Install the extra, then verify what's wired up:

pip install 'evi-assistant[mcp]'

evi mcp list-servers
# on  filesystem — npx -y @modelcontextprotocol/server-filesystem C:/Users
# on  git — uvx mcp-server-git --repository C:/evi

evi mcp list-tools
# filesystem.read_file — Read the complete contents of a file...
# filesystem.list_directory — Get a detailed listing...
# git.git_log — Shows the commit logs...
# ...

From here the agent can call e.g. git.git_log or filesystem.read_file during any chat.

Example 2 — Serve: expose eVi to Claude Desktop / Cursor

Generate a client config snippet:

evi mcp serve-config --categories memory,index,calendar

It prints a block you paste into your MCP client's mcpServers config (the command is your current Python interpreter):

{
  "mcpServers": {
    "evi": {
      "command": "/path/to/python",
      "args": ["-m", "evi", "mcp", "serve", "--categories", "memory,index,calendar"]
    }
  }
}

The client spawns evi mcp serve over stdio on demand; eVi's memory, index, and calendar tools — plus your memory entries (as evi://memory/<name> resources) and saved commands (as prompts) — show up inside that app.

Example 3 — Serve over HTTP for a remote client

Run a token-gated streamable-HTTP server, narrowing to two exact tools within the memory category:

evi mcp serve --http \
  --host 0.0.0.0 \
  --port 8765 \
  --token "$(openssl rand -hex 32)" \
  --categories memory \
  --tools memory.search,memory.read

The MCP endpoint is served at /mcp (e.g. http://host:8765/mcp). Every request must send Authorization: Bearer <token> or it gets a 401.

Notes / limits

Source of record: C:\evi\evi\mcp\__init__.py, C:\evi\evi\mcp\manager.py, C:\evi\evi\mcp\bridge.py, C:\evi\evi\mcp\servers.py, C:\evi\evi\mcp\publish.py, and the mcp_app commands in C:\evi\evi\apps\cli\main.py (lines 4488-4600). Config keys: C:\evi\evi\config.py (ToolToggles.mcp, ToolToggles.mcp_allow; MCP_CONFIG_PATH).

Edit on GitHub ↗