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

# Approve an MCP connection

> Consume exact fresh account approval and return a one-time OAuth callback.

<Note>MCP is implemented locally and is not production active. These synthetic examples do not announce availability or submit requests.</Note>

## Authorization

Authenticated Liftx Settings session. API keys, TradingView capabilities and MCP access tokens cannot authenticate this application route. Use [Settings → Integrations](https://app.liftx.io/settings/integrations); the public API hostname does not provide session access.

## Behavior

First start fresh authentication with `purpose:"mcp"` and the same complete `MCPConsentRequest`, then verify the offered password/Apple proof and MFA when required. Submit the unchanged request with that operation UUID before the five-minute approval expires. The grant requires current Pro/trial entitlement, one to 32 owned active links, a subset of requested scopes and an expiry within 30 days. Any selected position/template write scope requires `trading_consent:true`. Omitted `instrument_ids` permits all instruments on the selected links; otherwise exact canonical IDs are required.

## JSON body example

This is a synthetic local-client shape. The actual client owns registration, callback, state and PKCE challenge. Do not manually construct consent from a chat or substitute a different client request. Fixed timestamps are illustrative.

```json theme={null}
{
  "operation_id": "66666666-6666-4666-8666-666666666666",
  "request": {
    "authorization": {
      "client_id": "lx_mcp_client_WyJodHRwOi8vMTI3LjAuMC4xOjU0MzIxL2NhbGxiYWNrIl0",
      "redirect_uri": "http://127.0.0.1:54321/callback",
      "response_type": "code",
      "resource": "https://mcp.liftx.io/mcp",
      "code_challenge": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
      "code_challenge_method": "S256",
      "state": "SYNTHETIC_CLIENT_STATE",
      "scope": "read"
    },
    "exchange_link_ids": [
      17
    ],
    "instrument_ids": [
      "COPY_EXACT_CATALOG_ID"
    ],
    "scopes": [
      "read"
    ],
    "expires_at": 1893542400,
    "trading_consent": false
  }
}
```

## Response and errors

Success is `201` with `credential` metadata of kind `mcp` and a sensitive `redirect_url` containing the one-time code, original state and issuer. The code lasts two minutes; the OAuth client exchanges it with its retained verifier. No access token, refresh token or ordinary API key is returned here. Never log or paste the callback URL into chat.

An identical committed retry is `409 OAUTH_APPROVAL_CONSUMED`; it cannot recover the code. After an ambiguous response inspect connection metadata and deliberately restart OAuth rather than replaying approval. The operation shares the 12-requests/minute/IP issuance limiter.

MCP activation is required. When disabled, `503` uses `{error:"temporarily_unavailable",error_description,iss}`; other validation/admission failures retain the documented integration error envelope. Check status and content type. Unknown/duplicate JSON fields and compressed bodies are rejected; request size is limited to 256 KiB.

See [Settings contract](/api/settings-contract) and [MCP authorization](/mcp/overview).


## OpenAPI

````yaml api/openapi.json POST /integrations/mcp/approve
openapi: 3.1.0
info:
  title: Liftx integrations
  version: 1.0.0
  description: >-
    Preview contract for the Liftx API, TradingView webhook and session-owned
    integration settings. Production availability is pending activation and
    release qualification. External access requires Pro or trial entitlement and
    is unmetered within bounded resource limits. Receipt acceptance does not
    confirm execution completion. MCP is not active.
servers:
  - url: https://api.liftx.io
security: []
paths:
  /integrations/mcp/approve:
    post:
      summary: Approve an MCP OAuth grant with exact fresh account proof
      description: >-
        Session-owned Liftx Settings contract. Ordinary API keys, TradingView
        capabilities and MCP access tokens cannot authenticate this route. Use
        the authenticated Settings application; the public API hostname does not
        grant session access. Submit the exact consent request and operation
        UUID after successful purpose=mcp reauthentication. Atomically consumes
        the five-minute approval, validates current Pro/trial entitlement and
        owned active links, and creates the scoped MCP grant plus a two-minute
        authorization code. Returns no API key, MCP access token or refresh
        token; the OAuth client exchanges the callback code. An identical
        committed retry returns 409 OAUTH_APPROVAL_CONSUMED, never another code.
        On an ambiguous result inspect connection metadata, then explicitly
        restart authorization; never automatically repeat approval. Shares the
        existing 12 requests/minute/IP credential-security limiter. JSON is
        strict, uncompressed and limited to 256 KiB.
      operationId: post_integrations_mcp_approve
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                operation_id:
                  type: string
                  format: uuid
                  description: >-
                    Required nonzero UUID of the approved mcp-purpose request
                    for this same account session.
                request:
                  $ref: '#/components/schemas/MCPConsentRequest'
              required:
                - operation_id
                - request
              additionalProperties: false
            examples:
              synthetic:
                summary: Synthetic exact read-only consent
                description: >-
                  Synthetic documentation data, not an account snapshot, valid
                  credential or production-availability assertion. Fixed times,
                  IDs and callback state are illustrative; use the OAuth client
                  request in the real Settings flow.
                value:
                  operation_id: 66666666-6666-4666-8666-666666666666
                  request:
                    authorization:
                      client_id: >-
                        lx_mcp_client_WyJodHRwOi8vMTI3LjAuMC4xOjU0MzIxL2NhbGxiYWNrIl0
                      redirect_uri: http://127.0.0.1:54321/callback
                      response_type: code
                      resource: https://mcp.liftx.io/mcp
                      code_challenge: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
                      code_challenge_method: S256
                      state: SYNTHETIC_CLIENT_STATE
                      scope: read
                    exchange_link_ids:
                      - 17
                    instrument_ids:
                      - COPY_EXACT_CATALOG_ID
                    scopes:
                      - read
                    expires_at: 1893542400
                    trading_consent: false
      responses:
        '201':
          description: New MCP credential metadata and sensitive one-time callback URL
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MCPApprovalResponse'
              examples:
                synthetic:
                  summary: >-
                    Synthetic callback placeholder; not a valid authorization
                    code
                  description: >-
                    Synthetic documentation data, not an account snapshot, valid
                    credential or production-availability assertion. Fixed
                    times, IDs and callback state are illustrative; use the
                    OAuth client request in the real Settings flow.
                  value:
                    credential:
                      id: 77777777-7777-4777-8777-777777777777
                      integration_id: 88888888-8888-4888-8888-888888888888
                      name: Local MCP client
                      kind: mcp
                      prefix: lx_mcp_77777777
                      scopes:
                        - read
                      exchange_link_ids:
                        - 17
                      instrument_ids:
                        - COPY_EXACT_CATALOG_ID
                      created_at: 1893456000
                      expires_at: 1893542400
                      revoked_at: null
                    redirect_url: >-
                      http://127.0.0.1:54321/callback?code=ONE_TIME_AUTHORIZATION_CODE_PLACEHOLDER&state=SYNTHETIC_CLIENT_STATE&iss=https%3A%2F%2Fmcp.liftx.io
        '409':
          description: >-
            Approval already consumed or credential capacity reached; inspect
            metadata before starting a new flow
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: >-
            Credential security admission limit reached; existing security
            middleware response. Wait before an explicit retry, and never
            automatically replay secret issuance.
        '503':
          description: >-
            MCP disabled returns the OAuth-shaped availability error; internal
            admission failure retains the integration error shape
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/MCPOAuthUnavailable'
                  - $ref: '#/components/schemas/Error'
        default:
          description: >-
            Integration errors use Error; existing trading handlers retain their
            own JSON or text/plain error codec. Inspect status and content type.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
            text/plain:
              schema:
                type: string
      security:
        - SessionBearer: []
components:
  schemas:
    MCPConsentRequest:
      type: object
      properties:
        authorization:
          $ref: '#/components/schemas/OAuthAuthorization'
        exchange_link_ids:
          type: array
          minItems: 1
          maxItems: 32
          uniqueItems: true
          items:
            type: integer
            minimum: 1
            maximum: 9007199254740991
          description: >-
            Required. One to 32 unique active exchange links owned by this
            account; ownership is checked before grant issuance.
        instrument_ids:
          type: array
          maxItems: 64
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
            pattern: ^[!-~]+$
          description: >-
            Optional. Exact adapter-owned canonical IDs. Empty/omitted permits
            all instruments on the selected links; no inferred venue or symbol
            conversion.
        scopes:
          type: array
          minItems: 1
          maxItems: 5
          uniqueItems: true
          items:
            type: string
            enum:
              - read
              - positions:open
              - positions:modify
              - positions:terminate
              - templates:write
          description: >-
            Required. One to five unique selected permissions, each a subset of
            authorization.scope. Consent never expands the OAuth request. Values
            are normalized into sorted order.
        expires_at:
          type: integer
          format: int64
          description: >-
            Required. Future UTC Unix seconds, no more than 30 days from consent
            validation. Must still be valid when approval is consumed.
        trading_consent:
          type: boolean
          default: false
          description: >-
            Optional for read-only consent, where omitted means false. Required
            true when any selected scope permits position or template mutation.
            OAuth capability consent is separate from authorization of a
            concrete trade.
      required:
        - authorization
        - exchange_link_ids
        - scopes
        - expires_at
      additionalProperties: false
      allOf:
        - if:
            properties:
              scopes:
                contains:
                  enum:
                    - positions:open
                    - positions:modify
                    - positions:terminate
                    - templates:write
            required:
              - scopes
          then:
            properties:
              trading_consent:
                const: true
            required:
              - trading_consent
    MCPApprovalResponse:
      type: object
      properties:
        credential:
          allOf:
            - $ref: '#/components/schemas/Credential'
            - properties:
                kind:
                  const: mcp
        redirect_url:
          type: string
          format: uri
          description: >-
            Exact registered callback with one-time code, original state and
            iss=https://mcp.liftx.io. The code expires after two minutes and is
            exchanged by the OAuth client with PKCE. Sensitive: never log, paste
            into chat or repeat after an ambiguous response.
      required:
        - credential
        - redirect_url
      additionalProperties: false
    Error:
      type: object
      properties:
        success:
          const: false
        error:
          type: object
          properties:
            code:
              type: string
            message:
              type: string
          required:
            - code
            - message
          additionalProperties: false
      required:
        - success
        - error
      additionalProperties: false
    MCPOAuthUnavailable:
      type: object
      properties:
        error:
          type: string
          const: temporarily_unavailable
        error_description:
          type: string
        iss:
          type: string
          const: https://mcp.liftx.io
      required:
        - error
        - error_description
        - iss
      additionalProperties: false
    OAuthAuthorization:
      type: object
      properties:
        client_id:
          type: string
          maxLength: 3072
          pattern: ^lx_mcp_client_[A-Za-z0-9_-]+$
          description: >-
            Required. Exact opaque identifier returned by public OAuth client
            registration. Server revalidates its canonical callback set; do not
            invent an ID or treat it as a secret.
        redirect_uri:
          type: string
          format: uri
          maxLength: 512
          description: >-
            Required. Exact callback registered in client_id. Supported
            callbacks: https://claude.ai/api/mcp/auth_callback;
            https://chatgpt.com/connector_platform_oauth_redirect;
            https://chatgpt.com/connector/oauth/{identifier} with 1–128 ASCII
            letters/digits, hyphens or underscores; or
            http://localhost:{port}/callback / http://127.0.0.1:{port}/callback
            with port 1024–65535. No userinfo, query, fragment or escaped path.
        response_type:
          type: string
          const: code
        resource:
          type: string
          format: uri
          pattern: >-
            ^[hH][tT][tT][pP][sS]://[mM][cC][pP]\.[lL][iI][fF][tT][xX]\.[iI][oO]/mcp$
          description: >-
            Required. https://mcp.liftx.io/mcp with no port, credentials, query,
            fragment or escaped path. Scheme/hostname case is normalized; the
            path is exact.
        code_challenge:
          type: string
          minLength: 43
          maxLength: 43
          pattern: ^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$
          description: >-
            Required. Canonical unpadded base64url encoding of the 32-byte
            SHA-256 PKCE challenge. The client retains the verifier; do not send
            it to this Settings route.
        code_challenge_method:
          type: string
          const: S256
        state:
          type: string
          minLength: 1
          maxLength: 1024
          not:
            pattern: '[\u0000\r\n]'
          description: >-
            Required. Client-owned opaque state, 1–1024 UTF-8 bytes; NUL/CR/LF
            are rejected. Returned unchanged to the exact callback.
        scope:
          type: string
          default: read
          description: >-
            Optional. Whitespace-separated requested scopes: read,
            positions:open, positions:modify, positions:terminate,
            templates:write. At most five distinct scopes; duplicates/unknown
            values are rejected. Omitted or empty defaults to read. Preview
            returns a sorted, space-separated value.
      required:
        - client_id
        - redirect_uri
        - response_type
        - resource
        - code_challenge
        - code_challenge_method
        - state
      additionalProperties: false
    Credential:
      type: object
      properties:
        id:
          type: string
          format: uuid
        integration_id:
          type: string
          format: uuid
        name:
          type: string
        kind:
          type: string
          enum:
            - api
            - tradingview
            - mcp
          description: >-
            Credential metadata type. MCP credentials come from OAuth consent,
            not ordinary API-key issuance.
        prefix:
          type: string
        scopes:
          type: array
          items:
            type: string
        exchange_link_ids:
          type: array
          items:
            type: integer
            format: int64
        instrument_ids:
          type: array
          items:
            type: string
        created_at:
          type: integer
          format: int64
        expires_at:
          type: integer
          format: int64
        revoked_at:
          anyOf:
            - type: integer
              format: int64
            - type: 'null'
      required:
        - id
        - integration_id
        - name
        - kind
        - prefix
        - scopes
        - exchange_link_ids
        - instrument_ids
        - created_at
        - expires_at
        - revoked_at
      additionalProperties: false
  securitySchemes:
    SessionBearer:
      type: http
      scheme: bearer
      bearerFormat: existing authenticated Liftx session

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.