Quickstart

moriti decides, field by field, what an agent may receive for its assigned purpose. This page takes you from nothing to an agent that never sees a field it shouldn't.

Install

Python 3.11 or newer.

bash
pip install moriti

The core has two dependencies. Each integration is an extra: moriti[mcp] for the proxy, moriti[serve] for the HTTP service, and others listed under Integrations.

Make a decision

python
from moriti import PrivacyPlane, RequestContext, Destination, LegalBasis

plane = PrivacyPlane.from_policy("popia_hr_v1")

result = plane.mediate(
    {"name": "Sarah Ndlovu", "email": "sarah@example.co.za", "salary": 1450000},
    RequestContext(
        agent="hr_assistant",
        purpose="employee_directory_lookup",
        destination=Destination.INTERNAL_MODEL,
        legal_basis=LegalBasis.LEGITIMATE_INTEREST,
        requested_fields=["name", "email", "salary"],
    ),
)

print(result.data)      # {'name': 'Sarah Ndlovu', 'email': 'sarah@example.co.za'}
print(result.withheld)  # ['salary']
print(result.explain()) # the reason and the guard for every field

popia_hr_v1 is a policy pack that ships with the package. A directory lookup needs contact details, so salary never leaves mediate(). The decision involves no model: the same input always gives the same answer.

Put it in the path of an agent

An engine the agent has to volunteer to call is not enforcement. The MCP proxy sits between the agent and the real tool server, so every result is mediated before the agent sees it.

bash
pip install "moriti[mcp]"

Describe the deployment. The purpose is set here, by whoever provisions the agent. The agent cannot declare or change it.

yaml
# hr_assistant.yaml
agent: hr_assistant
purpose: employee_directory_lookup
policy: popia_hr_v1
legal_basis: legitimate_interest
allowed_tools: [get_employee, list_employees]
downstream:
  command: "{python}"                    # a demo HR server ships in the package
  args: [-m, moriti.mcp.demo_hr_server]

Point the agent at the proxy. In Claude Code, or any MCP client, that is .mcp.json:

json
{
  "mcpServers": {
    "moriti-hr": {
      "command": "moriti-proxy",
      "args": ["hr_assistant.yaml"]
    }
  }
}

Ask the agent for an employee. The downstream server returns the whole record; the agent receives:

json
{
  "name": "Sarah Ndlovu",
  "email": "sarah.ndlovu@example.co.za",
  "department": "Engineering",
  "job_title": "Staff Engineer"
}

Employee ID, salary, bank account and tax number never reach the agent's context. To use your own tool server, replace downstream with its command, or with url: for a server that speaks streamable HTTP.

Next

  • Concepts: purpose, guards and decisions.
  • Policies: write your own pack.
  • Integrations: ContextForge, Google ADK, Strands, Microsoft Agent Framework, AgentCore, and any gateway that can call HTTP.