<!-- Generated by crates/swarmops-cli/src/reference/mcp_page.rs from the code: do not edit. Change the code, then run SWARMOPS_REGEN_REFERENCE=1 with the regen_reference test. -->

# diff

The files that differ between two revisions, in path order: with format names (the default) each one's path and status, with stat also its line counts, with unified also its hunks; pass a limit of 100 or less, keep max\_bytes at 32768 or less for unified, and page on with the cursor, since clients limit long tool results.

```yaml
title: "diff"
description: "Diff two revisions."
since: "0.1.0"
mutates: false
identity_required: false
cli_command: "swarmops diff"
envelope_op: "diff"
request_type: "DiffReq"
annotations: {"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true}
max_result_chars: null
```

## Input schema

`arguments` is a `DiffReq`: `swarmops schema DiffReq` prints the type. A field the type does not know is ignored.

| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `context` | integer | no | `3` | The unchanged lines around each change in a `unified` hunk. |
| `cursor` | string or null | no |  | Resume a diff: the previous page's `cursor`. Pass it back exactly as returned; do not construct it (cursors are opaque). This page starts after the file it names. |
| `format` | string | no | `"names"` | What to say of each file: `names` its path and status; `stat` also whether it is binary and, for text, the lines it inserts and deletes; `unified` also its kinds, its blobs and, for text, its hunks. What `diff` says of each file. |
| `from` | string | yes |  | The revision to diff from: the old side. A revision as the caller wrote it, for the store to resolve: a ref name (`main`, `release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits, optionally followed by `~N` for the N-th first-parent ancestor. |
| `limit` | integer | no | `500` | The most files to return: at least 1 (0 is `E_INVALID_ARG`). |
| `max_bytes` | integer | no | `32768` | The most bytes a `unified` page's files take, as the JSON array `files` they print as: at least 1 (0 is `E_INVALID_ARG`). The page stops before the first file that would take it past this, but always holds one file, whose hunks are cut to the first ones that fit (`hunks_truncated`) when they alone would pass it. A `names` or `stat` page is held by `limit` alone. |
| `paths` | array of string | no |  | Only the files at or below these repository paths (a file's path takes that file; one trailing `/` is taken); every file when empty or omitted. |
| `to` | string | yes |  | The revision to diff to: the new side. A revision as the caller wrote it, for the store to resolve: a ref name (`main`, `release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits, optionally followed by `~N` for the N-th first-parent ancestor. |

### JSON Schema

As `tools/list` gives it: each `$ref` replaced by its definition, so that it stands alone.

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "`diff`: the files that differ between two revisions, in path order, a page at a time.",
  "properties": {
    "context": {
      "default": 3,
      "description": "The unchanged lines around each change in a `unified` hunk.",
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    },
    "cursor": {
      "description": "Resume a diff: the previous page's `cursor`. Pass it back exactly as returned; do not\nconstruct it (cursors are opaque). This page starts after the file it names.",
      "type": [
        "string",
        "null"
      ]
    },
    "format": {
      "default": "names",
      "description": "What to say of each file: `names` its path and status; `stat` also whether it is binary\nand, for text, the lines it inserts and deletes; `unified` also its kinds, its blobs and,\nfor text, its hunks. What `diff` says of each file.",
      "oneOf": [
        {
          "const": "names",
          "description": "Its path and status.",
          "type": "string"
        },
        {
          "const": "stat",
          "description": "Also whether it is binary and, for text, the lines it inserts and deletes.",
          "type": "string"
        },
        {
          "const": "unified",
          "description": "Also its kinds, its blobs and, for text, its hunks.",
          "type": "string"
        }
      ]
    },
    "from": {
      "description": "The revision to diff from: the old side. A revision as the caller wrote it, for the store to resolve: a ref name (`main`,\n`release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits,\noptionally followed by `~N` for the N-th first-parent ancestor.",
      "type": "string"
    },
    "limit": {
      "default": 500,
      "description": "The most files to return: at least 1 (0 is `E_INVALID_ARG`).",
      "format": "uint",
      "minimum": 1,
      "type": "integer"
    },
    "max_bytes": {
      "default": 32768,
      "description": "The most bytes a `unified` page's files take, as the JSON array `files` they print as: at\nleast 1 (0 is `E_INVALID_ARG`). The page stops before the first file that would take it\npast this, but always holds one file, whose hunks are cut to the first ones that fit\n(`hunks_truncated`) when they alone would pass it. A `names` or `stat` page is held by\n`limit` alone.",
      "format": "uint64",
      "minimum": 1,
      "type": "integer"
    },
    "paths": {
      "description": "Only the files at or below these repository paths (a file's path takes that file; one\ntrailing `/` is taken); every file when empty or omitted.",
      "items": {
        "type": "string"
      },
      "type": "array"
    },
    "to": {
      "description": "The revision to diff to: the new side. A revision as the caller wrote it, for the store to resolve: a ref name (`main`,\n`release/1.2`), a full 64-hex changeset id or a unique prefix of at least 12 hex digits,\noptionally followed by `~N` for the N-th first-parent ancestor.",
      "type": "string"
    }
  },
  "required": [
    "from",
    "to"
  ],
  "title": "DiffReq",
  "type": "object"
}
```

## Annotations

It only reads the store.

| Annotation | Value | Meaning |
| --- | --- | --- |
| `destructiveHint` | `false` | True: a tool that changes the store may overwrite or delete what is there, not only add to it. |
| `idempotentHint` | `true` | True: calling it again with the same arguments changes nothing more. |
| `openWorldHint` | `false` | True: it reaches beyond a closed set of things. Every tool here stays inside the store. |
| `readOnlyHint` | `true` | True: the tool does not change its environment, the store. |

Its entry in `tools/list` carries no `_meta`.

## CLI command

[`swarmops diff`](../commands/diff.md) runs the same operation and prints the same envelope.

## Examples

### MCP

```json
{
  "id": 16,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "format": "stat",
      "from": "[c1]",
      "to": "main"
    },
    "name": "diff"
  }
}
```

```json
{
  "id": 16,
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "text": "{\"ok\":true,\"op\":\"diff\",\"api\":1,\"result\":{\"from\":\"[c1]\",\"to\":\"[landed]\",\"files\":[{\"path\":\"docs.md\",\"status\":\"added\",\"binary\":false,\"insertions\":1,\"deletions\":0},{\"path\":\"src/lib.rs\",\"status\":\"modified\",\"binary\":false,\"insertions\":1,\"deletions\":1}],\"truncated\":false},\"meta\":{\"repo\":\"[tmp]/store\",\"elapsed_ms\":\"[elapsed_ms]\",\"resolved\":{\"[c1]\":\"[c1]\",\"main\":\"[landed]\"},\"auth\":\"trust\"}}",
        "type": "text"
      }
    ],
    "isError": false,
    "structuredContent": {
      "api": 1,
      "meta": {
        "auth": "trust",
        "elapsed_ms": "[elapsed_ms]",
        "repo": "[tmp]/store",
        "resolved": {
          "[c1]": "[c1]",
          "main": "[landed]"
        }
      },
      "ok": true,
      "op": "diff",
      "result": {
        "files": [
          {
            "binary": false,
            "deletions": 0,
            "insertions": 1,
            "path": "docs.md",
            "status": "added"
          },
          {
            "binary": false,
            "deletions": 1,
            "insertions": 1,
            "path": "src/lib.rs",
            "status": "modified"
          }
        ],
        "from": "[c1]",
        "to": "[landed]",
        "truncated": false
      }
    }
  }
}
```

The server ran as `swarmops mcp --repo [tmp]/store --as agent/worker-17`, after `initialize`. Recorded by the MCP tests. A name in square brackets, such as `[tmp]` or `[c1]`, stands for what differs from run to run: the test's temporary directory, an id, a time, the version.

## Details

`format` picks how much each differing file carries: `names` (the default) its path and status,
`stat` also its line counts, `unified` also its hunks.
Pass a `limit` of 100 or less, keep `max_bytes` at 32768 or less for `unified`, and page on with
the `cursor`.
Files come in path order.

## Return information

A tool result: the envelope, whose `op` is `diff`, as `structuredContent` and again as the text of its one `content` block. `isError` is true exactly when the envelope's `ok` is false. On success the envelope's `result` is what [`swarmops diff`](../commands/diff.md#return-information) returns.

## Errors

A failed call is a tool result with `isError: true`; the envelope's `error.code` is one of the [error codes](../errors.md).

- [`E_INVALID_ARG`](../errors.md#e_invalid_arg): arguments that do not fit `DiffReq`, a field named twice among them included, or that break one of the server's own rules.
- [`E_NOT_A_REPO`](../errors.md#e_not_a_repo): the server's store is not there; the server serves on.
- [`E_INTERNAL`](../errors.md#e_internal): the operation panicked, a bug.

Then the errors of the operation, as [`swarmops diff`](../commands/diff.md#errors) lists them.

## See also

- [`swarmops diff`](../commands/diff.md)
- [MCP tools](README.md)
- [MCP integration](../../develop/mcp.md)
- [Error codes](../errors.md)
