Skip to content
← Back to blog
AI engineering

Automate repetitive checks with local n8n and an AI agent

12 min read

Written by: Jonathan Reis on

A lot of engineering time goes to checks that follow the same shape: look at something, compare it to what you saw before, decide whether it matters, tell someone if it does. This guide wires n8n as a local state machine and an AI agent as the reader — from a bare npm install to a pull-request monitor that pings your desktop.

OpenGraph preview image for this article. Automate repetitive checks with local n8n and an AI agent

Every developer keeps a private list of things to re-check throughout the day: PRs waiting for review, schedules that may have failed, dashboards drifting. None of it is hard. All of it is interruption — the kind that breaks focus and still gets missed.

This post shows how to turn one of those checks into an automated pipeline: n8n running locally as the state machine, an AI coding agent wired in through n8n’s MCP server, and a pull-request monitor that sends a macOS notification when something actually changes. Everything runs on your machine — no cloud account, no Docker required.

Step 1: install n8n locally without Docker

The official recommendation is Docker, but for a personal machine npm is enough and skips an abstraction layer:

npx --yes --registry=https://registry.npmjs.org n8n

Two constraints worth knowing before the first run:

  • Node version: the supported range is 20.19 to 24.x. Newer versions often work, but the range is what the docs guarantee.
  • Corporate npm proxies: if your company routes npm through a CVE-blocking security proxy, some transitive dependency of n8n may be refused. Adding --registry=https://registry.npmjs.org bypasses the proxy for this single install.

The first launch creates ~/.n8n: a SQLite database, session state, and — the important one — an encryption key in ~/.n8n/config. That key encrypts every credential you will ever store in n8n. Losing it means losing the credentials, not just a config file. Copy it somewhere safe before anything else:

cp ~/.n8n/config ~/.n8n/config.backup-$(date +%Y%m%d)

The editor will also ask you to create an account on first browser access. That is a local owner account in ~/.n8n — no cloud signup — protecting the editor and the tokens inside it.

Step 2: wire the MCP server to your AI agent

n8n ships an official MCP server since 2.33. It exposes the whole instance to AI clients: search workflows, read and edit them, execute them, create and query data tables, validate workflow SDK code. This is the piece that makes an agent a first-class operator instead of someone you copy JSON to.

Enable it in the UI:

  1. Settings → Instance-level MCP → Enable MCP access.
  2. Connect → API key — copy the generated token.

Then point your agent harness at the endpoint:

{
  "mcpServers": {
    "n8n": {
      "type": "http",
      "url": "http://localhost:5678/mcp-server/http",
      "headers": {
        "Authorization": "Bearer ${N8N_MCP_TOKEN}"
      }
    }
  }
}

The credential distinction that costs hours

n8n has two authentication surfaces, and they are not interchangeable:

  • The MCP access token (above) authenticates /mcp-server/http — the AI client surface.
  • The Public API key, under Settings → n8n API, authenticates /api/v1/* — the REST surface for scripts and CI.

The JWT payload tells them apart: the MCP token’s aud claim is mcp-server-api. Try to use it against the REST API and you get a bare 401 with no hint why. This distinction alone consumed a good chunk of my afternoon — a variable named N8N_API_KEY holding an MCP token is a trap that bites twice.

One more detail inside that trap: avoid naming your own variables with the N8N_ prefix. The n8n process consumes N8N_* variables as its own configuration, and they do not reach command execution inside workflows the way you would expect. A neutral name like N8N_MCP_TOKEN replaced by something outside the host’s namespace saves a restart cycle.

Step 3: build a state store with data tables

Monitors need memory: what did I see last time, what changed since. n8n data tables are built exactly for this — structured rows scoped to a project, no external database.

Create one under the Data tables tab. For a pull-request monitor:

pr_number | repo | title | head_sha | review_decision
approved_by | comment_count | updated_at

The comparison keys are the design. A changed head_sha is a new push. A changed review_decision or approved_by is review movement. A changed comment_count is someone talking. Everything else gets swallowed silently.

The Data Table node handles get, insert, update, and upsert inside the workflow with internal authentication — no HTTP, no tokens, no credentials to configure.

Step 4: build the check — a pull-request monitor

The workflow graph:

Schedule Trigger (15 min)
  → Execute Command: collect open PRs as JSON (gh CLI)
  → Data Table: Get rows (previous snapshot)
  → Code node: diff current vs previous
  → IF anything changed
      → Execute Command: macOS notification per change
      → Data Table: Insert new snapshot rows

The collector is a small script on disk, called by the Execute Command node — deterministic logic does not belong inline in the workflow. It uses the GitHub CLI you already have authenticated:

gh search prs --author @me --state open --json repository,number,title,updatedAt,url

then gh pr view <n> --json reviewDecision,reviews,comments,headRefOid per PR for the comparison fields. No credential to set up in n8n; auth rides the existing keyring.

Three implementation notes that prevented real rework:

  • Close stdin on CLI commands. The Execute Command node keeps stdin open by default, and CLIs that wait on input hang forever — the execution stays “running” indefinitely. Appending </dev/null to the command is the fix.
  • Keep snapshots append-only. Data tables have no row delete through the node or the MCP. Insert rows only for new or changed items, and diff against the latest row per key. GitHub never reuses PR numbers, so stale rows cannot produce false positives.
  • Notify on changes only. “Nothing changed” is the most common outcome and must stay silent. New PR, new push, approval, change request, comment delta, PR closed — each becomes one notification with the delta in the body.

The notification is a one-liner in macOS, fired from the same node that runs the diff:

osascript -e 'display notification "delta here" with title "PR Monitor" sound name "Glass"'

Desktop notifications before Slack was deliberate: zero setup, and swapping the channel later is a one-node edit.

Step 5: let the agent build it — where AI actually helps

The monitor itself needs no model; the diff is mechanical. The agent earns its place in two other spots.

Assembly through MCP. Describe the automation in one sentence — “watch my open PRs, notify me when there are new reviews, comments, or pushes” — and the agent assembles it through the MCP: it searches for the right nodes, validates the Workflow SDK code before saving, creates the workflow, wires the schedule, and publishes. Mine made mistakes — a node type the MCP refused, a hardcoded session ID — and each fix was a conversation, not a JSON rewrite.

Interpretation nodes. The diff in the graph is field-level. For a human summary — “3 new comments, one requesting changes on the auth file” — a Code node calls a local agent headlessly with the delta. n8n stays the state machine; the agent becomes the reader.

Cross-system mornings. The same skeleton pulls Shortcut stories, Grafana alerts, and Notion updates into one daily brief.

The division that held: n8n owns scheduling, state, retries, and delivery. The agent owns reading, judgment, and writing.

The complete flow, consolidated

# 1. Install (in a fresh terminal so shell env vars load)
npx --yes --registry=https://registry.npmjs.org n8n

# 2. Back up the encryption key immediately
cp ~/.n8n/config ~/.n8n/config.backup-$(date +%Y%m%d)

# 3. In the UI: enable instance-level MCP, generate the MCP token
# 4. Export it for your MCP client — avoid the N8N_ prefix in the name
export N8N_MCP_TOKEN="..."

# 5. Point the agent's MCP config at http://localhost:5678/mcp-server/http
# 6. Create the data table holding the monitor's before/after state
# 7. Build the workflow: schedule → collect → diff → notify → persist
# 8. Enable MCP access on the workflow and publish it

Checklist

  • Back up ~/.n8n/config before storing any credential.
  • Keep the two credential types straight: MCP token vs public API key.
  • Avoid the N8N_ prefix when naming your own environment variables.
  • Close stdin (</dev/null) on CLIs inside Execute Command nodes.
  • Enable MCP access per workflow; eligibility requires a supported trigger.
  • Store snapshots append-only and diff against the latest row per key.
  • Notify on changes only — silence is a feature.

Extending the pattern

Once the skeleton exists — schedule, collect, diff, judge, notify — anything that changes and matters plugs in: Grafana alert digests, Shortcut story movement, Notion page updates, release deploys. If you want the full context of the agent behind it, the earlier posts cover why I switched to OMP and how a personal marketplace packages that setup.

Related postHow to create a personal OMP marketplace for shared agent configuration10 min readWritten by: Jonathan Reis on