# MCP Policy Integration
## Overview
This document describes how the Policy Engine integrates with the MCP (Model Context Protocol) server to provide **agent access control** following the **Semantic + Guidance** pattern.
---
## Core Principle: Proactive Policy Disclosure
The agent learns about policy restrictions **upfront**, not reactively after failures.
```
Traditional (Reactive):
Agent → api_preview → 403 Denied → "What happened?"
Semantic + Guidance (Proactive):
Agent → api_help → "Policy: 3 rules, only GET/HEAD/OPTIONS allowed"
Agent → api_show → "This DELETE operation is blocked by policy"
Agent → (knows not to try DELETE operations)
```
---
## Policy File Locations
The MCP server auto-detects policy files in this priority order:
| 1 | `--policy <path>` | Explicit CLI flag |
| 2 | `.mrapids/policy.yaml` | Project-local |
| 3 | `.mrapids/policy.toml` | Project-local (TOML) |
| 4 | `policy.yaml` | Current directory |
| 5 | `policy.toml` | Current directory |
### Startup Behavior
```bash
# With policy file found:
[MCP] Policy auto-loaded from: .mrapids/policy.yaml
# Without policy file:
[MCP] No policy file found, running in permissive mode
```
---
## Policy in Semantic + Guidance
### Where Policy Appears
| `api_help` | Full policy summary | Agent learns restrictions upfront |
| `api_auth` | Policy status with auth | Combined security view |
| `api_show` | Per-operation policy status | Check before attempting |
| `api_preview` | Blocker if denied | Enforcement gate 1 |
| `api_run` | Blocker if denied | Enforcement gate 2 (defense in depth) |
---
## Response Examples
### 1. api_help (Policy Active)
```json
{
"data": {
"version": "0.1.30",
"commands": [...],
"workflow": [...],
"policy": {
"active": true,
"name": "petstore-agent-policy",
"rules_count": 3,
"default_methods": ["GET", "HEAD", "OPTIONS"],
"require_auth": false,
"message": "Policy active: 3 rules. Default methods: GET, HEAD, OPTIONS. Check api_show for per-operation policy status."
}
},
"guidance": {
"ready": true,
"next_action": {
"tool": "api_find",
"reason_code": "start_discovery"
}
}
}
```
### 2. api_help (No Policy - Permissive Mode)
```json
{
"data": {
"policy": {
"active": false,
"message": "No policy configured. All operations are allowed (permissive mode)."
}
}
}
```
### 3. api_show (Operation Allowed)
```json
{
"data": {
"operation_id": "getPetById",
"method": "GET",
"path": "/pet/{petId}",
"policy": {
"active": true,
"allowed": true,
"rule": "allow-read-operations"
}
}
}
```
### 4. api_show (Operation Denied)
```json
{
"data": {
"operation_id": "deletePet",
"method": "DELETE",
"path": "/pet/{petId}",
"policy": {
"active": true,
"allowed": false,
"rule": "deny-delete-operations",
"reason": "Delete operations are not allowed for agents"
}
}
}
```
### 5. api_preview (Policy Denied)
```json
{
"data": {
"operation_id": "deletePet",
"denied": true,
"policy_active": true
},
"guidance": {
"ready": false,
"blockers": [{
"code": "policy_denied",
"message": "Delete operations are not allowed for agents",
"field": "policy",
"resolution": {
"tool": "api_auth",
"reason_code": "configure_auth"
}
}],
"display_hint": "Operation blocked by policy. Contact administrator for access."
}
}
```
### 6. api_auth (With Policy Status)
```json
{
"data": {
"environment": "development",
"spec_auth_schemes": ["api_key", "oauth2"],
"config_status": {
"current_environment": "development",
"config_exists": true
},
"policy": {
"active": true,
"name": "petstore-agent-policy",
"rules_count": 3,
"default_methods": ["GET", "HEAD", "OPTIONS"],
"require_auth": false
}
}
}
```
---
## Enforcement Architecture
```
┌─────────────────────────────────────────────────────────────────────┐
│ MCP Server Startup │
├─────────────────────────────────────────────────────────────────────┤
│ 1. Load policy from --policy flag or auto-detect │
│ 2. Validate policy structure (rules, patterns, actions) │
│ 3. Create PolicyEngine with pre-compiled glob patterns │
│ 4. Store PolicySet for reporting │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ api_help │
├─────────────────────────────────────────────────────────────────────┤
│ SEMANTIC: Policy summary │
│ ├── active: true/false │
│ ├── name: "petstore-agent-policy" │
│ ├── rules_count: 3 │
│ ├── default_methods: ["GET", "HEAD", "OPTIONS"] │
│ └── message: guidance for agent │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ api_show │
├─────────────────────────────────────────────────────────────────────┤
│ SEMANTIC: Per-operation policy status │
│ ├── policy.active: true │
│ ├── policy.allowed: true/false │
│ ├── policy.rule: "allow-read-operations" │
│ └── policy.reason: null or "Delete not allowed" │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ api_preview │
├─────────────────────────────────────────────────────────────────────┤
│ ENFORCEMENT GATE 1: Policy check before token creation │
│ ├── If ALLOWED → Create preview token, return preview_id │
│ └── If DENIED → Return blocker, no token created │
│ │
│ Audit: policy_denied logged with operation_id, rule, reason │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ api_run │
├─────────────────────────────────────────────────────────────────────┤
│ ENFORCEMENT GATE 2: Policy re-check at execution │
│ ├── Defense in depth (policies may change) │
│ ├── If ALLOWED → Execute operation │
│ └── If DENIED → Return blocker │
│ │
│ Audit: api_execute logged with operation_id, environment │
└─────────────────────────────────────────────────────────────────────┘
```
---
## Policy Evaluation
### EvaluationContext
The policy engine evaluates each request with:
```rust
EvaluationContext {
method: "GET" | "POST" | "DELETE" | ...,
tags: ["pet", "store"], // From OpenAPI tags
source_ip: None, // Optional IP filtering
timestamp: "2024-01-20T10:00:00Z", // For time-based rules
}
```
### Decision Flow
```
1. Check defaults.require_auth
└── If true and no auth_profile → DENY
2. For each rule in order:
└── If pattern matches URL:
├── Check conditions (auth, time_window, environment)
├── Check deny FIRST (deny takes precedence)
└── Then check allow
3. No matching rule → Apply defaults
└── If method in defaults.allow_methods → ALLOW
└── Otherwise → DENY
```
### PolicyDecision
```rust
enum PolicyDecision {
Allow {
rule: String, // "allow-read-operations"
audit: Option<AuditConfig>
},
Deny {
rule: String, // "deny-delete-operations"
reason: String, // "Delete not allowed for agents"
audit: Option<AuditConfig>
},
}
```
---
## Example Policy File
```yaml
# .mrapids/policy.yaml
version: "1.0"
metadata:
name: "petstore-agent-policy"
description: "Safe defaults for AI agent access"
defaults:
allow_methods: ["GET", "HEAD", "OPTIONS"]
deny_external_refs: true
require_auth: false
audit_level: "basic"
rules:
# Allow read operations
- name: "allow-read-operations"
description: "Allow safe read-only operations"
pattern: "*"
allow:
methods: ["GET", "HEAD", "OPTIONS"]
operations: ["get*", "list*", "find*", "search*"]
audit:
level: "basic"
# Deny admin endpoints
- name: "deny-admin-endpoints"
pattern: "*/admin/*"
deny:
all: true
explain: "Admin endpoints are restricted"
# Deny delete operations
- name: "deny-delete-operations"
pattern: "*"
deny:
methods: ["DELETE"]
operations: ["delete*", "remove*"]
explain: "Delete operations require human approval"
# Allow specific write with conditions
- name: "allow-create-pet"
pattern: "*/pet"
conditions:
- environment: "development"
allow:
methods: ["POST"]
operations: ["addPet"]
audit:
level: "detailed"
include_body: true
```
---
## Audit Logging
Policy denials are logged to `.mrapids/mcp_audit.log`:
```
2024-01-20T10:30:00Z policy_denied {"operation_id":"deletePet","method":"DELETE","rule":"deny-delete-operations","reason":"Delete not allowed"}
```
---
## Security Model
### Why Two Enforcement Gates?
```
┌─────────────────────────────────────────────────────────────────────┐
│ GATE 1: api_preview │
│ ├── Prevents token creation for denied operations │
│ └── Agent learns early, saves API calls │
├─────────────────────────────────────────────────────────────────────┤
│ GATE 2: api_run │
│ ├── Defense in depth │
│ ├── Policies may change between preview and run │
│ └── Ensures no stale tokens bypass policy │
└─────────────────────────────────────────────────────────────────────┘
```
### Permissive Mode
When no policy file exists:
| Aspect | Behavior |
|--------|----------|
| All operations | ALLOWED |
| Audit logging | Still active |
| api_help | Shows `policy.active: false` |
| api_show | `policy: null` (field omitted) |
This ensures:
- Backwards compatibility
- No friction during initial setup
- Opt-in security model
---
## Agent Workflow
### Recommended Agent Behavior
```
1. Call api_help first
└── Check policy.active and policy.default_methods
└── If restrictive, inform user about limitations
2. Before attempting operations:
└── Call api_show to check policy.allowed
└── If policy.allowed: false, explain to user
3. If blocked at api_preview:
└── Read blocker.message for reason
└── Suggest user contact administrator
4. Never retry denied operations:
└── Policy decisions are deterministic
└── Retrying wastes resources
```
### Example Agent Logic
```python
# Pseudo-code for agent behavior
def execute_operation(operation_id, params):
# Check policy status first
show_result = api_show(operation_id)
if show_result.policy and not show_result.policy.allowed:
return f"Operation blocked by policy: {show_result.policy.reason}"
# Proceed with preview
preview = api_preview(operation_id, params)
if preview.guidance.blockers:
return f"Blocked: {preview.guidance.blockers[0].message}"
# Execute
return api_run(preview.preview_id)
```
---
## Summary
| Policy File | Define rules, defaults, conditions |
| PolicyEngine | Evaluate requests against rules |
| api_help | Disclose policy summary to agent |
| api_show | Disclose per-operation policy status |
| api_preview | Enforce policy before token creation |
| api_run | Enforce policy before execution |
| Audit Log | Record all policy denials |
**Key Principle**: Agent learns about restrictions through **Semantic** information, receives **Guidance** on what's allowed, and policy is **Enforced** at multiple gates.