scratch-unified-mcp
Provides tools for building, playtesting, and publishing Scratch projects, including website/social graph access, text-based project authoring, Python-to-blocks conversion, and a headless Scratch VM for running and inspecting projects without a browser.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@scratch-unified-mcp Playtest my Scratch project headlessly and summarize what sprites do in the first 5 seconds."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.shThat 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/sidecarThen 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 |
| Load / write |
| Load the open project into a fresh VM |
| Press green flag (clears bubbles, question, errors) |
| Advance N seconds/frames, paced or flat-out. Returns state + ordered event timeline ( |
| Snapshot now: targets (x/y/vars/lists/costume), monitors, bubbles, question, thread count, errors |
| Keys, mouse position/clicks (stage coords), |
| Every live thread: target, clone flag, starting hat, stack depth, status ( |
| Full monitor table (visible or not) — watch a variable without pixels |
| Exactly one frame + before/after counts + delta (new threads, events that tick) |
| Deterministic PRNG (mulberry32 over |
| Poll-and-diff variable watcher: old/new/changed per key, per-clone capable |
| Recorded pen/sound stub calls since load |
| Pen raster (480×360 software canvas) as PNG base64 + pixel count. Strokes only |
| Offline sound mix as WAV base64: every play at its timer offset, volume/pitch approximate |
| Run on virtual time until a predicate fires ( |
| Write live VM state mid-run: variables/lists by target, sprite pose/costume. Fault injection the disk-editing |
| Clone census: pose, costume, sprite-locals per live clone |
| Query every target's blocks by opcode substring / field value / input name / hat-only |
| 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, IDENTIFIERSEverything 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
uukelele/scratch-mcp (MIT) — social + goboscript core
playforge-coding/scratch4js (MPL-2.0) — sb3/VM/bridge core
ZDStudios/scratchpy-studio (MIT) — blocks↔Python core
TurboWarp/scratch-vm — the headless engine
Available Tools
117 toolsproject_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_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_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 ownunreadflag.
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, soinclude_repliescosts one extra request per comment.source="profile": uses
page(30 top-level comments per page);offsetis ignored. Replies always come back, free of charge, soinclude_repliesis 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
Scored across 117 tools
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.
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.
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.
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
Related MCP Connectors
214 tools for AI agents over one MCP endpoint: image, video, audio, 3D, data, storage, mail, GPUs.
Free public MCP for AI agents — 193 tools, 44 workflows. No API key.
A registry of AI agent tools — MCP servers, APIs, CLIs, SDKs — kept current by automated ingestion.
Hosted MCP with 91 agent tools: X, domains, SEO, Maps, Trends, Search, YouTube, TikTok, and more.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA production MCP integration that lets AI agents control Roblox Studio to autonomously build, test, and debug Roblox games. Provides 39 tools for explorer control, script management, terrain generation, and autonomous testing.3MIT
- AlicenseCqualityAmaintenanceEnables AI agents to autonomously develop and test Godot 4 games through an MCP-based feedback loop, providing tools for authoring, running, observing, playtesting, and verifying game projects.20112 npm9MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to directly control Roblox, reverse engineer games, intercept network traffic, simulate user input, and manipulate game state through 150+ MCP tools.115 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to control Minecraft characters and build structures from blueprint files via MCP, with tools for listing, parsing, building, and verifying schematics.80 npmApache 2.0
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