# merge_base

> Release: 0.11.0 · Last changed: 2026-11-09 · Source: https://github.com/KeiaiLab/SwarmOps/blob/stable/docs/reference/mcp-tools/merge-base.md

Where rev forks from onto along first parents: the base, both revisions as full ids, and the first page of the unified diff from the base to rev; pass a limit of 100 or less and keep max\_bytes at 32768 or less, then page on with diff from the base to the rev id with the page's cursor, since clients limit long tool results and the ids keep the pages on this base.

```yaml
title: "merge_base"
description: "Find a merge base."
since: "0.11.0"
mutates: false
identity_required: false
cli_command: "swarmops merge-base"
envelope_op: "merge_base"
request_type: "MergeBaseReq"
annotations: {"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true}
max_result_chars: null
```

## Input schema

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

| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `limit` | integer | no | `500` | The most files of the diff's first page, as `diff`'s `limit`: at least 1 (0 is `E_INVALID_ARG`). |
| `max_bytes` | integer | no | `32768` | The most bytes of the first page's files, as `diff`'s `max_bytes`: at least 1 (0 is `E_INVALID_ARG`). |
| `onto` | string | yes |  | The side it would land on, such as `main`. 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, or a ref's earlier value: `<ref>@{N}` the value it held N moves before its current one (`@{0}` is the current value), `<ref>@{at=<unix seconds>}` the value set by its newest move recorded at or before that instant (the writer's clock, moves read in journal order); optionally followed by `~N` for the N-th first-parent ancestor. |
| `rev` | string | yes |  | The change's side: the diff's 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, or a ref's earlier value: `<ref>@{N}` the value it held N moves before its current one (`@{0}` is the current value), `<ref>@{at=<unix seconds>}` the value set by its newest move recorded at or before that instant (the writer's clock, moves read in journal order); 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": "`merge_base` (CLI `merge-base`): where `rev` forks from `onto` along first parents, and the\nfirst page of what `rev` changed since, in one snapshot.",
  "properties": {
    "limit": {
      "default": 500,
      "description": "The most files of the diff's first page, as `diff`'s `limit`: at least 1 (0 is\n`E_INVALID_ARG`).",
      "format": "uint",
      "minimum": 1,
      "type": "integer"
    },
    "max_bytes": {
      "default": 32768,
      "description": "The most bytes of the first page's files, as `diff`'s `max_bytes`: at least 1 (0 is\n`E_INVALID_ARG`).",
      "format": "uint64",
      "minimum": 1,
      "type": "integer"
    },
    "onto": {
      "description": "The side it would land on, such as `main`. 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, or a\nref's earlier value: `<ref>@{N}` the value it held N moves before its current one (`@{0}`\nis the current value), `<ref>@{at=<unix seconds>}` the value set by its newest move\nrecorded at or before that instant (the writer's clock, moves read in journal order);\noptionally followed by `~N` for the N-th first-parent ancestor.",
      "type": "string"
    },
    "rev": {
      "description": "The change's side: the diff's 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, or a\nref's earlier value: `<ref>@{N}` the value it held N moves before its current one (`@{0}`\nis the current value), `<ref>@{at=<unix seconds>}` the value set by its newest move\nrecorded at or before that instant (the writer's clock, moves read in journal order);\noptionally followed by `~N` for the N-th first-parent ancestor.",
      "type": "string"
    }
  },
  "required": [
    "rev",
    "onto"
  ],
  "title": "MergeBaseReq",
  "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 merge-base`](../commands/merge-base.md) runs the same operation and prints the same envelope.

## Examples

### MCP

```json
{
  "id": 20,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "limit": 1,
      "onto": "main",
      "rev": "[head]"
    },
    "name": "merge_base"
  }
}
```

```json
{
  "id": 20,
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "text": "{\"api\":1,\"meta\":{\"auth\":\"trust\",\"elapsed_ms\":\"[elapsed_ms]\",\"repo\":\"[tmp]/store\",\"resolved\":{\"[head]\":\"[head]\",\"main\":\"[landed]\"}},\"ok\":true,\"op\":\"merge_base\",\"result\":{\"base\":\"[c1]\",\"diff\":{\"files\":[{\"binary\":false,\"deletions\":1,\"hunks\":[{\"lines\":[{\"op\":\"remove\",\"text\":\"pub fn f() {}\\n\"},{\"op\":\"add\",\"text\":\"pub fn g() {}\\n\"}],\"new_lines\":1,\"new_start\":1,\"old_lines\":1,\"old_start\":1}],\"insertions\":1,\"new_blob\":\"[id6]\",\"new_kind\":\"file\",\"old_blob\":\"[id7]\",\"old_kind\":\"file\",\"path\":\"src/lib.rs\",\"status\":\"modified\"}],\"from\":\"[c1]\",\"to\":\"[head]\",\"truncated\":false},\"onto\":\"[landed]\",\"rev\":\"[head]\"}}",
        "type": "text"
      }
    ],
    "isError": false,
    "structuredContent": {
      "api": 1,
      "meta": {
        "auth": "trust",
        "elapsed_ms": "[elapsed_ms]",
        "repo": "[tmp]/store",
        "resolved": {
          "[head]": "[head]",
          "main": "[landed]"
        }
      },
      "ok": true,
      "op": "merge_base",
      "result": {
        "base": "[c1]",
        "diff": {
          "files": [
            {
              "binary": false,
              "deletions": 1,
              "hunks": [
                {
                  "lines": [
                    {
                      "op": "remove",
                      "text": "pub fn f() {}\n"
                    },
                    {
                      "op": "add",
                      "text": "pub fn g() {}\n"
                    }
                  ],
                  "new_lines": 1,
                  "new_start": 1,
                  "old_lines": 1,
                  "old_start": 1
                }
              ],
              "insertions": 1,
              "new_blob": "[id6]",
              "new_kind": "file",
              "old_blob": "[id7]",
              "old_kind": "file",
              "path": "src/lib.rs",
              "status": "modified"
            }
          ],
          "from": "[c1]",
          "to": "[head]",
          "truncated": false
        },
        "onto": "[landed]",
        "rev": "[head]"
      }
    }
  }
}
```

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.

## Return information

A tool result: the envelope, whose `op` is `merge_base`, 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 merge-base`](../commands/merge-base.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 `MergeBaseReq`, 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 merge-base`](../commands/merge-base.md#errors) lists them.

## See also

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