使用说明
Open source from Noise Factor · more projects
shade-mcp
MCP server for shader effect development.
Distilled from three projects:
- noisemaker — browser-based shader testing
- portable — portable effect authoring
- shade — agent-assisted shader editing
Requirements
- Node.js 22 or newer
- A viewer page for the seven viewer-driven browser tools;
runDslProgramneeds a Noisemaker checkout. See Viewer.
Quick Start
npm install # installs dependencies and builds dist/
npm run setup # install Playwright Chromium (browser tools only)
Using shade-mcp with noisemaker or portable? See docs/SETUP.md for step-by-step configuration.
Viewer
The browser tools use Chromium. Seven control a viewer page that hosts the renderer;
runDslProgram creates a fresh renderer on a blank page for each call.
shade-mcp does not include a viewer.
Set SHADE_VIEWER_ROOT to the smallest directory containing the viewer and its imported modules.
For the seven viewer tools, the page must expose the renderer through window globals.
Set SHADE_GLOBALS_PREFIX to match those globals. With prefix <p>, the
viewer publishes <p>CanvasRenderer, <p>RenderingPipeline,
<p>CurrentBackend() (glsl or wgsl), <p>CurrentEffect (the effect id, or
an entry with id or namespace and name, and instance.globals),
<p>SetPaused, <p>SetPausedTime, <p>FrameCount and optionally
<p>PipelineGeneration, a number it bumps after every successful compile. To
select an effect, shade-mcp calls <p>SelectEffect(id, { url }) when the
viewer publishes it, where url is where shade-mcp's server serves the
effect's directory (/effects/<id>/); the call resolves when the compile
finishes and may resolve to { error }. Without it, shade-mcp drives a
<select id="effect-select">. To switch backends, it calls
<p>SetBackend('glsl' | 'wgsl') when published, which recompiles, and
otherwise the renderer's switchBackend. Portable's viewer publishes the
selection and backend calls. runDslProgram uses a fresh
renderer independently of the viewer globals and SHADE_VIEWER_PATH. By default
it imports Noisemaker source modules from /shaders/src/index.js and assets from
/shaders under SHADE_VIEWER_ROOT. For Portable's built-in Noisemaker DSL,
set SHADE_DSL_RENDERER_MODULE=https://shaders.noisedeck.app/1/noisemaker-shaders-core.esm.js,
SHADE_DSL_ASSETS_BASE=https://shaders.noisedeck.app/1, and
SHADE_DSL_USE_BUNDLES=true; keep its viewer settings for the other browser tools.
To use authored user.* effects, pass effects as comma-separated Portable
package IDs under SHADE_EFFECTS_DIR (up to 16), such as user/gradient,user/tint.
For a flat single-effect directory, use its directory name as the ID. The fresh
renderer loads each package's raw definition.json and the requested backend's
shader files. Omitting effects loads built-ins only. Authored registration
requires a Noisemaker runtime with CanvasRenderer.registerPortableEffect;
older CDN bundles return an explicit unsupported-runtime error. Viewer state
is never inherited, and duplicate authored function names fail the batch.
| Project | SHADE_VIEWER_ROOT |
SHADE_VIEWER_PATH |
SHADE_GLOBALS_PREFIX |
|---|---|---|---|
| noisemaker | <noisemaker> |
/demo/shaders/ |
__noisemaker |
| portable | <portable> |
/viewer/index.html |
__portable |
noisemaker's viewer page is in demo/shaders/ and imports the engine from shaders/src/ at the repository root.
For noisemaker, set SHADE_VIEWER_ROOT to the repository root.
If you use the page's directory as the root, every module request returns 404.
The server refuses dotfiles regardless of the root.
The analysis, knowledge, and utility tools read from disk and need no viewer.
Configuration
Environment variables:
| Variable | Default | Description |
|---|---|---|
SHADE_EFFECTS_DIR |
<project root>/effects |
Path to the effects library |
SHADE_PROJECT_ROOT |
cwd | Project root, used for relative paths and AI key lookup |
SHADE_VIEWER_ROOT |
<project root>/viewer |
Directory served as the viewer |
SHADE_VIEWER_PATH |
/ |
Path within the viewer to open |
SHADE_VIEWER_PORT |
0 (OS-assigned) |
Port for the local viewer server |
SHADE_GLOBALS_PREFIX |
__shade |
Prefix of the viewer's window globals |
SHADE_DSL_RENDERER_MODULE |
/shaders/src/index.js |
Root-relative or HTTPS Noisemaker module exporting CanvasRenderer and compile for runDslProgram |
SHADE_DSL_ASSETS_BASE |
/shaders |
Root-relative or HTTPS Noisemaker asset base for runDslProgram |
SHADE_DSL_USE_BUNDLES |
false |
Set true when DSL effects load from bundled assets (Portable's CDN) |
SHADE_BACKEND |
webgl2 |
Default rendering backend (webgl2 or webgpu) |
SHADE_MAX_BROWSERS |
1 |
Concurrent browser sessions |
SHADE_HEADLESS |
1 (headless) |
Set to 0 to watch the browser window |
SHADE_TIMEOUT_MS |
120000 |
Ceiling for every browser and page operation |
SHADE_AI_TIMEOUT_MS |
120000 |
Ceiling for a single AI provider request |
SHADE_AI_MODEL |
provider default | Model used by the AI-powered tools |
ANTHROPIC_API_KEY |
— | Required for AI-powered tools (vision, analysis) |
OPENAI_API_KEY |
— | Fallback AI provider |
MCP Client Configuration
Claude Code
Put this in .mcp.json at the project root, or register it with claude mcp add shade -- node /path/to/shade-mcp/dist/index.js.
{
"mcpServers": {
"shade": {
"command": "node",
"args": ["/path/to/shade-mcp/dist/index.js"],
"env": {
"SHADE_EFFECTS_DIR": "/path/to/effects"
}
}
}
}
VS Code Copilot
In .vscode/mcp.json:
{
"servers": {
"shade": {
"command": "node",
"args": ["/path/to/shade-mcp/dist/index.js"],
"env": {
"SHADE_EFFECTS_DIR": "/path/to/effects"
}
}
}
}
Cursor
In .cursor/mcp.json:
{
"mcpServers": {
"shade": {
"command": "node",
"args": ["/path/to/shade-mcp/dist/index.js"],
"env": {
"SHADE_EFFECTS_DIR": "/path/to/effects"
}
}
}
}
Windsurf
In ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"shade": {
"command": "node",
"args": ["/path/to/shade-mcp/dist/index.js"],
"env": {
"SHADE_EFFECTS_DIR": "/path/to/effects"
}
}
}
}
Tool Reference
Result envelope
Every tool result's text is JSON in one envelope, errors included.
- A single result carries a top-level
outcome, computed from the tool's ownstatus:ok(the check passed or the query answered),fail(the check ran and its verdict is negative: a paritymismatch, apassthroughfilter,divergentalgorithms, a uniform that did not affect output,meets_target: false),warning(findings to review),skipped(nothing was testable) orerror(no verdict). A payload with nostatusiserrorwhen it carries anerrorstring. Anoutcomekey inside the payload never overrides the computed one. Verb-specific fields such asstatusandmismatchPercentare kept beside it. - A batch (several effects in one call) is
{ outcome, summary, results }. Each entry carries its ownoutcomeandsummarycounts entries per outcome. The batchoutcomeiserrorwhen every entry is an error,failwhen any entry isfailorerror,warningwhen any entry iswarning,skippedwhen every entry is skipped, andokotherwise. - MCP
isErroris set when the call failed as a whole: a singleerrorresult, or a batch in which every entry is anerror. Afailverdict is a successful call and does not setisError. - Errors raised inside a tool before it reaches its own handling, such as an
unresolvable effect selector, and calls the MCP SDK rejects before the
tool runs (an argument that fails the tool's input schema, an unknown
tool) arrive in the same envelope with
isError:{ "outcome": "error", "status": "error", "error": "..." }. For a rejected argument,erroris the SDK's message, for exampleMCP error -32602: Input validation error: ....
Browser Tools (8)
These tools require Playwright Chromium; all except runDslProgram require a viewer (see Viewer).
Each result of the six viewer verbs (compileEffect, renderEffectFrame,
benchmarkEffectFPS, testUniformResponsiveness, testNoPassthrough,
testPixelParity), and each batch entry, carries effect_id, the effect id
that was requested, so entries match their requests. page_effect_id is the
effect the viewer page reports as loaded (null when the viewer does not expose
it) and backend is the backend the page reports. When the page shows another
effect or renders on another backend, the entry's status is error.
| Tool | Description |
|---|---|
compileEffect |
Compile a shader effect. Return diagnostics for each pass. Use comma-separated effect IDs for a batch. |
renderEffectFrame |
Render one frame. Return mean RGB, variance, and blank/monochrome detection. Optionally capture a PNG. |
describeEffectFrame |
Render a frame. Analyze the image with AI vision. The tool requires ANTHROPIC_API_KEY or OPENAI_API_KEY. |
benchmarkEffectFPS |
Measure FPS, jitter, and frame timing statistics against a target frame rate. |
testUniformResponsiveness |
Check whether each uniform control changes the output: range, dropdown, boolean and vector controls, gates opened first, against a reference captured twice to measure the output's own change between renders. Each control is pass, fail, error, gated, unstable (no verdict) or untested. The status is ok only when every measured control passed; any error or unstable control makes it error, otherwise any fail makes it fail. |
testNoPassthrough |
Check that a filter effect modifies the input it consumes: compares output and input at two paused times by mean difference, changed grid pixels and strongly changed pixels; a filter that is the identity at its defaults is measured again with its controls moved. |
testPixelParity |
Compare WebGL2 and WebGPU output pixel by pixel within the epsilon tolerance. |
runDslProgram |
Render every written output surface at frames 1, 120, and 600 after warmup by default, at 960×540. Return one PNG contact sheet as MCP image content plus metrics for each surface and frame. |
Analysis Tools (4)
These tools analyze files on disk and do not need a browser.
| Tool | Description |
|---|---|
checkEffectStructure |
Detect unused files, broken references, naming violations, leaked uniforms, and structural parity issues. |
checkAlgEquiv |
Compare the semantics of GLSL/WGSL pairs with AI. The tool requires an AI key. |
compareShaders |
Compare shader structure: function names, uniforms, and line counts. |
analyzeBranching |
Analyze unnecessary shader branching with AI. Return optimization suggestions. The tool requires an AI key. |
Knowledge Tools (7)
In-memory search indexes.
| Tool | Description |
|---|---|
searchEffects |
Search effect library by concept, tag, algorithm, or visual style. |
analyzeEffect |
Return the full definition, shader source, uniforms, and passes for an effect ID. |
searchShaderSource |
Search GLSL source code across all effects with a regular expression. |
searchShaderKnowledge |
Search curated shader documentation by meaning: DSL grammar, GLSL techniques, patterns, errors, and the shader-craft references (category shader-craft). |
searchShaderLibrary |
Search local GLSL/WGSL library symbols by name, signature, summary and module. |
checkShader |
Return advisory source findings for GLSL/WGSL text or a shader file inside the effects directory or project root. |
bundleShader |
Inline used library symbols and their dependencies with credits. Return source; write a file only with explicit write: true. |
Utility Tools (2)
| Tool | Description |
|---|---|
listEffects |
List all effects, optionally filtered by namespace. |
generateManifest |
Rebuild effect manifest by scanning effects directory. |
Development
npm test # run tests
npm run typecheck # tsc --noEmit, including tests
npm run build # build with tsup
npm run dev # watch mode
Tests and typecheck are separate checks. Vitest does not typecheck. Run both commands.
Credits
The shader-craft knowledge in src/knowledge/shader-craft/ adapts the shader-art-coding skill from
shader-skills by gokayfem (MIT, Copyright (c) 2026 gokayfem) for
Noisemaker effects. It is pinned to upstream commit e09bc993bf04201f26c03c87c89210e9b472f526 (2026-10-19).
provenance.json records each upstream file and its blob hash. The upstream licence ships in the knowledge bundle as
SHADER_CRAFT_LICENSE.
The techniques come from their original authors, each credited in SHADER_CRAFT_CREDITS:
- Inigo Quilez: distance functions, smooth minimum, soft shadows, filtering, noise derivatives, domain warping
- Mercury demogroup: hg_sdf combinators and repetition
- Jarzynski and Olano: PCG hashes
- Stefan Gustavson, and Gustavson and McEwan: simplex noise
- Ken Perlin: gradient noise and turbulence; Perlin and Neyret: flow noise
- Bridson, Hourihan and Nordenstam: curl noise
- Jorge Jimenez: interleaved gradient noise; Martin Roberts: R2 sequence
- Troy Sobotka and Benjamin Wrensch: AgX; the Khronos Group: PBR Neutral; Stephen Hill: ACES fit and wrap lighting
- Bjorn Ottosson: Oklab; Brian Karis: environment BRDF; the Filament documentation: GGX shading
- Barre-Brisebois and Bouchard: fast translucency
- Sebastien Hillaire: volumes and sky; Andrew Schneider: clouds
- Mark Finch: water waves; Ryan Geiss: terrain; Müller et al.: procedural buildings
The shader-craft building-block library (SHADER_CRAFT_LIBRARY_MODULES) is the upstream GLSL library adapted to the
Noisemaker effect contract, with a WGSL twin of every function. SHADER_CRAFT_LIBRARY indexes both, and each entry
cites its technique credits by key.
License
MIT
