{
  "openapi": "3.1.1",
  "info": {
    "title": "Quillly Public API",
    "version": "1.0.0",
    "summary": "MCP endpoint, OAuth discovery and free Tools API for quillly.com.",
    "description": "Quillly is an MCP server that gives your AI 59 tools to create, SEO-optimize, and publish blogs directly to your websites. Connect Claude, ChatGPT, or Cursor via MCP — then ask your AI in natural language to manage your blog.\n\nThis document covers the endpoints a third party or an AI agent may call. The MCP endpoint (POST /api/mcp) is the main one: it exposes 59 tools for creating, scoring, publishing and tracking content on the websites a Quillly user has connected. The Tools API under /tools/*/api is free and needs no credential. Everything else on quillly.com is session-authenticated application code and is intentionally undocumented here.\n\nConnect an MCP client: claude mcp add --transport http quillly https://quillly.com/api/mcp -H \"Authorization: Bearer qly_...\"",
    "contact": {
      "name": "Quillly support",
      "email": "support@quillly.com",
      "url": "https://quillly.com/contact"
    },
    "termsOfService": "https://quillly.com/terms"
  },
  "servers": [
    {
      "url": "https://quillly.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Quillly documentation",
    "url": "https://quillly.com/docs"
  },
  "tags": [
    {
      "name": "MCP",
      "description": "The Model Context Protocol server — 59 content and SEO tools for connected websites.",
      "externalDocs": {
        "url": "https://quillly.com/dashboard/connections"
      }
    },
    {
      "name": "Discovery",
      "description": "Machine-readable indexes of the site itself."
    },
    {
      "name": "Image Generator",
      "description": "Generate images from raw HTML (with inline styles) or pre-built templates using Satori. Returns a signed R2 URL to the PNG (3h expiry) or raw binary. Identical requests are cached via content hash.",
      "externalDocs": {
        "url": "https://quillly.com/tools/image-generator"
      }
    },
    {
      "name": "QR Code Generator",
      "description": "Generate QR codes as PNG or SVG. Customize size, colors, margin, and error-correction level. Returns a signed R2 URL (3h) or raw binary. Identical requests are cached via content hash.",
      "externalDocs": {
        "url": "https://quillly.com/tools/qr-code-generator"
      }
    },
    {
      "name": "Favicon Generator",
      "description": "Generate a complete favicon ZIP bundle from an emoji, letter, or short text: 16/32/48/180/192/512 PNGs, multi-resolution favicon.ico, a web manifest, and ready-to-paste HTML tags. One API call, one ZIP.",
      "externalDocs": {
        "url": "https://quillly.com/tools/favicon-generator"
      }
    },
    {
      "name": "Code to Image",
      "description": "Turn any code snippet into a beautiful, share-ready screenshot. Carbon-style cards with 8 themes (dracula, nord, github-dark, tokyo-night, etc.), window controls, line numbers, and custom gradient backgrounds. Returns a signed R2 URL or raw PNG.",
      "externalDocs": {
        "url": "https://quillly.com/tools/code-to-image"
      }
    }
  ],
  "paths": {
    "/api/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "summary": "Call the Quillly MCP server (JSON-RPC 2.0)",
        "description": "Streamable HTTP transport for the Model Context Protocol. Every call is a JSON-RPC 2.0 request on this single endpoint. `initialize` and `notifications/initialized` need no credential; `tools/list` and `tools/call` require a bearer token. Send an API key (`qly_…`, created in the Quillly dashboard under Connections) or an OAuth access token (`qat_…`, issued by the authorization server advertised at /.well-known/oauth-authorization-server). The tool set returned by `tools/list` depends on the caller's plan and per-connection settings, so read it at runtime instead of hard-coding tool names.",
        "operationId": "mcpJsonRpc",
        "security": [
          {
            "bearerAuth": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response. A tool that fails for a business reason (quota, validation, disabled tool) still answers 200 with `result.isError = true` and a human-readable message — only transport-level problems use the `error` member.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "202": {
            "description": "Accepted with no body — the response to `notifications/initialized`."
          },
          "400": {
            "description": "Body was not valid JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or revoked credential. Carries `WWW-Authenticate: Bearer resource_metadata=\"…\"` pointing at the protected-resource metadata, unless a supplied API key was itself rejected (authorizing would not help).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "405": {
            "description": "GET is not supported — this server is stateless Streamable HTTP, POST only.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "tags": [
          "MCP"
        ],
        "summary": "Protected-resource metadata for the MCP endpoint (RFC 9728)",
        "description": "Tells an MCP client which authorization server protects /api/mcp. Fetched automatically by clients that receive a 401 with a `WWW-Authenticate` challenge.",
        "operationId": "getOauthProtectedResourceMetadata",
        "security": [],
        "responses": {
          "200": {
            "description": "Protected-resource metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthProtectedResourceMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "tags": [
          "MCP"
        ],
        "summary": "Authorization-server metadata (RFC 8414)",
        "description": "Authorization, token and dynamic-registration endpoints for the OAuth 2.1 flow that issues `qat_…` MCP access tokens. PKCE (S256) is required; the only scope is `mcp`.",
        "operationId": "getOauthAuthorizationServerMetadata",
        "security": [],
        "responses": {
          "200": {
            "description": "Authorization-server metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthAuthorizationServerMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/llms.txt": {
      "get": {
        "tags": [
          "Discovery"
        ],
        "summary": "Machine-readable index of quillly.com",
        "description": "Every marketing page, published article, doc and public endpoint as plain text (llmstxt.org convention), generated live from the same records the site serves. `/llms-full.txt` returns the full text of recent items instead of a list. Append `/llms.txt` to any published article URL, or `.md` to it, for that one item as markdown.",
        "operationId": "getLlmsTxt",
        "security": [],
        "responses": {
          "200": {
            "description": "The content index.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/tools/image-generator/api/generate": {
      "post": {
        "tags": [
          "Image Generator"
        ],
        "summary": "Generate an image from HTML or a preset template",
        "description": "Renders HTML (inline styles, Satori-compatible) OR a preset template into a PNG. Exactly ONE of `html` or `template` must be provided. Max HTML size 10 KB, max canvas 2400×2400. Results are cached by content hash; repeat calls are ~10x cheaper.",
        "operationId": "image-generator_generate_post",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "html": {
                    "type": "string",
                    "description": "Raw HTML with inline styles (Satori constraint: every element needs display:flex or be a leaf). Max 10 KB. Only https:// or data:image/* URLs allowed in <img src>. Provide EITHER html OR template.",
                    "maximum": 10000
                  },
                  "template": {
                    "type": "string",
                    "description": "Preset template name (legacy path). Provide EITHER html OR template.",
                    "enum": [
                      "og",
                      "twitter-header",
                      "linkedin-banner",
                      "square",
                      "story"
                    ]
                  },
                  "props": {
                    "type": "object",
                    "description": "Props for the template (title, subtitle, theme, accentColor, etc.). Only used with `template`."
                  },
                  "width": {
                    "type": "number",
                    "description": "Image width in pixels",
                    "minimum": 200,
                    "maximum": 2400,
                    "default": 1200
                  },
                  "height": {
                    "type": "number",
                    "description": "Image height in pixels. Provide EITHER height OR aspectRatio.",
                    "minimum": 200,
                    "maximum": 2400
                  },
                  "aspectRatio": {
                    "type": "string",
                    "description": "Format: \"W:H\" like \"1.91:1\" — height is auto-calculated from width.",
                    "example": "1.91:1"
                  },
                  "format": {
                    "type": "string",
                    "description": "Output image format",
                    "enum": [
                      "png",
                      "jpeg"
                    ],
                    "default": "png"
                  },
                  "response": {
                    "type": "string",
                    "description": "Return JSON with signed URL, or raw image bytes.",
                    "enum": [
                      "url",
                      "binary"
                    ],
                    "default": "url"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiSuccess"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          }
        }
      }
    },
    "/tools/qr-code-generator/api/generate": {
      "post": {
        "tags": [
          "QR Code Generator"
        ],
        "summary": "Generate a QR code as PNG or SVG",
        "description": "Encodes any URL, text, or data into a QR code. Max 2048 chars. PNG is a rasterized image; SVG is infinitely scalable. Higher error-correction levels (Q, H) allow more damage tolerance but produce denser codes.",
        "operationId": "qr-code-generator_generate_post",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string",
                    "description": "URL, text, or data to encode (max 2048 chars)",
                    "maximum": 2048
                  },
                  "size": {
                    "type": "number",
                    "description": "Output image width/height in pixels (ignored for SVG, used for viewBox)",
                    "minimum": 64,
                    "maximum": 2048,
                    "default": 512
                  },
                  "margin": {
                    "type": "number",
                    "description": "Quiet-zone margin around the QR (in modules)",
                    "minimum": 0,
                    "maximum": 16,
                    "default": 2
                  },
                  "fgColor": {
                    "type": "string",
                    "description": "Foreground (dark module) color as hex, e.g. \"#0f172a\"",
                    "default": "#000000"
                  },
                  "bgColor": {
                    "type": "string",
                    "description": "Background color as hex (use \"#ffffff00\" for transparent PNG)",
                    "default": "#ffffff"
                  },
                  "errorCorrection": {
                    "type": "string",
                    "description": "Error correction level: L (~7%), M (~15%), Q (~25%), H (~30%). Higher = more damage tolerant, denser.",
                    "enum": [
                      "L",
                      "M",
                      "Q",
                      "H"
                    ],
                    "default": "M"
                  },
                  "format": {
                    "type": "string",
                    "description": "Output format",
                    "enum": [
                      "png",
                      "svg"
                    ],
                    "default": "png"
                  },
                  "response": {
                    "type": "string",
                    "description": "JSON with signed URL, or raw image bytes",
                    "enum": [
                      "url",
                      "binary"
                    ],
                    "default": "url"
                  }
                },
                "required": [
                  "text"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiSuccess"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/svg+xml": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          }
        }
      }
    },
    "/tools/favicon-generator/api/generate": {
      "post": {
        "tags": [
          "Favicon Generator"
        ],
        "summary": "Generate a complete favicon bundle (ZIP)",
        "description": "Renders all standard favicon sizes (16, 32, 48, 180, 192, 512 PNG), a multi-resolution favicon.ico (with 16/32/48 embedded), site.webmanifest, and favicon-tags.html — then zips everything. Returns a signed R2 URL to the ZIP (3h) or the raw ZIP bytes.",
        "operationId": "favicon-generator_generate_post",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "source": {
                    "type": "string",
                    "description": "How to interpret `content`",
                    "enum": [
                      "emoji",
                      "letter",
                      "text"
                    ],
                    "default": "letter"
                  },
                  "content": {
                    "type": "string",
                    "description": "Emoji (e.g. \"🚀\"), single letter (\"Q\"), or short text (max 8 chars, 1-2 recommended)",
                    "maximum": 8
                  },
                  "bgColor": {
                    "type": "string",
                    "description": "Background color as hex",
                    "default": "#b9502a"
                  },
                  "fgColor": {
                    "type": "string",
                    "description": "Text color as hex (unused for emoji source)",
                    "default": "#ffffff"
                  },
                  "radius": {
                    "type": "number",
                    "description": "Corner radius as percent of icon size. 0 = square, 50 = circle.",
                    "minimum": 0,
                    "maximum": 50,
                    "default": 20
                  },
                  "fontFamily": {
                    "type": "string",
                    "description": "Font family for letter/text source",
                    "enum": [
                      "Inter",
                      "serif",
                      "mono"
                    ],
                    "default": "Inter"
                  },
                  "fontWeight": {
                    "type": "number",
                    "description": "Font weight (400, 600, 700, 800, or 900)",
                    "enum": [
                      "400",
                      "600",
                      "700",
                      "800",
                      "900"
                    ],
                    "default": 800
                  },
                  "response": {
                    "type": "string",
                    "description": "JSON with signed URL, or raw ZIP bytes",
                    "enum": [
                      "url",
                      "binary"
                    ],
                    "default": "url"
                  }
                },
                "required": [
                  "content"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiSuccess"
                }
              },
              "application/zip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          }
        }
      }
    },
    "/tools/code-to-image/api/generate": {
      "post": {
        "tags": [
          "Code to Image"
        ],
        "summary": "Generate a Carbon-style code screenshot",
        "description": "Renders a styled code card via Satori. Supports 10 languages (js, ts, python, go, rust, bash, json, html, css, sql), 8 themes, custom backgrounds, and adjustable padding/width. Height is computed automatically from line count. Max 8KB code.",
        "operationId": "code-to-image_generate_post",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "code": {
                    "type": "string",
                    "description": "Code to render (max 8000 chars)",
                    "maximum": 8000
                  },
                  "language": {
                    "type": "string",
                    "description": "Language for syntax highlighting",
                    "enum": [
                      "javascript",
                      "typescript",
                      "python",
                      "go",
                      "rust",
                      "bash",
                      "json",
                      "html",
                      "css",
                      "sql",
                      "plaintext"
                    ],
                    "default": "typescript"
                  },
                  "theme": {
                    "type": "string",
                    "description": "Color theme for the code card",
                    "enum": [
                      "dracula",
                      "nord",
                      "github-dark",
                      "github-light",
                      "monokai",
                      "one-dark",
                      "solarized-dark",
                      "tokyo-night"
                    ],
                    "default": "tokyo-night"
                  },
                  "title": {
                    "type": "string",
                    "description": "Filename shown in title bar (empty \"\" to hide)",
                    "maximum": 60,
                    "default": "example.ts"
                  },
                  "windowControls": {
                    "type": "boolean",
                    "description": "Show Mac-style traffic-light window controls",
                    "default": true
                  },
                  "lineNumbers": {
                    "type": "boolean",
                    "description": "Show line numbers",
                    "default": true
                  },
                  "padding": {
                    "type": "number",
                    "description": "Padding around the code card in pixels",
                    "minimum": 16,
                    "maximum": 128,
                    "default": 48
                  },
                  "width": {
                    "type": "number",
                    "description": "Canvas width in pixels (height auto-calculated)",
                    "minimum": 400,
                    "maximum": 2000,
                    "default": 900
                  },
                  "background": {
                    "type": "string",
                    "description": "Canvas background: CSS color or gradient (max 100 chars)",
                    "default": "linear-gradient(135deg,#b9502a,#f59e0b)"
                  },
                  "response": {
                    "type": "string",
                    "description": "JSON with signed URL, or raw PNG bytes",
                    "enum": [
                      "url",
                      "binary"
                    ],
                    "default": "url"
                  }
                },
                "required": [
                  "code"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiSuccess"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          },
          "500": {
            "description": "Internal error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ToolApiError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "A Quillly API key (`qly_…`) created at /dashboard/connections, or an OAuth 2.1 access token (`qat_…`). Keys belong to one account and carry that account's plan limits."
      }
    },
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "required": [
          "jsonrpc",
          "method"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "description": "Request id echoed back on the response. Omitted for notifications.",
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "method": {
            "type": "string",
            "enum": [
              "initialize",
              "notifications/initialized",
              "tools/list",
              "tools/call",
              "ping"
            ],
            "description": "MCP method. `tools/call` carries the tool name and arguments in `params`."
          },
          "params": {
            "type": "object",
            "additionalProperties": true,
            "description": "Method parameters. For `tools/call`: `{ \"name\": \"list_websites\", \"arguments\": { … } }`. For `initialize`: `{ \"protocolVersion\": \"2025-06-18\", \"clientInfo\": { … } }`.",
            "properties": {
              "name": {
                "type": "string",
                "description": "Tool name (tools/call only)."
              },
              "arguments": {
                "type": "object",
                "additionalProperties": true,
                "description": "Tool arguments, matching the input schema from tools/list."
              },
              "protocolVersion": {
                "type": "string",
                "enum": [
                  "2025-06-18",
                  "2025-03-26"
                ],
                "description": "Requested MCP protocol version (initialize only)."
              }
            }
          }
        }
      },
      "JsonRpcResponse": {
        "type": "object",
        "required": [
          "jsonrpc"
        ],
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "type": "object",
            "additionalProperties": true,
            "description": "Method result. `tools/list` returns `{ tools: [...] }`; `tools/call` returns `{ content: [{ type: \"text\", text: \"…\" }], isError?: boolean }`."
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "integer",
                "description": "JSON-RPC error code. -32001 authorization, -32600 invalid request, -32601 unknown method, -32602 bad params, -32700 parse error."
              },
              "message": {
                "type": "string"
              },
              "data": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "examples": [
              "unauthorized"
            ]
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "OAuthProtectedResourceMetadata": {
        "type": "object",
        "properties": {
          "resource": {
            "type": "string",
            "format": "uri"
          },
          "authorization_servers": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "bearer_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "OAuthAuthorizationServerMetadata": {
        "type": "object",
        "properties": {
          "issuer": {
            "type": "string",
            "format": "uri"
          },
          "authorization_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "token_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "registration_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "response_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grant_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "token_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "code_challenge_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ToolApiSuccess": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "data": {
            "type": "object"
          },
          "meta": {
            "$ref": "#/components/schemas/ToolApiMeta"
          },
          "quillly": {
            "type": "object"
          }
        }
      },
      "ToolApiError": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "retryAfter": {
                "type": "number"
              }
            },
            "required": [
              "code",
              "message"
            ]
          },
          "meta": {
            "$ref": "#/components/schemas/ToolApiMeta"
          },
          "quillly": {
            "type": "object"
          }
        }
      },
      "ToolApiMeta": {
        "type": "object",
        "properties": {
          "tool": {
            "type": "string"
          },
          "endpoint": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "cached": {
            "type": "boolean"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "rateLimit": {
            "type": "object",
            "properties": {
              "limit": {
                "type": "number"
              },
              "remaining": {
                "type": "number"
              },
              "resetAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      }
    }
  },
  "security": [],
  "x-mcp": {
    "endpoint": "https://quillly.com/api/mcp",
    "transport": "streamable-http",
    "protocolVersions": [
      "2025-06-18",
      "2025-03-26"
    ],
    "serverInfo": {
      "name": "quillly",
      "version": "1.0.0"
    },
    "toolCount": 59,
    "configGuide": "https://quillly.com/dashboard/connections",
    "integrations": {
      "claudeDesktop": "Add Custom Connector in Claude Desktop settings → paste https://quillly.com/api/mcp → sign in with Google (OAuth)",
      "claudeCode": "claude mcp add --transport http quillly https://quillly.com/api/mcp -H \"Authorization: Bearer qly_...\"",
      "cursor": "Add to mcp.json: { \"mcpServers\": { \"quillly\": { \"url\": \"https://quillly.com/api/mcp\", \"headers\": { \"Authorization\": \"Bearer qly_...\" } } } }"
    }
  },
  "x-llms-txt": "https://quillly.com/llms.txt"
}
