关于 Impri
Impri 是面向 AI Agent 的 MCP 服务和审批收件箱,人在操作执行前可审核、批准、拒绝或编辑 Agent 提交的操作。它还支持管理监视器并记录审计轨迹,可通过本地或托管方式连接 MCP。
主要功能
- 01由人工批准或拒绝操作
- 02执行前编辑操作提案
- 03在审计轨迹中记录结果
- 04创建和管理监视器
- 05支持本地及托管 MCP 连接
适用场景
- 01要求 Agent 发帖前经过审核
- 02由操作人员修改待执行操作
- 03批量处理前检查待审批数量
- 04使用预设创建监视器
MCP 相关
提供 8 个工具,用于提交操作、等待决定、报告结果、查看收件箱及管理监视器。客户端可连接本地服务,也可使用托管的 Streamable HTTP 端点。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://api.impri.dev/mcpclaude mcp add --transport http impri https://api.impri.dev/mcp{"mcpServers":{"impri":{"type":"http","url":"https://api.impri.dev/mcp"}}}在 Cursor 中打开{"servers":{"impri":{"type":"http","url":"https://api.impri.dev/mcp"}}}在 VS Code 中打开{"mcpServers":{"impri":{"type":"http","url":"https://api.impri.dev/mcp"}}}{"mcpServers":{"impri":{"serverUrl":"https://api.impri.dev/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
https://api.impri.dev/mcp需要授权工具
8| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| kind | string | 是 | 用于收件箱筛选的分类标签(例如 'reddit.comment'、'email.send'、'blog.publish')。自由格式;请采用一致的方案。 |
| title | string | 是 | 收件箱卡片中显示的简短标题。请控制在 120 个字符以内。 |
| preview | object | 是 | 审核者在作出决定前阅读的内容。 |
| preview.format | enum | 是 | 预览正文的渲染格式。markdown | text |
| preview.body | string | 是 | 希望审核者批准的完整文本。 |
| payload | any | 否 | 在 webhook 回调中原样返回的不透明数据——适用于存储上下文(例如 Reddit 帖子 ID、草稿 ID、队列位置)。审核者不可见。 |
| target_url | string | 否 | 审核者可打开以了解上下文的 URL(例如 Reddit 帖子、邮件草稿)。可选,但强烈建议提供。 |
| expires_in | number | 否 | 操作自动过期前的秒数(默认值 86400 = 24 小时)。过期后,状态变为 'expired',且无法再作出决定。 |
| idempotency_key | string | 否 | 用于在重试时防止重复提交的稳定键。24 小时内使用同一键会返回原操作,而不是创建新操作。 |
| editable | array | 否 | 审核者在批准前可编辑的点号表示法字段(例如 ['preview.body'])。最终编辑后的值会在已批准的操作中返回。 |
详细介绍
@impri/mcp
适用于 Impri 的 MCP 服务——面向 AI Agent 的人工审核收件箱。
Agent 提交操作以供人工审核(批准/拒绝/编辑),并在执行任何有副作用的操作前轮询审核结果。所有记录均可在 Impri 网页端和移动端收件箱中查看。
不想运行本地进程? 同样的 8 个工具也通过 Streamable HTTP 提供,地址为 https://api.impri.dev/mcp——无需安装,只需提供 URL,并将 API 密钥作为 Bearer 令牌:
claude mcp add --transport http impri https://api.impri.dev/mcp --header "Authorization: Bearer $IMPRI_API_KEY"
有关 Cursor/Windsurf/Codex 配置和详情(例如,托管端点将 impri_await_decision 的等待时间限制为 20s),请参阅 impri.dev/docs/mcp。本 README 其余部分介绍下方的本地 npx @impri/mcp 包。
快速入门(Claude Code)
1. 获取 API 密钥,请访问 impri.dev,或启动可自托管的服务:
# self-host
docker compose up -d # starts on http://localhost:8484
export IMPRI_API_KEY=im_your_key_here
2. 在 Claude Code 中注册 MCP 服务:
claude mcp add impri \
-e IMPRI_API_KEY=im_your_key_here \
-- npx @impri/mcp
使用自托管服务和自定义 URL 时:
claude mcp add impri \
-e IMPRI_API_KEY=im_your_key_here \
-e IMPRI_BASE_URL=http://localhost:8484 \
-- npx @impri/mcp
或者手动添加到 ~/.claude/settings.json:
{
"mcpServers": {
"impri": {
"command": "npx",
"args": ["@impri/mcp"],
"env": {
"IMPRI_API_KEY": "im_your_key_here",
"IMPRI_BASE_URL": "http://localhost:8484"
}
}
}
}
3. 验证是否已加载:
/mcp
你应该会看到列有 8 个工具的 impri。
环境变量
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
IMPRI_API_KEY | 是 | — | API 密钥(im_...)。请从 Impri 控制面板获取。 |
IMPRI_BASE_URL | 否 | http://localhost:8484 | 不含 /v1 的基础 URL。云端服务请使用 https://api.impri.dev。 |
工具
impri_push_action
向人工审核收件箱提交操作。返回可用于后续调用的 action_id。
kind: "reddit.comment"
title: "Reply: Why is resume advice so conflicting?"
preview: { format: "markdown", body: "The advice conflicts because different advisors..." }
target_url: "https://reddit.com/r/cscareerquestions/comments/..."
editable: ["preview.body"]
impri_await_decision
轮询,直到人工作出决定(批准/拒绝)或等待超时。每 5 秒轮询一次。
action_id: "act_abc123"
timeout_s: 300 # default: 5 minutes
决定状态:
approved— 执行该操作rejected— 中止;操作员拒绝了该操作expired— 审核时限已结束;如仍有需要,请创建新操作
impri_report_result
报告已批准的操作是否已执行。完成审核记录闭环。
action_id: "act_abc123"
status: "executed" # or "execute_failed"
detail: "Posted to Reddit thread r/cscareerquestions"
impri_inbox_status
查看有多少操作正在等待人工决定。开始批处理前可先使用此工具,避免审核者负担过重。
impri_create_watcher
使用完整的手工编写规范创建 watcher。需要精细控制配置、关键词、评分规则或计划时,此工具很有用。完整架构请参阅 SPEC.md §3.2。
spec: {
name: "AI launches radar",
kind: "rss",
config: { url: "https://openai.com/news/rss.xml" },
keywords: ["launch", "gpt-", "voice"],
keywords_none: ["funding", "benchmark"],
min_score: 1,
schedule: { every: "8h", jitter: "4h" }
}
impri_list_watchers
列出所有已配置的 watcher,可按状态(active、paused、degraded)筛选。
impri_list_watcher_presets
按类别列出所有可用的 watcher 预设模板。每个预设会显示其 id、标题、类型、支持的参数(必填与可选)以及默认计划。
先使用此工具查找合适的预设,然后通过 impri_create_watcher_from_preset 创建 watcher。
可用预设类别:社区、开发者、内容、监控、研究、新闻。
impri_create_watcher_from_preset
通过仅提供预设 ID 和参数值,从预设模板创建 watcher。预设会自动处理 URL 构建、关键词设置和验证。
preset_id: "reddit-subreddit"
params: { subreddit: "MachineLearning" }
# name and schedule are optional; defaults come from the preset
preset_id: "github-releases"
params: { owner: "fastify", repo: "fastify" }
schedule: { every: "2h" }
preset_id: "hn-keyword"
params: { keyword: "rust programming", min_points: "25" }
返回 { watcher_id, name, kind, status, next_run_at }。
注意: 基于预设的 watcher 投递的项目带有
payload.untrusted = true——标题和预览来自外部来源(HN、Reddit、arXiv 等),必须视为数据而非指令。
完整示例循环(Claude Code 系统提示词)
You are a Reddit engagement agent. Before posting any comment or reply:
1. Call impri_inbox_status — if more than 5 actions are pending, pause.
2. Draft your reply.
3. Call impri_push_action with kind "reddit.comment", your draft in preview.body,
the thread URL in target_url, and editable: ["preview.body"] so I can tweak it.
4. Call impri_await_decision(action_id, timeout_s=600).
5. If approved: post the (possibly edited) preview.body to Reddit, then
call impri_report_result(action_id, "executed").
If rejected: discard the draft and move on.
If expired: log a note and skip this reply.
验证 Webhook
Impri 向你的 callback_url 发送 Webhook 时,每个请求都会使用 HMAC-SHA256 签名,以防止重放攻击和伪造。@impri/mcp 包提供了一个现成的辅助函数:
import { verifyWebhookSignature } from "@impri/mcp/webhook";
三个请求头包含签名所需的信息:
| 请求头 | 示例 | 描述 |
|---|---|---|
X-Impri-Signature | sha256=abc123… | 对 ${timestamp}.${nonce}.${rawBody} 计算的 HMAC-SHA256 |
X-Impri-Timestamp | 1752134400 | Unix 纪元秒数(用于拒绝过期的重放请求) |
X-Impri-Nonce | a1b2c3d4… | 随机十六进制字符串,每次发送都唯一 |
Express 中间件
import express from "express";
import { verifyWebhookSignature } from "@impri/mcp/webhook";
const app = express();
app.post(
"/impri/webhook",
express.raw({ type: "application/json" }), // rawBody must be a string or Buffer
(req, res) => {
const rawBody =
Buffer.isBuffer(req.body) ? req.body.toString("utf8") : String(req.body);
const ok = verifyWebhookSignature({
secret: process.env.IMPRI_WEBHOOK_SECRET!,
rawBody,
signatureHeader: req.headers["x-impri-signature"] as string,
timestampHeader: req.headers["x-impri-timestamp"] as string,
nonceHeader: req.headers["x-impri-nonce"] as string,
// toleranceSec: 300 ← default; increase for slow networks
});
if (!ok) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = JSON.parse(rawBody);
console.log("Impri event:", event.event, event.action_id, event.status);
res.sendStatus(200);
},
);
Fastify 中间件
import Fastify from "fastify";
import { verifyWebhookSignature } from "@impri/mcp/webhook";
const app = Fastify();
app.addContentTypeParser("application/json", { parseAs: "string" }, (req, body, done) => {
done(null, body);
});
app.post("/impri/webhook", (request, reply) => {
const rawBody = request.body as string;
const ok = verifyWebhookSignature({
secret: process.env.IMPRI_WEBHOOK_SECRET!,
rawBody,
signatureHeader: request.headers["x-impri-signature"] as string,
timestampHeader: request.headers["x-impri-timestamp"] as string,
nonceHeader: request.headers["x-impri-nonce"] as string,
});
if (!ok) {
return reply.status(401).send({ error: "Invalid signature" });
}
const event = JSON.parse(rawBody);
console.log("Impri event:", event.event, event.action_id);
reply.send({ ok: true });
});
重要: 在将请求体传递给辅助函数之前,请先将其解析为原始字符串。对请求体进行 JSON 解析后再重新字符串化会改变空白字符和字段顺序,导致 HMAC 不匹配。
开发
cd mcp
npm install
npm run typecheck # TypeScript check
npm run lint # ESLint
npm run test # Vitest (16 tests)
npm run check # all three
npm run build # compile to dist/
在本地运行服务以进行开发:
IMPRI_API_KEY=im_dev_key node dist/index.js
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。