{
  "schema": "kompo-tools/1.1",
  "name": "kompo.ai",
  "description": "LLM-guided music video composition. Create kompositions (markdown documents describing a music video), generate AI images/video/music, upload source media, render videos with ffmpeg, and track jobs.",
  "authentication": {
    "type": "bearer",
    "description": "Cognito JWT via PKCE authorization-code flow (public OAuth client). Most routes require auth (API Gateway Cognito authorizer); /api/health and /api/tools are public bootstrap endpoints. Include as: Authorization: Bearer <idToken>",
    "environments": {
      "prod": {
        "api": "https://api.ai.makeshitapp.com",
        "auth": "https://auth.ai.makeshitapp.com"
      },
      "test": {
        "api": "https://api.test.ai.makeshitapp.com",
        "auth": "https://auth.test.ai.makeshitapp.com"
      }
    }
  },
  "content_negotiation": {
    "description": "Endpoints marked markdown:true support Accept: text/markdown for compact, LLM-optimised responses (~80% fewer tokens than JSON). Default is application/json.",
    "header": "Accept: text/markdown"
  },
  "tools": [
    {
      "name": "health",
      "description": "Check API health, version, and build info. Public endpoint, no auth required.",
      "http": {
        "method": "GET",
        "path": "/api/health"
      },
      "authentication": false,
      "markdown": true,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "chat",
      "description": "Send a natural language message to the multimedia assistant (Haiku chat model). This endpoint is discussion/planning only in webMVC mode; it does not execute generation tools directly.",
      "http": {
        "method": "POST",
        "path": "/api/multimedia/chat"
      },
      "authentication": true,
      "markdown": true,
      "timeout_hint": "2-10s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "message": {
            "type": "string",
            "description": "Natural language request",
            "location": "body"
          },
          "conversation_history": {
            "type": "array",
            "description": "Prior turns: [{role: \"user\"|\"assistant\", content: \"...\"}]",
            "location": "body",
            "items": {
              "type": "object",
              "properties": {
                "role": {
                  "type": "string",
                  "enum": [
                    "user",
                    "assistant"
                  ]
                },
                "content": {
                  "type": "string"
                }
              },
              "required": [
                "role",
                "content"
              ]
            }
          },
          "model": {
            "type": "string",
            "enum": [
              "haiku",
              "haiku-bedrock"
            ],
            "description": "Chat model variant. Default: haiku",
            "location": "body"
          }
        },
        "required": [
          "message"
        ]
      }
    },
    {
      "name": "generate_image",
      "description": "Generate an image with Gemini. Optionally persist it to staging or library using storage_location.",
      "http": {
        "method": "POST",
        "path": "/api/generate-image"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "5-20s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "location": "body"
          },
          "resolution": {
            "type": "string",
            "enum": [
              "1080p",
              "720p",
              "4K",
              "vertical_1080p",
              "square_1080"
            ],
            "location": "body"
          },
          "style": {
            "type": "string",
            "location": "body"
          },
          "storage_location": {
            "type": "string",
            "enum": [
              "staging",
              "library"
            ],
            "location": "body"
          }
        },
        "required": [
          "prompt"
        ]
      }
    },
    {
      "name": "create_multimedia_task",
      "description": "Create an async multimedia generation task (video generation). Poll get_multimedia_task_status until completion.",
      "http": {
        "method": "POST",
        "path": "/api/multimedia/tasks"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "3-10s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "video_generation"
            ],
            "location": "body"
          },
          "prompt": {
            "type": "string",
            "location": "body"
          },
          "options": {
            "type": "object",
            "location": "body"
          },
          "referenceImage": {
            "type": "object",
            "location": "body",
            "properties": {
              "data": {
                "type": "string"
              },
              "mimeType": {
                "type": "string"
              }
            }
          }
        },
        "required": [
          "prompt"
        ]
      }
    },
    {
      "name": "get_multimedia_task_status",
      "description": "Get async multimedia task status/result.",
      "http": {
        "method": "GET",
        "path": "/api/multimedia/tasks/{taskId}"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "taskId": {
            "type": "string",
            "location": "path"
          }
        },
        "required": [
          "taskId"
        ]
      }
    },
    {
      "name": "list_kompositions",
      "description": "List all user's kompositions (video projects). Returns name, status, updated date, ID, and access level.",
      "http": {
        "method": "GET",
        "path": "/api/kompositions"
      },
      "authentication": true,
      "markdown": true,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "search_kompositions",
      "description": "Search the user's kompositions by name substring. Use this to resolve a komposition name (e.g. 'boat trip video') to its UUID before calling get_komposition, update_komposition, or create_video. Returns matching kompositions with name and ID.",
      "http": {
        "method": "GET",
        "path": "/api/kompositions"
      },
      "authentication": true,
      "markdown": true,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "Name substring to search for (case-insensitive)",
            "location": "query"
          }
        },
        "required": [
          "query"
        ]
      }
    },
    {
      "name": "get_komposition",
      "description": "Get a single komposition by ID, including full markdown content with tracks, sources, and BPM.",
      "http": {
        "method": "GET",
        "path": "/api/kompositions/{id}"
      },
      "authentication": true,
      "markdown": true,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Komposition UUID",
            "location": "path"
          }
        },
        "required": [
          "id"
        ]
      }
    },
    {
      "name": "create_komposition",
      "description": "Create a new komposition. Content MUST start with # Title (H1 header) — name is extracted from it. contentType MUST be \"markdown\". Do NOT pass a separate \"name\" field.",
      "http": {
        "method": "POST",
        "path": "/api/kompositions"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "content": {
            "type": "string",
            "description": "Markdown content. Must begin with # Title on line 1. See /knowledge.yaml → komposition-format for the build-ready structure.",
            "location": "body"
          },
          "contentType": {
            "type": "string",
            "enum": [
              "markdown"
            ],
            "description": "Always \"markdown\"",
            "location": "body"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "complete"
            ],
            "description": "Optional, defaults to \"draft\"",
            "location": "body"
          }
        },
        "required": [
          "content",
          "contentType"
        ]
      }
    },
    {
      "name": "update_komposition",
      "description": "Update a komposition. Name is re-extracted from updated H1. Do NOT pass a separate name field.",
      "http": {
        "method": "PUT",
        "path": "/api/kompositions/{id}"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Komposition UUID",
            "location": "path"
          },
          "content": {
            "type": "string",
            "description": "New markdown content (must start with # Title)",
            "location": "body"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "complete"
            ],
            "location": "body"
          }
        },
        "required": [
          "id"
        ]
      }
    },
    {
      "name": "delete_komposition",
      "description": "Delete a komposition by ID.",
      "http": {
        "method": "DELETE",
        "path": "/api/kompositions/{id}"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Komposition UUID",
            "location": "path"
          }
        },
        "required": [
          "id"
        ]
      }
    },
    {
      "name": "list_sources",
      "description": "List reusable multimedia source assets (audio, video, image) with segment definitions and file references.",
      "http": {
        "method": "GET",
        "path": "/api/kompositions/sources"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "analyze_audio_source",
      "description": "Get structural audio analysis for a specific source: BPM, beat grid, and bar-aligned segment labels (Intro, Verse, Drop, Breakdown, Outro). Segments are expressed as bar ranges on the beat grid; ms timestamps are downstream derived positions for the ffmpeg render layer. Executes via POST /api/execute-tool with tool_name=analyze_audio_source.",
      "http": {
        "method": "POST",
        "path": "/api/execute-tool"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "source_id": {
            "type": "string",
            "description": "The source komposition ID to retrieve audio analysis for",
            "location": "body"
          }
        },
        "required": [
          "source_id"
        ]
      }
    },
    {
      "name": "get_musicdna",
      "description": "Fetch musicDNA analysis data for a library file: BPM, confidence, backends (madmom/librosa/aubio/beat-this), beat timing, duration, and fused semantic segment labels when the fusion pipeline has run. Reuses GET /api/multimedia/{fileId}/analysis — the canonical musicDNA endpoint. Returns 404 with hint if file has not been analyzed (run analyze_file first). The response also includes a presigned audioUrl and the full beat/downbeat arrays.",
      "http": {
        "method": "GET",
        "path": "/api/multimedia/{fileId}/analysis"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "3s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "fileId": {
            "type": "string",
            "description": "Library file ID",
            "location": "path"
          }
        },
        "required": [
          "fileId"
        ]
      }
    },
    {
      "name": "get_beat_grid",
      "description": "Fetch the exact beat grid for an analyzed audio file: per-beat timestamps (beatTimesMs) and downbeat/bar-start timestamps (downbeatsMs) in absolute milliseconds, plus BPM, confidence, median beat interval, first and last beat positions. The general `/analysis` endpoint also exposes beat data under `beats`/`downbeats` arrays with a different response shape; this tool is the dedicated canonical beat-grid contract with explicit fields and response/status for precise segment placement and beatmatching. Returns status not_analyzed | analyzed | no_beat_grid. Reads musicDNA directly from DynamoDB (ref #1572).",
      "http": {
        "method": "GET",
        "path": "/api/multimedia/{fileId}/beat-grid"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "fileId": {
            "type": "string",
            "description": "Library file ID of an analyzed audio source",
            "location": "path"
          }
        },
        "required": [
          "fileId"
        ]
      }
    },
    {
      "name": "calculate_beat_segments",
      "description": "Convert a beat-based segment layout into millisecond timeline positions. Provide beats_per_segment, segment_count, and file_ids; the tool looks up the exact beat grid for each file and returns contiguous timeline_start_ms/timeline_end_ms plus source offsets. Use start_beat to skip the intro (e.g. 128). arrangement: alternate (cycles files) or sequential (exhausts each before next). Call get_beat_grid or get_musicdna first to confirm beat data is available.",
      "http": {
        "method": "POST",
        "path": "/api/multimedia/beat-segments"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "3s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "beats_per_segment": {
            "type": "integer",
            "description": "Number of beats per segment (e.g. 16)",
            "location": "body"
          },
          "segment_count": {
            "type": "integer",
            "description": "Total number of segments to generate",
            "location": "body"
          },
          "file_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Library file IDs to use as audio sources",
            "location": "body"
          },
          "start_beat": {
            "type": "integer",
            "description": "Absolute beat index to start from (default 0)",
            "location": "body"
          },
          "arrangement": {
            "type": "string",
            "enum": [
              "alternate",
              "sequential"
            ],
            "description": "How to assign files to segments (default: alternate)",
            "location": "body"
          }
        },
        "required": [
          "beats_per_segment",
          "segment_count",
          "file_ids"
        ]
      }
    },
    {
      "name": "create_job",
      "description": "Submit a video build or youtube_download job. For video_build: first GET the komposition to obtain its content, then submit with komposition_id + content. Returns immediately with job_id — poll with get_job until SUCCEEDED or FAILED.",
      "http": {
        "method": "POST",
        "path": "/api/jobs"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "5s (submission only — build runs async)",
      "inputSchema": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "video_build",
              "youtube_download"
            ],
            "description": "Job type",
            "location": "body"
          },
          "params": {
            "type": "object",
            "description": "For video_build: {komposition_id: string, content: string, ephemeral?: boolean}. For youtube_download: {url: string, quality?: \"1080p\"|\"720p\"|\"480p\"|\"best\"}",
            "location": "body"
          }
        },
        "required": [
          "type",
          "params"
        ]
      }
    },
    {
      "name": "get_job",
      "description": "Get job status and output files. Poll every 5s until status is SUCCEEDED or FAILED. On SUCCEEDED, output_files[].download_url contains a 7-day presigned S3 URL.",
      "http": {
        "method": "GET",
        "path": "/api/jobs/{jobId}"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "3s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "description": "Job ID (e.g. vb-abc123... or job_xxx_123456)",
            "location": "path"
          }
        },
        "required": [
          "jobId"
        ]
      }
    },
    {
      "name": "list_jobs",
      "description": "List user's jobs, optionally filtered by type. All parameters are query string parameters.",
      "http": {
        "method": "GET",
        "path": "/api/jobs"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "video_build",
              "youtube_download"
            ],
            "location": "query"
          },
          "limit": {
            "type": "number",
            "description": "Max results, default 20",
            "location": "query"
          },
          "cursor": {
            "type": "string",
            "description": "Pagination cursor from previous response",
            "location": "query"
          }
        }
      }
    },
    {
      "name": "list_library",
      "description": "List user's uploaded media files in the library. Files have IDs that can be referenced in komposition source tracks.",
      "http": {
        "method": "GET",
        "path": "/api/files/user"
      },
      "authentication": true,
      "markdown": true,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "list_staging",
      "description": "List AI-generated files in the staging area. These are generated via chat but not yet in the library. Use promote_staging to move them to the library.",
      "http": {
        "method": "GET",
        "path": "/api/multimedia/staging"
      },
      "authentication": true,
      "markdown": true,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "promote_staging",
      "description": "Promote one or more staging files to the permanent media library so they can be referenced in kompositions.",
      "http": {
        "method": "POST",
        "path": "/api/multimedia/promote"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "3s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "fileIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Array of staging file IDs to promote",
            "location": "body"
          }
        },
        "required": [
          "fileIds"
        ]
      }
    },
    {
      "name": "list_outputs",
      "description": "List user's finished rendered video outputs with presigned download URLs.",
      "http": {
        "method": "GET",
        "path": "/api/outputs"
      },
      "authentication": true,
      "markdown": true,
      "timeout_hint": "2s",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "generate_image",
      "description": "Generate an AI image directly using Gemini. Returns base64-encoded image data. Optionally saves to library with save_to_library:true, returning a file_id for use in komposition source tracks.",
      "http": {
        "method": "POST",
        "path": "/api/generate-image"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "10-20s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "prompt": {
            "type": "string",
            "description": "Natural language image description",
            "location": "body"
          },
          "resolution": {
            "type": "string",
            "enum": [
              "1080p",
              "720p",
              "vertical_1080p"
            ],
            "description": "Output resolution. Default: 1080p",
            "location": "body"
          },
          "style": {
            "type": "string",
            "description": "Style guidance appended to prompt",
            "location": "body"
          },
          "save_to_library": {
            "type": "boolean",
            "description": "If true, saves image to media library and returns file_id",
            "location": "body"
          }
        },
        "required": [
          "prompt"
        ]
      }
    },
    {
      "name": "get_build_logs",
      "description": "Get CloudWatch logs, Step Functions execution history, and Batch job status for a specific build job. Admin only.",
      "http": {
        "method": "GET",
        "path": "/api/admin/diagnostics/jobs/{jobId}/logs"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "10s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "description": "Job ID (e.g. vb-abc123...)",
            "location": "path"
          }
        },
        "required": [
          "jobId"
        ]
      }
    },
    {
      "name": "check_infrastructure",
      "description": "Verify all AWS resources (S3 buckets, DynamoDB tables, Batch queues, Step Functions, Cognito) are healthy. Returns per-resource status. Admin only.",
      "http": {
        "method": "GET",
        "path": "/api/admin/diagnostics/infrastructure"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "10s",
      "inputSchema": {
        "type": "object",
        "properties": {}
      }
    },
    {
      "name": "execute_tool",
      "description": "Execute a tool by name. Used by Bedrock direct streaming chat when the model returns tool_use. Supports: get_user_sources, analyze_audio_source, search_library, search_kompositions, get_komposition, create_komposition, update_komposition, create_video, import_legacy_json.",
      "http": {
        "method": "POST",
        "path": "/api/execute-tool"
      },
      "authentication": true,
      "markdown": false,
      "timeout_hint": "5s",
      "inputSchema": {
        "type": "object",
        "properties": {
          "tool_name": {
            "type": "string",
            "description": "Name of the tool to execute",
            "location": "body"
          },
          "tool_input": {
            "type": "object",
            "description": "Input parameters for the tool",
            "location": "body"
          }
        },
        "required": [
          "tool_name"
        ]
      }
    }
  ]
}