# AUTO-GENERATED tier-A mirror spec — do not edit.
# Source: openapi/runtime/tools.yaml (runtime 0.71.2)
#  + policy: openapi/mirror-policies/tools.json
# Regenerate: node scripts/generate-mirror-specs.mjs
openapi: 3.0.3
info:
  title: naturali.ai — Tools API
  version: 1.0.0
  description: >-
    Tools: the HTTP, MCP, client and pipeline capabilities an agent can call, and the endpoint that
    calls one directly. A fully runtime-backed module — this spec is generated verbatim from the
    runtime's own, re-rooted under /v1/projects/{project_id}. The project in the path is authorized
    by naturali and enforced upstream by the project's scoped credential.


    This module mirrors the upstream runtime verbatim (tier A, #304): paths are the runtime's own
    re-rooted under /v1/projects/{project_id}, and every field, method, status code and error shape
    passes through unchanged. Errors raised by the runtime arrive in its envelope; errors raised by
    naturali itself (authentication, project resolution, an unreachable runtime) use naturali's.
  contact:
    name: naturali.ai
    url: https://naturali.ai
servers:
  - url: "{baseUrl}"
    description: Host of your naturali.ai deployment; every path carries the /v1 prefix.
    variables:
      baseUrl:
        description: Base host URL.
        default: https://api.naturali.ai
tags:
  - name: Tools
    description: Manage tools
security:
  - bearerAuth: []
  - oauth2:
      - mcp:access
paths:
  /v1/projects/{project_id}/tools:
    post:
      tags:
        - Tools
      summary: Create a tool
      description: Creates a new tool in the project.
      operationId: createTool
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateToolRequest"
            examples:
              http:
                summary: HTTP tool
                value:
                  name: get-weather
                  type: http
                  description: Fetches current weather for a city
                  parameters:
                    type: object
                    properties:
                      city:
                        type: string
                    required:
                      - city
                  execute:
                    url: https://api.weather.example/v1/current?city={city}
              http_with_output_mapping:
                summary: HTTP tool reshaping its result with output_mapping
                value:
                  name: transcribe-audio
                  type: http
                  description: Transcribes an audio file and returns the bare text
                  parameters:
                    type: object
                    properties:
                      file:
                        type: string
                    required:
                      - file
                  execute:
                    url: https://api.x.ai/v1/stt
                    method: POST
                    body_mode: multipart
                  output_mapping:
                    var: output.text
              http_aws_sigv4:
                summary: HTTP tool signed with AWS Signature Version 4
                value:
                  name: get-s3-object
                  type: http
                  description: Reads an object from an S3 bucket
                  parameters:
                    type: object
                    properties:
                      key:
                        type: string
                    required:
                      - key
                  execute:
                    url: https://my-bucket.s3.us-east-1.amazonaws.com/{key}
                    method: GET
                    auth:
                      type: aws_sigv4
                      region: us-east-1
                      service: s3
                      access_key_id: "{{secret:sec_awsKeyId}}"
                      secret_access_key: "{{secret:sec_awsSecret}}"
              http_gcp_service_account:
                summary: HTTP tool authenticated as a GCP service account
                value:
                  name: create-bigquery-job
                  type: http
                  description: Submits a BigQuery job
                  parameters:
                    type: object
                    properties:
                      query:
                        type: string
                    required:
                      - query
                  execute:
                    url: https://bigquery.googleapis.com/bigquery/v2/projects/my-gcp-project/jobs
                    method: POST
                    auth:
                      type: gcp_service_account
                      credentials: "{{secret:sec_gcpServiceAccount}}"
                      scopes:
                        - https://www.googleapis.com/auth/bigquery
              client:
                summary: Client tool
                value:
                  name: show-dialog
                  type: client
                  description: Displays a confirmation dialog to the user
                  parameters:
                    type: object
                    properties:
                      message:
                        type: string
              mcp_scoped:
                summary: Read-only MCP tool (allowlisted to a subset of actions)
                value:
                  name: oneclick
                  type: mcp
                  mcp:
                    url: https://mcp.oneclick.example/sse
                  actions:
                    - list_campaigns
                    - get_campaign
              mcp_denylist:
                summary: Read-only MCP tool (whole surface minus write actions)
                value:
                  name: oneclick
                  type: mcp
                  mcp:
                    url: https://mcp.oneclick.example/sse
                  denied_actions:
                    - create_optimization
                    - update_optimization
                    - deactivate_all_optimizations
              pipeline:
                summary: Pipeline tool (compute → persist)
                value:
                  name: compute-and-save
                  type: pipeline
                  description: Computes a sum and persists the result
                  parameters:
                    type: object
                    properties:
                      x:
                        type: number
                      y:
                        type: number
                    required:
                      - x
                      - y
                  pipeline:
                    steps:
                      - id: compute
                        tool_id: tool_calc
                        action: add
                        input:
                          a:
                            var: input.x
                          b:
                            var: input.y
                      - id: persist
                        tool_id: tool_save_record
                        input:
                          value:
                            var: steps.compute.sum
                    output:
                      saved_id:
                        var: steps.persist.id
      responses:
        "201":
          description: Tool created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Tool"
        "400":
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    get:
      tags:
        - Tools
      summary: List tools
      description: Returns all tools in the project.
      operationId: listTools
      parameters:
        - name: limit
          in: query
          required: false
          description: Maximum number of results to return
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: offset
          in: query
          required: false
          description: Number of results to skip
          schema:
            type: integer
            default: 0
      responses:
        "200":
          description: List of tools
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - total
                  - limit
                  - offset
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Tool"
                  total:
                    type: integer
                  limit:
                    type: integer
                  offset:
                    type: integer
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/tools/{tool_id}:
    get:
      tags:
        - Tools
      summary: Get a tool
      description: >
        Returns a single tool by ID. A credential scoped to a project the tool is shared with,
        through an accepted share, reads its `id`, `name`, `description` and `parameters` only.
      operationId: getTool
      x-naturali-resource:
        kind: tool
        from: tool_id
      parameters:
        - name: tool_id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Tool
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Tool"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    patch:
      tags:
        - Tools
      summary: Update a tool
      description: Updates an existing tool.
      operationId: updateTool
      x-naturali-resource:
        kind: tool
        from: tool_id
      parameters:
        - name: tool_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateToolRequest"
      responses:
        "200":
          description: Tool updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Tool"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    delete:
      tags:
        - Tools
      summary: Delete a tool
      description: Deletes a tool by ID. A tool that is a decider's backend is refused with `409
        TOOL_HAS_DEPENDENTS`, and so is one another project has accepted a share of, or one of its
        own project's ingestion rules converts with, until `force=true`. Every share of the tool is
        revoked when it is deleted; an ingestion rule keeps its `tool_id` and fails the documents it
        matches with `CONVERTER_FAILED` until repointed.
      operationId: deleteTool
      x-naturali-resource:
        kind: tool
        from: tool_id
      parameters:
        - name: tool_id
          in: path
          required: true
          schema:
            type: string
        - name: force
          in: query
          required: false
          description: Delete the tool even when another project has accepted a share of it, revoking those
            shares, or an ingestion rule converts with it. A decider backend still refuses.
          schema:
            type: boolean
            default: false
      responses:
        "204":
          description: Deleted
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "409":
          description: The tool is a decider's backend, or has accepted shares and `force` is not set
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
  /v1/projects/{project_id}/tools/{tool_id}/call:
    post:
      tags:
        - Tools
      summary: Call a tool
      description: >
        Directly invokes a tool and returns its output. Supported for `http`, `mcp`, and `pipeline`
        tools. `client` tools cannot be invoked server-side and will return 422. A `pipeline` tool
        runs its declared steps in order and returns the mapped `output` (or the last step's
        output); `action` is ignored and `input` is the pipeline input.

        For `mcp` tools the `action` field is required and identifies which tool name to invoke. For
        `http` tools `action` is ignored. When an `mcp` tool declares an `actions` allowlist, an
        action outside it is rejected with `400 VALIDATION_FAILED` ("not available on this tool")
        before any outbound request is made.

        `preset_parameters` stored on the tool are pinned over the caller-supplied `input` before
        execution: a key the tool presets keeps its preset value even when `input` sets it. Keys the
        presets do not name are taken from `input` as sent.

        Guardrails attached to the tool or to its project adjudicate the call before dispatch,
        composing project + tool scope. A call this route cannot await a decision on — class C
        (human sign-off), class D, or a class-B tripwire — is refused with `422
        TOOL_DISPATCH_FAILED`, whose `meta` carries the `tool_id` and the `outcome`. A `pipeline`
        tool is adjudicated before its first step runs, and every step is adjudicated as the call of
        that tool it is.


        A credential scoped to a project the tool is shared with, through an accepted share, calls
        it in that project: the calling project's guardrails adjudicate it, it is metered there with
        `publisher_project_id`, and the tool receives the calling project as the
        `calling_project_id` tool context key.
      operationId: callTool
      x-naturali-resource:
        kind: tool
        from: tool_id
      parameters:
        - name: tool_id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CallToolRequest"
            examples:
              http_tool:
                summary: Call an HTTP tool
                value:
                  input:
                    city: London
              mcp_tool:
                summary: Call an MCP tool
                value:
                  action: get_weather
                  input:
                    location: Paris
      responses:
        "200":
          description: Tool output
          content:
            application/json:
              schema:
                description: The raw output returned by the tool — any JSON value (object, array, string, number,
                  boolean). `null` when the tool produced no output — an action answering `204 No
                  Content`, for instance.
        "400":
          description: Bad Request — invalid input or unknown action
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "401":
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "403":
          description: >
            Forbidden — the caller lacks permission, or the tool's target is blocked by the
            deployment's egress policy (TOOL_EGRESS_BLOCKED). An `http`/`mcp` tool may only reach
            publicly routable addresses unless the destination is listed in the server's
            TOOL_EGRESS_ALLOWED_HOSTS; the check runs against the resolved address and on every
            redirect hop, so `meta.tool_address` names the address that was refused.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "404":
          description: Tool not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "422":
          description: Unprocessable — tool type cannot be invoked server-side
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "429":
          description: "`SHARE_CAP_EXCEEDED`: the tool is another project's, reached through a share whose
            `cap` is spent for the window. Carries a `Retry-After` header and
            `error.meta.retry_after`."
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
        "502":
          description: >
            Upstream tool target error (TOOL_HTTP_ERROR). Returned when an `http`-type tool's target
            responds with a non-2xx status. The error `meta` carries the real upstream
            `tool_status_code`, `tool_response_body`, `tool_url`, and `tool_method`.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
    parameters:
      - $ref: "#/components/parameters/ProjectId"
components:
  schemas:
    Tool:
      type: object
      properties:
        id:
          type: string
          description: Public ID of the tool
          example: tool_V1StGXR8Z5jdHi6B
        project_id:
          x-naturali-ref: projects
          type: string
          description: Public ID of the owning project
          example: proj_V1StGXR8Z5jdHi6B
        name:
          type: string
          description: Tool name
          example: get-weather
        type:
          type: string
          enum:
            - http
            - client
            - mcp
            - pipeline
          description: Tool type
          example: http
        description:
          type: string
          nullable: true
          description: What the tool does (sent to the model)
        parameters:
          type: object
          nullable: true
          description: JSON Schema for tool input
        execute:
          type: object
          nullable: true
          description: >
            Execution config for http tools. Supported fields: `url` (required), `method` (default
            `POST`), `headers`, and `body_mode`. The `url` may contain `{paramName}` placeholders
            (e.g. `/users/{userId}`) that are replaced at call time with the corresponding tool
            argument value (URL-encoded). Arguments consumed as path parameters are excluded from
            the query string and request body. `body_mode` is `json` (default) or `multipart`. In
            `multipart` mode the merged tool arguments are sent as a `multipart/form-data` body:
            scalar fields become plain form fields and a field shaped like `{ content_type,
            filename, data_base64 }` is decoded from base64 and attached as a file part (the
            hardcoded `Content-Type: application/json` is dropped so `fetch` sets the multipart
            boundary itself).


            `auth` adds a computed request credential, for targets whose `Authorization` value
            cannot be expressed as a static header. Supported `auth.type` values:


            - `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`,
            `service`, `access_key_id` and `secret_access_key`; `session_token` is optional
            (temporary credentials). Incompatible with `body_mode: multipart`, whose body bytes are
            not known at signing time.

            - `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service
            account assertion and sends it as a bearer token. Requires `credentials` (the service
            account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached
            per service account and scope set until shortly before they expire.


            Credential fields accept `{{secret:...}}` references and should use them — a tool is
            readable by anyone who can `GET /tools`, and the stored reference is what is echoed
            back, never the resolved value.


            A credential written as a **literal** is stored and still sent on every call, but it is
            masked on every read: `secret_access_key`, `session_token` and `credentials` under
            `auth`, and any credential-named `headers` value (`Authorization`, `Cookie`, or a name
            containing `api-key`/`token`/`secret`/`password`), come back as `{"no_echo": true}`. The
            mask is an object rather than a string so a read-edit-write round trip fails the schema
            check instead of writing the placeholder in as the credential. A value carrying a
            `{{secret:...}}` reference is the wiring, not the credential, and stays readable.


            `headers` values additionally accept `{{context:<key>}}` references, resolved per call
            from the caller's `tool_context`, so a per-user credential can be placed in the real
            header the target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only
            in a prefixed context header. Valid **only** inside `headers` — a context value is
            caller-supplied, so it may not steer the `url` — and a key missing from the
            `tool_context` at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather
            than sending an empty credential. See the Tool Context reference.
        mcp:
          type: object
          nullable: true
          description: MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and
            `{{context:<key>}}` references, resolved right before the outbound MCP request; `url`
            accepts `{{secret:...}}` only. A literal credential in `headers` is masked on read,
            exactly as in `execute`.
        actions:
          type: array
          nullable: true
          items:
            type: string
          description: "Allowlist of actions to expose. For `mcp` tools: an optional allowlist of MCP tool
            names — when set, only those tools are exposed to the model and callable via `/call`;
            when `null`, the entire MCP server surface is exposed. Ignored for other tool types."
        denied_actions:
          type: array
          nullable: true
          items:
            type: string
          description: "For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after
            `actions` and taking precedence over it — a name in both lists is denied. This is the
            ergonomic way to scope a read+write MCP server read-only: deny just the write tools
            instead of enumerating every read tool in `actions`. Names not listed are exposed.
            `null` (default) denies nothing. Ignored for other tool types."
        context_keys:
          type: array
          nullable: true
          items:
            type: string
          description: Optional allowlist of `tool_context` keys that may be forwarded to this tool as
            prefixed context headers (`X-Naturali-Context-<key>` by default). When `null`, every key
            in the caller's `tool_context` is forwarded. When set, only the listed keys are, so a
            per-user credential in `tool_context` can be confined to the tools that need it; `[]`
            forwards none. The server-pinned identity keys (`session_id`, `actor_id`,
            `actor_external_id`) are always forwarded. A key consumed by a `{{context:<key>}}` token
            in this tool's own headers is substituted regardless of this list — the tool declared
            that header itself.
        preset_parameters:
          type: object
          nullable: true
          description: >-
            Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching
            fields in the input schema are removed from the schema shown to the model, and a pinned
            value wins over one the model or a direct caller supplies for the same key.


            Values accept `{{context:<key>}}` references, resolved per call from the caller's
            `tool_context`, so a pin can be the run's own value — the one account this run may act
            on — rather than one fixed when the tool was created. A resolved value is retyped to the
            parameter's declared schema type; a key missing from the call's `tool_context` fails the
            call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder.
            `{{secret:...}}` is not resolved here. See the Tool Context reference.
        pipeline:
          type: object
          nullable: true
          description: "Pipeline definition for `pipeline` tools: an ordered `steps` array, each step invoking
            a tool (optional `action`) and building its `input` from earlier results via JSON Logic
            evaluated over `{ input, steps }`. A step references its tool either by `tool_id` (an
            existing, persisted tool) or by an inline `tool` definition — the same shape as
            `CreateToolRequest` minus `project_id` — executed directly without a Tool row, but never
            both. An inline step `tool` cannot itself be of type `pipeline`. An optional `output`
            maps the final result."
        output_mapping:
          type: object
          nullable: true
          description: "Universal JSON Logic mapping applied to the tool's raw result, for every tool type
            (`http`, `mcp`, `pipeline`, `client`). Evaluated over `{ output: <raw result>, input:
            <merged input> }`, so `{ \"var\": \"output.text\" }` extracts a bare scalar field
            instead of requiring a wrapping `pipeline` tool, and `{ \"var\": \"input.title\" }`
            echoes back a field of the request that produced the response. For `pipeline` tools this
            runs *after* the pipeline's own `output` mapping, over that mapping's result."
        guardrail_ids:
          x-naturali-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the tool scope, governing this tool wherever it is used, by any
            agent.
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CreateToolRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Tool name
        type:
          type: string
          enum:
            - http
            - client
            - mcp
            - pipeline
          description: Tool type (default http)
        description:
          type: string
          description: What the tool does
        parameters:
          type: object
          description: JSON Schema for tool input
        execute:
          $ref: "#/components/schemas/ToolExecuteConfig"
        mcp:
          $ref: "#/components/schemas/ToolMcpConfig"
        actions:
          type: array
          items:
            type: string
          description: "Allowlist of actions. For `mcp` tools: an optional allowlist of MCP tool names to
            scope the server surface — omit or set `null` to expose every tool the MCP server
            offers. Ignored for other tool types."
        denied_actions:
          type: array
          items:
            type: string
          description: "For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after
            `actions` and taking precedence over it. Use it to scope a read+write MCP server
            read-only by denying just the write tools. Omit or set `null` to deny nothing. Ignored
            for other tool types."
        context_keys:
          type: array
          nullable: true
          items:
            type: string
          description: Optional allowlist of `tool_context` keys that may be forwarded to this tool as
            prefixed context headers (`X-Naturali-Context-<key>` by default). When `null` or
            omitted, every key in the caller's `tool_context` is forwarded. When set, only the
            listed keys are, so a per-user credential in `tool_context` can be confined to the tools
            that need it; `[]` forwards none. The server-pinned identity keys (`session_id`,
            `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a
            `{{context:<key>}}` token in this tool's own headers is substituted regardless of this
            list — the tool declared that header itself.
        preset_parameters:
          type: object
          description: >-
            Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching
            fields in the input schema are removed from the schema shown to the model, and a pinned
            value wins over one the model or a direct caller supplies for the same key.


            Values accept `{{context:<key>}}` references, resolved per call from the caller's
            `tool_context`, so a pin can be the run's own value — the one account this run may act
            on — rather than one fixed when the tool was created. A resolved value is retyped to the
            parameter's declared schema type; a key missing from the call's `tool_context` fails the
            call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder.
            `{{secret:...}}` is not resolved here. See the Tool Context reference.
        pipeline:
          type: object
          description: Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema
            for the full structure.
        output_mapping:
          type: object
          description: Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping`
            field on the Tool schema for details.
        guardrail_ids:
          x-naturali-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the tool scope.
    UpdateToolRequest:
      type: object
      additionalProperties: false
      properties:
        name:
          type: string
        type:
          type: string
          enum:
            - http
            - client
            - mcp
            - pipeline
        description:
          type: string
          nullable: true
        parameters:
          type: object
          nullable: true
        execute:
          $ref: "#/components/schemas/ToolExecuteConfig"
        mcp:
          $ref: "#/components/schemas/ToolMcpConfig"
        actions:
          type: array
          nullable: true
          items:
            type: string
          description: "Allowlist of actions. For `mcp` tools: an optional allowlist of MCP tool names to
            scope the server surface (`null` exposes every tool). Ignored for other tool types."
        denied_actions:
          type: array
          nullable: true
          items:
            type: string
          description: "For `mcp` tools: an optional denylist of MCP tool names to hide. Applied after
            `actions` and taking precedence over it. Use it to scope a read+write MCP server
            read-only by denying just the write tools. `null` denies nothing. Ignored for other tool
            types."
        context_keys:
          type: array
          nullable: true
          items:
            type: string
          description: Optional allowlist of `tool_context` keys that may be forwarded to this tool as
            prefixed context headers (`X-Naturali-Context-<key>` by default). When `null` or
            omitted, every key in the caller's `tool_context` is forwarded. When set, only the
            listed keys are, so a per-user credential in `tool_context` can be confined to the tools
            that need it; `[]` forwards none. The server-pinned identity keys (`session_id`,
            `actor_id`, `actor_external_id`) are always forwarded. A key consumed by a
            `{{context:<key>}}` token in this tool's own headers is substituted regardless of this
            list — the tool declared that header itself.
        preset_parameters:
          type: object
          nullable: true
          description: >-
            Fixed parameters pinned on every call this tool makes, whatever its type. Keys matching
            fields in the input schema are removed from the schema shown to the model, and a pinned
            value wins over one the model or a direct caller supplies for the same key.


            Values accept `{{context:<key>}}` references, resolved per call from the caller's
            `tool_context`, so a pin can be the run's own value — the one account this run may act
            on — rather than one fixed when the tool was created. A resolved value is retyped to the
            parameter's declared schema type; a key missing from the call's `tool_context` fails the
            call with `MISSING_TOOL_CONTEXT_KEY` rather than sending the literal placeholder.
            `{{secret:...}}` is not resolved here. See the Tool Context reference.
        pipeline:
          type: object
          nullable: true
          description: Pipeline definition for `pipeline` tools. See the `pipeline` field on the Tool schema
            for the full structure.
        output_mapping:
          type: object
          nullable: true
          description: Universal JSON Logic mapping applied to the tool's raw result. See the `output_mapping`
            field on the Tool schema for details.
        guardrail_ids:
          x-naturali-ref: guardrails
          type: array
          nullable: true
          items:
            type: string
          description: Guardrails attached at the tool scope.
    CallToolRequest:
      type: object
      properties:
        action:
          type: string
          description: >
            For `mcp` tools: the MCP tool name to invoke (must be in the tool's `actions` allowlist
            when one is set, and must not be in its `denied_actions` denylist). Ignored for `http`
            tools.
        input:
          type: object
          description: >
            Input parameters for the tool call. These are merged with the tool's `preset_parameters`
            before execution; a preset value wins over the same key sent here.
          additionalProperties: true
        tool_context:
          type: object
          additionalProperties:
            type: string
          description: >
            Key/value context for this call, forwarded to the tool as `X-Naturali-Context-<key>`
            request headers and resolving any `{{context:<key>}}` token in the tool's
            `execute.headers`, `mcp.headers` or `preset_parameters`. Narrowed by the tool's
            `context_keys` allowlist when it sets one.

            This route has no session, so it stamps no server-derived identity: the reserved keys
            `session_id`, `actor_id` and `actor_external_id` are dropped from this bag (in any
            casing) rather than forwarded, so a downstream tool can still trust that a context
            header naming one is server-derived. Every other key becomes an HTTP header name and
            must match that grammar, or the call fails with `INVALID_TOOL_CONTEXT_KEY`.
          example:
            tenantId: acme
            userToken: tok_abc123
    ToolExecuteConfig:
      type: object
      nullable: true
      additionalProperties: false
      description: >
        Execution config for http tools. Supported fields: `url` (required), `method` (default
        `POST`), `headers`, and `body_mode`. The `url` may contain `{paramName}` placeholders (e.g.
        `/users/{userId}`) that are replaced at call time with the corresponding tool argument value
        (URL-encoded). Arguments consumed as path parameters are excluded from the query string and
        request body. `body_mode` is `json` (default) or `multipart`. In `multipart` mode the merged
        tool arguments are sent as a `multipart/form-data` body: scalar fields become plain form
        fields and a field shaped like `{ content_type, filename, data_base64 }` is decoded from
        base64 and attached as a file part (the hardcoded `Content-Type: application/json` is
        dropped so `fetch` sets the multipart boundary itself).


        `auth` adds a computed request credential, for targets whose `Authorization` value cannot be
        expressed as a static header. Supported `auth.type` values:


        - `aws_sigv4` — signs the request with AWS Signature Version 4. Requires `region`,
        `service`, `access_key_id` and `secret_access_key`; `session_token` is optional (temporary
        credentials). Incompatible with `body_mode: multipart`, whose body bytes are not known at
        signing time.

        - `gcp_service_account` — mints a Google OAuth 2.0 access token from a signed service
        account assertion and sends it as a bearer token. Requires `credentials` (the service
        account key file JSON, as a string) and `scopes` (a non-empty array). Tokens are cached per
        service account and scope set until shortly before they expire.


        Credential fields accept `{{secret:...}}` references and should use them — a tool is
        readable by anyone who can `GET /tools`, and the stored reference is what is echoed back,
        never the resolved value.


        A credential written as a **literal** is stored and still sent on every call, but it is
        masked on every read: `secret_access_key`, `session_token` and `credentials` under `auth`,
        and any credential-named `headers` value (`Authorization`, `Cookie`, or a name containing
        `api-key`/`token`/`secret`/`password`), come back as `{"no_echo": true}`. The mask is an
        object rather than a string so a read-edit-write round trip fails the schema check instead
        of writing the placeholder in as the credential. A value carrying a `{{secret:...}}`
        reference is the wiring, not the credential, and stays readable.


        `headers` values additionally accept `{{context:<key>}}` references, resolved per call from
        the caller's `tool_context`, so a per-user credential can be placed in the real header the
        target expects (`Authorization: Bearer {{context:ocaToken}}`) instead of only in an
        `X-Naturali-Context-<key>` header. Valid **only** inside `headers` — a context value is
        caller-supplied, so it may not steer the `url` — and a key missing from the `tool_context`
        at call time fails the tool call with `MISSING_TOOL_CONTEXT_KEY` rather than sending an
        empty credential. See the Tool Context reference.
      properties:
        url:
          type: string
          description: Endpoint URL. May carry `{paramName}` placeholders resolved from the tool arguments at
            call time.
        method:
          type: string
          nullable: true
          description: "HTTP method (default: `POST`)"
        headers:
          type: object
          nullable: true
          additionalProperties:
            type: string
          description: Static headers sent on every request. Values accept `{{secret:...}}` and
            `{{context:<key>}}` references.
        body_mode:
          type: string
          nullable: true
          enum:
            - json
            - multipart
            - null
          description: "Request body encoding. Incompatible with `auth.type: aws_sigv4`."
        auth:
          $ref: "#/components/schemas/ToolExecuteAuthConfig"
    ToolExecuteAuthConfig:
      type: object
      nullable: true
      additionalProperties: false
      description: Computed request credential, for a target whose `Authorization` value cannot be
        expressed as a static header. `aws_sigv4` requires `region`, `service`, `access_key_id` and
        `secret_access_key`; `gcp_service_account` requires `credentials` and `scopes`. Every
        credential field accepts a `{{secret:...}}` reference and should carry one, since a literal
        is stored as written and masked on read.
      required:
        - type
      properties:
        type:
          type: string
          enum:
            - aws_sigv4
            - gcp_service_account
          description: Which credential is computed.
        region:
          type: string
          nullable: true
          description: "`aws_sigv4`: the signing region, e.g. `us-east-1`."
        service:
          type: string
          nullable: true
          description: "`aws_sigv4`: the signing service, e.g. `execute-api`."
        access_key_id:
          type: string
          nullable: true
          description: "`aws_sigv4`: the access key id."
        secret_access_key:
          type: string
          nullable: true
          description: '`aws_sigv4`: the secret access key. Masked on read as `{"no_echo": true}` when written
            as a literal.'
        session_token:
          type: string
          nullable: true
          description: "`aws_sigv4`: the session token of a temporary credential. Masked on read, like
            `secret_access_key`."
        credentials:
          type: string
          nullable: true
          description: "`gcp_service_account`: the service account key file JSON, as a string. Masked on read,
            like `secret_access_key`."
        scopes:
          type: array
          nullable: true
          items:
            type: string
          description: "`gcp_service_account`: the OAuth scopes to mint the token for."
    ToolMcpConfig:
      type: object
      nullable: true
      additionalProperties: false
      description: MCP server config (`url`, `headers`). `headers` values accept `{{secret:...}}` and
        `{{context:<key>}}` references, resolved right before the outbound MCP request; `url`
        accepts `{{secret:...}}` only. A literal credential in `headers` is masked on read, exactly
        as in `execute`.
      properties:
        url:
          type: string
          description: MCP server URL. Accepts a `{{secret:...}}` reference.
        headers:
          type: object
          nullable: true
          additionalProperties:
            type: string
          description: Headers sent on every MCP request. Values accept `{{secret:...}}` and
            `{{context:<key>}}` references.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          description: Structured error. Every error response uses this shape, so `code` can be read without
            first checking the type of `error`.
          required:
            - code
            - message
          properties:
            code:
              type: string
              description: "Machine-readable error code: lower_snake for an error naturali raises
                (`access_denied`), UPPER_SNAKE for one the runtime reports (`RESOURCE_NOT_FOUND`)."
              example: access_denied
            message:
              type: string
              description: Human-readable explanation.
              example: Your role in this project does not carry this action.
            details:
              type: object
              additionalProperties: true
              description: Structured context for an error naturali raises, such as the `resource` and `limit` of
                a `plan_limit_reached`.
            meta:
              type: object
              description: Structured context for an error the runtime reports.
  parameters:
    ProjectId:
      name: project_id
      in: path
      required: true
      description: Project public ID (proj_ prefix).
      schema:
        type: string
        example: proj_V1StGXR8Z5jdHi6B
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: A naturali API key (nat_sk_…) or a session JWT.
    oauth2:
      type: oauth2
      description: "A connected app's OAuth access token, issued by this API's authorization server
        (discovery: /.well-known/oauth-authorization-server). Its one scope carries every operation,
        confined to the projects the user chose when approving the app."
      flows:
        authorizationCode:
          authorizationUrl: https://api.naturali.ai/authorize
          tokenUrl: https://api.naturali.ai/token
          refreshUrl: https://api.naturali.ai/token
          scopes:
            mcp:access: Every operation this API serves, on the projects the grant covers.
