{"serverId":"mcp-registry/asia.simpler/simpler-asia","tools":[{"name":"ai.budget","fixed":{"name":"ai_budget","description":"Your prepaid wallet, period spend, caps, and runway as a compact line DSL (~8 lines). Requires auth (your balance is closed data). Read this BEFORE committing paid actions; pair with ai.estimate_cost to price a specific call first.","inputSchema":{"type":"object","required":[],"properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"ai.budget\" → \"ai_budget\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":100,"tokensAfter":100},{"name":"ai.estimate_cost","fixed":{"name":"ai_estimate_cost","description":"Price an AI task BEFORE running it: provider passthrough at wholesale cost, no markup — the per-token platform fee is retired, so text/reasoning passes through at cost. Anonymous — open catalog includes open pricing. Returns integer micro-USD plus a USD total. NEXT: run the action, then ai.budget to watch the runway.","inputSchema":{"type":"object","required":["task"],"properties":{"task":{"type":"string","description":"A token-billed task name from the platform task→model map (e.g. 'assistant.ask')."},"tokens_in":{"type":"integer","description":"Estimated input tokens (default 1000)."},"tokens_out":{"type":"integer","description":"Estimated output tokens (default 500)."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"ai.estimate_cost\" → \"ai_estimate_cost\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":210,"tokensAfter":210},{"name":"banking.import_statement","fixed":{"name":"banking_import_statement","description":"Import one bank account's statement as CSV text (CSV today, PDF later) so the forecast starts from the bank's own balance. Owner or admin only. Stores each line and each day's closing balance for the account named by bank_label, account_last4 and currency; uploading the same statement again adds nothing. Name an account the same way every time (the label is matched ignoring case). Before anything is stored, only the last 4 digits of the account number are kept, and runs of 6 or more digits, emai","inputSchema":{"type":"object","required":["csv_text","bank_label","account_last4","currency"],"properties":{"mapping":{"type":"object","required":[],"properties":{"date":{"type":"integer","description":"The date column number."},"debit":{"type":"integer","description":"The debit column number."},"amount":{"type":"integer","description":"The amount column number."},"credit":{"type":"integer","description":"The credit column number."},"balance":{"type":"integer","description":"The balance column number."},"description":{"type":"integer","description":"The description column number."},"counterparty":{"type":"integer","description":"The counterparty column number."}},"description":"Optional: 1-based column numbers, sent after a refusal lists the file's columns. Needs date and balance, plus amount (money in positive) or both debit and credit.","additionalProperties":false},"csv_text":{"type":"string","description":"The statement exported from online banking as CSV, at most 2,000,000 bytes. A title block above the header row is fine."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"The account's currency."},"bank_label":{"type":"string","description":"The bank's name, such as DBS or Maybank. No account number."},"date_order":{"enum":["day_first","month_first"],"type":"string","description":"Optional: send only when a refusal says every date can be read two ways. day_first reads 03/04/2026 as 3 April."},"new_account":{"type":"boolean","description":"Optional: send true only after a SIMPLER_BANKING_ACCOUNT_LABEL_CONFLICT refusal, when this statement is from a different account that shares the saved account's last 4 digits and currency. Leave it out otherwise."},"account_last4":{"type":"string","description":"The account number's last 4 digits as text, such as \"0123\". A longer number is cut to its last 4."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"banking.import_statement\" → \"banking_import_statement\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":700,"tokensAfter":700},{"name":"document.extract","fixed":{"name":"document_extract","description":"Fetch a document from a URL, classify its type (personal/medical/business/unknown) with keyword + PII-pattern heuristics (documents containing actual PII data are NEVER business), and return extracted text capped at 100K chars. Free. STREAMING SUPPORTED: send Accept: text/event-stream for started/fetched/classifying/completed phases.","inputSchema":{"type":"object","required":["document_url"],"properties":{"document_url":{"type":"string","description":"Public HTTPS URL (fetched server-side, 30s timeout; text/* formats classify, binary answers unknown)."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"document.extract\" → \"document_extract\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":177,"tokensAfter":177},{"name":"forecast.read","fixed":{"name":"forecast_read","description":"Read the tenant's STORED forecast — the verify-read for this rail. view picks the answer, one of summary, monthly, variance, scenarios, weekly (default summary). summary: the outcome bands (label, growth_direction, probability_pct, final_month_revenue_low and _high, distress_risk_pct, path_count), the cash block, blend {yours_pct, peers_pct} (the share of the result from this business's own history versus businesses like it; null when shallow). monthly: per month revenue, costs and net at p10, p","inputSchema":{"type":"object","required":[],"properties":{"view":{"enum":["summary","monthly","variance","scenarios","weekly"],"type":"string","description":"One of summary, monthly, variance, scenarios, weekly; leave it out for summary."},"cash_on":{"type":"string","description":"View weekly only: a date YYYY-MM-DD inside the 13 weeks; the answer adds that day's end-of-day balance."},"drivers":{"type":"array","items":{"type":"object","required":["kind","start"],"properties":{"end":{"type":"string","description":"Optional last month or day; leave it out to run to the end of the forecast."},"pct":{"type":"number","description":"Percent change, -100 to 500: 10 for +10%."},"set":{"type":"integer","description":"Which compared set, 1 to 3; default 1."},"kind":{"enum":["revenue_pct","cost_pct","revenue_step","cost_step","hire","one_off_in","one_off_out","lose_customer","loan_drawdown","loan_repayment","festive_month"],"type":"string"},"start":{"type":"string","description":"YYYY-MM (the whole month) or YYYY-MM-DD (that day)."},"amount":{"type":"number","description":"Major units of the forecast currency: 4000 means 4,000 - never cents."},"customer_ref":{"type":"string","description":"lose_customer: an invoice client id from invoice_client_list, in place of amount."}},"additionalProperties":false},"description":"Optional, views scenarios and weekly only: at most 20 drivers in each of up to 3 sets, one flat list; weekly takes one set (no set field)."},"recurring":{"type":"array","items":{"type":"object","required":["direction","amount","cadence","next_date"],"properties":{"amount":{"type":"number","description":"Each occurrence's amount in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"cadence":{"enum":["weekly","monthly","quarterly"],"type":"string","description":"How often it repeats; monthly and quarterly keep next_date's day of the month."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"Optional: the row's currency; leave it out for the books' currency (weekly.currency)."},"direction":{"enum":["in","out"],"type":"string","description":"in (money you receive) or out (money you pay)."},"next_date":{"type":"string","description":"The next date it falls due, from this week's Monday on, YYYY-MM-DD."}},"additionalProperties":false},"description":"Optional, view weekly only, joined with the books' own for this answer: repeating money, at most 100 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). direction out leaves on each date; direction in falls due on each date and is paid on the receipt-timing curve, like a receivable."},"paid_history":{"type":"array","items":{"type":"object","required":["due_date","paid_at"],"properties":{"amount":{"type":"number","description":"Optional: the amount paid in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"paid_at":{"type":"string","description":"The day it was paid, not after today, YYYY-MM-DD."},"due_date":{"type":"string","description":"The due date, YYYY-MM-DD."},"client_ref":{"type":"string","description":"Optional: your own id for the customer, at most 60 characters. It only groups and labels rows; nothing is looked up."}},"additionalProperties":false},"description":"Optional, view weekly only, joined with the books' own for this answer: invoices already paid, at most 5,000 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). The receipt-timing curve learns from the days between due_date and paid_at; with no paid history at all the labelled prior times every receivable (source 'prior')."},"open_payables":{"type":"array","items":{"type":"object","required":["due_date","amount"],"properties":{"amount":{"type":"number","description":"The amount owed in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"Optional: the row's currency; leave it out for the books' currency (weekly.currency)."},"due_date":{"type":"string","description":"The due date, YYYY-MM-DD."},"vendor_ref":{"type":"string","description":"Optional: your own id for the supplier, at most 60 characters. It only groups and labels rows; nothing is looked up."}},"additionalProperties":false},"description":"Optional, view weekly only, joined with the books' own for this answer: bills you owe, at most 2,000 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). Each is paid on its due date (one already due is paid on the first modelled day)."},"open_receivables":{"type":"array","items":{"type":"object","required":["due_date","amount"],"properties":{"amount":{"type":"number","description":"The amount still unpaid in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"Optional: the row's currency; leave it out for the books' currency (weekly.currency)."},"due_date":{"type":"string","description":"The due date, YYYY-MM-DD."},"issued_at":{"type":"string","description":"Optional: the invoice date, on or before due_date and not after today, YYYY-MM-DD."},"client_ref":{"type":"string","description":"Optional: your own id for the customer, at most 60 characters. It only groups and labels rows; nothing is looked up."}},"additionalProperties":false},"description":"Optional, view weekly only, joined with the books' own for this answer: invoices your own accounting system shows unpaid, at most 2,000 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). Each is paid on the receipt-timing curve from its due date."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"forecast.read\" → \"forecast_read\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":1776,"tokensAfter":1776},{"name":"forecast.run","fixed":{"name":"forecast_run","description":"WHAT-IF / scenario lane: runs monthly data YOU supply through a seeded 5,000-path simulation of monthly revenue and costs; bands are simulation outputs, not calibrated probabilities. The paths are clustered into 3-7 outcome bands for the next 6 months, with per-month revenue and cost percentiles and deathRisk, the share of paths the engine marks closed (a distress signal, not a cash-out date). Amounts are in currency (major units, default USD): 1500 or \"1500.00\" - never cents. currency is one of","inputSchema":{"type":"object","required":["monthlyData"],"properties":{"cash_on":{"type":"string","description":"Optional, with any of open_receivables, paid_history, open_payables or recurring: a date YYYY-MM-DD inside the 13 weeks; needs cash_balance and cash_as_of within 31 days. The answer adds that day's end-of-day balance."},"drivers":{"type":"array","items":{"type":"object","required":["kind","start"],"properties":{"end":{"type":"string","description":"Optional last month or day; leave it out to run to the end of the forecast."},"pct":{"type":"number","description":"Percent change, -100 to 500: 10 for +10%."},"kind":{"enum":["revenue_pct","cost_pct","revenue_step","cost_step","hire","one_off_in","one_off_out","lose_customer","loan_drawdown","loan_repayment","festive_month"],"type":"string"},"start":{"type":"string","description":"YYYY-MM (the whole month) or YYYY-MM-DD (that day)."},"amount":{"type":"number","description":"Major units of the forecast currency: 4000 means 4,000 - never cents."}},"additionalProperties":false},"description":"Optional: at most 20 what-if drivers, applied to the same simulated paths as the base."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"The currency of every amount sent, one of 16 codes. Leave it out for USD; the output then says currency_assumed: true."},"recurring":{"type":"array","items":{"type":"object","required":["direction","amount","cadence","next_date"],"properties":{"amount":{"type":"number","description":"Each occurrence's amount in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"cadence":{"enum":["weekly","monthly","quarterly"],"type":"string","description":"How often it repeats; monthly and quarterly keep next_date's day of the month."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"Optional: the row's currency; leave it out for currency (USD when currency is left out)."},"direction":{"enum":["in","out"],"type":"string","description":"in (money you receive) or out (money you pay)."},"next_date":{"type":"string","description":"The next date it falls due, from this week's Monday on, YYYY-MM-DD."}},"additionalProperties":false},"description":"Optional: repeating money, at most 100 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). direction out leaves on each date; direction in falls due on each date and is paid on the receipt-timing curve, like a receivable."},"cash_as_of":{"type":"string","description":"Optional: the date of cash_balance as YYYY-MM-DD, within the last 45 days. Send it with cash_balance."},"monthlyData":{"type":"array","items":{"type":"object","required":["date","revenue","costs"],"properties":{"date":{"type":"string"},"costs":{"type":"number"},"revenue":{"type":"number"},"customers":{"type":"number"}},"additionalProperties":false},"description":"2-120 monthly rows, oldest first: { date: 'YYYY-MM', revenue, costs, customers? }. revenue and costs are amounts in currency (major units, default USD): 1500 or \"1500.00\" - never cents."},"cash_balance":{"type":"number","description":"Optional: the bank balance on cash_as_of in currency (major units, negative for an overdraft). Send it with cash_as_of."},"paid_history":{"type":"array","items":{"type":"object","required":["due_date","paid_at"],"properties":{"amount":{"type":"number","description":"Optional: the amount paid in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"paid_at":{"type":"string","description":"The day it was paid, not after today, YYYY-MM-DD."},"due_date":{"type":"string","description":"The due date, YYYY-MM-DD."},"client_ref":{"type":"string","description":"Optional: your own id for the customer, at most 60 characters. It only groups and labels rows; nothing is looked up."}},"additionalProperties":false},"description":"Optional: invoices already paid, at most 5,000 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). The receipt-timing curve learns from the days between due_date and paid_at; with no paid history at all the labelled prior times every receivable (source 'prior')."},"cash_currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"Optional: the currency of cash_balance when it is not currency. Send it with cash_balance and cash_as_of."},"open_payables":{"type":"array","items":{"type":"object","required":["due_date","amount"],"properties":{"amount":{"type":"number","description":"The amount owed in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"Optional: the row's currency; leave it out for currency (USD when currency is left out)."},"due_date":{"type":"string","description":"The due date, YYYY-MM-DD."},"vendor_ref":{"type":"string","description":"Optional: your own id for the supplier, at most 60 characters. It only groups and labels rows; nothing is looked up."}},"additionalProperties":false},"description":"Optional: bills you owe, at most 2,000 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). Each is paid on its due date (one already due is paid on the first modelled day)."},"declared_totals":{"type":"object","required":[],"properties":{"costs":{"type":"number"},"revenue":{"type":"number"}},"description":"Optional: the revenue and costs totals your own source shows for the months sent, in currency. A total more than 0.5% away from the months' sum refuses with SIMPLER_FORECAST_GRID_TOTALS_MISMATCH.","additionalProperties":false},"open_receivables":{"type":"array","items":{"type":"object","required":["due_date","amount"],"properties":{"amount":{"type":"number","description":"The amount still unpaid in the row's currency (major units): 1500 or \"1500.00\" - never cents."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"Optional: the row's currency; leave it out for currency (USD when currency is left out)."},"due_date":{"type":"string","description":"The due date, YYYY-MM-DD."},"issued_at":{"type":"string","description":"Optional: the invoice date, on or before due_date and not after today, YYYY-MM-DD."},"client_ref":{"type":"string","description":"Optional: your own id for the customer, at most 60 characters. It only groups and labels rows; nothing is looked up."}},"additionalProperties":false},"description":"Optional: invoices your own accounting system shows unpaid, at most 2,000 rows (more refuses with SIMPLER_FORECAST_AGENT_INPUT_TOO_LARGE). Each is paid on the receipt-timing curve from its due date."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"forecast.run\" → \"forecast_run\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":2083,"tokensAfter":2083},{"name":"forecast.run_stored","fixed":{"name":"forecast_run_stored","description":"Run the tenant's REAL forecast from the books — cash receipts (recorded invoice payments by payment date, plus paid invoices with no recorded payment at their paid date, flagged) and expenses by expense date, monthly in the tenant's base currency, up to the last complete month (the current month is left out; months are the business's calendar months in its own time zone, set in sales settings, Asia/Singapore when unset, and the books block names it in bucket_timezone) and at most 36 months — thr","inputSchema":{"type":"object","required":[],"properties":{"cash_as_of":{"type":"string","description":"The date of cash_balance as YYYY-MM-DD."},"cash_balance":{"type":"number","description":"Optional: the bank balance on cash_as_of in cash_currency (major units, negative for an overdraft). Send cash_balance, cash_currency and cash_as_of together; owner or admin only. Refused with SIMPLER_FORECAST_CASH_MULTI_ACCOUNT_REFUSED when more than one bank account is saved: update those on the Forecast page."},"cash_currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"The currency of cash_balance."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"forecast.run_stored\" → \"forecast_run_stored\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":354,"tokensAfter":354},{"name":"image.add_shadow","fixed":{"name":"image_add_shadow","description":"Add a product shadow to a cut-out (subject on transparency), cast from its alpha: kind drop, contact or reflection. angle 0..359 is the light direction counterclockwise from 3 o'clock (default 120; the shadow falls the other way); distance 0..200 px (default 12; the gap under the base for a reflection); blur 0..100 px (default 16); opacity 0..100 (default 50); transparent true (default) keeps the backdrop see-through, false fills background #RRGGBB (default #ffffff) behind the shadow. contact ig","inputSchema":{"type":"object","required":[],"properties":{"blur":{"type":"integer","maximum":100,"minimum":0,"description":"Edge softness in pixels, 0..100 (default 16)."},"kind":{"enum":["drop","contact","reflection"],"type":"string","description":"Shadow kind (default drop)."},"angle":{"type":"integer","maximum":359,"minimum":0,"description":"Light direction in degrees, counterclockwise from 3 o'clock (default 120)."},"opacity":{"type":"integer","maximum":100,"minimum":0,"description":"Shadow strength, 0..100 (default 50)."},"distance":{"type":"integer","maximum":200,"minimum":0,"description":"Offset in pixels, 0..200 (default 12)."},"image_url":{"type":"string","description":"Cut-out subject on transparency: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"background":{"type":"string","pattern":"^(#[0-9A-Fa-f]{6}|transparent)$","description":"#RRGGBB backdrop used when transparent is false (default #ffffff). The word transparent is also accepted and means transparent true; it cannot be combined with transparent false."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"transparent":{"type":"boolean","description":"true (default) keeps the backdrop see-through; false fills background behind the shadow."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Cut-out subject on transparency, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.add_shadow\" → \"image_add_shadow\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":824,"tokensAfter":824},{"name":"image.adjust","fixed":{"name":"image_adjust","description":"Apply 1..10 adjustments to a photo in order, with the editor's own kernels: a one-op stack returns the same pixels as that tool in the editor. ops is a list of {op, params}; each op takes only its own params keys, and a key left out takes that op's default: brightness_contrast (brightness, contrast), exposure (stops), temperature_tint (temperature, tint), vibrance_saturation (vibrance, saturation), curves (rgb, red, green, blue), levels (in_black, in_white, levels_gamma, out_black, out_white), h","inputSchema":{"type":"object","required":["ops"],"properties":{"ops":{"type":"array","items":{"type":"object","required":["op"],"properties":{"op":{"enum":["brightness_contrast","exposure","temperature_tint","vibrance_saturation","curves","levels","hsl_per_range","sharpen","blur","denoise","vignette","grain","black_and_white","auto_tone"],"type":"string","description":"The adjustment to apply."},"params":{"type":"object","properties":{"hue":{"type":"number","maximum":180,"minimum":-180,"description":"hsl_per_range -180..180, default 0, degrees"},"red":{"type":"array","items":{"type":"array","items":{"type":"number","maximum":1,"minimum":0},"maxItems":2,"minItems":2},"maxItems":8,"minItems":2,"description":"curves red curve, 2..8 [x,y] points in 0..1"},"rgb":{"type":"array","items":{"type":"array","items":{"type":"number","maximum":1,"minimum":0},"maxItems":2,"minItems":2},"maxItems":8,"minItems":2,"description":"curves master curve, 2..8 [x,y] points in 0..1"},"blue":{"type":"array","items":{"type":"array","items":{"type":"number","maximum":1,"minimum":0},"maxItems":2,"minItems":2},"maxItems":8,"minItems":2,"description":"curves blue curve, 2..8 [x,y] points in 0..1"},"tint":{"type":"number","maximum":100,"minimum":-100,"description":"temperature_tint -100..100, default 0, positive is greener"},"green":{"type":"array","items":{"type":"array","items":{"type":"number","maximum":1,"minimum":0},"maxItems":2,"minItems":2},"maxItems":8,"minItems":2,"description":"curves green curve, 2..8 [x,y] points in 0..1"},"range":{"enum":["reds","oranges","yellows","greens","aquas","blues","purples","magentas"],"type":"string","description":"hsl_per_range required, the colour band"},"stops":{"type":"number","maximum":5,"minimum":-5,"description":"exposure -5..5, default 0, a gain in linear light; +1 doubles the light and whites clip"},"amount":{"type":"number","maximum":4,"minimum":-1,"description":"sharpen 0..4, default 1; vignette -1..1, default -0.3, negative darkens the edges, positive lightens them; grain 0..1, default 0.1"},"radius":{"type":"number","maximum":60,"minimum":1,"description":"sharpen 1..20, default 2, pixels; blur 1..60, default 5, pixels; denoise 1..8, default 4, pixels"},"feather":{"type":"number","maximum":1,"minimum":0,"description":"vignette 0..1, default 0.5, 0 is a hard edge"},"contrast":{"type":"number","maximum":1,"minimum":-1,"description":"brightness_contrast -1..1, default 0, around mid-grey"},"in_black":{"type":"number","maximum":1,"minimum":0,"description":"levels 0..1, default 0, input black point"},"in_white":{"type":"number","maximum":1,"minimum":0,"description":"levels 0..1, default 1, input white point, above in_black"},"midpoint":{"type":"number","maximum":1,"minimum":0,"description":"vignette 0..1, default 0.5, where the falloff centres, from the middle (0) to the corners (1)"},"vibrance":{"type":"number","maximum":1,"minimum":-1,"description":"vibrance_saturation -1..1, default 0, moves muted colours most and protects skin"},"luminance":{"type":"number","maximum":1,"minimum":-1,"description":"hsl_per_range -1..1, default 0"},"out_black":{"type":"number","maximum":1,"minimum":0,"description":"levels 0..1, default 0, output black"},"out_white":{"type":"number","maximum":1,"minimum":0,"description":"levels 0..1, default 1, output white"},"aggressive":{"type":"boolean","description":"auto_tone default false, scales the detected scene's preset 1.5x"},"brightness":{"type":"number","maximum":1,"minimum":-1,"description":"brightness_contrast -1..1, default 0, added to every channel"},"red_weight":{"type":"number","maximum":2,"minimum":-2,"description":"black_and_white -2..2, default 0.3"},"saturation":{"type":"number","maximum":1,"minimum":-1,"description":"vibrance_saturation -1..1, default 0; hsl_per_range -1..1, default 0"},"tint_color":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","description":"black_and_white #RRGGBB tint; omit for neutral grey"},"blue_weight":{"type":"number","maximum":2,"minimum":-2,"description":"black_and_white -2..2, default 0.11"},"temperature":{"type":"number","maximum":100,"minimum":-100,"description":"temperature_tint -100..100, default 0, positive is warmer"},"green_weight":{"type":"number","maximum":2,"minimum":-2,"description":"black_and_white -2..2, default 0.59"},"levels_gamma":{"type":"number","maximum":3,"minimum":0.1,"description":"levels 0.1..3, default 1, midtone gamma"},"shadow_protect":{"type":"number","maximum":1,"minimum":0,"description":"sharpen 0..1, default 0.2"},"highlight_protect":{"type":"number","maximum":1,"minimum":0,"description":"sharpen 0..1, default 0.2"},"luminance_strength":{"type":"number","maximum":50,"minimum":0,"description":"denoise 0..50, default 10"},"chrominance_strength":{"type":"number","maximum":80,"minimum":0,"description":"denoise 0..80, default 25"}},"description":"This op's settings; omit a key for its default. A key another op takes is refused.","additionalProperties":false}},"additionalProperties":false},"maxItems":10,"minItems":1,"description":"Adjustments applied in order, 1..10. Each op takes only its own params keys: brightness_contrast (brightness, contrast), exposure (stops), temperature_tint (temperature, tint), vibrance_saturation (vibrance, saturation), curves (rgb, red, green, blue), levels (in_black, in_white, levels_gamma, out_black, out_white), hsl_per_range (range, hue, saturation, luminance), sharpen (amount, radius, shadow_protect, highlight_protect), blur (radius), denoise (luminance_strength, chrominance_strength, radius), vignette (amount, midpoint, feather), grain (amount), black_and_white (red_weight, green_weight, blue_weight, tint_color), auto_tone (aggressive)."},"image_url":{"type":"string","description":"Photo to adjust: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"output_format":{"enum":["png","jpeg","webp"],"type":"string","description":"Result encoding: png (the default, free), jpeg or webp ($0.0005, one Images encode at quality 85; a JPEG has no transparency, so transparent pixels come out white)."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Photo to adjust, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.adjust\" → \"image_adjust\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":2169,"tokensAfter":2169},{"name":"image.convert","fixed":{"name":"image_convert","description":"[paid: $0.0005] Convert an image to PNG, JPEG or WebP, and optionally bound its size, with the Cloudflare Images service: a PNG, JPEG, WebP, GIF or HEIC goes in and the result is stored in this workspace. format is png, jpeg, webp (AVIF is not offered). quality (1-100, default 85) applies to jpeg and webp. width and height (16-4096 px) bound the result and fit picks how: scale-down (the default) shrinks to fit and never enlarges, contain fits inside and may enlarge, cover and crop fill the box a","inputSchema":{"type":"object","required":["format"],"properties":{"fit":{"enum":["scale-down","contain","cover","crop","pad"],"type":"string","description":"How the image fits width and height (default scale-down). Needs width or height."},"width":{"type":"integer","maximum":4096,"minimum":16,"description":"Bounding width in pixels; leave width and height out to keep the size."},"format":{"enum":["png","jpeg","webp"],"type":"string","description":"The result's file type: png (lossless, keeps transparency), jpeg (smallest for photos, no transparency) or webp."},"height":{"type":"integer","maximum":4096,"minimum":16,"description":"Bounding height in pixels; leave width and height out to keep the size."},"quality":{"type":"integer","maximum":100,"minimum":1,"description":"jpeg and webp only: 1-100, default 85. Refused with png."},"image_url":{"type":"string","description":"The image to convert: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP, GIF or HEIC; SVG is refused). Pass this or image_asset_key, not both."},"background":{"type":"string","pattern":"^#[0-9A-Fa-f]{6}$","description":"#RRGGBB under transparent pixels and around a padded image. A jpeg defaults to #FFFFFF."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"The image to convert, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.convert\" → \"image_convert\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":803,"tokensAfter":803},{"name":"image.cut_background","fixed":{"name":"image_cut_background","description":"[paid: $0.02] Remove the background from a photo, returning the subject on transparency (BiRefNet). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, d","inputSchema":{"type":"object","required":[],"properties":{"image_url":{"type":"string","description":"Source photo: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Source photo, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.cut_background\" → \"image_cut_background\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":538,"tokensAfter":538},{"name":"image.describe","fixed":{"name":"image_describe","description":"Analyze a photo with vision (subjects, defects, OCR text, composition) and return a description. Token-billed at the platform's vision rates. Returns JSON {text, price, next}. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_MEDIA_PIN_TOO_LARGE (under 20MB), SIMPLER_MEDIA_UNKNOWN_TYPE (send PNG, JPEG or WebP), INSUFFICIENT_CREDITS (add credit","inputSchema":{"type":"object","required":[],"properties":{"question":{"type":"string","description":"Optional focused question."},"image_url":{"type":"string","description":"Photo to analyze: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"image_asset_key":{"type":"string","description":"Photo to analyze, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.describe\" → \"image_describe\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":471,"tokensAfter":471},{"name":"image.generate","fixed":{"name":"image_generate","description":"[paid: $0.042/image max] Generate 1-3 images from a text prompt on the first lane that delivers, tried in order: gemini-2.5-flash-image via the Cloudflare AI Gateway, square requests only ($0.0406/image), then @cf/black-forest-labs/flux-2-dev via Cloudflare Workers AI ($0.00/image), then google/nano-banana-2-lite via fal ($0.042/image). Each lane settles its own price per delivered image, so an image never costs more than the fal price, the credit a call must have available before it runs; price","inputSchema":{"type":"object","required":["prompt"],"properties":{"count":{"type":"number","description":"Number of images (1-3, default 1)."},"width":{"type":"number","description":"Desired width px (256-2048; the model outputs 1K-class at the nearest aspect ratio)."},"height":{"type":"number","description":"Desired height px (256-2048; the model outputs 1K-class at the nearest aspect ratio)."},"prompt":{"type":"string","description":"Image description (max 1500 chars)."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.generate\" → \"image_generate\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":512,"tokensAfter":512},{"name":"image.generate_for_edit","fixed":{"name":"image_generate_for_edit","description":"[paid: $0.04] Generate an image from a text prompt (google/nano-banana-2-lite) sized for editing. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: VALIDATION_ERROR on an empty prompt (describe the image), SIMPLER_IMAGE_PROMPT_REFUSED names the label (sexual, minor, real_person or other_unsafe): rewrite the p","inputSchema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","description":"Full image description."},"image_size":{"enum":["square_hd","landscape_4_3","landscape_16_9","portrait_4_3","portrait_16_9"],"type":"string"},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.generate_for_edit\" → \"image_generate_for_edit\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":451,"tokensAfter":451},{"name":"image.harmonize","fixed":{"name":"image_harmonize","description":"Match a pasted subject's light and colour to its scene: a Reinhard colour transfer in CIELAB moves the subject's mean and spread toward the scene's, weighted by the subject mask, at strength 0..100 (default 60). Pixels outside the mask never change. Pass the composite plus a subject mask (white = subject, black = scene). Free: runs on the platform's own deterministic kernel, so the same input and arguments always return the same pixels. Takes PNG, JPEG, WebP, GIF or HEIC. Returns JSON {images:[{","inputSchema":{"type":"object","required":[],"properties":{"strength":{"type":"integer","maximum":100,"minimum":0,"description":"How far to move the subject toward the scene, 0..100 (default 60)."},"image_url":{"type":"string","description":"Composite image: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Composite image, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."},"subject_mask_url":{"type":"string","description":"Subject mask (white = subject): a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"subject_mask_asset_key":{"type":"string","description":"Subject mask (white = subject), as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.harmonize\" → \"image_harmonize\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":732,"tokensAfter":732},{"name":"image.inpaint","fixed":{"name":"image_inpaint","description":"[paid: $0.07] Remove or replace a masked region of a photo (FLUX Fill). White mask pixels = edit region. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use htt","inputSchema":{"type":"object","required":[],"properties":{"prompt":{"type":"string","description":"What to paint in; empty removes."},"mask_url":{"type":"string","description":"Mask (white = edit region): a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"image_url":{"type":"string","description":"Source photo: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"mask_asset_key":{"type":"string","description":"Mask (white = edit region), as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Source photo, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.inpaint\" → \"image_inpaint\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":706,"tokensAfter":706},{"name":"image.marketplace_check","fixed":{"name":"image_marketplace_check","description":"Check a product photo against a marketplace's listing-image rules before the seller uploads it: shopee, lazada or tiktok_shop. Free and read-only: the platform's own deterministic check, which stores nothing, calls no marketplace and uploads nothing. Returns JSON {images: [], price ($0.00), output: {verdict (pass, fail or partial), failed, advice, skipped, file: {width, height, format, bytes}, rules: [{rule, level (required or recommended), status (pass, fail or skipped), expected, measured, fix","inputSchema":{"type":"object","required":["platform"],"properties":{"platform":{"enum":["shopee","lazada","tiktok_shop"],"type":"string","description":"The marketplace whose listing-image rules to check: shopee, lazada or tiktok_shop."},"image_url":{"type":"string","description":"Product photo to check: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"image_asset_key":{"type":"string","description":"Product photo to check, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"image.marketplace_check\" → \"image_marketplace_check\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":375,"tokensAfter":375},{"name":"image.replace_background","fixed":{"name":"image_replace_background","description":"[paid: $0.07] Replace a photo's background with a generated scene (FLUX Fill). Pass a mask covering the background (white = background). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SI","inputSchema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","description":"Scene description."},"mask_url":{"type":"string","description":"Background mask (white = background): a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"image_url":{"type":"string","description":"Source photo: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"mask_asset_key":{"type":"string","description":"Background mask (white = background), as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Source photo, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.replace_background\" → \"image_replace_background\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":713,"tokensAfter":713},{"name":"image.replace_sky","fixed":{"name":"image_replace_sky","description":"[paid: $0.07] Replace a photo's sky (FLUX Fill). Pass a mask covering the sky (white = sky). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:ima","inputSchema":{"type":"object","required":[],"properties":{"prompt":{"type":"string","description":"Sky description."},"mask_url":{"type":"string","description":"Sky mask (white = sky): a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"image_url":{"type":"string","description":"Source photo: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"mask_asset_key":{"type":"string","description":"Sky mask (white = sky), as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Source photo, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.replace_sky\" → \"image_replace_sky\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":700,"tokensAfter":700},{"name":"image.segment_at","fixed":{"name":"image_segment_at","description":"[paid: $0.01] Segment the object at pixel (x, y) via SAM 2 and return its mask image. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or a","inputSchema":{"type":"object","required":["x","y"],"properties":{"x":{"type":"number","maximum":16384,"minimum":0,"description":"Pixel X of a point on the object (0..16384)."},"y":{"type":"number","maximum":16384,"minimum":0,"description":"Pixel Y of a point on the object (0..16384)."},"image_url":{"type":"string","description":"Source photo: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Source photo, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.segment_at\" → \"image_segment_at\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":601,"tokensAfter":601},{"name":"image.style_transfer","fixed":{"name":"image_style_transfer","description":"[paid: $0.04] Apply a style to a photo while preserving the content (FLUX img2img, strength 0.55). Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, da","inputSchema":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","description":"Style descriptor."},"image_url":{"type":"string","description":"Source photo: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Source photo, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.style_transfer\" → \"image_style_transfer\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":557,"tokensAfter":557},{"name":"image.upscale","fixed":{"name":"image_upscale","description":"[paid: $0.06] Upscale a photo 4x via aura-sr super-resolution. Returns JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; images are stored in this workspace, never provider links, and a result with no download_url says why in download_url_unavailable. Refuses: SIMPLER_MEDIA_INPUT_MISSING (pass the url or the asset_key), SIMPLER_MEDIA_FOREIGN_KEY (use this workspace's asset_key), SIMPLER_MEDIA_URL_REFUSED (use https, data:image or an asset_key), SIMPLER_M","inputSchema":{"type":"object","required":[],"properties":{"image_url":{"type":"string","description":"Source photo: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"Source photo, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.upscale\" → \"image_upscale\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":536,"tokensAfter":536},{"name":"image.variation","fixed":{"name":"image_variation","description":"[paid: $0.04/image] Generate 1-3 variations of a reference image guided by a prompt (FLUX Pro Kontext, context-aware). Pass image_asset_key (an earlier result) or image_url (public https or data:image) plus a prompt describing the change. Each image is stored in this workspace's image library and returned as JSON {images:[{url, asset_key, download_url?, width, height}], price, next} plus a preview; never a provider link. A result with no download_url says why in download_url_unavailable. Refuses","inputSchema":{"type":"object","required":["prompt"],"properties":{"count":{"type":"number","description":"Number of variations (1-3, default 1)."},"prompt":{"type":"string","description":"How to vary the image (max 1500 chars)."},"image_url":{"type":"string","description":"The reference image: a public https URL, a data:image/...;base64 URL, or the url/url_path of an earlier image result (max 20MB; PNG, JPEG, WebP or GIF; SVG is refused). Pass this or the matching *_asset_key, not both."},"request_ref":{"type":"string","description":"Optional idempotency key (1-64 chars: letters, digits, . _ : -). Retrying with the same request_ref and the same arguments within 24 hours returns the first result at $0 with replayed: true; the same request_ref with different arguments is refused (SIMPLER_IMAGE_REQUEST_REF_CONFLICT), and so is a retry whose first result was deleted since (SIMPLER_IMAGE_REQUEST_REF_RESULT_DELETED): send a new request_ref to run it again."},"preview_format":{"enum":["png","jpeg","none"],"type":"string","description":"Preview block after the JSON, at most 384px on the long edge and never charged: png, jpeg or none. Left out, a PNG result gets a png preview and a JPEG or WebP result a jpeg one. A result with no preview says why in preview_unavailable."},"image_asset_key":{"type":"string","description":"The reference image, as the asset_key of an earlier image result for this workspace (edit-projects/<tenant>/... or images/<tenant>/...). A key from another workspace is refused (SIMPLER_MEDIA_FOREIGN_KEY)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"image.variation\" → \"image_variation\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":589,"tokensAfter":589},{"name":"platform.request_capability","fixed":{"name":"platform_request_capability","description":"File a missing-capability report when no available tool can accomplish your task. The platform operator reviews these to decide what gets built next. Free; capped at 10 reports per tenant per day. Refuses: VALIDATION_ERROR when intent is under 10 characters (describe the task in a sentence), RATE_LIMITED after 10 reports in 24 hours (wait the retry_after seconds it carries).","inputSchema":{"type":"object","required":["intent"],"properties":{"intent":{"type":"string","description":"What you were trying to accomplish (plain language)."},"proposed_shape":{"type":"string","description":"Optional: the tool shape that would have worked."},"attempted_tools":{"type":"string","description":"Tools you tried, comma-separated."},"why_insufficient":{"type":"string","description":"Why the existing tools fell short."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"platform.request_capability\" → \"platform_request_capability\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":250,"tokensAfter":250},{"name":"simpler.approval_status","fixed":{"name":"simpler_approval_status","description":"Check a parked approval by approval_id (returned in an APPROVAL_REQUIRED refusal). States: pending (the account owner has not decided — tell the user, do NOT retry the original call), executed (done — the result rides this response; never re-run the call; an approved invoice_share or quote_share keeps no link or document here, so read the current link with that tool and action 'get' as result.link_not_stored.next_call says), rejected / expired / failed (terminal — re-compose only if the user ask","inputSchema":{"type":"object","required":["approval_id"],"properties":{"approval_id":{"type":"string","description":"The approval id from the APPROVAL_REQUIRED refusal envelope."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"simpler.approval_status\" → \"simpler_approval_status\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":212,"tokensAfter":212},{"name":"simpler.describe_service","fixed":{"name":"simpler_describe_service","description":"DISCOVERY TIER 2 of 3 — one service's operations and entity shapes from the live catalog, token-dense (op lines name the underlying tool). No auth required. NEXT: simpler.invoke {service, operation, args} — or call the named tool directly.","inputSchema":{"type":"object","required":["service"],"properties":{"service":{"type":"string","description":"A service name from simpler.directory."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"simpler.describe_service\" → \"simpler_describe_service\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":132,"tokensAfter":132},{"name":"simpler.directory","fixed":{"name":"simpler_directory","description":"DISCOVERY TIER 1 of 3 — the service directory: one token-dense line per service (s:name st:status cost:class ops:N — purpose). START HERE instead of reading every tool. No auth required. NEXT: simpler.describe_service with a service name from the listing.","inputSchema":{"type":"object","required":[],"properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"simpler.directory\" → \"simpler_directory\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":109,"tokensAfter":109},{"name":"simpler.invoke","fixed":{"name":"simpler_invoke","description":"DISCOVERY TIER 3 of 3 — execute one operation: dispatches to the underlying generated tool (same gates, same envelope, same idempotency as calling it directly). Requires auth. args = the underlying tool's arguments object.","inputSchema":{"type":"object","required":["service","operation"],"properties":{"args":{"type":"object","description":"Arguments for the resolved tool."},"service":{"type":"string","description":"A service name from simpler.directory."},"operation":{"type":"string","description":"An op name from simpler.describe_service (e.g. 'create'), or the full tool name (e.g. 'sticky_note_create')."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"simpler.invoke\" → \"simpler_invoke\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":191,"tokensAfter":191},{"name":"simpler.outcomes","fixed":{"name":"simpler_outcomes","description":"DISCOVERY TIER 0 — the OUTCOME MENU: one line per common business outcome, each mapped to ONE tool with one-call notes (composites: ONE call creates the whole document). START HERE for a business task. No auth required. NEXT: call the named tool directly, or simpler.directory for the full catalog.","inputSchema":{"type":"object","required":[],"properties":{},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"simpler.outcomes\" → \"simpler_outcomes\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":121,"tokensAfter":121},{"name":"spreadsheet.create_with_content","fixed":{"name":"spreadsheet_create_with_content","description":"Create a spreadsheet in the user's library from CSV content in ONE call: the platform stores the bytes (honest text/csv) and registers the file row. Free — storage costs nothing; only AI lanes bill. Returns the file row including its id. Refuses: VALIDATION_ERROR when title or csv_content is empty (send both), VALIDATION_ERROR when csv_content is over 10 MB (split it into smaller files), NOT_FOUND when folder_id names no folder in this workspace (call spreadsheet_folder_list, or leave folder_id ","inputSchema":{"type":"object","required":["title","csv_content"],"properties":{"title":{"type":"string","description":"Library title for the file (≤255 chars)."},"folder_id":{"type":"string","description":"Optional spreadsheet_folder id to file it under."},"csv_content":{"type":"string","description":"The complete CSV content (first row = headers). At most 10 MB."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"spreadsheet.create_with_content\" → \"spreadsheet_create_with_content\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":267,"tokensAfter":267},{"name":"studio.add_audio_to_timeline","fixed":{"name":"studio_add_audio_to_timeline","description":"Place an audio asset on a role track: 'audio-music' (background, default volume 0.3 — voice ducks music by role) or 'audio-voice' (narration, volume 1.0). It lands on the role's first track, or on the one track_id names (a second music bed, a second voice).","inputSchema":{"type":"object","required":["project_id","asset_id"],"properties":{"track":{"enum":["audio-music","audio-voice"],"type":"string","default":"audio-music"},"volume":{"type":"number","description":"0-2; defaults 0.3 for music, 1.0 for voice."},"asset_id":{"type":"string"},"track_id":{"type":"string","description":"An audio track (vtr_) from get_composition; its role wins when track is left out. Defaults to the role's first track."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"start_time_ms":{"type":"number","default":0}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_audio_to_timeline\" → \"studio_add_audio_to_timeline\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":259,"tokensAfter":259},{"name":"studio.add_clip_to_timeline","fixed":{"name":"studio_add_clip_to_timeline","description":"Place an existing video/image asset on a b-roll track at a position: the first b-roll track, or the one track_id names (a later b-roll track draws over an earlier one, for picture-in-picture). Overlap on that track is refused with the conflicting range so you can re-place or stack it on another track.","inputSchema":{"type":"object","required":["project_id","asset_id","start_time_ms"],"properties":{"asset_id":{"type":"string"},"track_id":{"type":"string","description":"A video-broll track (vtr_) from get_composition; defaults to the first one."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_ms":{"type":"number","description":"Defaults to the asset's duration."},"start_time_ms":{"type":"number"}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_clip_to_timeline\" → \"studio_add_clip_to_timeline\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":235,"tokensAfter":235},{"name":"studio.add_scene","fixed":{"name":"studio_add_scene","description":"Add a PLANNED scene (storyboard placeholder) to the primary lane — free; no pixels are generated. The prompt is the scene's recipe. NEVER mention camera equipment (crane, dolly, rig) — describe what to SHOW. Say 'camera rises' NOT 'crane shot'. Include: subject, action, lighting, color palette, mood.","inputSchema":{"type":"object","required":["project_id","prompt"],"properties":{"prompt":{"type":"string","description":"Detailed visual description for the scene."},"position":{"type":"number","description":"0-based index in scene order; appends when omitted."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_seconds":{"type":"number","default":5,"description":"Scene duration in seconds (1-30)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_scene\" → \"studio_add_scene\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":231,"tokensAfter":231},{"name":"studio.add_scene_from_asset","fixed":{"name":"studio_add_scene_from_asset","description":"Append an uploaded video or image asset to the main track as a finished scene, or insert it at at_index. Assets longer than 10 minutes become consecutive scenes. A video with no measured length lands as one untrimmed 5s scene marked length_estimated: true, here and in get_composition, until the file is measured in the studio editor. Audio assets are refused; use add_audio_to_timeline.","inputSchema":{"type":"object","required":["project_id","asset_id"],"properties":{"asset_id":{"type":"string","minLength":1,"description":"A video or image asset id (vas_...) from get_composition's assets list."},"at_index":{"type":"integer","maximum":100,"minimum":0,"description":"0-based position in scene order; appends when omitted."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_scene_from_asset\" → \"studio_add_scene_from_asset\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":252,"tokensAfter":252},{"name":"studio.add_sound","fixed":{"name":"studio_add_sound","description":"[$0.001/sec at provider cost] Generate sound effects and ambience FOR a scene from its own footage (foley). Different from generate_music: this is the sound the shot itself would make.","inputSchema":{"type":"object","required":["project_id","scene_id"],"properties":{"prompt":{"type":"string","description":"What it should sound like ('sizzling wok, street chatter'). Omit for natural ambience."},"scene_id":{"type":"string","description":"Scene to add sound to."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_sound\" → \"studio_add_sound\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":176,"tokensAfter":176},{"name":"studio.add_sticker","fixed":{"name":"studio_add_sticker","description":"Add a sticker/element overlay (creator CTAs, emojis, shapes) at a normalized position with its own time range and animation.","inputSchema":{"type":"object","required":["project_id","sticker","x","y"],"properties":{"x":{"type":"number","description":"0-1"},"y":{"type":"number","description":"0-1"},"emoji":{"type":"string","description":"Emoji glyph when the sticker is an emoji."},"sticker":{"type":"string","description":"Catalog key (e.g. subscribe, sale, live) or emoji name."},"animation":{"enum":["none","shake","glow","pulse","bounce"],"type":"string","default":"none"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_ms":{"type":"number","default":3000},"start_time_ms":{"type":"number","default":0}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_sticker\" → \"studio_add_sticker\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":233,"tokensAfter":233},{"name":"studio.add_text_overlay","fixed":{"name":"studio_add_text_overlay","description":"Add a text clip to the first text track, or to the one track_id names (a later text track draws over an earlier one). Pick a semantic preset (title/subtitle/lower_third/callout/price_tag) — it carries complete opinionated styling; override only what matters. Keep text SHORT (max 5-7 words).","inputSchema":{"type":"object","required":["project_id","content"],"properties":{"preset":{"enum":["title","subtitle","lower_third","callout","price_tag"],"type":"string","default":"title"},"content":{"type":"string","description":"The text (max 500 chars; aim for 5-7 words)."},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"description":"Normalized 0-1; 0.5,0.5 is center.","additionalProperties":false},"track_id":{"type":"string","description":"A text track (vtr_) from get_composition; defaults to the first one."},"animation":{"enum":["none","fade_in","fade_up","fade_down","slide_left","slide_right","pop","typewriter"],"type":"string","default":"fade_up"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_ms":{"type":"number","default":3000,"description":"Visible time; keep text readable (3-5s minimum)."},"start_time_ms":{"type":"number","default":0}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_text_overlay\" → \"studio_add_text_overlay\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":369,"tokensAfter":369},{"name":"studio.add_track","fixed":{"name":"studio_add_track","description":"Add an empty track of a role (video-broll, text, audio-music or audio-voice), up to 16 tracks in the project. The new track lands on top of its role: a video-broll or text track draws over the ones before it (picture-in-picture, a second title layer), an audio track mixes in beside the others. Place clips on it by passing the returned track_id to add_clip_to_timeline, add_audio_to_timeline or add_text_overlay. Refuses at the track cap, naming the fix.","inputSchema":{"type":"object","required":["project_id","role"],"properties":{"name":{"type":"string","maxLength":40,"description":"A display name up to 40 characters; the role's label shows when left out."},"role":{"enum":["video-broll","text","audio-music","audio-voice"],"type":"string","description":"The kind of track: video-broll, text, audio-music or audio-voice."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.add_track\" → \"studio_add_track\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":277,"tokensAfter":277},{"name":"studio.adjust_audio_levels","fixed":{"name":"studio_adjust_audio_levels","description":"Adjust an audio role's mix: set every clip's volume on every track of that role (or only on the track track_id names) and/or toggle ducking (voice ducks music BY ROLE — no track ids needed).","inputSchema":{"type":"object","required":["project_id","track"],"properties":{"track":{"enum":["audio-music","audio-voice"],"type":"string"},"volume":{"type":"number","description":"0-2 linear volume for every clip on the track."},"track_id":{"type":"string","description":"One audio track (vtr_) of that role from get_composition; left out, every track of the role."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"apply_ducking":{"type":"boolean","default":true,"description":"Enable/disable voice-over-music ducking."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.adjust_audio_levels\" → \"studio_adjust_audio_levels\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":235,"tokensAfter":235},{"name":"studio.analyze_scene","fixed":{"name":"studio_analyze_scene","description":"Look at a generated scene's actual frames (vision) and describe subject, lighting, composition, mood. Make editing decisions based on what you SEE, not assumptions. Refuses with SIMPLER_STUDIO_VISION_NONE while no model on this platform can look at video frames (nothing is sent or spent): judge the scene from its prompt and the composition instead.","inputSchema":{"type":"object","required":["project_id","scene_id"],"properties":{"focus":{"type":"string","default":"all","description":"composition, lighting, mood, pacing, all"},"scene_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"studio.analyze_scene\" → \"studio_analyze_scene\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":204,"tokensAfter":204},{"name":"studio.apply_ops","fixed":{"name":"studio_apply_ops","description":"Apply several edits atomically against base_version; any refusal applies nothing and names the failing op. Small models: prefer the single-purpose verbs. Each op is {op, args}; the ops and their args are: add_scene_from_asset {asset_id, at_index?}; delete_elements {element_ids, ripple_secondary?}; shift_clips {clip_ids, delta_ms}; duplicate_clips {clip_ids, at_ms}; insert_time {at_ms, length_ms}; ripple_delete {element_id, scope?}; roll_edit {left_id, right_id, delta_ms}; slip_edit {clip_id, del","inputSchema":{"type":"object","required":["project_id","base_version","ops"],"properties":{"ops":{"type":"array","items":{"type":"object","required":["op","args"],"properties":{"op":{"enum":["add_scene_from_asset","delete_elements","shift_clips","duplicate_clips","insert_time","ripple_delete","roll_edit","slip_edit","slide_edit","move_caption","cut_transcript_span","add_track","remove_track","move_track","rename_track","set_track_muted","move_clips_to_track","place_product_card"],"type":"string","description":"The document op to apply."},"args":{"type":"object","properties":{"name":{"type":"string","maxLength":40,"description":"A display name up to 40 characters; the role's label shows when left out."},"role":{"enum":["video-broll","text","audio-music","audio-voice"],"type":"string","description":"The kind of track: video-broll, text, audio-music or audio-voice."},"at_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Timeline millisecond where the earliest copy starts."},"muted":{"type":"boolean","description":"true silences the track; false plays it again."},"scope":{"enum":["all_tracks","own_track"],"type":"string","description":"\"all_tracks\" (default) closes the time on every lane; \"own_track\" closes it on the element's own lane only."},"style":{"enum":["tag","banner","price_only"],"type":"string","description":"The card's layout; tag when left out."},"clip_id":{"type":"string","minLength":1,"description":"A video or audio clip (vcl_) or a scene (vs_) with finished video."},"left_id":{"type":"string","minLength":1,"description":"The scene or clip before the cut."},"asset_id":{"type":"string","minLength":1,"description":"A video or image asset id (vas_...) from get_composition's assets list."},"at_index":{"type":"integer","maximum":100,"minimum":0,"description":"0-based position in scene order; appends when omitted."},"clip_ids":{"type":"array","items":{"type":"string","minLength":1},"maxItems":200,"minItems":1,"description":"Timeline clip ids (vcl_) from get_composition's tracks; duplicates count once."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"The ISO code the price prints with; the business's sales_settings default_currency."},"delta_ms":{"type":"integer","maximum":36000000,"minimum":-36000000,"description":"Milliseconds to move every listed clip; negative moves them earlier."},"position":{"type":"integer","maximum":15,"minimum":0,"description":"0-based position among the tracks of the same role; 0 draws lowest."},"right_id":{"type":"string","minLength":1,"description":"The scene or clip right after the cut."},"start_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Timeline millisecond where the card appears; 0 when left out."},"track_id":{"type":"string","minLength":1,"description":"The track (vtr_) from get_composition's tracks."},"length_ms":{"type":"integer","maximum":600000,"minimum":100,"description":"How much time to insert, 100 to 600000ms (one placeholder scene)."},"caption_id":{"type":"string","minLength":1,"description":"The caption row (vc_) to move. Leave it out and pass at_ms instead to pick the row showing then."},"element_id":{"type":"string","minLength":1,"description":"A scene (vs_) or timeline clip (vcl_) id from get_composition."},"duration_ms":{"type":"integer","maximum":600000,"minimum":100,"description":"How long the card shows, in milliseconds; 4000 when left out."},"element_ids":{"type":"array","items":{"type":"string","minLength":1},"maxItems":200,"minItems":1,"description":"Scene (vs_), clip (vcl_), sticker (vov_) or caption (vc_) ids; duplicates count once."},"delete_clips":{"type":"boolean","description":"true removes the track's clips with it; left out, a track with clips is refused."},"product_name":{"type":"string","maxLength":255,"minLength":1,"description":"The item's name as the stock record holds it."},"stock_item_id":{"type":"string","maxLength":64,"minLength":1,"description":"The stock item's id from stock_item_list; kept on the card so it can be refreshed from the record."},"to_caption_id":{"type":"string","minLength":1,"description":"The caption row (vc_) the span ends in; the same id as from_caption_id for a span inside one row."},"to_word_index":{"type":"integer","maximum":99,"minimum":0,"description":"0-based index of the last word to cut in the to row; the row's end when left out."},"from_caption_id":{"type":"string","minLength":1,"description":"The caption row (vc_) the span starts in."},"from_word_index":{"type":"integer","maximum":99,"minimum":0,"description":"0-based index of the first word to cut in the from row; the row's start when left out."},"ripple_secondary":{"enum":["shift","none"],"type":"string","description":"For deleted scenes: \"shift\" (default) removes their time from every lane; \"none\" closes the main track alone. Clips, stickers and captions ignore it."},"sell_price_cents":{"type":"integer","maximum":9007199254740991,"minimum":0,"description":"The selling price in cents (1290 prints as 12.90)."}},"description":"The op's own arguments; the tool description lists which ones each op takes.","additionalProperties":false}},"additionalProperties":false},"maxItems":50,"minItems":1,"description":"The edits, applied in order; all land or none do."},"project_id":{"type":"string","description":"The studio project (vp_…) this batch edits."},"base_version":{"type":"integer","minimum":0,"description":"The composition_version your ops were planned against (0 for a project with no saved composition)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.apply_ops\" → \"studio_apply_ops\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":1718,"tokensAfter":1718},{"name":"studio.apply_template","fixed":{"name":"studio_apply_template","description":"Append a stored choreography - its scenes (as PENDING prompts, nothing generated yet) plus its text timing - onto this composition. FREE. Templates: tpl_product_showcase (Perfect for product demos and commercials); tpl_social_clip (Vertical format perfect for TikTok, Reels, and Shorts); tpl_explainer (Classic explainer video structure); tpl_testimonial (Square format ideal for customer testimonials); tpl_ad_bumper (Short 6-second ad bumper); tpl_announcement (Perfect for news, launches, and impo","inputSchema":{"type":"object","required":["project_id","template_id"],"properties":{"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"template_id":{"enum":["tpl_product_showcase","tpl_social_clip","tpl_explainer","tpl_testimonial","tpl_ad_bumper","tpl_announcement","tpl_story_highlight","tpl_tutorial_intro"],"type":"string","description":"Which choreography to apply."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.apply_template\" → \"studio_apply_template\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":277,"tokensAfter":277},{"name":"studio.begin_asset_upload","fixed":{"name":"studio_begin_asset_upload","description":"Start uploading a video, audio or image file (up to 2 GB) into a studio project. Returns upload_id and the plan: one PUT for a file up to 64 MB, else fixed 64 MB parts, each a PUT to this server with the same Bearer token this MCP connection uses and the part's exact Content-Length. The file's bytes are reserved against the workspace's 20 GB studio upload quota until the upload finishes or is cancelled (after 24 hours without a part). Then call studio.finish_asset_upload. Refuses: a size over 2 ","inputSchema":{"type":"object","required":["project_id","filename","bytes","mime"],"properties":{"mime":{"enum":["video/mp4","video/x-m4v","video/quicktime","audio/mp4","audio/m4a","audio/x-m4a","video/webm","video/x-matroska","audio/webm","audio/ogg","application/ogg","audio/wav","audio/x-wav","audio/wave","audio/vnd.wave","audio/mpeg","audio/mp3","image/jpeg","image/png","image/gif","image/webp"],"type":"string","description":"The file's type. The stored bytes are checked when the upload finishes; a file that is not what it declares is deleted and refused."},"bytes":{"type":"integer","maximum":2147483648,"minimum":1,"description":"The file's exact size in bytes."},"filename":{"type":"string","maxLength":200,"description":"The file's name, shown in the project's media list."},"project_id":{"type":"string","description":"The studio project (vp_...) the file belongs to."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.begin_asset_upload\" → \"studio_begin_asset_upload\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":408,"tokensAfter":408},{"name":"studio.color_grade","fixed":{"name":"studio_color_grade","description":"Apply color grading to a scene (each channel -100..100; temperature negative = cool, positive = warm).","inputSchema":{"type":"object","required":["project_id","scene_id"],"properties":{"contrast":{"type":"number"},"scene_id":{"type":"string"},"brightness":{"type":"number"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"saturation":{"type":"number"},"temperature":{"type":"number"}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.color_grade\" → \"studio_color_grade\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":141,"tokensAfter":141},{"name":"studio.create_social_clip","fixed":{"name":"studio_create_social_clip","description":"Make one short as a NEW studio project from a range of an existing project: the finished scenes in the range are trimmed copies (no footage is generated or billed again), the captions move with them and the new project takes the target preset's shape. The source project is not changed. Free. Use the start_ms, end_ms and composition_version from studio.suggest_social_clips. Calling again within 24 hours with the same range, preset and version returns the same project (replayed: true) instead of a","inputSchema":{"type":"object","required":["project_id","start_ms","end_ms"],"properties":{"title":{"type":"string","maxLength":50,"description":"Optional name for the new project (a suggestion's title)."},"end_ms":{"type":"integer","maximum":36000000,"minimum":1,"description":"Where the short ends on the source timeline, in milliseconds (a suggestion's end_ms)."},"start_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Where the short starts on the source timeline, in milliseconds (a suggestion's start_ms)."},"project_id":{"type":"string","maxLength":64,"minLength":1,"description":"The source studio project (vp_...) the suggestion came from."},"target_preset":{"enum":["original","youtube_16x9","shorts_9x16","tiktok_9x16","reels_9x16","feed_1x1","feed_4x5","x_16x9","shopee_1x1","lazada_1x1","tiktok_shop_9x16"],"type":"string","description":"The export preset the short is for (default shorts_9x16); the new project takes its shape."},"composition_version":{"type":"integer","maximum":9007199254740991,"minimum":0,"description":"The source composition_version the range was chosen on (the suggest result carries it). When the source has changed since, nothing is created and the answer names the current version."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.create_social_clip\" → \"studio_create_social_clip\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":508,"tokensAfter":508},{"name":"studio.cut_range","fixed":{"name":"studio_cut_range","description":"Cut a span of TIME out of the whole video. Everything after it moves earlier by the same amount, on every lane at once - scenes, clips, captions and stickers stay in sync. A scene or voice clip the cut lands inside is SPLIT, so the material after the cut still plays. A scene piece the cut would leave under 100ms merges into the scene beside it; when no scene beside it has the footage, the cut widens to that scene's edge on every lane, and cut_start_ms / cut_end_ms report the range applied. Refus","inputSchema":{"type":"object","required":["project_id","start_ms","end_ms"],"properties":{"end_ms":{"type":"number","description":"Where the cut ends, in ms on the timeline."},"start_ms":{"type":"number","description":"Where the cut starts, in ms on the timeline."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.cut_range\" → \"studio_cut_range\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":260,"tokensAfter":260},{"name":"studio.cut_transcript_span","fixed":{"name":"studio_cut_transcript_span","description":"Cut the words of a transcript span out of the whole video, the way deleting text in a transcript editor does. The span runs from from_caption_id to to_caption_id (row ids from get_transcript) and takes both rows whole; from_word_index and to_word_index narrow it to a word inside the from row and the to row (0-based, in the order get_transcript lists a row's words). The span's time comes out of every lane through the same cut as cut_range, so the scenes, clips, captions and stickers after it move","inputSchema":{"type":"object","required":["project_id","from_caption_id","to_caption_id"],"properties":{"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"to_caption_id":{"type":"string","minLength":1,"description":"The caption row (vc_) the span ends in; the same id as from_caption_id for a span inside one row."},"to_word_index":{"type":"integer","maximum":99,"minimum":0,"description":"0-based index of the last word to cut in the to row; the row's end when left out."},"from_caption_id":{"type":"string","minLength":1,"description":"The caption row (vc_) the span starts in."},"from_word_index":{"type":"integer","maximum":99,"minimum":0,"description":"0-based index of the first word to cut in the from row; the row's start when left out."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.cut_transcript_span\" → \"studio_cut_transcript_span\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":382,"tokensAfter":382},{"name":"studio.delete_element","fixed":{"name":"studio_delete_element","description":"Delete a scene, clip, or sticker by id (captions soft-delete reversibly). Deleting a scene with ripple_secondary \"shift\" (the default) removes its span of time from every lane, so clips, captions and stickers over the scene go with it and everything after it moves earlier in sync; \"none\" closes the main track alone. A deleted clip still leaves its gap open whatever ripple_secondary says; ripple_delete closes it. Refuses a clip on a locked track, and a shift that moves a locked track (unlock it w","inputSchema":{"type":"object","required":["project_id","element_id"],"properties":{"element_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"ripple_secondary":{"enum":["shift","none"],"type":"string","default":"shift","description":"\"shift\" (default) keeps clips, captions and stickers in sync with the scenes; \"none\" edits the main track alone."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.delete_element\" → \"studio_delete_element\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":279,"tokensAfter":279},{"name":"studio.delete_elements","fixed":{"name":"studio_delete_elements","description":"Delete several scenes, timeline clips, stickers and captions in one edit: pass 1 to 200 ids in element_ids. All or nothing: an unknown id, a clip on a locked track, or a locked track a scene's cut would move refuses the whole call, names the first offender, and deletes nothing (unlock it with lock_track, or pass ripple_secondary \"none\"). Scenes follow ripple_secondary as in delete_element: \"shift\" (default) removes each deleted scene's time from every lane, keeping later clips in sync; \"none\" cl","inputSchema":{"type":"object","required":["project_id","element_ids"],"properties":{"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"element_ids":{"type":"array","items":{"type":"string","minLength":1},"maxItems":200,"minItems":1,"description":"Scene (vs_), clip (vcl_), sticker (vov_) or caption (vc_) ids; duplicates count once."},"ripple_secondary":{"enum":["shift","none"],"type":"string","description":"For deleted scenes: \"shift\" (default) removes their time from every lane; \"none\" closes the main track alone. Clips, stickers and captions ignore it."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.delete_elements\" → \"studio_delete_elements\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":333,"tokensAfter":333},{"name":"studio.duplicate_clip","fixed":{"name":"studio_duplicate_clip","description":"Copy a timeline clip and place the copy immediately after the original on the same track. Refuses if there is no room after it. Returns the new clip id.","inputSchema":{"type":"object","required":["project_id","clip_id"],"properties":{"clip_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.duplicate_clip\" → \"studio_duplicate_clip\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":121,"tokensAfter":121},{"name":"studio.duplicate_clips","fixed":{"name":"studio_duplicate_clips","description":"Copy 1 to 200 timeline clips so the earliest copy starts at at_ms and the others keep their offsets from it, each copy on its original's track. All or nothing: an unknown clip id, a clip on a locked track, copies that would run past the timeline cap, a track that would pass its clip cap, or a copy that overlaps any existing clip (the originals included) refuses the whole call, names the first offender, and copies nothing; an overlap refusal gives the nearest at_ms that fits. Returns new_clip_ids","inputSchema":{"type":"object","required":["project_id","clip_ids","at_ms"],"properties":{"at_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Timeline millisecond where the earliest copy starts."},"clip_ids":{"type":"array","items":{"type":"string","minLength":1},"maxItems":200,"minItems":1,"description":"Timeline clip ids (vcl_) to copy; duplicates count once."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.duplicate_clips\" → \"studio_duplicate_clips\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":295,"tokensAfter":295},{"name":"studio.edit_caption","fixed":{"name":"studio_edit_caption","description":"Edit ONE transcript row: its text, its start, its end, or any combination. Times are timeline milliseconds. Editing the TEXT marks the row hand-edited, so a later replace_transcribed will not overwrite it - which is exactly how you correct a name the recognizer got wrong.","inputSchema":{"type":"object","required":["project_id","caption_id"],"properties":{"text":{"type":"string"},"end_ms":{"type":"integer"},"start_ms":{"type":"integer"},"caption_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.edit_caption\" → \"studio_edit_caption\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":178,"tokensAfter":178},{"name":"studio.enhance_audio","fixed":{"name":"studio_enhance_audio","description":"Clean up an audio clip on the timeline: noise removal, voice shaping, loudness normalization. FREE (platform compute, no provider). Mints an enhanced asset beside the original - the original is never destroyed.","inputSchema":{"type":"object","required":["project_id","clip_id"],"properties":{"noise":{"type":"boolean","default":true,"description":"Remove background noise."},"voice":{"type":"boolean","default":true,"description":"Shape for speech (high-pass, compress, de-ess)."},"clip_id":{"type":"string","description":"Audio clip on the timeline to enhance."},"normalize":{"type":"boolean","default":true,"description":"Normalize loudness to broadcast level."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.enhance_audio\" → \"studio_enhance_audio\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":232,"tokensAfter":232},{"name":"studio.finish_asset_upload","fixed":{"name":"studio_finish_asset_upload","description":"Finish an upload started with studio.begin_asset_upload once every part is PUT: the stored bytes are checked (the moov box is read wherever it sits), counted against the 20 GB quota, and added to the project's media. Returns {asset_id, media_type, probe}. Calling it again returns the same asset. Refuses: parts not yet sent (SIMPLER_STUDIO_UPLOAD_PARTS_MISSING lists them): PUT them, then finish again; bytes that are not the declared type (SIMPLER_STUDIO_UPLOAD_CONTENT_MISMATCH; the file is delete","inputSchema":{"type":"object","required":["project_id","upload_id"],"properties":{"upload_id":{"type":"string","description":"The upload (vup_...) studio.begin_asset_upload returned."},"project_id":{"type":"string","description":"The studio project (vp_...) the upload belongs to."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.finish_asset_upload\" → \"studio_finish_asset_upload\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":242,"tokensAfter":242},{"name":"studio.generate_music","fixed":{"name":"studio_generate_music","description":"[$0.05/track at provider cost] Generate a background music track from a mood/style description. Returns an audio asset id — place it with add_audio_to_timeline.","inputSchema":{"type":"object","required":["project_id","mood","duration_seconds"],"properties":{"mood":{"type":"string","description":"Mood/style: uplifting, dramatic, corporate, chill, energetic — or free text."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_seconds":{"type":"number","description":"Track length in seconds."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.generate_music\" → \"studio_generate_music\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":174,"tokensAfter":174},{"name":"studio.generate_scene","fixed":{"name":"studio_generate_scene","description":"[$0.10/sec at provider cost] Generate video for a scene from its prompt (text-to-video). Pass scene_id to fill a planned scene, or prompt to create-and-generate in one step. NEVER mention camera equipment (crane, dolly, rig) — describe what to SHOW. Say 'camera rises' NOT 'crane shot'. Include: subject, action, lighting, color palette, mood.","inputSchema":{"type":"object","required":["project_id"],"properties":{"style":{"type":"string","default":"cinematic","description":"Visual style: cinematic, corporate, playful, dramatic"},"prompt":{"type":"string","description":"Visual description; required when scene_id is omitted."},"scene_id":{"type":"string","description":"Planned scene to generate (optional)."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_seconds":{"type":"number","default":5,"description":"Whole seconds (3-10)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.generate_scene\" → \"studio_generate_scene\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":270,"tokensAfter":270},{"name":"studio.generate_voiceover","fixed":{"name":"studio_generate_voiceover","description":"Generate narration audio from a script. voice_tier is the REAL choice the user makes: 'standard' ($0.015/1K chars at provider cost) vs 'premium' ($0.15/1K chars at provider cost) — quote both prices when proposing; never pick silently. Returns an audio asset id.","inputSchema":{"type":"object","required":["project_id","script","voice_tier"],"properties":{"voice":{"type":"string","description":"Provider voice id/name (optional)."},"script":{"type":"string","description":"The narration text (max 5000 chars)."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"voice_tier":{"enum":["standard","premium"],"type":"string","description":"REQUIRED — the user's explicit price-visible choice."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.generate_voiceover\" → \"studio_generate_voiceover\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":228,"tokensAfter":228},{"name":"studio.get_composition","fixed":{"name":"studio_get_composition","description":"Read the current composition: scenes in order (with derived start times and generation status; length_estimated: true marks a scene sized by an estimate because its video file reported no length), tracks (id, role, name, locked, muted) and clips, overlays, caption count, total duration, and the assets (the ids every asset_id argument takes). Call when you need fresh truth after edits.","inputSchema":{"type":"object","required":["project_id"],"properties":{"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"studio.get_composition\" → \"studio_get_composition\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":176,"tokensAfter":176},{"name":"studio.get_transcript","fixed":{"name":"studio_get_transcript","description":"Read the caption rows in time order with the ids edit_caption, move_caption and cut_transcript_span take. Each row gives id, type (speech, filler, silence or manual), source (transcription or manual), start_ms, end_ms, speaker when the transcription labelled one, hidden: true for a row remove_fillers hid, text, and words. words lists a transcribed row's words in order, so a word's index is its position there (the from_word_index and to_word_index cut_transcript_span takes); a manual row and a si","inputSchema":{"type":"object","required":["project_id"],"properties":{"from_row":{"type":"integer","description":"0-based row to start reading from; 0 when left out."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"studio.get_transcript\" → \"studio_get_transcript\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":234,"tokensAfter":234},{"name":"studio.import_asset_from_url","fixed":{"name":"studio_import_asset_from_url","description":"Copy a video, audio or image file from a public http(s) link into a studio project's media (up to 2 GB, within the 20 GB quota). Returns {asset_id, media_type, probe}, the same as studio.finish_asset_upload. The link is only downloaded; nothing is posted to it. Refuses: localhost, private, link-local or cloud-metadata addresses, or a redirect to one (SIMPLER_STUDIO_IMPORT_URL_REFUSED): give a public link; a link that fails or answers an error (SIMPLER_STUDIO_IMPORT_FETCH_FAILED): check it downlo","inputSchema":{"type":"object","required":["project_id","url"],"properties":{"url":{"type":"string","maxLength":2000,"description":"A public http(s) link that downloads the media file itself."},"project_id":{"type":"string","description":"The studio project (vp_...) to add the file to."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.import_asset_from_url\" → \"studio_import_asset_from_url\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":244,"tokensAfter":244},{"name":"studio.insert_product_card","fixed":{"name":"studio_insert_product_card","description":"Put a stock item's name and selling price on the video as a product card: a price_tag text clip on the first text track (or the one track_id names), from start_ms for duration_ms. It reads the stock record as you, so the card shows what stock_item_read shows; the values are copied into the project, and later stock edits do not change a card already placed. The price prints with the currency code (SGD 12.90), taken from the business's sales_settings default_currency unless you pass currency. styl","inputSchema":{"type":"object","required":["project_id","stock_item_id","start_ms"],"properties":{"style":{"enum":["tag","banner","price_only"],"type":"string","description":"The card's layout; tag when left out."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"The ISO code the price prints with; the business's sales_settings default_currency when left out."},"start_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Timeline millisecond where the card appears."},"track_id":{"type":"string","minLength":1,"description":"A text track (vtr_) from studio.get_composition; the first text track when left out."},"project_id":{"type":"string","minLength":1,"description":"The studio project (vp_...) the card goes into."},"duration_ms":{"type":"integer","maximum":600000,"minimum":100,"description":"How long the card shows, in milliseconds; 4000 when left out."},"stock_item_id":{"type":"string","maxLength":64,"minLength":1,"description":"The stock item to show, from stock_item_list; its name and sell_price_cents are copied onto the card."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.insert_product_card\" → \"studio_insert_product_card\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":485,"tokensAfter":485},{"name":"studio.insert_time","fixed":{"name":"studio_insert_time","description":"Open length_ms of empty time at at_ms on every lane, so everything at or after at_ms moves later together. The main track receives a placeholder scene there (a pending scene with the prompt \"Gap\"; fill it with update_scene and generate_scene, or add footage). A scene or voice clip at_ms lands inside is split around the new time; text, stills, captions and stickers that span it stretch. Refuses, naming the fix: at_ms past the end of the main track, a locked track with clips at or after at_ms, at_","inputSchema":{"type":"object","required":["project_id","at_ms","length_ms"],"properties":{"at_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Timeline millisecond where the new time opens, from 0 to the main track's end."},"length_ms":{"type":"integer","maximum":600000,"minimum":100,"description":"How much time to insert, 100 to 600000ms (one placeholder scene)."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.insert_time\" → \"studio_insert_time\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":295,"tokensAfter":295},{"name":"studio.list_versions","fixed":{"name":"studio_list_versions","description":"List a studio project's stored versions, newest first: {version, created_at, label, current}, plus every named version (snapshots) and how many of the 20 names are used. A project keeps its newest 20 saved versions, one per 10 minutes for the last day, one per day for the last 30 days, and every named snapshot (up to 20); older unnamed versions are deleted. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a limit outside 1 to 5","inputSchema":{"type":"object","required":["project_id"],"properties":{"limit":{"type":"integer","maximum":50,"minimum":1,"description":"How many of the newest versions to return (default 20). Named versions are always all listed under snapshots."},"project_id":{"type":"string","description":"The studio project (vp_...) whose history to use."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"studio.list_versions\" → \"studio_list_versions\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":257,"tokensAfter":257},{"name":"studio.lock_track","fixed":{"name":"studio_lock_track","description":"Lock or unlock a track. A locked track refuses every edit - move, trim, split, duplicate, delete - until it is unlocked. Lock a finished voiceover before restructuring the picture so the timing cannot drift underneath it. track names the role and locks every track of it; add track_id to lock only that one.","inputSchema":{"type":"object","required":["project_id","track","locked"],"properties":{"track":{"enum":["video-broll","text","audio-music","audio-voice"],"type":"string"},"locked":{"type":"boolean"},"track_id":{"type":"string","description":"One track (vtr_) of that role from get_composition; left out, every track of the role."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.lock_track\" → \"studio_lock_track\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":225,"tokensAfter":225},{"name":"studio.move_caption","fixed":{"name":"studio_move_caption","description":"Move ONE whole caption row along the timeline by delta_ms (negative moves it earlier): its start, its end and every word timing move together, so the words keep their spacing. Pick the row by caption_id, or by at_ms: the visible caption showing at that timeline millisecond (hidden rows are skipped). The row stops at 0, at the timeline cap and at the visible caption on either side, so a move never runs into its neighbours. To change only a start or an end, use edit_caption. Refuses, naming the fi","inputSchema":{"type":"object","required":["project_id","delta_ms"],"properties":{"at_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"A timeline millisecond inside the visible caption to move; used when caption_id is left out."},"delta_ms":{"type":"integer","maximum":36000000,"minimum":-36000000,"description":"Milliseconds to move the whole row; negative moves it earlier."},"caption_id":{"type":"string","minLength":1,"description":"The caption row (vc_) to move. Leave it out and pass at_ms instead to pick the row showing then."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.move_caption\" → \"studio_move_caption\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":341,"tokensAfter":341},{"name":"studio.move_clips_to_track","fixed":{"name":"studio_move_clips_to_track","description":"Move 1 to 200 timeline clips onto another track of the same kind (a b-roll clip to a video-broll track, a title to a text track, music to an audio-music track, a voice clip to an audio-voice track), keeping their spacing; delta_ms also moves them in time (default 0). Stacking follows the track: get_composition lists tracks in stacking order, and a clip on a later video-broll or text track draws over the earlier ones. All or nothing. Refuses, naming the first offender and moving nothing: an unkno","inputSchema":{"type":"object","required":["project_id","clip_ids","track_id"],"properties":{"clip_ids":{"type":"array","items":{"type":"string","minLength":1},"maxItems":200,"minItems":1,"description":"Timeline clip ids (vcl_) from get_composition's tracks; duplicates count once."},"delta_ms":{"type":"integer","maximum":36000000,"minimum":-36000000,"description":"Milliseconds to move the clips in time as they change track; negative moves them earlier. 0 when left out."},"track_id":{"type":"string","minLength":1,"description":"The target track (vtr_) from get_composition's tracks, of the same role as every listed clip's track."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.move_clips_to_track\" → \"studio_move_clips_to_track\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":365,"tokensAfter":365},{"name":"studio.move_track","fixed":{"name":"studio_move_track","description":"Move a track to position within its own role. Position 0 is the lowest: its pictures draw first, so every later video-broll or text track draws over it. Tracks of other roles keep their order; text always draws over video. Locked tracks may be reordered. Refuses an unknown track_id or a position past the role's last track, naming the fix.","inputSchema":{"type":"object","required":["project_id","track_id","position"],"properties":{"position":{"type":"integer","maximum":15,"minimum":0,"description":"0-based position among the tracks of the same role; 0 draws lowest."},"track_id":{"type":"string","minLength":1,"description":"The track (vtr_) from get_composition's tracks."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.move_track\" → \"studio_move_track\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":236,"tokensAfter":236},{"name":"studio.place_product_card","fixed":{"name":"studio_place_product_card","description":"Place a product card: a price_tag text clip showing a stock item's name and price, built from values you already hold. studio.insert_product_card reads them from the stock record for you; use this op inside studio.apply_ops or when the values come from elsewhere. The price prints as the currency code and the amount with two decimals (SGD 12.90), never a symbol. style: tag (name over price, lower left), banner (name and price on one line, bottom centre), price_only (the price alone, large, top ri","inputSchema":{"type":"object","required":["project_id","stock_item_id","product_name","sell_price_cents","currency"],"properties":{"style":{"enum":["tag","banner","price_only"],"type":"string","description":"The card's layout; tag when left out."},"currency":{"enum":["USD","EUR","GBP","JPY","CNY","AUD","HKD","INR","SGD","MYR","IDR","THB","PHP","VND","BND","MMK"],"type":"string","description":"The ISO code the price prints with; the business's sales_settings default_currency."},"start_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Timeline millisecond where the card appears; 0 when left out."},"track_id":{"type":"string","minLength":1,"description":"A text track (vtr_) from get_composition; the first text track when left out."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_ms":{"type":"integer","maximum":600000,"minimum":100,"description":"How long the card shows, in milliseconds; 4000 when left out."},"product_name":{"type":"string","maxLength":255,"minLength":1,"description":"The item's name as the stock record holds it."},"stock_item_id":{"type":"string","maxLength":64,"minLength":1,"description":"The stock item's id from stock_item_list; kept on the card so it can be refreshed from the record."},"sell_price_cents":{"type":"integer","maximum":9007199254740991,"minimum":0,"description":"The selling price in cents (1290 prints as 12.90)."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.place_product_card\" → \"studio_place_product_card\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":563,"tokensAfter":563},{"name":"studio.reframe_scene","fixed":{"name":"studio_reframe_scene","description":"Re-frame one scene for a different aspect ratio. method 'analytic' re-centers and scales the EXISTING footage on a focus point (cheap, no new pixels, needs the source dimensions); method 'generative' outpaints new edges [$0.10/sec at provider cost].","inputSchema":{"type":"object","required":["project_id","scene_id","target_aspect"],"properties":{"method":{"enum":["analytic","generative"],"type":"string","default":"analytic","description":"analytic = crop/scale existing footage; generative = outpaint new edges."},"prompt":{"type":"string","description":"What the new edges should contain (generative only)."},"focus_x":{"type":"number","default":0.5,"description":"Focus point across the frame, 0-1 (analytic only)."},"focus_y":{"type":"number","default":0.5,"description":"Focus point down the frame, 0-1 (analytic only)."},"scene_id":{"type":"string","description":"Scene to re-frame."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"target_aspect":{"enum":["16:9","9:16","1:1","4:5"],"type":"string","description":"Aspect to frame for."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.reframe_scene\" → \"studio_reframe_scene\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":325,"tokensAfter":325},{"name":"studio.remove_dead_air","fixed":{"name":"studio_remove_dead_air","description":"Cut every silent pause the transcript found OUT OF THE FOOTAGE, keeping a little breathing room at each one. This is the real edit, not a transcript cleanup: the video gets shorter. Needs a transcript first (transcribe). Not undoable.","inputSchema":{"type":"object","required":["project_id"],"properties":{"keep_ms":{"type":"number","default":150,"description":"Breathing room left at the end of each pause. 0 makes speech run together."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"min_silence_ms":{"type":"number","default":700,"description":"Only cut pauses at least this long. Below ~400ms you are cutting natural speech rhythm."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.remove_dead_air\" → \"studio_remove_dead_air\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":213,"tokensAfter":213},{"name":"studio.remove_fillers","fixed":{"name":"studio_remove_fillers","description":"Hide filler words (um, uh, you know, kind of) from the transcript. REVERSIBLE: rows are soft-deleted, excluded from burn-in and SRT, and restore_fillers brings them back. Read the composition afterwards if you need to see which rows went.","inputSchema":{"type":"object","required":["project_id"],"properties":{"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.remove_fillers\" → \"studio_remove_fillers\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":134,"tokensAfter":134},{"name":"studio.remove_silence","fixed":{"name":"studio_remove_silence","description":"Delete every '[silence]' row from the TRANSCRIPT. This cleans up the caption text only - the footage keeps its full length and the pauses still play. To cut the pauses out of the video for real, use remove_dead_air. Silence rows are derived markers, not content, so this is a HARD delete and is not undoable - re-run transcription to get them back.","inputSchema":{"type":"object","required":["project_id"],"properties":{"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.remove_silence\" → \"studio_remove_silence\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":165,"tokensAfter":165},{"name":"studio.remove_snapshot","fixed":{"name":"studio_remove_snapshot","description":"Remove the name from a studio project's named version, freeing one of its 20 names. The version stays stored; from the next save it follows the normal retention and may be deleted. A version without a name answers removed: false. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a version the project no longer stores (SIMPLER_STUDIO_VERSION_NOT_STORED): call studio.list_versions and pick a listed one; an environment where names ","inputSchema":{"type":"object","required":["project_id","version"],"properties":{"version":{"type":"integer","minimum":1,"description":"A version number studio.list_versions lists."},"project_id":{"type":"string","description":"The studio project (vp_...) whose history to use."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.remove_snapshot\" → \"studio_remove_snapshot\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":240,"tokensAfter":240},{"name":"studio.remove_track","fixed":{"name":"studio_remove_track","description":"Remove one track by track_id. A track that still holds clips is refused unless delete_clips is true, which removes its clips with it. Refuses, naming the fix: an unknown track_id, a locked track, a track with clips and no delete_clips, or the last track of its role (every project keeps one of each).","inputSchema":{"type":"object","required":["project_id","track_id"],"properties":{"track_id":{"type":"string","minLength":1,"description":"The track (vtr_) from get_composition's tracks."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"delete_clips":{"type":"boolean","description":"true removes the track's clips with it; left out, a track with clips is refused."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.remove_track\" → \"studio_remove_track\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":220,"tokensAfter":220},{"name":"studio.rename_track","fixed":{"name":"studio_rename_track","description":"Give a track a display name up to 40 characters, or pass an empty name to go back to the role's label. Names are for people and never address a track: tools take track_id. Locked tracks may be renamed. Refuses an unknown track_id, naming the fix.","inputSchema":{"type":"object","required":["project_id","track_id","name"],"properties":{"name":{"type":"string","maxLength":40,"description":"The new name; an empty string clears it."},"track_id":{"type":"string","minLength":1,"description":"The track (vtr_) from get_composition's tracks."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.rename_track\" → \"studio_rename_track\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":197,"tokensAfter":197},{"name":"studio.reorder_scenes","fixed":{"name":"studio_reorder_scenes","description":"Move a scene to a different position in the primary lane. Positions are 0-based, so to_index 0 makes it the opening scene. Scene start times are DERIVED from order, so every scene after the move shifts in time. With ripple_secondary \"shift\" (the default) every clip, caption and sticker that sits wholly inside a scene moves with that scene; one that spans a scene edge stays put and is listed in straddling_ids. \"none\" moves the scenes alone. Refuses a shift that moves a clip on a locked track (unl","inputSchema":{"type":"object","required":["project_id","scene_id","to_index"],"properties":{"scene_id":{"type":"string"},"to_index":{"type":"integer","description":"0-based destination position."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"ripple_secondary":{"enum":["shift","none"],"type":"string","default":"shift","description":"\"shift\" (default) keeps clips, captions and stickers in sync with the scenes; \"none\" edits the main track alone."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.reorder_scenes\" → \"studio_reorder_scenes\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":302,"tokensAfter":302},{"name":"studio.replace_transcribed","fixed":{"name":"studio_replace_transcribed","description":"Replace the transcript with fresh rows from your own transcription. Rows the operator hand-edited (source 'manual') SURVIVE - only machine-transcribed rows are replaced, so a re-transcribe never silently discards someone's correction. Rows are re-sorted by start time.","inputSchema":{"type":"object","required":["project_id","captions"],"properties":{"captions":{"type":"array","items":{"type":"object","required":["start_ms","end_ms","text"],"properties":{"text":{"type":"string"},"type":{"enum":["speech","filler","silence","manual"],"type":"string","default":"speech"},"end_ms":{"type":"number"},"speaker":{"type":"string"},"start_ms":{"type":"number"},"confidence":{"type":"number","description":"0-1 recognizer confidence."}},"additionalProperties":false},"description":"The new transcript, in any order."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.replace_transcribed\" → \"studio_replace_transcribed\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":275,"tokensAfter":275},{"name":"studio.request_regeneration","fixed":{"name":"studio_request_regeneration","description":"Flag a generated scene that does not match intent: record WHY and an improved prompt. This stages the improved prompt on the scene (regeneration is a separate paid, user-approved step — it never silently destroys footage or spends).","inputSchema":{"type":"object","required":["project_id","scene_id","reason","improved_prompt"],"properties":{"reason":{"type":"string","description":"Why it needs regeneration."},"scene_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"improved_prompt":{"type":"string","description":"The improved prompt."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.request_regeneration\" → \"studio_request_regeneration\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":192,"tokensAfter":192},{"name":"studio.restore_fillers","fixed":{"name":"studio_restore_fillers","description":"Un-hide transcript rows that were soft-deleted. Scope is EVERY hidden row, not only filler words - a caption hidden by delete_element comes back too. There is no per-row restore.","inputSchema":{"type":"object","required":["project_id"],"properties":{"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.restore_fillers\" → \"studio_restore_fillers\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":118,"tokensAfter":118},{"name":"studio.restore_version","fixed":{"name":"studio_restore_version","description":"Bring back an earlier version of a studio project: it saves that version as a new version; history is never rewritten, so the version it replaces stays listed and restoring that one undoes this. Returns {version, restored_from, previous_version}. A person editing the project in the browser is told at their next save that a newer version is on the server and reloads. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a version the","inputSchema":{"type":"object","required":["project_id","version"],"properties":{"version":{"type":"integer","minimum":1,"description":"A version number studio.list_versions lists."},"project_id":{"type":"string","description":"The studio project (vp_...) whose history to use."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.restore_version\" → \"studio_restore_version\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":240,"tokensAfter":240},{"name":"studio.ripple_delete","fixed":{"name":"studio_ripple_delete","description":"Delete a scene or timeline clip AND close the time it held. scope \"all_tracks\" (the default) removes that span of time from every lane, so everything after it moves earlier together and stays in sync; clips and captions over the span go with it, and one that crosses its edge is split or shortened. scope \"own_track\" closes the gap on the element's own lane only (for a scene, the other tracks then drift by its length). delete_element and delete_elements leave a clip's gap open instead. Refuses, na","inputSchema":{"type":"object","required":["project_id","element_id"],"properties":{"scope":{"enum":["all_tracks","own_track"],"type":"string","description":"\"all_tracks\" (default) closes the time on every lane; \"own_track\" closes it on the element's own lane only."},"element_id":{"type":"string","minLength":1,"description":"A scene (vs_) or timeline clip (vcl_) id from get_composition."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.ripple_delete\" → \"studio_ripple_delete\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":298,"tokensAfter":298},{"name":"studio.roll_edit","fixed":{"name":"studio_roll_edit","description":"Move the cut between two neighbours by delta_ms (positive moves it later): left_id's end and right_id's start move together, and their footage in/out points follow. The pair's total length and every other element stay where they are. The neighbours are a scene and the next scene, or two touching clips on one track, left one first. Refuses, naming the fix: an unknown id, ids that do not share a cut, a locked track, or a delta that would leave either side under 100ms, past its length cap or past i","inputSchema":{"type":"object","required":["project_id","left_id","right_id","delta_ms"],"properties":{"left_id":{"type":"string","minLength":1,"description":"The scene or clip before the cut."},"delta_ms":{"type":"integer","maximum":36000000,"minimum":-36000000,"description":"Milliseconds to move the cut; negative moves it earlier."},"right_id":{"type":"string","minLength":1,"description":"The scene or clip right after the cut."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.roll_edit\" → \"studio_roll_edit\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":307,"tokensAfter":307},{"name":"studio.set_config","fixed":{"name":"studio_set_config","description":"Set project output settings: aspect ratio, resolution, frame rate, background colour, caption style. Every field is optional - only what you pass changes. Changing aspect does NOT re-crop existing footage (it letterboxes); reframe the scenes if you need the framing to follow.","inputSchema":{"type":"object","required":["project_id"],"properties":{"fps":{"type":"integer","description":"Frame rate - 24, 25, 30 or 60 only."},"aspect":{"enum":["16:9","9:16","1:1","4:5"],"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"resolution":{"enum":["720p","1080p","4k"],"type":"string"},"caption_preset":{"enum":["default","bold","neon","minimal","pop","karaoke"],"type":"string"},"background_color":{"type":"string","description":"Hex #RRGGBB - what shows in letterbox bars."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.set_config\" → \"studio_set_config\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":252,"tokensAfter":252},{"name":"studio.set_ducking","fixed":{"name":"studio_set_ducking","description":"Configure the voice-over-music ducking mix intent (by role; the renderer honors it).","inputSchema":{"type":"object","required":["project_id","enabled"],"properties":{"enabled":{"type":"boolean"},"duck_level":{"type":"number","description":"Music level under voice, 0-1 (default 0.3)."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.set_ducking\" → \"studio_set_ducking\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":127,"tokensAfter":127},{"name":"studio.set_track_muted","fixed":{"name":"studio_set_track_muted","description":"Mute or unmute a whole track. A muted audio track plays nothing and a muted voice track stops ducking the music; a muted video-broll track keeps its pictures and drops its clips' own sound. The preview and the export both honor it, and each clip keeps its own muted and volume. Locked tracks may be muted. Refuses an unknown track_id or a text track, naming the fix.","inputSchema":{"type":"object","required":["project_id","track_id","muted"],"properties":{"muted":{"type":"boolean","description":"true silences the track; false plays it again."},"track_id":{"type":"string","minLength":1,"description":"The track (vtr_) from get_composition's tracks."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.set_track_muted\" → \"studio_set_track_muted\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":230,"tokensAfter":230},{"name":"studio.set_transition","fixed":{"name":"studio_set_transition","description":"Set a scene's transition (in = how it enters, out = how it leaves). Dissolve creates connection (500-800ms); fade for scene breaks; cut clears the transition.","inputSchema":{"type":"object","required":["project_id","scene_id","type"],"properties":{"type":{"enum":["cut","fade","dissolve","wipe_left","wipe_right","wipe_up","wipe_down","slide_left","slide_right","circle_open","circle_close","zoom_in"],"type":"string"},"scene_id":{"type":"string"},"direction":{"enum":["in","out"],"type":"string","default":"out"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_ms":{"type":"number","default":500}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.set_transition\" → \"studio_set_transition\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":204,"tokensAfter":204},{"name":"studio.shift_clips","fixed":{"name":"studio_shift_clips","description":"Move 1 to 200 timeline clips by the same delta_ms (negative moves earlier), keeping their spacing and their tracks. All or nothing: an unknown clip id, a clip on a locked track, a clip pushed before 0 or past the timeline cap, or a landing that overlaps a clip you did not list refuses the whole call, names the first offending clip, and moves nothing; an overlap refusal gives the nearest delta_ms that fits. Scenes on the main track keep their order and are not clips; use reorder_scenes for them.","inputSchema":{"type":"object","required":["project_id","clip_ids","delta_ms"],"properties":{"clip_ids":{"type":"array","items":{"type":"string","minLength":1},"maxItems":200,"minItems":1,"description":"Timeline clip ids (vcl_) from get_composition's tracks; duplicates count once."},"delta_ms":{"type":"integer","maximum":36000000,"minimum":-36000000,"description":"Milliseconds to move every listed clip; negative moves them earlier."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.shift_clips\" → \"studio_shift_clips\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":308,"tokensAfter":308},{"name":"studio.slide_edit","fixed":{"name":"studio_slide_edit","description":"Move a video or audio clip by delta_ms between its neighbours on its track: the clip touching its start gets longer or shorter at its end, the clip touching its end at its start, so the three-clip span and every other track stay put. A side with a gap instead lets the clip move into the gap and no further. Refuses, naming the fix: an unknown id, a scene (scenes reorder with reorder_scenes), text or a still image (move it with shift_clips), a locked track, or a delta that would leave a neighbour ","inputSchema":{"type":"object","required":["project_id","clip_id","delta_ms"],"properties":{"clip_id":{"type":"string","minLength":1,"description":"A video or audio clip (vcl_) on a secondary track."},"delta_ms":{"type":"integer","maximum":36000000,"minimum":-36000000,"description":"Milliseconds to move the clip; negative moves it earlier."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.slide_edit\" → \"studio_slide_edit\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":282,"tokensAfter":282},{"name":"studio.slip_edit","fixed":{"name":"studio_slip_edit","description":"Show a different part of a clip's or scene's footage without moving it: its in and out points move by delta_ms of timeline (positive shows later footage), while its position and length stay. Works on video and audio clips and on scenes with finished video. Refuses, naming the fix: an unknown id, text or a still image or a scene with no footage yet (only video and audio can slip), a locked track, or a delta that runs past the footage (the refusal gives the delta_ms range that fits).","inputSchema":{"type":"object","required":["project_id","clip_id","delta_ms"],"properties":{"clip_id":{"type":"string","minLength":1,"description":"A video or audio clip (vcl_) or a scene (vs_) with finished video."},"delta_ms":{"type":"integer","maximum":36000000,"minimum":-36000000,"description":"Milliseconds to move the footage window; positive shows later footage."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.slip_edit\" → \"studio_slip_edit\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":285,"tokensAfter":285},{"name":"studio.snapshot_version","fixed":{"name":"studio_snapshot_version","description":"Name a studio project's version (the current one unless version is given) so it is never deleted by retention; naming a named version renames it. At most 20 named versions per project. Adds no new version, so an editor open on the project keeps saving. Refuses: a project_id outside your scope (NOT_FOUND): studio_project_list lists your projects; pass one of their ids; a label that is empty, over 80 characters or more than one line (SIMPLER_STUDIO_SNAPSHOT_LABEL_INVALID): send one line of 1 to 80","inputSchema":{"type":"object","required":["project_id","label"],"properties":{"label":{"type":"string","maxLength":80,"minLength":1,"description":"The version's name, one line, for example \"Approved by client\"."},"version":{"type":"integer","minimum":1,"description":"The version to name; the current version when left out."},"project_id":{"type":"string","description":"The studio project (vp_...) whose history to use."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.snapshot_version\" → \"studio_snapshot_version\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":280,"tokensAfter":280},{"name":"studio.split","fixed":{"name":"studio_split","description":"Cut a scene or a timeline clip in two at a timeline millisecond. The left half keeps the id; the right half is new and is returned. Both halves must keep at least 100ms of footage or the split refuses. Transitions stay on the outer edges - the new seam is a hard cut.","inputSchema":{"type":"object","required":["project_id","target_id","at_ms"],"properties":{"at_ms":{"type":"number","description":"TIMELINE position of the cut, not an offset into the element."},"target_id":{"type":"string","description":"A scene id or a timeline clip id."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.split\" → \"studio_split\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":196,"tokensAfter":196},{"name":"studio.style_transfer","fixed":{"name":"studio_style_transfer","description":"[$0.04/sec at provider cost] Restyle a scene's footage, keeping its motion. style must be one of the presets (anime, comic, watercolor, oil_painting, sketch, cinematic, vintage, neon). A free-text style is refused, because free text over the user's footage could change who appears in it; pick the closest preset instead. Mints a NEW asset - the original is never destroyed.","inputSchema":{"type":"object","required":["project_id","scene_id","style"],"properties":{"style":{"enum":["anime","comic","watercolor","oil_painting","sketch","cinematic","vintage","neon"],"type":"string","description":"A preset key: anime, comic, watercolor, oil_painting, sketch, cinematic, vintage, neon. Free text is refused."},"scene_id":{"type":"string","description":"Scene whose footage to restyle."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.style_transfer\" → \"studio_style_transfer\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":266,"tokensAfter":266},{"name":"studio.suggest_social_clips","fixed":{"name":"studio_suggest_social_clips","description":"Find the best short-form moments in a long studio video and return them as ranges with a title, a hook line, a one-sentence rationale and measured evidence (captioned words, words per second, whether it starts on a scene cut). It creates and changes nothing. To make a suggestion into a project, call studio.create_social_clip with its start_ms and end_ms; then render that project with video.export_create and the same preset. Pass count (1 to 10), target_preset (default shorts_9x16; it sets the sh","inputSchema":{"type":"object","required":["project_id"],"properties":{"count":{"type":"integer","maximum":10,"minimum":1,"description":"How many suggestions to return, 1 to 10 (default 3). Fewer can come back when the video has fewer distinct moments."},"focus":{"type":"string","maxLength":200,"description":"Optional: what to look for, such as 'the product demo'. It only ranks moments; it is never treated as an instruction."},"asset_id":{"type":"string","maxLength":64,"minLength":1,"description":"Optional: suggest only from the scenes cut from this video asset (vas_...). studio.get_composition lists the assets; the asset must already be on the timeline (studio.add_scene_from_asset places it)."},"project_id":{"type":"string","maxLength":64,"minLength":1,"description":"The studio project (vp_...) holding the long video. studio_project_list shows your projects."},"target_preset":{"enum":["original","youtube_16x9","shorts_9x16","tiktok_9x16","reels_9x16","feed_1x1","feed_4x5","x_16x9","shopee_1x1","lazada_1x1","tiktok_shop_9x16"],"type":"string","description":"The export preset the shorts are for (default shorts_9x16). It sets the shape and the longest allowed length; pass the same preset to studio.create_social_clip and video.export_create."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.suggest_social_clips\" → \"studio_suggest_social_clips\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":514,"tokensAfter":514},{"name":"studio.transcribe","fixed":{"name":"studio_transcribe","description":"[$0.0043/min at provider cost] Transcribe an audio or video asset into word-timed captions. REPLACES every previously transcribed caption on this project; captions you wrote by hand survive. Speaker labels are asked for: a row carries speaker (Speaker 1, Speaker 2, ...) only when the recognizer labelled its words, and the reply's speaker_count is 0 when it labelled none. Read the rows with get_transcript; follow with remove_fillers / remove_silence to clean the transcript.","inputSchema":{"type":"object","required":["project_id","asset_id"],"properties":{"asset_id":{"type":"string","description":"Asset to transcribe (audio or video) - read the ids from get_composition."},"language":{"type":"string","description":"Language hint such as 'en' or 'ms'. Omit to auto-detect."},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.transcribe\" → \"studio_transcribe\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":264,"tokensAfter":264},{"name":"studio.trim_clip","fixed":{"name":"studio_trim_clip","description":"Trim a timeline clip's start or end edge to an absolute TIMELINE millisecond. The clip keeps its id and its source in/out points move with the edge, so the footage stays in sync. Refuses if the trim would leave under 100ms, cross a neighbouring clip, or run past the source media.","inputSchema":{"type":"object","required":["project_id","clip_id","edge","at_ms"],"properties":{"edge":{"enum":["start","end"],"type":"string"},"at_ms":{"type":"integer","description":"Absolute TIMELINE position for the new edge, not an offset."},"clip_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.trim_clip\" → \"studio_trim_clip\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":201,"tokensAfter":201},{"name":"studio.trim_scene","fixed":{"name":"studio_trim_scene","description":"Trim a generated scene's footage: cut milliseconds off its start and/or end. With ripple_secondary \"shift\" (the default) the other lanes follow the footage: time the scene loses at its start or end is cut from every lane at that edge, and time it gains is inserted there, so clips, captions and stickers stay over the frames they were placed on (one that spans that edge is split or stretched). \"none\" changes the main track alone. Refuses a shift that moves a locked track (unlock it with lock_track","inputSchema":{"type":"object","required":["project_id","scene_id"],"properties":{"scene_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"trim_end_ms":{"type":"number","default":0,"description":"Milliseconds to cut from the end."},"trim_start_ms":{"type":"number","default":0,"description":"Milliseconds to cut from the start."},"ripple_secondary":{"enum":["shift","none"],"type":"string","default":"shift","description":"\"shift\" (default) keeps clips, captions and stickers in sync with the scenes; \"none\" edits the main track alone."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.trim_scene\" → \"studio_trim_scene\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":330,"tokensAfter":330},{"name":"studio.update_clip","fixed":{"name":"studio_update_clip","description":"Move a clip or adjust its volume/mute on any secondary track.","inputSchema":{"type":"object","required":["project_id","clip_id"],"properties":{"muted":{"type":"boolean"},"volume":{"type":"number"},"clip_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"start_time_ms":{"type":"number"}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.update_clip\" → \"studio_update_clip\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":119,"tokensAfter":119},{"name":"studio.update_scene","fixed":{"name":"studio_update_scene","description":"Update a scene's prompt or duration. EDIT-INVALIDATES-ASSET: changing the prompt of a generated scene resets it to pending and drops its footage (a stale render cannot survive a prompt edit) — say so before doing it. With ripple_secondary \"shift\" (the default) the other lanes follow the footage: time the scene loses at its start or end is cut from every lane at that edge, and time it gains is inserted there, so clips, captions and stickers stay over the frames they were placed on (one that spans","inputSchema":{"type":"object","required":["project_id","scene_id"],"properties":{"prompt":{"type":"string","description":"New prompt (optional)."},"scene_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_seconds":{"type":"number","description":"New duration in seconds (optional)."},"ripple_secondary":{"enum":["shift","none"],"type":"string","default":"shift","description":"\"shift\" (default) keeps clips, captions and stickers in sync with the scenes; \"none\" edits the main track alone."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.update_scene\" → \"studio_update_scene\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":320,"tokensAfter":320},{"name":"studio.update_sticker","fixed":{"name":"studio_update_sticker","description":"Move, resize, rotate, fade or re-time a sticker overlay. Only the fields you pass change. Positions are normalized 0-1 of the frame.","inputSchema":{"type":"object","required":["project_id","overlay_id"],"properties":{"x":{"type":"number","description":"0-1"},"y":{"type":"number","description":"0-1"},"scale":{"type":"number","description":"0.1-10, 1 is native size."},"opacity":{"type":"number","description":"0-1."},"animation":{"enum":["none","shake","glow","pulse","bounce"],"type":"string"},"overlay_id":{"type":"string"},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_ms":{"type":"integer"},"rotation_deg":{"type":"number","description":"-360 to 360."},"start_time_ms":{"type":"integer"}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.update_sticker\" → \"studio_update_sticker\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":230,"tokensAfter":230},{"name":"studio.update_text_overlay","fixed":{"name":"studio_update_text_overlay","description":"Update an existing text clip: content, position, timing.","inputSchema":{"type":"object","required":["project_id","clip_id"],"properties":{"clip_id":{"type":"string","description":"Text clip id (from get_composition)."},"content":{"type":"string"},"position":{"type":"object","properties":{"x":{"type":"number"},"y":{"type":"number"}},"additionalProperties":false},"project_id":{"type":"string","description":"The studio project (vp_…) this tool acts on."},"duration_ms":{"type":"number"},"start_time_ms":{"type":"number"}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"studio.update_text_overlay\" → \"studio_update_text_overlay\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":169,"tokensAfter":169},{"name":"video.export","fixed":{"name":"video_export","description":"Follow one video export to completion and return its download links. Start the export with video.export_create, then pass the job_id it returned (without job_id this follows the project's newest export). Polls the durable job row every 3s through queued -> processing -> uploading -> completed. On completion it returns download_path (for a session signed in to this workspace) and, when signed links are enabled, download_url (plus captions_url when there is an SRT): a link valid for 24 hours that ","inputSchema":{"type":"object","required":["project_id"],"properties":{"job_id":{"type":"string","description":"The export job (vx_...) video.export_create returned. Omit it to follow the project's newest export."},"project_id":{"type":"string","description":"The studio project (vp_...) the export belongs to."}},"additionalProperties":false},"annotations":{"readOnlyHint":true}},"changes":[{"kind":"name","detail":"Renamed \"video.export\" → \"video_export\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":246,"tokensAfter":246},{"name":"video.export_cancel","fixed":{"name":"video_export_cancel","description":"Stop a video export that is queued or rendering. The job ends as failed with \"Cancelled by user.\", anything it charged is refunded, the render stops, and any partial file is deleted. Cancelling a job that already finished or failed changes nothing (cancelled: false), so a repeat is safe. Refuses an unknown job (SIMPLER_STUDIO_EXPORT_JOB_NOT_FOUND): video.export_create returns job ids. Refuses a read-only connection (SIMPLER_MCP_SCOPE_READONLY): connect again with the 'mcp' scope. Free.","inputSchema":{"type":"object","required":["job_id"],"properties":{"job_id":{"type":"string","description":"The export job (vx_...) to stop."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"video.export_cancel\" → \"video_export_cancel\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":199,"tokensAfter":199},{"name":"video.export_create","fixed":{"name":"video_export_create","description":"Start rendering a studio project to a file: a video, a GIF, the sound mix alone or one poster still (format). Returns job_id immediately; call video.export with job_id to follow progress and receive a download link valid for 24 hours. Refuses, naming the fix, when the project is not exportable, the preset's aspect differs from the project, or the tenant's active-job or daily limit is reached. format picks the file (mp4_h264_aac, webm_vp9_opus, gif, m4a_aac, wav_pcm, poster_jpg; default mp4_h264_","inputSchema":{"type":"object","required":["project_id"],"properties":{"format":{"enum":["mp4_h264_aac","webm_vp9_opus","gif","m4a_aac","wav_pcm","poster_jpg"],"type":"string","description":"The output file; default mp4_h264_aac. mp4_h264_aac: H.264 video with AAC sound; plays everywhere and takes the platform presets. webm_vp9_opus: VP9 video with Opus sound; smaller files for the web, but encoding takes about three times as long. gif: Silent looping animation, up to 30 s, at most 480 px on the long side and 15 fps. Captions can burn in; no SRT file. m4a_aac: The sound mix only, as AAC 192 kbps; video is never decoded. Captions come only as an SRT file. wav_pcm: The sound mix only, uncompressed 16-bit 48 kHz; video is never decoded. Captions come only as an SRT file. poster_jpg: One JPEG still of the finished frame at the chosen time, with text and burned captions; no SRT file."},"preset":{"enum":["original","youtube_16x9","shorts_9x16","tiktok_9x16","reels_9x16","feed_1x1","feed_4x5","x_16x9","shopee_1x1","lazada_1x1","tiktok_shop_9x16"],"type":"string","description":"Encoder profile; default original (the project's own size and aspect). A preset whose aspect differs from the project is refused."},"project_id":{"type":"string","description":"The studio project (vp_...) to render."},"caption_mode":{"enum":["none","burned_in","srt","both"],"type":"string","description":"none (default), burned_in (drawn into the video), srt (a separate captions file), or both."},"poster_time_ms":{"type":"integer","maximum":36000000,"minimum":0,"description":"Required with format \"poster_jpg\", refused with any other: the timeline millisecond to capture, from 0 to the timeline length minus 1."},"composition_version":{"type":"integer","minimum":0,"description":"Optional: the composition_version you reviewed (studio.get_composition). If a newer version is saved, the export is refused instead of rendering something you have not seen."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"video.export_create\" → \"video_export_create\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":702,"tokensAfter":702},{"name":"video.export_delete","fixed":{"name":"video_export_delete","description":"Delete a finished video export's files (the mp4 and any SRT) now, before the automatic deletion 30 days after the export finished. Every download link for that job stops working at once; the job record stays, so the daily export limit still counts it. Refuses a job that is still rendering (SIMPLER_STUDIO_EXPORT_DELETE_ACTIVE): stop it with video.export_cancel, or follow it to the end with video.export, then delete it. Refuses an unknown job (SIMPLER_STUDIO_EXPORT_JOB_NOT_FOUND): video.export_cre","inputSchema":{"type":"object","required":["job_id"],"properties":{"job_id":{"type":"string","description":"The export job (vx_...) whose files to delete."}},"additionalProperties":false},"annotations":{"readOnlyHint":false}},"changes":[{"kind":"name","detail":"Renamed \"video.export_delete\" → \"video_export_delete\" (letters, digits, _ and - only; max 64). Keep the old name as an alias for a release if clients already call it.","verify":true}],"tokensBefore":205,"tokensAfter":205}],"toolsChanged":104,"tokensSaved":0,"todo":[{"tool":"agent_note_mark_read","params":["body","status","subject","from_tenant_id","consent_grant_id","related_entity_id","related_entity_type"]},{"tool":"agent_note_send_cross_tenant","params":["body","status","subject","from_tenant_id","consent_grant_id","related_entity_id","related_entity_type"]},{"tool":"contact_create","params":["name","tags","email","phone","source","status","address","company","last_contact_at"]},{"tool":"contact_note_create","params":["content","contact_id"]},{"tool":"contact_update","params":["name","tags","email","phone","source","status","address","company","last_contact_at"]},{"tool":"contact_upsert","params":["name","tags","email","phone","source","status","address","company","last_contact_at"]},{"tool":"desk_surface_create","params":["h","w","x","y","kind","z_index","widget_key","content_ref"]},{"tool":"desk_surface_update","params":["h","w","x","y","kind","z_index","widget_key","content_ref"]},{"tool":"edit_project_create","params":["name","width","height","version","canvas_json","thumbnail_key"]},{"tool":"edit_project_update","params":["name","width","height","version","canvas_json","thumbnail_key"]},{"tool":"expense_aggregate","params":["function"]},{"tool":"expense_approve","params":["notes","status","vendor","currency","ocr_json","tax_type","tags_json","approved_at","approved_by","category_id","receipt_key","rejected_at","amount_cents","expense_date","submitted_at","tax_rate_bps","recurrence_id","reimbursed_at","subtotal_cents","rejection_reason","tax_amount_cents"]},{"tool":"expense_budget_create","params":["currency","is_active","category_id","alert_at_pct","monthly_limit_cents"]},{"tool":"expense_budget_update","params":["currency","is_active","category_id","alert_at_pct","monthly_limit_cents"]},{"tool":"expense_category_create","params":["icon","name","slug","color","is_default","sort_order"]},{"tool":"expense_category_delete","params":["icon","name","slug","color","is_default","sort_order"]},{"tool":"expense_category_ensure_defaults","params":["icon","name","slug","color","is_default","sort_order"]},{"tool":"expense_category_update","params":["icon","name","slug","color","is_default","sort_order"]},{"tool":"expense_create","params":["notes","status","vendor","currency","ocr_json","tax_type","tags_json","approved_at","approved_by","category_id","receipt_key","rejected_at","amount_cents","expense_date","submitted_at","tax_rate_bps","recurrence_id","reimbursed_at","subtotal_cents","rejection_reason","tax_amount_cents"]},{"tool":"expense_delete","params":["notes","status","vendor","currency","ocr_json","tax_type","tags_json","approved_at","approved_by","category_id","receipt_key","rejected_at","amount_cents","expense_date","submitted_at","tax_rate_bps","recurrence_id","reimbursed_at","subtotal_cents","rejection_reason","tax_amount_cents"]},{"tool":"expense_recurrence_create","params":["notes","vendor","currency","next_due","frequency","is_active","category_id","day_of_week","amount_cents","day_of_month","total_generated","last_generated_at"]},{"tool":"expense_recurrence_update","params":["notes","vendor","currency","next_due","frequency","is_active","category_id","day_of_week","amount_cents","day_of_month","total_generated","last_generated_at"]},{"tool":"expense_reimburse","params":["notes","status","vendor","currency","ocr_json","tax_type","tags_json","approved_at","approved_by","category_id","receipt_key","rejected_at","amount_cents","expense_date","submitted_at","tax_rate_bps","recurrence_id","reimbursed_at","subtotal_cents","rejection_reason","tax_amount_cents"]},{"tool":"expense_reject","params":["notes","status","vendor","currency","ocr_json","tax_type","tags_json","approved_at","approved_by","category_id","receipt_key","rejected_at","amount_cents","expense_date","submitted_at","tax_rate_bps","recurrence_id","reimbursed_at","subtotal_cents","rejection_reason","tax_amount_cents"]},{"tool":"expense_submit","params":["notes","status","vendor","currency","ocr_json","tax_type","tags_json","approved_at","approved_by","category_id","receipt_key","rejected_at","amount_cents","expense_date","submitted_at","tax_rate_bps","recurrence_id","reimbursed_at","subtotal_cents","rejection_reason","tax_amount_cents"]},{"tool":"expense_update","params":["notes","status","vendor","currency","ocr_json","tax_type","tags_json","approved_at","approved_by","category_id","receipt_key","rejected_at","amount_cents","expense_date","submitted_at","tax_rate_bps","recurrence_id","reimbursed_at","subtotal_cents","rejection_reason","tax_amount_cents"]},{"tool":"image_generate_for_edit","params":["image_size"]},{"tool":"interaction_create","params":["summary","contact_id","source_ref","occurred_at","interaction_type"]},{"tool":"interaction_upsert","params":["summary","contact_id","source_ref","occurred_at","interaction_type"]},{"tool":"invoice_adjustment_delete","params":["reason","currency","invoice_id","occurred_at","reason_code","amount_cents","credit_outcome","adjustment_type"]},{"tool":"invoice_adjustment_record","params":["reason","currency","invoice_id","occurred_at","reason_code","amount_cents","credit_outcome","adjustment_type"]},{"tool":"invoice_client_create","params":["name","email","notes","phone","address","company","contact_id","message_language","reminder_cadence"]},{"tool":"invoice_client_link_tenant","params":["name","email","notes","phone","address","company","contact_id","message_language","reminder_cadence"]},{"tool":"invoice_client_update","params":["name","email","notes","phone","address","company","contact_id","message_language","reminder_cadence"]},{"tool":"invoice_client_upsert","params":["name","email","notes","phone","address","company","contact_id","message_language","reminder_cadence"]},{"tool":"invoice_create","params":["notes","status","paid_at","sent_at","currency","due_date","cash_sale","client_id","source_id","issue_date","source_type","tax_rate_bps","payment_terms","invoice_number","payment_method","fx_rate_to_sgd_ppm"]},{"tool":"invoice_item_create","params":["unit","position","tax_code","invoice_id","description","unit_price_cents"]},{"tool":"invoice_item_delete","params":["unit","position","quantity","tax_code","invoice_id","description","discount_bps","unit_price_cents"]},{"tool":"invoice_item_update","params":["unit","position","tax_code","invoice_id","description","unit_price_cents"]},{"tool":"invoice_payment_delete","params":["note","method","currency","invoice_id","occurred_at","amount_cents"]},{"tool":"invoice_payment_record","params":["note","method","currency","invoice_id","occurred_at","amount_cents"]},{"tool":"invoice_recurring_create","params":["notes","title","ends_on","currency","next_due","client_id","frequency","is_active","items_json","tax_scheme","day_of_week","day_of_month","max_invoices","tax_rate_bps","total_generated","last_generated_at"]},{"tool":"invoice_recurring_update","params":["notes","title","ends_on","currency","next_due","client_id","frequency","is_active","items_json","tax_scheme","day_of_week","day_of_month","max_invoices","tax_rate_bps","total_generated","last_generated_at"]},{"tool":"invoice_update","params":["notes","status","paid_at","sent_at","currency","due_date","cash_sale","client_id","source_id","issue_date","tax_scheme","source_type","tax_rate_bps","payment_terms","invoice_number","payment_method","fx_rate_to_sgd_ppm","prices_include_tax"]},{"tool":"market_signal_create","params":["notes","status","product","currency","deadline","location","quantity","price_cents","signal_type"]},{"tool":"market_signal_update","params":["notes","status","product","currency","deadline","location","quantity","price_cents","signal_type"]},{"tool":"myinvois_customer_create","params":["brn","tin","city","name","email","notes","address","sst_number","state_code","postal_code","country_code","contact_number"]},{"tool":"myinvois_customer_update","params":["brn","tin","city","name","email","notes","address","sst_number","state_code","postal_code","country_code","contact_number"]},{"tool":"myinvois_invoice_generate","params":["status","currency","buyer_tin","lhdn_uuid","tax_cents","buyer_name","issue_date","customer_id","total_cents","invoice_data","invoice_type","lhdn_long_id","invoice_number","subtotal_cents","lhdn_submitted_at","reference_invoice_id"]},{"tool":"myinvois_invoice_record_submission","params":["status","currency","buyer_tin","lhdn_uuid","tax_cents","buyer_name","issue_date","customer_id","total_cents","invoice_data","invoice_type","lhdn_long_id","invoice_number","subtotal_cents","lhdn_submitted_at","reference_invoice_id"]},{"tool":"myinvois_settings_save","params":["brn","tin","city","email","scope","msic_code","sst_number","state_code","postal_code","company_name","country_code","address_line1","address_line2","contact_number","invoice_prefix","default_currency","business_activity","tourism_tax_number","default_payment_terms"]},{"tool":"myinvois_template_create","params":["name","notes","description","payment_terms","line_items_json"]},{"tool":"myinvois_template_update","params":["name","notes","description","payment_terms","line_items_json"]},{"tool":"quote_convert","params":["notes","status","currency","client_id","issue_date","tax_scheme","valid_until","quote_number","tax_rate_bps","prices_include_tax","converted_invoice_id"]},{"tool":"quote_create","params":["notes","status","currency","client_id","issue_date","valid_until","quote_number","tax_rate_bps","converted_invoice_id"]},{"tool":"quote_item_create","params":["unit","position","quote_id","tax_code","description","unit_price_cents"]},{"tool":"quote_item_delete","params":["unit","position","quantity","quote_id","tax_code","description","discount_bps","unit_price_cents"]},{"tool":"quote_item_update","params":["unit","position","quote_id","tax_code","description","unit_price_cents"]},{"tool":"quote_update","params":["notes","status","currency","client_id","issue_date","tax_scheme","valid_until","quote_number","tax_rate_bps","prices_include_tax","converted_invoice_id"]},{"tool":"sales_settings_upsert","params":["scope","tax_id","timezone","bank_name","tax_scheme","bank_account","quote_prefix","business_name","business_email","business_phone","invoice_prefix","template_style","business_address","default_currency","brand_accent_color","payment_terms_days","brand_primary_color","default_tax_rate_bps","payment_instructions"]},{"tool":"settlement_proposal_accept","params":["status","currency","accepted_at","description","amount_cents","payer_tenant_id","consent_grant_id","related_entity_id","related_entity_type"]},{"tool":"settlement_proposal_propose_cross_tenant","params":["status","currency","accepted_at","description","amount_cents","payer_tenant_id","consent_grant_id","related_entity_id","related_entity_type"]},{"tool":"spreadsheet_file_create","params":["tags","title","r2_key","source","ai_prompt","file_size","folder_id","row_count","deleted_at","sheet_count","sheet_names","column_count","analysis_summary","last_analyzed_at","original_filename"]},{"tool":"spreadsheet_file_delete","params":["tags","title","r2_key","source","ai_prompt","file_size","folder_id","row_count","deleted_at","sheet_count","sheet_names","column_count","analysis_summary","last_analyzed_at","original_filename"]},{"tool":"spreadsheet_file_update","params":["tags","title","r2_key","source","ai_prompt","file_size","folder_id","row_count","deleted_at","sheet_count","sheet_names","column_count","analysis_summary","last_analyzed_at","original_filename"]},{"tool":"spreadsheet_folder_create","params":["name","color"]},{"tool":"spreadsheet_folder_update","params":["name","color"]},{"tool":"sticky_note_create","params":["color","width","height","content","z_index","position_x","position_y","triggers_at"]},{"tool":"sticky_note_dismiss","params":["color","width","height","content","z_index","position_x","position_y","triggers_at"]},{"tool":"sticky_note_due","params":["color","width","height","content","z_index","position_x","position_y","triggers_at"]},{"tool":"sticky_note_update","params":["color","width","height","content","z_index","position_x","position_y","triggers_at"]},{"tool":"stock_item_adjust","params":["unit","category","is_active","expiry_date","sell_price_cents","cost_per_unit_cents","low_stock_threshold"]},{"tool":"stock_item_create","params":["name","unit","notes","category","quantity","is_active","expiry_date","sell_price_cents","cost_per_unit_cents","low_stock_threshold"]},{"tool":"stock_item_delete","params":["name","unit","notes","category","quantity","is_active","expiry_date","sell_price_cents","cost_per_unit_cents","low_stock_threshold"]},{"tool":"stock_item_update","params":["name","unit","notes","category","quantity","is_active","expiry_date","sell_price_cents","cost_per_unit_cents","low_stock_threshold"]},{"tool":"studio_add_audio_to_timeline","params":["track","asset_id","start_time_ms"]},{"tool":"studio_add_clip_to_timeline","params":["asset_id","start_time_ms"]},{"tool":"studio_add_sticker","params":["animation","duration_ms","start_time_ms"]},{"tool":"studio_add_text_overlay","params":["preset","animation","start_time_ms"]},{"tool":"studio_adjust_audio_levels","params":["track"]},{"tool":"studio_analyze_scene","params":["scene_id"]},{"tool":"studio_color_grade","params":["contrast","scene_id","brightness","saturation","temperature"]},{"tool":"studio_delete_element","params":["element_id"]},{"tool":"studio_duplicate_clip","params":["clip_id"]},{"tool":"studio_edit_caption","params":["text","end_ms","start_ms","caption_id"]},{"tool":"studio_lock_track","params":["track","locked"]},{"tool":"studio_reorder_scenes","params":["scene_id"]},{"tool":"studio_request_regeneration","params":["scene_id"]},{"tool":"studio_set_config","params":["aspect","resolution","caption_preset"]},{"tool":"studio_set_ducking","params":["enabled"]},{"tool":"studio_set_transition","params":["type","scene_id","direction","duration_ms"]},{"tool":"studio_trim_clip","params":["edge","clip_id"]},{"tool":"studio_trim_scene","params":["scene_id"]},{"tool":"studio_update_clip","params":["muted","volume","clip_id","start_time_ms"]},{"tool":"studio_update_scene","params":["scene_id"]},{"tool":"studio_update_sticker","params":["animation","overlay_id","duration_ms","start_time_ms"]},{"tool":"studio_update_text_overlay","params":["content","position","duration_ms","start_time_ms"]},{"tool":"studio_character_create","params":["name","color_tag","description","prompt_suffix","reference_image_key"]},{"tool":"studio_character_update","params":["name","color_tag","description","prompt_suffix","reference_image_key"]},{"tool":"studio_environment_create","params":["name","color_tag","description","prompt_suffix","reference_image_key"]},{"tool":"studio_environment_update","params":["name","color_tag","description","prompt_suffix","reference_image_key"]},{"tool":"studio_project_create","params":["name","status","thumbnail_key"]},{"tool":"studio_project_update","params":["name","status","thumbnail_key"]},{"tool":"team_assignment_accept","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_add_photo","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_complete","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_create","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_decline","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_delete","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_reassign","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_start","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_undo_reassign","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"team_assignment_update","params":["title","due_at","status","priority","started_at","accepted_at","artifact_id","assigned_to","declined_at","description","photos_json","completed_at","reassigned_at","reassigned_by","attachment_key","decline_reason","attachment_name","attachment_type","previous_status","completion_notes","previous_assignee"]},{"tool":"user_interest_upsert","params":["interest"]},{"tool":"user_setting_upsert","params":["key","value","value_type"]},{"tool":"workflow_create","params":["name","is_active","role_name","description","next_run_at","trigger_type","imported_from","definition_json"]},{"tool":"workflow_prepared_outputs","params":["name","is_active","role_name","description","next_run_at","trigger_type","imported_from","definition_json"]},{"tool":"workflow_update","params":["name","is_active","role_name","description","next_run_at","trigger_type","imported_from","definition_json"]}],"note":"Deterministic fixes. Changes marked verify need a human look before shipping (renames, inferred annotations, inferred types, removed text). Parameter descriptions in `todo` need to be written by someone who knows the API."}