@pinepaper.studio/mcp-server 1.6.8 → 1.6.10
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.
- package/README.de.md +1 -1
- package/README.es.md +1 -1
- package/README.fr.md +1 -1
- package/README.hi.md +1 -1
- package/README.ja.md +1 -1
- package/README.ko.md +1 -1
- package/README.md +207 -7
- package/README.pt-BR.md +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/agent/export-manager.d.ts.map +1 -1
- package/dist/agent/export-manager.js +5 -3
- package/dist/agent/export-manager.js.map +1 -1
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/puppeteer-controller.d.ts +86 -2
- package/dist/browser/puppeteer-controller.d.ts.map +1 -1
- package/dist/browser/puppeteer-controller.js +271 -63
- package/dist/browser/puppeteer-controller.js.map +1 -1
- package/dist/cli.js +12 -0
- package/dist/cli.js.map +1 -1
- package/dist/design/design-systems.d.ts +105 -7
- package/dist/design/design-systems.d.ts.map +1 -1
- package/dist/design/design-systems.js +210 -16
- package/dist/design/design-systems.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/definitions.js +94 -6
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/engine-methods.d.ts.map +1 -1
- package/dist/tools/engine-methods.js +2 -0
- package/dist/tools/engine-methods.js.map +1 -1
- package/dist/tools/engine-surface.d.ts +30 -0
- package/dist/tools/engine-surface.d.ts.map +1 -0
- package/dist/tools/engine-surface.js +1188 -0
- package/dist/tools/engine-surface.js.map +1 -0
- package/dist/tools/handlers/font.d.ts.map +1 -1
- package/dist/tools/handlers/font.js +77 -10
- package/dist/tools/handlers/font.js.map +1 -1
- package/dist/tools/handlers.d.ts +16 -0
- package/dist/tools/handlers.d.ts.map +1 -1
- package/dist/tools/handlers.js +329 -12
- package/dist/tools/handlers.js.map +1 -1
- package/dist/tools/minimal-descriptions.d.ts.map +1 -1
- package/dist/tools/minimal-descriptions.js +2 -1
- package/dist/tools/minimal-descriptions.js.map +1 -1
- package/dist/tools/toolkits.d.ts.map +1 -1
- package/dist/tools/toolkits.js +1 -0
- package/dist/tools/toolkits.js.map +1 -1
- package/dist/types/code-generator.d.ts +10 -0
- package/dist/types/code-generator.d.ts.map +1 -1
- package/dist/types/code-generator.js +437 -87
- package/dist/types/code-generator.js.map +1 -1
- package/dist/types/schemas.d.ts +119 -74
- package/dist/types/schemas.d.ts.map +1 -1
- package/dist/types/schemas.js +62 -6
- package/dist/types/schemas.js.map +1 -1
- package/manifest.json +5 -1
- package/package.json +5 -2
|
@@ -351,7 +351,9 @@ USE WHEN:
|
|
|
351
351
|
IMPORTANT:
|
|
352
352
|
- Save the returned itemId! You need it for pinepaper_add_relation, pinepaper_modify_item, pinepaper_delete_item, etc.
|
|
353
353
|
- If there's a welcome template on the canvas, use pinepaper_clear_canvas first
|
|
354
|
-
- Position defaults to canvas center (400, 300). Use position: {x, y} or [x, y] (both forms accepted) to place elsewhere.
|
|
354
|
+
- Position defaults to canvas center (400, 300). Use position: {x, y} or [x, y] (both forms accepted) to place elsewhere. Position is the item's bounding-box CENTRE, not its top-left corner.
|
|
355
|
+
- ROTATION is a property, at creation: properties: { rotation: 45 } — degrees, clockwise, about the item's own centre. It does not need a second call, and pinepaper_update_item takes the same property afterwards; keyframe_animate and the driven_by relation both animate it. A radial figure — a sunburst of thin triangles, a clock face, a compass rose — is N creates at rotation: i * (360/N), which is what pinepaper_agent_batch_execute is for. Nothing here needs to be faked with arcs or chevrons.
|
|
356
|
+
⚠️ Rotation applies at create and in keyframes; it is BAKED INTO THE GEOMETRY, so reading item.rotation back returns 0. Verify by geometry or pixels, never by re-reading the property — a 12-ray sunburst has 12 visibly different rays that every report rotation 0, and checking the property is how you conclude a working feature does nothing.
|
|
355
357
|
- DO NOT take screenshots after every create operation. Trust the API response - if it returns success, the item was created. Take ONE screenshot at the end to verify the final result.
|
|
356
358
|
|
|
357
359
|
⚠️ COMPLEX CHARACTERS/ILLUSTRATIONS:
|
|
@@ -2264,13 +2266,15 @@ An AESTHETIC STYLE is a look: Bauhaus, Art Deco, Swiss typographic, De Stijl, va
|
|
|
2264
2266
|
|
|
2265
2267
|
- list_systems / get_system: the licensed systems and their tokens, filterable by token type.
|
|
2266
2268
|
- list_easings: every motion curve across every system as a named easing, with its licence and whether it was authored here. Use these to make a motion match a system it has to sit beside.
|
|
2267
|
-
-
|
|
2269
|
+
- list_motion: those curves AND the duration scale sitting beside them — the other half of a motion token, published nowhere until now. A curve is a shape; a motion is a shape over a length, and matching a system's easing while inventing its timing gets only the easy half right. Durations come back in ms and in seconds, because every animation tool here takes seconds. These are the licensed SYSTEMS' motion scales: an aesthetic style carries no upstream motion data, so pair one of these with a style rather than expecting the style to name its own.
|
|
2270
|
+
- list_styles: every style with its TOKENS — palette, background, font stacks, type sizes and any variants — derived by composing it, so they are what the generator will actually draw. Read these to hand-build in a style rather than reverse-engineering its colours.
|
|
2271
|
+
- compose: build a titled scene in a style. Layout is decided in this server by a pure function, so 'compose' with draw:false returns the scene and the ops as DATA and draws nothing — read it before committing, the same way pinepaper_story's distill works. Element boxes are converted to the centre anchor app.create wants, and text carries its alignment, so a left-aligned headline starts at its x instead of centring on it.
|
|
2268
2272
|
|
|
2269
2273
|
A partial composition reports as a failure with the elements that did not create, rather than as a success with half a poster.`,
|
|
2270
2274
|
inputSchema: {
|
|
2271
2275
|
type: 'object',
|
|
2272
2276
|
properties: {
|
|
2273
|
-
action: { type: 'string', enum: ['list_systems', 'get_system', 'list_easings', 'list_styles', 'compose'], description: 'Which design-system operation.' },
|
|
2277
|
+
action: { type: 'string', enum: ['list_systems', 'get_system', 'list_easings', 'list_motion', 'list_styles', 'compose'], description: 'Which design-system operation.' },
|
|
2274
2278
|
systemId: { type: 'string', description: "get_system: e.g. 'material_3', 'ibm_carbon', 'uswds'. Call list_systems for ids." },
|
|
2275
2279
|
tokenType: { type: 'string', enum: ['dimension', 'duration', 'cubicBezier', 'color', 'fontFamily', 'fontWeight', 'number', 'shadow', 'grid'], description: 'get_system: return only tokens of this DTCG type.' },
|
|
2276
2280
|
style: { type: 'string', description: "compose: the aesthetic style, e.g. 'bauhaus_geometric'. Call list_styles for the composable ones." },
|
|
@@ -3111,10 +3115,23 @@ USE WHEN:
|
|
|
3111
3115
|
- Custom item registration
|
|
3112
3116
|
- Accessing PinePaper APIs directly
|
|
3113
3117
|
- Prototyping new features
|
|
3118
|
+
- A scene whose repetition is the point: dozens of items built in a loop. This is the cheapest route by an order of magnitude — a 63-item animated scene is ~4k characters here against ~147k as keyframe tool calls — because the loop collapses the repetition instead of restating it per item. Synchronous build work is bounded by the governor's 4s deadline, which such a scene fits easily.
|
|
3119
|
+
|
|
3120
|
+
ENGINE SEMANTICS THAT BITE RAW-JS AUTHORS. The tool schemas protect tool callers from these; writing JS opts out of that protection, and each of these fails SILENTLY:
|
|
3121
|
+
- app.create() returns a Paper.js item whose .id is a NUMBER. The id every tool and relation wants is the STRING at item.data.registryId ('item_1'). Passing the number to addAnimation throws "Cannot create property 'data' on number".
|
|
3122
|
+
- app.create() with an unknown type returns undefined and creates nothing — 'rect' is not 'rectangle'. The schema catches that for tool callers; a loop does not.
|
|
3123
|
+
- app.addAnimation(id, keyframeArray, options) takes a real ARRAY. A JSON string installs nothing, silently, and the scene exports frozen. It also returns without error on an id that does not resolve.
|
|
3124
|
+
- Keyframes live at item.data.keyframes. app.keyframeItems is not the live store, so probing it reports false negatives.
|
|
3125
|
+
- transformOrigin is IGNORED by the keyframe engine: rotation always pivots on the item centre. For true joint pivots use pinepaper_rigging bones, or keyframe computed x/y per joint.
|
|
3126
|
+
- Bone angles are RADIANS in the engine and relative to the parent, though pinepaper_rigging converts degrees for you — that conversion is the tool's, not the engine's.
|
|
3127
|
+
|
|
3128
|
+
WHAT THE CHEAP ROUTE COSTS, so the choice is deliberate: items built and animated in raw JS carry NO behavioral record. No graph edge, nothing SMIL or widget export can lift, nothing the ontology can read — the scene renders and cannot describe itself. Relations are the canonical behavior surface here, so for motion that is a function of time prefer pinepaper_add_relation with 'time_expression' (still ~5.7x cheaper than keyframes), and for a whole group moving together staggered_with or wave_through. Reach for a loop when the geometry is procedural, not merely to avoid the typing.
|
|
3114
3129
|
|
|
3115
3130
|
AVAILABLE GLOBALS:
|
|
3116
3131
|
- app: PinePaper application instance
|
|
3117
3132
|
- app.create(type, params): Create items
|
|
3133
|
+
- app.addAnimation(itemId, keyframes, options): Attach keyframe TRACKS programmatically — the other half of a procedural scene, and what makes the loop route cheap: the keyframes are generated, not typed. Accepts an id or the item itself; options.timeUnits ('seconds'|'ms') overrides the ms auto-detect, which long-form timelines need
|
|
3134
|
+
- app.animate(item, params): Attach a LOOP animation (pulse, bounce, wobble, …) — the other kind of motion, and not what addAnimation does
|
|
3118
3135
|
- app.addRelation(sourceId, targetId, type, params): Add relations
|
|
3119
3136
|
- app.registerRelationRule(name, definition): Register custom relations
|
|
3120
3137
|
- app.itemRegistry: Access all registered items
|
|
@@ -3233,6 +3250,15 @@ USE WHEN:
|
|
|
3233
3250
|
- "first fade in, then rotate, then fade out"
|
|
3234
3251
|
- Any animation with specific timing or sequential stages
|
|
3235
3252
|
|
|
3253
|
+
BEFORE AUTHORING DENSE TRACKS — three routes are cheaper, and two of them keep the scene describable. Measured on the same 63-item, 32-keyframes-each scene:
|
|
3254
|
+
- Motion that is a FUNCTION OF TIME (bob, pulse, sweep, phase-offset wave) → pinepaper_add_relation with relationType 'time_expression'. One call per property instead of one keyframe per sample: 26k characters against 147k. It also survives into the design graph, save/reload and widget export, which keyframes generated from a formula do not describe.
|
|
3255
|
+
- PER-ITEM VARIATION across many items → staggered_with or wave_through express the whole group in ONE call. That collapses the 63-fold repetition a per-item time_expression still pays for.
|
|
3256
|
+
- GENUINELY PROCEDURAL scenes → pinepaper_execute_custom_code with app.create + app.addAnimation (which attaches keyframe tracks programmatically, so the keyframes are generated rather than typed), at 4k characters for the same scene — cheapest by an order of magnitude, because a loop collapses the repetition entirely. The trade is real and worth making deliberately: raw JS leaves NO behavioral record — no graph edge, nothing SMIL or widget export can lift, nothing the ontology can read. Scene building fits the governor's 4s synchronous deadline easily (it is the injected loop guards, not the 10s async-tail budget, that bites a build loop).
|
|
3257
|
+
|
|
3258
|
+
- TEXT THAT CHANGES OVER TIME -> 'content' IS keyframeable, and it is DISCRETE: it holds the current word until the next keyframe and does not tween. One thing worth knowing before you build a cascade out of it. A cascade of N words is N keyframes you have to write out, which is the reason to prefer the call below rather than any limitation. For the common case prefer pinepaper_execute_custom_code with app.textSequence(itemOrId, words, { interval }): ONE item cycling its words in one call, against one item per word with opacity tracks faking the cut. It returns { ok: false, reason } on an id it cannot resolve.
|
|
3259
|
+
|
|
3260
|
+
Keyframes are for motion that is AUTHORED, not computed. A track of hundreds of entries produced from a formula is the signal that one of the three above is the right route.
|
|
3261
|
+
|
|
3236
3262
|
ANIMATABLE PROPERTIES:
|
|
3237
3263
|
- position: [x, y] array (a { x, y } object is also accepted and normalized)
|
|
3238
3264
|
- x, y: Individual coordinates
|
|
@@ -3547,7 +3573,9 @@ Requires maskType and keyframes array.`,
|
|
|
3547
3573
|
idempotentHint: true,
|
|
3548
3574
|
openWorldHint: false,
|
|
3549
3575
|
},
|
|
3550
|
-
description: `[Utility]
|
|
3576
|
+
description: `[Utility] MASK properties only, despite the name: which properties each MASK TYPE can animate.
|
|
3577
|
+
|
|
3578
|
+
If you are asking what you can animate on a canvas ITEM, this is the wrong tool and its name will not tell you so. The item-side list lives in pinepaper_keyframe_animate's ANIMATABLE PROPERTIES section, and two entries there are DISCRETE rather than interpolated - content and blendMode hold their value until the next keyframe instead of blending toward it. Treating content as tweenable is what makes every word in a cycler appear half a beat early.`,
|
|
3551
3579
|
inputSchema: {
|
|
3552
3580
|
type: 'object',
|
|
3553
3581
|
properties: {},
|
|
@@ -6330,9 +6358,15 @@ ACTIONS:
|
|
|
6330
6358
|
idempotentHint: false,
|
|
6331
6359
|
openWorldHint: false,
|
|
6332
6360
|
},
|
|
6333
|
-
description: `
|
|
6361
|
+
description: `Fonts: which ones you can USE, and authoring new ones.
|
|
6334
6362
|
|
|
6335
|
-
|
|
6363
|
+
Most of this tool is glyph authoring — drawing a typeface. If you just want to know what font families are available to set on a text item, that is ONE action:
|
|
6364
|
+
|
|
6365
|
+
- list_available — { category?, loadedOnly? } every family the studio can render, with whether its file is loaded yet
|
|
6366
|
+
- check — { name, text? } is this family really there, and can it draw THIS string? Pass the text: a font with four glyphs is installed and will still render most characters in the fallback face, and that second answer is the one that decides the pixels. Do NOT rely on document.fonts.check() — it returns true for a family that does not exist.
|
|
6367
|
+
- fallbacks — {} which text items on the canvas are silently drawing in something other than the family they asked for
|
|
6368
|
+
|
|
6369
|
+
ACTIONS (authoring):
|
|
6336
6370
|
- show_studio — {} open the Font Studio UI
|
|
6337
6371
|
- set_name — { name } name the in-progress font
|
|
6338
6372
|
- get_required_chars — { set?: string } list chars to draw (default: minimum)
|
|
@@ -6355,6 +6389,7 @@ ACTIONS:
|
|
|
6355
6389
|
action: {
|
|
6356
6390
|
type: 'string',
|
|
6357
6391
|
enum: [
|
|
6392
|
+
'list_available', 'check', 'fallbacks',
|
|
6358
6393
|
'show_studio', 'set_name', 'get_required_chars', 'get_status',
|
|
6359
6394
|
'create_glyph', 'create_space', 'remove_glyph', 'set_metrics',
|
|
6360
6395
|
'export', 'load_into_document', 'export_data', 'import_data',
|
|
@@ -6362,6 +6397,9 @@ ACTIONS:
|
|
|
6362
6397
|
],
|
|
6363
6398
|
description: 'Font Studio action',
|
|
6364
6399
|
},
|
|
6400
|
+
text: { type: 'string', description: 'check: the string you intend to draw. Without it you learn only whether the family is installed, not whether it has glyphs for your characters.' },
|
|
6401
|
+
category: { type: 'string', description: 'list_available: narrow to one category (e.g. display, serif, mono).' },
|
|
6402
|
+
loadedOnly: { type: 'boolean', description: 'list_available: only families whose font file has already loaded. Unloaded ones still render once the studio fetches them.' },
|
|
6365
6403
|
name: { type: 'string', description: 'Font name (set_name)' },
|
|
6366
6404
|
set: { type: 'string', description: 'Character set name (get_required_chars; default "minimum")' },
|
|
6367
6405
|
character: { type: 'string', description: 'Single-character glyph identifier (create_glyph, remove_glyph)' },
|
|
@@ -7593,6 +7631,50 @@ EXAMPLE — Animated sky scene with timed reveals:
|
|
|
7593
7631
|
required: ['operations'],
|
|
7594
7632
|
},
|
|
7595
7633
|
},
|
|
7634
|
+
{
|
|
7635
|
+
name: 'pinepaper_export_store',
|
|
7636
|
+
annotations: {
|
|
7637
|
+
title: 'Export Store',
|
|
7638
|
+
readOnlyHint: false,
|
|
7639
|
+
destructiveHint: true,
|
|
7640
|
+
idempotentHint: false,
|
|
7641
|
+
openWorldHint: false,
|
|
7642
|
+
},
|
|
7643
|
+
description: `Recover an export the studio is still holding.
|
|
7644
|
+
|
|
7645
|
+
A long export is not returned inline — the studio keeps the encoded file and pinepaper_agent_export pages it out to a file for you. When that PAGING half fails, the render has already succeeded and the bytes are sitting in the store. This is how you go and get them, instead of re-rendering something that already exists.
|
|
7646
|
+
|
|
7647
|
+
ACTIONS:
|
|
7648
|
+
- list: {} — what is held, newest first: {id, format, size, createdAt}. The id IS the stored filename and the store is origin-persistent, so ids survive a page reload; an id lost with a dropped tool result is recoverable here.
|
|
7649
|
+
- save: {exportId} — page it out to a file and release it once the file is whole. Returns a filePath, the same shape agent_export returns. Safe to repeat: reading a held export mutates nothing, so a failed save can simply be run again.
|
|
7650
|
+
- release: {exportId} — drop it WITHOUT saving. The store is the only copy; this is not undoable.
|
|
7651
|
+
|
|
7652
|
+
WHEN TO REACH FOR IT:
|
|
7653
|
+
- An export failed with "Failed to write data to data pipe". That is the browser's transport refusing to carry one oversized value, not the encoder failing — the bytes may already be in the store. Call 'list' BEFORE re-rendering.
|
|
7654
|
+
- An export error named an exportId and a byte count reached. That error is a true promise: call 'save' with the id.
|
|
7655
|
+
|
|
7656
|
+
RETENTION, AND THE ONE WAY TO LOSE WORK:
|
|
7657
|
+
Held exports last until released, or until a LATER export needs the space — and eviction drops the OLDEST first. Rendering five chunks and paging them out at the end can evict chunk 1 to make room for chunk 5. Save or release each export before starting the next one. An id dropped this way usually says "evicted" rather than "not found", including after a page reload — but that record is best-effort browser storage, so a miss can still be genuinely ambiguous. Treat the absence of "evicted" as "unknown", never as proof the export never existed.`,
|
|
7658
|
+
inputSchema: {
|
|
7659
|
+
type: 'object',
|
|
7660
|
+
properties: {
|
|
7661
|
+
action: {
|
|
7662
|
+
type: 'string',
|
|
7663
|
+
enum: ['list', 'save', 'release'],
|
|
7664
|
+
description: "'list' (what is held), 'save' (page it to a file, then release), 'release' (drop it unsaved).",
|
|
7665
|
+
},
|
|
7666
|
+
exportId: {
|
|
7667
|
+
type: 'string',
|
|
7668
|
+
description: "Required for 'save' and 'release'. From a failed export's error, or from 'list'.",
|
|
7669
|
+
},
|
|
7670
|
+
platform: {
|
|
7671
|
+
type: 'string',
|
|
7672
|
+
description: "save: a label for the saved filename only. Defaults to 'recovered'.",
|
|
7673
|
+
},
|
|
7674
|
+
},
|
|
7675
|
+
required: ['action'],
|
|
7676
|
+
},
|
|
7677
|
+
},
|
|
7596
7678
|
{
|
|
7597
7679
|
name: 'pinepaper_agent_export',
|
|
7598
7680
|
annotations: {
|
|
@@ -7604,6 +7686,12 @@ EXAMPLE — Animated sky scene with timed reveals:
|
|
|
7604
7686
|
},
|
|
7605
7687
|
description: `Smart export with automatic format detection and platform optimization.
|
|
7606
7688
|
|
|
7689
|
+
WHAT quality ACTUALLY SETS, because it bundles three things and only one of them is "quality": draft = 15fps / 72dpi / compression 0.6, standard = 30fps / 150dpi / 0.85, high = 60fps / 300dpi / 0.95. So asking for high DOUBLES the frame count and the render time against standard. Override either half directly - fps for frame rate, scale for resolution - and leave quality to mean compression.
|
|
7690
|
+
|
|
7691
|
+
DOES THIS SURVIVE EXPORT? It is a PATH fact, not a per-feature one, so the rule is short and does not go stale as capabilities land: IF IT TICKS INSIDE THE ENGINE'S UPDATE LOOP, IT EXPORTS. Loop animations, relations, keyframes, generators and camera moves all do - measured frame-by-frame, not assumed. What does NOT survive is anything driven by the wall clock outside that loop, or anything on the realtime recorder path.
|
|
7692
|
+
|
|
7693
|
+
If exported frames look frozen, check the SAMPLING before believing it: a loop animation at animationSpeed 1 has a one-second period, so two frames a whole second apart are identical by design. Compare frames that are not a whole number of periods apart.
|
|
7694
|
+
|
|
7607
7695
|
PLATFORMS & OPTIMAL FORMATS:
|
|
7608
7696
|
| Platform | Dimensions | Static | Animated |
|
|
7609
7697
|
|-----------------|------------|--------|----------|
|