{
  "openapi": "3.1.0",
  "info": {
    "title": "YAB MCP API",
    "version": "2026-07-28",
    "description": "YAB exposes MCP over Streamable HTTP. Use a preregistered OAuth client and a workspace where your identity has access.\n\n## Obtain a JWT access token\n\n1. Read the MCP protected resource metadata for your workspace. Its `resource` value must be the sole audience (`aud`) of the JWT. Follow `authorization_servers` to the Better Auth issuer.\n2. For a human user, open the authorization endpoint with Authorization Code and PKCE `S256`, a fresh `state`, and the exact MCP `resource`. Request the registered `yab:*` scopes. Use `offline_access` if the client is allowed to receive a refresh token. Requesting `openid` adds a UserInfo audience, which makes the token ineligible for MCP. Exchange the returned code with the original `code_verifier`, registered `redirect_uri`, and the same MCP `resource`.\n3. For an approved service account, send a Client Credentials grant to the token endpoint. Use the client authentication method configured during registration and include the same `resource`.\n4. Use the returned `access_token` as `Authorization: Bearer <access_token>`. The access token is an RS256-signed JWT with `typ=at+jwt` and the exact MCP endpoint as its sole audience, for example `https://another-board.app/t/<tenant_id>/mcp`. An ID token or browser session cookie cannot authorize MCP. Workspace membership, project grants and scopes all apply. Keep access tokens and client secrets in your own client or secret store.\n\n## Call MCP\n\nSend a `POST` to the workspace endpoint with `Content-Type: application/json`, `Accept: application/json, text/event-stream`, and `MCP-Protocol-Version: 2026-07-28`. Set `MCP-Method` to the JSON-RPC method. On named operations, set `MCP-Name` to the tool name or resource URI. Use `tools/list` to get current tool schemas and `tools/call` to invoke one. Restart a subscription with a current token when the connection closes.\n\nUse a registered OAuth client and an MCP client to run requests. This reference contains placeholders only."
  },
  "servers": [
    {
      "url": "https://another-board.app"
    }
  ],
  "tags": [
    {
      "name": "MCP"
    },
    {
      "name": "OAuth"
    }
  ],
  "paths": {
    "/t/{tenant_id}/mcp": {
      "parameters": [
        {
          "name": "tenant_id",
          "in": "path",
          "required": true,
          "description": "ID of the authorized YAB workspace.",
          "schema": {
            "type": "string",
            "format": "uuid"
          },
          "example": "00000000-0000-4000-8000-000000000001"
        }
      ],
      "post": {
        "operationId": "mcpRequest",
        "summary": "Send an MCP JSON-RPC request",
        "description": "Discover capabilities, list or call tools, read resources, or subscribe to resource updates. Reads and subscriptions need yab:read. Writes and leases require their respective scopes and domain grants. The endpoint takes a single JSON object and no query string. Notifications may return an empty acknowledgement.",
        "tags": [
          "MCP"
        ],
        "responses": {
          "200": {
            "description": "JSON-RPC response or event stream.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              },
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "202": {
            "description": "Notification accepted without a response body."
          },
          "400": {
            "description": "Invalid request."
          },
          "401": {
            "description": "Missing or invalid credential.",
            "headers": {
              "WWW-Authenticate": {
                "description": "Bearer challenge with the resource_metadata URL.",
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "description": "The OAuth scope or current domain permission is insufficient."
          },
          "429": {
            "description": "Rate or concurrency limit reached."
          },
          "413": {
            "description": "The body exceeds 1 MiB."
          },
          "415": {
            "description": "Expected application/json without content encoding."
          }
        },
        "security": [
          {
            "McpJwt": []
          },
          {
            "McpOAuth": []
          }
        ],
        "parameters": [
          {
            "name": "MCP-Protocol-Version",
            "in": "header",
            "description": "Streamable HTTP protocol version.",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "2026-07-28"
              ]
            }
          },
          {
            "name": "MCP-Method",
            "in": "header",
            "description": "Matches the JSON-RPC method.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "server/discover"
          },
          {
            "name": "MCP-Name",
            "in": "header",
            "description": "Matches the tool name or resource URI when applicable.",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "const": "2.0"
                  },
                  "id": {
                    "type": [
                      "string",
                      "number",
                      "null"
                    ]
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "server/discover",
                      "tools/list",
                      "tools/call",
                      "resources/list",
                      "resources/templates/list",
                      "resources/read",
                      "subscriptions/listen"
                    ]
                  },
                  "params": {
                    "type": "object"
                  }
                }
              },
              "examples": {
                "discover": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "server/discover",
                    "params": {}
                  }
                },
                "tools": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/list",
                    "params": {}
                  }
                },
                "search": {
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 3,
                    "method": "tools/call",
                    "params": {
                      "name": "issue.search",
                      "arguments": {
                        "limit": 10
                      },
                      "_meta": {
                        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
                        "io.modelcontextprotocol/clientCapabilities": {}
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "shell",
            "label": "MCP with cURL",
            "example": "discover",
            "contentType": "application/json",
            "source": "curl --no-buffer --request POST \\\n  \"https://another-board.app/t/${YAB_TENANT_ID}/mcp\" \\\n  --header \"Authorization: Bearer ${YAB_ACCESS_TOKEN}\" \\\n  --header 'Accept: application/json, text/event-stream' \\\n  --header 'Content-Type: application/json' \\\n  --header 'MCP-Protocol-Version: 2026-07-28' \\\n  --header 'MCP-Method: server/discover' \\\n  --data '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"server/discover\",\"params\":{}}'"
          },
          {
            "lang": "shell",
            "label": "MCP with cURL",
            "example": "tools",
            "contentType": "application/json",
            "source": "curl --no-buffer --request POST \\\n  \"https://another-board.app/t/${YAB_TENANT_ID}/mcp\" \\\n  --header \"Authorization: Bearer ${YAB_ACCESS_TOKEN}\" \\\n  --header 'Accept: application/json, text/event-stream' \\\n  --header 'Content-Type: application/json' \\\n  --header 'MCP-Protocol-Version: 2026-07-28' \\\n  --header 'MCP-Method: tools/list' \\\n  --data '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\",\"params\":{}}'"
          },
          {
            "lang": "shell",
            "label": "MCP with cURL",
            "example": "search",
            "contentType": "application/json",
            "source": "curl --no-buffer --request POST \\\n  \"https://another-board.app/t/${YAB_TENANT_ID}/mcp\" \\\n  --header \"Authorization: Bearer ${YAB_ACCESS_TOKEN}\" \\\n  --header 'Accept: application/json, text/event-stream' \\\n  --header 'Content-Type: application/json' \\\n  --header 'MCP-Protocol-Version: 2026-07-28' \\\n  --header 'MCP-Method: tools/call' \\\n  --header 'MCP-Name: issue.search' \\\n  --data '{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"issue.search\",\"arguments\":{\"limit\":10},\"_meta\":{\"io.modelcontextprotocol/protocolVersion\":\"2026-07-28\",\"io.modelcontextprotocol/clientCapabilities\":{}}}}'"
          }
        ]
      }
    },
    "/.well-known/oauth-protected-resource/t/{tenant_id}/mcp": {
      "get": {
        "operationId": "mcpProtectedResource",
        "summary": "Discover the protected MCP resource",
        "description": "Read the exact resource URL and trusted authorization server before requesting an access token.",
        "tags": [
          "MCP"
        ],
        "responses": {
          "200": {
            "description": "Discovery document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "resource",
                    "authorization_servers"
                  ],
                  "properties": {
                    "resource": {
                      "$ref": "#/components/schemas/McpResource"
                    },
                    "authorization_servers": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uri"
                      }
                    },
                    "scopes_supported": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "bearer_methods_supported": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          },
          "429": {
            "description": "The discovery rate or concurrency limit was reached."
          }
        },
        "security": []
      },
      "head": {
        "operationId": "mcpProtectedResourceHeaders",
        "summary": "Read protected resource discovery headers",
        "description": "Returns discovery headers with an empty body.",
        "tags": [
          "MCP"
        ],
        "responses": {
          "200": {
            "description": "Discovery headers."
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          },
          "429": {
            "description": "The discovery rate or concurrency limit was reached."
          }
        },
        "security": []
      },
      "parameters": [
        {
          "name": "tenant_id",
          "in": "path",
          "required": true,
          "description": "ID of the authorized YAB workspace.",
          "schema": {
            "type": "string",
            "format": "uuid"
          },
          "example": "00000000-0000-4000-8000-000000000001"
        }
      ]
    },
    "/api/auth/.well-known/openid-configuration": {
      "get": {
        "operationId": "openidConfiguration",
        "summary": "Discover the OpenID issuer",
        "description": "Read the issuer, authorization endpoint, token endpoint and JWKS URL from Better Auth.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Discovery document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          }
        },
        "security": []
      },
      "head": {
        "operationId": "openidConfigurationHeaders",
        "summary": "Read OpenID issuer discovery headers",
        "description": "Returns discovery headers with an empty body.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Discovery headers."
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          }
        },
        "security": []
      }
    },
    "/api/auth/.well-known/oauth-authorization-server": {
      "get": {
        "operationId": "oauthMetadata",
        "summary": "Discover the OAuth server",
        "description": "Read the issuer, authorization endpoint, token endpoint and JWKS URL from Better Auth.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Discovery document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          }
        },
        "security": []
      },
      "head": {
        "operationId": "oauthMetadataHeaders",
        "summary": "Read OAuth server discovery headers",
        "description": "Returns discovery headers with an empty body.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Discovery headers."
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          }
        },
        "security": []
      }
    },
    "/.well-known/oauth-authorization-server/api/auth": {
      "get": {
        "operationId": "oauthMetadataRfc8414",
        "summary": "Discover the OAuth server via RFC 8414",
        "description": "Read the issuer, authorization endpoint, token endpoint and JWKS URL from Better Auth.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Discovery document.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          }
        },
        "security": []
      },
      "head": {
        "operationId": "oauthMetadataRfc8414Headers",
        "summary": "Read RFC 8414 discovery headers",
        "description": "Returns discovery headers with an empty body.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Discovery headers."
          },
          "404": {
            "description": "Discovery is unavailable for this resource."
          },
          "400": {
            "description": "The request contains a query string or a body."
          }
        },
        "security": []
      }
    },
    "/api/auth/jwks": {
      "get": {
        "operationId": "oauthJwks",
        "summary": "Read public JWT verification keys",
        "description": "Use the issuer JWKS and key ID when verifying a signature. Validate issuer, expiry, audience and permissions in addition to the signature.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "JSON Web Key Set.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "security": []
      }
    },
    "/api/auth/oauth2/authorize": {
      "get": {
        "operationId": "oauthAuthorize",
        "summary": "Authorize a human MCP client",
        "description": "Starts the user-facing authorization code flow with PKCE S256. The user may be asked to sign in and consent. Keep the original verifier locally and validate state when receiving the code. Request product scopes for the exact MCP resource. For a browser redirect, use Accept: text/html.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "302": {
            "description": "Redirect to sign-in, consent, or the registered callback."
          },
          "400": {
            "description": "Invalid authorization request."
          },
          "200": {
            "description": "JSON continuation when the client requests application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "redirect",
                    "url"
                  ],
                  "properties": {
                    "redirect": {
                      "const": true
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [],
        "parameters": [
          {
            "name": "client_id",
            "in": "query",
            "description": "Preregistered OAuth client ID.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "response_type",
            "in": "query",
            "description": "Authorization code flow.",
            "required": true,
            "schema": {
              "type": "string",
              "const": "code"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "description": "Exact registered callback URL.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "state",
            "in": "query",
            "description": "Fresh client-generated value verified on callback.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code_challenge",
            "in": "query",
            "description": "Base64url-encoded SHA-256 of the original PKCE verifier.",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            }
          },
          {
            "name": "code_challenge_method",
            "in": "query",
            "description": "PKCE S256.",
            "required": true,
            "schema": {
              "type": "string",
              "const": "S256"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "description": "Space-separated scopes allowed for this client.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "resource",
            "in": "query",
            "description": "Exact MCP URL from protected resource discovery.",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/McpResource"
            }
          }
        ]
      },
      "post": {
        "operationId": "oauthAuthorizeForm",
        "summary": "Submit authorization parameters",
        "description": "Starts the user-facing authorization code flow with PKCE S256. The user may be asked to sign in and consent. Keep the original verifier locally and validate state when receiving the code. Request product scopes for the exact MCP resource. For a browser redirect, use Accept: text/html.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "302": {
            "description": "Redirect to sign-in, consent, or the registered callback."
          },
          "400": {
            "description": "Invalid authorization request."
          },
          "200": {
            "description": "JSON continuation when the client requests application/json.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "redirect",
                    "url"
                  ],
                  "properties": {
                    "redirect": {
                      "const": true
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          }
        },
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "client_id",
                  "response_type",
                  "redirect_uri",
                  "state",
                  "code_challenge",
                  "code_challenge_method",
                  "scope",
                  "resource"
                ],
                "properties": {
                  "client_id": {
                    "type": "string",
                    "description": "Preregistered OAuth client ID."
                  },
                  "response_type": {
                    "type": "string",
                    "description": "Authorization code flow.",
                    "const": "code"
                  },
                  "redirect_uri": {
                    "type": "string",
                    "description": "Exact registered callback URL.",
                    "format": "uri"
                  },
                  "state": {
                    "type": "string",
                    "description": "Fresh client-generated value verified on callback."
                  },
                  "code_challenge": {
                    "type": "string",
                    "description": "Base64url-encoded SHA-256 of the original PKCE verifier.",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  },
                  "code_challenge_method": {
                    "type": "string",
                    "description": "PKCE S256.",
                    "const": "S256"
                  },
                  "scope": {
                    "type": "string",
                    "description": "Space-separated scopes allowed for this client."
                  },
                  "resource": {
                    "$ref": "#/components/schemas/McpResource"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/oauth2/token": {
      "post": {
        "operationId": "oauthToken",
        "summary": "Obtain an OAuth access token",
        "description": "Use client_credentials for a registered service account. For authorization_code supply code, redirect_uri and code_verifier. Use refresh_token with a refresh token previously issued to this client. Include the same exact MCP resource on every request. Authenticate with the registered method: HTTP Basic, form credentials, or a signed client assertion. A registered public client can use Authorization Code with PKCE.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "Access token response.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "access_token",
                    "token_type"
                  ],
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "token_type": {
                      "type": "string",
                      "example": "Bearer"
                    },
                    "expires_in": {
                      "type": "integer"
                    },
                    "scope": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string",
                      "description": "Refresh credential when granted."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request."
          },
          "401": {
            "description": "Missing or invalid credential."
          },
          "403": {
            "description": "The current OAuth grant or identity proof is insufficient."
          },
          "429": {
            "description": "Rate or concurrency limit reached."
          },
          "415": {
            "description": "Expected application/x-www-form-urlencoded without content encoding."
          }
        },
        "security": [
          {
            "OAuthClientBasic": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type",
                  "resource"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "description": "Grant configured for the client.",
                    "enum": [
                      "authorization_code",
                      "client_credentials",
                      "refresh_token"
                    ]
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Preregistered OAuth client ID."
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "For confidential clients registered with client_secret_post. Use HTTP Basic for client_secret_basic.",
                    "writeOnly": true
                  },
                  "resource": {
                    "$ref": "#/components/schemas/McpResource"
                  },
                  "scope": {
                    "type": "string",
                    "description": "Space-separated scopes allowed for this client."
                  },
                  "code": {
                    "type": "string",
                    "description": "One-use authorization code."
                  },
                  "redirect_uri": {
                    "type": "string",
                    "description": "Exact registered callback URL.",
                    "format": "uri"
                  },
                  "code_verifier": {
                    "type": "string",
                    "description": "Original PKCE verifier.",
                    "pattern": "^[A-Za-z0-9._~-]{43,128}$"
                  },
                  "refresh_token": {
                    "type": "string",
                    "description": "Refresh token if issued to the client.",
                    "writeOnly": true
                  },
                  "client_assertion_type": {
                    "type": "string",
                    "const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
                    "description": "Required with client_assertion for private_key_jwt."
                  },
                  "client_assertion": {
                    "type": "string",
                    "description": "Signed client assertion for private_key_jwt.",
                    "writeOnly": true
                  }
                },
                "allOf": [
                  {
                    "if": {
                      "properties": {
                        "grant_type": {
                          "const": "authorization_code"
                        }
                      }
                    },
                    "then": {
                      "required": [
                        "code",
                        "redirect_uri",
                        "code_verifier"
                      ]
                    }
                  },
                  {
                    "if": {
                      "properties": {
                        "grant_type": {
                          "const": "refresh_token"
                        }
                      }
                    },
                    "then": {
                      "required": [
                        "refresh_token"
                      ]
                    }
                  }
                ]
              }
            }
          }
        }
      }
    },
    "/api/auth/oauth2/introspect": {
      "post": {
        "operationId": "oauthIntrospect",
        "summary": "Check an OAuth token",
        "description": "A registered confidential client can check its own token. An authorized resource client can also introspect a JWT addressed to that resource. Authenticate with HTTP Basic, form credentials, or a signed client assertion. An inactive token returns active=false.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "OAuth response."
          },
          "400": {
            "description": "Invalid request."
          },
          "401": {
            "description": "Missing or invalid credential."
          },
          "403": {
            "description": "The current OAuth grant or identity proof is insufficient."
          },
          "429": {
            "description": "Rate or concurrency limit reached."
          },
          "415": {
            "description": "Expected application/x-www-form-urlencoded without content encoding."
          }
        },
        "security": [
          {
            "OAuthClientBasic": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Token owned by the registered OAuth client.",
                    "writeOnly": true
                  },
                  "token_type_hint": {
                    "type": "string",
                    "description": "Optional token type.",
                    "enum": [
                      "access_token",
                      "refresh_token"
                    ]
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Preregistered OAuth client ID."
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "For confidential clients registered with client_secret_post. Use HTTP Basic for client_secret_basic.",
                    "writeOnly": true
                  },
                  "client_assertion_type": {
                    "type": "string",
                    "const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
                    "description": "Required with client_assertion for private_key_jwt."
                  },
                  "client_assertion": {
                    "type": "string",
                    "description": "Signed client assertion for private_key_jwt.",
                    "writeOnly": true
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/oauth2/revoke": {
      "post": {
        "operationId": "oauthRevoke",
        "summary": "Revoke an OAuth token",
        "description": "The issuing client can revoke an OAuth token. Use its registered authentication method. A public client supplies its registered client_id.",
        "tags": [
          "OAuth"
        ],
        "responses": {
          "200": {
            "description": "OAuth response."
          },
          "400": {
            "description": "Invalid request."
          },
          "401": {
            "description": "Missing or invalid credential."
          },
          "403": {
            "description": "The current OAuth grant or identity proof is insufficient."
          },
          "429": {
            "description": "Rate or concurrency limit reached."
          },
          "415": {
            "description": "Expected application/x-www-form-urlencoded without content encoding."
          }
        },
        "security": [
          {
            "OAuthClientBasic": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Token owned by the registered OAuth client.",
                    "writeOnly": true
                  },
                  "token_type_hint": {
                    "type": "string",
                    "description": "Optional token type.",
                    "enum": [
                      "access_token",
                      "refresh_token"
                    ]
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Preregistered OAuth client ID."
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "For confidential clients registered with client_secret_post. Use HTTP Basic for client_secret_basic.",
                    "writeOnly": true
                  },
                  "client_assertion_type": {
                    "type": "string",
                    "const": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
                    "description": "Required with client_assertion for private_key_jwt."
                  },
                  "client_assertion": {
                    "type": "string",
                    "description": "Signed client assertion for private_key_jwt.",
                    "writeOnly": true
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "McpJwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Better Auth JWT access token (`typ=at+jwt`) whose sole audience is the exact workspace MCP URL."
      },
      "McpOAuth": {
        "type": "oauth2",
        "description": "Preregistered Better Auth client. Include the exact MCP resource in OAuth requests. Use PKCE S256 for Authorization Code.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://another-board.app/api/auth/oauth2/authorize",
            "tokenUrl": "https://another-board.app/api/auth/oauth2/token",
            "scopes": {
              "yab:read": "Read data allowed by workspace and project permissions.",
              "yab:write": "Submit allowed changes.",
              "yab:work": "Claim and maintain an authorized work lease.",
              "offline_access": "Request a refresh token when the client is registered for refresh."
            }
          },
          "clientCredentials": {
            "tokenUrl": "https://another-board.app/api/auth/oauth2/token",
            "scopes": {
              "yab:read": "Read data allowed by workspace and project permissions.",
              "yab:write": "Submit allowed changes.",
              "yab:work": "Claim and maintain an authorized work lease."
            }
          }
        }
      },
      "OAuthClientBasic": {
        "type": "http",
        "scheme": "basic",
        "description": "Authenticate a preregistered OAuth client using client_secret_basic."
      }
    },
    "schemas": {
      "McpResource": {
        "type": "string",
        "format": "uri",
        "examples": [
          "https://another-board.app/t/00000000-0000-4000-8000-000000000001/mcp"
        ],
        "pattern": "^https://another-board\\.app/t/[0-9A-Fa-f]{8}(?:-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}/mcp$"
      }
    }
  }
}
