@basicbit/vrchat-mcp 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/README.md +1 -0
- package/dist/package.json +2 -1
- package/dist/src/auth/index.js +4 -1
- package/dist/src/auth/index.js.map +1 -1
- package/dist/src/core/client.js +27 -1
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/generatedToolOverrides.js +2 -0
- package/dist/src/core/generatedToolOverrides.js.map +1 -1
- package/dist/src/core/operationPolicy.js +23 -1
- package/dist/src/core/operationPolicy.js.map +1 -1
- package/dist/src/generated/vrchat-schemas.js +3 -1
- package/dist/src/generated/vrchat-schemas.js.map +1 -1
- package/dist/src/models/avatars.js +85 -0
- package/dist/src/models/avatars.js.map +1 -1
- package/dist/src/models/groups.js +38 -0
- package/dist/src/models/groups.js.map +1 -1
- package/dist/src/models/instances.js +16 -0
- package/dist/src/models/instances.js.map +1 -1
- package/dist/src/models/invites.js +31 -2
- package/dist/src/models/invites.js.map +1 -1
- package/dist/src/models/status.js +4 -2
- package/dist/src/models/status.js.map +1 -1
- package/dist/src/services/api/client.js +3 -0
- package/dist/src/services/api/client.js.map +1 -1
- package/dist/src/services/avatars/index.js +166 -1
- package/dist/src/services/avatars/index.js.map +1 -1
- package/dist/src/services/events/curated.js +16 -10
- package/dist/src/services/events/curated.js.map +1 -1
- package/dist/src/services/instances/curated.js +76 -1
- package/dist/src/services/instances/curated.js.map +1 -1
- package/dist/src/services/invites/curated.js +35 -16
- package/dist/src/services/invites/curated.js.map +1 -1
- package/dist/src/tools/curated/avatars.js +41 -3
- package/dist/src/tools/curated/avatars.js.map +1 -1
- package/dist/src/tools/curated/groups.js +2 -2
- package/dist/src/tools/curated/groups.js.map +1 -1
- package/dist/src/tools/curated/instances.js +29 -2
- package/dist/src/tools/curated/instances.js.map +1 -1
- package/dist/src/tools/curated/invites.js +1 -1
- package/dist/src/tools/curated/invites.js.map +1 -1
- package/dist/src/tools/raw.js +5 -1
- package/dist/src/tools/raw.js.map +1 -1
- package/docs/curated-tools.md +14 -0
- package/docs/robust-notes.md +9 -1
- package/docs/spec-drift.md +104 -0
- package/docs/tools.md +339 -215
- package/package.json +2 -1
- package/server.json +2 -2
package/dist/src/tools/raw.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { callOperation, CallError } from '../core/client.js';
|
|
3
|
-
import { getBlockedOperationReason } from '../core/operationPolicy.js';
|
|
3
|
+
import { getBlockedOperationReason, getCuratedOnlyReason } from '../core/operationPolicy.js';
|
|
4
4
|
import { CallInputSchema } from '../schemas/call.js';
|
|
5
5
|
import { writeToolAnnotations } from '../utils/toolAnnotations.js';
|
|
6
6
|
import { toolName } from '../utils/toolNames.js';
|
|
@@ -22,6 +22,10 @@ export function registerRawTools(server) {
|
|
|
22
22
|
if (blockedReason) {
|
|
23
23
|
return toolError(`Operation ${args.operationId} is disabled: ${blockedReason}`);
|
|
24
24
|
}
|
|
25
|
+
const curatedOnlyReason = getCuratedOnlyReason(args.operationId);
|
|
26
|
+
if (curatedOnlyReason) {
|
|
27
|
+
return toolError(curatedOnlyReason);
|
|
28
|
+
}
|
|
25
29
|
try {
|
|
26
30
|
const result = await callOperation(args);
|
|
27
31
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"raw.js","sourceRoot":"","sources":["../../../src/tools/raw.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,SAAS,EAAkB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"raw.js","sourceRoot":"","sources":["../../../src/tools/raw.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,SAAS,EAAkB,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAC7F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEtD,MAAM,UAAU,gBAAgB,CAAC,MAAiB;IAChD,MAAM,CAAC,YAAY,CACjB,QAAQ,CAAC,aAAa,CAAC,EACvB;QACE,WAAW,EAAE,iDAAiD;QAC9D,WAAW,EAAE,eAAe;QAC5B,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC;YACrB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;YACf,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAC7B,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;YACpD,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;YACxB,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;SAC/B,CAAC;QACF,WAAW,EAAE,oBAAoB;KAClC,EACD,KAAK,EAAE,IAAe,EAAE,EAAE;QACxB,MAAM,aAAa,GAAG,yBAAyB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAClE,IAAI,aAAa,EAAE,CAAC;YAClB,OAAO,SAAS,CACd,aAAa,IAAI,CAAC,WAAW,iBAAiB,aAAa,EAAE,CAC9D,CAAC;QACJ,CAAC;QAED,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACjE,IAAI,iBAAiB,EAAE,CAAC;YACtB,OAAO,SAAS,CAAC,iBAAiB,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;YACzC,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACtC;iBACF;gBACD,iBAAiB,EAAE,MAA4C;aAChE,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,SAAS,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;gBAC5C,OAAO,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YAC7C,CAAC;YACD,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACrE,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/docs/curated-tools.md
CHANGED
|
@@ -94,10 +94,24 @@ Events and calendar (write):
|
|
|
94
94
|
Instances and invites (write):
|
|
95
95
|
|
|
96
96
|
- `vrchat_instance_create`
|
|
97
|
+
- `vrchat_instance_link_event`
|
|
97
98
|
- `vrchat_invite`
|
|
98
99
|
- `vrchat_invite_user`
|
|
99
100
|
- `vrchat_invite_user_to_me`
|
|
100
101
|
|
|
102
|
+
`vrchat_instance_link_event` accepts an explicit group, event, world, and instance ID. It first
|
|
103
|
+
checks `groups.allowlist`, then freshly reads both objects and requires the event and group-type
|
|
104
|
+
instance to have that same group owner. It refuses to replace a different existing event link.
|
|
105
|
+
Calls targeting one instance are serialized within the MCP process so concurrent requests cannot
|
|
106
|
+
replace one another after both read an unlinked instance. Re-linking the same event is an
|
|
107
|
+
idempotent no-op that still invalidates the relevant caches. The result includes the group and
|
|
108
|
+
event IDs plus readable event and instance names for safe follow-up calls. The tool can only set
|
|
109
|
+
`calendarEntryId`; it cannot unlink an event or change any other instance field, and it does not
|
|
110
|
+
send invitations or notifications. A conflicting existing link is rejected after invalidating the
|
|
111
|
+
same caches so later reads see that fresh state. VRChat accepts the link only when the event starts
|
|
112
|
+
within six hours or ended within the previous six hours. Like other account-changing tools,
|
|
113
|
+
approval belongs at the MCP client or agent-harness boundary.
|
|
114
|
+
|
|
101
115
|
Groups and social writes:
|
|
102
116
|
|
|
103
117
|
- `vrchat_group_invite`
|
package/docs/robust-notes.md
CHANGED
|
@@ -26,4 +26,12 @@ The separate unresolved policy-sensitive boundary remains VRChat authentication:
|
|
|
26
26
|
|
|
27
27
|
## API Content Upload Boundary
|
|
28
28
|
|
|
29
|
-
VRChat Creator Guidelines (API Usage / Bots, checked 2026-05-26) say not to create or use software, scripts, or applications that upload content such as worlds, avatars, or similar asset types on a user's behalf. Treat generated operations for uploading or managing uploaded asset content as policy-sensitive, not merely high-risk writes. Default policy blocks explicit avatar/world content-management operations (`createAvatar`, `
|
|
29
|
+
VRChat Creator Guidelines (API Usage / Bots, checked 2026-05-26) say not to create or use software, scripts, or applications that upload content such as worlds, avatars, or similar asset types on a user's behalf. Treat generated operations for uploading or managing uploaded asset content as policy-sensitive, not merely high-risk writes. Default policy blocks explicit avatar/world content-management operations (`createAvatar`, `deleteAvatar`, `createWorld`, `updateWorld`, `deleteWorld`, `publishWorld`, `unpublishWorld`) in central operation validation and hides their generated write tools.
|
|
30
|
+
|
|
31
|
+
`updateAvatar` is the one exception, and the boundary moved rather than disappeared. It is no longer in central operation validation, because the curated `vrchat_avatar_update` tool has to reach it; it is listed as curated-only instead, so the raw call tool and the generated write registry both still refuse it. That tool writes `name`, `description`, `releaseStatus` and content tags, and never `assetUrl`, `unityPackageUrl`, `unityVersion` or `version`. The reasoning: editing metadata on already-uploaded content is a different act from uploading content on a user's behalf, which is what the guideline text addresses.
|
|
32
|
+
|
|
33
|
+
**Avatar `releaseStatus` is deliberately allowed** (decided 2026-08-03). It looks like the avatar analogue of `publishWorld` / `unpublishWorld`, but those are blocked for a reason avatars do not share: unpublishing a world sends it back through Community Labs, so toggling world publication has a real and costly consequence an agent should not be able to trigger. Avatars have no equivalent gate. The avatar act this reasoning permits is metadata editing and nothing more: `createAvatar` and `deleteAvatar` both remain hard-blocked, creation because it is upload and deletion because it is destructive and irreversible. Changing an existing avatar's publication state is metadata management, not upload or republication.
|
|
34
|
+
|
|
35
|
+
This does not make `releaseStatus` casual — it can still expose a creator's work against their terms, leak an avatar meant to stay private, or breach content policy depending on what the avatar contains, which is why the tool description tells callers to confirm intent first.
|
|
36
|
+
|
|
37
|
+
Do not assume all image-like uploads are prohibited: icons, prints, gallery images, file upload pipeline operations, and similar user image/file operations remain separate policy decisions. Selecting or viewing avatars is a different class from uploading avatar content; do not over-block ordinary user account actions without mapping them to the guideline text.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Spec Drift
|
|
2
|
+
|
|
3
|
+
`specs/vrchat-openapi.yaml` is the **community** OpenAPI description
|
|
4
|
+
([vrchatapi/specification](https://github.com/vrchatapi/specification)), not one VRChat
|
|
5
|
+
publishes. It is reverse-engineered and maintained by volunteers, so it drifts from the live API
|
|
6
|
+
continuously and permanently. That is expected, not a bug in the spec.
|
|
7
|
+
|
|
8
|
+
**The live API is the source of truth — not the spec, and not this file.** This is a log of
|
|
9
|
+
divergences we have actually observed, with dates and evidence, so nobody re-investigates the same
|
|
10
|
+
thing from scratch.
|
|
11
|
+
|
|
12
|
+
Every entry is a point-in-time observation and can go stale. VRChat may change behavior, the
|
|
13
|
+
community spec may catch up, and a workaround recorded here may become unnecessary or actively
|
|
14
|
+
wrong. **Before making a substantive change that depends on an entry, re-verify it against the
|
|
15
|
+
live API and update the entry with what you found.** An old date is a reason for suspicion, not
|
|
16
|
+
confidence.
|
|
17
|
+
|
|
18
|
+
Add an entry whenever you find a new divergence, including ones you decide not to work around.
|
|
19
|
+
Where a fix is needed, prefer `scripts/postprocess-schemas.ts` over editing the spec:
|
|
20
|
+
`specs/vrchat-openapi.yaml` is gitignored, so spec edits are local-only and vanish on a fresh
|
|
21
|
+
clone.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Confirmed divergences
|
|
26
|
+
|
|
27
|
+
### `GroupPermissions` enum is incomplete
|
|
28
|
+
*Observed 2026-08-02, not re-verified since · patched in `postprocess-schemas.ts`*
|
|
29
|
+
|
|
30
|
+
Spec lists 25 values. `GET /groups/{groupId}/permissions` returns **27**, the extras being
|
|
31
|
+
`group-instance-announcement-create` and `group-instance-bypass-avatar-performance`. Any role
|
|
32
|
+
update using them failed zod validation before reaching VRChat.
|
|
33
|
+
|
|
34
|
+
### `Group.transferTargetId` is nullable
|
|
35
|
+
*Observed 2026-08-02, not re-verified since · patched in `postprocess-schemas.ts`*
|
|
36
|
+
|
|
37
|
+
Spec types it as a non-null `UserID`. Groups with no pending ownership transfer return `null`,
|
|
38
|
+
which failed the whole `getGroup` parse and took `vrchat_group_profile` down with it.
|
|
39
|
+
|
|
40
|
+
### `InviteRequest.instanceId` needs the worldId prefix
|
|
41
|
+
*Observed 2026-08-02, not re-verified since · handled in `services/invites/curated.ts`*
|
|
42
|
+
|
|
43
|
+
The spec describes `InstanceID` as the bare instance part
|
|
44
|
+
(`12345~hidden(usr_…)~region(eu)`). `POST /invite/{userId}` rejects that form with
|
|
45
|
+
`400: Invalid location` and requires the **full** `worldId:instanceId~…` string.
|
|
46
|
+
|
|
47
|
+
Verified live: full string → `200`, worldId stripped → `400`.
|
|
48
|
+
|
|
49
|
+
### `CreateInstanceRequest.canRequestInvite` is restricted to private instances
|
|
50
|
+
*Observed 2026-09-01 · handled in `services/instances/curated.ts`*
|
|
51
|
+
|
|
52
|
+
The community schema permits `canRequestInvite` for every instance type. The live API accepts
|
|
53
|
+
`true` only when `type` is `private`; a group instance returns
|
|
54
|
+
`400: Cannot add canRequestInvite to non-private instances.` The same group instance succeeds
|
|
55
|
+
when the field is omitted or set to `false`.
|
|
56
|
+
|
|
57
|
+
### Single calendar events report `occurrenceKind: "single"`
|
|
58
|
+
*Observed 2026-09-01 · handled in `services/events/curated.ts`*
|
|
59
|
+
|
|
60
|
+
The generated `CalendarEvent` schema does not declare `occurrenceKind`. The live group calendar
|
|
61
|
+
event endpoint reports `"single"` for a confirmed non-recurring event, while the curated delete
|
|
62
|
+
tool deliberately exposes the more explicit `targetKind: "single_event"` safety value.
|
|
63
|
+
|
|
64
|
+
### Role permissions have undocumented prerequisites
|
|
65
|
+
*Observed 2026-08-02, not re-verified since*
|
|
66
|
+
|
|
67
|
+
Not a schema issue — the API enforces dependencies the spec never mentions.
|
|
68
|
+
`group-members-remove` and `group-bans-manage` both require `group-members-manage` on the same
|
|
69
|
+
role, otherwise `PUT /groups/{groupId}/roles/{roleId}` returns
|
|
70
|
+
`400: Role missing required permissions: group-members-manage`.
|
|
71
|
+
|
|
72
|
+
### `UserStatus` has no color mapping
|
|
73
|
+
*Observed 2026-08-02, not re-verified since*
|
|
74
|
+
|
|
75
|
+
The spec defines the enum but says nothing about colors, because they are a client-UI concept.
|
|
76
|
+
For reference: **Join Me = blue, Active = green, Ask Me = orange, Busy = red.**
|
|
77
|
+
|
|
78
|
+
### Existing instances can be linked to calendar events with an undocumented `PUT`
|
|
79
|
+
*Observed and re-verified 2026-08-31 · handled by the curated instance linker*
|
|
80
|
+
|
|
81
|
+
The community spec has no update operation for an existing instance. The live API accepts
|
|
82
|
+
`PUT /instances/{worldId}:{instanceId}` with `{"calendarEntryId":"cal_..."}` and returns the
|
|
83
|
+
updated `Instance`. `PATCH` on the same path returned `405`.
|
|
84
|
+
|
|
85
|
+
VRChat returned `400` when the event was outside its link window, with the rule that an event
|
|
86
|
+
must start within six hours or have ended within the previous six hours. Moving the test event
|
|
87
|
+
inside that window made the same `PUT` return `200`. Sending `{"calendarEntryId":null}` also
|
|
88
|
+
returned `200` and removed the link, but the curated tool intentionally exposes linking only.
|
|
89
|
+
|
|
90
|
+
Verified live with an owned disposable test group, event, and group-only instance. No invitation
|
|
91
|
+
or announcement was sent. Because the endpoint is missing from the spec, `core/client.ts` carries
|
|
92
|
+
a narrow operation fallback until the community spec catches up. Raw access is blocked and the
|
|
93
|
+
curated tool sends only `calendarEntryId` after checking the configured group allowlist and both
|
|
94
|
+
objects' ownership.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Suspected, not yet verified
|
|
99
|
+
|
|
100
|
+
### `UpdateAvatarRequest.description` minLength
|
|
101
|
+
Spec declares `minLength: 1`, which would make an empty description an invalid request and leave
|
|
102
|
+
no way to clear one. BASIC reports descriptions are effectively optional in practice, so
|
|
103
|
+
`vrchat_avatar_update` deliberately does **not** enforce a minimum and lets VRChat decide. If a
|
|
104
|
+
`description: ""` write is ever seen failing, record the result here and add the constraint.
|