> ## Documentation Index
> Fetch the complete documentation index at: https://mcpjam-mintlify-docs-update-pr-3812-1786323532738.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Run one headless agent turn

> Runs ONE assistant turn over the supplied message history and responds synchronously with the final assistant text, the operations it invoked, references to any resources it created, and any actions awaiting human approval.

The caller owns conversation state: resend the full history each turn. The model is pinned server-side (hosted catalog) and billed to the project.

Tools are TIERED. Read operations and non-spending writes execute directly. Three categories are PROPOSAL-ONLY: operations that SPEND once (running a suite or a case, generating cases, cancelling a run), operations whose spend RECURS (setting a suite's schedule), and operations whose effects MCPJam cannot describe or undo because they leave MCPJam entirely (`call_server_tool`, which runs a tool on the caller's own MCP server). For each, the turn validates against the operation's real schema, persists a proposal, and returns it in `proposedActions` for a person to approve; `confirmSeverity` says which hazard the approver should be warned about. MCPJam-side deletions are excluded from the surface entirely — a proposal makes spend deliberate, but it does not make a deletion recoverable. Proposals are only offered when the request supplies a `conversationId`; without somewhere to collect a click there is nothing to approve, so the tools are withheld rather than offered and then refused.

Every tool invocation is hard-clamped to the path `projectId`. Turns are capped at 4 concurrent per organization (`429 RATE_LIMITED`; enforced per server instance) and ~90s wall clock (`504 TIMEOUT`). Guest callers are denied. Requires a hosted MCPJam deployment (`422 FEATURE_NOT_SUPPORTED` otherwise).

When a turn fails or times out AFTER work already persisted, the error body's `details.createdResources` and `details.proposedActions` carry what survived — check them before retrying.

**Retry policy:** send `idempotencyKey` as a STABLE identity for the triggering event (not a fresh uuid per attempt). Every write the turn performs derives its own key from it, so a retried turn's mutations land on the rows the first attempt created instead of duplicating them. The key makes a retry SAFE; it does not make one free — dedupe at your own trigger so most retries never re-run the turn at all. A retry whose model authors materially different arguments hashes differently and is correctly treated as a different write.



## OpenAPI

````yaml /reference/openapi.json post /projects/{projectId}/agent
openapi: 3.1.0
info:
  title: MCPJam API
  version: 1.0.0-preview
  description: >-
    Programmatic access to MCP servers saved in your MCPJam projects — live
    diagnostics (validate, inspect, export) and operations: call tools, render
    prompts, run eval suites asynchronously and poll their results, and import
    OAuth tokens.


    **The API is in preview**: the surface may change while we finish the
    design. Error `code` values are stable; error `message` strings are not.
    Write clients that ignore unknown response fields.
  contact:
    name: MCPJam
    url: https://github.com/MCPJam/inspector/issues
servers:
  - url: https://app.mcpjam.com/api/v1
    description: Hosted MCPJam
security:
  - bearerAuth: []
tags:
  - name: Hosts
    description: >-
      Project hosts: named model + capability profiles you run chats and eval
      suites against.
  - name: Environments
    description: >-
      Project environments: named, live-editable execution bundles (one host, an
      optional standalone server group, optionally pinned skills and plugin
      versions) that eval suites and journeys run against. Distinct from Sandbox
      images, which are Computer base images. Reads require project membership;
      every write requires project admin.
  - name: Sandbox images
    description: >-
      Custom Computer images: a digest-pinned Dockerfile built into an immutable
      image your project's computers boot from.
  - name: Server diagnostics
    description: Connect-level health checks against a saved MCP server.
  - name: Primitives
    description: 'The server''s MCP primitives: tools, prompts, and resources.'
  - name: Export
    description: Full-server snapshots for diffing and CI.
  - name: Execution
    description: 'Run the server''s primitives: call tools, render prompts.'
  - name: Eval runs
    description: >-
      Asynchronous eval suite runs: create with 202, poll status, iterations,
      and traces.
  - name: OAuth
    description: 'Bring-your-own OAuth: import externally obtained tokens for a server.'
  - name: Chatboxes
    description: >-
      Read-only access to the chatboxes published from a project: listing,
      settings, attached servers, and share links.
  - name: Catalog
    description: >-
      Discover the resources the other routes operate on: your account,
      projects, servers, eval suites, and chat sessions.
  - name: Tunnels
    description: >-
      Relay tunnels that expose local MCP servers through a public URL,
      registered as first-class project servers (the `mcpjam tunnel` CLI flow).
  - name: Agent
    description: >-
      Headless agent turns over the public API: send a message history, the
      server runs one assistant turn with project-scoped workspace tools (eval
      reads + suite creation) on a pinned hosted model, and returns the reply
      plus created-resource references.
paths:
  /projects/{projectId}/agent:
    post:
      tags:
        - Agent
      summary: Run one headless agent turn
      description: >-
        Runs ONE assistant turn over the supplied message history and responds
        synchronously with the final assistant text, the operations it invoked,
        references to any resources it created, and any actions awaiting human
        approval.


        The caller owns conversation state: resend the full history each turn.
        The model is pinned server-side (hosted catalog) and billed to the
        project.


        Tools are TIERED. Read operations and non-spending writes execute
        directly. Three categories are PROPOSAL-ONLY: operations that SPEND once
        (running a suite or a case, generating cases, cancelling a run),
        operations whose spend RECURS (setting a suite's schedule), and
        operations whose effects MCPJam cannot describe or undo because they
        leave MCPJam entirely (`call_server_tool`, which runs a tool on the
        caller's own MCP server). For each, the turn validates against the
        operation's real schema, persists a proposal, and returns it in
        `proposedActions` for a person to approve; `confirmSeverity` says which
        hazard the approver should be warned about. MCPJam-side deletions are
        excluded from the surface entirely — a proposal makes spend deliberate,
        but it does not make a deletion recoverable. Proposals are only offered
        when the request supplies a `conversationId`; without somewhere to
        collect a click there is nothing to approve, so the tools are withheld
        rather than offered and then refused.


        Every tool invocation is hard-clamped to the path `projectId`. Turns are
        capped at 4 concurrent per organization (`429 RATE_LIMITED`; enforced
        per server instance) and ~90s wall clock (`504 TIMEOUT`). Guest callers
        are denied. Requires a hosted MCPJam deployment (`422
        FEATURE_NOT_SUPPORTED` otherwise).


        When a turn fails or times out AFTER work already persisted, the error
        body's `details.createdResources` and `details.proposedActions` carry
        what survived — check them before retrying.


        **Retry policy:** send `idempotencyKey` as a STABLE identity for the
        triggering event (not a fresh uuid per attempt). Every write the turn
        performs derives its own key from it, so a retried turn's mutations land
        on the rows the first attempt created instead of duplicating them. The
        key makes a retry SAFE; it does not make one free — dedupe at your own
        trigger so most retries never re-run the turn at all. A retry whose
        model authors materially different arguments hashes differently and is
        correctly treated as a different write.
      operationId: runAgentTurn
      parameters:
        - $ref: '#/components/parameters/projectId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AgentTurnRequest'
            example:
              messages:
                - role: user
                  content: >-
                    Create an eval suite for my weather server with one case
                    that checks get_forecast is called for "forecast for Paris".
      responses:
        '200':
          description: The completed turn.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgentTurnResponse'
              example:
                reply: >-
                  Created the suite "weather smoke" with 1 case. It is ready to
                  run.
                toolCalls:
                  - operation: list_project_servers
                  - operation: create_eval_suite
                createdResources:
                  - type: eval_suite
                    id: ts_abc123
                    name: weather smoke
                    url: https://app.mcpjam.com/evals/suite/ts_abc123
                usage:
                  inputTokens: 2450
                  outputTokens: 312
                proposedActions:
                  - actionId: act_8f2c1d
                    operation: run_eval_suite
                    description: Run eval suite weather smoke
                    buttonLabel: Run it
                    kind: start
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '422':
          $ref: '#/components/responses/FeatureNotSupported'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '504':
          $ref: '#/components/responses/Timeout'
components:
  parameters:
    projectId:
      name: projectId
      in: path
      required: true
      description: ID of the hosted project that contains the server.
      schema:
        type: string
  schemas:
    AgentTurnRequest:
      type: object
      required:
        - messages
      properties:
        messages:
          type: array
          minItems: 1
          maxItems: 50
          description: >-
            The conversation so far, oldest first. The caller owns state and
            resends the full history each turn. Each message content is capped
            at 8,000 characters AND 8,192 UTF-8 bytes; the whole history is
            additionally capped at 98,304 UTF-8 bytes (96 KB).
          items:
            type: object
            required:
              - role
              - content
            properties:
              role:
                type: string
                enum:
                  - user
                  - assistant
              content:
                type: string
                minLength: 1
                maxLength: 8000
                description: Plain text. Max 8,000 characters and 8,192 UTF-8 bytes.
        idempotencyKey:
          type: string
          minLength: 1
          maxLength: 200
          description: >-
            A STABLE identity for the triggering event — not a fresh uuid per
            attempt. Each write the turn performs derives its own key from this
            one, so a retried turn re-issues the same mutations onto the same
            rows instead of authoring duplicates. Printable ASCII only (the
            value becomes a header on every write the turn issues). Omitting it
            preserves the older non-idempotent behaviour rather than being
            rejected.
        conversationId:
          type: string
          minLength: 1
          maxLength: 64
          description: >-
            Where an approval control can be rendered for this turn (a channel,
            a thread, a DM). Its presence is what makes the GATED tools
            available at all: without somewhere to collect a click, the turn is
            not offered them, so the model never proposes an action nobody can
            approve.
        slackChannelId:
          type: string
          minLength: 1
          maxLength: 64
          description: >-
            Slack-named spelling of `conversationId`. Both are accepted
            indefinitely; `conversationId` wins when both are sent.
    AgentTurnResponse:
      type: object
      required:
        - reply
        - toolCalls
        - createdResources
        - proposedActions
        - usage
      properties:
        reply:
          type: string
          description: The assistant's final text for this turn.
        toolCalls:
          type: array
          items:
            type: object
            required:
              - operation
            properties:
              operation:
                type: string
                description: Platform operation name invoked during the turn.
        createdResources:
          type: array
          description: Resources the turn created, with app deep links.
          items:
            type: object
            required:
              - type
              - id
              - url
            properties:
              type:
                type: string
                description: >-
                  Resource type. New types may be added; render an unknown type
                  generically rather than dropping it.
              id:
                type: string
              name:
                type: string
              url:
                type: string
                format: uri
        proposedActions:
          type: array
          description: >-
            Actions the turn wants to take but may not take on its own — they
            SPEND, or reach a third-party system. Nothing here has happened:
            each entry is a persisted proposal awaiting a human click. The
            `actionId` is all an approval control needs to carry, because the
            server holds what the action does; the remaining fields are for
            RENDERING only and must never be echoed back as instructions.
          items:
            type: object
            required:
              - actionId
              - operation
              - description
              - buttonLabel
              - kind
            properties:
              actionId:
                type: string
                description: Opaque id. The only value a click may carry.
              operation:
                type: string
                description: Platform operation name. Display and telemetry only.
              description:
                type: string
                description: >-
                  Short summary of the TARGET, not a cost estimate.
                  Model-influenced text — escape before rendering.
              buttonLabel:
                type: string
                description: Verb for the approval control, e.g. "Run it".
              kind:
                type: string
                enum:
                  - start
                  - cancel
                  - generate
                  - schedule
                  - external
                description: >-
                  What approving it does, so a host can word its confirmation
                  and its after-the-fact announcement truthfully. Treat an
                  unrecognised value as neutral rather than guessing.
              confirmSeverity:
                type: string
                enum:
                  - spend
                  - external
                  - none
                description: >-
                  How stern the confirmation copy should be: `spend` consumes
                  quota or credits, `external` runs somewhere MCPJam does not
                  control and cannot undo, `none` states explicitly that the
                  default cost warning would be FALSE for this action (disabling
                  a schedule stops spend). Absent means the host's default copy
                  is honest enough.
              target:
                type: object
                required:
                  - type
                  - selector
                properties:
                  type:
                    type: string
                    description: Resource type the proposal is about, e.g. `eval_suite`.
                  selector:
                    type: string
                    description: >-
                      The validated input's own selector for it — an id where
                      the server minted the proposal, possibly a name where the
                      model authored it. Match against both.
                description: >-
                  What the proposal is about, for correlating it with other turn
                  output (e.g. suppressing a duplicate run affordance on the
                  created suite it already offers to run). Display/dedup only —
                  never an instruction. Absent means match-unknown; fall back to
                  coarser behavior.
        usage:
          type: object
          required:
            - inputTokens
            - outputTokens
          properties:
            inputTokens:
              type: integer
            outputTokens:
              type: integer
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: >-
            Stable, machine-readable error code. New codes may be added over
            time; treat unknown codes as non-retryable failures unless the HTTP
            status says otherwise.
          enum:
            - UNAUTHORIZED
            - FORBIDDEN
            - NOT_FOUND
            - CONFLICT
            - VALIDATION_ERROR
            - RATE_LIMITED
            - FEATURE_NOT_SUPPORTED
            - SERVER_UNREACHABLE
            - TIMEOUT
            - OAUTH_REQUIRED
            - INTERNAL_ERROR
        message:
          type: string
          description: >-
            Human-readable description. May change between releases — don't
            match on it.
        details:
          type: object
          description: Optional, unstructured context bag.
          additionalProperties: true
  responses:
    ValidationError:
      description: Malformed body or parameters.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: VALIDATION_ERROR
            message: Invalid JSON body
    Unauthorized:
      description: >-
        Missing, invalid, revoked, or orphaned key (`UNAUTHORIZED`) — or the
        **target MCP server** needs an OAuth grant (`OAUTH_REQUIRED`), which is
        a property of the server, not your key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            badKey:
              summary: Invalid or revoked key
              value:
                code: UNAUTHORIZED
                message: Invalid API key
            oauthRequired:
              summary: Target server needs an OAuth grant
              value:
                code: OAUTH_REQUIRED
                message: Server requires OAuth authorization
    FeatureNotSupported:
      description: The target server doesn't support this MCP capability.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: FEATURE_NOT_SUPPORTED
            message: Server does not support resources
    RateLimited:
      description: >-
        Per-key rate limit exceeded (60 requests/minute sustained, bursts up to
        10). Honor `Retry-After` and back off with jitter.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: RATE_LIMITED
            message: API key rate limit exceeded. Slow down and retry.
    InternalError:
      description: Something failed on MCPJam's side.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: INTERNAL_ERROR
            message: Unexpected internal error
    Timeout:
      description: The target MCP server connected but didn't respond in time.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            code: TIMEOUT
            message: Request to server timed out
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        MCPJam API key (`sk_…`). Create one at [Settings → API
        keys](https://app.mcpjam.com/settings/api-keys). Guest sessions cannot
        use the API, and API keys cannot manage other API keys.

````