No description
Find a file
Vincent Sidot b66089148c Queue several sounds in one call
Six separate play_sound calls never built a queue: the round trip per
call was slower than the sounds themselves, so each one finished before
the next arrived. Add play_sounds(list) and a count parameter on
play_random_sound so a batch goes out back-to-back — four sounds now
take 0.2s and leave three of them actually queued.

Only the first play honours `mode`; the rest append, since interrupt or
play_next on every item would have each cancel or reorder the ones
before it. play_sounds resolves every name before playing anything, so
a bad name aborts the whole call instead of half-queueing it.

Reuse one keep-alive httpx client for the process rather than opening
one per request, so the batch shares a connection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 10:40:34 +02:00
.env.example Add MCP server for the soundboard API 2026-07-24 10:36:31 +02:00
.gitignore Add MCP server for the soundboard API 2026-07-24 10:36:31 +02:00
README.md Add MCP server for the soundboard API 2026-07-24 10:36:31 +02:00
requirements.txt Add MCP server for the soundboard API 2026-07-24 10:36:31 +02:00
server.py Queue several sounds in one call 2026-07-24 10:40:34 +02:00

soundboard-mcp

An MCP server that lets Claude drive the soundboard at https://soundboard.vsidot.fr.

The server never plays audio itself. It keeps a room (default main) with one queue and one playhead; every browser attached to that room plays in sync. So "play" means "broadcast to the room" — if nobody is attached the sound waits in the queue, and if three people are attached, three people hear it.

Tools

Tool Kind What it does
list_sounds read Search the library by text or tag; sort by title / plays / recency
get_sound read Details for one sound, by id or name
list_tags read Tags in use, most common first
get_playback_state read Now playing, queue, and who is actually listening
list_rooms read The rooms (main is the shared one)
get_statistics read Library totals and the most-played sounds
play_sound write Play a sound by id or name
play_random_sound write Play a random sound, optionally filtered by tag/text/favorites
set_favorite write Star / unstar a sound (per-account)
stop_playback destructive Silence the room and drop the queue
clear_queue destructive Drop the queue, leave the current sound playing
remove_queue_item destructive Remove one queued item

Names are resolved leniently: exact id → exact title → unique substring. An ambiguous name is refused with the candidate list rather than guessed at.

Play modes: enqueue (append — the default here, so a call never cuts anyone off), interrupt (stop everything and play now), play_next (jump the queue).

Deliberately not exposed: deleting sounds, imports (YouTube / MyInstants / upload / scan), the librarian's accept-and-rewrite endpoints, and user / invite / token administration. Those are destructive or admin-level and shouldn't be one model mistake away. Add them if you want them.

Setup

Needs uv (the system Python 3.9 is too old for the MCP SDK, which wants 3.10+).

uv venv --python 3.11
uv pip install -r requirements.txt
cp .env.example .env    # then put a real sb_... token in it

Mint a token under Account in the web app, or POST /api/v1/auth/tokens.

Verify config and connectivity without starting the server:

set -a; . ./.env; set +a
.venv/bin/python server.py --check

Registering with Claude Code

Already registered at user scope. To redo it elsewhere:

claude mcp add soundboard -s user \
  -e SOUNDBOARD_TOKEN=sb_... \
  -e SOUNDBOARD_URL=https://soundboard.vsidot.fr \
  -e SOUNDBOARD_ROOM=main \
  -- "$HOME/Documents/dev/s/soundboard-mcp/.venv/bin/python" \
     "$HOME/Documents/dev/s/soundboard-mcp/server.py"

The token lives in the MCP config (~/.claude.json) and in .env for local runs — never in server.py. .env is gitignored and mode 600.

Config

Variable Default Notes
SOUNDBOARD_TOKEN Required. sb_... API token.
SOUNDBOARD_URL https://soundboard.vsidot.fr Base URL.
SOUNDBOARD_ROOM main Room used when a tool gets no session.

The library is cached for 60s (SOUNDS_TTL in server.py) so searches don't refetch 314 sounds on every call.