threenative-animation-mcp 0.1.0 → 0.1.1
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/dist/index.js +1 -1
- package/dist/server.js +0 -0
- package/package.json +20 -20
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -78,7 +78,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
78
78
|
`;function i$(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll('"',""")}function s$(e){return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Pose Lab \u2014 ${i$(e)}</title><style>${bP}</style></head><body><div id="root" data-project="${i$(e)}"></div><script type="module" src="/app.js"></script></body></html>`}var wP=new URL("./client/app.js",import.meta.url),EP=256e3;function IP(e){let t=new Map;for(let r=0;r<e.length;r+=1){let i=e[r];if(i!=="--port"&&i!=="--project")throw new Error(`Unknown option: ${i}`);let s=e[r+1];if(s===void 0)throw new Error(`${i} requires a value.`);t.set(i,s),r+=1;}let n=t.get("--port"),o=n===void 0?void 0:Number(n);if(o!==void 0&&(!Number.isInteger(o)||o<1||o>65535))throw new Error("--port must be an integer from 1 to 65535.");return {...o===void 0?{}:{port:o},...t.get("--project")===void 0?{}:{project:t.get("--project")}}}function a$(e,t){if(t.length===0||tr.isAbsolute(t)||t.split(/[\\/]/u).some(r=>r.length===0||r.startsWith(".")))return;let n=tr.resolve(e),o=tr.resolve(n,t);return o.startsWith(`${n}${tr.sep}`)?o:void 0}async function zP(e){let t=[],n=new Set([".git","build","dist","node_modules"]),o=async(r,i,s)=>{if(s>6||t.length>=2e3)return;let a=await readdir(r,{withFileTypes:true}).catch(()=>[]);for(let c of a){if(c.name.startsWith(".")||n.has(c.name))continue;let u=i.length===0?c.name:`${i}/${c.name}`;c.isDirectory()?await o(tr.join(r,c.name),u,s+1):c.name.toLowerCase().endsWith(".glb")&&t.push(u);}};return await o(e,"",0),t.sort((r,i)=>r.localeCompare(i))}async function OP(e){let t=[],n=0;for await(let o of e){let r=Buffer.from(o);if(n+=r.length,n>EP)throw new Error("Request exceeds 256 KB.");t.push(r);}return JSON.parse(Buffer.concat(t).toString("utf8"))}function Ed(e,t,n,o){e.writeHead(t,{"cache-control":"no-store","content-type":o,"x-content-type-options":"nosniff"}),e.end(n);}function er(e,t,n){Ed(e,t,JSON.stringify(n),"application/json; charset=utf-8");}async function lv(e={}){let t=tr.resolve(e.project??process.cwd()),n=e.port??4192;await readdir(t).catch(()=>{throw new Error(`Project directory does not exist: ${t}`)});let o=createServer(async(i,s)=>{try{let a=new URL(i.url??"/","http://pose-lab");if(i.method==="GET"&&(a.pathname==="/"||a.pathname==="/pose"))return Ed(s,200,s$(tr.basename(t)),"text/html; charset=utf-8");if(i.method==="GET"&&a.pathname==="/app.js")return Ed(s,200,await readFile(wP),"text/javascript; charset=utf-8");if(i.method==="GET"&&a.pathname==="/favicon.ico")return s.writeHead(204),s.end();if(i.method==="GET"&&a.pathname==="/api/models")return er(s,200,{models:await zP(t)});if(i.method==="GET"&&a.pathname==="/api/model"){let c=a.searchParams.get("path")??"",u=c.endsWith(".glb")?a$(t,c):void 0;if(u===void 0)return er(s,404,{error:"GLB not found."});let l=await readFile(u).catch(()=>{});return l===void 0?er(s,404,{error:"GLB not found."}):Ed(s,200,l,"model/gltf-binary")}if(a.pathname==="/api/recipe"){let c=a.searchParams.get("path")??"",u=c.endsWith(".json")?a$(t,c):void 0;if(u===void 0)return er(s,404,{error:"Recipe not found."});if(i.method==="GET"){let l=await readFile(u,"utf8").catch(()=>{});return l===void 0?er(s,404,{error:"Recipe not found."}):er(s,200,{recipe:JSON.parse(l),path:c})}if(i.method==="PUT"){let l=await OP(i);return typeof l.recipe!="object"||l.recipe===null?er(s,400,{error:"Body must contain a recipe object."}):(await mkdir(tr.dirname(u),{recursive:!0}),await writeFile(u,`${JSON.stringify(l.recipe,null,2)}
|
|
79
79
|
`,"utf8"),er(s,200,{path:c,saved:!0}))}}return er(s,404,{error:"Not found."})}catch(a){return er(s,500,{error:a instanceof Error?a.message:String(a)})}});await new Promise((i,s)=>{o.once("error",s),o.listen(n,"127.0.0.1",i);});let r=o.address();return {port:typeof r=="object"&&r!==null?r.port:n,close:()=>new Promise((i,s)=>o.close(a=>a?s(a):i()))}}function kP(){let e=process.argv[1];return e!==void 0&&tr.resolve(e)===tr.resolve(fileURLToPath(import.meta.url))}kP()&&lv(IP(process.argv.slice(2))).then(({port:e})=>process.stdout.write(`Pose Lab: http://127.0.0.1:${e}/pose
|
|
80
80
|
`)).catch(e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}
|
|
81
|
-
`),process.exitCode=1;});var AP=re.object({position:re.tuple([re.number(),re.number(),re.number()]),rotation:re.tuple([re.number(),re.number(),re.number()]),scale:re.tuple([re.number(),re.number(),re.number()])}),f$=re.enum(wd.map(e=>e.id)),CP=re.object({attachment:re.enum(["attached","detached"]).optional(),clip:re.string().min(1).max(200),time:re.number().min(0).max(600),transform:AP}),MP=re.object({file:re.string().min(1)}),u$=re.object({bones:re.array(re.string()),bytes:re.number(),clips:re.array(re.object({channels:re.number(),duration:re.number(),horizontalTravel:re.number(),inPlace:re.boolean(),name:re.string()})),extensionsUsed:re.array(re.string()),file:re.string(),generator:re.string(),inPlace:re.boolean(),landmarks:re.array(re.object({id:re.string(),label:re.string(),node:re.string(),source:re.enum(["detected","estimated"])})),meshes:re.number(),rootBone:re.string().nullable(),skins:re.number(),warnings:re.array(re.string())}),l$=re.object({anchor:f$.default("right-hand"),attached:re.string().min(1).optional(),bone:re.string().min(1).optional(),host:re.string().min(1).optional(),keys:re.array(CP).max(2e3).optional(),log:re.string().min(1).optional(),output:re.string().min(1).optional(),recipe:re.string().min(1).optional()}),d$=re.object({anchor:re.string(),attached:re.string(),clips:re.array(re.string()),detached:re.array(re.string()),file:re.string(),log:re.string(),node:re.string()}),mv=re.object({anchor:f$.default("right-hand"),attach:re.string().min(1).optional(),host:re.string().min(1).optional(),output:re.string().min(1).optional(),port:re.number().int().min(0).max(65535).optional(),project:re.string().min(1).optional()}),m$=re.object({port:re.number(),url:re.string()}),dv=e=>({destructiveHint:false,idempotentHint:true,openWorldHint:false,readOnlyHint:e}),Qo=e=>[{type:"text",text:JSON.stringify(e)}];function jP(e,t,n){if(n!==void 0){let i=wd.find(s=>s.id===t);if(i===void 0)throw new Error(`Unknown anchor '${t}'.`);return {id:t,label:i.label,node:n,source:"detected"}}let o=e.find(i=>i.id===t);if(o!==void 0)return o;let r=e.map(i=>`${i.id} \u2192 ${i.node}`);throw new Error(`The host names no '${t}' landmark. It answers to: ${r.length===0?"none":r.join(", ")}. Pass 'bone' to name one directly.`)}function DP(e){return {position:e.position,rotation:e.rotation,scale:e.scale}}function UP(e){let t=tr.parse(e);return tr.join(t.dir,`${t.name}.anim.glb`)}async function LP(e){let t=(e.keys??[]).map(i=>({attachment:i.attachment,clip:i.clip,op:"key",time:i.time,transform:DP(i.transform)})),n;if(e.recipe!==void 0){n=XT(JSON.parse(await readFile(e.recipe,"utf8")));for(let i of t)n=YT(n,i);}else {if(e.host===void 0||e.attached===void 0)throw new Error("Pass 'host' and 'attached', or a 'recipe' that names both.");let i=await cv(e.host);n={host:e.host,ops:[{anchor:jP(i.landmarks,e.anchor,e.bone),attached:e.attached,op:"attach"},...t],version:4};}let o=e.log??QT(n.host);return await e$(o,n),{...await n$(n,e.output??UP(n.host)),log:tr.resolve(o)}}var p$;function h$(e){return p$??=(async()=>{let t=await lv({...e.port===void 0?{}:{port:e.port},...e.project===void 0?{}:{project:e.project}}),n=new URLSearchParams;return e.host!==void 0&&n.set("host",e.host),e.attach!==void 0&&n.set("attach",e.attach),e.output!==void 0&&n.set("output",e.output),n.set("anchor",e.anchor),{port:t.port,url:`http://127.0.0.1:${t.port}/pose?${n.toString()}`}})(),p$}function FP(){let e=new qs({name:"threenative-animation",version:"0.1.
|
|
81
|
+
`),process.exitCode=1;});var AP=re.object({position:re.tuple([re.number(),re.number(),re.number()]),rotation:re.tuple([re.number(),re.number(),re.number()]),scale:re.tuple([re.number(),re.number(),re.number()])}),f$=re.enum(wd.map(e=>e.id)),CP=re.object({attachment:re.enum(["attached","detached"]).optional(),clip:re.string().min(1).max(200),time:re.number().min(0).max(600),transform:AP}),MP=re.object({file:re.string().min(1)}),u$=re.object({bones:re.array(re.string()),bytes:re.number(),clips:re.array(re.object({channels:re.number(),duration:re.number(),horizontalTravel:re.number(),inPlace:re.boolean(),name:re.string()})),extensionsUsed:re.array(re.string()),file:re.string(),generator:re.string(),inPlace:re.boolean(),landmarks:re.array(re.object({id:re.string(),label:re.string(),node:re.string(),source:re.enum(["detected","estimated"])})),meshes:re.number(),rootBone:re.string().nullable(),skins:re.number(),warnings:re.array(re.string())}),l$=re.object({anchor:f$.default("right-hand"),attached:re.string().min(1).optional(),bone:re.string().min(1).optional(),host:re.string().min(1).optional(),keys:re.array(CP).max(2e3).optional(),log:re.string().min(1).optional(),output:re.string().min(1).optional(),recipe:re.string().min(1).optional()}),d$=re.object({anchor:re.string(),attached:re.string(),clips:re.array(re.string()),detached:re.array(re.string()),file:re.string(),log:re.string(),node:re.string()}),mv=re.object({anchor:f$.default("right-hand"),attach:re.string().min(1).optional(),host:re.string().min(1).optional(),output:re.string().min(1).optional(),port:re.number().int().min(0).max(65535).optional(),project:re.string().min(1).optional()}),m$=re.object({port:re.number(),url:re.string()}),dv=e=>({destructiveHint:false,idempotentHint:true,openWorldHint:false,readOnlyHint:e}),Qo=e=>[{type:"text",text:JSON.stringify(e)}];function jP(e,t,n){if(n!==void 0){let i=wd.find(s=>s.id===t);if(i===void 0)throw new Error(`Unknown anchor '${t}'.`);return {id:t,label:i.label,node:n,source:"detected"}}let o=e.find(i=>i.id===t);if(o!==void 0)return o;let r=e.map(i=>`${i.id} \u2192 ${i.node}`);throw new Error(`The host names no '${t}' landmark. It answers to: ${r.length===0?"none":r.join(", ")}. Pass 'bone' to name one directly.`)}function DP(e){return {position:e.position,rotation:e.rotation,scale:e.scale}}function UP(e){let t=tr.parse(e);return tr.join(t.dir,`${t.name}.anim.glb`)}async function LP(e){let t=(e.keys??[]).map(i=>({attachment:i.attachment,clip:i.clip,op:"key",time:i.time,transform:DP(i.transform)})),n;if(e.recipe!==void 0){n=XT(JSON.parse(await readFile(e.recipe,"utf8")));for(let i of t)n=YT(n,i);}else {if(e.host===void 0||e.attached===void 0)throw new Error("Pass 'host' and 'attached', or a 'recipe' that names both.");let i=await cv(e.host);n={host:e.host,ops:[{anchor:jP(i.landmarks,e.anchor,e.bone),attached:e.attached,op:"attach"},...t],version:4};}let o=e.log??QT(n.host);return await e$(o,n),{...await n$(n,e.output??UP(n.host)),log:tr.resolve(o)}}var p$;function h$(e){return p$??=(async()=>{let t=await lv({...e.port===void 0?{}:{port:e.port},...e.project===void 0?{}:{project:e.project}}),n=new URLSearchParams;return e.host!==void 0&&n.set("host",e.host),e.attach!==void 0&&n.set("attach",e.attach),e.output!==void 0&&n.set("output",e.output),n.set("anchor",e.anchor),{port:t.port,url:`http://127.0.0.1:${t.port}/pose?${n.toString()}`}})(),p$}function FP(){let e=new qs({name:"threenative-animation",version:"0.1.1"});return e.registerTool("animation_inspect",{annotations:dv(true),description:"Inspect a local rigged GLB: bones, skeleton landmarks, clips with durations, root-motion travel and in-place detection, meshes, skins and extensions. Reads one local file and writes nothing.",inputSchema:MP,outputSchema:u$,title:"Inspect a rig and its clips"},async t=>{try{let n=u$.parse(await cv(t.file));return {content:Qo(n),structuredContent:n}}catch(n){return {content:Qo({message:n instanceof Error?n.message:String(n)}),isError:true}}}),e.registerTool("animation_attach",{annotations:dv(false),description:"Attach a prop GLB to a bone, with per-clip keyed offsets, and bake it into an ordinary GLB beside the source. Writes the v4 op log (<model>.anim.json) next to the source GLB and the baked GLB; a version 3 pose recipe loads as the same ops and still bakes.",inputSchema:l$,outputSchema:d$,title:"Attach a prop to a bone and bake it"},async t=>{try{let n=d$.parse(await LP(l$.parse(t)));return {content:Qo(n),structuredContent:n}}catch(n){return {content:Qo({message:n instanceof Error?n.message:String(n)}),isError:true}}}),e.registerTool("animation_open_view",{annotations:dv(false),description:"Start the animation calibrator page on 127.0.0.1 and return its URL. A human drags the prop into place there; the same operations an agent calls are what the page records.",inputSchema:mv,outputSchema:m$,title:"Open the animation calibrator"},async t=>{try{let n=m$.parse(await h$(mv.parse(t)));return {content:Qo(n),structuredContent:n}}catch(n){return {content:Qo({message:n instanceof Error?n.message:String(n)}),isError:true}}}),e}function qP(){BR(()=>FP());}function VP(){let e=process.argv[1];return e!==void 0&&tr.resolve(e)===tr.resolve(fileURLToPath(import.meta.url))}VP()&&(process.argv[2]==="--view"?h$(mv.parse({project:process.cwd()})).then(({url:e})=>process.stdout.write(`Animation view: ${e}
|
|
82
82
|
`),e=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}
|
|
83
83
|
`),process.exitCode=1;}):qP());/*! Bundled license information:
|
|
84
84
|
|
package/dist/server.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "threenative-animation-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Author, repair and verify character animation through one MCP: inspect rigs, attach props, edit and verify clips, and open a calibrator view.",
|
|
6
6
|
"keywords": [
|
|
@@ -43,28 +43,28 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"sideEffects": false,
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsup --config tsup.config.ts && publint",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"test": "publint --strict"
|
|
50
|
+
},
|
|
46
51
|
"dependencies": {
|
|
47
|
-
"@gltf-transform/core": "
|
|
48
|
-
"@gltf-transform/extensions": "
|
|
49
|
-
"@modelcontextprotocol/server": "
|
|
50
|
-
"react": "
|
|
51
|
-
"react-dom": "
|
|
52
|
-
"three": "
|
|
53
|
-
"zod": "
|
|
52
|
+
"@gltf-transform/core": "catalog:",
|
|
53
|
+
"@gltf-transform/extensions": "catalog:",
|
|
54
|
+
"@modelcontextprotocol/server": "catalog:",
|
|
55
|
+
"react": "catalog:",
|
|
56
|
+
"react-dom": "catalog:",
|
|
57
|
+
"three": "catalog:",
|
|
58
|
+
"zod": "catalog:"
|
|
54
59
|
},
|
|
55
60
|
"devDependencies": {
|
|
56
|
-
"@types/node": "
|
|
57
|
-
"@types/react": "
|
|
58
|
-
"@types/react-dom": "
|
|
59
|
-
"@types/three": "
|
|
60
|
-
"@typescript/native": "
|
|
61
|
+
"@types/node": "catalog:",
|
|
62
|
+
"@types/react": "catalog:",
|
|
63
|
+
"@types/react-dom": "catalog:",
|
|
64
|
+
"@types/three": "catalog:",
|
|
65
|
+
"@typescript/native": "catalog:",
|
|
61
66
|
"publint": "0.3.22",
|
|
62
67
|
"tsup": "8.5.1",
|
|
63
|
-
"typescript": "
|
|
64
|
-
},
|
|
65
|
-
"scripts": {
|
|
66
|
-
"build": "tsup --config tsup.config.ts && publint",
|
|
67
|
-
"typecheck": "tsc --noEmit",
|
|
68
|
-
"test": "publint --strict"
|
|
68
|
+
"typescript": "catalog:"
|
|
69
69
|
}
|
|
70
|
-
}
|
|
70
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 João Paulo Furtado
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|