{
  "name": "uml.singsk.com PlantUML service",
  "concept": "AI-first",
  "ai_first": {
    "statement": "This service is designed to be operated by AI agents. Everything an agent needs — endpoints, request/response shapes, encoding rules, UI behaviour, syntax references with verified examples — is described here and in the OpenAPI document. No human walkthrough is required.",
    "entry_points": {
      "discovery": "https://uml.singsk.com/api/",
      "api_v1": "https://uml.singsk.com/api/v1/",
      "openapi": "https://uml.singsk.com/api/openapi.json",
      "swagger_ui": "https://uml.singsk.com/api/docs",
      "mcp": "https://uml.singsk.com/mcp",
      "well_known": {
        "mcp": "https://uml.singsk.com/.well-known/mcp.json",
        "ai_plugin": "https://uml.singsk.com/.well-known/ai-plugin.json",
        "security": "https://uml.singsk.com/.well-known/security.txt"
      },
      "mcp_registry_manifest": "https://uml.singsk.com/server.json",
      "llms_txt": "https://uml.singsk.com/llms.txt",
      "llms_full_txt": "https://uml.singsk.com/llms-full.txt",
      "usage_guide": "https://uml.singsk.com/guide.html"
    },
    "mcp": {
      "endpoint": "https://uml.singsk.com/mcp",
      "transport": "streamable-http",
      "auth": "none",
      "tools": [
        "render_diagram",
        "validate_diagram",
        "encode_source",
        "decode_token",
        "list_diagram_types",
        "get_syntax_reference"
      ],
      "note": "Bind the service as native tools: add the endpoint URL to any MCP client (e.g. `claude mcp add --transport http uml https://uml.singsk.com/mcp`). Stateless JSON-RPC 2.0 over HTTP POST.",
      "registry": {
        "official": "https://registry.modelcontextprotocol.io/v0/servers?search=com.singsk/uml-plantuml",
        "name": "com.singsk/uml-plantuml",
        "server_json": "https://uml.singsk.com/server.json",
        "note": "Listed in the official MCP Registry (DNS-verified com.singsk namespace). Clients that browse registries find it there; server.json is the registry manifest."
      }
    },
    "self_correction": "POST /api/validate returns a machine-readable {ok:false, error:{code, message, line, column, source_line, hint, diagram_type_detected, help}} for invalid PlantUML - use it to fix a diagram before rendering. /api/render/batch applies the same check per item.",
    "quick_start": [
      "1. Render: POST https://uml.singsk.com/api/render with JSON {\"text\":\"@startuml\\nA -> B: hi\\n@enduml\",\"format\":\"svg\"} — the response body is the SVG (or PNG bytes with format:\"png\").",
      "2. Or GET https://uml.singsk.com/plantuml/svg/?src=<token> where <token> comes from POST https://uml.singsk.com/api/encode - only while the token is at most 16000 characters (check url_ok in the encode response); larger diagrams must use POST /api/render.",
      "3. Decode any existing diagram URL token back to source with POST https://uml.singsk.com/api/decode."
    ]
  },
  "version": "1.1.0.2",
  "released": "2026-08-30",
  "spec_version": "1",
  "api_versions": {
    "v1": "https://uml.singsk.com/api/v1/",
    "note": "/api/v1/* is the canonical, stable prefix; unversioned /api/* paths are permanent aliases of v1."
  },
  "description": "Free online PlantUML editor and rendering service. Input is PlantUML source text; output is an SVG or PNG diagram rendered by the official PlantUML engine. Everything runs on this single origin; all assets are served locally (no CDN).",
  "endpoints": {
    "GET /api/": "This discovery document.",
    "GET /api/openapi.json": "OpenAPI 3.0 specification of every endpoint.",
    "GET /api/docs": "Human-browsable Swagger UI over the OpenAPI spec.",
    "GET /api/health": "Health check: verifies the rendering engine end-to-end and reports latency, version, uptime.",
    "POST /api/render": "Body {\"text\": \"<plantuml source>\", \"format\": \"svg\"|\"png\"} → diagram image. The core function: PlantUML code in, image out. Responses carry a strong ETag + Cache-Control: immutable; send If-None-Match to get 304 for a repeated source.",
    "POST /api/validate": "Body {\"text\": \"<plantuml source>\"} → 200 {ok:true, diagram_type, warnings[]} or 422 {ok:false, error:{code, message, line, column, source_line, hint, diagram_type_detected, help}}. Cheap syntax check before rendering; the error object is what lets an agent fix its own diagram.",
    "POST /api/render/batch": "Body {\"diagrams\":[{\"id\",\"text\",\"format\"}]} (max 20) → per-item results {id, ok, svg|png_base64, url, etag} or {id, ok:false, error}. Partial success allowed; one round trip for many diagrams.",
    "GET /api/stats": "Aggregate agent telemetry (counts by endpoint, status, error code, diagram type, agent family, entry point). No personal data.",
    "POST /api/encode": "Body {\"text\": \"<plantuml source>\"} → {\"encoded\": \"<token>\", \"token_length\", \"url_ok\", \"url_limit\": 16000, \"urls\": {svg, png}}. url_ok is false when the token exceeds the GET URL limit (then use POST /api/render). Token algorithm: UTF-8 → raw DEFLATE (no zlib header) → every 3 bytes mapped to 4 characters of PlantUML's own 64-character alphabet \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_\", no padding. This is PlantUML's custom encoding — it is NOT base64; do not use a base64 codec.",
    "GET /api/encode?text=": "Query variant of encode for short sources (URL-encode the text).",
    "POST /api/decode": "Body {\"encoded\": \"<token>\"} → {\"text\": \"<plantuml source>\"}. Reverses encode; works on tokens from any PlantUML server URL.",
    "GET /api/decode?src=": "Query variant of decode.",
    "GET /api/syntax": "Catalog of all 22 diagram and reference types with descriptions, help links, and render-verified example source.",
    "GET /api/help": "Catalog of the bundled help/reference pages.",
    "GET /api/about": "About this project: what it is, links, hashtags, copyright. Also at /about.html, /about.md, /about.json.",
    "GET /plantuml/svg/?src=<token>": "Native render endpoint (also /png, /txt, /pdf). Same engine the editor uses; token from /api/encode.",
    "GET /version.json": "Version single source of truth."
  },
  "ui": {
    "url": "https://uml.singsk.com",
    "description": "Single-page editor at / for humans; every UI capability is also reachable through the endpoints above.",
    "left_panel": "Header \"PlantUML Editor (AI-first supported)\". PlantUML source editor (Ace) with custom syntax highlighting, ~150-token autocomplete, brace folding (Ctrl+Alt+L), Ctrl+D line duplication, and a 3-dot tools menu: Full screen, Dark/Light mode toggle, Indentation for selected lines, Duplicate, Print code (A4 header, odd/even page numbers), Help. Typing re-renders the preview after a 500 ms debounce.",
    "right_panel": "Live diagram preview (SVG inline or PNG). Header holds a settings menu: Full screen, Swap panels (source<->preview; above/below on mobile), Download SVG, Download PNG, AI Documents (opens /api/), Offline version (coming soon, disabled), Dark/Light mode toggle, Print (diagram), Print all (diagram + code, reorderable), Print all to PDF (browser print-to-PDF with page arrangement), Help, Reset (centers the diagram at zoom 1), version, About (opens a 99%-viewport modal framing /about.html, theme-aware) and copyright. An \"AI Assistant (coming soon)\" entry is shown disabled - the in-editor assistant is in development and not published yet. Bottom bar: format selector (SVG/PNG), Decode button (paste a diagram URL/token to recover source), and the shareable render URL which doubles as the API call.",
    "splitter": "Draggable divider between panels; each side keeps a 200 px minimum; position persists in a cookie; vertical stacking below 768 px. The diagram tracks the frame in real time during drags.",
    "persistence": "The splitter position, panel-swap state, and the diagram pan/zoom view (scale + center as a fraction of the preview frame) persist in cookies and are re-applied before first paint and after every re-render — the diagram always reappears exactly where the user left it. Settings → Reset returns it to center at zoom 1.",
    "zoom": {
      "editor": "Ctrl + mouse wheel over the left panel changes the editor font size (8-48 px).",
      "diagram": "Ctrl or Shift + mouse wheel over the right panel zooms/pans the diagram (panzoom). The whole panel is a hover target.",
      "browser": "Ctrl + wheel outside both panels is untouched — normal browser page zoom."
    }
  },
  "help": {
    "index": "https://uml.singsk.com/help/",
    "note": "Per-diagram-type syntax references, bundled and served locally. /api/syntax cross-references these pages and carries one render-verified example from each.",
    "pages": [
      {
        "label": "Sequence",
        "description": "Show interactions between objects",
        "url": "https://uml.singsk.com/help/sequence-diagram.html"
      },
      {
        "label": "Use Case",
        "description": "Describe system functionality",
        "url": "https://uml.singsk.com/help/use-case-diagram.html"
      },
      {
        "label": "Class",
        "description": "Define class structure",
        "url": "https://uml.singsk.com/help/class-diagram.html"
      },
      {
        "label": "Activity",
        "description": "Model workflows and processes",
        "url": "https://uml.singsk.com/help/activity-diagram-beta.html"
      },
      {
        "label": "Activity (legacy)",
        "description": "Activity Diagram Legacy",
        "url": "https://uml.singsk.com/help/activity-diagram-legacy.html"
      },
      {
        "label": "Component",
        "description": "Organize system components",
        "url": "https://uml.singsk.com/help/component-diagram.html"
      },
      {
        "label": "State",
        "description": "Track object states",
        "url": "https://uml.singsk.com/help/state-diagram.html"
      },
      {
        "label": "Object",
        "description": "Show object instances",
        "url": "https://uml.singsk.com/help/object-diagram.html"
      },
      {
        "label": "Deployment",
        "description": "Define infrastructure",
        "url": "https://uml.singsk.com/help/deployment-diagram.html"
      },
      {
        "label": "Timing",
        "description": "Analyze timing behavior",
        "url": "https://uml.singsk.com/help/timing-diagram.html"
      },
      {
        "label": "Regex",
        "description": "Regular expression reference",
        "url": "https://uml.singsk.com/help/regex.html"
      },
      {
        "label": "Network",
        "description": "Draw network diagrams",
        "url": "https://uml.singsk.com/help/nwdiag.html"
      },
      {
        "label": "Wireframe",
        "description": "Create UI mockups",
        "url": "https://uml.singsk.com/help/salt.html"
      },
      {
        "label": "Archimate",
        "description": "Enterprise architecture",
        "url": "https://uml.singsk.com/help/archimate-diagram.html"
      },
      {
        "label": "Gantt",
        "description": "Project timeline charts",
        "url": "https://uml.singsk.com/help/gantt-diagram.html"
      },
      {
        "label": "Chronology",
        "description": "Time-based sequences",
        "url": "https://uml.singsk.com/help/chronology-diagram.html"
      },
      {
        "label": "MindMap",
        "description": "Brainstorm ideas visually",
        "url": "https://uml.singsk.com/help/mindmap-diagram.html"
      },
      {
        "label": "WBS",
        "description": "Work breakdown structure",
        "url": "https://uml.singsk.com/help/wbs-diagram.html"
      },
      {
        "label": "EBNF",
        "description": "Grammar notation reference",
        "url": "https://uml.singsk.com/help/ebnf.html"
      },
      {
        "label": "JSON",
        "description": "JSON data visualization",
        "url": "https://uml.singsk.com/help/json.html"
      },
      {
        "label": "YAML",
        "description": "YAML data visualization",
        "url": "https://uml.singsk.com/help/yaml.html"
      },
      {
        "label": "Pre-processing",
        "description": "Preprocessor directives",
        "url": "https://uml.singsk.com/help/preprocessing.html"
      }
    ]
  },
  "coming_soon": {
    "ai_assistant": "An in-editor AI Assistant (natural language -> PlantUML, grounded on /api/syntax and validated with /api/validate) is in development. It is not published yet; the settings menu shows it as \"coming soon\".",
    "offline_version": "Offline/desktop edition - coming soon."
  },
  "limits": {
    "max_render_body": "5 MB POST body on /api endpoints; native GET URLs accept very long ?src= tokens.",
    "rate_limit": "60 requests/min per client (burst 120) and at most 6 concurrent requests per client on POST /api/render, /api/validate and /api/render/batch. Every response carries RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset; a breach returns 429 with Retry-After and {ok:false, error:{code:\"RATE_LIMITED\"}}. Well-behaved agents read these headers instead of retrying blindly.",
    "render_timeout": "20 s per render; exceeded -> 504 {ok:false, error:{code:\"TIMEOUT\"}}.",
    "native_endpoints": "GET /plantuml/* (token form): 300 requests/min per client; a breach answers 403 for the rest of the minute. Prefer the /api endpoints, which throttle with RateLimit-* headers.",
    "native_url_length": "GET /plantuml/{svg|png|txt|pdf}/?src=<token>: the token must be at most 16000 characters (about a 16 KB URL, roughly 40-60 KB of PlantUML source). Longer tokens answer 414 {ok:false, error:{code:\"URL_TOO_LONG\"}} - this is a hard transport limit, not a syntax error. POST /api/encode reports token_length, url_ok and url_limit for every source; when url_ok is false, render with POST /api/render (JSON body up to 5 MB) or POST /api/render/batch, and do not publish the GET URL.",
    "batch": "20 diagrams per /api/render/batch call.",
    "cors": "Access-Control-Allow-Origin: * on all endpoints.",
    "auth": "None - anonymous public service."
  },
  "caching": {
    "render": "POST /api/render responses are a pure function of (source, format): strong ETag, Cache-Control: public, max-age=86400, immutable, If-None-Match -> 304, plus a server-side memo (X-Cache: HIT|MISS).",
    "native": "GET /plantuml/{svg|png}/?src=<token> responses: Cache-Control: public, max-age=86400, immutable.",
    "documents": "HTML/JSON/text documents: no-cache (always revalidated); static assets: 7 days."
  },
  "error_codes": [
    "EMPTY_SOURCE",
    "MISSING_START_TAG",
    "UNKNOWN_DIAGRAM_TYPE",
    "SYNTAX_ERROR",
    "TOO_LARGE",
    "TIMEOUT",
    "ENGINE_ERROR",
    "RATE_LIMITED",
    "BAD_FORMAT",
    "URL_TOO_LONG"
  ],
  "crawling": {
    "robots_txt": "https://uml.singsk.com/robots.txt",
    "policy": "User-agent: * / Allow: / - every crawler and AI agent may read the whole site. No X-Robots-Tag headers; every page is index,follow.",
    "fetching": "Documents are served whatever Accept header you send (text/html, application/json, text/markdown or none). If a fetch tool reports the site as blocked, retry with a plain GET; nothing here is disallowed."
  },
  "copyright": "Copyright (C) SingSK - 2025"
}