> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowyte.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Freeze an MCP tool into a skill

> Turns one equipped MCP tool (see the tools list) into a frozen `mcp` skill on the agent. The agent never freeforms the call: only the parameters you expose become the skill's inputs, and pinned arguments are fixed at runtime. Every required input must be pinned or exposed, and sensitive fields (government ID, financial, credential, health) can't be pinned or exposed. Because an MCP tool carries no read/write metadata, the skill is treated as a write (needs confirmation, runs non-parallel) unless you attest that it's read-only. `allowedFields` is deny-by-default — list the dotted field paths the agent is allowed to receive back.



## OpenAPI

````yaml /openapi.yaml post /agents/{agentId}/integrations/{kind}/mcp-skills
openapi: 3.1.0
info:
  title: Flowyte V2 Control-Plane REST API
  version: 1.0.0
  description: >-
    The single REST API for the Flowyte platform (base path `/api/v1`).
    Authenticate every request with a secret API key — `Authorization: Bearer
    flowyte_sk_…` — and each operation lists the scope the key must hold.
    Successful responses use the `ApiResponse<T>` envelope; list responses use
    cursor-based `PaginatedResponse<T>`. Errors are RFC 9457 problem+json.
    Streaming endpoints return Server-Sent Events
    (`event:<type>\ndata:<json>\n\n`, terminating with `event: done`).
  contact:
    name: Flowyte Platform
  license:
    name: Proprietary
servers:
  - url: /api/v1
    description: Flowyte control-plane (versioned URI; additive in v1).
security:
  - apiKey: []
tags:
  - name: Agents
    description: The single user-facing entity.
  - name: Support
    description: In-app "Get help" form → support inbox email.
  - name: Skills
    description: >
      Agent capabilities / tools. A skill is ONE atomic action — typically a
      single API call the agent makes in one step (look up an order, create a
      record, send a message, transfer the call). Use a skill when the task is a
      single step. When a task needs several details gathered across turns
      BEFORE acting, build a Playbook (which gathers the inputs and then calls
      skills) — see the Playbooks tag.
  - name: Integrations
    description: Native OAuth integrations.
  - name: Knowledge
    description: RAG knowledge sources & preview.
  - name: Playbooks
    description: >
      Multi-turn conversation scripts — a node graph (gather → confirm → branch)
      the agent follows to collect several inputs IN ORDER across turns before
      acting. Build a playbook when a single skill call isn't enough because the
      agent must gather MANY details first, or run a SEQUENCE of skills, before
      it can finish (e.g. take a full service request: gather the problem,
      address, and time, confirm, THEN file it; qualify a lead; a multi-step
      intake). A playbook does NOT call an integration itself — it owns the
      conversation and holds the state across turns; the actual action is
      performed by the SKILL(s) it gathers the inputs for. So a playbook
      ORCHESTRATES skills. Rule of thumb — one API call → a Skill; "gather N
      things in order, then submit" → a Playbook that drives the conversation
      and calls the skill(s) at the end.
  - name: Variables
    description: >
      The agent-wide interaction-variable registry (B.4b) — DERIVED at read time
      from the agent's playbooks and skills (collect slots, skill output
      bindings, {var} placeholders) and merged with a thin annotation overlay
      (notes, declared type hints, manual declarations). Read-only observation,
      NEVER a gate: it never validates a reference and never affects authoring,
      publish, or runtime behaviour.
  - name: Guardrails
    description: Deterministic guardrail policies & caller verification.
  - name: Numbers
    description: Phone numbers / DIDs.
  - name: Test
    description: Test, simulate, talk-token, probe.
  - name: Observe
    description: Post-call analytics, conversations, receipts, transcripts.
  - name: Billing
    description: Plans, wallet, usage, fixed phrases.
  - name: Voices
    description: Voice catalog.
  - name: Webhooks
    description: Webhook endpoints & deliveries.
  - name: Records
    description: >
      Caller Context Store — pre-synced external records the agent greets a
      caller from, plus the learned object-type field registry. Fed by the
      Zapier app's Create/Update Record action and directly by this REST API;
      read at call time by the context_lookup skill (a sub-100ms local lookup,
      no Zapier round-trip).
  - name: AuditLogs
    description: API/key activity logs.
  - name: Chat
    description: Chat channel — sessions, messages, OpenAI-compatible, widget.
  - name: PublishableKeys
    description: Browser-safe, one-agent publishable keys.
  - name: Widget
    description: Embed widget config + snippet.
  - name: Uploads
    description: Multipart file uploads backing file_id params
  - name: Meta
    description: Platform metadata (language/SKU/tier capabilities).
paths:
  /agents/{agentId}/integrations/{kind}/mcp-skills:
    parameters:
      - $ref: '#/components/parameters/AgentIdPathScoped'
      - $ref: '#/components/parameters/IntegrationKindPath'
    post:
      tags:
        - Integrations
      summary: Freeze an MCP tool into a skill
      description: >-
        Turns one equipped MCP tool (see the tools list) into a frozen `mcp`
        skill on the agent. The agent never freeforms the call: only the
        parameters you expose become the skill's inputs, and pinned arguments
        are fixed at runtime. Every required input must be pinned or exposed,
        and sensitive fields (government ID, financial, credential, health)
        can't be pinned or exposed. Because an MCP tool carries no read/write
        metadata, the skill is treated as a write (needs confirmation, runs
        non-parallel) unless you attest that it's read-only. `allowedFields` is
        deny-by-default — list the dotted field paths the agent is allowed to
        receive back.
      operationId: freezeMcpSkill
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - toolName
                - name
                - description
              properties:
                toolName:
                  type: string
                  description: >-
                    The equipped MCP tool to freeze to (stamped into
                    execution_config.tool_name).
                name:
                  type: string
                  description: The LLM-facing skill/function name.
                description:
                  type: string
                  description: The LLM-facing skill description.
                pinnedArgs:
                  type: object
                  additionalProperties: true
                  description: >-
                    Operator-pinned args (override/augment LLM args); the second
                    lock. Cannot overlap exposedParams; sensitive-PII fields are
                    refused.
                exposedParams:
                  type: array
                  items:
                    type: string
                  description: >-
                    The subset of the tool's input the LLM may fill → becomes
                    parameters_schema (byte-faithful sub-schemas). Sensitive-PII
                    fields are refused.
                allowedFields:
                  type: array
                  items:
                    type: string
                  description: >-
                    Dotted leaf paths (from a probe) the agent may READ back.
                    Deny-by-default: an empty list returns NO data (a warning is
                    emitted).
                timeoutMs:
                  type: integer
                  description: >-
                    Optional per-call overall timeout (bounded at 25s; the mcp
                    type is slow-listed so filler masks the dip).
                enabled:
                  type: boolean
                  description: >-
                    Start enabled (default true; the confirm gate — not a draft
                    flag — protects a write).
                readAttestation:
                  type: object
                  description: >-
                    When attestedRead is true, DOWNGRADES the fail-closed write
                    to an un-gated read, recorded verbatim (who + when + note).
                  properties:
                    attestedRead:
                      type: boolean
                    note:
                      type: string
                agentic:
                  type: object
                  description: >
                    Optional AGENTIC block — present to freeze ONE concrete
                    action from a Zapier AGENTIC (dynamic-discovery) server (see
                    POST …/tools/{tool}/resolve). When present, the server
                    RE-RESOLVES the action from selectedApi + action +
                    parentParams (never trusting a client-supplied schema),
                    synthesizes a CLOSED schema, and rejoins the same freeze
                    pipeline. `instructions`/`output` are OPERATOR-authored and
                    LOCKED — they are stored, NEVER LLM-filled, and are NOT
                    schema inputs (listing them in exposedParams is rejected).
                    Omit for the classic per-tool freeze (unchanged).
                  required:
                    - selectedApi
                    - action
                  properties:
                    selectedApi:
                      type: string
                      description: >-
                        The app's internal resolve handle (from the resolve
                        step).
                    action:
                      type: string
                      description: The resolved action `key` to freeze.
                    parentParams:
                      type: object
                      additionalProperties: true
                      description: >-
                        The parent pins that CLOSE the action's dynamic children
                        at resolve time.
                    instructions:
                      type: string
                      description: >-
                        OPERATOR-authored, LOCKED natural-language instructions
                        for the action (never LLM-filled; not a schema input).
                    output:
                      type: string
                      description: >-
                        OPERATOR-authored, LOCKED output spec (must demand
                        strict JSON; not a schema input).
      responses:
        '200':
          description: >-
            The created (frozen) mcp skill, plus authoring warnings and whether
            it was attested a read.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      skill:
                        $ref: '#/components/schemas/Skill'
                      warnings:
                        type: array
                        items:
                          type: string
                      attestedRead:
                        type: boolean
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/Conflict'
        '422':
          $ref: '#/components/responses/ValidationError'
      security:
        - apiKey:
            - skills:write
components:
  parameters:
    AgentIdPathScoped:
      name: agentId
      in: path
      required: true
      schema:
        type: string
    IntegrationKindPath:
      name: kind
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/IntegrationKind'
  schemas:
    Skill:
      type: object
      required:
        - id
        - agentId
        - organizationId
        - name
        - description
        - skillType
      properties:
        id:
          type: string
        agentId:
          type: string
        organizationId:
          type: string
        name:
          type: string
        description:
          type: string
        skillType:
          $ref: '#/components/schemas/SkillType_enum'
        parametersSchema:
          type: object
          additionalProperties: true
        requiredParams:
          type: array
          items:
            type: string
        toolMode:
          type: string
          enum:
            - two_way
            - one_way
        executionConfig:
          type: object
          additionalProperties: true
          description: >-
            Skill-type-specific operator settings (NOT the caller-collected
            values — those go in parametersSchema). Keys depend on skillType:
            email — { to: recipient email(s), comma-separated, REQUIRED,
            supports {param} templating e.g. "team@co.com" or
            "{customer_email}"; subject: REQUIRED, supports {param}; from_name:
            optional inbox display name (the address is always the verified
            no-reply@ sender, never set it here); intro: optional lead line;
            reply_to: optional, supports {param} e.g. "{customer_email}" }.
            http_webhook — { url (REQUIRED), method, headers, timeout_ms }.
            db_query — { sql_template }. transfer — { destination }. sms — {
            from: the messaging-enabled sending number in E.164 (OPTIONAL —
            leave it blank and the sending number is DERIVED at send time from
            the agent's SMS-active number, preferring the DID the call/chat
            arrived on); message: the body template, supports {param} }. The
            recipient is collected at call time as the "to" argument (the
            consent moment). An agent that owns an SMS-active number also gets a
            DERIVED send_sms tool automatically (no skill row needed) so it can
            OFFER TO TEXT the caller. builtin — { action:
            end_call|take_message|repeat }. geo — { action:
            check_service_area|find_nearest_location; for check_service_area:
            origin_address + radius_miles (is the caller within radius_miles of
            origin_address?); for find_nearest_location: roster = an array of {
            label, address, phone, lat?, lng? } locations to find the closest of
            (pre-geocode a large roster via POST /agents/{id}/geocode-roster so
            lat/lng are stored and routing is instant), plus optional
            route_by_caller_number:true to route from the caller's
            inbound-number area code (approximate — confirmed with the caller)
            when no address is given, plus optional radius_miles to gate
            coverage (each location covers that many miles, so the result
            carries in_service_area=false and the agent says it doesn't serve
            the area when the nearest location is farther) }. The LLM passes the
            caller's { address }. Geo is FREE to customers. Native-integration
            skills (calendar/google_sheets/shopify) are configured via the
            connect/provision flow, not here.
        dataEgressMap:
          type: object
          additionalProperties:
            type: string
        allowedFields:
          type: array
          items:
            type: string
        isWrite:
          type: boolean
        requiresVerifiedIdentity:
          type: boolean
        requiresConfirmation:
          type: boolean
        verificationLevel:
          $ref: '#/components/schemas/VerificationLevel'
        nonParallelizable:
          type: boolean
        channels:
          type: array
          items:
            $ref: '#/components/schemas/Channel'
          description: >-
            Which channels expose this skill's tool to the LLM. Empty = all
            channels the type supports (voice + chat + sms, minus voice-only
            types like transfer). Include "sms" to surface the tool on SMS
            sessions; the derived cross-channel send_sms offer-to-text tool is
            instead voice+chat only (you never offer to text ON a text).
        errorHandling:
          type: object
          additionalProperties: true
        isEnabled:
          type: boolean
        stakes:
          type: string
          enum:
            - low
            - medium
            - high
          description: >-
            Per-skill confirmation-gate hint the voice runtime reads
            defensively. Default low.
        availabilityWindow:
          type: string
          enum:
            - both
            - open
            - closed
          description: >-
            Per-skill availability by the agent's business-hours window — both
            (default, around the clock), open (business hours only), or closed
            (after hours only). The runtime drops this skill's tool when the
            verdict excludes the window.
        expectedLatencyMs:
          type: integer
          readOnly: true
          description: >-
            Learned average response time of this skill in milliseconds (an EWMA
            over recent real calls). Read-only; omitted until the skill has at
            least one call. Shown as a per-skill stat in the builder and used by
            the runtime to pace the spoken acknowledgment.
        schemaDrifted:
          type: boolean
          readOnly: true
          description: >-
            True when a frozen binding-compiled skill (native_rest / /
            native_sql) has a PIN — a filtered/read property, a called
            operation, an association object type — that no longer resolves
            against the provider's freshly re-discovered schema (#39).
            Re-discovery diffs each frozen skill's pins against the new schema
            and stamps this so the builder can badge a "needs re-authoring"
            skill; a drifted WRITE is additionally auto-disabled at revalidation
            (a drifted read degrades gracefully). Default false; read-only from
            the API.
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
    IntegrationKind:
      type: string
      enum:
        - google_calendar
        - google_sheets
        - calendly
        - hubspot
        - square
        - opentable
        - shopify
        - postgres
        - mysql
        - zapier
    SkillType_enum:
      type: string
      enum:
        - db_query
        - http_webhook
        - transfer
        - sms
        - email
        - calendar
        - google_sheets
        - crm
        - mcp
        - playbook
        - knowledge_query
        - builtin
        - geo
        - native_graphql
        - native_rest
        - native_sql
    VerificationLevel:
      type: string
      enum:
        - none
        - basic
        - step_up
    Channel:
      type: string
      enum:
        - voice
        - chat
        - sms
      description: Open enum (future whatsapp|email slot in with no schema change).
    ProblemDetails:
      description: RFC 9457 problem+json.
      type: object
      properties:
        type:
          type: string
          format: uri
          default: about:blank
        title:
          type: string
        status:
          type: integer
        detail:
          type: string
        instance:
          type: string
        code:
          type: string
        errors:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
              message:
                type: string
    ApiResponse_Void:
      allOf:
        - $ref: '#/components/schemas/ApiResponseBase'
        - type: object
          properties:
            data:
              type:
                - object
                - 'null'
    ApiResponseBase:
      type: object
      required:
        - success
      properties:
        success:
          type: boolean
        message:
          type: string
        errors:
          type: array
          items:
            type: object
            required:
              - field
              - message
            properties:
              field:
                type: string
              message:
                type: string
  responses:
    NotFound:
      description: Resource not found in org scope.
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    Conflict:
      description: Conflict (duplicate name, publish race, version mismatch, no diff).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
    ValidationError:
      description: Validation failure (errors[] populated on the envelope).
      content:
        application/problem+json:
          schema:
            $ref: '#/components/schemas/ProblemDetails'
        application/json:
          schema:
            $ref: '#/components/schemas/ApiResponse_Void'
  securitySchemes:
    apiKey:
      type: oauth2
      description: >
        Flowyte secret API key (`Authorization: Bearer flowyte_sk_live_…`).
        Scope-gated; is scoped to your organization — a key can never reach
        another tenant. The listed scopes in each operation's `apiKey`
        requirement are the scopes that key must hold. The `tokenUrl` is
        nominal: keys are minted in the dashboard.
      flows:
        clientCredentials:
          tokenUrl: /api/v1/api-keys
          scopes:
            agents:read: Read agents.
            agents:write: Create/update/delete agents, publish, rollback.
            knowledge:read: Read knowledge sources & preview.
            knowledge:write: Add/remove knowledge sources, uploads.
            skills:read: Read skills & skill-types.
            skills:write: Create/update/delete skills.
            playbooks:read: Read playbooks & graphs.
            playbooks:write: Create/update/delete playbooks & graphs.
            guardrails:read: Read guardrail policies & caller-verification.
            guardrails:write: Update guardrail policies & caller-verification.
            numbers:read: Read phone numbers / search availability.
            numbers:write: Purchase / assign / release numbers.
            sms:read: Read the org's SMS (10DLC) registration status and numbers.
            sms:write: Save/submit the 10DLC registration and toggle numbers for SMS.
            outbound:read: Read outbound contact lists and campaigns.
            outbound:write: >-
              Create/import contact lists, create/launch/pause/resume/cancel
              outbound campaigns, and enqueue single outbound calls.
            integrations:read: Read connected native integrations (status only — never tokens).
            integrations:write: Discover schemas, set data scoping, and disconnect a connection.
            integrations:connect: >-
              Connect a data source (submit credentials / begin OAuth) — a
              SEPARATE, higher-privilege scope because connecting INGESTS
              credentials and opens a new egress path; a discover/scope/author
              key need not carry it.
            records:read: >-
              Read the pre-synced Caller Context Store records and object-type
              field registry.
            records:write: >-
              Upsert / delete / bulk-import / purge caller-context records and
              edit type metadata.
            calls:read: Read conversations, receipts, transcripts, analytics.
            analytics:read: >-
              Read the Observe history list, per-agent analytics (the
              answer-rate summary), and the raw knowledge-gap list.
            analytics:write: >-
              Curate knowledge gaps (dismiss / mark in-progress). [M4 —
              reserved]
            billing:read: Read plans, wallet, usage.
            audit:read: Read API/key activity logs.
            webhooks:write: Manage webhook endpoints.
            keys:write: Manage secret API keys.
            chat:read: Read chat sessions & messages.
            chat:write: Create chat sessions & send messages (server-side).
            widgets:read: Read widget config & embed snippet.
            widgets:write: Update widget config.
            pubkeys:read: Read publishable keys.
            pubkeys:write: Manage publishable keys.

````