EVC Mesh MCP Server
# EVC Mesh MCP Server
<!-- mcp-name: io.github.entire-vc/evc-mesh-mcp -->
[](https://spark.entire.vc/assets/evc-mesh-mcp?utm_source=github&utm_medium=readme)
> Tasks, comments, shared memory and handoffs for teams of people and AI agents, over MCP.
EVC Mesh is an open-source task manager built for teams where people and AI agents work side by side. This [Model Context Protocol](https://modelcontextprotocol.io/) server connects your agent to a [Mesh](https://github.com/entire-vc/evc-mesh) workspace: it can pick up assigned tasks, check out work, comment, move tasks through your workflow, hand off to teammates, and keep decisions in shared project memory.
Two tool profiles: `core` (34 tools, for everyday task work) and `full` (84 tools, including recurring tasks, docs and workspace admin). Every tool is marked read-only or write, so clients can ask before changing anything.
You need a Mesh workspace and an agent key from it. Mesh is self-hosted (Apache-2.0, `docker compose` setup in the [evc-mesh](https://github.com/entire-vc/evc-mesh) repository). Set `MESH_API_URL` to your instance and `MESH_AGENT_KEY` to the agent's key. Workspaces on the hosted instance are by invitation.
Works with Cursor, Cline, OpenClaw and other MCP clients.
**This is the actively developed copy.** [`evc-mesh`](https://github.com/entire-vc/evc-mesh) also ships an MCP server (`./cmd/mcp`, same `internal/mcp` tool set) that it builds and deploys itself — the two exist because Go's `internal/` visibility rules mean one repo can't import the other's package, not because they're meant to diverge. New tools and fixes land here first.
## Prerequisites
- Go 1.22+
- Running EVC Mesh instance
- Agent registered in Mesh with an API key (`agk_...`)
## Installation
```bash
go install github.com/entire-vc/evc-mesh-mcp@latest
```
Or build from source:
```bash
git clone https://github.com/entire-vc/evc-mesh-mcp.git
cd evc-mesh-mcp
go build -o evc-mesh-mcp .
```
### Docker
```bash
docker run -i --rm \
-e MESH_API_URL \
-e MESH_AGENT_KEY \
ghcr.io/entire-vc/evc-mesh-mcp
```
`-i` is required — the server speaks MCP over stdio, and Docker only wires up
stdin when the container runs interactively. The image starts with the `core`
profile; add `-e MESH_MCP_PROFILE=full` for every tool (see
[Tool Profiles](#tool-profiles) below). The image is
published for `linux/amd64` and `linux/arm64` from `Dockerfile` in this repo
on every tagged release (`docs/RELEASING.md`).
## Tool Profiles
The MCP server supports two profiles to optimize context window usage:
| Profile | Tools | Context overhead | Best for |
|---------|-------|-----------------|----------|
| **core** | 34 | ~8K tokens (4% of 200K) | Claude Code, Cursor, small-context models |
| **full** | 84 | ~18K tokens (9% of 200K) | Power users, automation agents, admin ops |
Set via `MESH_MCP_PROFILE` environment variable. Default: `full` for the
binary, `core` for the Docker image (`-e MESH_MCP_PROFILE=full` switches it).
## Configuration
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `MESH_API_URL` | Yes | `http://localhost:8005` | Base URL of the Mesh API |
| `MESH_AGENT_KEY` | Yes (stdio) | — | Agent API key (`agk_...`) |
| `MESH_CHAT_TOKEN_FILE` | No | — | Stdio chat mode: path to a file holding a chat-session token (`cst_...`), sent as `Authorization: Bearer` and re-read on every request. Replaces `MESH_AGENT_KEY`; if both are set the agent key is ignored. |
| `MESH_MCP_PROFILE` | No | `full` (Docker image: `core`) | Tool profile for stdio: `core` or `full` (SSE serves both) |
| `MESH_MCP_TRANSPORT` | No | `stdio` | Transport mode: `stdio` or `sse` |
| `MESH_MCP_HOST` | No | `0.0.0.0` | SSE server bind host |
| `MESH_MCP_PORT` | No | `8081` | SSE server bind port |
| `MESH_MCP_AUTH_FAIL_RPM` | No | `20` | SSE mode: per-IP budget for authentication attempts against a not-yet-cached agent key on `/sse`, `/core/sse`, `/mcp`, `/mcp/core`. Over budget → `429` without calling Mesh API. `0` disables. |
| `MESH_MCP_SESSION_CACHE_TTL_MIN` | No | `15` | SSE mode: how long a successful authentication is trusted before the key is re-checked — bounds how long a revoked key keeps working without a restart. |
| `MESH_MCP_AUTH_FAIL_CACHE_SEC` | No | `30` | SSE mode: how long a failed authentication (bad/unknown key) is remembered, so repeating the same bad key doesn't call Mesh API every request. |
| `MESH_MCP_PUBLIC_URL` | No | — | SSE mode: the URL the MCP root is reachable at from outside, e.g. `https://mesh.example.com/mcp`. Used for the absolute SSE endpoint and as the OAuth resource URL (see [OAuth](#oauth-remote-connectors)). Unset: derived from each request's host, which is only right when clients reach this server directly — behind a proxy set it, or the derived core URL (`/core`) will not match the public one (`/mcp/core`). |
| `MESH_MCP_OAUTH_ISSUER` | No | origin of `MESH_MCP_PUBLIC_URL` | SSE mode: the OAuth authorization server named in the protected-resource metadata — your Mesh instance's public origin. Set it only if the MCP server is served from a different origin than the Mesh API. |
| `MESH_MCP_OAUTH_CACHE_TTL_SEC` | No | `60` | SSE mode: how long a verified OAuth access token is trusted before Mesh API is asked again. Deliberately much shorter than the agent-key TTL so a revoked grant stops working within about a minute. |
| `MESH_MCP_DECIDER_USERNAME` | No | — | Username recorded as `decided_by` when `record_owner_decision` answers a gated task. Unset: the workspace owner. |
| `MESH_MCP_LEGACY_TOOL_ALIASES` | No | off | `1` also registers the tools' earlier names, for deployments whose callers still use them. Leave off for new installs. |
### Running without credentials
In stdio mode the server also starts when `MESH_AGENT_KEY` is not set. It then
answers `initialize` and `tools/list` as usual, and every tool call returns
instructions for setting `MESH_API_URL` and `MESH_AGENT_KEY`. This lets MCP
clients and catalogs inspect the tool list before you have a key. If a key is
set but authentication fails at startup (API unreachable, key rejected), the
server keeps running: tools are listed, and each call retries authentication
and returns the reason until it succeeds.
### Tool annotations
Every tool declares the MCP hints `readOnlyHint`, `destructiveHint`,
`idempotentHint` and `openWorldHint`, so clients can tell read-only tools
(`get_*`, `list_*`, `recall`, `search_docs`, …) from ones that change or
remove data (`update_*`, `move_task`, `forget`, …).
### Client metrics
Each `initialize` is logged with the client's `clientInfo.name` and version,
and counted in the Prometheus metric `mesh_mcp_initialize_total{client,profile}`
(exposed on `/metrics` in SSE mode; client names are normalised and capped).
### Claude Code (stdio mode)
Add to your project's `.mcp.json`:
```json
{
"mcpServers": {
"evc-mesh": {
"command": "evc-mesh-mcp",
"env": {
"MESH_API_URL": "https://your-mesh-instance.example.com",
"MESH_AGENT_KEY": "agk_your-workspace_your-key",
"MESH_MCP_PROFILE": "core"
}
}
}
}
```
### Cursor
Add to Cursor MCP settings (Settings → MCP Servers):
```json
{
"evc-mesh": {
"command": "evc-mesh-mcp",
"env": {
"MESH_API_URL": "https://your-mesh-instance.example.com",
"MESH_AGENT_KEY": "agk_your-workspace_your-key",
"MESH_MCP_PROFILE": "core"
}
}
}
```
### SSE Mode (multi-agent, shared server)
For connecting multiple agents through a shared MCP endpoint:
```bash
MESH_API_URL=https://your-mesh-instance.example.com \
MESH_MCP_PORT=8081 \
evc-mesh-mcp --transport sse
```
SSE mode serves **two profiles simultaneously** on different paths:
| Path | Profile | Description |
|------|---------|-------------|
| `/sse` + `/message` | full | All 84 tools (backward compatible) |
| `/core/sse` + `/core/message` | core | 34 essential tools |
The same process also serves the **Streamable HTTP** transport (stateless, one
agent key per request, sent in the `Authorization: Bearer` or `X-Agent-Key`
header — the query parameter is refused there):
| Path | Profile |
|------|---------|
| `/mcp` | full |
| `/core` | core |
Authentication per connection via:
- `Authorization: Bearer agk_...` header
- `X-Agent-Key: agk_...` header
- `?agent_key=agk_...` query parameter (SSE connect only)
- `Authorization: Bearer mot_...` — an OAuth access token issued by your Mesh instance (see below); the header only, never the query string, and on the Streamable HTTP endpoints only (SSE connections need an agent key)
### OAuth (remote connectors)
Clients that sign users in with OAuth — remote-connector directories, MCP
Inspector, editors that follow the MCP authorization spec — connect to the
Streamable HTTP endpoints without a pre-shared key. Your Mesh instance is the
authorization server (dynamic client registration, PKCE, user consent); this
server is the resource server and does two things:
- **Challenges.** A request with no credential, or with an OAuth access token
Mesh API rejects (expired, revoked, never issued), gets `401` and
```
WWW-Authenticate: Bearer resource_metadata="https://mesh.example.com/.well-known/oauth-protected-resource/mcp", scope="mesh"
```
which is where a client starts the authorization flow. A rejected agent key
keeps answering `403`. Only a verdict from Mesh API (a `4xx` other than
`408`/`429`) makes a token invalid: if Mesh API cannot be reached or answers
with an error that says nothing about the token (`5xx`, `429`), the answer is
`503` with `Retry-After`, so a valid token is not thrown away over an outage.
- **Serves the metadata** ([RFC 9728](https://www.rfc-editor.org/rfc/rfc9728))
at the well-known path derived from each endpoint's URL:
| Endpoint | Metadata |
|----------|----------|
| `https://mesh.example.com/mcp` (full) | `/.well-known/oauth-protected-resource/mcp` |
| `https://mesh.example.com/mcp/core` (core) | `/.well-known/oauth-protected-resource/mcp/core` |
```json
{
"resource": "https://mesh.example.com/mcp",
"authorization_servers": ["https://mesh.example.com"],
"scopes_supported": ["mesh"],
"bearer_methods_supported": ["header"]
}
```
`resource` is `MESH_MCP_PUBLIC_URL` (trailing slash, query and fragment
removed; `/core` appended for the core profile), so set it to the URL your
users paste into the client.
The authorization server defaults to that URL's origin; override it with
`MESH_MCP_OAUTH_ISSUER`.
An OAuth token acts as a connector agent in the workspace the user chose when
they granted access, with that agent's permissions — the same tools, the same
permission model as an agent key. Verified tokens are cached for a minute (`MESH_MCP_OAUTH_CACHE_TTL_SEC`). The
per-IP budget (`MESH_MCP_AUTH_FAIL_RPM`) is spent by *rejected* tokens, not by
verifications, so many users behind one address are not throttled by their own
token refreshes. The token is not audience-bound: any valid access token from
your Mesh instance is accepted, which is the intent while the authorization
server and this server belong to the same deployment. Agent keys (`agk_...`) in `Authorization` or
`X-Agent-Key` work exactly as before, and stdio mode is unaffected.
Your reverse proxy must send `/.well-known/oauth-protected-resource*` to this
server instead of the web app's catch-all: a single-page app answers every
unknown path with `200 text/html`, which a client cannot tell from missing
metadata.
## Agent Context Protocol (ACP)
At session start, follow these 5 steps in order:
```
1. heartbeat(status="online") → register as alive
2. get_project_knowledge(project_id) → load accumulated decisions & conventions
3. get_my_rules(project_id) → understand constraints
4. get_context(project_id) → see recent activity + project knowledge
5. get_my_tasks() → check assigned work
```
At session end:
```
publish_event(type="summary", memory={persist: true}) → broadcast + persist
session_report(model, tokens_in, tokens_out) → report metrics
```
## MCP Tools — Core Profile (34)
### ACP & Identity
| Tool | Description |
|------|-------------|
| `heartbeat` | Send heartbeat. Call at session start with status=online. Response includes `mesh_version` (the running binary's build git-SHA, or `"dev"` for an unpinned local build) — cheap way to check whether a fix has actually reached the installed binary without shelling out to the host. |
| `get_project_knowledge` | Get ALL permanent knowledge (decisions, conventions). ACP Step 2 |
| `get_my_rules` | Get ALL governance rules (workflow + assignment). ACP Step 3 |
| `get_context` | Get recent activity + project knowledge. ACP Step 4 |
| `get_my_tasks` | Get assigned tasks with complete descriptions and optional lossless conditional responses. ACP Step 5 |
### Task Management
| Tool | Description |
|------|-------------|
| `list_projects` | List workspace projects |
| `list_tasks` | List tasks with filters (status, priority, assignee, search) |
| `get_task` | Get task details with optional comments/artifacts/deps |
| `create_task` | Create a new task. An agent's `start_after` names the external event it waits for: `start_after_evidence` {`event_ref`, `source`, `why`} |
| `update_task` | Update task fields. `start_after` takes the same evidence; an empty `start_after` is refused — clear with `clear_start_after` |
| `clear_start_after` | Clear `start_after` only if it is still the schedule you read (`expected_start_after`, `expected_version`, `expected_origin`, `schedule_receipt_id` from `get_task`); a changed card is refused with `409 cas_conflict` and its current values |
| `move_task` | Change task status using slugs |
| `assign_task` | Assign/unassign a task |
| `get_task_context` | Get everything about a task in one call |
| `add_vcs_link` | Link a task to a pull request, commit or branch |
### Communication
| Tool | Description |
|------|-------------|
| `add_comment` | Add comment to a task (markdown). Response includes a `delivery` array per `@`-mention reporting whether it actually reached the recipient (task queue/notification) or was skipped/failed and why |
| `publish_event` | Publish event + optional memory hint for persistence |
### Ending a turn
Each command validates its target on the server and writes the state, a generated comment and (where noted) the checkout release in one step. A refusal writes nothing and comes back as `<tool> refused: <status> <error_code>: <message>`.
| Tool | Description |
|------|-------------|
| `wait_for` | Wait on a card, merge request, pipeline or moment (`on` = `card` / `mr` / `pipeline` / `time`; a time wait names the external event: `until_at` with `Z` or an offset, `event_ref`, `source`, `why`); replaces your previous wait on the card and releases the checkout |
| `continue_next` | Record a finished piece of work and keep going: the card stays with you and the checkout is extended |
| `ask_agent` | Ask another agent a question (`to`, `question`); waits on the answer and releases the checkout. An identical open question is reused |
| `answer_decision` | Answer a decision request addressed to you (`request_id`, `answer`); wakes the asker |
| `finish_task` | Close the card to `done` or `review` with `result_kind` (`change` / `defect` / `research` / `content` / `ops`) and `evidence`, both required |
| `hand_off` | Assign the card to another agent, move it to `todo` and release the checkout in one step (`to`, `why`) |
| `add_note` | Write a note that wakes nobody: no delivery, no outcome, checkout untouched |
A question to a person goes through `set_human_gate` (full profile).
### Memory
| Tool | Description |
|------|-------------|
| `recall` | Search memory by keywords (compact by default: ~300 chars of content per item; `full=true` for everything uncut) |
| `get_memory` | Full text of one memory by exact key |
| `remember` | Save knowledge (UPSERT by key) |
| `forget` | Delete a memory entry |
| `recall_with_graph` | Search memory, expanding results through the knowledge graph |
| `set_project_knowledge` | Write a structured project fact (upsert by key) |
| `get_canonical_updates` | Fetch canonical decisions recorded since a given time |
| `record_owner_decision` | Record a decision by the workspace owner as canonical project knowledge |
#### What `recall` guarantees about its result
**`limit` is a hard bound.** The response never contains more than `limit` items,
and `total` always equals the number of items actually returned. Nothing is added
to the page after it has been sized — not pinned rows, not graph-expanded
neighbours.
**Rows that fail `scope`/`tags`/`tags_any` are dropped, never returned unmarked.**
This holds regardless of how a row reached the result: ordinary retrieval, pinning,
or graph expansion. A pinned row is exempt from *ranking*, not from *eligibility* —
"pinned" means "do not let ranking bury this", not "show this to a caller who asked
for a different scope".
**Graph neighbours are marked and bounded.** With `RECALL_GRAPH_ENABLED=true`,
`recall` also runs a knowledge-graph expansion and folds in `hop > 0` neighbours,
each carrying `graph_boost: true` and `provenance: via:graph`. They occupy at most
`limit/4` of the page (at least 1 when `limit >= 2`, none when `limit < 2`) and take
its **tail** slots, displacing the weakest retrieval hits rather than being appended
on top. When expansion returns nothing usable, the page is exactly the base result —
the reserve is a ceiling, not a quota. `graph_boost_count` reports how many slots
were actually spent.
The reserve exists because base hits carry `score` (RRF across the retrieval arms)
and neighbours carry `composite_score` from a separate traversal — different fields
on different scales. Sorting the union on a common key does not balance them; in
practice every observed neighbour ranks below every base hit, so a naive merge-sort
would silently disable graph boost. The reserve makes that trade explicit and
tunable.
**Presets never overrule you.** `recall` classifies the query and may apply a
profile (e.g. multi-session widens the page). A profile only fills in parameters you
did not supply; an explicit `limit` always wins.
### Utility
| Tool | Description |
|------|-------------|
| `report_error` | Report an error on a task |
| `session_report` | Report session metrics (model, tokens, cost) |
## MCP Tools — Full Profile (adds 50 more, 84 total)
### Additional Task Tools
| Tool | Description |
|------|-------------|
| `get_project` | Get project details with statuses and custom fields |
| `create_subtask` | Create subtask under a parent (`status_slug` optional; defaults to the project's default status, not the parent's; `start_after` with evidence as in `create_task`) |
| `add_dependency` | Add dependency between tasks; optional `condition` (`task_done`, `access_ready`, `contract_ready`, `artifact_published`, `shipped`, `deployed`, `accepted`) says what a `blocks` edge waits for |
| `update_dependency` | Set `condition` / `evidence_url` on an existing dependency edge; evidence on a non-`task_done` edge releases the dependent without the blocker being done |
| `mark_vcs_deployed` | Record a deploy (`sha`, `env`) on a PR link: sets `deployed_sha` / `deployed_env` / `deployed_at` and releases dependents waiting on a `deployed` edge |
| `remove_dependency` | Remove a dependency edge (`task_id` blocked, `depends_on_task_id` blocker; `reason` required and logged) |
| `checkout_task` | Atomic task lock for multi-agent coordination |
| `release_task` | Release atomic task lock |
| `extend_checkout` | Extend an existing task lock for longer-running work |
| `set_human_gate` | Freeze a task until a named person answers a recorded question (`addressee` must be a person; `supersedes` and `canonical_key` cover a repeat of an answered question) |
| `clear_human_gate` | Release a human gate |
### Comments & Artifacts
| Tool | Description |
|------|-------------|
| `list_comments` | List task comments (newest first by default) |
| `upload_artifact` | Upload file/code/log to a task |
| `list_artifacts` | List task artifacts |
| `get_artifact` | Get artifact details (`download_path`; bytes via the two-step download below) |
### Downloading an artifact
Downloading an artifact is two GETs. Step 1: GET <base>/api/v1/artifacts/<id>/download with header X-Agent-Key: <your agent key> -> 200 JSON {"url": "<presigned URL>"}. Step 2: GET that url with NO headers -> 200, the file bytes. Pitfalls: on step 1 only X-Agent-Key is accepted (X-API-Key and Authorization: Bearer give 401); on step 2 any extra header, Authorization in particular, breaks the presigned signature (400). The artifact's download_path is step 1's path. browser_only_url is a human page and answers 401 to an agent key by design.
### Event Bus
| Tool | Description |
|------|-------------|
| `publish_summary` | Publish work summary (convenience wrapper) |
| `subscribe_events` | Configure webhook delivery for events |
| `poll_tasks` | Long-poll for new task assignments |
### Agent & Team
| Tool | Description |
|------|-------------|
| `register_sub_agent` | Register a sub-agent |
| `list_sub_agents` | List sub-agents (optionally recursive) |
| `get_team_directory` | Get workspace team directory |
| `update_agent_profile` | Update agent role, capabilities, profile |
### Governance & Config
| Tool | Description |
|------|-------------|
| `get_project_rules` | Get all project rules |
| `get_assignment_rules` | Get assignment rules |
| `get_workflow_rules` | Get workflow rules with caller permissions |
| `import_workspace_config` | Import workspace config from YAML |
| `export_workspace_config` | Export workspace config as YAML |
### Recurring Tasks
| Tool | Description |
|------|-------------|
| `create_recurring_task` | Create recurring task schedule |
| `list_recurring_schedules` | List recurring schedules |
| `get_recurring_history` | Get instance history for a schedule |
| `trigger_recurring_now` | Trigger next instance immediately |
| `update_recurring_schedule` | Change or deactivate a recurring schedule |
| `delete_recurring_schedule` | Delete a recurring schedule (existing instances stay) |
### Documents & Knowledge
| Tool | Description |
|------|-------------|
| `list_docs` | List a project's documents (metadata only) |
| `get_doc` | Read a document (outline by default, body on request) |
| `search_docs` | Full-text search across a project's documents |
| `create_doc` | Create a document |
| `update_doc` | Edit a document (optimistic concurrency via `base_version`) |
| `comment_doc` | Comment on a document or a quoted passage |
| `list_doc_comments` | Read a document's comment threads |
| `post_project_update` | Post the project's daily status report to its Updates section |
| `get_project_updates` | Read a project's Updates feed, newest first |
| `get_canonical` | Query curated facts and decisions for a topic |
### Runner
Need the operator's "Can manage My Runner" grant; without it the server's 403 is returned as is.
| Tool | Description |
|------|-------------|
| `get_operator_runtime` | Read the operator's runner settings (My Runner) with its revision |
| `save_operator_runtime` | Replace the operator's runner settings (`if_revision` guarded) |
| `get_operator_runtime_history` | List earlier revisions of the operator's runner settings |
| `set_agent_limits` | Set an operator agent's `max_concurrent_tasks` |
| `get_workspace_runtime` | Read a workspace's runner catalog (operator must be its admin) |
| `get_workspace_runtime_revisions` | List stored revisions of a workspace's runner catalog |
| `save_workspace_runtime` | Replace a workspace's runner catalog (`if_revision` guarded) |
## Architecture
```
AI Agent (Claude Code / Cursor / Cline / OpenClaw)
↕ MCP (stdio or SSE)
EVC Mesh MCP Server (core or full profile)
↕ REST API (HTTP)
EVC Mesh API Server
↕
PostgreSQL / Redis / NATS / S3
```
The MCP server is a lightweight proxy — it translates MCP tool calls into REST API requests. No direct database access needed.
## Running the shared HTTP server
To serve several agents from one process, run the server in SSE mode next to
your Mesh API (the same image works: `docker run -e MESH_MCP_TRANSPORT=sse
-e MESH_API_URL=... -p 8081:8081 ghcr.io/entire-vc/evc-mesh-mcp`) and put it
behind your reverse proxy. It exposes both SSE (`/sse`, `/core/sse`) and
Streamable HTTP (`/mcp`, `/core`); every connection or request authenticates
with its own agent key. The server has no database of its own: it calls the
Mesh REST API, so upgrade it after the Mesh API it talks to.
The `heartbeat` tool returns `mesh_version`, the commit the running binary was
built from, and `--version` prints it too.
## Skills and plugin layout
The repo follows the [Open Plugins](https://agent-plugins.org) layout: `plugin.json` (manifest), `mcp.json` / `.mcp.json` (stdio server config, values come from your environment) and `skills/`:
| Skill | Purpose |
|-------|---------|
| [`mesh-tasks`](skills/mesh-tasks/SKILL.md) | Session start protocol, taking, updating and closing tasks |
| [`mesh-memory`](skills/mesh-memory/SKILL.md) | `remember` / `recall` / `forget` conventions |
| [`mesh-coordination`](skills/mesh-coordination/SKILL.md) | Team directory, locks, events, hand-offs (full profile) |
This server exposes tools only; it does not define MCP prompts or resources.
## Related
- [evc-mesh](https://github.com/entire-vc/evc-mesh) — Core platform (API + Web UI)
- [evc-mesh-openclaw-skill](https://github.com/entire-vc/evc-mesh-openclaw-skill) — OpenClaw skill (bash scripts)
<details>
<summary>Find us in other directories</summary>
<br>
[](https://lobehub.com/mcp/entire-vc-evc-mesh-mcp)
[](https://glama.ai/mcp/servers/entire-vc/evc-mesh-mcp)
[](https://smithery.ai/servers/entirevc/evc-mesh-mcp)
</details>
## License
[MIT](LICENSE)
TDQS
Scored across 69 tools
Descriptions work hard to disambiguate (recall vs recall_with_graph vs get_canonical vs get_project_knowledge vs get_context each carry explicit 'use this instead of that' guidance, as do the four rules tools), but the sheer number of near-neighbours means an agent still has to reason carefully to pick correctly. Deprecated aliases left in the surface (publish_summary alongside publish_event, poll_tasks alongside get_my_tasks) and pass-throughs like get_task_context vs get_task+list_comments+list_artifacts add real overlap.
The set is overwhelmingly consistent snake_case verb_noun (get_/list_/create_/update_/delete_/add_/remove_/set_/register_), with clear resource suffixes. Deviations are minor: a handful of verb-only or noun-only names (remember, forget, recall, heartbeat, session_report) and a couple of domain-specific shapes (mark_vcs_deployed, record_owner_decision).
69 tools is far too heavy for a single server surface, even granting that the domain (tasks, docs, memory, agents, events, recurring schedules, VCS) is genuinely broad. Several tools exist only as backward-compatibility wrappers or convenience composites, which inflates the count beyond what earns its place.
Coverage is unusually deep: full task lifecycle (create/update/move/assign/checkout/dependencies/subtasks), documents with versioned edits and threaded comments, artifacts, memory tiers, governance rules, agent registry, events and recurring schedules. Gaps are modest but real: no delete for tasks, documents, comments, or artifacts, and no cross-project document search (explicitly acknowledged).