Deliverd Screen

An open-source pre-send check for every prompt: find and redact personal data, secrets and your own terms before text goes to a model. No account, no network.

Deliverd Screen finds what should not go to an AI model and replaces it with placeholders the model can still reason about, such as [EMAIL_1]. It runs where your code runs, and sends nothing anywhere. It is MIT-licensed, on npm as @deliverd/screen and on PyPI and crates.io as deliverd-screen, and finds what Deliverd's SDKs and servers find: all of them are tested against one shared set of cases.

TypeScript

import { screen } from "@deliverd/screen";

const checked = await screen(prompt);
if (!checked.allowed) throw new Error(checked.reasons.join(" "));
const answer = await model.generate(checked.text);
return checked.restore(answer);

Python

from deliverd_screen import screen

checked = screen(prompt)
if not checked.allowed:
    raise RuntimeError(" ".join(checked.reasons))
answer = model.generate(checked.text)
return checked.restore(answer)

Rust

let checked = deliverd_screen::screen(&prompt);
if !checked.allowed {
    return Err(checked.reasons.join(" ").into());
}
let answer = model.generate(checked.text.as_deref().unwrap())?;
Ok(checked.restore(&answer))

What it finds

Patterns with checksums where one exists: email address, phone number, iban, payment card number, uk national insurance number, irish pps number, us social security number, ip address, date of birth, special-category data and secret or credential. Your own terms, such as clients, code names and deals, are passed as terms. People's names need a model, which you plug in (below). By default a model may receive internal data and no personal data. Say otherwise with allow.

allow
Nothing it may not receive. Send the text as it is.
redact
Send text, with placeholders in place of what it may not receive; restore() puts the values back in the answer.
ask
A person has to decide, for example about health data. Nobody can be asked from a library, so allowed is false.
deny
Nothing may be sent. Secrets and credentials are refused by default.

Names: bring a model

A detector is a function from the text to spans. @deliverd/screen/adapters (and deliverd_screen.adapters in Python) turns a Hugging Face token classifier or a Presidio analyzer into one. Any model with the same output works, including Perplexity's PII-Tracer or a GLiNER model. Check its licence before you ship it, and run it where the text already is.

A token classifier for names

import { pipeline } from "@huggingface/transformers";
import { screen } from "@deliverd/screen";
import { tokenClassifier } from "@deliverd/screen/adapters";

const ner = await pipeline("token-classification", "Xenova/bert-base-NER");
const names = tokenClassifier((text) => ner(text));

const checked = await screen(prompt, { detectors: [names] });

Where it plugs in

WhereHow
Vercel AI SDKscreenMiddleware() from @deliverd/screen/ai-sdk. It screens the whole conversation, restores streamed answers, and throws before the model is called on a deny or an ask.
A terminal, pre-commit or CInpx @deliverd/screen --fail fixtures/*.json exits 1 when something is found; --redact prints the redacted text; --json prints kinds and offsets, never values.
Claude Code, Cursor, Codexnpx @deliverd/screen mcp is a local MCP server with one tool, screen_text. It returns the redacted text and never the values it replaced.
Pythondeliverd-screen on the command line, or screen() in code.
Rustdeliverd_screen::screen() or a Screen set up once; a detector is any closure from the text to spans. cargo install deliverd-screen gives the same CLI and deliverd-screen mcp.

Vercel AI SDK

import { generateText, wrapLanguageModel } from "ai";
import { screenMiddleware } from "@deliverd/screen/ai-sdk";

const model = wrapLanguageModel({ model: openai("gpt-5"), middleware: screenMiddleware() });
const { text } = await generateText({ model, prompt });

When a person has to decide: Data Gate

A library can only tell you to stop. Deliverd's Data Gate makes the decision: deliverd.screen() in the SDKs runs the same detectors on your machine, sends Deliverd only the counts, and gets back a verdict from your organisation's policy and from what the AI register approves that model to receive. On an ask it puts the question to the right person as an ordinary approval and waits. Every check is on the audit trail as counts and a verdict.

The same check, with the policy and a person

import { deliverd } from "@deliverd/sdk";

const screened = await deliverd.screen(prompt, { aiSystemId: SUMMARISER, detectors: [names] });
if (!screened.allowed) return screened.reasons;
return screened.restore(await model.generate(screened.text));

Data Gate is on Team, Business Starter, Business and Enterprise. On Free it runs report-only: the checks are recorded and nothing is held back, so an administrator sees under Data screening what it would have stopped.

Microsoft Purview: your DLP policies too

If your organisation already writes its data loss prevention policies in Microsoft Purview, Data Gate holds Purview's answer beside its own. purviewCheck() (purview_check() in Python) asks Purview about the text through Microsoft Graph, from where your agent runs and with your own Entra app, so the text goes to Microsoft and never to Deliverd. Deliverd is sent only Purview's answer, applies the stricter of the two verdicts, and records Purview's answer on the audit trail with the rest of the check.

Screen with Purview

import { Deliverd, purviewCheck } from "@deliverd/sdk";

const purview = purviewCheck({
  token: () => credential.getToken("https://graph.microsoft.com/.default").then((t) => t.token),
  userId: entraObjectIdOfTheUser,
  app: { name: "Claims assistant", entraAppId: claimsAssistantAppId },
  onUnavailable: "ask",
});

const screened = await deliverd.screen(prompt, { aiSystemId: CLAIMS_MODEL, purview });
if (!screened.allowed) return screened.reasons;
Purview saysWhat happens
blockNothing is sent, on every plan, report-only included: it is your organisation's own policy.
warnA person decides, through an ordinary approval, so the override Purview allows is recorded with who made it.
auditNothing is held back. Purview has recorded the activity itself.
Cannot be reachedonUnavailable decides: ask (the default) puts it to a person, deny sends nothing, allow carries on and says it was not checked.

The same check reaches the tools that cannot run an SDK. No-code tools (n8n, Zapier, Make, any MCP client) send the text to Deliverd's screen call: link your tenant under Data screening and Deliverd asks Purview before it answers. Without an account, @deliverd/screen takes the same purview option, so does its Vercel AI SDK middleware, and its local MCP server for Claude Code, Cursor and Codex turns it on from DELIVERD_PURVIEW_* environment variables.