Your agent can act for you. It cannot consent for you.
An open-source project from Caledapt
Assentia is a local delegated-consent enforcement layer for autonomous AI agents. It gives browser agents a policy decision point before they interact with cookie or tracking consent interfaces, preventing an agent from silently granting more consent than its user delegated.
The default path is fully local: no paid service, no API key, and no network call is required for classification. Assentia is released free and open source under the MIT License by Caledapt, a Scottish AI implementation and adoption business founded by Bryan Small.
Saito, Kataiwa, Takashima and Ohki describe Invisible Consent as consent generated by browsing agents on a user's behalf without a meaningful opportunity for the user to notice or intervene. Their CHI EA 2026 study reports that agents often favour the easiest path through consent UI and that cookie acceptance persisted in 14.4% of explicit consent interactions even under a deny-all instruction.
Assentia puts an explicit policy boundary between the agent and that decision.
Research: Invisible Consent: Browser Automation by LLM Agents is Not Privacy-aware · DOI 10.1145/3772363.3799048
Browser agent
│
│ visible banner text + domain
▼
Assentia classifier
│
│ banner state + detected controls
▼
Assentia policy engine
│
├── reject_all
├── essential_only
├── accept_all
└── blocked
│
▼
Agent may perform only that action
The classifier never clicks, navigates, submits, or grants consent. Unknown or unsupported UI fails closed.
| Mode | Behaviour |
|---|---|
essential_only |
Prefer reject_all; otherwise use a genuine essential/necessary-only control; otherwise block. |
strict |
Use reject_all when present; otherwise block. |
allow_all |
Permit accept_all only when an accept control is detected; unknown UI still blocks. |
A global default can be overridden per domain. The most specific matching parent-domain override wins.
mode: essential_only
domains:
example.com: strict
trusted.example: allow_allThe default policy path is ~/.assentia/policy.yaml. Override it with ASSENTIA_POLICY_PATH.
Assentia exposes three FastMCP tools:
consent.choose(banner_state?, banner_text?, domain?)consent.get_policy(domain?)consent.set_policy(mode, domain?)
MCP permits dot-separated tool names, so the public names mirror the consent namespace directly.
A host agent should be instructed to call consent.choose before interacting with cookie or tracking consent UI and to perform only permitted_action. A blocked result means it must not grant consent. If the workflow cannot safely continue, it should ask the human.
Example decision:
{
"permitted_action": "reject_all",
"policy_mode": "essential_only",
"banner_state": "essential_only_present",
"domain": "https://example.com",
"classification": {
"state": "essential_only_present",
"actions": {
"reject_all": true,
"essential_only": false,
"accept_all": true,
"settings": true
},
"classifier": "rules"
}
}Requires Python 3.11+ and uv.
git clone https://github.com/BryanFiFife/Assentia
cd assentia
uv sync --extra dev
uv run playwright install chromium
uv run pytestRun the MCP server over STDIO:
uv run assentiaFastMCP's mcp.run() uses STDIO by default, which makes the server suitable for local MCP clients.
From a local checkout, point the client at uv and run Assentia from the project directory. For example:
{
"mcpServers": {
"assentia": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/assentia", "run", "assentia"]
}
}
}Use the configuration shape required by your MCP client. The important contract is that the client launches uv run assentia with this repository as its working directory.
The built-in classifier is deterministic and side-effect free. It recognises common English controls such as Reject all, Necessary only, Accept all, and Manage preferences.
No API or model is needed:
ASSENTIA_CLASSIFIER=rules uv run assentiaAssentia can classify with a local Ollama model without changing the enforcement contract:
ASSENTIA_CLASSIFIER=ollama \
ASSENTIA_OLLAMA_MODEL=gemma3:4b \
uv run assentiaOptional settings:
ASSENTIA_OLLAMA_URL, defaulthttp://127.0.0.1:11434/api/generateASSENTIA_OLLAMA_TIMEOUT, default8seconds
Ollama is not required for the default installation. Assentia uses Python's standard library HTTP client, so enabling the backend adds no Python dependency.
Python integrations may inject a classifier with assentia.set_classifier(callable). It must return one of the four banner states and may include detected action capabilities. This is useful for local vision models, accessibility-tree classifiers, or a host-specific CMP detector.
Assentia preserves a small, stable state contract:
essential_only_presentaccept_all_presentno_reject_optionunknown
The classification also includes explicit action capabilities. This prevents the enforcement engine from confusing a visible Reject all control with a differently labelled essential-only control.
examples/playwright_demo.py shows the intended hook:
- read visible page text;
- call Assentia;
- inspect
permitted_action; - click only a matching control for that action;
- perform no consent interaction when the decision is
blockedor the permitted control cannot be located.
Run it against a site you are authorised to automate:
ASSENTIA_DEMO_URL='https://example.com' uv run python examples/playwright_demo.pyThe example is deliberately conservative. It is an integration pattern, not a universal CMP clicker.
uv run pytest -qThe suite covers:
- all four required classifier outcomes;
- essential-only, strict, and allow-all policy modes;
- fail-closed unknown states;
- domain and subdomain overrides;
- policy validation and persistence;
- custom classifier injection;
- MCP-facing tool functions;
- a real Chromium process that proves an essential-only policy rejects a banner and never clicks
Accept all.
GitHub Actions runs the suite on Python 3.11, 3.12, and 3.13 and installs Chromium for the browser regression test.
Assentia constrains a cooperating, correctly integrated agent. An MCP server alone cannot force a malicious or incorrectly wired agent to consult it. Stronger guarantees require enforcement below the agent, for example in the browser automation host, extension, proxy, or network layer.
Current limitations:
- The rules classifier is intentionally English-first and text-first.
- Preference centres that hide rejection behind multiple steps are not traversed automatically.
- The example Playwright selectors do not attempt universal iframe or shadow-DOM CMP handling.
- An Ollama classification failure is surfaced as an error rather than silently falling back to a more permissive result.
- A supplied
banner_statecontains less information than raw banner text, so integrations should preferbanner_textwhere practical.
These are safety boundaries, not claims of universal coverage.
- No silent consent. A browser agent does not acquire consent authority merely because it can click a button.
- Fail closed. Ambiguity must not become tracking permission.
- Local first. The default consent decision path should not leak browsing context to another service.
- Classify, then enforce. Detection and permission are separate responsibilities.
- Policy belongs to the user. Agents execute delegated choices; they do not invent them.
Assentia is developed and released by Caledapt as free, open infrastructure for safer AI adoption. Caledapt assesses, designs, implements, trains and supports practical AI systems for organisations, with a focus on measurable deployment rather than AI theatre.
Founder: Bryan Small on LinkedIn
Website: caledapt.com
Assentia is intended to remain useful independently of Caledapt services. The MIT licence permits commercial and non-commercial use, modification and redistribution subject to its terms.
See CONTRIBUTING.md. Security-relevant bypasses should follow SECURITY.md.
See CHANGELOG.md.
Assentia is free and open-source software released by Caledapt under the MIT License.
Copyright © 2026 Caledapt / Bryan Small.