Skip to main content
Glama
scratch-unified-mcp

scratch-unified-mcp

by blessed0x

scratch-unified-mcp

The first standalone headless Scratch VM over MCP — plus everything needed to build, playtest, and publish Scratch projects without opening a browser.

117 tools. One stdio command. Zero name collisions.

MCP client ──stdio──▶ python3 -m scratch_unified
                        ├── social_*   website + social graph (scratchattach)
                        ├── project_*  goboscript text-authoring loop
                        ├── spy_*      blocks ↔ real Python (ScratchPy)
                        └── sb3_*      block surgery + headless VM control loop (vendored Node sidecar fork)

Why this exists

Three Scratch tool ecosystems each covered part of the loop — website API, block-level editing with a headless VM, Python-to-blocks — but none covered all of it, and switching between three servers with three naming schemes killed momentum. This merges all three behind one transport.

The piece that didn't exist anywhere else: a headless TurboWarp scratch-vm you can drive and inspect over MCP. Load a .sb3, press green flag, click sprites, step frames, read threads, watch variables — all from tool calls, all inspectable.

Related MCP server: Godot Agent Loop

Quickstart

One command, macOS / Linux / Windows (Git Bash, MSYS2, WSL) — needs Python ≥ 3.12 and Node ≥ 18 (both checked up front):

git clone https://github.com/x3vu/scratch-unified-mcp.git && cd scratch-unified-mcp
./install.sh

That installs the Python package (scratch-unified on PATH), the sidecar's npm deps (unlocks all sb3_* tools), and verifies both. Flags: --no-sidecar (skip npm deps), --no-verify (skip checks), --prefix DIR (portable pip target). install.ps1 is the same flow for native PowerShell. Requires nothing but this repo — the ScratchPy core and the VM sidecar are vendored; the old upstream-* clones are gone (see "Layout").

Manual install (same steps, no script):

pip install .
npm install --no-audit --no-fund --legacy-peer-deps ./scratch_unified/sidecar

Then point any MCP client at it:

// MCP client config — easiest: the installed entry point
{ "mcpServers": { "scratch-unified": {
  "command": "scratch-unified",
  "env": {
    "SCRATCH_MCP_DATA_DIR": "/path/to/scratch-unified-mcp/.sessions"
    // "SCRATCH_MCP_BRIDGE_PORT": "9060"  // only if something else holds 9060
  },
  "timeout": 600000
} } }
// …or without installing: repo checkout + interpreters
{ "mcpServers": { "scratch-unified": {
  "command": "python3",
  "args": ["-m", "scratch_unified"],
  "cwd": "/path/to/scratch-unified-mcp",
  "env": {
    "PYTHONPATH": "/path/to/scratch-unified-mcp",
    "SCRATCH_MCP_DATA_DIR": "/path/to/scratch-unified-mcp/.sessions"
  },
  "timeout": 600000
} } }

The headless VM loop

Six core tools, debug tools, media tools, and five live-run tools, all proxied to a lazily-spawned Node sidecar (TurboWarp scratch-vm, interpreted mode, stdout muted so MCP framing stays clean). The sidecar is a vendored fork at scratch_unified/sidecar/ (upstream scratch4js @ cb1669aa, gitignored/read-only) — VM improvements land in the fork, never upstream:

Tool

What it does

sb3_open_project / sb3_save_project

Load / write .sb3 (saves live-reload TurboWarp Desktop)

sb3_vm_load

Load the open project into a fresh VM

sb3_vm_green_flag

Press green flag (clears bubbles, question, errors)

sb3_vm_run

Advance N seconds/frames, paced or flat-out. Returns state + ordered event timeline (say, broadcast, question/answer, errors)

sb3_vm_state

Snapshot now: targets (x/y/vars/lists/costume), monitors, bubbles, question, thread count, errors

sb3_vm_input

Keys, mouse position/clicks (stage coords), ask answers. Includes a click shim so when this sprite clicked fires headless

sb3_vm_threads

Every live thread: target, clone flag, starting hat, stack depth, status (running/yield/promise-wait/done), kill flag

sb3_vm_monitors

Full monitor table (visible or not) — watch a variable without pixels

sb3_vm_step_frame

Exactly one frame + before/after counts + delta (new threads, events that tick)

sb3_vm_seed

Deterministic PRNG (mulberry32 over Math.random; scratch-vm has no seedable RNG). Same seed, same run

sb3_vm_watch

Poll-and-diff variable watcher: old/new/changed per key, per-clone capable

sb3_vm_stub_calls

Recorded pen/sound stub calls since load

sb3_vm_pen_png

Pen raster (480×360 software canvas) as PNG base64 + pixel count. Strokes only

sb3_vm_mix_wav

Offline sound mix as WAV base64: every play at its timer offset, volume/pitch approximate

sb3_vm_run_until

Run on virtual time until a predicate fires (varEquals / broadcastSeen / threadsIdle, OR) — one call replaces N× vm_run polls

sb3_vm_poke

Write live VM state mid-run: variables/lists by target, sprite pose/costume. Fault injection the disk-editing set_variable can't do

sb3_vm_clones

Clone census: pose, costume, sprite-locals per live clone

sb3_find_blocks

Query every target's blocks by opcode substring / field value / input name / hat-only

sb3_validate_blocks

Catalog validator on demand: unknown opcodes (did-you-mean), bad inputs, disabled extensions. Advisory, custom blocks ignored

Headless gaps are patched, not hidden: distance-based touching fallback (no renderer means every touching returns false upstream), sprite-click shim, broadcast logging via wrapped startHats, interpreter mode to dodge a JIT pickrandom false-alarm. Details: scratch_unified/sidecar/src/runtime.js.

Tool census

social_* 20 · project_* 18 · spy_* 14 · sb3_* 65 (51 proxied + 9 native + 5 new) = 117. Full per-tool reference: docs/IDENTIFIERS.md. Architecture: docs/ARCHITECTURE.md.

Layout

scratch_unified/          the server (this is what runs)
  server.py               FastMCP app + main() (--help/--version/--list-tools)
  vendor_uu/              uukelele/scratch-mcp, vendored (social + projects)
  vendor_spy/             ScratchPy Studio core, vendored (blocks↔Python, MIT)
  sidecar/                vendored VM sidecar fork (editable; FORK.md + package.json)
  spy_loader.py           headless ScratchPy import (tkinter stubbed if absent)
  spy_tools.py            14 spy_* wrappers, one shared .spy server
  node_bridge.py          lazy Node sidecar transport (newline-delimited JSON-RPC, TIMEOUT-guarded)
  typed_proxy.py          typed sb3_* proxies (FastMCP rejects **kwargs)
  sb3_extra.py            git unpack/pack/diff, studio/remix/favorites, cloud vars
install.sh / install.ps1  one-command setup (sh / PowerShell) + verification
bin/scratch-unified       thin launcher (used when pip dir is on PATH but entry point isn't)
scripts/smoke_sidecar.py  post-install sidecar handshake check
docs/                     ARCHITECTURE, IDENTIFIERS

Everything the server needs ships in this repo (vendored vendor_uu/, vendor_spy/, sidecar/src/); npm install inside sidecar/ is the only network step, and it's optional (without it only sb3_* tools report unavailable). Sessions persist as session IDs only, never passwords.

Credits

Available Tools

117 tools
project_add_costumeProject Add CostumeA

Add a costume to a sprite, copying the file into the project's assets/.

Give either file (an existing .svg/.png/.jpg) or svg (SVG markup as a string, which needs name). Appended last, so it becomes the sprite's highest costume number.

Inputs: sprite (required), svg, file, name, +1 more. Returns: kind, name, path, exists

project_add_soundProject Add SoundA

Add a sound to a sprite, copying the file into the project's assets/.

Scratch accepts only MP3 and WAV; other formats make the project refuse to load, with no warning.

Inputs: file (required), sprite (required), name, path. Returns: kind, name, path, exists

project_buildProject BuildA

Compile the project to .sb3 with goboscript. Use this rather than running the goboscript CLI yourself.

A failed build raises with the compiler's diagnostics passed through verbatim -- file, line, column and the offending source -- so fix those and call it again.

Also reports which extensions the project uses and whether any of them are TurboWarp-only, which would block publishing to scratch.mit.edu.

Inputs: path. Returns: output, sb3_path, extensions, is_scratch_compatible, +1 more

project_check_toolchainProject Check ToolchainA

Report whether the goboscript toolchain is installed and usable.

Call this first if any other project_* tool complains about a missing binary. Never raises, so it is safe to use for diagnosis.

Inputs: no named parameters. Returns: object

project_closeProject CloseB

Forget a project. Does not delete anything from disk.

Inputs: path. Returns: result

project_downloadProject DownloadA

Decompile a Scratch project into editable goboscript source, and make it active.

Give either project_id to pull straight from scratch.mit.edu, or sb3_path for a local .sb3 file. Decompiling is lossy in layout terms: the result is equivalent code, not a byte-identical copy of the original.

Inputs: path (required), verify, sb3_path, overwrite, +1 more. Returns: note, project, compiles, diagnostics

project_editing_guideProject Editing GuideA

Read this BEFORE creating or editing any Scratch project.

Covers Scratch's hard limits (block counts, asset sizes, clone caps, cloud variable rules and so on) and the goboscript language used to write projects as text. Scratch projects are not edited as raw project.json here; they are written as goboscript .gs source and compiled.

Inputs: no named parameters. Returns: result

project_goboscript_docs_helpProject Goboscript Docs HelpA

Read the goboscript language documentation.

Call with no arguments to get the index of every documentation page. Call with page set to one of those paths to get that page as raw markdown.

Use this whenever you are unsure of a block or reporter name -- goboscript names differ from the Scratch block text (for example switch_costume, change_x, touching("sprite"), clone, set_ghost_effect) and guessing wastes a build cycle.

Inputs: page. Returns: result

project_infoProject InfoB

Summarise a project: its sprites, publish target and compatibility.

Inputs: path. Returns: name, path, sprites, built_sb3, +4 more

project_listProject ListA

List the goboscript projects this server knows about.

Inputs: no named parameters. Returns: object

project_list_assetsProject List AssetsA

List the costumes and sounds a sprite declares.

Inputs: sprite (required), path. Returns: sounds, sprite, costumes

project_newProject NewA

Create a new goboscript project and make it the active project.

Scaffolds a project directory with stage.gs, main.gs, a blank costume and a goboscript.toml.

Inputs: path (required), git, published_project_id. Returns: name, path, sprites, built_sb3, +4 more

project_openProject OpenB

Register an existing goboscript project directory and make it active.

Inputs: path (required), published_project_id. Returns: name, path, sprites, built_sb3, +4 more

project_remove_assetProject Remove AssetA

Remove a costume or sound declaration from a sprite.

Leaves the file in assets/ in case other sprites use it.

Inputs: kind (required), name (required), sprite (required), path. Returns: result

project_save_to_cloudProject Save To CloudA

Build the project and upload it to scratch.mit.edu as the active session.

Creates a new Scratch project the first time, then reuses that id for later saves. Uploads costume and sound files as well as the code, since Scratch stores assets separately and the project would render broken without them.

Refuses to upload a project using TurboWarp-only extensions, and names them, because scratch.mit.edu will not accept it.

Inputs: path, build, title, thumbnail, +1 more. Returns: url, title, created, warnings, +4 more

project_selectProject SelectA

Choose which open project the other project_* tools act on.

Inputs: path (required). Returns: name, path, sprites, built_sb3, +4 more

project_set_thumbnailProject Set ThumbnailA

Set the thumbnail of a published project.

Scratch generates thumbnails in its editor, so a project uploaded through this server keeps the grey placeholder until one is set. project_save_to_cloud does this automatically; use this tool to change it afterwards.

With no file, the Stage's first backdrop from the latest build is used, converted from SVG if a rasteriser is available. 480x360 suits Scratch best.

Inputs: file, path. Returns: object

project_summaryProject SummaryA

Inspect what a project actually compiled to.

Reads the built .sb3 and reports every target with its block and script counts, costumes, sounds, variables and lists, plus the assets embedded and which sprites use them. Use it to verify a build did what you intended -- that a costume really got attached, that layer order is right, that a sound is present and not oversized.

Requires a build; run project_build first. Warns if the source has been edited since the .sb3 was written.

Inputs: path. Returns: stale, assets, sb3_kb, project, +8 more

sb3_add_broadcastSb3 Add BroadcastC

Add a broadcast message. (proxied)

Inputs: name (required). Returns: result

sb3_add_commentSb3 Add CommentC

Add a sprite comment. (proxied)

Inputs: text (required), target (required), x, y. Returns: result

sb3_add_costumeSb3 Add CostumeC

Add a costume from a file. (proxied)

Inputs: name (required), path (required), target (required), dataFormat. Returns: result

sb3_add_soundSb3 Add SoundC

Add a sound from a file. (proxied)

Inputs: name (required), path (required), target (required), dataFormat. Returns: result

sb3_add_spriteSb3 Add SpriteC

Add a sprite; props is a JSON object string. (proxied)

Inputs: name (required), props. Returns: result

sb3_cloud_get_varsSb3 Cloud Get VarsA

Read current cloud variable values for a project.

Inputs: project_id (required). Returns: result

sb3_cloud_logsSb3 Cloud LogsC

Recent cloud activity log for a project.

Inputs: project_id (required), limit. Returns: result

sb3_cloud_set_varSb3 Cloud Set VarC

Set a cloud variable value.

Inputs: name (required), value (required), project_id (required). Returns: result

sb3_delete_listSb3 Delete ListC

Delete a list. (proxied)

Inputs: name (required), target (required). Returns: result

sb3_delete_variableSb3 Delete VariableC

Delete a variable. (proxied)

Inputs: name (required), target (required). Returns: result

sb3_enable_extensionSb3 Enable ExtensionB

Register an extension so its blocks load. (proxied)

Inputs: id (required), url. Returns: result

sb3_favoritesSb3 FavoritesA

List a user's favorited projects (id + title).

Inputs: username (required). Returns: result

sb3_find_blocksSb3 Find BlocksA

Query every target's blocks map in the open project. (proxied)

Filters combine (AND): opcode substring, field value, input name, topLevel-only. Needs an open project, not the VM.

Inputs: hat, limit, opcode, inputName, +1 more. Returns: result

sb3_get_block_schemaSb3 Get Block SchemaB

Full schema for one opcode incl. shadow encodings. (proxied)

Inputs: opcode (required), target. Returns: result

sb3_get_targetSb3 Get TargetB

Full details for a sprite or Stage. (proxied)

Inputs: name (required). Returns: result

sb3_get_target_jsonSb3 Get Target JsonC

Raw project.json entry for a target, or subtree at a JSON Pointer. (proxied)

Inputs: name (required), pointer. Returns: result

sb3_git_diffSb3 Git DiffA

Summarise an unpacked project dir: targets, block counts, assets.

Inputs: project_dir (required). Returns: result

sb3_git_packSb3 Git PackB

Pack a diffable directory back into an .sb3.

Inputs: sb3_path (required), project_dir (required). Returns: result

sb3_git_unpackSb3 Git UnpackA

Unpack an .sb3 into a diffable directory (project.json + assets/).

Inputs: out_dir (required), sb3_path (required). Returns: result

sb3_list_blocksSb3 List BlocksB

Catalog of standard opcodes from scratch-vm, optionally by category. (proxied)

Inputs: category. Returns: result

sb3_list_commentsSb3 List CommentsA

List sprite comments. (proxied)

Inputs: no named parameters. Returns: result

sb3_list_spritesSb3 List SpritesB

Every sprite with position/size/media. (proxied)

Inputs: no named parameters. Returns: result

sb3_open_projectSb3 Open ProjectC

Load an .sb3 file into the Node editor. (proxied)

Inputs: path (required). Returns: result

sb3_open_scratch_projectSb3 Open Scratch ProjectB

Download a scratch.mit.edu project by id for editing. (proxied)

Inputs: projectId (required). Returns: result

sb3_patch_targetSb3 Patch TargetC

Apply RFC 6902 JSON Patch (as JSON string) to a target. (proxied)

Inputs: name (required), patch (required). Returns: result

sb3_project_infoSb3 Project InfoB

Targets, extensions, monitors, meta of open project. (proxied)

Inputs: no named parameters. Returns: result

sb3_push_to_scratchSb3 Push To ScratchC

Save the open project back to scratch.mit.edu (confirm-gated). (proxied)

Inputs: confirm, projectId. Returns: result

sb3_reloadSb3 ReloadC

Load an .sb3 from disk in TurboWarp Desktop via bridge. (proxied)

Inputs: path. Returns: result

sb3_remixesSb3 RemixesC

List remix lineage info for a project (id, title, author).

Inputs: project_id (required). Returns: result

sb3_remove_commentSb3 Remove CommentB

Remove a sprite comment. (proxied)

Inputs: comment_id (required). Returns: result

sb3_remove_costumeSb3 Remove CostumeC

Remove a costume. (proxied)

Inputs: name (required), target (required). Returns: result

sb3_remove_soundSb3 Remove SoundC

Remove a sound. (proxied)

Inputs: name (required), target (required). Returns: result

sb3_remove_spriteSb3 Remove SpriteC

Remove a sprite. (proxied)

Inputs: name (required). Returns: result

sb3_rename_targetSb3 Rename TargetC

Rename a sprite/stage target. (proxied)

Inputs: name (required), newName (required). Returns: result

sb3_run_projectSb3 Run ProjectB

Green flag in TurboWarp Desktop via bridge. (proxied)

Inputs: no named parameters. Returns: result

sb3_save_projectSb3 Save ProjectB

Write the open project back to .sb3, live-reloading TurboWarp. (proxied)

Inputs: path, compressionLevel. Returns: result

sb3_scratch_loginSb3 Scratch LoginC

Log in to scratch.mit.edu for the Node session. (proxied)

Inputs: password, username. Returns: result

sb3_screenshotSb3 ScreenshotA

Capture the live TurboWarp stage as PNG (base64-wrapped note). (proxied)

Inputs: no named parameters. Returns: result

sb3_screenshot_jpegSb3 Screenshot JpegB

Capture the live stage as compressed JPEG. (proxied)

Inputs: quality. Returns: result

sb3_set_commentSb3 Set CommentC

Edit a sprite comment. (proxied)

Inputs: text (required), comment_id (required). Returns: result

sb3_set_listSb3 Set ListC

Set/create a list on a target; items is a JSON array string. (proxied)

Inputs: name (required), target (required), items. Returns: result

sb3_set_spriteSb3 Set SpriteC

Set sprite props; props is a JSON object string (x, y, size, ...). (proxied)

Inputs: name (required), props. Returns: result

sb3_set_stageSb3 Set StageC

Set stage props; props is a JSON object string. (proxied)

Inputs: props. Returns: result

sb3_set_variableSb3 Set VariableC

Set/create a variable on a target. (proxied)

Inputs: name (required), target (required), value. Returns: result

sb3_share_projectSb3 Share ProjectC

Publish the project so it is public (confirm-gated). (proxied)

Inputs: confirm, projectId. Returns: result

sb3_stop_projectSb3 Stop ProjectC

Stop in TurboWarp Desktop via bridge. (proxied)

Inputs: no named parameters. Returns: result

sb3_studio_infoSb3 Studio InfoA

Fetch a Scratch studio's title, description, and stats.

Inputs: studio_id (required). Returns: result

sb3_validate_blocksSb3 Validate BlocksA

Validate target blocks against the opcode catalog. (proxied)

Omit target for all targets. Advisory — custom extension blocks ignored.

Inputs: target. Returns: result

sb3_vm_clonesSb3 Vm ClonesA

Live clone census: name, pose, costume, sprite-locals per clone. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_green_flagSb3 Vm Green FlagC

Press green flag in the headless VM. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_inputSb3 Vm InputB

Feed keyboard/mouse/answer input to the headless VM. (proxied)

keys accepts a JSON array of {key, isDown?} objects or a single key name (wrapped to a full tap). Anything else that parses to a non-list raises ValueError before touching the sidecar — the sidecar's zod schema would reject it anyway, but failing here gives the shorter, proxy-attributed error.

Inputs: keys, answer, mouseX, mouseY, +1 more. Returns: result

sb3_vm_loadSb3 Vm LoadB

Load the open project into the headless VM. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_mix_wavSb3 Vm Mix WavB

Offline sound mix as WAV base64 + event count + seconds. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_monitorsSb3 Vm MonitorsA

Full monitor table (visible or not): label, opcode, value, mode. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_pen_pngSb3 Vm Pen PngB

Pen raster as PNG base64 + non-transparent pixel count. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_pokeSb3 Vm PokeA

Set live VM state (fault injection mid-wave). (proxied)

Each arg is a JSON array string: variables entries {target?, name, value}, lists entries {target?, name, items}, sprites entries {target, x?, y?, direction?, size?, visible?, costume?}. Unknown target/variable/list/costume is an error.

Inputs: lists, sprites, variables. Returns: result

sb3_vm_runSb3 Vm RunA

Advance the headless VM; returns state + event timeline. (proxied)

Budgets (seconds/frames) are omitted when 0 so the sidecar's own defaults apply (its zod schema rejects an explicit 0), and untilIdle / paced are only forwarded when set (None = sidecar default). paced=False runs the budget back-to-back with no per-frame sleep, which is what deterministic step-debug wants.

Inputs: paced, frames, seconds, untilIdle. Returns: result

sb3_vm_run_untilSb3 Vm Run UntilA

Run the headless VM on virtual time until a predicate fires. (proxied)

until is a JSON object string, e.g. {"broadcastSeen": ["GameStart"]}, {"varEquals": [{"name": "Score", "value": 100}]}, {"threadsIdle": true} — fires when ANY entry matches (OR). Budgets omitted when 0 so sidecar defaults apply. Replaces N× sb3_vm_run poll loops with one call.

Inputs: until, seconds, chunkFrames. Returns: result

sb3_vm_seedSb3 Vm SeedB

Deterministic PRNG seed for operator_random; None restores Math.random. (proxied)

Inputs: seed. Returns: result

sb3_vm_stateSb3 Vm StateB

Snapshot headless VM state. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_step_frameSb3 Vm Step FrameA

Step exactly one frame; returns before/after + delta. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_stopSb3 Vm StopA

Stop all scripts in the headless VM. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_stub_callsSb3 Vm Stub CallsB

Recorded pen/sound stub calls since load. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_threadsSb3 Vm ThreadsA

Live thread inspector: target, clone flag, hat, stack, status. (proxied)

Inputs: no named parameters. Returns: result

sb3_vm_watchSb3 Vm WatchC

Poll-and-diff variable watcher: old/new/changed per key. (proxied)

Inputs: name, target. Returns: result

social_add_project_to_studioSocial Add Project To StudioA

Add a project to a studio as the active account.

Scratch only allows this if the account may add to that studio: it must be the owner, a curator, or the studio must allow anyone to add.

Inputs: studio_id (required), project_id (required). Returns: result

social_become_scratcherSocial Become ScratcherA

Accept a "become a Scratcher" invitation for the active account.

Scratch promotes a New Scratcher only after the Scratch Team invites them. On the website the invitation is accepted by reading through the Community Guidelines and clicking to agree, so accepting here means the account holder accepts those guidelines. For that reason nothing happens unless confirm is set: called without it, this only reports eligibility.

The guidelines are at https://scratch.mit.edu/community_guidelines and ask everyone on Scratch, in summary, to: be respectful, remembering the audience is broad and includes children; be constructive when commenting; share freely and give credit when remixing; keep personal information private; be honest rather than impersonating others or spreading rumours; and report anything inappropriate rather than escalating it.

Promotion cannot be undone.

Inputs: confirm. Returns: note, rank, invited, promoted, +3 more

social_check_inboxSocial Check InboxA

Check the active account's message inbox.

Scratch splits the inbox across three endpoints with independent unread state, and this reports all of them:

  • unread_count + messages: ordinary activity (comments, follows, loves). The count is genuinely unread, but the list is simply the newest messages whether read or not, so the two do not necessarily correspond.

  • scratch_team_messages: alerts from the Scratch Team.

  • invitation: a pending "become a Scratcher" invite, carrying its own unread flag.

unread_elsewhere is true when something is unread outside the activity feed, so an unread_count of 0 does not by itself mean an empty inbox.

Inputs: limit, offset. Returns: note, limit, offset, messages, +6 more

social_connect_sessionSocial Connect SessionA

Login to the Scratch site using set login details.

You may either provide a path_to_env, a scratch_username and scratch_password, or a scratch_session_id, or a browser_name (optional).

If none is provided, it will fallback to logging in via the user's installed browser.

Sessions are remembered across restarts by default, so this usually only needs to be called once per account. Check social_list_sessions first.

Inputs: remember, path_to_env, browser_name, scratch_password, +2 more. Returns: result

social_follow_userSocial Follow UserA

Follow, unfollow, or check whether the active account follows a user.

Inputs: username (required), action. Returns: object

social_forget_sessionSocial Forget SessionA

Drop a session from this server and from the on-disk store.

Inputs: username (required), logout. Returns: result

social_get_comment_repliesSocial Get Comment RepliesA

Fetch the replies to one top-level comment.

For source="profile" this is slow: Scratch has no endpoint for a single profile comment, so scratchattach pages through the profile until it finds the id. Prefer social_get_comments, which returns profile replies inline.

Inputs: source (required), source_id (required), comment_id (required), limit, +1 more. Returns: note, page, limit, offset, +5 more

social_get_commentsSocial Get CommentsA

Page through the comments on a project, a studio, or a user's profile.

The sources paginate differently because Scratch exposes them differently, so read the argument notes:

  • source="project" / "studio": uses limit + offset. Replies are a separate request, so include_replies costs one extra request per comment.

  • source="profile": uses page (30 top-level comments per page); offset is ignored. Replies always come back, free of charge, so include_replies is irrelevant here.

Only top-level comments are listed. Scratch has a single level of nesting: a reply to a reply is stored as a reply to the top-level comment.

Both sources page over a live feed, so on a busy project or profile a comment can shift between pages while you read them.

Inputs: source (required), source_id (required), page, limit, +2 more. Returns: note, page, limit, offset, +5 more

social_get_project_infoSocial Get Project InfoB

Fetch a Scratch project's metadata.

Inputs: id (required), recent_comments. Returns: id, url, loves, notes, +18 more

social_get_user_infoSocial Get User InfoB

Fetch a Scratch user's profile.

Inputs: username (required), recent_comments. Returns: id, name, wiwo, country, +9 more

social_like_projectSocial Like ProjectA

Love and/or favourite a project, remove either, or just check.

"like" is Scratch's love (the heart); "favourite" is the star. They are independent, so both and removeboth operate on each.

Inputs: id (required), action. Returns: object

social_list_sessionsSocial List SessionsA

List the Scratch sessions this server currently holds.

Use it to discover what is already available before asking the user for credentials.

Inputs: no named parameters. Returns: object

social_post_commentSocial Post CommentA

Post a comment on a project, a studio, or a user's profile, as the active session.

Requires a logged-in session. To reply to an existing comment, prefer social_reply_to_comment.

Scratch rate-limits commenting and rejects content it considers spam or disallowed, which surfaces as a CommentPostFailure.

Inputs: source (required), content (required), source_id (required), parent_id, +1 more. Returns: id, source, content, author_id, +8 more

social_reply_to_commentSocial Reply To CommentA

Reply to an existing comment on a project, studio or profile, as the active session.

parent_id must be a TOP-LEVEL comment id. Scratch supports only one level of nesting, so replying to a reply must target that reply's top-level parent; social_get_comments reports is_top_level and parent_id for every comment so you can pick the right id.

Inputs: source (required), content (required), parent_id (required), source_id (required), +1 more. Returns: id, source, content, author_id, +8 more

social_search_projectsSocial Search ProjectsA

Search Scratch's shared projects, or browse them when no query is given.

Results are a lean summary: id, title, author, url, thumbnail and stats. Follow up with social_get_project_info for the full record of one project, or project_download to pull one apart and edit it.

Note: as of 27/07/26, the API is broken right now, so if it doesn't work it's an upstream issue -- not our one. You can, if you wish, search the web for status updates as to Scratch search functionality.

Inputs: sort, limit, query, offset, +1 more. Returns: note, sort, limit, query, +6 more

social_set_active_sessionSocial Set Active SessionA

Choose which logged-in account subsequent authenticated tools act as.

Inputs: username (required). Returns: result

social_set_bioSocial Set BioB

Set the 'About me' section on the active session's profile.

Inputs: text (required). Returns: result

social_set_pfpSocial Set PfpA

Set the profile picture of the active session's account.

Scratch caps avatars at 500x500 pixels. An oversized image is rejected with HTTP 200 and an error in the body, so this checks the size before uploading and reads the response rather than assuming success.

SVG is not accepted by Scratch; rasterise it first (for example inkscape logo.svg -w 500 -h 500 -o logo.png).

Inputs: file (required). Returns: object

social_set_whatimworkingonSocial Set WhatimworkingonB

Set the "What I'm working on" section on the active session's profile.

Inputs: text (required). Returns: result

social_verify_sessionSocial Verify SessionA

Check with Scratch whether a stored session id is still valid.

Restored sessions are rebuilt offline from the session id, so an expired or revoked login looks fine until it is used. This performs a real request to confirm, and refreshes the account details on success.

Inputs: username. Returns: object

spy_add_package_blocksSpy Add Package BlocksB

Make blocks for an already-installed module (stdlib modules work too).

Inputs: module (required). Returns: result

spy_delete_fileSpy Delete FileC

Remove a tab from the project.

Inputs: file (required). Returns: result

spy_import_python_fileSpy Import Python FileB

Turn an existing .py file on disk into blocks.

Inputs: path (required). Returns: result

spy_install_packageSpy Install PackageC

pip install a package and turn it into blocks.

Inputs: name (required). Returns: result

spy_list_block_typesSpy List Block TypesC

Every kind of block ScratchPy knows, with the Python each one produces.

Inputs: category. Returns: result

spy_list_packagesSpy List PackagesB

Python packages installed in the environment ScratchPy uses.

Inputs: no named parameters. Returns: result

spy_open_projectSpy Open ProjectA

Open (or create) a ScratchPy .spy project file; all other spy_* tools act on it.

Inputs: path (required). Returns: result

spy_project_overviewSpy Project OverviewB

What is in the .spy project: tabs, variables, lists, custom blocks, package packs.

Inputs: no named parameters. Returns: result

spy_read_blocksSpy Read BlocksC

Readable outline of every script in a tab.

Inputs: file. Returns: result

spy_read_codeSpy Read CodeD

The Python a tab's blocks generate.

Inputs: file. Returns: result

spy_remove_package_blocksSpy Remove Package BlocksC

Take a package's blocks back out of the project.

Inputs: module (required). Returns: result

spy_runSpy RunB

Run a tab's generated Python and return what it printed.

Inputs: file, stdin, timeout. Returns: result

spy_set_variableSpy Set VariableB

Create a variable or list, or change its starting value.

Inputs: name (required), kind, value. Returns: result

spy_write_pythonSpy Write PythonC

THE MAIN BUILD TOOL: give ordinary Python, it becomes Scratch blocks in a tab.

Inputs: source (required), file, replace. Returns: result

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    C2.8/5.0

    Scored across 117 tools

    Disambiguation3/5

    The prefix families (project_, social_, sb3_, spy_) provide clear context, so many tools are distinguishable. However, there are multiple overlapping same-purpose tools across subsystems—project_info vs sb3_project_info vs social_get_project_info, project_download vs sb3_open_scratch_project, project_save_to_cloud vs sb3_push_to_scratch, and project_add_costume vs sb3_add_costume—which can cause misselection if the agent is not attentive to the descriptions.

    Naming Consistency3/5

    All names are snake_case and there is a recognizable prefix convention, which is helpful. But within each prefix the pattern is mixed: some are verb_noun (project_add_costume, social_post_comment), some are noun phrases (project_summary, sb3_project_info, spy_project_overview), and some are noun+verb (sb3_vm_run, sb3_vm_input). Retrieval verbs also vary between get_, list_, and read_ without a consistent rule.

    Tool Count1/5

    117 tools is an extreme tool count, far beyond what is practical for an agent to navigate or select from reliably. Even for a 'unified' Scratch server, this appears to be four or more separate tool surfaces merged into one, and the rubric explicitly flags 50+ tools as an extreme mismatch.

    Completeness4/5

    The server is functionally very complete: it covers goboscript project authoring, build/upload, Scratch website social interactions, .sb3 editing, VM-based testing, and ScratchPy code-to-blocks conversion. Minor gaps remain—social comment deletion/editing, studio creation/management, and project deletion/unsharing are absent—but these are workable around and do not block core workflows.

    Maintenance

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers