Skip to main content
Glama
TheArmagan

vrchat-mcp

by TheArmagan

vrchat-mcp

Un servidor MCP para la API de VRChat. Las 297 operaciones de la especificación OpenAPI, generadas en tiempo de compilación, más herramientas escritas a mano para lo que un solo endpoint no puede hacer: inicio de sesión con dos factores, subida de archivos, visualización de imágenes y el pipeline de eventos.

Se ejecuta localmente sobre stdio, como subproceso de Claude Code o Claude Desktop. Solo lectura hasta que tú digas lo contrario.

Construido con Bun, el SDK oficial de MCP TypeScript v2 y el SDK oficial de JavaScript de vrchat. Las herramientas provienen de la especificación OpenAPI de VRChat y están confirmadas, de modo que la superficie sigue el ritmo de la especificación en lugar de pudrirse.

Hoja de referencia

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

.env mínimo:

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

Quiero...

Hago esto...

Habilitar creación y edición

VRCHAT_MCP_ALLOW_WRITES=1

Habilitar borrado y moderación

añadir VRCHAT_MCP_ALLOW_DESTRUCTIVE_WRITES=1

Habilitar gasto de saldo

añadir VRCHAT_MCP_ALLOW_PURCHASES=1

Exponer solo herramientas de tienda

VRCHAT_MCP_TAGS=store

Exponer todo

VRCHAT_MCP_TAGS=everything

Saber qué herramienta falta

llamar a vrchat_authStatus

Evitar que cargas útiles enormes consuman contexto

_responseKeys: ["id","name"] en cualquier herramienta

Ver una imagen

vrchat_getImage con un imageUrl

Subir una imagen

vrchat__uploadImage con una ruta, o { data, mimeType }

Arreglar un inicio de sesión bloqueado en una red nueva

abrir el enlace del correo y luego vrchat_retryLogin

Los nombres de las herramientas llevan su origen. Dos guiones bajos significan generado a partir de la especificación (vrchat__getCurrentUser), de modo que el nombre se puede buscar en la documentación propia de VRChat. Un guion bajo significa que este servidor lo escribió (vrchat_authStatus).

Herramientas escritas a mano, en su totalidad:

Herramienta

Qué hace

vrchat_authStatus

Estado de inicio de sesión, limitador de tasa y qué grupos de herramientas están ocultos por qué variable de entorno

vrchat_submitTwoFactorCode

Responde a un inicio de sesión en espera con el código que el usuario ha leído

vrchat_retryLogin

Reinicia un inicio de sesión después de que se abra el enlace del correo en una red nueva

vrchat_logout

Borra la sesión almacenada

vrchat_getImage

Descarga una imagen de VRChat y la devuelve como imagen visible

vrchat_uploadFile

Ejecuta la subida de archivos en cuatro pasos para archivos que no son imágenes

vrchat_setProductImage

Sube una imagen y la adjunta a un producto de la tienda

vrchat_eventsRecent

Eventos desde un cursor

vrchat_eventsWait

Bloquea hasta el siguiente evento que coincida

vrchat_eventsSearch

Búsqueda de texto completo en el historial de eventos

Las últimas cuatro solo aparecen con VRCHAT_MCP_WEBSOCKET=1.

Related MCP server: Portals MCP

Instalación

bun link coloca un ejecutable vrchat-mcp en tu PATH, de modo que nada aguas abajo necesita saber dónde está el checkout.

bun install
bun link          # from the repo root

Regístralo por nombre:

claude mcp add vrchat -- vrchat-mcp

O, para Claude Desktop, en claude_desktop_config.json:

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

Esa es toda la configuración. Las credenciales provienen del .env del repositorio, así que no hace falta repetirlas aquí, aunque cualquier cosa que pongas en un bloque env tiene prioridad. Elimina el comando con bun unlink.

Si prefieres no tocar tu PATH, apunta al archivo de entrada con una ruta absoluta. El servidor se lanza desde un directorio de trabajo arbitrario, así que una ruta relativa no servirá.

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

El requisito de contacto

VRChat rechaza User-Agents genéricos con un 403. VRCHAT_CONTACT se incorpora al User-Agent descriptivo que el SDK envía en cada petición, tanto a la API como a WebSocket, y es efectivamente obligatorio.

El valor tiene que ser real. El SDK rechaza cualquier contacto que contenga @example.com, así que el marcador obvio es el único valor que garantiza el fallo. El servidor lo notifica como un error de configuración en la primera llamada a una herramienta, en lugar de dejarlo aparecer como un 403 misterioso.

Configuración

Tres capas, de mayor a menor prioridad. Un proyecto puede definir sus propias opciones sin repetir tus credenciales.

  1. Variables de entorno reales, incluido el bloque env de un cliente MCP

  2. .env en el directorio desde el que se ejecuta el comando, que Bun carga automáticamente

  3. .env en la raíz del repositorio

Así, un proyecto que solo quiere herramientas de tienda, usando las credenciales que ya configuraste, necesita una línea junto a las demás:

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

Variable

Valor por defecto

Efecto

VRCHAT_USERNAME

ninguna

Nombre de usuario o correo de la cuenta

VRCHAT_PASSWORD

ninguna

Contraseña de la cuenta

VRCHAT_TOTP_SECRET

ninguna

Secreto TOTP en base32. Si lo defines, el inicio de sesión nunca pedirá un código

VRCHAT_CONTACT

ninguna

Cadena de contacto en el User-Agent. Efectivamente obligatorio

VRCHAT_MCP_TAGS

all

Etiquetas a registrar. everything para no filtrar

VRCHAT_MCP_ALLOW_WRITES

off

Crear y editar

VRCHAT_MCP_ALLOW_DESTRUCTIVE_WRITES

off

Borrar y moderar. También requiere la puerta de escritura

VRCHAT_MCP_ALLOW_PURCHASES

off

Gastar saldo. También requiere la puerta de escritura

VRCHAT_MCP_ALLOW_ADMIN

off

Operaciones de administración. Independiente de la puerta de escritura

VRCHAT_MCP_RPS

20

Peticiones por segundo. 0 vuelve a 20; no se puede desactivar

VRCHAT_MCP_MAX_WAIT_MS

30000

Cuánto espera una llamada detrás del limitador antes de rendirse

VRCHAT_MCP_WEBSOCKET

off

Abre el pipeline de eventos y registra las herramientas de eventos

VRCHAT_MCP_WS_EVENTS

conjunto de bajo ruido

Tipos de eventos a los que suscribirse. Reemplaza el valor por defecto; no lo amplía

VRCHAT_MCP_HISTORY

1000

Eventos conservados por tipo. Anulaciones por tipo: 1000,friend-location:200

VRCHAT_MCP_HISTORY_MAX_AGE

30d

Tope de antigüedad. 0 lo desactiva. Acepta ms s m h d w

VRCHAT_MCP_DB

.vrchat-mcp/events.db del proyecto

Ruta de la base de datos de eventos

VRCHAT_MCP_SESSION

.vrchat-mcp/session.json del proyecto

Ruta del archivo de sesión

VRCHAT_MCP_PROXY

ninguna

Proxy HTTP o HTTPS para el tráfico de API y WebSocket

VRCHAT_MCP_2FA_TIMEOUT_MS

300000

Cuánto espera un inicio de sesión en pausa por un código

VRCHAT_LIVE_TESTS

off

Habilita la suite de pruebas en vivo

Los booleanos aceptan 1 o true, sin distinguir mayúsculas de minúsculas.

Dónde vive el estado

El estado es por proyecto. Ejecuta el servidor dentro de un proyecto y su sesión e historial de eventos viven en el .vrchat-mcp/ de ese proyecto. La raíz del proyecto se encuentra subiendo desde el directorio de trabajo en busca de un .git, package.json, deno.json, pyproject.toml o go.mod, de modo que al lanzar desde un subdirectorio se alcanza el mismo estado en lugar de dejar una segunda sesión varada un nivel más abajo.

El directorio se oculta solo del control de versiones: vrchat-mcp escribe un .gitignore que contiene * dentro de él al crearlo, así que el archivo de sesión, que es una credencial de autenticación, queda protegido sin que el proyecto anfitrión necesite una regla para ello.

Por tanto, cada proyecto inicia sesión por separado, y la primera llamada en un proyecto nuevo puede pedir un código 2FA. Para compartir un único inicio de sesión en todas partes, apunta cada instalación al mismo archivo:

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

Puertas de seguridad

El servidor arranca en modo solo lectura. 150 de las 297 operaciones se registran por defecto. Nada que escriba, borre, gaste o modere aparece hasta que tú lo pidas.

Clase

Qué cubre

Requiere

Ejemplos

read

Cada GET

nada

getCurrentUser, searchWorlds

write

POST / PUT / PATCH

ALLOW_WRITES

createInstance, updateWorld, updateProduct

destructive

Cada DELETE, más una lista de anulaciones

ALLOW_WRITES y ALLOW_DESTRUCTIVE_WRITES

deleteProduct, banGroupMember, kickGroupMember, closeInstance

money

Compras, y rutas de Tilia/KYC/pagos

ALLOW_WRITES y ALLOW_PURCHASES

purchaseProductListing, getEconomyPayouts, getUserTiliaKyc

admin

Administración y ciclo de vida de cuentas

ALLOW_ADMIN

deleteUser, registerUserAccount, informes de moderación

Destructivo y dinero se superponen a escritura, así que habilitar escritura concede exactamente la capacidad de crear y editar, nunca de borrar ni de gastar. Admin se mantiene independiente y nada lo implica: permitir que un agente edite tu propio contenido nunca debe permitirle también borrar la cuenta.

En qué te estás metiendo:

  • ALLOW_WRITES permite que un agente cree y cambie cosas que te pertenecen. Reversible, en su mayoría a mano.

  • ALLOW_DESTRUCTIVE_WRITES añade las llamadas sin deshacer. Borrados, expulsiones, bloqueos, cierre de instancias, borrado de persistencia de usuario.

  • ALLOW_PURCHASES permite que un agente gaste saldo real. purchaseProductListing es una transacción en vivo. No lo actives porque una lista de herramientas parecía incompleta.

  • ALLOW_ADMIN expone deleteUser, entre otras. La mayoría de estas dan 403 en una cuenta normal, pero deleteUser es la que nunca debe ser un accidente.

Las operaciones con puerta permanecen en la tabla generada de todos modos, de modo que la cobertura sigue siendo 1:1 con la especificación y la lista de denegación es revisable en el diff. Las anotaciones MCP (readOnlyHint, destructiveHint) también se establecen, para que los clientes que las muestran puedan avisar.

¿Qué herramientas me faltan?

Una herramienta restringida simplemente no existe, lo que se lee como "VRChat no puede hacer esto" en lugar de "a este servidor se le dijo que no". Ese error ya se ha cometido en la práctica: un agente informó de que la API de economía era de solo lectura cuando las herramientas de escritura existían y simplemente estaban detrás de una bandera.

vrchat_authStatus cierra esa brecha. Informa de cada etiqueta y clase de seguridad, cuántas operaciones contiene cada una, cuántas están expuestas actualmente y el cambio exacto de .env que expondría el resto.

{
  "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 ..."
    ]
  }
}

Llámalo antes de concluir que algo no es compatible.

Elegir qué herramientas exponer

VRCHAT_MCP_TAGS selecciona las etiquetas. Sin definir, registra todo, y everything lo dice explícitamente, lo cual es más fácil que borrar una clave de una configuración JSON. all y * también funcionan.

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

Etiquetas de la especificación: authentication, avatars, calendar, economy, favorites, files, friends, groups, instances, inventory, invite, jams, miscellaneous, notifications, playermoderation, prints, props, users, worlds. Además de store, que añade este servidor.

Una etiqueta que no coincide con nada se avisa en stderr al arrancar y se informa mediante vrchat_authStatus. Sin eso, un error tipográfico como stores no registra ninguna herramienta generada y parece exactamente un servidor roto.

Iniciar sesión

El inicio de sesión es perezoso. Nada se autentica al arrancar, así que tools/list funciona sin credenciales y el servidor sigue siendo inspeccionable. La primera llamada a una herramienta que necesite una sesión dispara el inicio de sesión.

Con VRCHAT_TOTP_SECRET definido, esa es toda la historia. Sin avisos, nunca.

Sin él, VRChat envía un código por correo y la llamada vuelve aparcada en lugar de quedarse colgada:

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

Respóndela con vrchat_submitTwoFactorCode y luego reintenta. La sesión persiste, así que esto ocurre una vez por proyecto hasta que caduca.

Iniciar sesión desde una red nueva

Cambiar de proxy, VPN o ISP dispara una comprobación que no es un código de doble factor, y ambos se parecen lo suficiente como para hacer perder tiempo real. VRChat responde con uno de estos:

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

Ambos significan lo mismo, y ninguno es lo que parece. El correo contiene un enlace, no un código de seis dígitos, así que vrchat_submitTwoFactorCode no puede ayudar. El inicio de sesión requiere dos rondas:

  1. Una llamada a una herramienta falla con uno de esos mensajes

  2. El usuario abre el enlace del correo

  3. Llama a vrchat_retryLogin. VRChat envía el código real solo en este segundo intento

  4. El usuario lee el código en voz alta y llama a vrchat_submitTwoFactorCode

  5. Reintenta la herramienta original

El 429 es un desafío de autenticación disfrazado de estado de límite de velocidad, así que el limitador local lo ignora. Esperar no lo resuelve, y cada intento adicional cuesta una de las ranuras de sesión limitadas de la cuenta, que es lo que produce el 429 en primer lugar. Un inicio de sesión fallido se guarda en caché durante 30 segundos para que una ráfaga de llamadas a herramientas no se convierta en una ráfaga de intentos de inicio de sesión. vrchat_retryLogin limpia esa caché, porque para entonces el usuario ya ha hecho lo que el fallo estaba esperando.

Llamadas paralelas durante el inicio de sesión

Los agentes lanzan herramientas a la vez, y en un arranque en frío todas aterrizan en un cliente no autenticado. Una llamada impulsa el inicio de sesión. Las demás esperan hasta tres segundos y luego devuelven login_pending en lugar de bloquearse, así que un inicio de sesión lento detiene una llamada a una herramienta en lugar de todas, y un inicio de sesión que se aparca en un código plantea un aviso en lugar de varios.

_responseKeys

Toda herramienta acepta _responseKeys, y toda herramienta devuelve la carga útil bruta ascendente por defecto. No hay curación en el servidor, porque una lista de campos seleccionados a mano adivina lo que importa, es incorrecta para quien necesitaba el otro campo, y hay que mantenerla para 297 operaciones contra una especificación que cambia. El agente sabe lo que quiere en esta llamada. Debería decirlo.

Un objeto World ocupa aproximadamente 4 KB. Reducirlo suele recortar más de la mitad.

Patrón

Selecciona

["*"]

toda la carga útil, byte a byte

["id","name"]

esos campos de nivel superior

["author.displayName"]

una ruta anidada

["*.id"]

id de cada elemento de un array de nivel superior

["items.*.name"]

ese campo de cada elemento de items

["unityPackages.*.**"]

todo lo que hay debajo de cada elemento

["!description"]

excluye, y se combina con ["*"]

La proyección conserva la forma. Los objetos siguen anidados, los arrays mantienen su orden y longitud, así que una ruta aprendida en una llamada sigue funcionando en la siguiente.

El descubrimiento importa más que la proyección. Un agente no puede pedir claves que no sabe que existen, y un resultado vacío en silencio haría este diseño peor que recortar. Así que una ruta que no coincide con nada vuelve como _unmatched, junto con _availableKeys que enumera lo que realmente había. Las claves de elementos de array se nombran como *.id, *.name, la forma que funciona como entrada de _responseKeys.

["*"] devuelve la entrada por referencia, así que la ruta bruta es demostrablemente sin pérdidas y nada queda oculto.

Ver imágenes

vrchat_getImage descarga una imagen de VRChat y la devuelve como bloque de imagen, para que el modelo pueda mirarla en lugar de informar de una URL.

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

Pasa cualquier imageUrl o thumbnailImageUrl de un usuario, mundo, avatar, print o producto, o pasa fileId y deja que la herramienta construya la URL. savePath también escribe los bytes en disco.

Prefiere una URL que termine en /256 o /512 cuando exista. La imagen se transporta como base64, así que una textura a tamaño completo cuesta mucho contexto y no aporta detalle extra. Cualquier cosa de más de 4 MB se rechaza; sube maxBytes si de verdad lo quieres.

La herramienta solo obtiene imágenes alojadas en VRChat, y solo api.vrchat.cloud recibe tu cookie de sesión. Una herramienta que obtiene una URL proporcionada por el llamador mientras tiene una sesión es una primitiva de falsificación de solicitudes a menos que esté restringida, y una cookie enviada a un CDN es una cookie regalada.

Subir archivos

Pasa una ruta de archivo local. El servidor se ejecuta en tu máquina y lee el archivo él mismo, así que el contenido del archivo nunca entra en la conversación. Incrustar un PNG de 2 MB como base64 costaría aproximadamente 2,7 MB de argumentos de herramienta, más que todo lo demás de la llamada combinado.

Cuando no hay archivo en disco, por ejemplo una imagen que acaba de producir el agente, el mismo argumento acepta los bytes incrustados:

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

Una URI data: también funciona en la posición de cadena, así que "file": "data:image/png;base64,iVBORw0..." es equivalente. filename es opcional y se inventa a partir del tipo MIME cuando se omite, porque VRChat rechaza una subida que no puede nombrar. Prefiere una ruta siempre que exista: incrustar cuesta aproximadamente 1,33 bytes de argumento de herramienta por byte de archivo, y eso sale del mismo presupuesto de contexto que todo lo demás.

Ocho operaciones aceptan un archivo directamente, una llamada cada una:

Herramienta

Campo

Para

vrchat__uploadImage

file

Iconos, galería, emoji, pegatinas, imágenes de producto (tag elige cuál)

vrchat__uploadPrint

image

Prints

vrchat__uploadIcon

file

Iconos de perfil

vrchat__uploadGalleryImage

file

Galería

vrchat__editPrint

image

Sustituir la imagen de un print

vrchat__inviteUserWithPhoto

image

Fotos de invitación

vrchat__requestInviteWithPhoto

image

Solicitudes de invitación

vrchat__respondInviteWithPhoto

image

Respuestas de invitación

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

El resultado nombra los bytes que se enviaron y en qué forma llegaron, que es la única manera de distinguir una subida correcta del archivo correcto de una subida correcta del archivo equivocado:

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

Para todo lo demás, vrchat_uploadFile ejecuta la secuencia de cuatro pasos de VRChat (crear el registro, solicitar una URL prefirmada, transferir los bytes, finalizar) y devuelve el registro de archivo completado. Úsalo para paquetes de assets y unity packages. Los bytes van directamente al proveedor de almacenamiento de VRChat con una solicitud simple, deliberadamente no a través del cliente de API, porque ese cliente adjunta tu cookie de sesión a todo lo que envía y el host de almacenamiento es un tercero.

Las subidas son escrituras, así que todo esto necesita VRCHAT_MCP_ALLOW_WRITES=1. Los archivos tienen un límite de 100 MB, y un archivo vacío se rechaza antes de llegar a VRChat, que de otro modo almacenaría un registro roto. Si vrchat_uploadFile falla a mitad de camino, nombra el registro de archivo que creó, para que puedas inspeccionarlo con vrchat__getFile y eliminarlo con vrchat__deleteFile.

Gestionar una tienda

Gestionar un escaparate es una escritura ordinaria, no una operación de money. Crear un producto, renombrarlo, cambiar su imagen, publicar o despublicar un listado: nada de esto gasta ni gana nada, así que solo necesita VRCHAT_MCP_ALLOW_WRITES=1. La puerta de money es para comprar y para el procesador de pagos.

VRCHAT_MCP_TAGS=store
VRCHAT_MCP_ALLOW_WRITES=1

Establecer la imagen de un producto lleva una llamada:

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

Eso sube con tag: "product" y adjunta el id de archivo devuelto como imageId del producto. A mano es vrchat__uploadImage con tag: "product" o "listinggallery", y luego vrchat__updateProduct con el id que devuelve.

Una cosa que VRChat mismo no permite: un listado solo expone active para editar, así que su precio, título y descripción no se pueden cambiar después de la creación. Elimina el listado y crea uno nuevo. El nombre, la descripción y la imagen viven en el producto y se pueden editar mediante vrchat__updateProduct.

Paginación

Una página por llamada. Las herramientas paginadas devuelven por defecto 25 resultados y repiten un nextOffset para continuar. No hay bucle de paginación interno, deliberadamente: una auto-paginación oculta quemaría el presupuesto de solicitudes y una gran porción de contexto dentro de lo que al agente le parece una sola llamada.

Una página corta significa el final. VRChat no informa de un total, así que esa es la única señal fiable.

Eventos WebSocket

Desactivado por defecto, porque un socket siempre activo en reposo quema una ranura de sesión. Define VRCHAT_MCP_WEBSOCKET=1 para abrirlo y registrar las cuatro herramientas vrchat_events*.

VRCHAT_MCP_WS_EVENTS elige a qué tipos suscribirse, reemplazando en lugar de ampliando el conjunto predeterminado de notification, notification-v2, economy-update, friend-online, friend-offline e instance-queue-ready.

Los mensajes de pipeline están doblemente codificados: el campo content es JSON serializado que necesita un segundo análisis, excepto see-notification y hide-notification, que llevan ids simples. Todo eso se normaliza una vez en la ingesta, así que ninguna herramienta te entrega jamás una cadena JSON dentro de JSON. El socket propio del SDK descarta silenciosamente esos dos tipos de mensaje, que es una de las razones por las que este servidor no lo usa. La otra es que no acepta proxy.

La retención es por tipo

El historial va a SQLite en .vrchat-mcp/events.db, y conserva 1000 eventos por tipo de evento, no 1000 en total. Un tipo locuaz como friend-location nunca puede expulsar a uno raro y valioso como economy-update, cosa que un límite global único haría en cuestión de minutos.

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

Un tope de antigüedad funciona junto al tope de recuento, y el que muerde primero gana. Solo el recuento permite que un tipo que se dispara raramente conserve eventos de hace meses que se leen como actuales. Solo la antigüedad permite que una ráfaga haga explotar la base de datos. vrchat_eventsStatus informa de qué límite está activo actualmente, por tipo, para que la ventana sea legible en lugar de silenciosa.

El historial sobrevive a los reinicios, así que vrchat_eventsSearch puede responder qué pasó mientras estabas ausente. Un buffer solo en vivo no puede.

Proxy

VRCHAT_MCP_PROXY enruta el tráfico a través de un proxy HTTP o HTTPS, con credenciales opcionales user:pass@.

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

SOCKS no es compatible. El fetch de Bun rechaza socks5:// directamente, por lo que una URL SOCKS falla al inicio con un error de configuración que menciona la limitación en lugar de funcionar a medias. En su lugar, colócalo detrás de un proxy HTTP local.

El proxy cubre tanto el tráfico de API como el de WebSocket. Ambos pasan por mecanismos diferentes, y el modo de fallo de hacer solo la mitad bien es un servidor que parece estar proxy mientras filtra su IP real en el flujo de eventos.

Si no se puede alcanzar el proxy, las llamadas fallan con un error claro. El servidor nunca recurre silenciosamente a una conexión directa, porque para cualquiera que use esto para separación de IP, ese es el peor resultado posible. La URL del proxy nunca se registra, ya que puede contener credenciales.

Desarrollo

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 obtiene vrchatapi/specification en main, lo agrupa y escribe la especificación agrupada más spec/VERSION.json (SHA ascendente, marca de tiempo, hash de contenido) junto con el src/generated/operations.ts regenerado. Ambos se confirman, por lo que cada regeneración produce dos diffs revisables: el cambio de especificación y el cambio de herramienta que causó, y una confirmación ascendente mala es reversible en lugar de ser una carga. --offline reproduce la salida de la instantánea confirmada byte por byte sin red alguna.

src/generated/operations.ts se genera. No lo edites a mano.

Diez operationIds no tienen un método correspondiente en el SDK de VRChat, porque la especificación avanza más rápido que la biblioteca del cliente. Esos se enrutan a través de un respaldo de solicitud sin procesar en el mismo cliente, por lo que las cookies, el User-Agent, el proxy y la limitación de velocidad siguen aplicándose, y la cobertura 1:1 sigue siendo cierta en lugar de convertirse silenciosamente en una mentira. Codegen imprime la lista en cada ejecución.

stdout es el canal JSON-RPC. Todo el registro va a stderr, y un console.log perdido corrompe el flujo del protocolo.

Pruebas

bun test es la suite sin conexión: salida de codegen, compuertas, el limitador de velocidad contra un reloj falso, retención y búsqueda de historial, proyección, mapeo de errores, manejo de rutas de carga. Sin red, sin credenciales, sin cuenta. Esto es lo que se ejecuta por defecto.

bun run test:live accede a una cuenta real, activado con VRCHAT_LIVE_TESTS=1 y omitido de lo contrario. Reglas a las que se atiene:

  • Solo lecturas y escrituras propiedad del creador. Se niega rotundamente a cualquier cosa clasificada como money o admin, antes incluso de construir un cliente. Una suite de pruebas no debe poder gastar dinero.

  • Cada escritura se limpia a sí misma y está etiquetada para que los artefactos sueltos sean identificables en el juego.

  • Se enruta a través del mismo limitador que la producción y se mantiene pequeño. Una ejecución que active la limitación de VRChat es peor que ninguna ejecución.

  • Las afirmaciones se basan en la forma y el estado, nunca en contenido volátil. Los recuentos de amigos y los listados de mundos cambian entre ejecuciones.

  • Usa una cuenta dedicada cuando puedas. Las credenciales provienen solo de .env.

Seguridad

  • .env y .vrchat-mcp/ están en gitignore, y .vrchat-mcp/ también se ignora a sí mismo desde dentro para permanecer oculto dentro de otros proyectos.

  • .vrchat-mcp/session.json es una credencial de autenticación, una cookie de sesión válida. Trátala como una contraseña. Eliminarla, o llamar a vrchat_logout, fuerza un nuevo inicio de sesión.

  • Los códigos 2FA, contraseñas, secretos TOTP y URLs de proxy nunca se registran, incluso en stderr.

  • Tu cookie de sesión va a api.vrchat.cloud y a ningún otro lugar. Las subidas al proveedor de almacenamiento de VRChat y las descargas de imágenes desde su CDN omiten deliberadamente el cliente autenticado.

  • Los errores vuelven como resultados estructurados que llevan estado, el mensaje propio de VRChat y una pista accionable. Las excepciones sin procesar y los seguimientos de pila nunca llegan a la transcripción.

  • Solo stdio, solo local. Sin transporte HTTP, sin aislamiento de credenciales multiusuario. Este servidor es para una cuenta en una máquina.

Estructura del proyecto

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

Licencia

Ver 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