---
name: run
description: Do real work in the user's own tools (Gmail, Calendar, HubSpot, Slack, Notion, Stripe and anything else they connect in One) through the Run connector. Use when the user asks to clean up, label, archive, update, move, send or report on many items, to chain calls across apps, to build a live view or dashboard of their data, or to re-run or trust a saved run. Writes are always reviewed and applied by the user, never by you.
---

# Run

Run (https://run.forkable.dev) is an MCP connector. You write a short
TypeScript script, Run executes it in a sandbox on the user's own One
sign-in, and every change it wants to make waits in a review table the user
ticks and applies. Every run is saved to the user's account so they can run
it again on the web.

## 1. Make sure the connector is there

Look for Run's tools (`run`, `action_types`, `list_connections`). If they are
missing, tell the user how to add it and stop:

- **Claude** (claude.ai or the desktop app): Settings, Connectors, Add custom
  connector, URL `https://run.forkable.dev/mcp`. Sign in with One when asked.
- **ChatGPT**: Settings, Apps and Connectors, turn on Developer mode, then
  create a connector with the same URL.
- **Claude Code**: `claude mcp add --transport http run https://run.forkable.dev/mcp`,
  then `/mcp` to sign in.
- **Any other MCP client** that supports remote servers with OAuth: the same
  URL.

On sign-in, One's consent page lists the user's own connections. They pick
the tools and the access for each (read only, read and write, or exact
actions). Run asks for nothing in advance.

## 2. Find the actions

1. `list_connections` shows what the grant reaches: each account (a Gmail
   shows its address), its connection key and the access on each. If the
   user has several accounts of one app, every call on it must say which:
   pass `connection: "<address or key>"` in the input. Use the account the
   user named; if they didn't, ask which one before you write the run.
   If the tool the user wants is not there, say so plainly: they connect it
   at https://app.withone.ai and sign in to Run again to add it to the grant.
2. If the One MCP is connected (`find_one_actions`), use it to find actions;
   it has the deepest docs and the same ids. Otherwise use `find_actions` and
   `action_docs`. If the One MCP is missing, mention once that adding
   `https://mcp.withone.ai/mcp` (same sign-in) makes runs better.
3. Call `action_types` with only the ids you picked. It returns typed stubs
   named `one.<platform>.<name>`.

## 3. Check for a saved run first

Call `list_my_runs` with a few words about the task ("newsletter gmail").
If a saved run already does the job, run it again with `run({ id, inputs })`
and skip to step 5. Only write a new one when nothing fits.

## 4. Write the run

Call `run` with `{ code, title }`. The script is plain TypeScript with
top-level await and no imports.

- `one.<platform>.<name>(input)` or `one.call(actionId, input)`, where input
  is `{ pathParams?, query?, body?, connection? }`. `connection` picks the
  account; each staged row then names it, and applying can only touch it.
- `console.log` is all you get back (about 8k tokens). Log counts and a few
  examples, never whole payloads.
- `input({ label, default })` for values the user may change when they run
  it again ("Older than (days)", 30).
- `review({ title, description, columns, rows, group, approveLabel })` to make
  the review read like the task. `columns` are `{ key, label }`; each row's `step` is the
  staged result of a write.
- `fetch` throws. There is no network except `one`.
- Limits: 50 reads, 500 staged writes, 30 seconds.

Reads run at once. Writes (send, create, update, delete, label, archive) do
not run: each returns `{ staged: true, step }` and changes nothing yet. A
call outside the grant throws `declined, 403`; report it, do not work around
it.

```ts
const days = input({ label: "Older than (days)", default: 30 });
const { threads } = await one.gmail.listViewEmailThreads({
  body: { query: `category:promotions older_than:${days}d`, numberOfThreads: 50 },
});
const rows = [];
for (const t of threads) {
  const step = await one.gmail.modifyThreadLabels({
    pathParams: { id: t.id },
    body: { removeLabelIds: ["INBOX"] },
  });
  rows.push({ step, subject: t.subject });
}
review({ title: `Archive ${rows.length} promotions`, columns: [{ key: "subject", label: "Subject" }], rows });
console.log(`${rows.length} threads staged`);
```

The stub names above are examples. Use the names `action_types` gave you.

## 5. After the run

- If writes were staged, the review opens in the chat. Say how many changes
  are waiting and stop. Never ask the user to confirm in chat, never call
  `apply_run` or `run_action` yourself, and never say a change happened until
  the result says it did.
- When the outcome comes back, give a one-line summary: applied, skipped,
  failed, declined.
- Read-only runs: answer from the log.

## 6. Saved runs, UIs and trust

- `list_my_runs`, `show_run` to find and open a saved run. `run({ id, inputs })`
  runs it again; `run({ id, code })` saves a new version. `rename_run`,
  `delete_run` as asked.
- For a live view or dashboard, call `ui_guide` once, then `render_ui`. Its
  writes wait for the user's confirm too.
- Only when the user asks for it ("trust this one", "run it without asking"),
  call `trust_run`. It shows a one-tap card; the tap turns trust on, not you.
  Trust covers one version, the write actions it has and a cap (50 by
  default). `untrust_run` turns it off at once.

## Good prompts to suggest

- "Archive every newsletter in my inbox older than 30 days."
- "Find HubSpot deals with no owner and assign them to me."
- "Make a dashboard of this week's Stripe payments."
- "Every Friday's meetings with no notes: list them and draft a follow-up for each."
