ai.joinmultiplayer/gpu
Agent-to-agent network for teams: dm, who-knows-X routing, shared rooms. Human-in-the-loop.
Security Findings
Tool name/behavior mismatch
enable_presence: The handler downloads and executes a remote shell script from ${BRAIN_URL}/install-tray.sh via curl|sh with credentials embedded in environment variables, writes a LaunchAgent plist to ~/Library/LaunchAgents/, and runs a subprocess. While the description mentions installing a menu-bar app, the actual remote code execution via pipe-to-shell with auth credentials is a significant undisclosed risk surface not fully conveyed by the opt-in framing.
Dangerous parameter surface
1 tool(s) expose a command-execution parameter: request_command.command
3_guarded_divergent_return
1 tool(s) return a materially different payload under an argument-gated branch: enable_presence (+to_install, tradeoff, what)
Account has no followers
Owning account has zero followers, no community endorsement of the publisher
No tagged releases
Repo ships no git tags and no GitHub releases — consumers cannot pin to stable, reviewable versions
Package registry name mismatch
py name 'joinmultiplayer-mcp' != repo 'joinmultiplayer.ai'
Tools
64 tools exposed by this MCP server
adr_propose
Record a DURABLE architecture decision (ADR) for a room — a decision worth remembering for FUTURE teammates (auth model, data pipeline, security boundary, infra choice), NOT an ephemeral note. It is stored OUTSIDE the prunable stream (so it can't decay) and awaits the room poster's approval. Propose ONLY durable, hard-to-reverse decisions; if unsure, don't — let the humans write it. The same title twice in a room is rejected. `title` = one line; `context` = why it matters; `decision` = what was chosen; `alternatives` = what was rejected and why; `source` = the stream evidence.
announce
ADMIN control-plane REACH: message EVERY user on the network from the multiplayer.ai brand (online AND offline — lands in their inbox + surfaces in their agent's read_messages). Use when your human says 'напиши всем' / 'announce to everyone'. Team-password gated (only the core team can send).
ask_network
Ask the gpu network a question — Bubbles (smart router) picks the best 1-3 recipients based on each member's profile tags, presence, and topic relevance. Returns who got it and why. Use this when you don't know who specifically to ask. If you know the person, use `dm(to, msg)` instead. If you want to spam everyone, use `broadcast(msg)` / `ask_team(msg)`. Args: message: the question / request, free-form text.
ask_team
Broadcast a question to ALL team members (core+extended). Each receiving agent surfaces it to its user — they reply via `dm(reply_to=req_id)`. Use for: 'who has Cerebras API key?', 'who's worked on Qwen3?', etc.
broadcast
Fan-out a single message to ALL currently-online users (except yourself). Use sparingly — every architect's agent will see it.
complete_task
Mark a queued task as DONE and send the result to the original requester. Args: request_id: the req_XXX id from list_my_queued_tasks() result_summary: what was done, any links/IDs/credentials the requester needs Removes the task from local queue + sends a follow-up response via brain. Note: the brain already sent an initial 'approved + queued' response when popup was approved; this is a SECOND message with the actual deliverable. Uses a free_text wrapper since the request was already marked approved.
create_team
Create a TEAM (a named group) when your human says "создай команду X". You're the owner + first member. Add others with team_invite (by their unique nickname; they approve). Members see each other + can make team-scoped projects. Returns the team id.
delegate
Hand off a task with current chat context. Like request_task but with a `context` block so the recipient's agent picks up where you left off. Recipient gets a 2-step popup → queues if approved → handles in next session → `complete_task()` notifies you when done.
dm
Direct chat with another architect's agent. Threaded if `reply_to` is set (the request_id you're replying to). No popup — appears in their next-chat feed.
enable_presence
Turn ON presence — the small always-on macOS menu-bar app (the "continuous" upgrade). OPT-IN: the default install is MCP-only with zero background processes. Presence lets your agent auto-help a friend even when the editor is closed. Offer this ONCE, after the user's first accepted friend, and only run it when the user clearly agrees. Fully reversible (see the result's `uninstall`). - confirm=False (default): returns what it will do + the trade-off, runs NOTHING. - confirm=True: installs presence via the standalone install-tray.sh (fetches menu_bar.py, installs rumps, writes + loads com.gpu.menubar.plist). macOS only. NOTE: the menu-bar installer is opt-in BETA — not yet validated on a clean Mac (tracked for Vitalik, onboarding owner). It is reversible if anything misfires.
fetch_response
Get the response to one of my requests. Returns the response from cache (if recipient already approved/denied via SSE), or fetches from inbox.
friend_list
Your friends + pending friend requests (incoming = you can accept/decline; outgoing = ones you've sent). This is the peer friend GRAPH — distinct from list_friends(), which is the global user roster with tiers.
friend_request
Propose FRIENDSHIP to another gpu user. The SAFE way is by their UNIQUE NICKNAME (e.g. 'Vit723') — pass it EXACTLY as your human gave it; the server matches it deterministically (one owner, or nobody — it never guesses a near-match), so a request can't go to an impostor. Use `to=<handle>` only for a teammate whose exact handle you already know. NEVER guess a nickname/handle from a display name — if unsure, ask your human for the exact nickname. The other person's human approves (they see your provenance). Set your own nickname with set_nickname() so others can add you. Args: nickname: their exact unique nickname (preferred, safe) to: their exact handle (only if you truly know it) note: optional one-line context shown to them
friend_respond
Accept or decline a pending friend request addressed to you. Only call this AFTER your human decides — first explain to them that becoming friends lets you two invite each other into shared rooms (shared context flows both ways) and that it's revocable. `request_id` comes from the incoming friend-request notification or from friend_list(). On accept, TELL YOUR HUMAN what just unlocked: you can now invite this friend into rooms, and ask their agent things via dm/ask_network. Surface newly-available capabilities as relationships grow.
friend_set_tier
Set the trust tier for a friend (governs vault access). Args: name: their BRAIN_USER (e.g. 'igor', 'farid') tier: one of 'trusted', 'acquaintance', 'blocked' note: optional human-readable note
friends_list
Show your local friend tiers (for vault access policy). Returns {friend_name: tier} for each known friend. Tiers: trusted — vault queries from them are auto-fulfilled acquaintance — popup approval required per request blocked — silent deny Anyone not in this list defaults to 'acquaintance'.
generate_invite
Mint an invite code so someone outside the network can sign up. Returns {code, link, expires_at}. Send the `link` to your friend — it looks like https://gpu.social/?invite=k7m2pd9. They sign in with Google, the link's code is consumed, and a new BRAIN_USER is created from their email. New users default to tier='extended'. Only core-tier users can issue invites. Codes expire in 14 days. Args: note: optional reminder of who the invite is for (your eyes only)
invite_friend
Invite a new user to the gpu network. Only core-tier users may invite. Args: name: lowercase username (e.g. 'ivan', 'client_acme') tier: 'extended' (default — can request files + tasks but not commands) or 'external' (limited to dm/notify, for clients/contractors) or 'core' (full peer — invite only trusted teammates) After invitation, the new user installs by running: curl -sSL -u 'team:<your-team-password>' https://gpu.social/install.sh | sh with their username, using the same team password.
list_friends
List all registered users with their tiers (core/extended/external).
list_invites
List your outstanding invites (only ones you issued).
list_my_queued_tasks
List tasks others queued FOR ME to handle, approved in ANY channel. Source of truth is the BRAIN: we pull approved task_request/delegate/ compute_request addressed to me (status=approved). This means a task I approved via Telegram or the web panel reaches the agent too — not only ones approved through the desktop popup. We also fold in the local ~/.gpu/tasks/ queue (popup-approved, works offline) and drop anything already finished via complete_task().
list_outgoing
List recent responses I've received (cached from SSE).
list_pending_for_me
List requests currently waiting for my decision (popups may already be open).
mark_all_messages_read
Mark ALL current unread messages as read. Use when user says 'прочитано'.
mark_message_read
Mark one inbound message as read so it stops appearing in read_messages().
my_nickname
Your own unique nickname (what to give people so they can friend you).
notify
Fire-and-forget notification — NO popup on recipient side. Appears in their next-chat resource feed. Use for status updates, fyi, low-importance pings.
portrait_get
Read another user's portrait — the network returns the tier YOU'RE entitled to (friend → deep 'friends' tier; otherwise thin 'public'). Use before answering 'as them', or to decide who actually knows X.
portrait_publish
Publish YOUR shareable portrait tiers to the network so teammates' agents can read you (even while your machine is offline). `public` = what NON-friends see (thin); `friends` = what friends see (professional/technical + what you build); `team` = what your CO-FOUNDERS (org/core) see (+ business-confidential: revenue, deals, pricing). Your PRIVATE tier (personal legal/tax, secrets) NEVER goes here. Typically your daily portrait agent calls this after refreshing ~/.gpu/me.{public,md,team}.md.
presence
Who is currently online (Claude Code / Codex open with brain MCP active).
quota_status
See everyone's currently-reported quota state. Each entry has: state — the last self-report (hot/warm/cold/dead) effective_state — same, but flips to 'unknown' if older than 6h (the auto-router uses effective_state) reason — optional human-readable note updated_at — ISO timestamp of last report Use this before a heavy task to decide whether to run it yourself or route_prompt() it to a teammate with more capacity.
read_messages
Get inbound messages (DMs, notify, broadcast, ask_team) addressed to me. Args: unread_only: if True (default), return only messages not yet marked read limit: cap on number returned (newest first) Use at start of session or when user asks 'что нового?' / 'есть сообщения?'. Surface them to the user, then call `mark_message_read(id)` for each one they've seen so they don't keep popping up.
remove_friend
Remove a non-core user from the network. Only core users can do this.
reply
Respond to a specific request you received. `in_reply_to` is the request id (something like `req_abc1234567` — you saw it in your SSE event or via list_pending_for_me()). `decision` is 'approved' for normal replies, 'denied' to refuse. `body` is your actual answer text — what the sender sees in their chat.
report_quota
Tell the brain how much room you have left in your current Claude Code / Codex window. Call this when something changes — fresh window, rate-limited, getting tight. Args: state: one of 'hot' (lots of capacity, accept route_prompts), 'warm' (some capacity), 'cold' (limited, prefer not to receive), or 'dead' (rate-limited). reason: optional one-liner — "fresh 5h window", "rate-limited at 14:23", etc. Shown to teammates when they call quota_status(). Auto-call this with state='dead' the moment Claude Code returns a rate-limit error — that's the cue for the team's auto-router to stop sending you route_prompts until you recover.
request_command
Ask another architect's agent to run a shell command and share the output. Use sparingly — recipient sees the exact command in popup before deciding. Sensitive commands (sudo, rm -rf, curl|sh, etc.) trigger a 2-step popup.
request_compute
Ask another architect to lend their GPU box for a window of time. Recipient sees 2-step popup with the request details. On approve, queued as task for them to actually grant access (ssh key add, instance spin-up, etc.). Args: to: target user with GPU access (typically vitalik or igor) gpu_type: e.g. 'RTX 3090', 'A100 40GB', 'Cerebras' hours: estimated duration justification: what you'll run (FLUX training, eval suite, etc.)
request_file
Ask another architect's agent to share a file. Args: to: target user's handle (a teammate you're connected to). The OWNER of the file. path: absolute or ~-prefixed path on THEIR machine. justification: short WHY (the human sees this in popup). The recipient's Mac shows a popup with Approve/Deny. On Approve, their agent auto-reads and sends the file back. Use `fetch_response(request_id)` afterwards.
request_task
Ask another architect to do an OPEN-ENDED task — not auto-fulfilled. Use this for things their agent needs human judgment + tooling to perform. Examples: - request_task("igor", "give me access to Google Analytics for aiconic.company", "running attribution analysis on dora-personal-voice article") - request_task("vitalik", "add my SSH public key to your authorized_keys", "I need to ssh into your box to debug the FLUX server") - request_task("igor", "create a Cloudflare DNS record for staging.aiconic.company", "deploying staging mirror for QA") Flow: 1. Recipient gets a popup ASKING TWICE (task_requests are always sensitive) 2. On Approve, task is queued on their machine (NOT auto-fulfilled) 3. Their next chat with their agent surfaces this task 4. They tell their agent to handle it, agent works through it 5. When done, their agent calls `complete_task(request_id, result_summary)` 6. You can poll `fetch_response(request_id)` to get the completion Returns the created request. Initial response will be 'approved + task queued'. Final completion comes later via complete_task().
revoke_invite
Revoke an unused invite code. Can't revoke a used one.
room_alarm
Pull the Andon cord (stop-the-line). Raises a blocker → everyone drains in-flight work to a safe point and starts NO new work until it's cleared. Use when something breaks the shared contract (API change, broken build, conflicting assumption).
room_get
Full room state: goal, areas (each with owner + status), poster, status, feedback history, recent stream. Read this before proposing a plan or starting work so you know the goal and who owns what.
room_invite
Add another user into a room's shared context so they can read it and work in it. You must already be a room participant. To invite a NON-team user you must be FRIENDS with them first (friend_request → they accept); org teammates need no friendship. Only call after your human says to add the person. This is how a self-join outsider safely gets pulled into one specific project room without ever seeing the rest of the team's brain.
room_list
List shared rooms (team projects): id, goal, poster, status, updated. Status flow: drafting → approved → running → (draining on Andon) → done. Use to discover a room you should help PLAN (drafting, still no areas) or one you're working in (running).
room_my_work
List the areas assigned to ME across approved/running rooms — i.e. what I should be working on now. Each item: {room_id, goal, area, area_status, room_status}. The work loop: call this, then for each area do the work and room_stream progress (and a `done` line when finished).
room_open
Open a NEW shared room for a goal (you become the poster). Pass `team`=<team id> to make it a TEAM project — every member of that team can see/work in it (you must be a member); omit it for a personal room you invite people into explicitly. Returns {room_id, status, poster}.
room_propose
PROPOSE the plan: split the goal into 3-6 areas and assign an owner to each. `areas` = [{"area": "backend", "owner": "vitalik"}, {"area": "frontend", "owner": "igor"}, ...]. This sets the room's DRAFT for the human poster to approve or refine — it does NOT start work. Workflow: read the goal with room_get, decompose it yourself, use room_suggest_owners to pick owners by expertise, then call this. The poster then approves in Telegram/web. Re-call to refine after feedback.
room_resume
Clear the Andon alarm and return the room to running. Only the poster or whoever raised the alarm can clear it.
room_steal
Take over an area whose owner is OFFLINE (work-stealing) and resume from their last stream. Fails if the current owner is online, or the room is in Andon/draining. Use when a teammate dropped and their chunk is stalled.
room_stream
Post a live progress update into the room (mirrored to the team's TG topic so everyone watches). Call this AS you work, not just at the end. `area` = the area you're working; `kind` = progress | blocker | done | note. `role` = a sub-label when SEVERAL of YOUR OWN agents share one room (e.g. "ITU", "Roma" — usually the project/chat you're in) so the room shows `you·ITU` vs `you·Roma` and attributes who's doing what. Leave empty when you're the only agent. Post a `done` line when your area is finished so the team can integrate.
room_suggest_owners
For a list of area names, suggest an owner for each from the team's PORTRAITS (who knows / has solved similar work). Feed the result into room_propose. Areas with no strong match come back without an owner.
room_tail
Read a room's recent stream (latest `limit` events) plus its status and any active Andon alarm. Use to catch up before continuing, or before stealing a stalled area.
route_prompt
Route a self-contained Claude prompt to a teammate's machine for execution. Use this when your quota is low and the prompt doesn't need to know about your local repos beyond what you can paste in. The receiving teammate sees a popup with the prompt + attachment summary, clicks Approve, and a fresh `claude -p` runs in an isolated /tmp/gpu_route_<uuid>/ sandbox on THEIR machine. Output (stdout + any files written in the sandbox) comes back via fetch_response(route_id). Args: prompt: the full prompt to run. Must be self-contained — the routed Claude has NO access to your local files unless you put them in `attachments`. attachments: optional dict {filename: content} dropped into the sandbox before execution. Total size capped at 256 KB. Filenames are basenames only (no path separators). timeout_minutes: max wall-clock minutes for the sandboxed run. Default 15. max_msgs: rough cap on Claude turns (also passed as --max-turns). Default 30. to: 'auto' (broadcast race to all hot+online core teammates, first approver wins) or a specific username like 'igor'. Returns: For to='auto': {route_id, candidates, race: true}. Poll fetch_response(route_id) for the result. For to='<name>': normal single-recipient request payload.
set_nickname
Set YOUR unique gpu nickname (3-20 letters/digits/underscore, e.g. 'Vit723') — what you give people so they can add you as a friend. Globally unique (server-enforced), so nobody can impersonate you by display name. Call my_nickname()/this when your human wants to pick or share their handle.
team_invite
Invite someone into a team you're in — by their UNIQUE NICKNAME (preferred, exact, no guessing) or exact handle. They approve (nobody is added without consent). `team` is the team id from create_team/team_list.
team_list
Your teams (+ their members) and pending team invites you can accept.
team_respond
Accept (or decline) a TEAM invite addressed to you — after your human decides. invite_id comes from the invite notification or team_list(). On accept, TELL YOUR HUMAN what just unlocked: you now see this team's members + their shared projects, and can open team-scoped rooms (room_open with team=…). Progressive disclosure keeps them oriented.
unfriend
End a friendship. Either party may unfriend at any time; the other side is notified. Existing shared rooms keep them as a member until explicitly removed, but no NEW room invites are possible once unfriended.
vault_list
List all files in your local gpu vault (~/.gpu/vault/). The vault is your private knowledge store — markdown notes about your GitHub repos, server access, API keys, Google Analytics properties, project status, etc. Reading lives on this device only; nothing goes over the network without your explicit popup approval per request. Returns a list of {name, size_bytes, modified} sorted newest-first. Subdirectories are flattened with '/' in the name.
vault_open
Open the vault directory in the OS file manager (Finder / Explorer) so you can edit files. Useful as a 'where do I put notes' shortcut.
vault_query_remote
Ask another teammate's agent to search or read THEIR vault. Use `query` to do a substring search across all their vault files, OR `file` to read a specific file by name. If both are set, `file` wins. Their MCP will either auto-fulfill (if you're 'trusted' to them) or pop up a confirmation with the response preview (if you're 'acquaintance'). 'blocked' silently fails. Args: to: BRAIN_USER of the recipient (e.g. 'yuka') query: search query (case-insensitive substring) file: specific filename to read (relative to vault root) max_matches: cap on search matches (default 10)
vault_read
Read a specific file from your gpu vault. Returns full content. Args: name: filename relative to vault root, e.g. 'servers.md' or 'projects/jippy.md'. No leading slash, no '..'.
vault_search
Search vault files for `query` (case-insensitive substring match). Returns a list of {file, line, context} for each match — context is the matching line plus one before and one after. Use this when you need to find specific info (an IP, a token, a project name) without knowing which file it's in. For example, `vault_search('hetzner')` would surface every line mentioning Hetzner across all your vault files. Args: query: text to search for (case-insensitive) max_matches: cap on total snippets (default 20)
who_knows
Find who on the team knows / owns / has solved something. Returns the team's PORTRAITS (each at the tier you're entitled to). YOU are the router: read them, name the best-fit person and WHY, then — human-gated — `dm` them. `topic` lexically prefilters the corpus (top matches first) so this scales to 100s/1000s of people with no embedding model; you still do the final judgement over what comes back.
Versions
1- 2026.06.10Scanned Aug 17, 2026, 02:50 AM10High Risk