MCP and Policy Search Examples

This page shows example requests and expected response shapes for the compact MCP search surface. It starts with simple policy searches and builds toward workload principal analysis, search sets, identity lookup, data operations, and historical comparison.

The examples are intentionally verbose for documentation. MCP tool descriptions should stay terse.

When to use a policy search set

For “Which groups can manage Object Storage, and which dynamic groups have read policies for it?”, prefer one policy_search_set call with two child searches:

{
  "intent": "access_review",
  "product_or_service": "Object Storage principal comparison",
  "searches": [
    {
      "search_id": "group-manage",
      "label": "Groups with manage allow statements",
      "detail_level": "summary",
      "filters": {
        "action": ["allow"],
        "principal": {"principal_type": "group"},
        "verb": ["manage"],
        "resource_family": "object storage"
      }
    },
    {
      "search_id": "workload-read",
      "label": "Dynamic groups with read allow statements",
      "detail_level": "summary",
      "filters": {
        "action": ["allow"],
        "principal": {"principal_type": "dynamic-group"},
        "verb": ["read"],
        "resource_family": "object storage"
      }
    }
  ]
}

The result keeps both counts/breakdowns under their search IDs and reports missing required checks. evaluation can be omitted for this ordinary comparison. This example searches exact policy verbs; it does not compute every grant that implies read access. Check scope, conditions and broader grants before concluding effective access.

Use a set for multiple groups/services or a permission checklist; use policy_search for one specific lookup. The legacy wrapped examples below still work, but direct filters make new requests simpler.

Result Detail Levels

Most policy_search examples use:

  • detail_level: summary for counts and breakdowns without statement rows.

  • detail_level: simple for compact statement rows.

  • detail_level: full for parsed statement metadata, principal evidence, condition atoms, residual conditions, resolved compartments, and confidence details.

limit is the largest match count eligible for detailed statement rows, capped at 50. It is not pagination. The server evaluates all matches; unfiltered searches and searches with more matches than the threshold return a compact summary without statement rows. A warning reports when a requested threshold above 50 is capped.

For product names, use filters.resource_family rather than guessing an IAM resource type. This query finds group allow statements with a manage verb on Object Storage resource types, their family, or all-resources:

{
  "mode": "simple",
  "detail_level": "summary",
  "filters": {
    "action": ["allow"],
    "principal": {"principal_type": "group"},
    "verb": ["manage"],
    "resource_family": "object storage"
  },
  "limit": 50
}

This is a statement search. Check conditions and compartment scope before describing a group as having effective access.

Each search has three conceptual inputs:

  • mode: simple for common statement and principal lookups, or advanced for workload principal, tag, parsed-condition, and evidence-aware lookups.

  • filters: the searchable fields. statement_text and subject_text are raw text filters; principal is structured identity context.

  • detail_level: how much metadata to return. summary is safest for broad searches, simple returns compact rows, and full returns parsed metadata and evidence fields.

Common top-level output fields:

  • response_type: summary, simple, or full.

  • summary_substituted: true when simple or full was requested but a summary was returned.

  • summary_reason: requested_summary, unfiltered_query, or too_many_matches when the response is a summary.

  • refinement_hint: guidance to narrow the query and request statement rows.

  • detail_row_limit: the applied detailed-row threshold, at most 50.

  • total_count: number of matching statements after all filters run.

  • returned_count: number of detailed rows returned; zero for a summary.

  • truncated: whether matching detailed rows were omitted.

  • breakdowns: count buckets by policy, subject type, verb, resource, and confidence. Each category has at most 20 named values plus __other__.

  • warnings: caveats such as broad filters, unsupported fields, or result truncation.

  • statements: detailed rows for simple or full responses; empty for a summary.

For example, a broad simple request can return:

{
  "response_type": "summary",
  "summary_substituted": true,
  "summary_reason": "too_many_matches",
  "detail_row_limit": 50,
  "total_count": 80,
  "returned_count": 0,
  "statements": [],
  "breakdowns": {"by_resource": {"buckets": 70, "objects": 10}},
  "refinement_hint": "Add or narrow filters such as principal, resource, verb, or effective_path, then request detail_level=\"simple\" or \"full\" for statement rows."
}

Compact statement rows include fields such as policy_name, compartment_path, statement_text, subject_type, principal_summary, verb, resource, permission, location, effective_path, where_clause_text, and match_confidence.

Advanced rows add evidence fields such as normalized_principals, principal_keys, principal_evidence, where_clause, condition_atoms, dynamic_group_rule_evidence, residual_conditions, match_confidence_reason, and resolved_compartments.

Principal Keys and Structured Fields

Callers can provide either a canonical principal_key or structured fields. They should not provide both in the same principal filter unless the server explicitly validates they are equivalent.

Structured fields are preferred for AI agents and UI forms because the server can compute the canonical key and apply workload-principal matching rules. When the MCP server runs with --log-level INFO, each tool call logs full input and truncated output, which is useful for confirming the computed filter shape. WARNING and higher suppress tool payload logging.

Examples:

{
  "principal": {
    "principal_key": "group:Default/Administrators"
  }
}
{
  "principal": {
    "principal_type": "resource-principal",
    "resource_type": "containerinstance",
    "compartment_ocid": "ocid1.compartment.oc1..app"
  }
}

The second example computes:

resource-principal:containerinstance/compartment:ocid1.compartment.oc1..app

Compartment Lookup

Use identity_search when a policy or workload-principal condition gives you a compartment OCID and you need the human-readable name or path for follow-up searches.

Lookup by OCID:

{
  "entity_types": ["compartment"],
  "ocid": ["ocid1.compartment.oc1..app"],
  "limit": 10
}

Lookup by name or path:

{
  "entity_types": ["compartment"],
  "name": ["app"],
  "compartment_path": ["ROOT/Prod/App"],
  "limit": 10
}

Expected output fields:

{
  "operation": "search",
  "total_count": 1,
  "compartments": [
    {
      "name": "App",
      "ocid": "ocid1.compartment.oc1..app",
      "compartment_path": "ROOT/Prod/App",
      "parent_ocid": "ocid1.compartment.oc1..prod"
    }
  ]
}

Resource Principal, Specific Resource

Use this for any-user / any-group statements constrained by request.principal.*.

Input:

{
  "mode": "advanced",
  "detail_level": "full",
  "filters": {
    "principal": {
      "principal_type": "resource-principal",
      "resource_type": "containerinstance",
      "ocid": "ocid1.computecontainerinstance.oc1..example",
      "match_mode": "auto"
    }
  },
  "limit": 25
}

Expected behavior:

  • Derive principal key resource-principal:containerinstance/ocid1.computecontainerinstance.oc1..example.

  • Search any-user / any-group statements.

  • Treat request.principal.type and request.principal.id as identity evidence.

  • Return residual conditions separately.

  • Return match_confidence: exact only when the condition evidence uniquely identifies the principal and no residual condition remains.

Expected output fields:

{
  "where_clause": "all { request.principal.type = 'containerinstance', request.operation = 'PullImage' }",
  "condition_atoms": [
    {
      "id": "c1",
      "left": "request.principal.type",
      "operator": "=",
      "right": "containerinstance",
      "evidence_kind": "resource_principal"
    },
    {
      "id": "c2",
      "left": "request.operation",
      "operator": "=",
      "right": "PullImage",
      "evidence_kind": "condition"
    }
  ],
  "residual_conditions": [
    {
      "left": "request.operation",
      "operator": "=",
      "right": "PullImage"
    }
  ],
  "match_confidence": "identity_match_with_residual",
  "match_confidence_reason": "Principal type matched, but one or more non-identity conditions remain."
}

Resource Principal, Compartment Scope

Input:

{
  "mode": "advanced",
  "detail_level": "full",
  "filters": {
    "principal": {
      "principal_type": "resource-principal",
      "resource_type": "containerinstance",
      "compartment_ocid": "ocid1.compartment.oc1..app",
      "match_mode": "condition"
    }
  }
}

Expected behavior:

  • Derive principal key resource-principal:containerinstance/compartment:ocid1.compartment.oc1..app.

  • Treat request.principal.type plus request.principal.compartment.id as identity evidence.

  • Return resolved_compartments when the compartment OCID can be mapped to a loaded compartment name and path.

  • Return exact only when no non-identity condition atoms remain.

  • Return identity_match_with_residual when the statement also includes operations, permissions, tags, time, network, or other conditions.

Expected output fields:

{
  "principal_keys": [
    "resource-principal:containerinstance/compartment:ocid1.compartment.oc1..app"
  ],
  "principal_evidence": [
    {
      "source": "condition",
      "field": "request.principal.compartment.id",
      "value": "ocid1.compartment.oc1..app"
    }
  ],
  "resolved_compartments": [
    {
      "ocid": "ocid1.compartment.oc1..app",
      "name": "App",
      "compartment_path": "ROOT/Prod/App"
    }
  ],
  "match_confidence": "exact"
}

Instance Principal, Dynamic Group Rule

Input:

{
  "mode": "advanced",
  "detail_level": "full",
  "filters": {
    "principal": {
      "principal_type": "instance-principal",
      "resource_type": "instance",
      "compartment_ocid": "ocid1.compartment.oc1..app",
      "match_mode": "dynamic_group_rule"
    }
  }
}

Expected behavior:

  • Derive principal key instance-principal:instance/compartment:ocid1.compartment.oc1..app.

  • Search dynamic group rules for instance.compartment.id.

  • Search policies for matching dynamic group subjects.

  • Return dynamic group rule evidence and resolved compartments when possible.

Dynamic group display fields:

{
  "dynamic_group_rule_evidence": [
    {
      "dynamic_group_name": "AppInstances",
      "matching_rule": "instance.compartment.id = 'ocid1.compartment.oc1..app'",
      "condition_atoms": [
        {
          "left": "instance.compartment.id",
          "operator": "=",
          "right": "ocid1.compartment.oc1..app",
          "evidence_kind": "instance_principal"
        }
      ]
    }
  ],
  "resolved_compartments": [
    {
      "ocid": "ocid1.compartment.oc1..app",
      "name": "App",
      "compartment_path": "ROOT/Prod/App"
    }
  ]
}

Instance Principal, Specific Instance

Input:

{
  "mode": "advanced",
  "detail_level": "full",
  "filters": {
    "principal": {
      "principal_type": "instance-principal",
      "resource_type": "instance",
      "ocid": "ocid1.instance.oc1..worker",
      "compartment_ocid": "ocid1.compartment.oc1..app"
    }
  },
  "limit": 25
}

Expected behavior:

  • Derive principal key instance-principal:instance/ocid1.instance.oc1..worker.

  • Search dynamic group rules for direct instance OCID evidence and compartment evidence.

  • Search policies where the matched dynamic groups are policy subjects.

  • Return dynamic group rule evidence separately from policy statement condition evidence.

Condition Display Fields

Policies should display condition analysis using condition-oriented names, even when the source OCI syntax uses where.

Output:

{
  "where_clause": "all { request.principal.type = 'containerinstance', target.resource.tag.Operations.Environment = 'prod' }",
  "condition_atoms": [
    {
      "id": "c1",
      "left": "request.principal.type",
      "operator": "=",
      "right": "containerinstance",
      "evidence_kind": "resource_principal"
    },
    {
      "id": "c2",
      "left": "target.resource.tag.Operations.Environment",
      "operator": "=",
      "right": "prod",
      "evidence_kind": "tag_condition"
    }
  ]
}

The first implementation should parse and display the structure without relying on full recursive evaluation for filtering.

Search Set: Service Installation Validation

Use a search set when a service/product requires multiple human and workload permissions.

Input:

{
  "intent": "install_validation",
  "product_or_service": "container image pull from OCIR",
  "searches": [
    {
      "search_id": "human-admins",
      "label": "Human admins can manage repos",
      "required": true,
      "query": {
        "mode": "simple",
        "detail_level": "summary",
        "filters": {
          "principal": {
            "principal_type": "group",
            "domain_name": "Default",
            "name": "ContainerAdmins"
          },
          "resource": ["repos"],
          "verb": ["manage"]
        }
      }
    },
    {
      "search_id": "container-runtime",
      "label": "Container instance resource principal can pull images",
      "required": true,
      "query": {
        "mode": "advanced",
        "detail_level": "full",
        "filters": {
          "principal": {
            "principal_type": "resource-principal",
            "resource_type": "containerinstance"
          },
          "resource": ["repos"]
        }
      }
    }
  ],
  "evaluation": {
    "require_human_principal_coverage": true,
    "require_workload_principal_coverage": true,
    "min_confidence": "identity_match_with_residual"
  }
}

set_summary output:

{
  "set_summary": {
    "intent": "install_validation",
    "product_or_service": "container image pull from OCIR",
    "total_searches": 2,
    "required_searches": 2,
    "matched_required_searches": 1,
    "missing_required_searches": ["container-runtime"],
    "human_principal_coverage": "present",
    "workload_principal_coverage": "ambiguous",
    "service_principal_coverage": "not_requested",
    "tag_condition_coverage": "not_requested",
    "missing_or_ambiguous_items": [
      {
        "search_id": "container-runtime",
        "reason": "Matched resource-principal type, but residual request.operation condition was not evaluated."
      }
    ],
    "likely_ready": "unknown",
    "confidence": "medium"
  }
}

set_summary fields:

  • total_searches: count of searches in the set.

  • required_searches: count of searches marked required.

  • matched_required_searches: required searches with acceptable matches.

  • missing_required_searches: required search IDs with no acceptable match.

  • human_principal_coverage: whether user/group requirements appear present.

  • workload_principal_coverage: whether resource or instance principal requirements appear present.

  • service_principal_coverage: whether service principal requirements appear present.

  • missing_or_ambiguous_items: actionable gaps for humans and AI agents.

  • likely_ready: yes, no, or unknown; this should stay conservative when residual conditions are not fully evaluated.

  • confidence: high-level confidence for the set result.

Data Operations

List available caches:

{
  "operation": "list_caches",
  "tenancy_name": "andgre5678"
}

Reload data and persist a fresh cache:

{
  "operation": "reload"
}

Expected reload output includes status, counts, data_as_of, and the cache name when a new cache is saved.

Reliable cache creation options:

  • Restart the desktop, web, or MCP process on a scheduled interval.

  • Call MCP reload externally from a scheduler.

  • Use a future reload policy setting at application startup or MCP server startup.

Automatic reload policies are a roadmap item. History search should report the cache selected for as_of and the confidence that it represents the requested time.

MCP Call Logging

Start the MCP server with INFO logging when testing clients or validating generated filters:

python -m oci_policy_analysis.mcp_server --use-cache andgre5678_2026-06-15-12-00-00-UTC --transport streamable-http --log-level INFO

At INFO level, the server logs each tool call with full input and truncated output:

MCP call policy_search input(full)={...}
MCP call policy_search output(truncated)={...}

The output payload is capped for log readability. Use --log-level WARNING when you want normal operation without tool payload logging.