name: msw-search description: "MSW search integration — (1) vector search for API docs and implementation guides via the msw-mcp MCP server (mlua_api_retriever / mlua_document_retriever), (2) REST API search for resources (sprite / animation / sound / resource pack / avatar). Use for 'find details, examples, or related APIs not in .d.mlua', 'need a SpriteRUID', 'monster sprite', 'background image', 'find a sound', 'avatar item lookup', etc. Keywords: document search, API details, examples, guide, retriever, resource, sprite, animation, sound, RUID, resource pack, avatar."
MSW Search
MSW has two distinct search targets:
- API docs & implementation guides — Vector search for descriptions, code examples, and related APIs missing from
.d.mlua. - Resources — REST API for sprites, animations, sounds, resource packs, and avatars. The only path for obtaining RUIDs.
Routing Table
| Request type | Go to section |
|---|---|
| "How do I implement this?", "Show me an example", "What related APIs exist?" | Document search |
| ".d.mlua only has the signature; the description is insufficient" | Document search |
| "I don't know the API name (semantic search)" | Document search |
| "Implementation guide / best practice / pattern" | Document search |
| "I need a SpriteRUID", "Find a sprite for monster / NPC / background" | Resource search → start with resource_pack |
| "Find an animation / sound / resource pack" | Resource search → start with resource_pack |
| "Details for this RUID", "Similar resources" | Resource search |
| "Avatar item / default avatar lookup" | Resource search |
| "Upload / list / update / delete my own assets" | Call msw-mcp asset_* tools directly (account_get_my_user_id first for ownerId) |
★ Resource search default — always
resource_packfirstUnless the user explicitly asks for an individual sprite / animationclip / sound / avatar item (or names a non-pack RUID directly), pass
resourceTypeFilter: ["resource_pack"]tosearchResources. A pack bundles every sprite + animation + sound for one asset, so picking a strayspriteoranimationclipfirst usually leaves the entity with a single frame, no animation set, or the wrong asset family.Search the pack → drill into
payload.elements→ assign individual RUIDs. Switch types only on explicit intent: "BGM file", "individual sprite only", "avatar item", "animationclip similar to this RUID", etc.
Section 1 — Document Search (APIs & Guides)
Vector search via the msw-mcp MCP server. Supplies the detailed descriptions, code examples, related APIs, and implementation guides missing from .d.mlua.
Decision Flow
Need API-related information
│
├─ Checking signature / type / property / enum
│ → Read .d.mlua first (highest priority)
│ → If .d.mlua is insufficient, call msw-mcp
│ (code examples, parameter details, related APIs, etc.)
│
├─ Implementation guide / pattern / best practice
│ → mlua_document_retriever
│
└─ Don't know the API name (semantic search)
→ mlua_api_retriever (and/or mlua_document_retriever for broader scope)
API Research Order
Priority 1 — .d.mlua (always first)
If you know the API name, always read .d.mlua first. Signatures, types, properties, event parameters, and enum values can be confirmed here accurately.
Path: Environment/NativeScripts/{Component,Service,Event,Enum,Logic,Misc}/Name.d.mlua
| Situation | Example |
|---|---|
| Confirm method signature | "Does TransformComponent have SetPosition?" |
| Property type / existence | "What is the type of SpriteRendererComponent.RUID?" |
| Event parameter structure | "What are the AttackEvent constructor parameters?" |
| List of enum values | "What are the BodyMoveType values?" |
| Method existence | "What methods does SpawnService have?" |
Priority 2 — Vector search (when .d.mlua is not enough)
.d.mlua contains only signatures and lacks detailed descriptions and examples. Use vector search when you need any of the following.
| Situation | MCP tool | Example query |
|---|---|---|
| Need a code example | mlua_api_retriever |
AIComponent example, BehaviorTree usage |
| Parameter details | mlua_api_retriever |
BadgeService GetBadgeInfosAndWait parameters |
| Related API cross-references | mlua_api_retriever |
AttackComponent related, HitComponent |
| ScriptOverridable check | mlua_api_retriever |
AttackComponent CalcCritical override |
| Don't know the API name | both retrievers | damage calculation, inventory save |
| "How do I …?" implementation guide | mlua_document_retriever |
how to make inventory system |
| Pattern / best practice | mlua_document_retriever |
collision detection best practice |
MCP Tools (msw-mcp)
| Tool | Description |
|---|---|
mlua_api_retriever |
API details for Service / Component / Misc etc. (signatures, parameters, examples). Pass an API/class/function/component name. |
mlua_document_retriever |
Authoring manuals, guidelines, MLua usage, and other document-style material. Pass a natural-language sentence describing what to implement. |
On failure: If a msw-mcp tool call errors out, surface the failure to the user and fall back to .d.mlua. Do not guess — state what you couldn't verify.
Default result count: request 3 results unless wider exploration is explicitly required.
.d.mlua vs Search — Information Comparison
.d.mlua is a type stub (~29 lines); Search returns the full document (254+ lines).
| Information | .d.mlua | Search |
|---|---|---|
| Method signature / types | O | O |
| Property declarations | O | O |
| Detailed method description (DetailDesc) | X | O |
| Code examples (AdditionalPageContent) | X | O |
| Per-parameter descriptions | X | O |
| Related APIs (SeeAlsoAPIs) | X | O |
| Related guides (SeeAlsoGuides) | X | O |
| ScriptOverridable flag | X | O |
| SyncDirection | Partial | O |
| Localized descriptions (Ko/Ja/Es/Zh) | X | O |
Maker Editor Syntax → .mlua Conversion Rules
Code examples in search results use Maker Editor syntax. They must be converted before being used in a local .mlua file.
| Item | Maker Editor | .mlua file | Note |
|---|---|---|---|
| Override declaration | override integer CalcDamage(...) |
method integer CalcDamage(...) |
override → method |
| Block | { ... } |
... end |
Braces → end |
| Exec space (own method) | [server only] |
@ExecSpace("ServerOnly") |
Self-defined methods: annotate explicitly |
| Exec space (override) | [server only] shown / omitted in editor |
Match the parent's @ExecSpace exactly — see warning below |
LEA-3014 if mismatched |
| Property | Property: int32 Score = 0 |
@Sync property int32 Score = 0 |
Add @Sync if synced |
Type int |
int |
integer |
C# int → mlua integer |
Type number |
number |
number |
Same (double) |
Type float |
float |
float |
Same (single) |
number(64-bit double) andfloat(32-bit single) are assignable to each other but remain distinct types. Follow the.d.mluadeclaration.
⚠ Override ExecSpace caveat — LEA-3014
SignatureMismatchThe Maker Editor often hides the parent's exec space and lets you toggle
[server only]freely on anoverrideblock. In.mlua, however, the override's@ExecSpacemust be byte-identical to the parent declared in.d.mlua. If the parent has no@ExecSpace(engine default =ExecSpace=All), the override must also omit@ExecSpaceentirely.Concretely, the AttackComponent / HitComponent damage hooks (
CalcDamage,CalcCritical,GetCriticalDamageRate,GetDisplayHitCount,IsAttackTarget,IsHitTarget,OnAttack) are allExecSpace=Allupstream. Adding@ExecSpace("ServerOnly")produces:[LEA-3014] SignatureMismatch : The signature of <Child>.CalcDamage[... (ExecSpace=ServerOnly)] must match the overridden <Parent>.CalcDamage.[... (ExecSpace=All)].Always look up the parent in
.d.mluafirst and copy its annotation block verbatim. Detail:msw-scripting/SKILL.md§9 "Method override → LEA-3014".
Conversion example — AttackComponent from search results:
-- Maker Editor syntax (search result)
override int CalcDamage(Entity attacker, Entity defender, string attackInfo) {
return 50
}
override boolean CalcCritical(Entity attacker, Entity defender, string attackInfo) {
return _UtilLogic:RandomDouble() < 0.3
}
-- Converted to .mlua
-- ⚠ Parent AttackComponent.CalcDamage / CalcCritical declare no @ExecSpace
-- (ExecSpace=All). Adding @ExecSpace here triggers LEA-3014 SignatureMismatch.
method integer CalcDamage(Entity attacker, Entity defender, string attackInfo)
return 50
end
method boolean CalcCritical(Entity attacker, Entity defender, string attackInfo)
return _UtilLogic:RandomDouble() < 0.3
end
Section 2 — Resource Search (Sprite / Animation / Sound / Resource Pack / Avatar)
REST API for searching and browsing MSW resources. Never guess or fabricate a RUID — always obtain one through this API.
Default search type =
resource_pack— see the pack-first rule under the Routing Table above.
Access — always go through msw_resource_api.cjs
All resource-API calls in this skill are made through the Node.js wrapper
scripts/msw_resource_api.cjs
Do not assemble curl commands by hand. The wrapper:
- Sends UTF-8 JSON bodies directly, so non-ASCII queries (Korean / Japanese / Chinese / emoji) avoid the
{"detail":"There was an error parsing the body"}failure mode that hits inlinecurl -d '...'. - URL-encodes slash-containing path parameters (e.g. pack IDs like
npc/1013617.img). - Zero dependencies (Node 18+ built-in
fetch/AbortController). - Uses the exact OpenAPI field names (
topK,resourceTypeFilter,categoryFilter,count, …). Legacy names likelimit/types/categoriesare silently ignored by the server.
Two ways to use it:
# 1) CLI — fire one call from a shell. Output is pretty-printed JSON.
node scripts/msw_resource_api.cjs \
search "orange mushroom" --resource-type resource_pack --category npc --topK 3
# Discover available subcommands:
node scripts/msw_resource_api.cjs --help
// 2) require — preferred when already in a Node.js context.
const {
searchResources, searchAvatarItems, findSimilarResources,
getResource, getResourcesBatch, getResourceTags,
listResources, randomResources, findPacksContaining,
listAvatars, getAvatarDefaults,
} = require('./scripts/msw_resource_api.cjs');
const result = await searchResources("orange mushroom", {
resourceTypeFilter: ["resource_pack"],
categoryFilter: ["npc"],
topK: 3,
});
Wrapper function ↔ endpoint map
| Wrapper function | CLI subcommand | Endpoint |
|---|---|---|
searchResources |
search |
POST /v3/search/resources |
searchAvatarItems |
search-avatar |
POST /v3/search/resources (avatar mode) |
findSimilarResources |
similar |
GET /v3/search/resources/similar/{ruid} |
getResource |
get |
GET /v3/resources/{ruid} (works for sprite / animationclip / resource_pack / avataritem) |
getResourcesBatch |
batch |
POST /v3/resources/batch |
getResourceTags |
tags |
GET /v3/resources/tags/{ruid} |
listResources |
list |
GET /v3/resources (Qdrant Scroll, opaque-string offset cursor) |
randomResources |
random |
GET /v3/resources/random |
findPacksContaining |
packs |
GET /v3/resources/packs/{ruid} (lists packs containing a RUID — pack id is NOT accepted here) |
listAvatars |
avatars |
GET /v3/avatars |
getAvatarDefaults |
avatar-defaults |
GET /v3/avatars/defaults |
No
/v3/avatars/{ruid}endpoint exists. To inspect an avataritem (color_hex, group members, …), callgetResource(ruid)— the/v3/resources/{ruid}endpoint returns avataritem detail just like any other resource.
Base URL & transport (informational)
The wrapper handles all of this — you do not need to set it manually.
- Base URL:
https://maplestoryworlds-resourcesearch-new.nexon.com/api - No auth (public),
/v3/prefix, POST bodies areapplication/json; charset=utf-8 - Default timeout: 15s (override via the wrapper's
_request(method, path, { timeout }))
Result count — this skill's default is 3
Unless explicitly told otherwise, always send 3 for the result-count parameter
on every search call. The wrapper defaults to 3 as well, and parameter names follow
the OpenAPI spec exactly — note that limit / count / topK differ per endpoint.
| Endpoint | Server parameter | Wrapper default |
|---|---|---|
POST /v3/search/resources (resources + avatar) |
topK |
3 |
GET /v3/search/resources/similar/{ruid} |
topK |
3 |
GET /v3/resources (browsing) |
limit |
3 |
GET /v3/resources/random |
count |
3 |
GET /v3/resources/packs/{ruid} (packs containing a RUID) |
limit |
3 |
The server-side default is 20 or 50, so always pass these parameters explicitly. Increase to 10+ (or 50–100 for avatar broad-browse) only when wider exploration is explicitly required.
offsetparameter caveat — forGET /v3/resourcesandGET /v3/resources/packs/{ruid},offsetis not an integer but the opaque string cursornextOffsetreturned by the previous response. Do not send it on the first page (sending integer0is interpreted as a cursor and returns empty results).
POST body rule — let msw_resource_api.cjs handle it
If you must POST without the wrapper (no HTTP client in your language), reproduce its behaviour:
- Serialize the body as UTF-8 JSON bytes (not a re-encoded shell string).
- Send
Content-Type: application/json; charset=utf-8. - POST raw bytes (e.g. curl's
--data-binary "@file"reading a UTF-8 temp file).
Otherwise, just call the wrapper.
Resource Types
type values (the type field on server responses, and the values you put
into the resourceTypeFilter array when searching):
| type | Description |
|---|---|
sprite |
Static image (PNG) |
animationclip |
Frame-based animation |
resource_pack |
Finished asset bundling sprites + animations + sounds |
bgm |
Background music (audio) |
voice |
Voice clip — NPC dialogue, etc. (audio) |
effect |
Sound effect (audio). Not a visual effect. For visual particles / hit / skill FX, search sprite or animationclip (categories skill / mob / etc). |
avataritem |
Avatar costume item (cap, coat, pants, shoes, weapon, …) — same POST /v3/search/resources endpoint with resourceTypeFilter: ["avataritem"]. See references/resource/search.md ("Avatar Item Search") and references/resource/avatar.md. |
All search and listing endpoints use the same type-filter field name:
resourceTypeFilter(an array). Other names liketypesare silently ignored by the server. The wrapper'sresource_type_filterargument (or CLI--resource-type) maps to this field.
⚠
SpriteRendererComponent.SpriteRUIDaccepts bothspriteandanimationclip, but renders them differently:
animationclip→ all frame layers play (shadow + body + foreground)sprite→ that single Sprite renders onlySymptom of mistake: feeding an
animationclipRUID where you intended asprite(or vice-versa) leaves only the shadow layer visible — the body silently vanishes. Always checkpayload.typeof the response before assigning toSpriteRUID. Usespritefor the static idle/default frame; useanimationcliponly for fields likeStateAnimationComponent.ActionSheetvalues.
skeletonandavataritemRUIDs fail silently (no error, nothing renders) when assigned toSpriteRUID/ImageRUIDwithout thethumbnail://prefix. Conversely,CostumeManagerComponent.Custom*Equip/SkeletonRendererComponent.SkeletonRUID/StateAnimationComponent.ActionSheetdo not accept thethumbnail://prefix — pass a plain RUID there. If the search query targeted an icon / thumbnail image and returned aspriteRUID, that RUID is already renderable directly — addingthumbnail://is redundant. Full assignment rules — accepted types, slot-by-slot prefix matrix, RUID-vs-prefix usage — live inmsw-sprite-ruid/SKILL.md.
Categories
category values that actually appear on responses. Use these with categoryFilter.
General resources (sprite / animationclip / resource_pack / bgm / voice / effect)
| category | Description |
|---|---|
mob |
Monster |
npc |
NPC |
item |
Item |
skill |
Skill effect / skill resources |
object |
Map object (tree, rock, decoration) |
background |
Background / map tile / BGM |
foothold |
Walkable platform |
rope |
Rope |
ladder |
Ladder |
etc |
Uncategorized |
Avatar (avataritem only)
| category | Slot |
|---|---|
cap, hair, face, faceaccessory, eyeaccessory, earaccessory |
Head / face |
coat, longcoat, pants, shoes, glove, cape |
Body |
weapon, twohandweapon, subweapon, shield |
Weapon |
map,effect,uiare not valid category values — they return zero results.
- Looking for maps / backgrounds →
category: "background"or"object".- Looking for visual effects → search
sprite/animationclipwithcategory: "skill"(ormob/etc);effectis the audio resource_type, not a category.- There is no
uiresource family in this index — UI sprites usually live assprite+category: "etc".
RUID
A 32-character hex string that uniquely identifies every resource. Example: "0017da7385e04bc4b2ddbe5949b4b462"
- The
idfield in search results is the RUID assetGuidis a separate Unity asset GUID (used inspawn_preset)- Never guess or fabricate a RUID — always obtain it from an API response
Common Response Fields
{
"id": "32-char hex RUID",
"type": "sprite|animationclip|resource_pack|bgm|voice|effect|avataritem",
"category": "mob|npc|item|skill|object|background|foothold|rope|ladder|etc | <avatar slot>",
"names": {
"ko": ["Korean name"],
"en": ["English name"]
},
"assetGuid": "Unity asset GUID (may or may not exist)",
"payload": {
"width": 64,
"height": 64,
"thumbnail": "https://...",
"pivot": {"x": 32, "y": 32},
"frames": [],
"elements": []
}
}
Pagination — same name, two flavors
nextOffset appears in every list-style response but means different things depending on the endpoint. Round-tripping a value into the wrong endpoint silently misbehaves.
| Endpoint | nextOffset type |
Meaning | How to paginate |
|---|---|---|---|
POST /v3/search/resources (search) |
integer | Item offset (0-based) | Pass it back as offset (number) |
GET /v3/search/resources/similar/{id} (similar) |
integer | Item offset | Same |
GET /v3/resources (list) |
opaque UUID string | Qdrant Scroll cursor | Pass the string back as offset. End-of-stream = null |
GET /v3/resources/packs/{ruid} (packs) |
opaque UUID string | Same cursor | Same |
GET /v3/resources/random |
n/a | No pagination | — |
Rules:
- Never feed a
listcursor into asearchcall (or vice versa) — the server ignores the wrong-shape value and returns the first page. - On the first page, omit
offsetentirely. Sending integer0tolist/packsis interpreted as a cursor and yields zero items (silent failure). - Stop paginating when the response returns
nextOffset: null(list / packs) or returns fewer items thantopK(search / similar).
Endpoint Summary
| Method | Endpoint | Purpose |
|---|---|---|
| POST | /v3/search/resources |
Natural-language semantic search (incl. avatar items via resourceTypeFilter: ["avataritem"]) |
| GET | /v3/search/resources/similar/{ruid} |
Find similar resources |
| GET | /v3/resources/{ruid} |
Single resource details (sprite / animationclip / resource_pack with populated elements / avataritem) |
| POST | /v3/resources/batch |
Batch fetch multiple resources |
| GET | /v3/resources/tags/{ruid} |
AI-generated multilingual tags |
| GET | /v3/resources |
List resources (Qdrant Scroll, opaque-string offset cursor) |
| GET | /v3/resources/random |
Random resource recommendation |
| GET | /v3/resources/packs/{ruid} |
List resource packs containing the given RUID — the path parameter is a 32-char-hex RUID, not a pack id |
| GET | /v3/avatars |
List all avatar items (cached) |
| GET | /v3/avatars/defaults |
Default avatar body / head RUIDs |
Single avataritem detail uses
/v3/resources/{ruid}(no/v3/avatars/{ruid}endpoint exists).
Resource Routing Guide
★ When in doubt, search
resource_packfirst. Only the rows marked with an explicit non-pack intent below should bypass the pack-first default.
| Situation | Wrapper call (CLI subcommand) | Reference file |
|---|---|---|
| "Find a slime / orange mushroom / monster / NPC / item / background / map asset" (default — no type specified) | searchResources(query, { resourceTypeFilter: ["resource_pack"], ... }) (search ... --resource-type resource_pack) |
references/resource/search.md |
| "Find an individual sprite / single image" (user explicitly asked for a sprite) | searchResources(query, { resourceTypeFilter: ["sprite"], ... }) |
references/resource/search.md |
| "Find an individual animationclip" (user explicitly asked for an animation) | searchResources(query, { resourceTypeFilter: ["animationclip"], ... }) |
references/resource/search.md |
| "Find a visual effect / particle / hit FX" | searchResources(query, { resourceTypeFilter: ["animationclip","sprite"], categoryFilter: ["skill","mob","etc"] }) — note: effect here would mean audio, not visual |
references/resource/search.md |
| "Find a sound / BGM / voice / sound-effect" (audio) | searchResources(query, { resourceTypeFilter: ["bgm"|"voice"|"effect"], ... }) — effect resource_type = sound-effect (audio) |
references/resource/search.md |
| "Find a background / map tile / scenery" | searchResources(query, { resourceTypeFilter: ["sprite","animationclip"], categoryFilter: ["background","object"] }) — there is no map category in the index |
references/resource/search.md |
| "Find a costume / hat / shoes / weapon (avatar item)" | searchAvatarItems(...) (search-avatar) |
references/resource/search.md (Avatar Item Search section) + references/resource/avatar.md |
| "Any more monsters like this one?" | findSimilarResources(ruid, ...) (similar) |
references/resource/search.md |
| "Details for RUID abc123" (any type incl. avataritem and resource_pack) | getResource(ruid) (get) |
references/resource/detail.md |
| "Show me a list of monster sprites" | listResources(...) (list) |
references/resource/browse.md |
| "Which resource packs include this RUID?" | findPacksContaining(ruid, ...) (packs) |
references/resource/browse.md |
| "Browse all avatar items" | listAvatars(...) (avatars) |
references/resource/avatar.md |
Typical Workflow (pack-first)
1. searchResources(query, { resourceTypeFilter: ["resource_pack"], topK: 3 })
→ obtain a resource_pack RUID (or pack id like "npc/9072309.img")
→ switch types only on explicit user intent, or fall back when 0 packs match
2. getResource(id)
→ resource_pack: payload.elements is pre-populated with element payloads
(sprite / animationclip / sound RUIDs live here)
→ avataritem: payload has color_hex / group meta
3. Pick the element from payload.elements and assign its RUID to
SpriteRendererComponent.SpriteRUID / StateAnimationComponent.ActionSheet
(or assign avataritem RUIDs through the slot mapping in `msw-avatar`)
Don't call
findPacksContaining(packId)to "open" a pack — that endpoint takes a 32-hex RUID and returns the packs that include that RUID, not the contents of a pack. UsegetResource(packId)for pack contents.
For detailed Request/Response of each endpoint, refer to the files under
references/resource/.
Sprite Orientation — Most Resources Face Left
Most MSW sprite / animationclip / resource_pack assets — especially mob, npc, and player-character — are authored facing left, so a freshly spawned SpriteRendererComponent renders left unless you flip it.
| Situation | What to do |
|---|---|
| Spawn an entity that should face right | Set FlipX = true on SpriteRendererComponent (default is false = left-facing as authored) |
Custom AI / chase using MovementComponent:MoveToDirection |
Update FlipX on direction change: sprite.FlipX = velocity.x > 0 (right ⇒ flip) |
| Monster model / monster collider alignment | Invert TransformComponent.Scale.x instead of FlipX so the sprite and collider stay aligned; see msw-general/references/monster.md |
Native AIChaseComponent / AIWanderComponent |
Engine flips automatically based on movement — do nothing |
Top-down (RectTile) movement |
Decide per-axis: usually flip when dx > 0; sprites with up/down frames need the StateAnimationComponent action set instead |
_EffectService:PlayEffect(...) should face right |
Pass FlipX = true in the options table |
| Player-attached effect must follow the player's facing | Use SyncFlip = true in PlayEffect options, or read PlayerControllerComponent.LookDirectionX |
| Resource is authored facing right (rare) | Inspect payload.thumbnail via GET /v3/resources/{ruid} and invert the rule for that asset |
-- Custom side-view chase: flip sprite to match movement direction
local sprite = self.Entity.SpriteRendererComponent
local selfX = self.Entity.TransformComponent.WorldPosition.x
local dx = targetPos.x - selfX
if dx ~= 0 then
sprite.FlipX = dx > 0 -- target on the right → flip
end
Sanity check — the left-facing convention is not contractual. Open
payload.thumbnailfromGET /v3/resources/{ruid}to confirm.Do not use
TransformComponent.Scale.xas a general renderer flip — for players / effects / non-monster renderers, useSpriteRendererComponent.FlipX. Monster exception: monster models should invertTransformComponent.Scale.xso the sprite and collider stay aligned. Related:msw-combat-system/SKILL.md"Direction check ★",msw-general/references/monster.md.
Shared Tips
- Keyword choice — Use the exact name if you know it; natural-language Korean/English also works.
- Adjust the page-size parameter — the name differs per endpoint (
topKfor search/similar,limitfor list/packs,countfor random). This skill's default is 3 (see the "Result count" table above). Keep it at 3 for precise lookups; increase to 10+ (or 50–100 for avatar broad-browse) only when wider exploration is required. - When search fails:
- Document search fails → read
.d.mluadirectly. - Resource search fails → retry with synonyms or a different category; browse with
listResources(...)(CLI:list) by type/category. POSTreturns{"detail":"There was an error parsing the body"}→ you bypassed the wrapper and sent JSON inline viacurl -d '{...}'. Switch tomsw_resource_api.cjs(or replicate its UTF-8 raw-body POST pattern) as described in Section 2.
- Document search fails → read
- Composite queries are allowed — e.g.
AttackComponent CalcCriticalfor docs,red slime jumpfor resources. - No guessing — Never guess API names, RUIDs, or enum values; always confirm via search or references.