prompt-protection
概要
作者による説明(英語の原文)
Scan prompts, tool definitions and model output for injection, and guard agent tool calls.
PopNx MCPの3つの柱
この3つを1つのスコアにまとめることはしません。意見、測定、人気はそれぞれ別のものだからです。
- 利用者の声iレビューはまだありませんレビューは今後のステップで、アカウント機能と一緒に始まります
- 私たちの基本チェックi2項目中2項目に合格公開情報からチェック。コードはまだスキャンしていません
- 利用状況i初期↓ 1384/週 · ★ 3
できること
このサーバーのやさしい解説はまだ書いていません。以下はレジストリから読み取った事実です。
- 接続方法: パソコンで動く
- トランスポート: stdio
- カテゴリ: 検索・AIの記憶
インストール
使っているAIアプリを選んで、設定をコピーしてください。
インストールの前に
- 基本チェック:2項目中2項目に合格
- あなたのパソコンで、あなたのユーザーと同じ権限(ファイル、ネットワーク、アプリ)で動きます。
- レジストリには、入力が必要なキーは載っていません。
必要なもの
どのアプリを使っていますか?
上でアプリを選んでください。選んだアプリを覚えておくので、次からはボタンに「…にインストール」と表示されます。
Claude Desktop → Settings → Developer → Edit Config を開き、claude_desktop_config.json にこれを貼り付けます
{
"mcpServers": {
"prompt-protection": {
"command": "npx",
"args": [
"-y",
"prompt-protection"
]
}
}
}ターミナルでこれを実行します
claude mcp add prompt-protection -- npx -y prompt-protectionまたは、設定を自分でコピーする
プロジェクトの .cursor/mcp.json、または ~/.cursor/mcp.json に貼り付けます
{
"mcpServers": {
"prompt-protection": {
"command": "npx",
"args": [
"-y",
"prompt-protection"
]
}
}
}ブラウザがアプリを開いてよいか確認し、そのあとアプリが設定を表示して確認を求めます。キーの値は空欄のままなので、自分で入力してください。
または、設定を自分でコピーする
プロジェクトの .vscode/mcp.json に貼り付けます
{
"servers": {
"prompt-protection": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"prompt-protection"
]
}
}
}ブラウザがアプリを開いてよいか確認し、そのあとアプリが設定を表示して確認を求めます。キーの値は空欄のままなので、自分で入力してください。
これはパソコンで動くサーバーですが、ChatGPTがつなげるのはインターネット上のサーバーだけです。ほかのアプリを選んでください。
選んだアプリは、この端末のCookieに保存されます。アカウントは不要です。
この設定は、作者のレジストリ登録情報から作成しています。READMEと違う場合は、READMEに従ってください。
使えるアプリ
このサーバーのつなぎ方と、各アプリの公式ドキュメントに基づきます。2026年11月10日に確認。
パソコン上で動かせるアプリ
ClaudeGooseLM StudioMsty StudioCherry StudioCursorVS Code + GitHub CopilotVisual StudioWindsurf (Devin Desktop)ZedKiroClineContinueJetBrains AI AssistantClaude CodeCodex CLIGemini CLIOpenCodenodecodeWarpLibreChatRaycast
できること
パソコンで動くサーバーです。インストールすると、ツール一覧がAIアプリに表示されます。
基本チェックi
誰でも見られる情報をチェックしており、コードは読んでいません。当てはまらない項目は数に入れていません。 · 2026年11月10日にチェック
- ✓ 合格
作者とリポジトリが一致
レジストリ名は、リポジトリを所有するGitHubアカウント mughalhere で確認済みです
- ✓ 合格
今もメンテナンスされている
コードの最終更新:5 日前
- … 未チェック
ライセンスがある
このリポジトリはまだGitHubから読み取っていません
- – 対象外
エンドポイントが応答する
これはパソコンで動くため、インターネット上のエンドポイントはありません
- – 対象外
OAuthでサインイン
パソコンで動き、代わりにローカルの設定を使います
- … 未チェック
ツールの説明に隠れた指示がない
パソコンで動くサーバーです。ツール一覧を読み取れるのは、インターネット上のエンドポイントがある場合だけです
検索・AIの記憶のほかのサーバーとの比較i
- このカテゴリのほかの5875件のうち84%よりスターが多い
- このカテゴリのほかの5875件のうち88%より最近レジストリに公開
- 基本チェックの結果が、このカテゴリのほかの5875件のうち100%と同じかそれ以上
レジストリでこのカテゴリにある有効なすべてのサーバーと比較しています。スターは、公開者がリポジトリの所有者である場合だけ数えます。
よくある質問
prompt-protectionは安全ですか?
当てはまる基本チェック2項目のうち2項目に合格しました。コードは読んでいません。キーやトークンを追加する前に、サーバーがどんなアクセスを求めているかを確認し、必要な権限だけを持つキーを使ってください。
どんなキーやコードが必要ですか?
レジストリには必須のキーは載っていません。ただし、最初に使うときにサインインを求めるサーバーもあります。
パソコンで動きますか、それともクラウドですか?
あなたのパソコンで動きます。必要なときにAIアプリが起動し、データは作者のサーバーを通りません(サーバー自体がオンラインサービスを呼び出す場合を除きます)。
今もメンテナンスされていますか?
GitHubリポジトリの最終更新は5 日前、最新バージョン(4.4.0)がレジストリに届いたのは5 日前です。
どのアプリで使えますか?
Claude Desktop、Claude Code、Cursor、VS CodeなどのMCP対応アプリで使えます。パソコンにNode.jsも必要です。
代わりに使えるものは?
同じカテゴリには:Ruflo, Context7, Metabase
代わりの候補
同じカテゴリで、ほかの作者が作ったサーバー。スターの多い順。
カードの見方:↓ 週あたりのダウンロードi★ GitHubスターi3つの柱:レビュー · チェック · 利用状況i更新=最新バージョンをレジストリに公開した日i
作者のREADME
READMEを開く
作者のリポジトリから書かれたとおりにコピーしたものです。PopNx MCPは内容を確認・推奨していません。HTML、画像、スクリプトは取り除いています。リンクはこのサイトの外へ移動します。
prompt-protection
A provenance-tracked tool-call guard for JavaScript agents. Tool results are tainted, destinations the user named are trusted, and every tool call the model proposes is checked before it runs: untrusted data does not reach a network, email, exec, file or payment sink. Fail-closed, in-process, zero runtime dependencies. Text detection, spotlighting and canaries ship as components, with their benchmark numbers published whether they flatter the library or not.
Listed in the Agent Threat Rules ADOPTERS registry (Tier 2, adapter), the official MCP registry as io.github.mughalhere/prompt-protection, MCPrush, and the XERJ Corpus Hub.
[Live Demo →](https://mughalhere.github.io/prompt-protection/)
What it is, and what it isn't
Prompt injection stopped being a text-classification problem the moment models started calling tools. The damage in an agent is a consequence, not a sentence: a URL lifted from an email ends up in an outbound request, an attendee address from a calendar entry becomes the recipient of send_email, a payload in a README lands in exec. Version 3 tracks where data came from and refuses to let untrusted data reach a dangerous sink. That is the capability model from Google DeepMind's CaMeL paper (arXiv 2503.18813), ported to a JavaScript tool-calling loop without the custom interpreter.
It is a policy layer inside your process. It is not an isolation boundary. It cannot see a flow that passes through the model's hidden state, a paraphrase that shares no identifiers with its source, or a source you never registered. The Limitations section lists what it misses, with the dataset rows that show it.
60-second agent quickstart
import { generateText } from 'ai';
import { createGuard } from 'prompt-protection/guard';
const guard = createGuard({ sinks: { http_post: 'network', send_email: 'email', run_shell: 'exec' }, // or rely on name heuristics });
guard.analyzeUserTurn(userMessage); // destinations the user names become trusted
const result = await generateText({ model, tools: guard.wrapTools(tools), // check → execute → taint result toolApproval: guard.vercelToolApproval(), // block → denied, confirm → user-approval prompt: userMessage, });
Or drive it by hand:
guard.taint('read_email', emailBody); // label a tool result as untrusted
const decision = guard.checkToolCall({ toolName: 'http_post', args: { url } }); // { action: 'block', policy: 'untrusted-to-exfil-sink', reasons: ['untrusted-to-exfil-sink'], flows: [{ kind: 'identifier', path: 'args.url', … }] }
Taint survives three places it used to leak (4.1):
// Memory: persist the entry next to the value, read it back next session with its lineage.
const { store, entry } = guard.taintMemoryWrite('summarise', summary, { key: 'notes/today' }); if (store) await db.put(entry.key, { value: summary, entry }); // … later, in a new guard: guard.memoryRead([savedEntry]); // a blocked lineage into an exfil/exec sink → 'lineage-untrusted'
// Sub-agents: the child guard inherits sources, labels and trusted identifiers; depth increments. const child = guard.fork(); // or createGuard({ inherit: guard.handoff() }) across a boundary
// Approvals: what the human saw is what runs. const card = await guard.approvalCard(call); // rendered fields, tainted marks, card.digest guard.confirm(card.id, card.digest, 'alice'); // UI echoes the digest back guard.checkToolCall(call); // identical args → allow ('approved'); changed args → block ('approval-mismatch')
Every decision carries reasons: DecisionReason[] (stable codes, docs/CONFORMANCE.md) and depth.
Static structure (4.2): pin what the agent may call, and bound how often.
const lock = await guard.pin(tools); // digests name, description, schema, annotations; persist it
// … next process: guard.lock(savedLock); await guard.verify(tools) guard.wrapTools(tools); // a redefined description now blocks with 'tool-drift' guard.checkToolCall({ toolName: 'new_tool', args }); // unlisted under a lock → 'tool-unpinned'
createGuard({ budgets: { maxCallsPerTurn: 20, maxRepeatIdentical: 3, maxDepth: 2 } }); // the 4th identical call, or the 21st this turn → 'budget-exceeded'
import { createBoundary } from 'prompt-protection/spotlight'; const boundary = createBoundary({ mode: 'datamark' }); createGuard({ spotlight: boundary }); // one marker, one system-prompt sentence: boundary.instruction
Under a lock, readOnlyHint: true makes a tool a non-sink and an unannotated tool the name heuristics cannot place resolves to unknown (treated as exfil and exec) unless you set annotationsDefault: 'heuristic' or name its sink in sinks.
Operability (4.3): pick a preset, observe before you enforce, and read the reason chain.
createGuard({ preset: 'strict' }); // 'balanced' (default) · 'strict' · 'permissive'; explicit options win
createGuard({ mode: 'observe' }); // action is always 'allow'; decision.observedAction is the verdict import { explain } from 'prompt-protection/guard'; explain(decision); // { summary, chain: [{ code, policy, because, flows }] }
One call per framework:
import { protect } from 'prompt-protection/adapters/vercel-guardrail'; // ai ≥ 6
const { tools, toolApproval, prepareStep, onToolExecutionEnd, stopWhen } = protect(guard, myTools); new WorkflowAgent({ tools, toolApproval, prepareStep, onToolExecutionEnd, stopWhen }); // WorkflowAgent has no default step limit
import { protect } from 'prompt-protection/adapters/openai-agents'; // @openai/agents ≥ 0.18 const { inputGuardrails, wrapTool } = protect(guard); new Agent({ inputGuardrails, tools: [wrapTool(sendEmail)] }); // tool guardrails + needsApproval bound per tool
import { protect, interruptWhen } from 'prompt-protection/adapters/langchain'; // langchain ≥ 1 createAgent({ middleware: [await protect(guard), humanInTheLoopMiddleware({ interruptOn: { stripe_transfer: { allowedDecisions: ['approve', 'reject'], when: interruptWhen(guard) } } })] });
Output side: analyzeOutput(text, { renderAllowlist: ['ourcompany.com', '*.cdn.ourcompany.com'], conversation }) blocks markdown/HTML that would render or link an unlisted host, a data: URI, or a query parameter carrying high-entropy or conversation-derived data (off unless the allowlist is set). redact(text) and analyzeOutput(text, { redact: 'all' }) replace secrets and PII (gitleaks / Presidio shapes, checksums validated in code).
Trust across boundaries (4.4, preview): a label is only worth what signs it.
import { seal, generateKey } from 'prompt-protection/envelope';
const key = await generateKey('HS256', 'k1'); // or 'EdDSA' (Ed25519, Node ≥ 20.19.3) const env = await seal(toolResult, key, { label: 'tool', ttlMs: 60_000, from: 'fetcher' }); // … in the receiving process: const { opened, error } = await guard.taintEnvelope(env, key); // forged / expired / replayed → a blocked source const child = await guard.sealHandoff(key); // sealed TaintHandoff for a sub-agent elsewhere await other.absorbSealed(child, key); // bad seal → 'handoff-untrusted' on any flow const entry = await guard.sealMemoryEntry(write.entry); // memoryReadSealed() reads unsigned entries as blocked
Architecture
tool result ──taint──▶ provenance label ──▶ shingles + identifiers (URL, host, email, path, token)
│ model proposes tool call ──▶ sink class ──▶ flow detection (exact / identifier / content) ──▶ policies ──▶ allow · flag · confirm · block ▲ ▲ explicit map or name heuristics plan() allow-list · user-trusted destinations
The default policies run in this order. plan-violation blocks any tool outside the plan() allow-list. injection-source-flow blocks when a source that itself scored as injection flows anywhere. untrusted-to-exfil-sink blocks untrusted data reaching network, email or messaging tools, and untrusted-to-exec does the same for exec and file writes. untrusted-to-payment asks for confirmation. injection-then-sink flags a sink call made in the same turn as an injection-scored source even when no flow was detected, because paraphrase is exactly the case the flow detectors miss. args-injection flags when the arguments themselves read as injection. Each of these is one of the patterns in *Design Patterns for Securing LLM Agents* (arXiv 2506.08837): Action-Selector via plan(), Plan-Then-Execute, Context-Minimisation via spotlighting.
Spotlighting (prompt-protection/spotlight, arXiv 2403.14720) marks untrusted spans by delimiting, datamarking or base64-encoding them, and gives you the system-prompt sentence that tells the model what the marker means. With wrapTools({ spotlight: 'datamark' }) the model sees marked text while the guard taints the original, and arguments are unmarked before flow detection so a copied span still matches.
Canaries (prompt-protection/canary) put a token in the system prompt and look for it in the output in exact, normalized, spaced, base64, hex, reversed and partial forms. There is also a shingle-similarity check between the output and the system prompt. Plain verbatim canaries were shown to fail against paraphrase (arXiv 2506.19109). Similarity closes part of that gap. Not all of it.
Text detection is a component, not the product: 106 input rules, 21 output rules and 9 tool-poisoning rules score text that has to be scored, and the Benchmark section says how well. An embedded 33 KB int8 n-gram classifier lives on prompt-protection/ml (preview tier, off by default, for reasons the benchmark explains). prompt-protection/lite is the rules-only entry at 20 KB gzipped.
Benchmark
npm run bench runs the shipped build against every set below and writes bench/results.json. The same run is a CI gate. Recall and false-positive rate are shown as regex / ml / hybrid; the shipped default is regex.
| Set | Licence | N (attack/benign) | Recall | False-positive rate |
|---|---|---|---|---|
| NotInject, over-defence benchmark (arXiv 2410.22770) | MIT | 339 (0/339) | n/a | 2.9% / 7.7% / 10.6% |
| datasets/benign-hard + datasets/attacks, ours, written to evade proximity matching | CC-BY-4.0 | 285 (130/155) | 14.6% / 19.2% / 30.8