Skip to main content
Glama
TheArmagan

vrchat-mcp

by TheArmagan

vrchat-mcp

Ein MCP-Server für die VRChat-API. Alle 297 Operationen aus der OpenAPI-Spezifikation, zur Build-Zeit generiert, plus handgeschriebene Tools für Dinge, die ein einzelner Endpunkt nicht leisten kann: Zwei-Faktor-Login, Datei-Uploads, Bildanzeige und die Ereignis-Pipeline.

Läuft lokal über stdio, als Unterprozess von Claude Code oder Claude Desktop. Schreibgeschützt, bis du etwas anderes sagst.

Basiert auf Bun, dem offiziellen MCP-TypeScript-SDK v2 und dem offiziellen vrchat JavaScript-SDK. Die Tools stammen aus der VRChat-OpenAPI-Spezifikation und sind eingecheckt, sodass die Oberfläche dem Upstream folgt, statt dagegen zu verrotten.

Spickzettel

bun install && bun link                # `vrchat-mcp` is now on PATH
cp .env.example .env                   # fill in username, password, contact
claude mcp add vrchat -- vrchat-mcp

Minimale .env:

VRCHAT_USERNAME=you
VRCHAT_PASSWORD=hunter2
VRCHAT_CONTACT=you@your-domain.tld     # must be real, VRChat 403s generic agents

Ich möchte

Tu das

Erstellen und Bearbeiten aktivieren

VRCHAT_MCP_ALLOW_WRITES=1

Löschen und Moderieren aktivieren

VRCHAT_MCP_ALLOW_DESTRUCTIVE_WRITES=1 hinzufügen

Ausgeben von Guthaben aktivieren

VRCHAT_MCP_ALLOW_PURCHASES=1 hinzufügen

Nur Storefront-Tools freigeben

VRCHAT_MCP_TAGS=store

Alles freigeben

VRCHAT_MCP_TAGS=everything

Herausfinden, warum ein Tool fehlt

vrchat_authStatus aufrufen

Riesige Payloads aus dem Kontext halten

_responseKeys: ["id","name"] bei jedem Tool

Ein Bild ansehen

vrchat_getImage mit einer imageUrl

Ein Bild hochladen

vrchat__uploadImage mit einem Pfad oder { data, mimeType }

Einen Login in einem neuen Netzwerk entstören

Den E-Mail-Link öffnen, dann vrchat_retryLogin

Toolnamen tragen ihre Herkunft. Zwei Unterstriche bedeuten aus der Spezifikation generiert (vrchat__getCurrentUser), der Name ist also in VRChats eigener Doku durchsuchbar. Ein Unterstrich bedeutet: Dieser Server hat es geschrieben (vrchat_authStatus).

Handgeschriebene Tools, vollständig:

Tool

Funktion

vrchat_authStatus

Login-Zustand, Rate-Limiter und welche Tool-Gruppen durch welche Env-Variable verborgen sind

vrchat_submitTwoFactorCode

Beantwortet einen geparkten Login mit dem Code, den der Benutzer vorgelesen hat

vrchat_retryLogin

Startet einen Login neu, nachdem ein Neues-Netz-E-Mail-Link geöffnet wurde

vrchat_logout

Löscht die gespeicherte Sitzung

vrchat_getImage

Lädt ein VRChat-Bild herunter und gibt es als anzeigbares Bild zurück

vrchat_uploadFile

Führt VRChats vierschrittigen Upload für Nicht-Bild-Dateien aus

vrchat_setProductImage

Lädt ein Bild hoch und hängt es an ein Store-Produkt

vrchat_eventsRecent

Ereignisse seit einem Cursor

vrchat_eventsWait

Blockiert, bis das nächste passende Ereignis eintrifft

vrchat_eventsSearch

Volltextsuche über die gespeicherte Ereignishistorie

vrchat_eventsStatus

Socket-Zustand und Aufbewahrung pro Typ

Die letzten vier erscheinen nur mit VRCHAT_MCP_WEBSOCKET=1.

Related MCP server: Portals MCP

Installation

bun link legt eine vrchat-mcp-ausführbare Datei auf deinen PATH, sodass nichts nachgelagert wissen muss, wo das Checkout liegt.

bun install
bun link          # from the repo root

Registriere es per Namen:

claude mcp add vrchat -- vrchat-mcp

Oder für Claude Desktop in claude_desktop_config.json:

{
  "mcpServers": {
    "vrchat": {
      "command": "vrchat-mcp"
    }
  }
}

Das ist die gesamte Konfiguration. Anmeldedaten kommen aus der .env des Repos, müssen hier also nicht wiederholt werden, obwohl alles, was du in einen env-Block schreibst, gewinnt. Entferne den Befehl mit bun unlink.

Wenn du lieber nichts auf deinen PATH legen möchtest, verweise mit einem absoluten Pfad auf die Einstiegsdatei. Der Server wird aus einem beliebigen Arbeitsverzeichnis gestartet, ein relativer Pfad reicht also nicht.

claude mcp add vrchat -- bun run /abs/path/to/vrchat-mcp/src/index.ts

Die Kontakt-Anforderung

VRChat lehnt generische User-Agents mit einem 403 ab. VRCHAT_CONTACT fließt in den beschreibenden User-Agent, den das SDK bei jeder Anfrage sendet, API und WebSocket gleichermaßen, und ist praktisch Pflicht.

Der Wert muss echt sein. Das SDK weigert sich, Kontakt mit @example.com zu akzeptieren, also ist der offensichtliche Platzhalter genau der Wert, der garantiert fehlschlägt. Der Server meldet das als Konfigurationsfehler beim ersten Tool-Aufruf, statt es als mysteriösen 403 auftauchen zu lassen.

Konfiguration

Drei Ebenen, höchste Priorität zuerst. Ein Projekt kann eigene Optionen setzen, ohne deine Anmeldedaten zu wiederholen.

  1. Echte Umgebungsvariablen, einschließlich des env-Blocks eines MCP-Clients

  2. .env im Verzeichnis, aus dem der Befehl läuft, das Bun automatisch lädt

  3. .env im Repo-Root

Ein Projekt, das nur Storefront-Tools will und deine bereits konfigurierten Anmeldedaten nutzt, braucht also eine Zeile daneben:

# ~/my-project/.env
VRCHAT_MCP_TAGS=store

Variable

Standard

Wirkung

VRCHAT_USERNAME

keine

Kontobenutzername oder E-Mail

VRCHAT_PASSWORD

keine

Kontopasswort

VRCHAT_TOTP_SECRET

keine

Base32-TOTP-Geheimnis. Gesetzt und Login fragt nie nach

VRCHAT_CONTACT

keine

Kontaktstring im User-Agent. Praktisch erforderlich

VRCHAT_MCP_TAGS

alle

Zu registrierende Tags. everything für keinen Filter

VRCHAT_MCP_ALLOW_WRITES

aus

Erstellen und Bearbeiten

VRCHAT_MCP_ALLOW_DESTRUCTIVE_WRITES

aus

Löschen und Moderieren. Braucht auch das Schreib-Gate

VRCHAT_MCP_ALLOW_PURCHASES

aus

Guthaben ausgeben. Braucht auch das Schreib-Gate

VRCHAT_MCP_ALLOW_ADMIN

aus

Admin-Operationen. Unabhängig vom Schreib-Gate

VRCHAT_MCP_RPS

20

Anfragen pro Sekunde. 0 fällt auf 20 zurück, es gibt kein Aus

VRCHAT_MCP_MAX_WAIT_MS

30000

Wie lange ein Aufruf hinter dem Limiter wartet, bevor er aufgibt

VRCHAT_MCP_WEBSOCKET

aus

Öffnet die Ereignis-Pipeline und registriert die Ereignis-Tools

VRCHAT_MCP_WS_EVENTS

Low-Noise-Set

Zu abonnierende Ereignistypen. Ersetzt den Standard, erweitert ihn nicht

VRCHAT_MCP_HISTORY

1000

Ereignisse pro Typ aufbewahrt. Pro-Typ-Überschreibungen: 1000,friend-location:200

VRCHAT_MCP_HISTORY_MAX_AGE

30d

Altersobergrenze. 0 deaktiviert. Akzeptiert ms s m h d w

VRCHAT_MCP_DB

Projekt-.vrchat-mcp/events.db

Ereignisdatenbank-Pfad

VRCHAT_MCP_SESSION

Projekt-.vrchat-mcp/session.json

Sitzungsdatei-Pfad

VRCHAT_MCP_PROXY

keine

HTTP- oder HTTPS-Proxy für API- und WebSocket-Verkehr

VRCHAT_MCP_2FA_TIMEOUT_MS

300000

Wie lange ein geparkter Login auf einen Code wartet

VRCHAT_LIVE_TESTS

aus

Optiert in die Live-Test-Suite

Booleans akzeptieren 1 oder true, ohne Beachtung der Groß-/Kleinschreibung.

Wo der Zustand lebt

Der Zustand ist pro Projekt. Starte den Server in einem Projekt und seine Sitzung und Ereignishistorie leben in dessen .vrchat-mcp/. Die Projektwurzel wird gefunden, indem man vom Arbeitsverzeichnis aus nach oben geht und nach .git, package.json, deno.json, pyproject.toml oder go.mod sucht. Ein Start aus einem Unterverzeichnis erreicht also denselben Zustand, statt eine zweite Sitzung eine Ebene tiefer zu stranden.

Das Verzeichnis versteckt sich selbst vor der Versionskontrolle: vrchat-mcp schreibt bei der Erstellung eine .gitignore mit * hinein, sodass die Sitzungsdatei, die eine Anmeldeberechtigung ist, geschützt ist, ohne dass das Host-Projekt eine Regel dafür braucht.

Jedes Projekt meldet sich also separat an, und der erste Aufruf in einem neuen Projekt kann nach einem 2FA-Code fragen. Um einen Login überall zu teilen, weise jede Installation auf dieselbe Datei:

VRCHAT_MCP_SESSION=/abs/path/to/shared/session.json

Sicherheits-Gates

Der Server startet schreibgeschützt. 150 der 297 Operationen registrieren sich standardmäßig. Nichts, was schreibt, löscht, ausgibt oder moderiert, erscheint, bis du danach fragst.

Klasse

Was sie abdeckt

Benötigt

Beispiele

read

Jedes GET

nichts

getCurrentUser, searchWorlds

write

POST / PUT / PATCH

ALLOW_WRITES

createInstance, updateWorld, updateProduct

destructive

Jedes DELETE, plus eine Überschreibungsliste

ALLOW_WRITES und ALLOW_DESTRUCTIVE_WRITES

deleteProduct, banGroupMember, kickGroupMember, closeInstance

money

Kaufen und Tilia/KYC/Payout-Pfade

ALLOW_WRITES und ALLOW_PURCHASES

purchaseProductListing, getEconomyPayouts, getUserTiliaKyc

admin

Admin- und Kontolebenszyklus

ALLOW_ADMIN

deleteUser, registerUserAccount, Moderationsberichte

Destruktiv und Geld schichten auf Schreiben auf, also gewährt das Aktivieren von Schreiben genau die Fähigkeit zu erstellen und zu bearbeiten, nie zu löschen oder auszugeben. Admin steht allein und wird von nichts impliziert: Einem Agenten das Bearbeiten eigener Inhalte zu erlauben, darf niemals auch das Löschen des Kontos erlauben.

Wofür du dich entscheidest:

  • ALLOW_WRITES lässt einen Agenten Dinge erstellen und ändern, die dir gehören. Umkehrbar, meist von Hand.

  • ALLOW_DESTRUCTIVE_WRITES fügt die Aufrufe ohne Rückgängig hinzu. Löschen, Sperren, Kicks, Schließen von Instanzen, Löschen der Benutzerpersistenz.

  • ALLOW_PURCHASES lässt einen Agenten echtes Guthaben ausgeben. purchaseProductListing ist eine Live-Transaktion. Setze das nicht, weil eine Tool-Liste unvollständig aussah.

  • ALLOW_ADMIN legt unter anderem deleteUser frei. Die meisten davon geben auf einem normalen Konto 403, aber deleteUser ist das, was niemals ein Unfall sein darf.

Gesperrte Operationen bleiben in der generierten Tabelle, sodass die Abdeckung 1:1 mit der Spezifikation bleibt und die Denylist im Diff überprüfbar ist. MCP-Annotationen (readOnlyHint, destructiveHint) sind ebenfalls gesetzt, sodass Clients, die sie anzeigen, warnen können.

Welche Tools fehlen mir?

Ein abgeschaltetes Tool ist schlicht nicht vorhanden, was sich als „VRChat kann das nicht“ liest statt als „diesem Server wurde untersagt, das zu tun“. Dieser Fehler ist in der Praxis bereits passiert: Ein Agent meldete die Economy-API als schreibgeschützt, obwohl die Schreib-Tools existierten und nur hinter einem Flag lagen.

vrchat_authStatus schließt diese Lücke. Es meldet jedes Tag und jede Sicherheitsklasse, wie viele Operationen jeweils dazugehören, wie viele aktuell verfügbar sind und die genaue .env-Änderung, die den Rest freischalten würde.

{
  "availability": {
    "toolsRegistered": 12,
    "toolsHidden": 285,
    "tagFilter": ["store"],
    "kinds": { "write": { "enabled": false, "hidden": 88 } },
    "nextSteps": [
      "88 `write` operations are hidden. Ask the user to set VRCHAT_MCP_ALLOW_WRITES=1 ..."
    ]
  }
}

Rufen Sie es auf, bevor Sie etwas als nicht unterstützt einstufen.

Welche Tools freigeschaltet werden

VRCHAT_MCP_TAGS wählt Tags aus. Ist die Variable nicht gesetzt, wird alles registriert, und everything sagt das explizit, was einfacher ist, als einen Schlüssel aus einer JSON-Konfiguration zu löschen. all und * funktionieren ebenfalls.

VRCHAT_MCP_TAGS=everything          # all 297 operations
VRCHAT_MCP_TAGS=store               # just the storefront, 19 operations
VRCHAT_MCP_TAGS=store,users,worlds  # matches any of the three

Spec-Tags: authentication, avatars, calendar, economy, favorites, files, friends, groups, instances, inventory, invite, jams, miscellaneous, notifications, playermoderation, prints, props, users, worlds. Dazu kommt store, das dieser Server hinzufügt.

Ein Tag, das auf nichts passt, wird beim Start über stderr gemeldet und von vrchat_authStatus angezeigt. Ohne das würde ein Tippfehler wie stores keine generierten Tools registrieren und genau wie ein defekter Server aussehen.

Anmelden

Die Anmeldung erfolgt erst bei Bedarf. Beim Start wird nichts authentifiziert, daher funktioniert tools/list auch ganz ohne Anmeldedaten, und der Server bleibt inspizierbar. Der erste Tool-Aufruf, der eine Sitzung benötigt, löst die Anmeldung aus.

Wenn VRCHAT_TOTP_SECRET gesetzt ist, ist das die ganze Geschichte. Keinerlei Aufforderungen.

Ohne das schickt VRChat einen Code per E-Mail, und der Aufruf kommt geparkt zurück, statt zu hängen:

vrchat__getCurrentUser
  -> Login paused: VRChat emailed a code. Ask the user for it, call
     vrchat_submitTwoFactorCode { requestId: 'a1b2c3d4', code: '……' },
     then retry the original call.

Beantworten Sie es mit vrchat_submitTwoFactorCode und versuchen Sie es dann erneut. Die Sitzung bleibt erhalten, sodass dies einmal pro Projekt passiert, bis sie abläuft.

Anmeldung aus einem neuen Netzwerk

Der Wechsel von Proxy, VPN oder ISP löst eine Prüfung aus, die kein Zwei-Faktor-Code ist, und beides sieht einander ähnlich genug, um echte Zeit zu verschwenden. VRChat antwortet mit einem der folgenden:

401  It looks like you're logging in from somewhere new! Check your email for a message from VRChat.
429  Logging in from too many places? Check your email for verification link

Beide bedeuten dasselbe, und keins ist das, was es zu sein scheint. Die E-Mail enthält einen Link, keinen sechsstelligen Code, daher kann vrchat_submitTwoFactorCode nicht helfen. Die Anmeldung erfordert zwei Runden:

  1. Ein Tool-Aufruf schlägt mit einer dieser Meldungen fehl.

  2. Der Benutzer öffnet den Link in der E-Mail.

  3. Rufen Sie vrchat_retryLogin auf. VRChat sendet den eigentlichen Code erst beim zweiten Versuch.

  4. Der Benutzer liest den Code vor; rufen Sie vrchat_submitTwoFactorCode auf.

  5. Wiederholen Sie das ursprüngliche Tool.

Die 429 ist eine Auth-Herausforderung, die den Status einer Rate-Limit-Meldung trägt, deshalb ignoriert sie der lokale Begrenzer. Warten behebt sie nicht, und jeder weitere Versuch kostet einen der begrenzten Sitzungsplätze des Kontos, was die 429 überhaupt erst verursacht. Eine fehlgeschlagene Anmeldung wird 30 Sekunden lang zwischengespeichert, damit aus einem Schub von Tool-Aufrufen kein Schub von Anmeldeversuchen wird. vrchat_retryLogin leert diesen Cache, weil der Benutzer bis dahin das getan hat, worauf der Fehler gewartet hat.

Parallele Aufrufe während der Anmeldung

Agents stoßen Tool-Aufrufe gleichzeitig an, und bei einem Kaltstart landen sie alle auf einem nicht authentifizierten Client. Ein Aufruf steuert die Anmeldung. Die anderen warten bis zu drei Sekunden und geben dann login_pending zurück, statt zu blockieren. So hält eine langsame Anmeldung einen Tool-Aufruf auf statt alle, und eine Anmeldung, die auf einen Code wartet, erzeugt eine Aufforderung statt mehrerer.

_responseKeys

Jedes Tool akzeptiert _responseKeys, und jedes Tool gibt standardmäßig die rohe Upstream-Payload zurück. Es gibt keine serverseitige Kuratierung, denn eine handverlesene Feldliste rät, was wichtig ist, ist für den falsch, der das andere Feld braucht, und müsste für 297 Operationen gegen eine sich bewegende Spezifikation gepflegt werden. Der Agent weiß, was er bei diesem Aufruf will. Er sollte es sagen.

Ein World-Objekt ist ungefähr 4 KB groß. Eine Einschränkung reduziert das meist um mehr als die Hälfte.

Muster

Wählt aus

["*"]

die gesamte Antwort, Byte für Byte

["id","name"]

diese Top-Level-Felder

["author.displayName"]

einen verschachtelten Pfad

["*.id"]

id aus jedem Element eines Top-Level-Arrays

["items.*.name"]

dieses Feld aus jedem Element von items

["unityPackages.*.**"]

alles unterhalb jedes Elements

["!description"]

schließt aus und kombiniert mit ["*"]

Die Projektion behält die Form. Objekte bleiben verschachtelt, Arrays behalten ihre Reihenfolge und Länge, sodass ein Pfad, der in einem Aufruf gelernt wurde, auch im nächsten funktioniert.

Das Entdecken ist wichtiger als die Projektion. Ein Agent kann nicht nach Schlüsseln fragen, von denen er nicht weiß, dass es sie gibt, und ein stilles, leeres Ergebnis würde dieses Design schlechter machen als das Beschneiden. Daher wird ein Pfad, der auf nichts passt, als _unmatched zurückgegeben, zusammen mit _availableKeys, das auflistet, was tatsächlich vorhanden war. Array-Element-Schlüssel werden als *.id, *.name benannt, also in der Form, die als _responseKeys-Eintrag funktioniert.

["*"] gibt die Eingabe per Referenz zurück, sodass der rohe Pfad nachweislich verlustfrei ist und nichts jemals verborgen bleibt.

Bilder anzeigen

vrchat_getImage lädt ein VRChat-Bild herunter und gibt es als Bildblock zurück, sodass das Modell es ansehen kann, statt nur eine URL zu melden.

{ "name": "vrchat_getImage",
  "arguments": { "url": "https://api.vrchat.cloud/api/1/file/file_.../1/256" } }

Übergeben Sie eine beliebige imageUrl oder thumbnailImageUrl von einem Benutzer, einer Welt, einem Avatar, einem Print oder einem Produkt, oder übergeben Sie fileId und lassen Sie das Tool die URL erstellen. savePath schreibt die Bytes zusätzlich auf die Festplatte.

Bevorzugen Sie eine URL, die auf /256 oder /512 endet, sofern eine solche existiert. Das Bild wird als Base64 übertragen, daher kostet eine Textur in voller Größe viel Kontext und bringt kein zusätzliches Detail. Alles über 4 MB wird abgelehnt; erhöhen Sie maxBytes, wenn Sie es ernst meinen.

Das Tool ruft nur bei VRChat gehostete Bilder ab, und nur an api.vrchat.cloud wird jemals Ihr Sitzungscookie gesendet. Ein Tool, das eine vom Aufrufer gelieferte URL abruft, während es eine Sitzung hält, ist ein Grundbaustein für Request Forgery, sofern es nicht eingeschränkt ist, und ein Cookie, das an ein CDN gesendet wird, ist ein verschenktes Cookie.

Dateien hochladen

Übergeben Sie einen lokalen Dateipfad. Der Server läuft auf Ihrem Rechner und liest die Datei selbst, daher gelangen die Dateiinhalte nie in die Unterhaltung. Ein 2-MB-PNG als Base64 inline einzubetten würde ungefähr 2,7 MB an Tool-Argumenten kosten, mehr als alles andere im Aufruf zusammen.

Wenn keine Datei auf der Festplatte liegt, etwa ein Bild, das der Agent gerade erzeugt hat, akzeptiert dasselbe Argument die Bytes inline:

{ "file": { "data": "iVBORw0KGgo...", "mimeType": "image/png", "filename": "icon.png" } }

Auch an der String-Position funktioniert eine data:-URI, daher ist "file": "data:image/png;base64,iVBORw0..." gleichwertig. filename ist optional und wird bei Weglassen aus dem MIME-Typ abgeleitet, weil VRChat einen Upload ablehnt, den es nicht benennen kann. Bevorzugen Sie einen Pfad, wann immer einer existiert: Inline kostet etwa 1,33 Bytes Tool-Argument pro Byte Datei, und das stammt aus demselben Kontextbudget wie alles andere.

Acht Operationen nehmen eine Datei direkt entgegen, jeweils ein Aufruf:

Tool

Feld

Verwendung

vrchat__uploadImage

file

Icons, Galerie, Emoji, Sticker, Produktbilder (tag wählt aus)

vrchat__uploadPrint

image

Prints

vrchat__uploadIcon

file

Profilbilder

vrchat__uploadGalleryImage

file

Galerie

vrchat__editPrint

image

Ersetzen des Bilds eines Prints

vrchat__inviteUserWithPhoto

image

Einladungsfotos

vrchat__requestInviteWithPhoto

image

Einladungsanfragen

vrchat__respondInviteWithPhoto

image

Einladungsantworten

{ "name": "vrchat__uploadImage",
  "arguments": { "file": "C:/Users/me/Pictures/icon.png", "tag": "icon" } }

Das Ergebnis benennt die gesendeten Bytes und die Form, in der sie angekommen sind. Das ist die einzige Möglichkeit, einen erfolgreichen Upload der richtigen Datei von einem erfolgreichen Upload der falschen zu unterscheiden:

{ "uploaded": [
    { "field": "file", "name": "icon.png", "bytes": 48211, "type": "image/png", "source": "path" }
  ],
  "result": { "id": "file_...", "name": "icon.png" } }

Für alles andere führt vrchat_uploadFile VRChats Vier-Schritte-Sequenz aus (Datensatz anlegen, eine vorab signierte URL anfordern, die Bytes übertragen, abschließen) und gibt den vollständigen Dateidatensatz zurück. Verwenden Sie es für Asset-Bundles und unity packages. Die Bytes gehen mit einer einfachen Anfrage direkt an VRChats Speicheranbieter, bewusst nicht über den API-Client, weil dieser Client Ihr Sitzungscookie an alles anhängt, was er sendet, und der Speicherhost ein Dritter ist.

Uploads sind Schreibvorgänge, daher benötigt das alles VRCHAT_MCP_ALLOW_WRITES=1. Dateien sind auf 100 MB begrenzt, und eine leere Datei wird abgelehnt, bevor sie VRChat erreicht, das andernfalls einen defekten Datensatz speichern würde. Wenn vrchat_uploadFile auf halbem Weg scheitert, nennt es den Dateidatensatz, den es angelegt hat, sodass Sie ihn mit vrchat__getFile untersuchen und mit vrchat__deleteFile entfernen können.

Einen Shop betreiben

Die Verwaltung einer Storefront ist ein gewöhnlicher Schreibvorgang, keine money-Operation. Ein Produkt zu erstellen, umzubenennen, sein Bild zu ändern, ein Angebot zu veröffentlichen oder zurückzuziehen: Nichts davon gibt Geld aus oder nimmt Geld ein, daher ist nur VRCHAT_MCP_ALLOW_WRITES=1 nötig. Das money-Gate ist für Käufe und den Zahlungsabwickler.

VRCHAT_MCP_TAGS=store
VRCHAT_MCP_ALLOW_WRITES=1

Das Setzen eines Produktbilds erfordert einen Aufruf:

{ "name": "vrchat_setProductImage",
  "arguments": { "productId": "prod_...", "file": "/abs/path/cover.png" } }

Das lädt mit tag: "product" hoch und setzt die zurückgegebene Datei-ID als imageId des Produkts. Von Hand sind das vrchat__uploadImage mit tag: "product" oder "listinggallery" und danach vrchat__updateProduct mit der zurückgegebenen ID.

Eines erlaubt VRChat selbst nicht: Ein Angebot legt nur active zum Bearbeiten offen, daher können Preis, Titel und Beschreibung nach der Erstellung nicht geändert werden. Löschen Sie das Angebot und erstellen Sie ein neues. Name, Beschreibung und Bild liegen am Produkt und sind über vrchat__updateProduct bearbeitbar.

Paginierung

Eine Seite pro Aufruf. Paginierte Tools verwenden standardmäßig 25 Ergebnisse und geben ein nextOffset zum Fortsetzen zurück. Es gibt bewusst keine interne Paginierungsschleife: Ein verstecktes automatisches Weiterblättern würde das Anfragebudget und einen großen Teil des Kontexts verbrauchen, und zwar innerhalb eines Aufrufs, der für den Agenten wie ein einzelner aussieht.

Eine kurze Seite bedeutet das Ende. VRChat meldet keine Gesamtzahl, daher ist das das einzige zuverlässige Signal.

WebSocket-Ereignisse

Standardmäßig deaktiviert, weil ein im Leerlauf dauerhaft geöffneter Socket einen Sitzungsplatz verbraucht. Setzen Sie VRCHAT_MCP_WEBSOCKET=1, um ihn zu öffnen und die vier vrchat_events*-Tools zu registrieren.

VRCHAT_MCP_WS_EVENTS wählt aus, welche Typen abonniert werden, und ersetzt statt zu erweitern die Standardmenge aus notification, notification-v2, economy-update, friend-online, friend-offline und instance-queue-ready.

Pipeline-Nachrichten sind doppelt kodiert: Das Feld content ist stringifiziertes JSON und muss ein zweites Mal geparst werden, außer bei see-notification und hide-notification, die nackte IDs tragen. All das wird beim Empfang einmal normalisiert, sodass kein Tool Ihnen jemals einen JSON-String innerhalb von JSON übergibt. Der eigene Socket des SDK verwirft diese beiden Nachrichtentypen stillschweigend, was ein Grund dafür ist, dass dieser Server es nicht verwendet. Der andere Grund ist, dass es keinen Proxy akzeptiert.

Aufbewahrung pro Typ

Der Verlauf wird in SQLite unter .vrchat-mcp/events.db abgelegt, und es werden 1000 Ereignisse pro Ereignis Typ aufbewahrt, nicht 1000 insgesamt. Ein gesprächiger Typ wie friend-location kann einen seltenen, wertvollen Typ wie economy-update nie verdrängen, was eine einzige globale Obergrenze innerhalb von Minuten tun würde.

VRCHAT_MCP_HISTORY=1000,friend-location:200,economy-update:5000
VRCHAT_MCP_HISTORY_MAX_AGE=7d

Eine Altersobergrenze läuft parallel zum Mengenlimit, und was zuerst greift, gewinnt. Nur die Menge würde einen selten ausgelösten Typ monatelang alte Ereignisse behalten lassen, die wie aktuell wirken. Nur das Alter würde einen Schwall die Datenbank sprengen lassen. vrchat_eventsStatus meldet, welches Limit pro Typ gerade greift, sodass das Zeitfenster nachvollziehbar ist, statt unbemerkt zu bleiben.

Der Verlauf überlebt Neustarts, daher kann vrchat_eventsSearch beantworten, was passiert ist, während Sie nicht da waren. Ein reiner Live-Puffer kann das nicht.

Proxy

VRCHAT_MCP_PROXY leitet den Datenverkehr über einen HTTP- oder HTTPS-Proxy, mit optionalen user:pass@-Zugangsdaten.

VRCHAT_MCP_PROXY=http://127.0.0.1:8080
VRCHAT_MCP_PROXY=https://user:pass@proxy.internal:8443

SOCKS wird nicht unterstützt. Buns fetch lehnt socks5:// rundweg ab, sodass eine SOCKS-URL beim Start mit einem Konfigurationsfehler fehlschlägt, der die Einschränkung benennt, anstatt nur halb zu funktionieren. Stellen Sie stattdessen einen lokalen HTTP-Proxy davor.

Der Proxy deckt sowohl API- als auch WebSocket-Datenverkehr ab. Die beiden laufen über unterschiedliche Mechanismen, und der Fehlerfall, nur die Hälfte richtig zu machen, ist ein Server, der wie ein Proxy aussieht, während er seine echte IP im Event-Stream preisgibt.

Wenn der Proxy nicht erreichbar ist, schlagen Aufrufe mit einem klaren Fehler fehl. Der Server fällt niemals stillschweigend auf eine direkte Verbindung zurück, denn für jeden, der dies zur IP-Trennung nutzt, wäre das das schlechtestmögliche Ergebnis. Die Proxy-URL wird nie protokolliert, da sie Anmeldedaten enthalten kann.

Entwicklung

bun link                    # install the vrchat-mcp command on PATH
bun unlink                  # remove it
bun run generate            # regenerate tools from the latest upstream spec
bun run generate --offline  # regenerate from the committed snapshot, no network
bun test                    # offline suite
bun run test:live           # live suite, needs VRCHAT_LIVE_TESTS=1
bun run inspect             # MCP Inspector against this server
bun run typecheck           # tsc --noEmit

bun run generate ruft vrchatapi/specification auf main ab, bündelt es und schreibt die gebündelte Spezifikation plus spec/VERSION.json (Upstream-SHA, Zeitstempel, Inhalts-Hash) zusammen mit der neu generierten src/generated/operations.ts. Beide werden committet, sodass jede Regenerierung zwei überprüfbare Diffs erzeugt, die Spezifikationsänderung und die dadurch verursachte Tool-Änderung, und ein schlechter Upstream-Commit ist revertierbar statt tragend. --offline reproduziert die Ausgabe aus dem committeten Snapshot Byte für Byte ohne jegliches Netzwerk.

src/generated/operations.ts wird generiert. Nicht von Hand bearbeiten.

Zehn operationIds haben keine passende Methode im VRChat SDK, weil die Spezifikation schneller voranschreitet als die Client-Bibliothek. Diese laufen über einen Raw-Request-Fallback auf demselben Client, sodass Cookies, User-Agent, Proxy und Ratenbegrenzung weiterhin gelten, und die 1:1-Abdeckung bleibt wahr, anstatt stillschweigend zur Lüge zu werden. Codegen gibt die Liste bei jedem Lauf aus.

stdout ist der JSON-RPC-Kanal. Die gesamte Protokollierung erfolgt über stderr, und ein einzelnes console.log beschädigt den Protokollstrom.

Tests

bun test ist die Offline-Suite: Codegen-Ausgabe, Gating, der Ratenbegrenzer gegen eine simulierte Uhr, Aufbewahrung und Suche des Verlaufs, Projektion, Fehlerzuordnung, Behandlung von Upload-Pfaden. Kein Netzwerk, keine Anmeldedaten, kein Konto. Das wird standardmäßig ausgeführt.

bun run test:live greift auf ein echtes Konto zu, das mit VRCHAT_LIVE_TESTS=1 aktiviert wird und andernfalls übersprungen wird. Regeln, an die es sich hält:

  • Nur Lese- und Ersteller-eigene Schreibvorgänge. Es lehnt alles, was als money oder admin klassifiziert ist, hart ab, bevor überhaupt ein Client konstruiert wird. Eine Testsuite darf kein Geld ausgeben können.

  • Jeder Schreibvorgang räumt nach sich selbst auf und ist markiert, sodass verirrte Artefakte im Spiel identifizierbar sind.

  • Es läuft durch denselben Begrenzer wie die Produktion und bleibt klein. Ein Lauf, der VRChats Drosselung auslöst, ist schlimmer als kein Lauf.

  • Assertions betreffen Form und Status, niemals volatile Inhalte. Freundeszahlen und Weltlisten ändern sich zwischen Läufen.

  • Verwenden Sie, wo möglich, ein dediziertes Konto. Anmeldedaten stammen nur aus .env.

Sicherheit

  • .env und .vrchat-mcp/ sind gitignored, und .vrchat-mcp/ ignoriert sich auch selbst von innen, sodass es in anderen Projekten verborgen bleibt.

  • .vrchat-mcp/session.json ist eine Anmeldeinformation, ein gültiges Session-Cookie. Behandeln Sie es wie ein Passwort. Das Löschen oder der Aufruf von vrchat_logout erzwingt eine neue Anmeldung.

  • 2FA-Codes, Passwörter, TOTP-Geheimnisse und Proxy-URLs werden nie protokolliert, auch nicht auf stderr.

  • Ihr Session-Cookie geht an api.vrchat.cloud und sonst nirgendwohin. Uploads an VRChats Speicheranbieter und Bildabrufe von dessen CDN umgehen bewusst den authentifizierten Client.

  • Fehler kommen als strukturierte Ergebnisse zurück, die Status, VRChats eigene Meldung und einen umsetzbaren Hinweis enthalten. Rohe Ausnahmen und Stack-Traces erreichen nie das Transkript.

  • Nur stdio, nur lokal. Kein HTTP-Transport, keine Isolierung von Anmeldedaten für mehrere Benutzer. Dieser Server ist für ein Konto auf einer Maschine.

Projektlayout

scripts/generate-tools.ts     # build-time codegen: spec -> src/generated/operations.ts
spec/openapi.bundled.json     # committed snapshot of the upstream spec
spec/VERSION.json             # upstream SHA + fetch timestamp + content hash
src/config.ts                 # the entire env surface, read once
src/types.ts                  # shared contracts
src/generated/operations.ts   # committed, generated, 297 entries, do not edit
src/vrchat/client.ts          # lazily-authed VRChat client, proxy, 2FA sniffing
src/vrchat/twofactor.ts       # pending-code broker
src/vrchat/ratelimit.ts       # token bucket + global 429 backoff
src/vrchat/events.ts          # websocket client + waiter registry
src/vrchat/history.ts         # bun:sqlite event store, per-type retention + FTS5 search
src/tools/auth.ts             # authStatus / submitTwoFactorCode / retryLogin / logout
src/tools/images.ts           # getImage
src/tools/upload.ts           # uploadFile / setProductImage
src/tools/events.ts           # eventsRecent / eventsWait / eventsSearch / eventsStatus
src/registry.ts               # gating, registration, the one shared handler
src/project.ts                # _responseKeys path projection
src/upload.ts                 # local path -> File, with size and type guards
src/errors.ts                 # HTTP status -> structured tool error with hint
src/index.ts                  # serveStdio entry point
tests/                        # offline suite; tests/live/ is the opt-in live suite
docs/PLAN.md                  # design document
PROGRESS.md                   # build status and verified SDK behaviour

Lizenz

Siehe LICENSE.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables remote control of Lovense toys through Claude using natural language commands. Supports vibration patterns, presets, and intensity control from any device via Cloudflare Workers.
    4
    Apache 2.0
  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables Claude to design and build interactive 3D games within the Portals virtual platform through direct API integration. It facilitates automated asset placement, interaction logic configuration, and quest management using natural language commands.
    4

View all related MCP servers

Related MCP Connectors

  • WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.

  • Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer

  • Garmin data in Claude & ChatGPT via the Garmin Health API. OAuth sign-in, no password sharing.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TheArmagan/vrchat-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server