# Scripts, Sentry and tools

> Building flows · https://spoletta.com/docs/tools

Each kind of process has settings of its own: an agent's model, prompt and tools, a script's code, a Sentry query. This page covers them, with tips on getting each right.

## Agents

| Setting | What it does |
| --- | --- |
| Model | Which model the agent runs on. *Default* uses the worker's default. Different processes can use different providers' models, for example to get independent opinions. |
| Thinking | How much the model reasons before answering: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, or the default. |
| Prompt | What to do, in plain words. |
| MCP servers | Which of the app's MCP servers (Slack, Sentry, Jira…) this agent may use; none by default. A server you haven't connected yet shows *Not connected* and a **Connect** button. |
| Repositories | Which of the flow's repositories it works in: *Every repository of the flow*, or some, or none for an agent that only talks to other systems. |
| Tell it about the flow it works in, in OPL | Adds the flow's OPL to its prompt. |
| Rounds, Timeout (s), Retries | See [Review loops](https://spoletta.com/docs/loops.md) and [Handling failure](https://spoletta.com/docs/failures.md). |

### Writing a good prompt

-   Name inputs and outputs as the board names them: “Fix the exception in **Exceptions** following the **Style guide**.”
-   Say what good looks like: “the smallest change that is clearly right, with a test that fails without it.”
-   Leave out JSON shapes and plumbing. The engine appends the inputs, the output contract and any change request; see [What a step sees](https://spoletta.com/docs/step-contracts.md).
-   Put the contract for each field in its attribute's definition, not the prompt.

MCP servers are added by an admin under **Settings** → **MCP servers**, and each person connects them for themselves. An agent acts with the connections of whoever its run is for.

## Scripts

A script process runs bash in the worker, with `curl` and `jq`. Use it for whatever should happen the same way every time: calling an API, running a build, reshaping JSON.

| Name | Holds |
| --- | --- |
| `$INPUTS` | A directory with one file per object it takes, named after the object with unsafe characters made underscores (*Jira Ticket* → `$INPUTS/Jira_Ticket`), holding the object's JSON. |
| stdout | What it prints is its output, in the same contract as an agent's. |
| `$OUTPUTS` | Where it writes files, with their paths in file attributes. |
| `$WORKSPACE`, `$BRANCH` | The checkouts' directory and the run's branch, when the flow has repositories. |
| `$SINCE`, `$LAST_RUN` | In a run a schedule started, when the schedule's last run that ended done started, as a Unix timestamp. Empty on its first run and in runs started by hand. See [Schedules](https://spoletta.com/docs/schedules.md#fetching-only-what-changed). |
| exit code | Non-zero fails the step, with stderr as the error. |

Fetch ticket: prints a Ticket with key, summary and priority:

```bash
key=$(jq -r .data "$INPUTS/Ticket_key")
curl -fsS "https://jira.example.com/rest/api/2/issue/$key" \
  -H "Authorization: Bearer $JIRA_TOKEN" |
  jq '{key: .key, summary: .fields.summary, priority: .fields.priority.id | tonumber}'
```

> **Watch out:** A script can't decide a state. Route its output with [state rules](https://spoletta.com/docs/states.md#rules).

## Sentry

Drop **Sentry** from **Connectors**. It fetches issues from Sentry's API and yields them as a list: the usual first step of a scheduled flow.

| Setting | Holds |
| --- | --- |
| Organization, Project | Slugs. The organization is required. |
| Query | Sentry search syntax, `is:unresolved` by default, such as `is:unresolved level:error`. |
| Environment | Only issues seen in it. |
| Sort by | Last seen, First seen, Events, Users or Trends. |
| Limit | How many issues, 1 to 50. |

Each attribute of the list takes the issue field it is named after: `id`, `shortId`, `title`, `culprit`, `level`, `status`, `count`, `userCount`, `firstSeen`, `lastSeen`, `permalink` and `project`. A dotted name reaches into a field, such as `metadata.value` for an error's message. A list without attributes gets all of them. The server needs a `SENTRY_AUTH_TOKEN`.
