Skip to content

Start a workflow from an MCP event

An MCP server can offer events as well as tools and views: "a pull request's checks failed", "a message arrived". A workflow can run on one of them. This guide uses this project's own CI watcher, a small MCP server that turns GitHub Actions failures into checks.failed events, and the Fix failed checks workflow that starts an agent on each one.

The app asks servers for events in poll mode, following the MCP events draft (experimental-ext-triggers-events). A server that offers events only by push or webhook is shown as not supported yet.

Before you start

  • A project with an MCP server that offers events. The CI watcher ships with Agents Host and the Linux host, and needs a signed-in gh (gh auth status) on the host. See Packages/MCPServers/README.md in the repo.
  • For every setting, see Workflow triggers and actions, and for what the event carries, Events from MCP servers.

Start the server

Server events come from servers the project can already use: the project's .agents/mcp.json, your own ~/.agents/mcp.json, or a plugin. The CI watcher is in this project's .agents/mcp.json as ci, a local server the app hosts:

{ "mcpServers": { "ci": { "command": "ci-watcher", "hosted": true } } }
  1. In Project Settings ▸ Agent Configuration ▸ MCP servers, ci arrived with the project, so it is waiting for your OK: choose Approve. Until you do, it is asked for nothing, and the workflow's page says it is waiting for approval. See Add an MCP server from the registry.
  2. There is nothing to start. The app runs one copy of it on the host, the first time a workflow or an agent asks it something, and every agent shares that copy. It holds no token of its own: every GitHub call goes through your gh. See Share one copy of a local MCP server.

A local server without "hosted": true works too: the app runs its own copy of it to ask for events, with the same environment and secrets an agent gets, and each agent runs another for its tools.

Write the workflow

A server's event is named the way the app's own are, noun.verbed, with no prefix. Under the name, the keys are the event's own filters, sent to the server:

---
name: Fix failed checks
on:
  - checks.failed:
      repo: alexec/Agents
agent: new
cooldown: 5m
labels: [ci]
---

A pull request's checks failed. The event's data says which PR, branch and jobs.
Use the `ci` server's `failed_log` to read each failed job. If it is a test known to be
flaky under load, use `rerun_failed`. Otherwise fix it on the PR's branch in a worktree,
push, and use `comment_on_pr` to say what you changed.
  • Without server:, the trigger listens to every server here that offers checks.failed. Add server: ci to listen to that one only, or server: [github, gitlab] for a list.
  • The ci server's checks.failed, pr.merged, pr.closed (closed without merging) and pr.conflicted (began to conflict with its base, #712) all take repo, branch (only that PR branch) and pr (only that pull request, by number). Left out, repo is the one the project's folder pushes to (#702). Given together, each must hold: {pr: 551} hears only PR 551's failures and merge.
  • Its pr.opened (#833) is raised when a pull request opens, or when a draft is marked ready for review. It takes the same filters and anyone. Drafts never raise it. The Review pull requests workflow in .agents/workflows/ starts an agent on it that reads the diff and leaves one comment.
  • checks.failed, pr.opened and the issue events are raised only for the repo's owner, members and collaborators, never a bot (#728). anyone: true raises them for everyone.
  • Its issue events, issue.opened, issue.labelled, issue.commented and issue.closed, take repo (optional, as above) and label; all but issue.opened also take issue, by number. On issue.labelled, label is the label added: {repo: alexec/Agents, label: bug} hears each issue as it is marked a bug.
  • A filter the server's event doesn't take, or a value of the wrong type, shows on the workflow's page as an error, naming the filters it does take.
  • This project's copy of the workflow arrives with enabled: false. Turn it on with its Enabled switch, and approve it.

Each event starts the workflow once, after its prompt. The agent is told the event's details and, in a fenced block, the server's data, marked as coming from the server and not from you. The workflow's prompt and permission mode decide what the agent may do with it.

Check it is listening

Open the workflow's page, on the Mac, the iPhone or iPad, or the web page. Under Triggers, a line for each server the trigger listens to says how it is doing:

  • ci · checks.failed: Checked 20 s ago · last event 25 min ago: it is listening. A new subscription starts from now, so events from before it are not run.
  • Can't reach ci since 12:01 · trying again in 40 s, in orange: the server is down. The app tries again, waiting longer each time up to 5 minutes, and the line comes back by itself once the server answers.
  • A line in red says why it stopped: the server no longer offers the event, refused it, or doesn't take its filters. It is asked again once the file or the server's events change.
  • Events may have been missed since 09:14: the app was away longer than the server keeps events. Events it did get still run. Clear takes the line away.

Each event is also on the Events page, under the name checks.failed, with the run it started.

Turn it off

Turn the workflow off with its Enabled switch, and the app stops asking the server for its events. The CI watcher itself stops once nothing uses it: no agent's session has it open and no workflow listens for its events.

See also