# Desktop MCP Tool Design Contract

> Canonical path: `specs/daemon/mcp-tool-design-contract.md`
> Status: active
> Scope: model-visible outbound tools owned by AgentTeam Desktop, including AgentTeam collaboration, daemon, and knowledge surfaces. Provider-app proxies follow the security, result, annotation, and exposure rules in this contract but may preserve provider-owned tool and argument names.
> Current domain profile: Task tools.

## 1. Goal

Desktop exposes product capabilities to agent runtimes through MCP HTTP JSON-RPC and, for selected first-party capabilities, AgentTeam Exec CLI. This contract defines one model-facing product API independent of those transports.

The contract exists to make tools:

- predictable from their names and schemas;
- small enough for a model to select reliably;
- aligned with user intent instead of backend endpoints;
- safe under retries, stale context, and partial failure;
- independent from hidden runtime and authorization context;
- consistent across MCP, Agent Tools CLI, prompt guidance, and documentation;
- evolvable without permanently exposing compatibility aliases to the model.

This document does not define MCP wire protocol behavior, product-specific authorization, or result byte budgets. Those remain in the transport and owner specs.

## 2. Core Principles

### 2.1 Product intent before transport

A canonical tool represents one product intent. MCP raw names, Exec catalog IDs, CLI commands, handler functions, and server endpoints are transport or implementation mappings of that intent.

A canonical tool has exactly one model-visible transport by static ownership and
registration. Moving a tool between MCP and AgentTeam Exec changes that registration
for all runtimes; Desktop must not preserve duplicate registrations or implement
ownership by hiding MCP schemas and prompt guidance per session.

### 2.2 One product owner

Every tool has exactly one owning product domain. A new tool belongs to the owner that authorizes, validates, commits, and audits the operation.

A journey facade may orchestrate multiple owners, but it must not collapse their authorization boundaries or impersonate a lower-level owner.

### 2.3 User intent, not storage operations

Tools should match what a user or agent is trying to accomplish. They must not expose intermediate steps that exist only because of backend implementation.

Examples:

- validation required before commit should normally be internal to one mutation;
- workspace, calling agent, current target, and active workflow run are hidden context;
- workflow state changes use the workflow surface;
- uploading a file before sending it may be an internal step of message sending.

### 2.4 Prefer focused tools over universal action routers

A tool should have one recognizable success condition. Do not create universal tools such as `agentteam_action` or a domain tool with dozens of unrelated action variants.

A small action enum is acceptable only when all variants:

- operate on the same resource;
- share authorization and result shape;
- differ only by a small state operation;
- remain understandable without a large conditional schema.

### 2.5 Contracts replace prompt instructions

If a rule can be enforced by hidden context, schema, enum, validation, idempotency, authorization, or server-side orchestration, it must not rely solely on prompt text.

Prompts explain communication policy and journey choice. They do not compensate for ambiguous APIs.

## 3. Canonical Tool Naming

### 3.1 Raw names

First-party raw MCP names use:

```text
<domain>_<verb>
<domain>_<resource>_<verb>  // only when a domain has multiple distinct resources
```

Rules:

- lowercase ASCII `snake_case` only;
- domain first, verb last;
- singular resource nouns unless the tool explicitly performs a batch operation;
- use a stable product verb, not an HTTP verb or handler name;
- do not encode transport, runtime, version, provider, or implementation owner in the name;
- avoid synonyms within a domain.

Preferred verbs:

| Intent | Verb |
| --- | --- |
| Fetch one resource | `get` |
| Browse a bounded collection | `list` |
| Search by text or semantic criteria | `search` |
| Create one resource | `create` |
| Create multiple resources | `create_batch` |
| Partially change resource fields | `update` |
| Permanently remove a resource | `delete` |
| Set an idempotent state | `set_*` or a precise domain verb |

Examples:

- `task_get`, `task_list`, `task_create`, `task_update`, `task_checkpoint`;
- `message_send`（必填 exact `target + content`；Desktop 根据 trusted
  credential-bound runtime state 将 target 与 active delivery 关联，未命中时按
  显式主动目标发送）；
- `review_context_add` when `review` owns more than one resource type.

Do not mix `list_tasks`, `create_tasks`, and `task_get` in the same canonical surface.

### 3.2 Runtime-visible names

Runtime drivers may prefix or escape raw names, for example:

- `mcp__agentteam__message_send`;
- `mcp_agentteam_message_send`.

Those names are runtime syntax, not product vocabulary. Specs, prompts, metrics, and authorization policies identify canonical raw names.

### 3.3 AgentTeam Exec mapping

Exec-owned first-party actions have one stable domain/action route:

```text
Action ID: <domain>.<action>
CLI:       agentteam exec <domain> <action> -
Help:      agentteam exec <domain> <action> --help
Input:     agentteam exec <domain> <action> --input-schema
Output:    agentteam exec <domain> <action> --output-schema
```

MCP-owned tools and Exec-owned actions are separate model-visible surfaces. Do not create compatibility aliases for older command names.

## 4. Argument Naming And Schema

### 4.1 JSON names

First-party model-controlled JSON fields use lowercase `snake_case`.

Correct:

- `task_ref`
- `assignee_ref`
- `idempotency_key`
- `expected_updated_at`

Incorrect for new contracts:

- `taskRef`
- `assigneeRef`
- `identityDocument`
- `relevantURLs`

Adapters may translate to camelCase server DTOs. Server conventions must not leak into the model-facing schema.

### 4.2 Required and optional fields

- Require only information necessary to express business intent.
- Context already known to Desktop must not be required again.
- Do not use empty strings or magic sentinel values to mean “current” or “all.”
- Optional fields must state their default behavior.
- Mutually exclusive fields should use a discriminated union when the transport
  supports it; otherwise split the tools when the branches have materially
  different journeys. Desktop MCP root schemas are flat objects without
  top-level `oneOf` / `anyOf` / `allOf`, because supported runtimes do not share
  that root-union capability. When one MCP tool must retain closely related
  branches, use a closed intent enum and enforce branch-specific fields in the
  handler.

### 4.3 Objects and patches

Create tools accept the resource fields directly unless a wrapper clarifies a real union. Avoid a single schema with mutually exclusive `task` and `tasks` properties.

Update tools use an explicit `patch` object:

```json
{
  "task_ref": "#t12",
  "patch": {
    "priority": "high",
    "assignee_ref": "@alice"
  }
}
```

Rules:

- omitted means unchanged;
- `null` means clear only when the field is explicitly nullable;
- an empty patch is rejected;
- write-once or owner-controlled fields do not appear in the patch schema.

### 4.4 Boolean and enum fields

- Boolean names describe the true state: `include_children`, not `children` or `no_children`.
- Do not use a boolean when more than two stable states exist.
- Enum values use lowercase `snake_case`.
- Every enum documents its default and forward-compatibility behavior.
- Product-facing views such as `mine` or `active` are preferable to requiring models to compose internal status/filter tuples.

### 4.5 Arrays and batch operations

- Use plural field names for arrays.
- Bound array length in schema or validation.
- Batch operations must report per-item outcomes and overall replay behavior.
- A batch variant is a separate tool when it has different authorization, confirmation, failure, or result semantics.

### 4.6 Time values

- Absolute timestamps use RFC3339 UTC strings and end in `_at`.
- Durations use integer seconds and end in `_seconds`.
- A time choice uses a discriminated union, not several optional mutually exclusive fields.
- Concurrency timestamps use names such as `expected_updated_at`.

## 5. Identifier And Reference Contract

### 5.1 IDs, refs, numbers, and targets

| Form | Meaning |
| --- | --- |
| `*_id` | One canonical opaque identity; no aliases or human syntax |
| `*_ref` | A resolvable reference accepting documented user-facing or canonical forms |
| `*_number` | A human-visible number meaningful only within a declared parent scope |
| `target` | A AgentTeam message/scope routing target |
| `cursor` | An opaque pagination token; never parsed by the model |

A field must not be called `*_id` if it also accepts names, handles, aliases, URLs, or numbers.

### 5.2 Resource refs

Prefer a single resource ref at tool boundaries when the product already has a visible canonical reference.

For tasks, `task_ref` should accept documented forms such as `#t12`, a canonical task scope ref, or a hidden current active task. Models should not normally need both `channel` and `task_number` after a task has been identified.

Resolution errors return structured candidate information; callers should not be forced to preload an options tool.

### 5.3 Targets

`target` is reserved for AgentTeam communication/scope routing, including channel, DM, reply thread, and raw task scope targets. Domain resource identifiers must not be called `target` unless they are message destinations.

### 5.4 Hidden principal and context

The following are daemon/runtime-controlled unless the product explicitly supports cross-scope operation:

- workspace ID;
- authenticated/calling agent ID;
- session/channel binding;
- current inbound target;
- active task reference;
- active workflow run ID;
- provider installation and authorization token;
- execution owner/private context.

They must not appear as model-controlled fields merely because the downstream API needs them. Desktop injects them after parsing model arguments.

## 6. Read, List, And Search

### 6.1 Get

`*_get` returns one canonical resource detail. It should accept one ref and default to the current resource only when hidden session context identifies it unambiguously.

Large child sections are bounded summaries. Full discussion/history and large artifacts use their owning read surfaces.

### 6.2 List versus search

- `*_list` browses a bounded collection without free-text retrieval.
- `*_search` requires a query and is used for locating, deduplication, or relevance retrieval.
- Do not overload list with an ignored or rejected search query.
- Keep both tools when their user journeys, ranking, pagination, or cost differ.

### 6.3 Pagination

Inputs use:

```json
{
  "limit": 20,
  "cursor": "opaque"
}
```

Outputs use:

```json
{
  "items": [],
  "next_cursor": "opaque"
}
```

Rules:

- omit `next_cursor` when no next page exists;
- do not expose page numbers and cursors on the same canonical surface;
- cursors are opaque and scoped to the query/filter set;
- changed filters invalidate an old cursor;
- time/sequence pagination is domain-specific and must use one documented direction;
- output must not mix `has_more`, `nextCursor`, and `next_cursor`.

### 6.4 Views and filters

Default model-facing list tools expose a small set of stable journey views, for example `active`, `mine`, or `done`. Do not expose every database field, lifecycle stage, UI quick filter, and sort mode by default.

Advanced filters belong in an advanced/conditional tool or in UI APIs unless agents have a demonstrated journey requiring them.

## 7. Mutations, Idempotency, And Concurrency

### 7.1 Mutation shape

A mutation:

- names the resource or intent precisely;
- validates the complete intent before side effects;
- returns the refreshed compact resource or a stable receipt;
- exposes partial outcomes only for explicit batch operations;
- does not require the model to execute backend preparation steps.

### 7.2 Idempotency

Create, send, request, submit, and composite mutations that may duplicate side effects accept optional `idempotency_key` unless the runtime can always inject one safely.

The key is scoped to actor, canonical tool, and owning resource scope. Replaying the same key and semantically identical arguments returns the original outcome. Reuse with different arguments returns a classified conflict.

Idempotent state setters such as claim/unclaim should express the desired state instead of toggling it.

### 7.3 Optimistic concurrency

Updates vulnerable to stale overwrite accept `expected_updated_at`, `expected_version`, or an owner-specific revision token. Conflict results include a suggested refresh action.

### 7.4 Destructive operations

Permanent deletion remains a distinct, clearly named tool. Its description states what is deleted, whether recovery is possible, and whether explicit human intent is required.

Do not hide destructive operations inside a generic action enum.

### 7.5 Composite operations

A composite tool is appropriate when multiple backend steps implement one user intent and share one authorization decision, for example validate-and-create.

It must be atomic or safely replayable. If true atomicity is impossible, the result must explicitly classify committed and uncommitted effects and provide recovery guidance.

## 8. Results And Errors

### 8.1 Structured results

First-party tools return structured content as the authority. A short text rendering may accompany it for runtimes that primarily consume text.

Recommended success envelope:

```json
{
  "data": {},
  "meta": {
    "next_cursor": "opaque"
  }
}
```

Simple results may omit the envelope only when the shape remains unambiguous and consistent across the tool family.

### 8.2 Error contract

Expected product failures return a structured error:

```json
{
  "error": {
    "code": "task_ref_ambiguous",
    "message": "Task reference matches multiple tasks.",
    "retryable": false,
    "field_errors": {
      "task_ref": "Use a channel-qualified task reference."
    },
    "suggested_action": {
      "tool": "task_list",
      "arguments": {"query": "..."}
    }
  }
}
```

Rules:

- codes use stable `snake_case`;
- messages are human-readable and safe to show;
- validation identifies fields;
- retryability is explicit;
- a suggested action is advisory and cannot bypass authorization;
- internal command, transport, stack, token, lease, and hidden-context details are not exposed.

### 8.3 Result budgets

Every transport enforces the product result budget after normalization. List/search/get tools bound item counts and large fields before the final transport budget.

Truncation is explicit and includes the continuation mechanism. A successful truncated result must not look complete.

## 9. Annotations, Permission, And Exposure

### 9.1 Required metadata

Canonical registry entries declare:

- owner;
- permission class;
- read-only status;
- destructive status;
- idempotency behavior;
- hidden context dependencies;
- result budget class;
- MCP name;
- Agent Tool ID and optional CLI route;
- exposure policy;
- deprecation state.

MCP annotations and CLI policy are generated from or checked against this metadata.

### 9.2 Permission classes

At minimum distinguish:

- read-only;
- agent-scoped mutation;
- human-confirmed/destructive mutation;
- owner-private or worker-only operation.

A journey facade spanning owners must perform each owner's authorization; facade authorization does not grant downstream authority.

### 9.3 Stable exposure

The registered Desktop MCP surface is complete and stable across runtime
sessions. Role, capability, authenticated product scope, or explicit user intent
may authorize a call, but must not remove registered tools from `tools/list`.
Advanced browse, diagnostics, bulk mutation, provider administration, and
low-frequency lifecycle operations can instead belong to the statically
separate AgentTeam Exec Catalog.

Visibility does not grant authorization. Every invocation is revalidated at the
owning MCP `CallTool` or AgentTeam Exec action boundary; unknown tools and actions
fail closed.

## 10. Descriptions And Prompt Guidance

### 10.1 Tool descriptions

A description answers:

1. what user intent the tool fulfills;
2. when to choose it over adjacent tools;
3. the most important side effect or destructive property.

Descriptions must not contain long communication policies, implementation history, obsolete migration details, or extensive multi-tool workflows.

Tool definitions are persistent model-visible Context. For large or frequently
available tools, keep a compact-JSON size budget beside the schema contract
tests. The budget is a deterministic regression guard, not a claim about an
exact provider tokenizer or billable Token count.

### 10.2 Field descriptions

A field description states accepted semantics, format, and default. Examples are short and do not introduce undocumented aliases.

### 10.3 Prompt layers

- Core prompt: cross-tool safety and communication invariants only.
- Tool schema: local selection and argument contract.
- Conditional journey guidance: injected only when the relevant family is visible or intent requires it.
- Server errors: actionable correction for runtime-specific validation failures.

A prompt must not instruct the model to supply a hidden principal/context field.

## 11. Compatibility And Versioning

### 11.1 Additive compatible changes

Normally compatible:

- adding an optional field with a documented default;
- adding a result field;
- broadening a ref resolver without ambiguity;
- improving descriptions without changing semantics.

### 11.2 Breaking changes

Breaking:

- renaming a tool or argument;
- changing requiredness or default behavior;
- narrowing accepted values;
- changing side effects, authorization, or result meaning;
- replacing an opaque ref with a different identity.

### 11.3 Alias policy

Compatibility aliases may remain in parsers and adapters but should not remain in the default model-visible schema.

A deprecated canonical tool:

- is excluded from new sessions by default;
- records usage telemetry;
- returns a structured replacement hint when directly invoked under compatibility policy;
- has a documented removal gate rather than an arbitrary date.

Do not expose two aliases such as `assigneeRef` and `assignee_ref` and ask the model to choose.

### 11.4 Provider proxy exception

Provider-owned tools may preserve upstream names and schemas to maintain provider compatibility. Desktop still enforces:

- authorization and token scope;
- bounded normalized results;
- safe annotations and exposure;
- no hidden principal/token parameters;
- classified proxy versus provider failures.

Desktop-owned facade tools over providers follow the full first-party naming contract.

## 12. Canonical Registry And Contract Tests

The target implementation uses one canonical registry as the source for:

- MCP tool definitions;
- Agent Tool IDs, parameters, permissions, and CLI help;
- prompt tool-name references;
- machine-readable inventory;
- documentation tables;
- contract lint tests.

Until full generation exists, CI must compare handwritten surfaces.

Required checks:

1. raw names follow the canonical naming grammar;
2. first-party model fields use `snake_case`;
3. no deprecated aliases appear in the default schema;
4. MCP and Agent Tools parameter requiredness/types agree;
5. permission and read-only/destructive annotations agree;
6. hidden context fields do not appear in model input;
7. list/search pagination follows the common contract;
8. mutation tools declare idempotency and concurrency behavior;
9. every tool is classified by owner and exposure policy;
10. prompt references only tools visible in the session;
11. results pass semantic and transport budgets;
12. old names have an explicit migration mapping or removal decision.

## 13. Canonical Task Tool Profile

This section is normative for the supported Desktop Task surface. The Task profile contains seven model-visible tools:

1. `task_list`
2. `task_get`
3. `task_create`
4. `task_update`
5. `task_set_claim`
6. `task_checkpoint`
7. `task_delete`

`task_list` browses when `query` is empty and searches or deduplicates when `query` is non-empty. `task_update` carries stable profile fields and lifecycle status. Claim and release use the `task_set_claim` idempotent desired-state setter (`claimed` expresses the target ownership state and is never a toggle of current state).

### 13.1 Task request context

Task tools provide bounded discovery, detail reads, creation, stable profile updates, hierarchy metadata, ownership changes, lifecycle status updates, and append-only checkpoints. Task scope messaging carries plans, questions, progress narratives, decisions, and evidence explanations.

Desktop supplies authenticated request context after parsing model input:

- workspace and authenticated agent identity;
- the active Workflow Run and callback effect identity when a Workflow-aware
  mutation explicitly opts into that context;
- the exact Task locators supplied by the model-visible tool;
- result routing metadata for the active task when a mutation or detail read succeeds.

Model input cannot override workspace, agent, Workflow Run, or callback effect
identity. Hidden Workflow identities are not properties in the model-visible
Task schemas. The seven tool schemas require their documented Task locators,
and every Task request uses a workspace-scoped Agent bearer token.

### 13.2 Current tool contracts

#### `task_list`

Browses or searches one bounded board collection. `channel` is required; `query`, `status`, `includeSubtasks`, `limit`, `cursor`, `sort`, and `filters` are optional. Empty `query` browses; non-empty `query` searches the same collection. The default limit is 20 and the maximum is 100. `status=all` is accepted only for a non-empty search query.

#### `task_get`

Returns compact task detail with bounded sections and metadata-only artifacts. Both `channel` and `number` are required.

#### `task_create`

Creates work from exactly one locator and a non-empty `tasks` collection. The
locator is either:

- `channel`, with optional positive `parent`, for board-local creation; or
- canonical `parentTaskScope: {kind: "task", scopeId: "..."}` for subtask
  creation when the caller already has an exact Task scope.

`parentTaskScope` is mutually exclusive with both `channel` and `parent`. The
server resolves its workspace, board, and parent Task from authenticated
storage inside the create transaction; it never asks the model to recover a
board slug or Task number. The scope must be an active Task in the route
workspace and the authenticated Agent must remain a member of that exact Task
scope. Malformed, non-Task, deleted, cross-workspace, unauthorized, and
ambiguous locators are rejected before Task mutation.

One task is represented by a length-one collection. A title-only collection
uses the bounded bulk path; one rich task can provide all inferable attributes
accepted by the schema, including optional canonical `assignee`. Creation has
one semantic operation; `mode` and assign/start variants are not accepted.

`parent`, when a positive integer, creates the task(s) as subtasks of that parent on the same board: the server routes to the subtask create operation, so a subtask is a real child rather than a silently flat top-level task. A rich single task travels as the subtask input; a title batch travels as titles — never both (the store rejects both set). A non-positive `parent` is rejected.

`target` is the explicit, model-provided source target (for example a reply
thread `#channel:threadid`) the task is created from. It is provenance, not a
second Task locator. When present the task is attributed to that thread so it
appears in that thread's task panel and nests with its subtasks; the server
resolves `target` and rejects it if the resolved board conflicts with either
the explicit `channel` or the board resolved from `parentTaskScope`. `target`
is model input expressing business intent.

`idempotencyKey` is an optional, trimmed, non-blank model-authored key of at
most 128 UTF-8 bytes for Task creation performed during an active Workflow
callback. When it is present, Desktop
injects the hidden active Run and callback effect identity after model input
validation. The server rejects the key outside an active callback and
revalidates the exact authenticated responder against the immutable callback
receipt. The first use reserves a durable receipt and creates the top-level
batch or subtask batch in the same transaction. A retry with the same
workspace, Agent, Run, callback, key, and canonical create payload returns the
original Task identities with `reused: true`, even if the callback has since
completed. Reusing the key with a different payload conflicts. Receipt replay
does not allocate Task numbers, append Task Activity, or emit another live
Task-created event. The canonical payload digest includes the exact
`parentTaskScope` locator and source provenance, so changing either while
reusing a key is a conflict rather than an accidental replay.

#### `task_update`

Updates stable task profile fields through required `channel` and `number` locators. The schema uses flat optional profile fields, including title, description, option/member refs, canonical `assignee`, checklists, messages, URLs, workspaces, attachments, blocker, thread, parent, and lifecycle status. At least one mutation field is required. Review Request reviewer is a separate review fact and is not a Task update field.

#### `task_checkpoint`

Appends one durable high-signal checkpoint. `channel`, `number`, and `summary` are required. Routine narrative progress belongs in task scope messages.

At the canonical Agent Runtime boundary, `summary` is the only checkpoint text input.

#### `task_set_claim`

Claims or releases a bounded batch of tasks on the explicitly selected board by expressing the desired ownership state. `channel` is required; `numbers` is a non-empty bounded batch; `claimed` is the idempotent desired state (`true` claims, `false` releases) and is never a toggle of the current state. The authenticated Agent principal is the assignee of record, set server-side; the model never supplies an assignee. The response reports the count of tasks brought to the requested state plus any per-task failures (for example, a task already held by another Agent, or a release attempted by a non-assignee).

#### `task_delete`

Deletes one task created by the authenticated Agent. `channel` and `number` are required. A leaf task needs no `subtaskMode`; a parent task requires `promote` to detach and preserve its children or `delete` to remove the full child set atomically. Recursive deletion is allowed only when every child has the same Agent creator. The operation reuses the canonical Task deletion transaction, including task-scope tombstones and deletion events, and returns board-local task numbers rather than internal ids.

### 13.3 Supported HTTP boundary

The seven tools make exactly one operation request each to the workspace-scoped Agent Runtime API:

| Tool | Method and route |
| --- | --- |
| `task_list` | `GET /api/v1/workspaces/{workspaceId}/agents/{agentId}/runtime/tasks` |
| `task_get` | `POST /api/v1/workspaces/{workspaceId}/agents/{agentId}/runtime/tasks/get` |
| `task_create` | `POST /api/v1/workspaces/{workspaceId}/agents/{agentId}/runtime/tasks` |
| `task_update` | `POST /api/v1/workspaces/{workspaceId}/agents/{agentId}/runtime/tasks/update` |
| `task_checkpoint` | `POST /api/v1/workspaces/{workspaceId}/agents/{agentId}/runtime/tasks/add-checkpoint` |
| `task_set_claim` | `POST /api/v1/workspaces/{workspaceId}/agents/{agentId}/runtime/tasks/set-claim` |
| `task_delete` | `POST /api/v1/workspaces/{workspaceId}/agents/{agentId}/runtime/tasks/delete` |

Desktop does not preload a resource locator, resolve endpoint, options endpoint, or search endpoint. Missing workspace/Agent credential context fails locally before any upstream request. A 401 may refresh the per-Agent token once and replay the same operation once; it never falls back to machine or channel credentials.

The human resource API under `/api/v1/workspaces/{workspaceId}/channels/...` and `/tasks/...` is a separate human-session surface. It is not the transport for Task MCP tools.

### 13.4 Runtime boundary

The seven workspace-scoped Agent Runtime operations above are the only Agent
Task HTTP surface. Desktop must have workspace and Agent credential context
before issuing them; there is no unscoped compatibility route.

## 14. Related Specs

- [Runtime Outbound Tools](./runtime-outbound-tools.md)
- [Agent MCP Result Budget Contract](./agent-mcp-result-budget.md)
- [Prompt Compiler Architecture](./prompt-compiler-architecture.md)
- [Task Scope](../task/task-scope.md)
