io.github.lixiaolin94/skia-studio
NPM · SKIA-STUDIO-MCP · SCANNED AUG 3
MCP bridge to a live Skia Studio tab: AGSL/SkSL shader authoring, graph editing, screenshots.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score →
Supply Chain Security87
- No malware found by supply-chain analysis.Pass
- Only part of the dependency tree could be resolved (96 of 100), so this covers what we could see, not the whole tree.Partial
- No install/post-install scripts declared.Pass
- Only part of the dependency tree could be resolved (96 of 100), so this covers what we could see, not the whole tree. View diagnostics → Partial
Provenance & Transparency6
- Repository check failed: the declared repository URL returned HTTP 404. See how to fix → View diagnostics → Fail
- Provenance check failed: no build-provenance attestation is published. See how to fix → View diagnostics → Fail
- License check failed: the license (PolyForm-Noncommercial-1.0.0) isn't a recognized OSI-approved license. See how to fix → Fail
- Actively maintained (last published 19 days ago).Pass
- Security-disclosure policy not yet verified: we couldn't inspect the source repository.Unverified
Schema Quality & AI Usability78
- 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 4548 tokens (~239/item across 19 items; 17 tools + 2 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management23
- Stability observed for 7 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage98
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 93% of tool parameters carry a description.Partial
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
Add this component to your MCP client. Where a client-specific snippet is available, pick your client below and copy it straight into your config; otherwise use the connection detail shown.
npm · skia-studio-mcp
claude mcp add lixiaolin94-skia-studio -- npx -y skia-studio-mcp
codex mcp add lixiaolin94-skia-studio -- npx -y skia-studio-mcp
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"lixiaolin94-skia-studio": {
"type": "local",
"command": [
"npx",
"-y",
"skia-studio-mcp"
],
"enabled": true
}
}
} openclaw mcp add lixiaolin94-skia-studio --command npx --arg -y --arg skia-studio-mcp
mcp_servers:
lixiaolin94-skia-studio:
command: "npx"
args: ["-y", "skia-studio-mcp"] {
"mcpServers": {
"lixiaolin94-skia-studio": {
"command": "npx",
"args": [
"-y",
"skia-studio-mcp"
]
}
}
} Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 2 Aug 26 +42
- Provenance: unverified → fail ▼ security
- Install scripts: unverified → pass ▲ security
- Known CVEs: unverified → partial ▲ security
- Malware scan: unverified → pass ▲ security
- License: unverified → fail ▼ functional
- Stability: unverified → 0.20 ▲ functional
- Maintenance: unverified → pass ▲ functional
- MCP protocol: unverified → pass ▲ functional
- Schema quality: unverified → excellent ▲ functional
- Dependency health: unverified → partial ▲ functional
- Licence: PolyForm-Noncommercial-1.0.0 functional
- 31 Jul 26 −25
- We updated how we score, so this day's move reflects our rubric, not a change to the server See what changed → functional
- 27 Jul 26 44
First indexed and scored.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 3 Aug 2026 · Analysed npm/[email protected]
Provenance none
Ecosystem: npm · Outcome: none
Dependencies 96 packages
96 packages in the resolved dependency tree · 96 deprecated · 29 stale.
The dependency tree was only partially resolved, so these counts may be incomplete.
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability.
connect_nodes Wire one node into another (or disconnect) ~330
Connect producerNodeId's output into one of consumerNodeId's input slots, or disconnect that slot by passing producerNodeId: null. slot depends on the consumer's type (see read_project's project.graph.nodes[].type / childSlots): for a runtimeShader consumer, slot is the uniformName of one of its childSlots (e.g. a shader declared as 'uniform shader background;' has slot 'background' — see that node's childSlots array; there is no slot for a runtimeShader that declares none); for shaderPass, blur, or dropShadow, slot is always 'input'; for renderPass, slot is always 'shader'; for feedback, slot is always 'source'. imageShader/videoShader/cameraShader/audioShader/colorShader are producer-only leaf nodes and can never be the consumer of this tool (they have no input slot at all — connecting into them always errors). Any node type may be the producer for any compatible slot (no type restriction on producerNodeId). This call is rejected up front (nothing is changed) if it would create a cycle in the shader graph — the error names the offending producer/consumer pair so you can inspect the current graph via read_project before retrying. Returns the consumer node's updated JSON plus verifierErrors if applicable.
| Name | Type | Req | Description |
|---|---|---|---|
| consumerNodeId | string | yes | The node whose input slot is being wired (or disconnected). |
| producerNodeId | — | yes | The node whose output feeds the slot, or null to disconnect the slot. |
| slot | string | yes | Which input slot on consumerNodeId; see description for the rules per node type. |
No output schema declared.
No examples provided.
create_node Create a new graph node ~417
Add a new node to the shader graph, using the same default property values Skia Studio's own "New" menus use for each type (e.g. blur defaults to sigmaX/sigmaY 4 with clamp tiling, dropShadow to a soft dark shadow, renderPass to scale 1). The new node starts unconnected (no inputNodeId/shaderNodeId/sourceNodeId, no child slot bindings) — use connect_nodes afterwards to wire it into the graph, and it is not graph.mainNodeId unless you call set_main_node. runtimeShader/shaderPass ignore props (they get a blank starter AGSL template); use update_shader_source to set real source and set_uniform for uniform values once the node exists. For every other type, props lets you override the initial fields for that type in one call instead of create_node then update_node — same field names/ranges as update_node's patch (e.g. { sigmaX: 8 } for blur, { color: [1, 0, 0, 1] } for colorShader, { assetId: 'asset_xyz' } for imageShader). name defaults to a numbered placeholder like the UI does ("Blur 2", "Shader Pass 3", ...) if omitted. Returns the full created node JSON, plus verifierErrors if the project is no longer fully valid afterwards (e.g. an imageShader created with an assetId is fine, but the graph overall might already have unrelated issues — this is informational, not a rejection of this call).
| Name | Type | Req | Description |
|---|---|---|---|
| name | string | — | Optional display name. Omit for a UI-style default name like "Blur 2". |
| props | object | — | Optional initial property overrides for the chosen type (ignored/rejected for runtimeShader/shaderPass). Same fields and validation as update_node's patch for that node type — see that tool's descrip… |
| type | string | yes | Graph node type. See read_project's project.graph.nodes[].type for these same 11 strings in context. |
No output schema declared.
No examples provided.
delete_animation Delete an Animation ~50
Permanently remove an Animation from project.animations. Rejected if animationId does not exist.
| Name | Type | Req | Description |
|---|---|---|---|
| animationId | string | yes | Animation id, e.g. from read_project's project.animations[].id. |
No output schema declared.
No examples provided.
delete_binding Delete a Binding ~50
Permanently remove a Binding from project.bindings. Rejected if bindingId does not exist.
| Name | Type | Req | Description |
|---|---|---|---|
| bindingId | string | yes | Binding id, e.g. from read_project's project.bindings[].id. |
No output schema declared.
No examples provided.
delete_node Delete a graph node ~249
Permanently remove a node from the graph. Refuses if nodeId is graph.mainNodeId (call set_main_node with a different node first) or if it is the only remaining node in the graph. If other nodes/bindings/animation tracks still reference this node (as a child shader slot, inputNodeId, shaderNodeId, sourceNodeId, a Binding target, or an Animation track target), the call is refused by default and the error lists every referrer so you can decide what to do; pass disconnectReferences: true to have this tool disconnect/remove all of those references first and then delete the node in the same call (this is the safe default to reach for once you have reviewed the referrer list, or immediately if you already know you want a clean removal). Returns { deleted, disconnected: { graphReferences, bindings, animationTracks } } describing what was touched, plus verifierErrors if applicable.
| Name | Type | Req | Description |
|---|---|---|---|
| disconnectReferences | boolean | — | Pass true to disconnect/remove all references to this node and delete it anyway. Default false (refuse if referenced). |
| nodeId | string | yes | Graph node id, e.g. from read_project's project.graph.nodes[].id. |
No output schema declared.
No examples provided.
import_asset Import an image/video/audio asset ~398
Add a media asset to project.assets, for later use as an imageShader/videoShader assetId (via create_node or update_node) or as an audio input source. Pass exactly one of filePath or dataBase64. filePath is read from disk by this mcp-server process itself — the file's bytes never pass through your context — and is the right choice for real media files (rejected if over 25MB; use the web UI's Assets panel for larger files); its mimeType is inferred from the extension, any mimeType argument is ignored in this branch. dataBase64 is for a small asset you generated yourself (e.g. a procedurally generated texture) and requires mimeType alongside it; decoded size is limited to ~2MB (this is a much smaller cap than filePath because these bytes DO pass through your context). Supported formats mirror docs/media-support-matrix.md: images png/jpg/jpeg/webp/avif/gif, video mp4/m4v/webm/mov, audio mp3/wav/ogg/m4a/aac/flac — an unsupported extension or mimeType is rejected with a clear message. Returns a summary { id, name, mimeType, approxByteLength } — never the asset bytes themselves (same summarization principle as read_project's project.assets[]).
| Name | Type | Req | Description |
|---|---|---|---|
| dataBase64 | string | — | Base64-encoded bytes for a small agent-generated asset. Requires mimeType. Up to ~2MB decoded. |
| filePath | string | — | Absolute path to a local file to import. Read by this mcp-server process, not sent through your context. Up to 25MB. |
| mimeType | string | — | Required with dataBase64. Ignored (inferred from the extension instead) with filePath. |
| name | string | — | Display name. Defaults to the file name (filePath) or a generic placeholder (dataBase64). |
No output schema declared.
No examples provided.
read_project Read Skia Studio project ~233
Read the Skia Studio project currently open in the connected browser tab: the full project document (graph nodes, shader sources, uniforms, bindings, controls, preview/canvas config — the same SkiaStudioProjectDocument the app itself uses), plus per-node compileStatus (idle | compiling | ok | error) and diagnostics (real Skia compiler errors/warnings with line/column, the same ones shown in the Inspector). project.assets[] is summarized: each asset's dataUrl (which can be several MB of inline base64) is stripped and replaced with { hasData, approxByteLength, mimeType } — this tool never returns binary asset data. Call this first to see the project shape before editing anything, and again after a change to confirm the resulting state. The result includes connectedTab.url — the page address of the tab you are bridged to; report it to the human so they know whether you are editing their local dev instance or the production site (the bridge serves one tab, last to connect wins). Takes no arguments. If no browser tab is connected, returns guidance on what URL to open.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
remove_asset Remove a media asset ~139
Permanently remove an asset from project.assets. If any imageShader/videoShader node, or audio/videoProgress input, still references this assetId, the call is refused by default and the error lists every referrer; pass disconnectReferences: true to have this tool null out those references first and then remove the asset in the same call. Returns { deleted, disconnected: { nodes, inputs } }.
| Name | Type | Req | Description |
|---|---|---|---|
| assetId | string | yes | Asset id, e.g. from read_project's project.assets[].id. |
| disconnectReferences | boolean | — | Pass true to clear all references to this asset and remove it anyway. Default false (refuse if referenced). |
No output schema declared.
No examples provided.
render_frame Render a PNG screenshot of the live preview ~329
Capture what the connected browser tab is currently rendering, as a PNG, so you can visually check your changes. Omit nodeId to capture the main preview output (the same frame the human sees). Pass nodeId to inspect one graph node's output instead: for a 'renderPass' node this reads its offscreen buffer directly without disturbing what the human is currently viewing; for any other node type this switches the tab's preview target to that node first (the human will see the preview switch in real time, like clicking that node in the UI) and then captures the main output. Pass restorePreview: true to switch the preview back to whatever it was before this call once the screenshot has been captured (the human still briefly sees it switch and switch back); omit it or pass false (the default) to leave the preview on that node afterwards. The image is always the tab's current canvas size (see project.preview.canvas from read_project) — there is no parameter to request a different size. Requires the previewer to be in single mode — graph mode (the previewer's node-graph view) does not host a live renderer, and this tool returns a clear error telling you to ask the human to switch the previewer to single mode.
| Name | Type | Req | Description |
|---|---|---|---|
| nodeId | string | — | Graph node id to preview instead of the main output. Optional. |
| restorePreview | boolean | — | When nodeId is given and refers to a non-renderPass node, restore the preview target to what it was before this call once the screenshot is captured, instead of leaving it switched to nodeId. Default… |
No output schema declared.
No examples provided.
set_main_node Set the graph's render output node ~127
Change graph.mainNodeId — the node whose output is the project's final rendered result (what render_frame with no nodeId, and the human's main preview, both show). nodeId must already exist in the graph (see read_project) but can be of any node type. This is also the only way to reassign the render output away from a node before calling delete_node on it (delete_node refuses to delete graph.mainNodeId directly). Returns the resulting mainNodeId plus verifierErrors if applicable.
| Name | Type | Req | Description |
|---|---|---|---|
| nodeId | string | yes | The node to make the new render output. |
No output schema declared.
No examples provided.
set_preview_node Switch (or restore) the live preview target ~189
Switch the connected browser tab's preview to show a specific graph node's output instead of the main render output — the human sees this switch happen in real time, exactly like clicking that node in the UI (this is the same mechanism render_frame uses internally when given a non-renderPass nodeId, but without taking a screenshot). Pass nodeId to switch to that node (it must already exist — see read_project for valid node ids, or this returns an error); omit nodeId to switch back to the main output. Useful paired with render_frame: e.g. switch the preview to a node so the human can see it, or call again with no nodeId afterwards to hand the view back to the main output. Returns the previewNodeId actually applied (null means main output).
| Name | Type | Req | Description |
|---|---|---|---|
| nodeId | string | — | Graph node id to preview. Omit to switch back to the main output. |
No output schema declared.
No examples provided.
set_uniform Set a shader uniform value ~236
Set the current value of one uniform on a runtimeShader node. nodeId + uniformName identify the target (uniformName as declared in the shader source; see read_project's project.graph.nodes[].uniforms for the node's uniform list — each entry's currentValue/defaultValue array length tells you the required component count for value). value is a flat number array matching that component count — e.g. [0.5] for a float, [1, 0, 0, 1] for a float4 or layout(color) uniform, [1] / [0] for a bool encoded as 1/0. This overwrites any existing binding on that uniform with a constant value and returns the value actually applied after the store commits (so you can confirm it took effect).
| Name | Type | Req | Description |
|---|---|---|---|
| nodeId | string | yes | Graph node id, e.g. from read_project's project.graph.nodes[].id. |
| uniformName | string | yes | Uniform name as declared in the shader source. |
| value | array | yes | Flat numeric components matching the uniform's component count (1 for scalar, up to 4 for vector/color). |
No output schema declared.
No examples provided.
trigger_animation Manually fire an Animation (for self-testing) ~222
Arm an Animation's tracks right now, as if its trigger event just happened — regardless of what trigger type it is actually configured with (click, onLoad, manual, ...). This lets you self-test an animation's visual effect: call this, then call render_frame one or more times (the animation plays in real time in the connected browser tab, same as CanvasKitRenderer's per-frame render loop) to see it progress toward its target values. Refuses with a clear error if the animation is disabled (call upsert_animation with enabled: true first), has no tracks, or if every one of its track targets is currently driven by an enabled Binding (bindings always take priority over animations — see docs/animation-model.md §2; use delete_binding or upsert_binding with enabled: false to free the target first). Also refuses if the previewer is in graph mode (same constraint as render_frame — no live renderer to arm tracks against).
| Name | Type | Req | Description |
|---|---|---|---|
| animationId | string | yes | Animation id, e.g. from read_project's project.animations[].id. |
No output schema declared.
No examples provided.
update_node Patch a graph node's own properties ~363
Change one or more of a node's own scalar/enum properties in place — NOT connections, source code, or uniform values (those are separate tools; see below). Which fields are legal depends on nodeId's type (see read_project's project.graph.nodes[].type): every type accepts name; blur accepts sigmaX/sigmaY/tileMode; dropShadow accepts dx/dy/sigmaX/sigmaY/color/mode; colorShader accepts color; renderPass accepts scale; feedback accepts clearOnResize; imageShader accepts assetId/tileModeX/tileModeY/filterMode; videoShader additionally accepts loop/muted/autoplay/playbackRate (0.25-2)/volume (0-1)/precise; cameraShader accepts tileModeX/tileModeY/filterMode/facingMode/resolution ('720p'|'1080p'|null)/inputId; audioShader accepts tileModeX/tileModeY/filterMode/resolution (256|512)/inputId. runtimeShader/shaderPass only accept name here. Rejected fields (with guidance to the right tool): source/uniforms (use update_shader_source / set_uniform), childSlots/inputNodeId/shaderNodeId/sourceNodeId (use connect_nodes). color arrays are 4 numbers in [0, 1] (linear, unmultiplied alpha), not 0-255. Unknown fields for the node's type are rejected with the exact supported-field list, not silently ignored. Returns the updated node JSON plus verifierErrors if applicable.
| Name | Type | Req | Description |
|---|---|---|---|
| nodeId | string | yes | Graph node id, e.g. from read_project's project.graph.nodes[].id. |
| patch | object | yes | Fields to change; see description for the legal set per node type. |
No output schema declared.
No examples provided.
update_shader_source Update shader source and get real compile diagnostics ~325
Replace the AGSL/SkSL source code of a runtimeShader graph node, then wait (up to 5s) for the real CanvasKit compiler to finish and return the resulting compileStatus (ok | error) and diagnostics — the exact compiler errors/warnings (with line/column) shown in the Skia Studio Inspector, not a mock. If compileStatus is 'error', read the diagnostics, fix the reported issues, and call this tool again with corrected code; the diagnostics are authoritative, do not guess blindly. If the error looks like a typical GLSL (Shadertoy-style) habit (sampler2D, texture()/texelFetch(), gl_FragCoord, mainImage, fwidth/dFdx/dFdy, precision qualifiers, #define, ...), the result includes a 'hint' field pointing out the SkSL equivalent and the skia-studio://guides/porting-glsl-to-sksl resource for the full porting rules — read that resource if you are porting from GLSL. nodeId must reference an existing runtimeShader node (see read_project — nodes of other types like imageShader/blur/renderPass have no editable source and will return an error). code is the full replacement source, not a diff/patch. This mutates the live project the human is viewing in real time.
| Name | Type | Req | Description |
|---|---|---|---|
| code | string | yes | Full replacement AGSL/SkSL source for the node. |
| nodeId | string | yes | Graph node id, e.g. from read_project's project.graph.nodes[].id. |
No output schema declared.
No examples provided.
upsert_animation Create or replace an event-triggered Animation ~344
Create a new Animation, or replace one entirely, in project.animations — the event-triggered parameter transitions described in docs/animation-model.md (spring/cubicBezier tracks armed by a gesture, onLoad, or manual trigger). Omit animation.id to create a new one; pass an existing project.animations[].id to replace that animation completely (this is a full-object replace, not a field patch — every field you omit reverts to this call's defaults, it does not merge with what was there before); an id that does not match any existing animation is rejected (to catch typos instead of silently creating a duplicate). Each track's target is either { type: 'uniform', nodeId, uniformName } (must exist — see read_project's project.graph.nodes[].uniforms) or { type: 'nodeProp', nodeId, propPath } (must be one of the closed set of numeric node properties in src/core/bindableNodeProps.ts — blur sigmaX/sigmaY, dropShadow dx/dy/sigmaX/sigmaY, renderPass scale). track.to (and track.from, if given) must be a number array matching that target's component count. curve is either { type: 'spring', stiffness: >0, dampingRatio: >0 } or { type: 'cubicBezier', x1, y1, x2, y2 (x1/x2 in [0,1], y unrestricted), durationMs: >0 }. Returns the stored animation object plus verifierErrors if applicable. Use trigger_animation afterwards (with render_frame) to see the animation actually play.
| Name | Type | Req | Description |
|---|---|---|---|
| animation | object | yes | — |
No output schema declared.
No examples provided.
upsert_binding Create or replace a Binding ~326
Create a new Binding, or replace one entirely, in project.bindings — the mechanism that drives a uniform or node property from an input (time/resolution/pointer/audio/microphone/camera) or a reusable Control. Omit binding.id to create a new one; pass an existing project.bindings[].id to replace that binding completely (full-object replace, not a patch); an id that does not match any existing binding is rejected. source.type 'input' needs an inputId that exists in project.inputs; 'control' needs a controlId that exists in project.controls; 'animation' and 'expression' are REJECTED — these are schema-reserved source types that Runtime v1 does not evaluate yet (see docs/runtime-binding-model.md §2.1), so accepting one would silently create a binding that never does anything. target is either { type: 'uniform', nodeId, uniformName } or { type: 'nodeProp', nodeId, propPath } (same closed nodeProp set as upsert_animation's tracks — see src/core/bindableNodeProps.ts). transform.scale/offset, if given, must be finite numbers. If the target already has another enabled binding, the result includes a warning: per the project's evaluation order, only the first enabled binding on a target (in project.bindings[] array order) takes effect, so a newly added second one may have no visible effect until the earlier one is disabled/removed. Returns the stored binding object plus that optional warning and verifierErrors if applicable.
| Name | Type | Req | Description |
|---|---|---|---|
| binding | object | yes | — |
No output schema declared.
No examples provided.