npx skills add ...
npx skills add msw-git/msw-ai-coding-plugins-official --skill msw-search
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.
npx skills add msw-git/msw-ai-coding-plugins-official --skill msw-search
MSW has two distinct search targets:
.d.mlua.| 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 |
| "Set sprite pivot", "set 9-slice border", "slice boundary for UI RUID", "asset properties" | Call msw-mcp asset_update_resource_storage_info directly (properties: [{ key, value }] — pivot_x/y, border_left/right/top/bottom, filter_mode, wrap_mode) |
★ 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.
Vector search via the msw-mcp MCP server. Supplies the detailed descriptions, code examples, related APIs, and implementation guides missing from .d.mlua.
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?" |
.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 |
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 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 |
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: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:
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.
msw_resource_api.cjsAll resource-API calls in this skill are made through the Node.js wrapper
Do not assemble curl commands by hand. The wrapper:
{"detail":"There was an error parsing the body"} failure mode that hits inline curl -d '...'.npc/1013617.img).fetch / AbortController).topK, resourceTypeFilter, categoryFilter, count, …). Legacy names like limit / types / categories are silently ignored by the server.Two ways to use it:
| 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.
The wrapper handles all of this — you do not need to set it manually.
https://maplestoryworlds-resourcesearch-new.nexon.com/api/v3/ prefix, POST bodies are application/json; charset=utf-8_request(method, path, { timeout }))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).
msw_resource_api.cjs handle itIf you must POST without the wrapper (no HTTP client in your language), reproduce its behaviour:
Content-Type: application/json; charset=utf-8.--data-binary "@file" reading a UTF-8 temp file).Otherwise, just call the wrapper.
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.
category values that actually appear on responses. Use these with categoryFilter.
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 |
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".
A 32-character hex string that uniquely identifies every resource. Example: "0017da7385e04bc4b2ddbe5949b4b462"
id field in search results is the RUIDassetGuid is a separate Unity asset GUID (used in spawn_preset)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:
list cursor into a search call (or vice versa) — the server ignores the wrong-shape value and returns the first page.offset entirely. Sending integer 0 to list / packs is interpreted as a cursor and yields zero items (silent failure).nextOffset: null (list / packs) or returns fewer items than topK (search / similar).| 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).
★ 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 |
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/.
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 |
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.
topK for search/similar, limit for list/packs, count for 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..d.mlua directly.listResources(...) (CLI: list) by type/category.POST returns {"detail":"There was an error parsing the body"} → you bypassed
the wrapper and sent JSON inline via curl -d '{...}'. Switch to
msw_resource_api.cjs (or replicate its UTF-8 raw-body POST pattern) as
described in Section 2.AttackComponent CalcCritical for docs, red slime jump for resources.