Skip to main content
Private BetaContact us to get set up.
The Policy Decision Point (PDP) Interceptor extends MCP with a standardized webhook flow for evaluating MCP calls before they execute. The callee posts a JSON-RPC request to your interceptor service, which returns a validation result: success, notify, or failure. Char does not provide a managed policy engine for custom governance logic. Use LangGuard or any BYO service that implements this contract.

Provider options

  • LangGuard for a ready-to-use external policy platform.
  • BYO interceptor service for internal or vendor governance engines.

What is authoritative

  • Identity and role claims come from your IdP.
  • Policy decisions come from your configured interceptor provider.
  • UI-level warnings are not authoritative enforcement.

When to use a PDP interceptor

  • Enforce enterprise policy beyond guardrails (e.g., geographic restrictions, tool allowlists).
  • Route sensitive requests to humans for approval.
  • Centralize policy decisions across multiple MCP servers.

Configuration

Configure the PDP provider per organization in SaaS UI:
  1. Open /beta/governance/integrations.
  2. Choose LangGuard or Bring Your Own.
  3. Set Interceptor URL, Interceptor Name, and optional API Key.
  4. Test the connection and save.
  5. Enable the provider for active enforcement.
If no provider is configured (or if the provider is disabled), tool calls bypass governance checks. For API-driven setup, use:
  • GET /governance/providers/config
  • PATCH /governance/providers/config
  • POST /governance/providers/test
  • DELETE /governance/providers/config

Decision point request

The callee sends a JSON-RPC request to the configured interceptor service URL. HTTP
  • Method: POST
  • Content-Type: application/json
  • Authorization: Bearer <token>
JSON-RPC envelope
{
  "jsonrpc": "2.0",
  "id": "call-12345",
  "method": "interceptor/validate",
  "params": {
    "interceptor_name": "policy-byo",
    "event": "tools/call",
    "phase": "request",
    "context": {
      "mcp_call_id": "call-12345",
      "caller_identity": "usr_abc123",
      "source": {
        "tabId": "12345",
        "origin": "https://example.com",
        "url": "https://example.com/page",
        "title": "Example Page"
      },
      "mcp_call": {
        "method": "tools/call",
        "params": {
          "name": "write_note",
          "arguments": {
            "slug": "morning",
            "content": "Start your day with clarity and confidence."
          }
        }
      }
    }
  }
}

Request fields

FieldTypeDescription
jsonrpcstringAlways "2.0".
idstringRequest identifier for correlation (same as mcp_call_id).
methodstringAlways "interceptor/validate".
params.interceptor_namestringThe configured interceptor name.
params.eventstringThe MCP event being intercepted (e.g., "tools/call").
params.phasestring"request" or "response" depending on lifecycle.
params.contextobjectOriginal MCP call details used for policy evaluation.
params.context.mcp_call_idstringUnique identifier for the MCP call.
params.context.caller_identitystringOpaque user identifier (internal user ID).
params.context.sourceobjectBrowser context: tabId, origin, url, title.
params.context.mcp_callobjectThe original MCP method and parameters.

Decision point response

The interceptor responds with a JSON-RPC result that contains a validation outcome. HTTP Status Code: 200 OK (for a valid JSON-RPC response)
{
  "jsonrpc": "2.0",
  "id": "call-12345",
  "result": {
    "status": "success",
    "message": "All policies passed.",
    "violations": []
  }
}

Response fields

FieldTypeDescription
result.statusstring"success", "notify", or "failure".
result.messagestringHigh-level description of the outcome.
result.violationsarrayList of policy violations (empty for success).

Violation object

FieldTypeDescription
policy_idstringIdentifier of the violated policy.
statusstring"notify" for escalation or "block" for denial.
detailsstringOptional human-readable message.
Example blocked response:
{
  "jsonrpc": "2.0",
  "id": "call-12345",
  "result": {
    "status": "failure",
    "message": "The call was denied due to multiple policy violations.",
    "violations": [
      {
        "policy_id": "POLICY_GEO_RESTRICTION",
        "status": "notify",
        "details": "Request originated from a restricted geographic area."
      },
      {
        "policy_id": "POLICY_UNSANCTIONED_TOOL",
        "status": "block",
        "details": "The requested tool is not sanctioned for use in this context."
      }
    ]
  }
}

Decision flow

1

Receive MCP call

An MCP request arrives at the callee.
2

Prepare the policy request

Wrap the MCP call in the JSON-RPC interceptor/validate payload.
3

Invoke the interceptor

POST the payload to your interceptor service URL.
4

Evaluate the response

Verify the HTTP status is 200, then read result.status and result.violations.
5

Decide the outcome

Success: proceed with the MCP call. Notify: log the notification for review and proceed with the MCP call. Failure: block the MCP call and log violations.

Error handling

  • Non-200 HTTP responses: treat as a policy failure and block the request.
  • Timeouts: default to block and log for follow-up.
  • Malformed JSON-RPC: treat as failure and return an internal error to the caller.

Security considerations

  • Authenticate the interceptor endpoint with a bearer token.
  • Data exposure: Tool call arguments are forwarded to the interceptor service for policy evaluation. Review your interceptor provider’s data handling policies to ensure compliance with your organization’s privacy requirements.
  • Log all validation outcomes to your audit system.
  • Keep a deny-by-default stance for transport or parsing errors.
Need help wiring this into your MCP server? Reach out at [email protected].