@pinepaper.studio/mcp-server 1.6.14 → 1.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.de.md +6 -1
  2. package/README.es.md +6 -1
  3. package/README.fr.md +6 -1
  4. package/README.hi.md +6 -1
  5. package/README.ja.md +6 -1
  6. package/README.ko.md +6 -1
  7. package/README.md +130 -6
  8. package/README.pt-BR.md +6 -1
  9. package/README.zh-CN.md +6 -1
  10. package/assets/brand/pinepaper-lockup-horizontal-white.svg +1 -0
  11. package/assets/brand/pinepaper-lockup-horizontal.svg +1 -0
  12. package/assets/edit-whenever.svg +1 -1
  13. package/dist/browser/puppeteer-controller.js +2 -2
  14. package/dist/browser/puppeteer-controller.js.map +1 -1
  15. package/dist/build-info.json +2 -2
  16. package/dist/ontology/upstream-vocabulary.d.ts +2 -2
  17. package/dist/ontology/upstream-vocabulary.js +2 -2
  18. package/dist/tools/definitions.d.ts.map +1 -1
  19. package/dist/tools/definitions.js +107 -9
  20. package/dist/tools/definitions.js.map +1 -1
  21. package/dist/tools/engine-methods.d.ts.map +1 -1
  22. package/dist/tools/engine-methods.js +1 -0
  23. package/dist/tools/engine-methods.js.map +1 -1
  24. package/dist/tools/engine-surface.d.ts +1 -1
  25. package/dist/tools/engine-surface.d.ts.map +1 -1
  26. package/dist/tools/engine-surface.js +59 -12
  27. package/dist/tools/engine-surface.js.map +1 -1
  28. package/dist/tools/handlers/camera.d.ts.map +1 -1
  29. package/dist/tools/handlers/camera.js +11 -0
  30. package/dist/tools/handlers/camera.js.map +1 -1
  31. package/dist/tools/handlers/generate.d.ts +324 -0
  32. package/dist/tools/handlers/generate.d.ts.map +1 -0
  33. package/dist/tools/handlers/generate.js +264 -0
  34. package/dist/tools/handlers/generate.js.map +1 -0
  35. package/dist/tools/handlers.d.ts.map +1 -1
  36. package/dist/tools/handlers.js +39 -4
  37. package/dist/tools/handlers.js.map +1 -1
  38. package/dist/tools/minimal-descriptions.d.ts.map +1 -1
  39. package/dist/tools/minimal-descriptions.js +3 -0
  40. package/dist/tools/minimal-descriptions.js.map +1 -1
  41. package/dist/tools/shape-params.d.ts +2 -2
  42. package/dist/tools/shape-params.d.ts.map +1 -1
  43. package/dist/tools/shape-params.js +20 -3
  44. package/dist/tools/shape-params.js.map +1 -1
  45. package/dist/tools/stick-vocabulary.d.ts +1 -1
  46. package/dist/tools/stick-vocabulary.js +1 -1
  47. package/dist/tools/toolkits.d.ts.map +1 -1
  48. package/dist/tools/toolkits.js +3 -0
  49. package/dist/tools/toolkits.js.map +1 -1
  50. package/dist/types/code-generator.d.ts +15 -0
  51. package/dist/types/code-generator.d.ts.map +1 -1
  52. package/dist/types/code-generator.js +339 -29
  53. package/dist/types/code-generator.js.map +1 -1
  54. package/dist/types/schemas.d.ts +532 -82
  55. package/dist/types/schemas.d.ts.map +1 -1
  56. package/dist/types/schemas.js +26 -10
  57. package/dist/types/schemas.js.map +1 -1
  58. package/dist/vendor/design/design-style.js +1 -1
  59. package/icon.png +0 -0
  60. package/icon.svg +1 -0
  61. package/manifest.json +15 -2
  62. package/package.json +3 -1
@@ -1755,6 +1755,91 @@ EXAMPLE: { checks: ["contrast", "flash"], duration: 6, fps: 20 }`,
1755
1755
  },
1756
1756
  },
1757
1757
  },
1758
+ {
1759
+ name: 'pinepaper_generate',
1760
+ annotations: { title: 'Generate Image (cloud)', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true },
1761
+ description: `Generate images with an AI model in the PinePaper cloud, and optionally place the first one on the canvas. CHARGED per output — price it first with pinepaper_generate_estimate (free).
1762
+
1763
+ Needs a service-account key in PINEPAPER_API_KEY (pp_sa_…, render:create scope). It is an internal beta enabled per organisation: without it the result says "not enabled for your organisation", and nothing is charged. Every refusal says whether anything was charged.
1764
+
1765
+ - model or useCase: a model id from pinepaper_generate_estimate {listModels: true}, or a use case and the cloud picks its default (hero stills, plates, text in the image, character-consistent edits).
1766
+ - input: { prompt, aspect ('16:9' | '9:16' | '1:1' | '4:5'), n (1-4), seed?, negativePrompt?, imageUrls? (https references for edit models) }.
1767
+ - brief / designRef: always passed on; the cloud records each generation against the design.
1768
+ - place: 'cover' (fill the canvas, centred; default when true) or 'contain' — places the first asset: an image as an image item, a video as a video layer (with its durationSeconds). The fit uses the asset's delivered size, not the requested aspect.
1769
+ - timeoutSec: how long to wait (default 180). A job that outlives it keeps running and is only charged if it delivers; collect it with pinepaper_generate_status.
1770
+
1771
+ WORDS ARE VECTOR: give words as input.text [{content, role?: headline|subhead|caption|cta|label, font?}]. The model makes a text-free plate; the words come back as textLayers (plus a hint), and with place they are added as real, editable text over it: headline top-left (Anton by default), subhead below, caption / cta bottom-left, label top-right, inside title-safe, each contrast-checked against the plate (a dark scrim is added only where it falls short of WCAG 4.5:1, or 3:1 for large text). Their item ids are in placement.text.
1772
+
1773
+ The result: assets [{assetId, ref, width, height}] — the sizes the files HAVE (a model may pick its own size for an aspect), chargedUsd, and any model notes (e.g. a SynthID watermark) to pass on.`,
1774
+ inputSchema: {
1775
+ type: 'object',
1776
+ properties: {
1777
+ model: { type: 'string' },
1778
+ useCase: { type: 'string' },
1779
+ input: {
1780
+ type: 'object',
1781
+ properties: {
1782
+ prompt: { type: 'string' },
1783
+ aspect: { type: 'string', enum: ['16:9', '9:16', '1:1', '4:5'] },
1784
+ n: { type: 'integer', minimum: 1, maximum: 4 },
1785
+ seed: { type: 'integer' },
1786
+ negativePrompt: { type: 'string' },
1787
+ imageUrls: { type: 'array', items: { type: 'string' }, maxItems: 14, description: 'Reference / start-frame images: https URLs, or your own generated images as their ref "/scene-assets/<id>" (chain a still into a video or a character edit).' },
1788
+ seconds: { type: 'number', exclusiveMinimum: 0, maximum: 60, description: 'Video models: clip length; each model allows its own lengths (listModels).' },
1789
+ text: { type: 'array', maxItems: 12, items: { type: 'object', properties: { content: { type: 'string' }, role: { type: 'string', enum: ['headline', 'subhead', 'caption', 'cta', 'label'] }, font: { type: 'string' } }, required: ['content'] }, description: 'Words for the design. The model never draws them: the plate comes back text-free and these return as textLayers — with place, laid out as real PinePaper text.' },
1790
+ allowText: { type: 'boolean', description: 'Let the MODEL draw text — eval-tier models only (e.g. ideogram-v3); refused (422) elsewhere. Prefer text.' },
1791
+ },
1792
+ required: ['prompt'],
1793
+ },
1794
+ brief: { type: 'string' },
1795
+ designRef: { type: 'object', properties: { kind: { type: 'string' }, id: { type: 'string' } }, required: ['kind', 'id'] },
1796
+ place: { anyOf: [{ type: 'boolean' }, { type: 'string', enum: ['cover', 'contain'] }] },
1797
+ timeoutSec: { type: 'integer', minimum: 10, maximum: 1800 },
1798
+ },
1799
+ required: ['input'],
1800
+ },
1801
+ },
1802
+ {
1803
+ name: 'pinepaper_generate_estimate',
1804
+ annotations: { title: 'Estimate Generation (free)', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true },
1805
+ description: `Free: price a pinepaper_generate call before making it, or list the models. { listModels: true } returns every model you may use with its price per output, use cases, licence notes and the defaults per use case; { model | useCase, input } returns the estimate for that request. Needs PINEPAPER_API_KEY, like pinepaper_generate.`,
1806
+ inputSchema: {
1807
+ type: 'object',
1808
+ properties: {
1809
+ model: { type: 'string' },
1810
+ useCase: { type: 'string' },
1811
+ input: {
1812
+ type: 'object',
1813
+ properties: {
1814
+ prompt: { type: 'string' },
1815
+ aspect: { type: 'string', enum: ['16:9', '9:16', '1:1', '4:5'] },
1816
+ n: { type: 'integer', minimum: 1, maximum: 4 },
1817
+ seed: { type: 'integer' },
1818
+ negativePrompt: { type: 'string' },
1819
+ imageUrls: { type: 'array', items: { type: 'string' }, maxItems: 14, description: 'Reference / start-frame images: https URLs, or your own generated images as their ref "/scene-assets/<id>" (chain a still into a video or a character edit).' },
1820
+ seconds: { type: 'number', exclusiveMinimum: 0, maximum: 60, description: 'Video models: clip length; each model allows its own lengths (listModels).' },
1821
+ text: { type: 'array', maxItems: 12, items: { type: 'object', properties: { content: { type: 'string' }, role: { type: 'string', enum: ['headline', 'subhead', 'caption', 'cta', 'label'] }, font: { type: 'string' } }, required: ['content'] }, description: 'Words for the design. The model never draws them: the plate comes back text-free and these return as textLayers — with place, laid out as real PinePaper text.' },
1822
+ allowText: { type: 'boolean', description: 'Let the MODEL draw text — eval-tier models only (e.g. ideogram-v3); refused (422) elsewhere. Prefer text.' },
1823
+ },
1824
+ required: ['prompt'],
1825
+ },
1826
+ listModels: { type: 'boolean' },
1827
+ },
1828
+ },
1829
+ },
1830
+ {
1831
+ name: 'pinepaper_generate_status',
1832
+ annotations: { title: 'Generation Status', readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
1833
+ description: `Collect a pinepaper_generate job that outlived its timeoutSec: its status, and when done its assets and chargedUsd. place: 'cover' | 'contain' places the first image, as pinepaper_generate does.`,
1834
+ inputSchema: {
1835
+ type: 'object',
1836
+ properties: {
1837
+ jobId: { type: 'string' },
1838
+ place: { anyOf: [{ type: 'boolean' }, { type: 'string', enum: ['cover', 'contain'] }] },
1839
+ },
1840
+ required: ['jobId'],
1841
+ },
1842
+ },
1758
1843
  {
1759
1844
  name: 'pinepaper_render_batch',
1760
1845
  annotations: {
@@ -2043,15 +2128,18 @@ EXAMPLE: { times: [0, 0.5, 1, 1.5, 2], seed: 42 }`,
2043
2128
  description: `Bring VIDEO and AUDIO onto the canvas from a URL, and control playback. Uploaded video is a first-class canvas item (drag, scale, animate, export like any shape); audio rides the timeline.
2044
2129
 
2045
2130
  ACTIONS:
2046
- - upload_video: { url (required), position?, scale?, timeOffset? (timeline start, s), clipInPoint?, clipOutPoint? (trim, s) } → { id, duration, width, height, name }
2047
- - upload_audio: { url (required), volume? (0–1, default 1), loop?, muted?, timeOffset? } → { id, registryId, duration, name }. Pass loop EXPLICITLY — loop:true for a music bed, loop:false for a voice-over or sting; when omitted, the studio's own default applies and differs between builds.
2131
+ - upload_video: { url (required), position?, scale?, timeOffset? (timeline start, s) or atPlayhead?: true (start at the current playback time), clipInPoint?, clipOutPoint? (trim, s) } → { id, duration, width, height, name }
2132
+ - upload_audio: { url (required), volume? (0–1, default 1), loop?, muted?, timeOffset? or atPlayhead? } → { id, registryId, duration, name }. Pass loop EXPLICITLY — loop:true for a music bed, loop:false for a voice-over or sting; when omitted, the studio's own default applies and differs between builds.
2048
2133
  - list: → all media [{ id, kind, duration, … }]
2049
2134
  - remove: { id } → removed boolean
2050
2135
  - set_playback_rate: { id, rate (0.25–4) } → applies to video or audio
2051
- - set_clip: { id, inPoint, outPoint (media-time s, outPoint > inPoint) } → re-trim an ALREADY-uploaded clip (video or audio)
2136
+ - set_clip: { id, inPoint, outPoint (media-time s, outPoint > inPoint), timeOffset? (canvas s: also MOVE the clip to start there) } → re-trim an ALREADY-uploaded clip (video or audio). A studio that cannot move clips refuses timeOffset rather than ignoring it.
2137
+ - split: { id, at (canvas s) } → cut a clip in two: { leftId, rightId, leftMediaId, rightMediaId, at }. The first half keeps its id. Refused (not claimed) where the studio cannot split or the time is outside the clip.
2138
+ - add_transition: { fromItemId (ends at the cut), toItemId (starts at it), transition?: { type: 'crossfade'|'dip', seconds, color } } → the same in preview and export. Split a clip, then transition between its halves, or join two clips that meet.
2139
+ - For an IMAGE on the timeline, use pinepaper_import_image with bornAt / ttl (seconds on screen).
2052
2140
 
2053
2141
  NOTES:
2054
- - URL-based: the URL is fetched in the browser, so it must be reachable + CORS-permitted.
2142
+ - The server fetches the URL (or reads a local path / data: URL) and hands the page the bytes, so it does not need to be CORS-permitted.
2055
2143
  - Use the returned id with set_playback_rate / set_clip / remove, and (for video) modify/animate it like any item.
2056
2144
 
2057
2145
  VIDEO EDITING:
@@ -2065,14 +2153,16 @@ EXAMPLE: { action: 'upload_video', url: 'https://…/clip.mp4', scale: 0.5, time
2065
2153
  inputSchema: {
2066
2154
  type: 'object',
2067
2155
  properties: {
2068
- action: { type: 'string', enum: ['upload_video', 'upload_audio', 'list', 'remove', 'set_playback_rate', 'set_clip', 'set_time_remap', 'speed_ramp', 'match_cut', 'apply_track_matte', 'stop_live_matte'], description: "Media action" },
2156
+ action: { type: 'string', enum: ['upload_video', 'upload_audio', 'list', 'remove', 'set_playback_rate', 'set_clip', 'split', 'add_transition', 'set_time_remap', 'speed_ramp', 'match_cut', 'apply_track_matte', 'stop_live_matte'], description: "Media action" },
2157
+ transition: { type: 'object', properties: { type: { type: 'string', enum: ['crossfade', 'dip'] }, seconds: { type: 'number', exclusiveMinimum: 0, maximum: 10 }, color: { type: 'string' } }, description: "add_transition: { type ('crossfade' default | 'dip'), seconds, color (dip) }." },
2158
+ atPlayhead: { type: 'boolean', description: 'upload_video / upload_audio: start at the current playback time (instead of timeOffset).' },
2069
2159
  remapTrack: { type: 'array', items: { type: 'object', properties: { time: { type: 'number' }, value: { type: 'number' }, easing: { type: 'string' } }, required: ['time', 'value'] }, description: 'set_time_remap: canvas-seconds → source-seconds curve (≥2 points). null clears the remap.' },
2070
2160
  segments: { type: 'array', items: { type: 'object', properties: { duration: { type: 'number' }, speed: { type: 'number' } }, required: ['duration', 'speed'] }, description: 'speed_ramp: consecutive {duration, speed} segments. speed 0 = freeze frame, negative = reverse.' },
2071
2161
  fromItemId: { type: 'string', description: 'match_cut: outgoing shot item id.' },
2072
2162
  toItemId: { type: 'string', description: 'match_cut: incoming shot item id.' },
2073
2163
  subject: { type: 'string', enum: ['detect', 'bounds'], description: "match_cut: subject source — 'detect' (on-device DETR, needs consent on first use) or 'bounds'." },
2074
2164
  label: { type: 'string', description: 'match_cut: narrow detection to one class ("person") when the biggest object is not the subject.' },
2075
- at: { type: 'number', description: 'match_cut: cut time in seconds (default 0).' },
2165
+ at: { type: 'number', description: 'match_cut: cut time in seconds (default 0). split: the canvas time to cut at (required).' },
2076
2166
  settle: { type: 'number', description: 'match_cut: seconds to relax to natural framing (default 0.8).' },
2077
2167
  fade: { type: 'number', description: 'match_cut: crossfade seconds; 0 = hard cut (default 0.12).' },
2078
2168
  consent: { type: 'boolean', description: 'match_cut: allow the detection model download (a first run without it returns needsConsent instead of downloading silently).' },
@@ -2089,7 +2179,7 @@ EXAMPLE: { action: 'upload_video', url: 'https://…/clip.mp4', scale: 0.5, time
2089
2179
  outPoint: { type: 'number', description: 'Clip out-point (media-time s, > inPoint) — set_clip.' },
2090
2180
  position: { type: 'object', properties: { x: { type: 'number' }, y: { type: 'number' } }, description: 'Canvas position — upload_video.' },
2091
2181
  scale: { type: 'number', description: 'Scale factor — upload_video.' },
2092
- timeOffset: { type: 'number', description: 'Timeline start offset (s) — upload_video / upload_audio.' },
2182
+ timeOffset: { type: 'number', description: 'Timeline start offset (s) — upload_video / upload_audio; set_clip: move the clip to start here.' },
2093
2183
  clipInPoint: { type: 'number', description: 'Trim in-point (s) — upload_video.' },
2094
2184
  clipOutPoint: { type: 'number', description: 'Trim out-point (s) — upload_video.' },
2095
2185
  volume: { type: 'number', description: 'Volume 0–1 (default 1) — upload_audio.' },
@@ -3571,6 +3661,10 @@ EXAMPLE — Reveal at 2s, skip first second of keyframe data, end at 4s of keyfr
3571
3661
  type: 'number',
3572
3662
  description: 'Stop at N seconds into the keyframe data (tail trim). Default: lastKeyframeTime.',
3573
3663
  },
3664
+ append: {
3665
+ type: 'boolean',
3666
+ description: 'Add these keys to the item\'s existing track (same-time keys merge, new values win) instead of replacing it — e.g. a button press after an entrance. Without it, a second call replaces the track. result.track says whether the track was appended to or replaced; studios without merge replace, and say so.',
3667
+ },
3574
3668
  },
3575
3669
  required: ['itemId', 'keyframes'],
3576
3670
  },
@@ -4901,7 +4995,9 @@ ACTIONS:
4901
4995
  - update_from_instance: Promote an instance's edits back into the master. Params: instanceId
4902
4996
  - detach: Make an instance an ordinary independent item. Params: instanceId
4903
4997
 
4904
- Overrides are the point: an instance can differ from its master and still receive master updates.`,
4998
+ Overrides are the point: an instance can differ from its master and still receive master updates.
4999
+
5000
+ Instances carry the master's LOOK (geometry, style, text), not its animation: instantiate and sync copy what the master is, not its keyframes or animations. To animate instances, animate each one (keyframe_animate on its instanceId, or stagger across them).`,
4905
5001
  inputSchema: {
4906
5002
  type: 'object',
4907
5003
  properties: {
@@ -8046,9 +8142,11 @@ VERIFY MOTION BEFORE YOU RENDER. An export takes seconds to minutes and shows yo
8046
8142
  maxBytes: { type: 'integer', minimum: 1, description: 'Upload size budget (zip for html5-ad, HTML for playable). html5-ad default 150000 (Google Ads display); set your network figure.' },
8047
8143
  },
8048
8144
  },
8049
- transparent: { type: 'boolean', description: 'apng only: keep the alpha channel (the default); false fills the background colour.' },
8145
+ transparent: { type: 'boolean', description: 'apng: keep the alpha channel (the default; false fills the background colour). webm: true writes a VP9 alpha channel where the studio supports it; result.video.alpha confirms it, and fidelity says when it did not. Not for mp4 / gif.' },
8146
+ alphaQuantizer: { type: 'integer', minimum: 0, maximum: 63, description: 'webm with transparent: alpha-stream quality, 0 = lossless … 63.' },
8050
8147
  loop: { anyOf: [{ type: 'boolean' }, { type: 'integer', minimum: 0, maximum: 1000 }], description: 'gif / apng: true = loop forever, false / 0 / 1 = play once, n = play n times (email clients often want a few plays, not forever). Refused on video formats — see SEAMLESS LOOPS.' },
8051
8148
  broadcast: { type: 'boolean', description: 'mp4 only: broadcast-safe — BT.709, limited range (16-235), tagged bt709, constant bitrate with an 8 Mbps floor at 720p+ (4 below). result.video reports what the encoder did.' },
8149
+ deterministic: { type: 'boolean', description: 'mp4 / webm: byte-identical files for the same scene (pins the container timestamps), so exports can be checksummed or cached by hash.' },
8052
8150
  broadcastHeadroom: { type: 'integer', minimum: 0, maximum: 40, description: 'mp4 with broadcast only, default 12: luma codes kept clear at both ends, so encoder ringing stays legal (12 measured clean on hard edges; 4 = ~100x fewer out-of-range samples, 12 = ~1000x; never zero). Fidelity raises luma_out_of_range with the legaliser command when any remain.' },
8053
8151
  bitrate: { type: 'integer', minimum: 100000, maximum: 200000000, description: 'mp4 / webm: target bits/s, replacing the quality-derived one. The browser encoder treats it as a CEILING; result.video.achievedBitrate is what it produced.' },
8054
8152
  minBitrate: { type: 'integer', minimum: 100000, maximum: 200000000, description: 'mp4 / webm: a floor; a miss is a fidelity warning naming the re-encode a delivery spec needs.' },