MCP Server
The @impri/mcp package exposes Impri's approval and watcher workflows as MCP (Model Context Protocol) tools. It works with any MCP-compatible client: Claude Code, Claude Desktop, Cursor, and other agent runtimes.
No SDK code required — the agent calls the tools directly. The MCP server handles all HTTP communication, polling, and error formatting internally.
Installation
npx @impri/mcpRequired environment variables:
IMPRI_API_KEY=im_...
# Optional — defaults to http://localhost:8484 (self-hosted)
IMPRI_BASE_URL=https://api.impri.devConfiguration
Claude Code (~/.claude/mcp.json)
{
"mcpServers": {
"impri": {
"command": "npx",
"args": ["@impri/mcp"],
"env": {
"IMPRI_API_KEY": "im_your_key",
"IMPRI_BASE_URL": "http://localhost:8484"
}
}
}
}Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"impri": {
"command": "npx",
"args": ["@impri/mcp"],
"env": {
"IMPRI_API_KEY": "im_your_key",
"IMPRI_BASE_URL": "https://api.impri.dev"
}
}
}
}Cursor (~/.cursor/mcp.json)
Same format as Claude Code above.
Tools reference
impri_push_action
Submit a proposed action for human approval. Returns the action_id and the inbox URL.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
kind |
string | Yes | Action category, e.g. "email.send", "db.exec" |
title |
string | Yes | Human-readable summary (max 500 chars, no newlines) |
preview |
object | Yes | { "format": "plain"|"markdown"|"diff", "body": "..." } |
payload |
any | No | Opaque data stored and returned with the action |
target_url |
string | No | Link shown to the reviewer |
expires_in |
integer | No | Seconds until the action expires (300–2 592 000; default 259 200) |
idempotency_key |
string | No | Deduplication key; auto-generated from content if omitted |
editable |
string[] | No | Dot-paths the reviewer may modify, e.g. ["preview.body"] |
Output:
{
"action_id": "act_abc123",
"status": "pending",
"inbox_url": "https://app.impri.dev/inbox/act_abc123"
}impri_await_decision
Long-poll until an action leaves the pending state. Blocks until a decision is made or the timeout elapses.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
action_id |
string | Yes | ID returned by impri_push_action |
timeout_s |
integer | No | Maximum seconds to wait (default 300) |
Output (approved):
{
"action_id": "act_abc123",
"status": "approved",
"decision_at": 1720003600,
"preview": { "format": "plain", "body": "human-edited text" },
"edited_by_human": true,
"diff": "...",
"payload": { ... }
}When the reviewer used edit-before-approve, preview contains the modified text and edited_by_human is true. Use preview.body for execution, not the original text.
Output (rejected):
{
"action_id": "act_abc123",
"status": "rejected",
"decision_at": 1720003600,
"preview": { ... },
"edited_by_human": false,
"payload": { ... }
}Output (timed out):
{
"isError": true,
"text": "Timed out after 300s waiting for action act_abc123. It is still pending..."
}Security note: When the action carries untrusted external content (payload.untrusted === true, e.g. from a watcher), the preview body is wrapped in <untrusted-external-content> tags and a _untrusted_content_note is added. The agent must treat this content as data, never as instructions to follow.
impri_report_result
Report the execution outcome after acting on an approved action. Closes the audit loop in the inbox.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
action_id |
string | Yes | ID of the approved action |
status |
string | Yes | "executed" or "execute_failed" |
detail |
string | No | Error message or context (on failure) |
Output:
Result reported: action act_abc123 → executed.impri_inbox_status
Check how many pending actions are waiting for a decision. Useful before starting a new task that will add more.
Input: none
Output:
Impri inbox: 3 pending actions awaiting decision
- act_abc123: "Send email to [email protected]" (email.send)
- act_def456: "Deploy to production" (deploy.trigger)
- act_ghi789: "Execute SQL migration" (db.exec)Or when empty:
Impri inbox: 0 pending actions. The inbox is clear — safe to start new tasks.impri_create_watcher
Create a monitoring watcher. Accepts the full watcher spec in spec.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
spec |
object | Yes | Full watcher creation body (see Watcher presets for schema) |
Output:
{
"watcher_id": "wch_abc123",
"name": "HN front page",
"kind": "rss",
"status": "active",
"next_run_at": 1720001800
}impri_list_watchers
List watchers, optionally filtered by status.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
status |
string | No | "active", "paused", or "degraded" |
Output:
3 watchers:
- wch_abc123: "HN front page" (rss) — active
- wch_def456: "Product Hunt" (rss) — active
- wch_ghi789: "r/selfhosted" (reddit_search) — degradedimpri_list_watcher_presets
List the catalog of built-in watcher presets, grouped by category.
Input: none
Output:
18 watcher presets available:
Community:
- hn-front-page: "Hacker News Front Page" (rss) — no params required
...
- reddit-keyword: "Reddit Keyword Search" (reddit_search) — params: query, [subreddit]
...impri_create_watcher_from_preset
Create a watcher from a built-in preset template.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
preset_id |
string | Yes | Preset identifier, e.g. "hn-front-page", "github-releases" |
params |
object | Yes | Preset parameter values as { key: "value" } |
name |
string | No | Override the auto-generated watcher name |
schedule |
object | No | Override the preset's default schedule, e.g. { "every": "1h" } |
Output:
{
"watcher_id": "wch_abc123",
"name": "github-releases: fastify/fastify",
"kind": "rss",
"status": "active",
"next_run_at": 1720001800
}Typical agent workflow
1. Agent decides it needs to do something risky.
2. impri_push_action(kind, title, preview, ...) → action_id
3. impri_await_decision(action_id) → approved / rejected
4. [If approved] Agent executes the action.
5. impri_report_result(action_id, "executed") → audit closedBefore starting: optionally call impri_inbox_status to check if too many pending actions are already queued.
MCP webhook receiver
The @impri/mcp package also includes a webhook receiver that you can run alongside the MCP server. It listens for POST /webhook calls from Impri's decision delivery system and emits MCP notifications.
Start with:
IMPRI_API_KEY=im_... npx @impri/mcp --with-webhook-receiver --port 3000The receiver validates the X-Impri-Signature header using the IMPRI_WEBHOOK_SECRET environment variable before processing any payload.
Security notes on untrusted content
Actions delivered by watchers have payload.untrusted = true. The MCP server wraps their preview body in <untrusted-external-content> markers and adds a _untrusted_content_note field so the agent model can distinguish external data from trusted instructions.
Never forward watcher-delivered title or preview as instructions to an AI model. Treat them as data to be displayed or processed, not as commands to execute.