---
title: "The smallest useful MCP tool"
excerpt: "Every tool on the MCP surface is a permanent commitment. The smallest one we've shipped, the one we keep considering but don't, and the discipline that makes the difference."
author: argus
category: Agents
date: "2026-04-26"
image: /blog-mockups/theme-e2-blueprint/the-smallest-mcp-tool.webp
---

Adding a tool to your MCP surface is more like adding a column to your database than adding an endpoint to your REST API. Once it's there, agents will use it. Once agents are using it, you can't easily remove it without breaking their behavior. Every tool is a long-lived commitment to a name, a parameter shape, and a semantic contract.

This is true even when the tool seems small. *Especially* when the tool seems small. The temptation is "just add `get_workspace_count` real quick" because the implementation is one line. The cost isn't the implementation; the cost is that now agents in the wild will assume `get_workspace_count` exists and use it for things you didn't anticipate.

This piece is the smallest tool we've actually shipped, the smallest tool we keep almost shipping but don't, and the discipline that decides which side of the line a proposal lands on.

## The smallest tool we ship: `recent_events`

Our smallest MCP tool is `recent_events`. It takes one optional parameter (a limit, defaulting to 50) and returns a list of recent events the calling principal has access to.

```typescript
recent_events({ limit: 50 }): Event[]
```

That's it. No filters, no pagination cursors, no event-type selection, no workspace targeting. The tool returns the last N events the principal can see, ordered by recency.

Why is this small? Because the *shape* of the response is rich: each event has actor, action, target, timestamp. The agent can filter what it cares about on the client side — "events about workspace X this week" or "events authored by Govind." The tool itself is dumb.

This dumbness is deliberate. We could have shipped `recent_events_for_workspace`, `recent_events_by_actor`, `recent_events_by_type`, and so on. We didn't. Each filter would have been a separate tool name, separate parameter shape, separate test surface, separate documentation page. The agent would have to learn five names instead of one.

By keeping the tool a single shape with a rich response, we let the agent compose. The agent can call `recent_events`, look at the results, and decide what's relevant. If the agent needs to see only events about a specific workspace, it filters in its own logic — not by us shipping a new tool.

## The tool we keep almost shipping: `set_workspace_visibility`

There's a tool we've considered shipping at least four times: `set_workspace_visibility`. It would let an agent change a workspace from `private` to `org` to `public` (or vice versa).

We don't ship it. Here's why.

Visibility changes are *dangerous* in the consent-gate sense — they widen access. An agent flipping a private workspace to public is the kind of mistake that can leak company data. We could ship `set_workspace_visibility` with a consent-gate handshake, but the more we examined it, the more we realized:

- **The use case is rare.** Visibility changes are usually a deliberate decision a human makes, not a routine operation an agent automates.
- **The recovery is hard.** If the agent flips a workspace public and an external party scrapes it before the human approves the gate (which we wouldn't trigger because we wouldn't ship the gated version)... wait, we *would* gate it. But still: the kind of agent that wants to change visibility is the kind we should be careful giving.
- **Adjacent tools are sufficient.** The agent can ask the human directly via the chat surface: "I think this workspace should be org-visible — can you change it?" The human does the change in the UI. We lose nothing.

The discipline is "if a tool's failure mode is high and its use case is rare, ship it as a human-only operation." Visibility falls in that bucket. So does account deletion, plan changes (we ship those, but with strong gates), API key rotation, and a few others.

## The tool we shipped after a long argument: `update_doc`

The middle ground is `update_doc`. This tool replaces the body of a doc with new TipTap JSON. It's the agent's primary write tool for prose.

We argued about this one for a week before shipping. The arguments against:

- It's a write tool, so the cost of mistakes is higher than a read tool.
- The TipTap JSON shape is complex; agents will produce malformed JSON and the tool will fail unhelpfully.
- It bypasses the live-collab merge layer (which humans use), so two agents writing simultaneously will overwrite each other.

The arguments for:

- Agents need to be able to draft prose. There's no smaller version of this.
- The shape-cap protection means even malformed JSON can't take down the doc.
- Last-write-wins is acceptable for now (CRDT is on the roadmap).

We shipped `update_doc`. The implementation goes through `writeDocBody` so all the substrate-level protections (shape cap, attribution, etc.) apply. The tool is the right shape for what agents actually do.

The lesson: the question isn't "is this tool dangerous?" The question is "is this tool *necessary* to a real workflow, and can we make the dangerous parts safe?" `update_doc` passed both tests; `set_workspace_visibility` passed neither.

## The discipline

What gets a tool onto the surface, in our actual checklist:

1. **Does it solve a recurring agent need?** Not "an agent might want this someday." A real, recurring need that exists in workflows we've seen.
2. **Is the shape stable?** The parameters and response shape need to make sense across the lifecycle of the agent. If we'd want to change the shape in 3 months, don't ship.
3. **Are the safety surfaces in place?** Identity, attribution, scope, consent gates, shape caps — does this tool fit cleanly into the existing layers, or does it require new ones?
4. **Is there a smaller alternative?** Could the agent compose existing tools to do this, even if less directly?

A proposal has to pass all four. Most don't. Most proposals fail at #4 — the agent can already do this with two existing tools and some logic, so we don't ship a new one.

## What our surface looks like today

We have 20 MCP tools as of this writing. They're divided into:

- **Workspaces** (5): list, get, create, update metadata, delete (gated).
- **Rows** (5): list, get, create, update, delete.
- **Doc** (2): get_doc, update_doc.
- **Comments** (2): list, create.
- **Billing** (3): get_plan, upgrade_plan (gated), downgrade_plan (gated).
- **Other** (3): support_request, recent_events, search.

The list grows about 1-2 tools per quarter. The discipline of "what gets in" is what keeps the surface stable.

## What this composes with

The MCP surface is the agent-facing edge of all the substrate-level protections we've built. It composes with:

- **Identity** — every tool call is attributed to the calling agent.
- **Authorization** — every tool checks the agent's workspace memberships before reading or writing.
- **Consent gates** — gated tools require the propose-confirm pattern; unrelated to the surface size, related to the operation type.
- **Shape caps** — write tools route through substrate functions that enforce caps.

We covered the bigger pattern in [AI-agent-first primitives](/blog/ai-agent-first-primitives). The smallest-MCP-tool discipline is one piece of that — keep the surface narrow, every tool earns its slot.

## Cross-links

- [AI-agent-first primitives](/blog/ai-agent-first-primitives) — the substrate primitives the surface sits on.
- [Consent gates for dangerous operations](/blog/consent-gates-for-dangerous-ops) — why the gated tools are gated.
- [Backmerges or bust](/blog/backmerges-or-bust) — substrate-level fixes generalize across surfaces.

## FAQ

**Why is the MCP surface a long-lived commitment?**

Once a tool is on the surface, agents in the wild start depending on it. Removing it later breaks their behavior. The cost isn't the engineering effort to add it; it's the cost of ever having to deprecate it. Treat each tool addition like a database column change.

**What makes a "small" MCP tool?**

A tool with one job, one parameter shape, and a rich response that lets the agent compose downstream. `recent_events` is small; `recent_events_for_workspace_filtered_by_actor_in_last_24h` is not. The smaller the tool's surface, the more flexible the agent's use of it.

**Why not ship more tools?**

Each tool is a permanent commitment to a name, parameters, and semantic contract. More tools mean more surface for agents to learn, more documentation to maintain, more breakage when shapes change. The discipline of "minimum viable surface" preserves long-term flexibility.

**How do you decide what goes on the surface?**

Four criteria: real recurring need, stable shape, safety surfaces in place, no smaller composition possible. A proposal has to pass all four. Most fail at the fourth — the agent can already do this with existing tools and some logic.

**What's a tool you specifically don't ship?**

`set_workspace_visibility`. It would let an agent change workspace privacy. We don't ship it because the use case is rare, the failure mode is high (data leakage), and the human-only path (user changes it in the UI) is fine. Adjacent tools are sufficient for the agent's actual needs.

<!-- json-ld -->

```json
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "The smallest useful MCP tool",
  "description": "Every tool on the MCP surface is a permanent commitment. The smallest one we've shipped, the one we keep considering but don't, and the discipline that makes the difference.",
  "datePublished": "2026-04-26",
  "author": { "@type": "Person", "name": "Argus" },
  "publisher": { "@type": "Organization", "name": "Dock", "url": "https://trydock.ai" },
  "image": "https://trydock.ai/blog-mockups/style-d-dreamscape/the-smallest-mcp-tool.webp",
  "mainEntityOfPage": "https://trydock.ai/blog/the-smallest-mcp-tool"
}
```
