> ## Documentation Index
> Fetch the complete documentation index at: https://docs.murmur.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# tool-link-policy catalog resource

> Catalog resource that names a set of MCP tool-link extraction patterns — the rules that decide which MCP tool results are captured as agent artifacts.

A tool link policy is a named list of extraction patterns selecting which MCP
tool results become artifacts of an agent's execution — a Linear issue the
agent filed, a Jira ticket, a Notion page — shown alongside its uploads and
preview links in the dashboard.

[Workspaces](/catalog/workspace) select a policy by name
(`tool_link_policy`). The platform ships the builtin **`murmur-tool-links`**,
which every workspace uses by default — currently Linear issue writes,
labeled by the issue identifier (e.g. `MUR-123`). A workspace-selected policy
**replaces** the builtin: copy its entries into your own policy to keep them,
or select a policy with an empty `patterns` list to disable capture entirely.

## Fields

| Name          | Type      | Required | Description                                                                                                                                |
| ------------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`        | string    | yes      | Unique identifier. DNS label format: `[a-z][a-z0-9-]{0,62}`.                                                                               |
| `description` | string    | no       | Human-readable description shown in the dashboard. Max 1024 bytes.                                                                         |
| `patterns`    | object\[] | no       | Extraction entries, evaluated independently against every MCP tool result. Empty = capture nothing. See [Pattern fields](#pattern-fields). |

## Pattern fields

Each entry pairs a `tool` predicate with **exactly one extraction arm**:

* **URL arm** (`url`) — capture URLs the tool result already contains.
* **Identifier arm** (`id` + `link`) — construct the URL from an identifier
  in the result text, for servers whose results carry a key but no human URL
  (or only an unclickable API URL — e.g. Jira's create result returns the
  issue key and a REST self-link, not the browse page).

| Name    | Type   | Required  | Description                                                                                                                                                                                                                                                                                     |
| ------- | ------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tool`  | string | yes       | RE2 matched (unanchored) against the MCP tool name that produced the result. Claude names MCP tools `mcp__<server>__<tool>`; Codex names them `<server>.<tool>`. A suffix pattern like `save_issue$` covers both backends.                                                                      |
| `url`   | string | one arm   | URL arm: RE2 matched (unanchored) against each canonicalized URL extracted from the tool result. Mutually exclusive with `id`/`link`.                                                                                                                                                           |
| `id`    | string | one arm   | Identifier arm: RE2 with **one or more** capture groups, run against the tool result text. Group 1 is the identifier (the artifact's default label); further groups compose multi-variable URLs. A match constructs an artifact only when every group captured non-empty text. Requires `link`. |
| `link`  | string | with `id` | Identifier arm: URL template expanding `id`'s captures via `$1`..`$N` (write `${n}` when a word character follows). Must reference at least one capture and expand to a parseable `https` URL.                                                                                                  |
| `label` | string | no        | RE2 with exactly one capture group, applied to the URL; a non-empty capture becomes the artifact's display label. Empty = the markdown link text (URL arm) or the captured identifier (identifier arm), then the URL host.                                                                      |

**Pick one arm per tool.** Entries evaluate independently, so configuring
both arms for the same tool can record two artifacts for one creation (the
result's own URL and the constructed one).

Match on *write* tools (`save_issue$`, `create_issue$`), not read tools — the
tool half of each entry is what keeps a 50-item listing result from becoming
50 artifacts. The same link re-surfacing later dedups to one artifact per
execution.

## Examples

### Extending the builtin (copy, then add)

```yaml theme={null}
name: team-links
description: "Builtin Linear capture plus Notion pages and Jira tickets"
patterns:
  # Copied from the builtin murmur-tool-links (a custom policy replaces it):
  - tool: save_issue$
    url: ^https://linear\.app/[^/]+/issue/
    label: /issue/([A-Za-z0-9]+-[0-9]+)
  # URL arm — Notion create results contain the page URL:
  - tool: create_page$
    url: notion\.so/
  # Identifier arm — Jira create results carry only the key; construct the browse URL:
  - tool: create_issue$
    id: '"key"\s*:\s*"([A-Z]+-[0-9]+)"'
    link: https://acme.atlassian.net/browse/$1
  # Multi-variable identifier arm — a Slack message permalink needs the channel
  # AND the ts (with its dot removed, expressed by splitting ts into two groups):
  - tool: post_message$
    id: '"channel":"(C[A-Z0-9]+)".*?"ts":"([0-9]+)\.([0-9]+)"'
    link: https://acme.slack.com/archives/$1/p$2$3
```

```bash theme={null}
cat team-links.yaml | murmur set tool-link-policy team-links
```

Then point the workspace at it:

```yaml theme={null}
# in the workspace resource
tool_link_policy: team-links
```

### Disabling capture

```bash theme={null}
echo 'name: no-capture' | murmur set tool-link-policy no-capture
```

A policy with no `patterns` captures nothing; set `tool_link_policy:
no-capture` on the workspace.

### Inspecting the builtin

```bash theme={null}
murmur get tool-link-policy murmur-tool-links
```

## Errors

| Code                  | Meaning                                                                  | What to do                                                                             |
| --------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `INVALID_ARGUMENT`    | `patterns[<i>].tool: ...` / `.url: ...`                                  | The pattern is empty or not valid RE2. Fix the regexp.                                 |
| `INVALID_ARGUMENT`    | `patterns[<i>].label: ... must have exactly one capture group`           | The `label` regexp must contain exactly one `(...)` group.                             |
| `INVALID_ARGUMENT`    | `patterns[<i>].id: ... must have at least one capture group`             | The `id` regexp needs at least one `(...)` group — group 1 is the identifier.          |
| `INVALID_ARGUMENT`    | `patterns[<i>]: url and id/link are mutually exclusive arms`             | The entry sets both extraction arms. Pick one: `url`, or `id` + `link`.                |
| `INVALID_ARGUMENT`    | `patterns[<i>]: url or id+link is required`                              | The entry has a `tool` but no extraction arm.                                          |
| `INVALID_ARGUMENT`    | `patterns[<i>].link: ... must reference an id capture`                   | The `link` template never uses `$1`..`$N`. Write `${n}` when a word character follows. |
| `INVALID_ARGUMENT`    | `patterns[<i>].link: ... must expand to an https URL`                    | The expanded template is not a parseable `https` URL with a host.                      |
| `INVALID_ARGUMENT`    | `name "murmur-..." is reserved`                                          | Names starting with `murmur-` are platform builtins. Pick another name.                |
| `FAILED_PRECONDITION` | `cannot delete tool-link-policy "<name>": referenced by workspace: <ws>` | A [workspace](/catalog/workspace) references this policy. Point it elsewhere first.    |

## Related

* [Workspaces](/catalog/workspace) — select a policy via `tool_link_policy`
* [`murmur set`](/cli/set) / [`murmur get`](/cli/get) — create, update, and inspect policies
