Platform API Reference

OpenAPI 3.1.0
v1.0.0

Complete reference for the FuseGov Control Plane (Catalog, Policy) and Runtime (Enforcement, Evidence).

Authentication

Use your FuseGov API Key as a Bearer token:

Authorization: Bearer fg_live_...

Boundary-only contract

Send only boundary fields. Prefer params_digest over raw params. Unknown fields are rejected.

Evidence

Every decision returns request_id + policy_digest and can be exported in an Evidence Pack with hash-chain verification.

yaml
openapi: 3.1.0
info:
  title: FuseGov Platform API
  version: 1.0.0
  summary: Boundary enforcement + audit-grade evidence, plus an Agent Catalog for ownership and rollout.
  description: |
    FuseGov provides **boundary enforcement** for AI agents at runtime, producing **audit-grade evidence**.
    It also includes an **Agent Catalog** (control plane) to register agents, assign ownership, map tool access,
    and compile **policy bundles** deployed to enforcement proxies.

    ### Design principles
    - **Boundary-only:** FuseGov evaluates *tool calls at the boundary* (tool name, destination class, scopes, digests, metadata).
      It does **not** require access to full prompts or internal agent state.
    - **Deterministic first:** Stage 1 deterministic rules are evaluated before any semantic checks.
    - **Evidence-first:** Every decision can be linked to a policy digest and evidence chain.
    - **Strict schemas:** Unknown fields are rejected (server enforces strict decoding).

  contact:
    name: FuseGov Developer Support
    email: api@fusegov.com

servers:
  - url: https://api.fusegov.com/v1
    description: FuseGov Cloud API (Multi-tenant)
  - url: http://localhost:8080/v1
    description: Local Sidecar / Proxy (Docker)

tags:
  - name: Enforcement
    description: Data-plane endpoints used at runtime to govern tool calls.
  - name: Catalog
    description: Control-plane endpoints to register agents, ownership, tools, and environments.
  - name: Policy
    description: Compile, retrieve, and deploy policy bundles.
  - name: Evidence
    description: Evidence packs, CTR chain export, and verification endpoints.
  - name: System
    description: Health, version, and diagnostics.

security:
  - ApiKeyBearer: []

x-tagGroups:
  - name: Runtime
    tags: [Enforcement, Evidence]
  - name: Control Plane
    tags: [Catalog, Policy]
  - name: Platform
    tags: [System]

paths:
  /enforcement/intercept:
    post:
      tags: [Enforcement]
      operationId: interceptToolCall
      summary: Intercept and govern a tool call (boundary enforcement)
      description: |
        Core runtime endpoint. Call this **before** executing a tool call.
        FuseGov evaluates against the active policy bundle and returns a decision.

        **Boundary-only note:** send minimal boundary fields; prefer `params_digest` and redacted metadata.
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InterceptRequest'
            examples:
              digestOnly:
                summary: Preferred (digest-only boundary enforcement)
                value:
                  agent_id: finance-bot-01
                  tool_name: read_db
                  tool_scope: read_only
                  destination_class: INTERNAL
                  params_digest: "sha256:8d3a4f..."
                  boundary:
                    environment: prod
                    data_classification: PROTECTED
                    tags: ["finance", "ap"]
      responses:
        '200':
          description: Decision rendered
          headers:
            X-Request-Id:
              $ref: '#/components/headers/XRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InterceptResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '409':
          $ref: '#/components/responses/Conflict'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'

  /enforcement/decision/{request_id}:
    get:
      tags: [Enforcement]
      operationId: getDecisionByRequestId
      summary: Fetch a previously rendered decision
      parameters:
        - in: path
          name: request_id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Decision found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InterceptResponse'
        '404':
          $ref: '#/components/responses/NotFound'

  /system/health:
    get:
      tags: [System]
      operationId: healthCheck
      summary: Health check
      description: Returns system status and active policy digest.
      responses:
        '200':
          description: System operational
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HealthResponse'

  /system/version:
    get:
      tags: [System]
      operationId: getVersion
      summary: Version and build metadata
      responses:
        '200':
          description: Version info
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VersionResponse'

  /catalog/agents:
    get:
      tags: [Catalog]
      operationId: listAgents
      summary: List registered agents
      parameters:
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/PageToken'
        - in: query
          name: owner_team
          schema: { type: string }
        - in: query
          name: environment
          schema: { $ref: '#/components/schemas/Environment' }
        - in: query
          name: risk_tier
          schema: { $ref: '#/components/schemas/RiskTier' }
        - in: query
          name: q
          schema: { type: string }
          description: Search by name, id, tags, capability.
      responses:
        '200':
          description: Page of agents
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PagedAgents'
    post:
      tags: [Catalog]
      operationId: createAgent
      summary: Register a new agent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentCreate'
      responses:
        '201':
          description: Agent created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          $ref: '#/components/responses/BadRequest'
        '409':
          $ref: '#/components/responses/Conflict'

  /catalog/agents/{agent_id}:
    get:
      tags: [Catalog]
      operationId: getAgent
      summary: Get an agent
      parameters:
        - $ref: '#/components/parameters/AgentId'
      responses:
        '200':
          description: Agent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      tags: [Catalog]
      operationId: updateAgent
      summary: Update an agent (partial)
      parameters:
        - $ref: '#/components/parameters/AgentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentPatch'
      responses:
        '200':
          description: Updated agent
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Agent'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      tags: [Catalog]
      operationId: deleteAgent
      summary: De-register an agent
      parameters:
        - $ref: '#/components/parameters/AgentId'
      responses:
        '204':
          description: Deleted

  /catalog/agents/{agent_id}/tools:
    get:
      tags: [Catalog]
      operationId: listAgentTools
      summary: List tools authorized for an agent
      parameters:
        - $ref: '#/components/parameters/AgentId'
      responses:
        '200':
          description: Tools authorized for agent
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/AgentToolGrant'
    post:
      tags: [Catalog]
      operationId: grantToolToAgent
      summary: Authorize a tool for an agent
      parameters:
        - $ref: '#/components/parameters/AgentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentToolGrantCreate'
      responses:
        '201':
          description: Granted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentToolGrant'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'

  /catalog/tools:
    get:
      tags: [Catalog]
      operationId: listTools
      summary: List known tools (tool registry)
      parameters:
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/PageToken'
        - in: query
          name: q
          schema: { type: string }
      responses:
        '200':
          description: Page of tools
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PagedTools'
    post:
      tags: [Catalog]
      operationId: createTool
      summary: Register a tool in the tool registry
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ToolCreate'
      responses:
        '201':
          description: Tool created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tool'

  /policy/bundles:
    post:
      tags: [Policy]
      operationId: compilePolicyBundle
      summary: Compile a policy bundle from current catalog state
      description: |
        Compiles the current Catalog state into a versioned, cryptographic policy bundle.
        Returns the `policy_digest` that binds future decisions to this configuration.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PolicyCompileRequest'
      responses:
        '201':
          description: Policy bundle compiled
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyBundleMeta'
    get:
      tags: [Policy]
      operationId: listPolicyBundles
      summary: List policy bundles
      parameters:
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/PageToken'
      responses:
        '200':
          description: Page of bundles
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PagedPolicyBundles'

  /policy/bundles/{policy_digest}:
    get:
      tags: [Policy]
      operationId: getPolicyBundle
      summary: Retrieve a policy bundle by digest
      parameters:
        - $ref: '#/components/parameters/PolicyDigest'
      responses:
        '200':
          description: Policy bundle
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyBundle'
        '404':
          $ref: '#/components/responses/NotFound'

  /policy/deploy:
    post:
      tags: [Policy]
      operationId: deployPolicyBundle
      summary: Deploy a policy bundle to enforcement
      description: |
        Deploys a compiled policy bundle to the enforcement engine (proxy).
        Use this to hot-swap the active bundle in a controlled manner.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PolicyDeployRequest'
      responses:
        '200':
          description: Deployed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PolicyDeployResponse'

  /evidence/packs:
    get:
      tags: [Evidence]
      operationId: listEvidencePacks
      summary: List evidence packs
      parameters:
        - $ref: '#/components/parameters/PageSize'
        - $ref: '#/components/parameters/PageToken'
      responses:
        '200':
          description: Page of evidence packs
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PagedEvidencePacks'
    post:
      tags: [Evidence]
      operationId: generateEvidencePack
      summary: Generate an evidence pack for a time window
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvidencePackRequest'
      responses:
        '201':
          description: Evidence pack created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvidencePack'

  /evidence/packs/{pack_id}:
    get:
      tags: [Evidence]
      operationId: getEvidencePack
      summary: Retrieve an evidence pack by id
      parameters:
        - in: path
          name: pack_id
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Evidence pack
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvidencePack'
        '404':
          $ref: '#/components/responses/NotFound'

  /evidence/verify:
    post:
      tags: [Evidence]
      operationId: verifyEvidencePack
      summary: Verify an evidence pack (hash-chain integrity)
      description: |
        Verifies that the CTR chain segment is intact and that `policy_digest` references a known bundle.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EvidenceVerifyRequest'
      responses:
        '200':
          description: Verification result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EvidenceVerifyResponse'

components:
  securitySchemes:
    ApiKeyBearer:
      type: http
      scheme: bearer
      bearerFormat: API_KEY
      description: |
        Use your FuseGov API key as a Bearer token.
        Example: `Authorization: Bearer fg_live_...`

  headers:
    XRequestId:
      description: Trace id for the request.
      schema:
        type: string
        format: uuid

  parameters:
    AgentId:
      in: path
      name: agent_id
      required: true
      schema:
        type: string
      description: Agent identifier (stable, human-friendly).
    PolicyDigest:
      in: path
      name: policy_digest
      required: true
      schema:
        type: string
        description: sha256 digest, e.g. `sha256:...`
    PageSize:
      in: query
      name: page_size
      schema:
        type: integer
        minimum: 1
        maximum: 200
        default: 50
    PageToken:
      in: query
      name: page_token
      schema:
        type: string
        description: Opaque pagination token.
    IdempotencyKey:
      in: header
      name: Idempotency-Key
      required: false
      schema:
        type: string
      description: Optional idempotency key for retries.

  responses:
    BadRequest:
      description: Bad request / schema violation
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    Unauthorized:
      description: Missing/invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    Forbidden:
      description: Not allowed by tenant policy or permissions
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    NotFound:
      description: Not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    Conflict:
      description: Conflict (duplicate id or idempotency collision)
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    RateLimited:
      description: Rate limited
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    ServiceUnavailable:
      description: Temporary outage / degraded upstream
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'

  schemas:
    # -------------------------
    # Common
    # -------------------------
    ApiError:
      type: object
      additionalProperties: false
      required: [code, message, request_id]
      properties:
        code:
          type: string
          example: SCHEMA_VIOLATION
        message:
          type: string
          example: Unknown field "chain_of_thought" is not allowed.
        request_id:
          type: string
          format: uuid
        details:
          type: array
          items:
            type: object
            additionalProperties: true

    Environment:
      type: string
      enum: [dev, test, prod]

    RiskTier:
      type: string
      enum: [LOW, MEDIUM, HIGH, CRITICAL]

    DestinationClass:
      type: string
      enum: [INTERNAL, EXTERNAL, VENDOR]

    ToolScope:
      type: string
      enum: [read_only, standard, privileged, admin]

    Decision:
      type: string
      enum: [PERMIT, BLOCK, QUARANTINE]

    # -------------------------
    # Enforcement
    # -------------------------
    InterceptRequest:
      type: object
      additionalProperties: false
      required: [tool_name, destination_class, tool_scope]
      properties:
        agent_id:
          type: string
          description: Agent identity (if not inferred from auth/context).
        tool_name:
          type: string
          description: Logical tool name (e.g. `read_db`, `send_email`).
        tool_scope:
          $ref: '#/components/schemas/ToolScope'
        destination_class:
          $ref: '#/components/schemas/DestinationClass'

        # Preferred boundary-only payload
        params_digest:
          type: string
          description: Digest of tool params (e.g. `sha256:...`) after redaction/canonicalization.
          examples: ["sha256:8d3a4f..."]

        # Optional for MVP / debugging: params allowed but should be redacted.
        params:
          type: object
          description: Tool arguments. Avoid sending sensitive content; server may redact.
          additionalProperties: true

        boundary:
          type: object
          additionalProperties: false
          properties:
            environment:
              $ref: '#/components/schemas/Environment'
            data_classification:
              type: string
              example: PROTECTED
            tags:
              type: array
              items: { type: string }

      oneOf:
        - required: [params_digest]
        - required: [params]

    InterceptResponse:
      type: object
      additionalProperties: false
      required: [decision, request_id, policy_digest, latency_ms]
      properties:
        decision:
          $ref: '#/components/schemas/Decision'
        request_id:
          type: string
          format: uuid
        rule_id:
          type: string
          description: Winning rule identifier (deterministic or matrix rule).
          example: allow_internal_reads
        policy_digest:
          type: string
          description: Digest of active policy bundle used for this decision.
          example: "sha256:a1b2c3..."
        proof:
          type: object
          additionalProperties: false
          properties:
            prev_hash:
              type: string
              example: "sha256:0000..."
            this_hash:
              type: string
              example: "sha256:abcd..."
        latency_ms:
          type: integer
          example: 3
        notes:
          type: string
          description: Optional human-readable explanation (non-sensitive).
          example: "PERMIT via deterministic rule allow_internal_reads"

    HealthResponse:
      type: object
      additionalProperties: false
      required: [status, version, policy_digest]
      properties:
        status:
          type: string
          example: ok
        version:
          type: string
          example: "1.0.0"
        policy_digest:
          type: string
          example: "sha256:a1b2c3..."
        dependencies:
          type: object
          additionalProperties: false
          properties:
            db:
              type: string
              example: ok
            oie:
              type: string
              example: ok

    VersionResponse:
      type: object
      additionalProperties: false
      properties:
        service:
          type: string
          example: fusegov-api
        version:
          type: string
          example: "1.0.0"
        build:
          type: string
          example: "2026-01-05T00:00:00Z"
        commit:
          type: string
          example: "a1b2c3d4"

    # -------------------------
    # Catalog
    # -------------------------
    AgentCreate:
      type: object
      additionalProperties: false
      required: [agent_id, display_name, owner_team, environment, risk_tier]
      properties:
        agent_id:
          type: string
          example: finance-bot-01
        display_name:
          type: string
          example: Finance Assistant
        owner_team:
          type: string
          example: CFO Office
        owner_email:
          type: string
          example: finance-ops@company.com
        environment:
          $ref: '#/components/schemas/Environment'
        risk_tier:
          $ref: '#/components/schemas/RiskTier'
        tags:
          type: array
          items: { type: string }
          example: ["finance", "ap"]
        capabilities:
          type: array
          items: { type: string }
          example: ["invoice_triage", "exception_routing"]
        version:
          type: string
          example: "1.1.0"
        active:
          type: boolean
          default: true

    AgentPatch:
      type: object
      additionalProperties: false
      properties:
        display_name: { type: string }
        owner_team: { type: string }
        owner_email: { type: string }
        environment: { $ref: '#/components/schemas/Environment' }
        risk_tier: { $ref: '#/components/schemas/RiskTier' }
        tags:
          type: array
          items: { type: string }
        capabilities:
          type: array
          items: { type: string }
        version: { type: string }
        active: { type: boolean }

    Agent:
      type: object
      additionalProperties: false
      required: [agent_id, display_name, owner_team, environment, risk_tier, created_at]
      properties:
        agent_id: { type: string }
        display_name: { type: string }
        owner_team: { type: string }
        owner_email: { type: string }
        environment: { $ref: '#/components/schemas/Environment' }
        risk_tier: { $ref: '#/components/schemas/RiskTier' }
        tags:
          type: array
          items: { type: string }
        capabilities:
          type: array
          items: { type: string }
        version: { type: string }
        active: { type: boolean }
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    AgentToolGrantCreate:
      type: object
      additionalProperties: false
      required: [tool_name, tool_scope, destination_class]
      properties:
        tool_name:
          type: string
          example: read_db
        tool_scope:
          $ref: '#/components/schemas/ToolScope'
        destination_class:
          $ref: '#/components/schemas/DestinationClass'
        notes:
          type: string
          example: "Allowed for finance reporting only."

    AgentToolGrant:
      type: object
      additionalProperties: false
      required: [agent_id, tool_name, tool_scope, destination_class, created_at]
      properties:
        agent_id: { type: string }
        tool_name: { type: string }
        tool_scope: { $ref: '#/components/schemas/ToolScope' }
        destination_class: { $ref: '#/components/schemas/DestinationClass' }
        notes: { type: string }
        created_at:
          type: string
          format: date-time

    ToolCreate:
      type: object
      additionalProperties: false
      required: [tool_name]
      properties:
        tool_name:
          type: string
          example: read_db
        description:
          type: string
          example: "Read-only database query tool"
        default_scope:
          $ref: '#/components/schemas/ToolScope'

    Tool:
      type: object
      additionalProperties: false
      required: [tool_name, created_at]
      properties:
        tool_name: { type: string }
        description: { type: string }
        default_scope: { $ref: '#/components/schemas/ToolScope' }
        created_at:
          type: string
          format: date-time

    # -------------------------
    # Policy
    # -------------------------
    PolicyCompileRequest:
      type: object
      additionalProperties: false
      properties:
        reason:
          type: string
          example: "Weekly rollout"
        dry_run:
          type: boolean
          default: false

    PolicyBundleMeta:
      type: object
      additionalProperties: false
      required: [policy_digest, created_at]
      properties:
        policy_digest:
          type: string
          example: "sha256:a1b2c3..."
        created_at:
          type: string
          format: date-time
        reason:
          type: string
        dry_run:
          type: boolean

    PolicyBundle:
      type: object
      additionalProperties: true
      description: |
        Policy bundle payload. Structure is versioned and may evolve.
        Decisions reference `policy_digest` for binding.

    PolicyDeployRequest:
      type: object
      additionalProperties: false
      required: [policy_digest]
      properties:
        policy_digest:
          type: string
          example: "sha256:a1b2c3..."
        rollout:
          type: object
          additionalProperties: false
          properties:
            mode:
              type: string
              enum: [immediate, canary]
              default: immediate
            canary_percent:
              type: integer
              minimum: 1
              maximum: 100
              default: 10

    PolicyDeployResponse:
      type: object
      additionalProperties: false
      required: [status, policy_digest]
      properties:
        status:
          type: string
          example: deployed
        policy_digest:
          type: string
          example: "sha256:a1b2c3..."
        active_since:
          type: string
          format: date-time

    PagedPolicyBundles:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/PolicyBundleMeta' }
        next_page_token:
          type: string

    # -------------------------
    # Evidence
    # -------------------------
    EvidencePackRequest:
      type: object
      additionalProperties: false
      required: [from, to]
      properties:
        from:
          type: string
          format: date-time
        to:
          type: string
          format: date-time
        agent_id:
          type: string
        decision:
          $ref: '#/components/schemas/Decision'
        format:
          type: string
          enum: [json]
          default: json

    EvidencePack:
      type: object
      additionalProperties: false
      required: [pack_id, generated_at, policy_digests, ctr_chain_segment]
      properties:
        pack_id:
          type: string
          format: uuid
        generated_at:
          type: string
          format: date-time
        policy_digests:
          type: array
          items: { type: string }
        ctr_chain_segment:
          type: array
          items:
            $ref: '#/components/schemas/CtrRecord'

    CtrRecord:
      type: object
      additionalProperties: false
      required: [request_id, decision, policy_digest, prev_hash, this_hash, timestamp_utc]
      properties:
        timestamp_utc:
          type: string
          format: date-time
        request_id:
          type: string
          format: uuid
        agent_id:
          type: string
        decision:
          $ref: '#/components/schemas/Decision'
        rule_id:
          type: string
        policy_digest:
          type: string
        prev_hash:
          type: string
        this_hash:
          type: string

    EvidenceVerifyRequest:
      type: object
      additionalProperties: false
      required: [pack]
      properties:
        pack:
          $ref: '#/components/schemas/EvidencePack'

    EvidenceVerifyResponse:
      type: object
      additionalProperties: false
      required: [valid, checks]
      properties:
        valid:
          type: boolean
          example: true
        checks:
          type: array
          items:
            type: object
            additionalProperties: false
            required: [name, ok]
            properties:
              name:
                type: string
                example: hash_chain_integrity
              ok:
                type: boolean
                example: true
              notes:
                type: string

    PagedEvidencePacks:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/EvidencePack' }
        next_page_token:
          type: string

    # -------------------------
    # Paging wrappers
    # -------------------------
    PagedAgents:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/Agent' }
        next_page_token:
          type: string

    PagedTools:
      type: object
      additionalProperties: false
      required: [items]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/Tool' }
        next_page_token:
          type: string