Ruflo
Agent teams, swarms, memory and hooks for Claude Code and Codex, served over MCP.
Descripción del creador (original en inglés)
Scan prompts, tool definitions and model output for injection, and guard agent tool calls.
Estos tres nunca se combinan en una sola puntuación: las opiniones, las mediciones y la popularidad son cosas distintas.
Todavía no escribimos un resumen sencillo para este. Abajo están los datos que leímos del registro.
Elige la app de IA que usas y copia la configuración.
Necesitas
¿Qué app usas?
Elige tu app arriba. La recordamos, así que la próxima vez el botón dirá “Instalar en …”.
Abre Claude Desktop → Settings → Developer → Edit Config y pega esto en claude_desktop_config.json
{
"mcpServers": {
"prompt-protection": {
"command": "npx",
"args": [
"-y",
"prompt-protection"
]
}
}
}Ejecuta esto en tu terminal
claude mcp add prompt-protection -- npx -y prompt-protectionPégalo en .cursor/mcp.json de tu proyecto, o en ~/.cursor/mcp.json
{
"mcpServers": {
"prompt-protection": {
"command": "npx",
"args": [
"-y",
"prompt-protection"
]
}
}
}Tu navegador te pide permiso para abrir la app; luego la app muestra la configuración y te pide confirmar. Los valores de las claves quedan vacíos para que los completes tú.
Pégalo en .vscode/mcp.json de tu proyecto
{
"servers": {
"prompt-protection": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"prompt-protection"
]
}
}
}Tu navegador te pide permiso para abrir la app; luego la app muestra la configuración y te pide confirmar. Los valores de las claves quedan vacíos para que los completes tú.
Este se ejecuta en tu computadora, y ChatGPT solo puede conectarse a servidores en internet. Elige otra app.
Tu elección se guarda en una cookie en este dispositivo; no necesitas cuenta.
Esta configuración se genera a partir de la ficha del creador en el registro. Si el README dice otra cosa, sigue el README.
Se ejecuta en tu computadora; la lista de herramientas aparece en tu app de IA después de instalarlo.
Revisamos información que cualquiera puede ver; no hemos leído el código. Las revisiones que no aplican no se cuentan. · Revisado el 10 nov 2026
El nombre en el registro se verificó con la cuenta de GitHub mughalhere, dueña del repo
Último cambio de código: hace 5 días
Todavía no hemos leído este repo en GitHub
Este se ejecuta en tu computadora; no hay endpoint en internet
Se ejecuta en tu computadora y usa ajustes locales en su lugar
Se ejecuta en tu computadora; solo podemos leer listas de herramientas de endpoints en internet
Comparado con todos los servidores activos de esta categoría en el registro. Las estrellas solo cuentan cuando quien lo publica es dueño del repo.
Superó 2 de 2 revisiones básicas aplicables. No hemos leído el código. Antes de agregar una clave o un token, revisa qué acceso pide el servidor y usa una clave que tenga solo los permisos que necesitas.
El registro no indica ninguna clave obligatoria, aunque algunos servidores te piden iniciar sesión la primera vez que los usas.
Se ejecuta en tu computadora. Tu app de IA lo inicia cuando hace falta, y los datos no pasan por los servidores del creador (a menos que el propio servidor llame a un servicio en línea).
El repo de GitHub cambió por última vez hace 5 días, y la última versión (4.4.0) llegó al registro hace 5 días.
Las apps compatibles con MCP, como Claude Desktop, Claude Code, Cursor y VS Code. También necesitas Node.js en tu computadora.
En la misma categoría: Ruflo, Context7, Metabase
Servidores de la misma categoría hechos por otros creadores, ordenados por estrellas.
Cómo leer una tarjeta:↓ Descargas por semanai★ Estrellas de GitHubiTres pilares: reseñas · revisiones · usoiActualizado = fecha en que se publicó la última versión en el registroi
Copiado tal cual del repo del creador. PopNx MCP no lo ha revisado ni lo respalda. Se quitaron HTML, imágenes y scripts. Los enlaces llevan fuera de este sitio.
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/)
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.
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
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.
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