Skip to main content

MCP Policy Enforcement

AxonFlow enforces policies at two phases during MCP connector operations:

  1. REQUEST Phase: Evaluates incoming queries before they reach the connector
  2. RESPONSE Phase: Scans connector responses before returning to the client

This provides comprehensive security for both data input and output.

Phase-Aware Evaluation

Request Phase

The request phase evaluates queries before they are sent to the connector:

  • SQL Injection Detection: Blocks malicious SQL patterns (DROP, DELETE without WHERE, UNION injection)
  • Dangerous Operation Blocking: Prevents schema modifications, privilege escalation
  • Access Control: Enforces tenant-specific permissions

If a policy blocks the request, the connector is never called and a 403 response is returned.

Response Phase

The response phase evaluates data after it's returned from the connector:

  • PII Detection: Identifies sensitive data patterns (SSN, credit cards, national IDs)
  • Data Redaction: Replaces sensitive values with redacted placeholders
  • Audit Logging: Records what data was accessed and redacted

Response Schema

All MCP responses now include policy enforcement metadata:

{
"success": true,
"connector": "postgres-demo",
"data": [
{
"name": "John Doe",
"ssn": "[REDACTED]",
"email": "[email protected]"
}
],
"row_count": 1,
"duration_ms": 45,
"redacted": true,
"redacted_fields": ["data[0].ssn"],
"policy_info": {
"policies_evaluated": 15,
"blocked": false,
"redactions_applied": 1,
"processing_time_ms": 3,
"matched_policies": [
{
"policy_id": "pii-us-ssn",
"policy_name": "US Social Security Number",
"category": "pii-us",
"severity": "critical",
"action": "redact"
}
]
}
}

New Response Fields

FieldTypeDescription
redactedbooleanWhether any fields were redacted
redacted_fieldsstring[]JSON paths of redacted fields
policy_infoobjectPolicy evaluation details

PolicyInfo Object

FieldTypeDescription
policies_evaluatedintegerNumber of policies checked
blockedbooleanWhether request was blocked
block_reasonstringReason if blocked
redactions_appliedintegerNumber of fields redacted
processing_time_msintegerPolicy evaluation time
matched_policiesarrayPolicies that matched

PolicyMatchInfo Object

Each entry in matched_policies contains:

FieldTypeDescription
policy_idstringUnique policy identifier
policy_namestringHuman-readable policy name
categorystringPolicy category (e.g., pii-us, security-sqli)
severitystringMatch severity (critical, high, medium, low)
actionstringAction taken (block, redact, warn, log)

Policy Categories

Security Policies (Request Phase)

CategoryDescriptionAction
security-sqliSQL injection patternsBlock
security-dangerousDDL, privilege escalationBlock

PII Policies (Response Phase)

CategoryDescriptionAction
pii-usUS SSN, Driver's LicenseRedact
pii-globalCredit cards, email, phoneRedact
pii-indiaAadhaar, PANRedact
pii-euNational IDs, IBANRedact

SDK Integration

Go SDK

resp, err := client.MCPQuery(ctx, axonflow.MCPQueryRequest{
Connector: "postgres-demo",
Statement: "SELECT name, ssn FROM customers",
})

if resp.Redacted {
fmt.Printf("Redacted fields: %v\n", resp.RedactedFields)
}

if resp.PolicyInfo != nil {
fmt.Printf("Evaluated %d policies in %dms\n",
resp.PolicyInfo.PoliciesEvaluated,
resp.PolicyInfo.ProcessingTimeMs)
}

TypeScript SDK

const resp = await client.mcpQuery({
connector: "postgres-demo",
statement: "SELECT name, ssn FROM customers",
});

if (resp.redacted) {
console.log("Redacted fields:", resp.redacted_fields);
}

if (resp.policy_info) {
console.log(`Evaluated ${resp.policy_info.policies_evaluated} policies`);
}

Python SDK

resp = await client.mcp_query(
connector="postgres-demo",
statement="SELECT name, ssn FROM customers"
)

if resp.redacted:
print(f"Redacted fields: {resp.redacted_fields}")

if resp.policy_info:
print(f"Evaluated {resp.policy_info.policies_evaluated} policies")

Java SDK

ConnectorResponse resp = client.mcpQuery(
"postgres-demo",
"SELECT name, ssn FROM customers"
);

if (resp.isRedacted()) {
System.out.println("Redacted fields: " + resp.getRedactedFields());
}

ConnectorPolicyInfo info = resp.getPolicyInfo();
if (info != null) {
System.out.println("Evaluated " + info.getPoliciesEvaluated() + " policies");
}

Handling Blocked Requests

When a request is blocked by policy, the SDK throws an exception or returns an error:

// Go
resp, err := client.MCPQuery(ctx, req)
if err != nil {
// Check if it's a policy violation
if policyErr, ok := err.(*axonflow.PolicyViolationError); ok {
fmt.Printf("Blocked by policy: %s\n", policyErr.Reason)
}
}
// TypeScript
try {
const resp = await client.mcpQuery(req);
} catch (err) {
if (err instanceof PolicyViolationError) {
console.log("Blocked by policy:", err.reason);
}
}

Performance

The policy engine is designed for minimal latency impact:

  • Request Phase: <5ms p99 for policy evaluation
  • Response Phase: <10ms p99 for redaction
  • Caching: Compiled regex patterns cached for reuse
  • Parallel Evaluation: Policies evaluated concurrently

Configuration

Environment Variables

VariableDefaultDescription
POLICY_CACHE_TTL5mPolicy cache refresh interval
POLICY_BLOCK_ON_ERRORfalseBlock if policy engine unavailable
PII_ACTIONredactDefault PII action (block/redact/warn/log)

Database Configuration

Policies are stored in the static_policies table with phase-aware columns:

SELECT id, name, category, phase, action_request, action_response
FROM static_policies
WHERE enabled = true AND tenant_id = 'your-tenant';

Examples

See the MCP connector examples in the AxonFlow repository for complete working code in all supported languages.