Skip to main content
Glama
TheArmagan

vrchat-mcp

by TheArmagan

vrchat-mcp

MCP-сервер для VRChat API. Все 297 операций из OpenAPI-спецификации, сгенерированные во время сборки, плюс написанные вручную инструменты для того, что не под силу одному эндпоинту: двухфакторный вход, загрузка файлов, просмотр изображений и конвейер событий.

Работает локально через stdio, как подпроцесс Claude Code или Claude Desktop. Только для чтения, пока вы не скажете иначе.

Собран на Bun, официальном MCP TypeScript SDK v2 и официальном vrchat JavaScript SDK. Инструменты взяты из спецификации VRChat OpenAPI и зафиксированы, так что поверхность отслеживает апстрим, а не гниёт против него.

Шпаргалка

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:

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

Я хочу

Сделайте это

Включить создание и редактирование

VRCHAT_MCP_ALLOW_WRITES=1

Включить удаление и модерацию

добавьте VRCHAT_MCP_ALLOW_DESTRUCTIVE_WRITES=1

Включить трату баланса

добавьте VRCHAT_MCP_ALLOW_PURCHASES=1

Показывать только инструменты витрины

VRCHAT_MCP_TAGS=store

Показать всё

VRCHAT_MCP_TAGS=everything

Узнать, почему инструмента нет

вызовите vrchat_authStatus

Не дать огромным ответам съесть контекст

_responseKeys: ["id","name"] на любом инструменте

Посмотреть изображение

vrchat_getImage с imageUrl

Загрузить картинку

vrchat__uploadImage с путём или { data, mimeType }

Починить вход, зависший в новой сети

откройте ссылку из письма, затем vrchat_retryLogin

Имена инструментов несут их происхождение. Два подчёркивания — сгенерировано из спецификации (vrchat__getCurrentUser), так что имя можно искать в собственной документации VRChat. Одно подчёркивание — написано этим сервером (vrchat_authStatus).

Написанные вручную инструменты, полностью:

Инструмент

Что делает

vrchat_authStatus

Состояние входа, ограничитель запросов и какие группы инструментов скрыты какой переменной окружения

vrchat_submitTwoFactorCode

Отвечает на приостановленный вход кодом, который прочитал пользователь

vrchat_retryLogin

Перезапускает вход после открытия ссылки из письма о новой сети

vrchat_logout

Очищает сохранённую сессию

vrchat_getImage

Скачивает изображение VRChat и возвращает его как просматриваемое изображение

vrchat_uploadFile

Выполняет четырёхшаговую загрузку VRChat для не-изображений

vrchat_setProductImage

Загружает изображение и прикрепляет его к товару витрины

vrchat_eventsRecent

События с курсора

vrchat_eventsWait

Блокируется до следующего подходящего события

vrchat_eventsSearch

Полнотекстовый поиск по сохранённой истории событий

vrchat_eventsStatus

Состояние сокета и удержание по типам

Последние четыре появляются только с VRCHAT_MCP_WEBSOCKET=1.

Related MCP server: Portals MCP

Установка

bun link помещает исполняемый файл vrchat-mcp в PATH, так что никому ниже по цепочке не нужно знать, где лежит копия репозитория.

bun install
bun link          # from the repo root

Зарегистрируйте его по имени:

claude mcp add vrchat -- vrchat-mcp

Или для Claude Desktop, в claude_desktop_config.json:

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

Это вся конфигурация. Учётные данные берутся из .env репозитория, так что их не нужно повторять здесь, хотя всё, что вы положите в блок env, побеждает. Удалите команду с помощью bun unlink.

Если вы не хотите ничего класть в PATH, укажите на входной файл абсолютным путём. Сервер запускается из произвольной рабочей директории, так что относительный путь не подойдёт.

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

Требование контакта

VRChat отклоняет общие User-Agent с кодом 403. VRCHAT_CONTACT попадает в описательный User-Agent, который SDK отправляет в каждом запросе, и в API, и в WebSocket, и он фактически обязателен.

Значение должно быть настоящим. SDK отказывает любому контакту, содержащему @example.com, так что очевидный плейсхолдер — это единственное значение, которое гарантированно не сработает. Сервер сообщает об этом как об ошибке конфигурации при первом вызове инструмента, а не позволяет ей всплыть как загадочный 403.

Конфигурация

Три слоя, приоритет сверху вниз. Проект может задать свои параметры, не повторяя ваши учётные данные.

  1. Настоящие переменные окружения, включая блок env MCP-клиента

  2. .env в директории, из которой запускается команда, который Bun загружает автоматически

  3. .env в корне репозитория

Так что проекту, которому нужны только инструменты витрины, с уже настроенными вами учётными данными, нужна одна строка рядом с ним:

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

Переменная

По умолчанию

Эффект

VRCHAT_USERNAME

нет

Имя пользователя или email аккаунта

VRCHAT_PASSWORD

нет

Пароль аккаунта

VRCHAT_TOTP_SECRET

нет

Base32 TOTP-секрет. Если задан, вход никогда не запрашивает код

VRCHAT_CONTACT

нет

Контактная строка в User-Agent. Фактически обязательна

VRCHAT_MCP_TAGS

все

Теги для регистрации. everything — без фильтра

VRCHAT_MCP_ALLOW_WRITES

выкл.

Создание и редактирование

VRCHAT_MCP_ALLOW_DESTRUCTIVE_WRITES

выкл.

Удаление и модерация. Нужен и write-гейт

VRCHAT_MCP_ALLOW_PURCHASES

выкл.

Трата баланса. Нужен и write-гейт

VRCHAT_MCP_ALLOW_ADMIN

выкл.

Админ-операции. Независимо от write-гейта

VRCHAT_MCP_RPS

20

Запросов в секунду. 0 откатывается к 20, выключить нельзя

VRCHAT_MCP_MAX_WAIT_MS

30000

Сколько вызов ждёт за лимитером, прежде чем сдаться

VRCHAT_MCP_WEBSOCKET

выкл.

Открывает конвейер событий и регистрирует инструменты событий

VRCHAT_MCP_WS_EVENTS

низкошумный набор

Типы событий для подписки. Заменяет набор по умолчанию, не расширяет его

VRCHAT_MCP_HISTORY

1000

Событий хранится на тип. Переопределения по типам: 1000,friend-location:200

VRCHAT_MCP_HISTORY_MAX_AGE

30d

Потолок возраста. 0 отключает. Принимает ms s m h d w

VRCHAT_MCP_DB

проект .vrchat-mcp/events.db

Путь к базе событий

VRCHAT_MCP_SESSION

проект .vrchat-mcp/session.json

Путь к файлу сессии

VRCHAT_MCP_PROXY

нет

HTTP или HTTPS прокси для API и WebSocket-трафика

VRCHAT_MCP_2FA_TIMEOUT_MS

300000

Как долго приостановленный вход ждёт код

VRCHAT_LIVE_TESTS

выкл.

Включает живой тестовый набор

Булевы значения принимают 1 или true, без учёта регистра.

Где живёт состояние

Состояние — на проект. Запустите сервер внутри проекта, и его сессия и история событий будут жить в .vrchat-mcp/ этого проекта. Корень проекта находится подъёмом вверх от рабочей директории в поисках .git, package.json, deno.json, pyproject.toml или go.mod, так что запуск из поддиректории достигает того же состояния, а не оставляет вторую сессию уровнем ниже.

Директория прячет себя от контроля версий: vrchat-mcp пишет .gitignore с содержимым * внутри неё при создании, так что файл сессии, который является учётным данным, защищён без необходимости правила в хост-проекте.

Каждый проект поэтому входит в систему отдельно, и первый вызов в новом проекте может попросить код 2FA. Чтобы использовать один вход везде, укажите всем установкам один и тот же файл:

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

Гейты безопасности

Сервер запускается в режиме только для чтения. 150 из 297 операций регистрируются по умолчанию. Ничего, что пишет, удаляет, тратит или модерирует, не появляется, пока вы не попросите.

Класс

Что покрывает

Требуется

Примеры

read

Каждый GET

ничего

getCurrentUser, searchWorlds

write

POST / PUT / PATCH

ALLOW_WRITES

createInstance, updateWorld, updateProduct

destructive

Каждый DELETE, плюс список переопределений

ALLOW_WRITES и ALLOW_DESTRUCTIVE_WRITES

deleteProduct, banGroupMember, kickGroupMember, closeInstance

money

Покупки, а также пути Tilia/KYC/выплат

ALLOW_WRITES и ALLOW_PURCHASES

purchaseProductListing, getEconomyPayouts, getUserTiliaKyc

admin

Админ и жизненный цикл аккаунта

ALLOW_ADMIN

deleteUser, registerUserAccount, отчёты о модерации

Деструктивные и денежные операции наслаиваются на write, так что включение writes даёт ровно возможность создавать и редактировать, но никогда не удалять и не тратить. Админ стоит отдельно и не подразумевается ничем: позволить агенту редактировать ваш собственный контент не должно никогда позволить ему удалить аккаунт.

На что вы соглашаетесь:

  • ALLOW_WRITES позволяет агенту создавать и изменять то, что принадлежит вам. Обратимо, в основном вручную.

  • ALLOW_DESTRUCTIVE_WRITES добавляет вызовы без отмены. Удаления, баны, кики, закрытие инстансов, стирание пользовательской персистентности.

  • ALLOW_PURCHASES позволяет агенту тратить реальный баланс. purchaseProductListing — это живая транзакция. Не устанавливайте это, потому что список инструментов выглядел неполным.

  • ALLOW_ADMIN открывает deleteUser среди прочих. Большинство из них дают 403 на обычном аккаунте, но deleteUser — это то, что никогда не должно быть случайностью.

Гейтированные операции остаются в сгенерированной таблице в любом случае, так что покрытие остаётся 1:1 со спецификацией, а денлист можно просмотреть в диффе. MCP-аннотации (readOnlyHint, destructiveHint) тоже задаются, так что клиенты, которые их показывают, могут подсказать.

Каких инструментов мне не хватает?

Скрытый за флагом инструмент просто отсутствует, и это воспринимается как «VRChat не умеет этого», а не «этому серверу запретили». Эта ошибка уже случалась в реальности: агент сообщил, что API экономики доступен только для чтения, хотя инструменты записи существовали и были просто скрыты за флагом.

vrchat_authStatus закрывает этот пробел. Он сообщает о каждом теге и классе безопасности, сколько операций содержит каждый из них, сколько сейчас доступно и какие именно изменения в .env откроют остальные.

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

Вызовите его, прежде чем делать вывод, что что-то не поддерживается.

Выбор инструментов для предоставления

VRCHAT_MCP_TAGS выбирает теги. Если переменная не задана, регистрируется всё, а значение everything говорит об этом явно — это проще, чем удалять ключ из JSON-конфига. all и * тоже работают.

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

Теги спецификации: authentication, avatars, calendar, economy, favorites, files, friends, groups, instances, inventory, invite, jams, miscellaneous, notifications, playermoderation, prints, props, users, worlds. Плюс store, который добавляет этот сервер.

О теге, которому ничего не соответствует, выводится предупреждение в stderr при запуске, и он сообщается через vrchat_authStatus. Без этого опечатка вроде stores не зарегистрирует ни одного сгенерированного инструмента и будет выглядеть точно как сломанный сервер.

Вход в систему

Вход ленивый. При запуске ничего не аутентифицируется, поэтому tools/list работает вообще без учётных данных, и сервер остаётся доступным для инспекции. Первый вызов инструмента, которому нужна сессия, запускает вход.

Если задан VRCHAT_TOTP_SECRET, на этом всё. Никаких запросов, никогда.

Без него VRChat отправляет код по электронной почте, и вызов возвращается в состоянии ожидания, а не зависает:

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

Ответьте на него с помощью vrchat_submitTwoFactorCode, затем повторите попытку. Сессия сохраняется, так что это происходит один раз на проект, пока сессия не истечёт.

Вход из новой сети

Смена прокси, VPN или провайдера запускает проверку, которая не является кодом двухфакторной аутентификации, и эти две вещи выглядят настолько похоже, что можно впустую потратить время. VRChat отвечает одним из следующих сообщений:

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

Оба означают одно и то же, и ни одно из них не является тем, чем кажется. В письме содержится ссылка, а не шестизначный код, поэтому vrchat_submitTwoFactorCode не поможет. Вход занимает два раунда:

  1. Вызов инструмента завершается ошибкой с одним из этих сообщений

  2. Пользователь открывает ссылку из письма

  3. Вызовите vrchat_retryLogin. VRChat отправляет настоящий код только при второй попытке

  4. Пользователь зачитывает код, вызовите vrchat_submitTwoFactorCode

  5. Повторите исходный вызов инструмента

Код 429 — это вызов аутентификации, замаскированный под статус ограничения частоты запросов, поэтому локальный ограничитель игнорирует его. Ожидание не устраняет его, и каждая лишняя попытка стоит один из ограниченных сессионных слотов аккаунта — именно это и порождает 429. Неудачный вход кэшируется на 30 секунд, чтобы всплеск вызовов инструментов не превратился во всплеск попыток входа. vrchat_retryLogin очищает этот кэш, потому что к этому моменту пользователь уже сделал то, чего ждал неудачный вход.

Параллельные вызовы во время входа

Агенты запускают инструменты веером одновременно, и при холодном старте все они попадают на неаутентифицированный клиент. Один вызов выполняет вход. Остальные ждут до трёх секунд и затем возвращают login_pending вместо блокировки, так что медленный вход задерживает один вызов инструмента, а не все вызовы, а вход, ожидающий код, вызывает одно приглашение, а не несколько.

_responseKeys

Каждый инструмент принимает _responseKeys, и по умолчанию каждый инструмент возвращает исходную полезную нагрузку от вышестоящего API. Никакой серверной курации нет, потому что вручную подобранный список полей угадывает, что важно, оказывается неверным для того, кому нужно другое поле, и его приходится поддерживать для 297 операций в условиях изменяющейся спецификации. Агент знает, что ему нужно в этом вызове. Он должен об этом сказать.

Объект World весит примерно 4 КБ. Сужение обычно сокращает его более чем вдвое.

Шаблон

Выбирает

["*"]

весь ответ, байт в байт

["id","name"]

эти поля верхнего уровня

["author.displayName"]

вложенный путь

["*.id"]

id из каждого элемента массива верхнего уровня

["items.*.name"]

это поле из каждого элемента items

["unityPackages.*.**"]

всё ниже каждого элемента

["!description"]

исключает и сочетается с ["*"]

Проекция сохраняет форму. Объекты остаются вложенными, массивы сохраняют порядок и длину, поэтому путь, изученный на одном вызове, работает и в следующем.

Обнаружение важнее проекции. Агент не может запросить ключи, о существовании которых не знает, а молча пустой результат сделал бы эту конструкцию хуже, чем обрезка. Поэтому путь, которому ничего не соответствует, возвращается как _unmatched, вместе с _availableKeys, перечисляющим то, что там реально было. Ключи элементов массива называются в форме *.id, *.name — той, которая работает как запись _responseKeys.

["*"] возвращает входные данные по ссылке, так что исходный путь доказуемо без потерь, и ничто никогда не скрывается.

Просмотр изображений

vrchat_getImage загружает изображение VRChat и возвращает его как изображение-блок, чтобы модель могла посмотреть на него, а не просто сообщить URL.

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

Передайте любой imageUrl или thumbnailImageUrl от пользователя, мира, аватара, принта или товара, либо передайте fileId и позвольте инструменту построить URL. savePath также записывает байты на диск.

Предпочитайте URL, заканчивающийся на /256 или /512, если такой существует. Изображение передаётся в формате base64, поэтому полноразмерная текстура стоит много контекста и не даёт дополнительной детализации. Всё, что больше 4 МБ, отклоняется; увеличьте maxBytes, если вы действительно этого хотите.

Инструмент загружает только изображения, размещённые на VRChat, и только api.vrchat.cloud когда-либо получает вашу сессионную cookie. Инструмент, который загружает URL, предоставленный вызывающей стороной, удерживая сессию, является примитивом подделки запроса, если он не ограничен, а cookie, отправленная на CDN, — это отданная cookie.

Загрузка файлов

Передайте локальный путь к файлу. Сервер работает на вашей машине и сам читает файл, поэтому содержимое файла никогда не попадает в разговор. Встраивание PNG размером 2 МБ в base64 стоило бы примерно 2,7 МБ аргументов инструмента — больше, чем всё остальное в вызове вместе взятое.

Когда файла на диске нет, например, если это изображение, только что созданное агентом, тот же аргумент принимает байты встроенно:

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

URI data: тоже работает в позиции строки, так что "file": "data:image/png;base64,iVBORw0..." эквивалентен. filename необязателен и при отсутствии выводится из MIME-типа, потому что VRChat отклоняет загрузку, которую не может назвать. Предпочитайте путь, когда он существует: встроенная передача стоит около 1,33 байта аргумента инструмента на байт файла, и это берётся из того же бюджета контекста, что и всё остальное.

Восемь операций принимают файл напрямую, по одному вызову каждая:

Инструмент

Поле

Назначение

vrchat__uploadImage

file

Иконки, галерея, эмодзи, стикеры, изображения товаров (tag выбирает)

vrchat__uploadPrint

image

Принты

vrchat__uploadIcon

file

Иконки профиля

vrchat__uploadGalleryImage

file

Галерея

vrchat__editPrint

image

Замена изображения принта

vrchat__inviteUserWithPhoto

image

Фотографии приглашений

vrchat__requestInviteWithPhoto

image

Запросы приглашений

vrchat__respondInviteWithPhoto

image

Ответы на приглашения

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

Результат называет отправленные байты и форму, в которой они поступили, — это единственный способ отличить успешную загрузку нужного файла от успешной загрузки не того файла:

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

Для всего остального vrchat_uploadFile выполняет четырёхшаговую последовательность VRChat (создать запись, запросить предварительно подписанный URL, передать байты, завершить) и возвращает заполненную запись файла. Используйте его для asset bundles и unity packages. Байты идут напрямую в хранилище VRChat обычным запросом, намеренно не через API-клиент, потому что этот клиент прикрепляет вашу сессионную cookie ко всему, что отправляет, а хост хранилища — третья сторона.

Загрузки — это операции записи, поэтому всё это требует VRCHAT_MCP_ALLOW_WRITES=1. Файлы ограничены 100 МБ, а пустой файл отклоняется до того, как попадёт в VRChat, который в противном случае сохранил бы битую запись. Если vrchat_uploadFile выходит из строя на полпути, он называет созданную запись файла, так что вы можете проверить её с помощью vrchat__getFile и удалить с помощью vrchat__deleteFile.

Ведение магазина

Управление витриной — это обычная операция записи, а не операция money. Создание товара, его переименование, изменение изображения, публикация или снятие с публикации объявления: ни одно из этих действий ничего не тратит и не зарабатывает, поэтому им нужен только VRCHAT_MCP_ALLOW_WRITES=1. Гейт money предназначен для покупки и для платёжного процессора.

VRCHAT_MCP_TAGS=store
VRCHAT_MCP_ALLOW_WRITES=1

Установка изображения товара занимает один вызов:

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

Это загружает файл с tag: "product" и прикрепляет возвращённый id файла как imageId товара. Вручную это vrchat__uploadImage с tag: "product" или "listinggallery", затем vrchat__updateProduct с возвращённым id.

Одна вещь, которую сам VRChat не позволяет: объявление выставляет для редактирования только active, поэтому его цену, название и описание нельзя изменить после создания. Удалите объявление и создайте новое. Название, описание и изображение живут на товаре и редактируются через vrchat__updateProduct.

Постраничная навигация

Одна страница за вызов. Инструменты с постраничной выдачей по умолчанию возвращают 25 результатов и отдают обратно nextOffset для продолжения. Внутреннего цикла пагинации нет, и это намеренно: скрытая автопагинация сожгла бы бюджет запросов и большой кусок контекста внутри того, что выглядит для агента как один вызов.

Короткая страница означает конец. VRChat не сообщает общее количество, так что это единственный надёжный сигнал.

События WebSocket

По умолчанию выключены, потому что постоянно открытый бездействующий сокет сжигает сессионный слот. Установите VRCHAT_MCP_WEBSOCKET=1, чтобы открыть его и зарегистрировать четыре инструмента vrchat_events*.

VRCHAT_MCP_WS_EVENTS выбирает, какие типы подписать, заменяя, а не расширяя набор по умолчанию: notification, notification-v2, economy-update, friend-online, friend-offline и instance-queue-ready.

Сообщения конвейера дважды закодированы: поле content — это строкифицированный JSON, требующий второго разбора, за исключением see-notification и hide-notification, которые несут голые id. Всё это нормализуется один раз при приёме, поэтому ни один инструмент никогда не передаёт вам JSON-строку внутри JSON. Собственный сокет SDK молча отбрасывает эти два типа сообщений, и это одна из причин, по которой этот сервер его не использует. Другая — он не принимает прокси.

Хранение по типам

История сохраняется в SQLite по пути .vrchat-mcp/events.db, и в ней хранится 1000 событий на каждый тип события, а не 1000 всего. Болтливый тип вроде friend-location никогда не сможет вытеснить редкий и ценный тип вроде economy-update, что единый глобальный лимит сделал бы за считанные минуты.

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

Ограничение по возрасту действует вместе с лимитом по количеству, и побеждает то, что срабатывает первым. Только количество позволяет редко срабатывающему типу хранить события месячной давности, которые выглядят как текущие. Только возраст позволяет всплеску раздуть базу данных. vrchat_eventsStatus сообщает, какой лимит в данный момент действует, по каждому типу, так что окно становится наглядным, а не безмолвным.

История переживает перезапуски, поэтому vrchat_eventsSearch может ответить, что произошло, пока вас не было. Буфер, живущий только в памяти, не может.

Прокси

VRCHAT_MCP_PROXY направляет трафик через HTTP- или HTTPS-прокси с опциональными учётными данными user:pass@.

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

SOCKS не поддерживается. fetch в Bun отклоняет socks5:// наотрез, поэтому URL с SOCKS падает при запуске с ошибкой конфигурации, называющей ограничение, а не работает наполовину. Вместо этого используйте локальный HTTP-прокси перед ним.

Прокси покрывает и API-трафик, и трафик WebSocket. Они идут через разные механизмы, и сценарий отказа, когда получается только половина, — это сервер, который выглядит проксированным, но утекает реальным IP в потоке событий.

Если прокси недоступен, вызовы завершаются с понятной ошибкой. Сервер никогда молча не откатывается к прямому соединению, потому что для тех, кто использует это для разделения IP, это худший возможный исход. URL прокси никогда не логируется, так как он может содержать учётные данные.

Разработка

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 загружает vrchatapi/specification из ветки main, собирает его и записывает собранную спецификацию вместе с spec/VERSION.json (SHA из апстрима, временная метка, хэш содержимого) рядом с перегенерированным src/generated/operations.ts. Оба файла коммитятся, так что каждая регенерация даёт два проверяемых диффа — изменение спецификации и вызванное им изменение инструмента, а плохой коммит из апстрима можно откатить, а не нести как нагрузку. --offline воспроизводит вывод из закоммиченного снимка байт в байт вообще без сети.

src/generated/operations.ts генерируется. Не редактируйте его вручную.

Десять operationId не имеют соответствующего метода в VRChat SDK, потому что спецификация движется быстрее, чем клиентская библиотека. Они идут через запасной путь с сырым запросом на том же клиенте, так что cookies, User-Agent, прокси и ограничение скорости по-прежнему применяются, и покрытие 1:1 остаётся честным, а не тихо превращается в ложь. Генератор кода выводит список при каждом запуске.

stdout — это канал JSON-RPC. Вся запись логов идёт в stderr, и один случайный console.log портит поток протокола.

Тестирование

bun test — это офлайн-набор: вывод кодогенерации, гейтинг, ограничитель скорости с фейковыми часами, хранение и поиск истории, проекция, сопоставление ошибок, обработка путей загрузки. Без сети, без учётных данных, без аккаунта. Это то, что запускается по умолчанию.

bun run test:live обращается к реальному аккаунту, включается через VRCHAT_LIVE_TESTS=1 и пропускается в противном случае. Правила, которых он придерживается:

  • Только чтения и записи, принадлежащие создателю. Он жёстко отказывается от всего, что классифицируется как money или admin, ещё до создания клиента. Тестовый набор не должен иметь возможности тратить деньги.

  • Каждая запись убирает за собой и помечается, чтобы случайные артефакты можно было опознать в игре.

  • Он идёт через тот же ограничитель, что и продакшн, и остаётся небольшим. Запуск, который вызывает троттлинг VRChat, хуже, чем отсутствие запуска.

  • Утверждения касаются формы и статуса, никогда не изменчивого содержимого. Количество друзей и списки миров меняются между запусками.

  • Используйте выделенный аккаунт, где возможно. Учётные данные берутся только из .env.

Безопасность

  • .env и .vrchat-mcp/ игнорируются git, а .vrchat-mcp/ также игнорирует сам себя изнутри, чтобы оставаться скрытым внутри других проектов.

  • .vrchat-mcp/session.json — это учётные данные аутентификации, действующий cookie сессии. Относитесь к нему как к паролю. Удаление его или вызов vrchat_logout приводит к принудительному новому входу.

  • Коды 2FA, пароли, TOTP-секреты и URL прокси никогда не логируются, включая stderr.

  • Ваш cookie сессии уходит на api.vrchat.cloud и никуда больше. Загрузки в хранилище VRChat и получение изображений с его CDN намеренно обходят аутентифицированный клиент.

  • Ошибки возвращаются как структурированные результаты, содержащие статус, собственное сообщение VRChat и полезную подсказку. Сырые исключения и стектрейсы никогда не попадают в транскрипт.

  • Только stdio, только локально. Никакого HTTP-транспорта, никакой изоляции учётных данных для нескольких пользователей. Этот сервер предназначен для одного аккаунта на одной машине.

Структура проекта

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

Лицензия

См. 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