<!-- 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. -->

# ref_set

Moves a ref, or creates one, by compare-and-swap: give create, or expected\_old, the full 64-hex changeset id the ref held when you read it; a ref name or a relative revision is refused, since it is read when the call runs instead of pinning the id you read, and so is an id prefix, which stops resolving once another changeset shares it.

```yaml
title: "ref_set"
description: "Move a ref."
since: "0.1.0"
mutates: true
identity_required: true
cli_command: "swarmops ref set"
envelope_op: "ref_set"
request_type: "RefSetReq"
annotations: {"destructiveHint":true,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":false}
max_result_chars: null
```

## Input schema

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

| Field | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `create` | boolean | no | `false` | To create the ref. If it exists at another target, the move fails with `E_CAS_MISMATCH`; if it already holds `new`, nothing is written and the response is a replay (`already_applied`). Give this or `expected_old`, not both. |
| `expected_old` | string or null | no |  | To move an existing ref: the target the caller last saw, by its full changeset id; if the ref holds another, the move fails with `E_CAS_MISMATCH`. A ref name or a relative revision is read from the store when the move runs instead of pinning the id the caller read: the ref's own name always matches, so the compare-and-swap protects nothing, and `main~1` on `main` never matches. An id prefix pins the id the caller read only until another changeset shares it; then it is `E_AMBIGUOUS_ID`. `swarmops mcp` refuses all three (`E_INVALID_ARG`), and the CLI's `--expect` help warns against them. Give this or `create`, not both. |
| `name` | string | yes |  | The ref: `main` or `release/<name>`. |
| `new` | string | yes |  | The revision to point it at. 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": "`ref set`: move a ref, or create one, by compare-and-swap. Give exactly one of\n`expected_old` and `create`; both or neither is `E_INVALID_ARG`.",
  "properties": {
    "create": {
      "default": false,
      "description": "To create the ref. If it exists at another target, the move fails with `E_CAS_MISMATCH`;\nif it already holds `new`, nothing is written and the response is a replay\n(`already_applied`). Give this or `expected_old`, not both.",
      "type": "boolean"
    },
    "expected_old": {
      "anyOf": [
        {
          "pattern": "^[0-9a-fA-F]{64}$",
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "description": "To move an existing ref: the target the caller last saw, by its full changeset id; if the\nref holds another, the move fails with `E_CAS_MISMATCH`. A ref name or a relative\nrevision is read from the store when the move runs instead of pinning the id the caller\nread: the ref's own name always matches, so the compare-and-swap protects nothing, and\n`main~1` on `main` never matches. An id prefix pins the id the caller read only until\nanother changeset shares it; then it is `E_AMBIGUOUS_ID`. `swarmops mcp` refuses all\nthree (`E_INVALID_ARG`), and the CLI's `--expect` help warns against them. Give this or\n`create`, not both."
    },
    "name": {
      "description": "The ref: `main` or `release/<name>`.",
      "type": "string"
    },
    "new": {
      "description": "The revision to point it at. 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": [
    "name",
    "new"
  ],
  "title": "RefSetReq",
  "type": "object"
}
```

## Annotations

It moves or deletes refs by compare-and-swap, changing what they name. Run again with the same arguments, it finds them as it asked (`already_applied`) or not as it expected (`E_CAS_MISMATCH`), and changes nothing more.

| Annotation | Value | Meaning |
| --- | --- | --- |
| `destructiveHint` | `true` | 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` | `false` | True: the tool does not change its environment, the store. |

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

## CLI command

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

## Examples

### MCP

```json
{
  "id": 6,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "create": true,
      "name": "main",
      "new": "[c1]"
    },
    "name": "ref_set"
  }
}
```

```json
{
  "id": 6,
  "jsonrpc": "2.0",
  "result": {
    "content": [
      {
        "text": "{\"ok\":true,\"op\":\"ref_set\",\"api\":1,\"result\":{\"name\":\"main\",\"new\":\"[c1]\",\"already_applied\":false},\"meta\":{\"repo\":\"[tmp]/store\",\"elapsed_ms\":\"[elapsed_ms]\",\"resolved\":{\"[c1]\":\"[c1]\"},\"auth\":\"trust\"}}",
        "type": "text"
      }
    ],
    "isError": false,
    "structuredContent": {
      "api": 1,
      "meta": {
        "auth": "trust",
        "elapsed_ms": "[elapsed_ms]",
        "repo": "[tmp]/store",
        "resolved": {
          "[c1]": "[c1]"
        }
      },
      "ok": true,
      "op": "ref_set",
      "result": {
        "already_applied": false,
        "name": "main",
        "new": "[c1]"
      }
    }
  }
}
```

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

Give `create` to make a new ref, or `expected_old` to move one: the full 64-hex changeset id the
ref held when you read it, from `ref_list`, `show`, `log` or a commit's result.
The server refuses a ref name, a relative revision such as `main~1` and an id prefix there
(`E_INVALID_ARG`): a ref name or a relative revision is read when the call runs, so the
compare-and-swap would protect nothing, and a prefix stops resolving once another changeset
shares it.
The input schema says the same with a 64-hex `pattern`.
The CLI's `ref set --expect` still takes any revision.

A ref that no longer holds `expected_old` is `E_CAS_MISMATCH`, and nothing moves: read the ref
again and decide.

## Return information

A tool result: the envelope, whose `op` is `ref_set`, 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 ref set`](../commands/ref-set.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 `RefSetReq`, a field named twice among them included, or that break one of the server's own rules.
- [`E_IDENTITY_REQUIRED`](../errors.md#e_identity_required): the server runs with no identity.
- [`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 ref set`](../commands/ref-set.md#errors) lists them.

## See also

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