# ancestry

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

Along every parent, a merge's other parents too: whether from is an ancestor of to, their merge bases, and the changesets to reaches and from does not, newest generation first; pass a limit of 100 or less and page on with the cursor and the same from and to, since clients limit long tool results. limit 0 gives the two answers alone.

```yaml
title: "ancestry"
description: "Compare two revisions' ancestry."
since: "0.9.0"
mutates: false
identity_required: false
cli_command: "swarmops ancestry"
envelope_op: "ancestry"
request_type: "AncestryReq"
annotations: {"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true}
max_result_chars: null
```

## Input schema

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

| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `cursor` | string or null | no |  | Resume the entries: the previous page's `cursor`. Pass it back exactly as returned, with the same `from` and `to`; do not construct it (cursors are opaque). It holds the full ids the first page resolved: once a ref among `from` and `to` moves, it is `E_INVALID_ARG`, and the listing starts over. |
| `from` | string | yes |  | The first revision: the one asked to be an ancestor, and the one `entries` leave out. 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. |
| `limit` | integer | no | `50` | The most entries to return, 0 to 1,000 (more is `E_INVALID_ARG`): 0 gives `is_ancestor` and `merge_bases` alone. |
| `to` | string | yes |  | The second revision: the one whose ancestors `entries` list. 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": "`ancestry`: two revisions' ancestry along every parent, and a page of the changesets the\nsecond reaches and the first does not.",
  "properties": {
    "cursor": {
      "description": "Resume the entries: the previous page's `cursor`. Pass it back exactly as returned, with\nthe same `from` and `to`; do not construct it (cursors are opaque). It holds the full ids\nthe first page resolved: once a ref among `from` and `to` moves, it is `E_INVALID_ARG`,\nand the listing starts over.",
      "type": [
        "string",
        "null"
      ]
    },
    "from": {
      "description": "The first revision: the one asked to be an ancestor, and the one `entries` leave out. 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"
    },
    "limit": {
      "default": 50,
      "description": "The most entries to return, 0 to 1,000 (more is `E_INVALID_ARG`): 0 gives\n`is_ancestor` and `merge_bases` alone.",
      "format": "uint",
      "maximum": 1000,
      "minimum": 0,
      "type": "integer"
    },
    "to": {
      "description": "The second revision: the one whose ancestors `entries` list. 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": [
    "from",
    "to"
  ],
  "title": "AncestryReq",
  "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 ancestry`](../commands/ancestry.md) runs the same operation and prints the same envelope.

## Examples

### MCP

```json
{
  "id": 17,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "from": "[c1]",
      "limit": 1,
      "to": "main"
    },
    "name": "ancestry"
  }
}
```

```json
{
  "id": 17,
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "text": "{\"api\":1,\"meta\":{\"auth\":\"trust\",\"elapsed_ms\":\"[elapsed_ms]\",\"repo\":\"[tmp]/store\",\"resolved\":{\"[c1]\":\"[c1]\",\"main\":\"[landed]\"}},\"ok\":true,\"op\":\"ancestry\",\"result\":{\"cursor\":\"3.[landed].[c1].[landed]\",\"entries\":[{\"author_agent\":\"agent/worker-17\",\"created_at\":\"[created_at]\",\"id\":\"[landed]\",\"parents\":[\"[theirs]\"],\"title\":\"change f\"}],\"from\":\"[c1]\",\"is_ancestor\":true,\"merge_bases\":[\"[c1]\"],\"merge_bases_truncated\":false,\"to\":\"[landed]\"}}",
        "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": "ancestry",
      "result": {
        "cursor": "3.[landed].[c1].[landed]",
        "entries": [
          {
            "author_agent": "agent/worker-17",
            "created_at": "[created_at]",
            "id": "[landed]",
            "parents": [
              "[theirs]"
            ],
            "title": "change f"
          }
        ],
        "from": "[c1]",
        "is_ancestor": true,
        "merge_bases": [
          "[c1]"
        ],
        "merge_bases_truncated": false,
        "to": "[landed]"
      }
    }
  }
}
```

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 `ancestry`, 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 ancestry`](../commands/ancestry.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 `AncestryReq`, 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 ancestry`](../commands/ancestry.md#errors) lists them.

## See also

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