@three-ws/avatar-agent 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 three.ws
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.
package/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # 3D AI Agent Avatar
2
+
3
+ > **The MCP server for 3D + AI agents.** Inspect, validate, and optimize any GLB. Spawn a textured 3D avatar with a Solana wallet, a voice, and full pump.fun powers — including atomic Jito-bundled launches and creator-fee collection.
4
+
5
+ [![npm](https://img.shields.io/npm/v/%40three-ws%2Favatar-agent?style=flat-square&color=9945FF)](https://www.npmjs.com/package/@three-ws/avatar-agent)
6
+ [![license](https://img.shields.io/badge/license-MIT-14F195?style=flat-square)](./LICENSE)
7
+ [![node](https://img.shields.io/badge/node-%E2%89%A520-9945FF?style=flat-square)](https://nodejs.org)
8
+ [![mcp](https://img.shields.io/badge/Model%20Context%20Protocol-✓-9945FF?style=flat-square)](https://modelcontextprotocol.io)
9
+
10
+ Registry name: **`io.github.nirholas/3D-AI-Agent-Avatar`** · npm: **`@three-ws/avatar-agent`**
11
+
12
+ A single MCP server that's two things at once:
13
+
14
+ 1. **A general-purpose 3D toolkit** — `inspect_glb`, `validate_glb`, `optimize_glb`, `viewer_url` work on **any** GLB/glTF model, no avatar required. Powered by `@gltf-transform/core` + Khronos's official `gltf-validator`.
15
+ 2. **A full 3D agent in a box** — spawn a textured GLB avatar, give it a voice (OpenAI TTS), a Solana wallet, and pump.fun trading powers (Jupiter swaps, atomic Jito-bundled launches, creator-fee collection).
16
+
17
+ Install it once. Your Claude / Cursor / Continue / Cline can now:
18
+
19
+ - **Inspect any GLB** — meshes, materials, textures, animations, bounding box, vertex + triangle counts.
20
+ - **Validate any GLB** against the Khronos glTF 2.0 spec (the same engine behind gltf.report).
21
+ - **Optimize a GLB** — dedup, prune, weld, Draco-compress; get the smaller file back inline.
22
+ - **Build a viewer URL** for any GLB so humans see it in WebGL with one click.
23
+ - **Spawn a 3D avatar** from the curated defaults (`default`, `cz`) or any GLB URL.
24
+ - **Dress it** — hats, glasses, earrings, poses.
25
+ - **Generate a fresh avatar** from a text prompt via Replicate (Hunyuan-3D).
26
+ - **Give it a voice** — OpenAI TTS, 11 voices.
27
+ - **Hand it a Solana wallet** — vanity-grind a `three…` address in milliseconds.
28
+ - **Read live pump.fun data** — Jupiter price, Dexscreener volume, pump.fun meta, holders.
29
+ - **Buy any token** — Jupiter swap, optionally inside a sniper-resistant Jito bundle.
30
+ - **Launch a coin atomically** — separate funder + creator wallets, both txs in the same block.
31
+ - **Collect creator fees atomically** — collect + drain to a safe wallet in one tx.
32
+ - **Resolve names** — ENS (`.eth`) and SNS (`.sol`).
33
+
34
+ Built and maintained by [three.ws](https://three.ws). Atomic pump.fun pieces are ported from [nirholas/atomic](https://github.com/nirholas/atomic).
35
+
36
+ ---
37
+
38
+ ## Quick start (Claude Desktop / Cursor / Continue)
39
+
40
+ Add this to your MCP config:
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "3d-ai-agent-avatar": {
46
+ "command": "npx",
47
+ "args": ["-y", "@three-ws/avatar-agent"],
48
+ "env": {
49
+ "SOLANA_RPC_URL": "https://api.mainnet-beta.solana.com",
50
+ "OPENAI_API_KEY": "sk-...",
51
+ "THREE_MINT": "<the $three CA on pump.fun>"
52
+ }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ Restart your client. Ask it: **"Inspect https://three.ws/avatars/cz.glb, then spawn CZ, give him shades, and read me the latest snapshot for $three."**
59
+
60
+ ---
61
+
62
+ ## Two 30-second demos
63
+
64
+ ### 3D toolkit demo (no avatar required)
65
+
66
+ ```
67
+ You ▸ Inspect this GLB: https://three.ws/avatars/cz.glb — how many triangles?
68
+ Then validate it against the Khronos spec and optimize it with Draco.
69
+ ```
70
+
71
+ Runs:
72
+ 1. `inspect_glb({ url })` → mesh/material/animation breakdown + triangle count + bbox
73
+ 2. `validate_glb({ url })` → official Khronos report
74
+ 3. `optimize_glb({ url, draco: true })` → dedup → prune → weld → Draco, returns the smaller GLB inline
75
+
76
+ ### Avatar agent demo
77
+
78
+ ```
79
+ You ▸ Spawn cz, give him shades, mint him a "three"-prefixed wallet,
80
+ pull a snapshot of $three, and have him say "we're so back."
81
+ ```
82
+
83
+ Behind the scenes the MCP runs:
84
+
85
+ 1. `spawn_avatar({ preset: "cz", voice: "onyx" })` → returns a `sessionId` + viewer URL
86
+ 2. `dress_avatar({ sessionId, accessoryIds: ["glasses-shades"] })`
87
+ 3. `wallet_create({ sessionId, vanityPrefix: "three" })` → base58 pubkey starting with `three…`
88
+ 4. `pump_snapshot({ token: "three" })` → live Jupiter price, Dexscreener volume, holders
89
+ 5. `speak({ sessionId, text: "we're so back" })` → mp3 base64 the client plays
90
+
91
+ ---
92
+
93
+ ## Tool surface (20 tools)
94
+
95
+ ### 3D toolkit (works on any GLB)
96
+ | Tool | What it does |
97
+ |---|---|
98
+ | `inspect_glb` | Mesh / material / animation / skin breakdown + bounding box + vertex & triangle counts. `@gltf-transform/core`. |
99
+ | `validate_glb` | Run Khronos's official `gltf-validator` on a GLB; errors, warnings, infos, hints with JSON pointers. |
100
+ | `optimize_glb` | Dedup → prune → weld → optional Draco. Returns optimized bytes inline + before/after sizes. |
101
+ | `thumbnail_glb` | **Render any GLB to a PNG** via three.ws's hosted three-light rig + auto-framing camera. Same pipeline as OG cards. Returns base64 PNG inline. |
102
+ | `viewer_url` | Build a `three.ws/viewer?...` URL + ready-to-paste iframe snippet for any GLB or avatar session. Supports background, auto-rotate, camera preset OR explicit orbit, AR mode, dimensions. |
103
+
104
+ ### Avatar
105
+ | Tool | What it does |
106
+ |---|---|
107
+ | `list_avatars` | Catalog of default GLB avatars (`default`, `cz`), accessories, and pose presets. |
108
+ | `list_animations` | Live fetch of three.ws's 24 pose presets (T-pose, wave, thinker, jump, dance, warrior2, …) grouped by category. |
109
+ | `spawn_avatar` | Create an avatar session from a preset or custom GLB URL. Returns `sessionId`. |
110
+ | `dress_avatar` | Apply accessories + pose to a session. |
111
+ | `render_avatar` | **Render a posed avatar to a PNG** — pose preset + camera orbit (theta/phi/radius) + ARKit-52 facial expression. Same rig + lighting as the three.ws customizer's save-snapshot. |
112
+ | `generate_avatar` | Text/image-to-3D via Replicate. New session preloaded with the generated GLB. |
113
+
114
+ ### Voice
115
+ | Tool | What it does |
116
+ |---|---|
117
+ | `speak` | OpenAI TTS — synthesize speech in the avatar's voice. Returns base64 audio. |
118
+
119
+ ### Wallet
120
+ | Tool | What it does |
121
+ |---|---|
122
+ | `wallet_create` | Generate a Solana keypair. Optional vanity grinder (`vanityPrefix: "three"`). |
123
+ | `wallet_balance` | Read SOL + all SPL token balances (incl. Token-2022). |
124
+ | `wallet_send` | Send SOL on mainnet. **Execution action.** |
125
+
126
+ ### pump.fun
127
+ | Tool | What it does |
128
+ |---|---|
129
+ | `pump_snapshot` | Live market snapshot: price, volume, holders, meta. Pass `target: "three"` for $three. |
130
+ | `pump_buy` | Jupiter swap, direct or **Jito-bundled** (funder→buyer transfer + swap atomic). **Execution action.** |
131
+ | `pump_launch` | **Atomic launch** via Jito bundle: separate funder + creator wallets, both txs same block. Uploads metadata to pump.fun IPFS if no URI is supplied. **Execution action.** |
132
+ | `pump_collect_fees` | **Atomic collect**: `collectCoinCreatorFee` + drain to safe wallet in one tx inside a Jito bundle — leaked-creator-key resistant. **Execution action.** |
133
+
134
+ ### Identity
135
+ | Tool | What it does |
136
+ |---|---|
137
+ | `ens_sns_resolve` | Resolve `.eth` (ENS) and `.sol` (Bonfida SNS) names to addresses with reverse + favorite-domain lookups. |
138
+
139
+ ---
140
+
141
+ ## Environment variables
142
+
143
+ | Variable | Required for | Notes |
144
+ |---|---|---|
145
+ | `SOLANA_RPC_URL` | All Solana ops | Defaults to `https://api.mainnet-beta.solana.com`. Bring your own (Helius/QuickNode/Triton) for production. |
146
+ | `ETH_RPC_URL` | `ens_sns_resolve` | Optional — falls back to ethers' default public providers. |
147
+ | `HELIUS_API_KEY` | `pump_snapshot` (enhanced) | Adds exact supply + DAS data. |
148
+ | `OPENAI_API_KEY` | `speak` | Used directly against `api.openai.com/v1/audio/speech`. |
149
+ | `REPLICATE_API_TOKEN` | `generate_avatar` | |
150
+ | `REPLICATE_TEXT_TO_AVATAR_MODEL` | `generate_avatar` | Pin a commercial-OK version, e.g. latest `tencent/hunyuan-3d-3.1`. |
151
+ | `SOLANA_SECRET_KEY` | `wallet_send` / `pump_buy` default signer | Per-call `secret` args override. Treat like cash. |
152
+ | `THREE_MINT` | `pump_snapshot` / `pump_buy` shorthand | Set so tools accept `target: "three"`. |
153
+
154
+ ---
155
+
156
+ ## Atomic pump.fun, briefly
157
+
158
+ The `pump_launch` and `pump_collect_fees` tools wrap the pattern from [nirholas/atomic](https://github.com/nirholas/atomic). Two things matter:
159
+
160
+ **Launch:** the create tx's `payerKey` is the **creator** wallet, not the funder — so the on-chain `creator` field (which receives pump.fun creator fees forever) is the creator wallet. But the creator doesn't need to hold SOL: the funder transfers rent + tip in Tx1 of the same Jito bundle. Either both land or neither does.
161
+
162
+ **Collect:** even if a creator key is shared / leaked, the collect-and-drain runs as a single tx inside a Jito bundle. No competing collector can interleave a tx between `collectCoinCreatorFee` and the drain, even with the same key.
163
+
164
+ If you start hitting `Bundles must write lock at least one tip account`, the [Jito tip account list](https://docs.jito.wtf/) has rotated — refresh `src/lib/jito.js`.
165
+
166
+ ---
167
+
168
+ ## Running locally
169
+
170
+ ```bash
171
+ npm install @three-ws/avatar-agent
172
+ npx three-avatar-agent # MCP stdio server
173
+ npx @modelcontextprotocol/inspector npx three-avatar-agent # GUI
174
+ ```
175
+
176
+ Or clone this repo:
177
+
178
+ ```bash
179
+ git clone https://github.com/nirholas/three.ws.git
180
+ cd three.ws/packages/avatar-agent-mcp
181
+ npm install
182
+ npm start
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Safety
188
+
189
+ `pump_buy`, `pump_launch`, `pump_collect_fees`, and `wallet_send` all execute real on-chain transactions and move real funds. The MCP makes no judgment about the input — if your client tells it to send 100 SOL to a random address with a valid signer, it will. Configure your client's tool-approval flow accordingly.
190
+
191
+ Secrets are never logged or persisted. Secrets returned by `wallet_create` are returned ONCE — store them yourself.
192
+
193
+ ---
194
+
195
+ ## License
196
+
197
+ MIT © 2026 three.ws
198
+
199
+ Powered by the [Model Context Protocol](https://modelcontextprotocol.io), the [@nirholas/pump-sdk](https://www.npmjs.com/package/@nirholas/pump-sdk), and Jito's [Block Engine](https://docs.jito.wtf/).
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@three-ws/avatar-agent",
3
+ "version": "1.0.0",
4
+ "mcpName": "io.github.nirholas/3D-AI-Agent-Avatar",
5
+ "description": "3D AI Agent Avatar — MCP server that spawns a textured GLB avatar, inspects/validates/optimizes any 3D model, gives the agent a Solana wallet + a voice, and ships full pump.fun powers (atomic Jito-bundled launches + creator-fee collection). Powered by three.ws.",
6
+ "type": "module",
7
+ "author": "nirholas <hello@three.ws>",
8
+ "license": "MIT",
9
+ "homepage": "https://three.ws",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/nirholas/three.ws.git",
13
+ "directory": "packages/avatar-agent-mcp"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/nirholas/three.ws/issues"
17
+ },
18
+ "keywords": [
19
+ "mcp",
20
+ "model-context-protocol",
21
+ "three.ws",
22
+ "avatar",
23
+ "3d",
24
+ "glb",
25
+ "solana",
26
+ "pump.fun",
27
+ "jito",
28
+ "atomic-bundle",
29
+ "wallet",
30
+ "tts",
31
+ "ai-agent",
32
+ "agent-payments",
33
+ "$three"
34
+ ],
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "files": [
39
+ "src",
40
+ "README.md",
41
+ "LICENSE",
42
+ "server.json"
43
+ ],
44
+ "bin": {
45
+ "three-avatar-agent": "./src/index.js",
46
+ "three-avatar-agent-mcp": "./src/index.js"
47
+ },
48
+ "main": "./src/index.js",
49
+ "exports": {
50
+ ".": "./src/index.js"
51
+ },
52
+ "engines": {
53
+ "node": ">=20"
54
+ },
55
+ "scripts": {
56
+ "start": "node src/index.js",
57
+ "inspect": "npx -y @modelcontextprotocol/inspector node src/index.js"
58
+ },
59
+ "dependencies": {
60
+ "@gltf-transform/core": "^4.3.0",
61
+ "@gltf-transform/extensions": "^4.3.0",
62
+ "@gltf-transform/functions": "^4.3.0",
63
+ "@modelcontextprotocol/sdk": "^1.29.0",
64
+ "@nirholas/pump-sdk": "^1.30.0",
65
+ "@solana/spl-token": "^0.4.14",
66
+ "@solana/web3.js": "^1.98.4",
67
+ "bs58": "^6.0.0",
68
+ "draco3dgltf": "^1.5.7",
69
+ "ethers": "^6.16.0",
70
+ "form-data": "^4.0.5",
71
+ "gltf-validator": "^2.0.0-dev.3.10",
72
+ "zod": "^3.23.8"
73
+ }
74
+ }
package/server.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.nirholas/3D-AI-Agent-Avatar",
4
+ "description": "3D AI Agent Avatar — spawn a textured GLB avatar, inspect/validate/optimize any 3D model, give the agent a Solana wallet + a voice, and ship full pump.fun powers (atomic Jito-bundled launches + creator-fee collection).",
5
+ "repository": {
6
+ "url": "https://github.com/nirholas/three.ws",
7
+ "source": "github"
8
+ },
9
+ "version": "1.0.0",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "identifier": "@three-ws/avatar-agent",
14
+ "version": "1.0.0",
15
+ "transport": {
16
+ "type": "stdio"
17
+ },
18
+ "environmentVariables": [
19
+ {
20
+ "name": "SOLANA_RPC_URL",
21
+ "description": "Solana mainnet RPC endpoint. Defaults to https://api.mainnet-beta.solana.com — bring your own (Helius / QuickNode / Triton) for production traffic.",
22
+ "format": "string",
23
+ "isRequired": false,
24
+ "default": "https://api.mainnet-beta.solana.com"
25
+ },
26
+ {
27
+ "name": "ETH_RPC_URL",
28
+ "description": "Ethereum mainnet RPC for ENS resolution. Optional — falls back to ethers' default public providers.",
29
+ "format": "string",
30
+ "isRequired": false
31
+ },
32
+ {
33
+ "name": "HELIUS_API_KEY",
34
+ "description": "Optional Helius API key — enables exact supply + DAS data in pump_snapshot.",
35
+ "format": "string",
36
+ "isRequired": false,
37
+ "isSecret": true
38
+ },
39
+ {
40
+ "name": "OPENAI_API_KEY",
41
+ "description": "Required for the speak tool (OpenAI TTS).",
42
+ "format": "string",
43
+ "isRequired": false,
44
+ "isSecret": true
45
+ },
46
+ {
47
+ "name": "REPLICATE_API_TOKEN",
48
+ "description": "Required for the generate_avatar tool (Replicate text/image-to-3D).",
49
+ "format": "string",
50
+ "isRequired": false,
51
+ "isSecret": true
52
+ },
53
+ {
54
+ "name": "REPLICATE_TEXT_TO_AVATAR_MODEL",
55
+ "description": "Replicate version hash pinned for text-to-3D — recommended: latest tencent/hunyuan-3d-3.1.",
56
+ "format": "string",
57
+ "isRequired": false
58
+ },
59
+ {
60
+ "name": "SOLANA_SECRET_KEY",
61
+ "description": "Optional default base58 Solana secret for wallet_send / pump_buy. Per-call secret args override this. Treat like cash.",
62
+ "format": "string",
63
+ "isRequired": false,
64
+ "isSecret": true
65
+ },
66
+ {
67
+ "name": "THREE_MINT",
68
+ "description": "Optional pump.fun mint address for $three so tools accept target=\"three\" as shorthand.",
69
+ "format": "string",
70
+ "isRequired": false
71
+ }
72
+ ]
73
+ }
74
+ ]
75
+ }
package/src/config.js ADDED
@@ -0,0 +1,31 @@
1
+ // Centralized env access. The MCP server is user-keyed: every Solana
2
+ // signing operation requires the user to supply a keypair via env or via
3
+ // tool arguments. We never sign on behalf of someone with a baked-in key.
4
+
5
+ export function env(key, fallback) {
6
+ const v = process.env[key];
7
+ return v !== undefined && String(v).trim() !== '' ? String(v).trim() : fallback;
8
+ }
9
+
10
+ export const SOLANA_RPC_URL = env('SOLANA_RPC_URL', 'https://api.mainnet-beta.solana.com');
11
+ export const ETH_RPC_URL = env('ETH_RPC_URL') || env('MAINNET_RPC_URL') || null;
12
+ export const HELIUS_API_KEY = env('HELIUS_API_KEY', '');
13
+ export const OPENAI_API_KEY = env('OPENAI_API_KEY', '');
14
+ export const REPLICATE_API_TOKEN = env('REPLICATE_API_TOKEN', '');
15
+ export const REPLICATE_TEXT_TO_AVATAR_MODEL = env('REPLICATE_TEXT_TO_AVATAR_MODEL', '');
16
+
17
+ // Optional default signer for Solana ops. Tools that sign accept a `secret`
18
+ // argument that overrides this on a per-call basis.
19
+ export const SOLANA_DEFAULT_SECRET = env('SOLANA_SECRET_KEY') || env('FUNDER_SECRET') || '';
20
+
21
+ // $three is the official three.ws token on pump.fun. Used as the canonical
22
+ // example mint in tool examples and README demos.
23
+ export const THREE_MINT = env(
24
+ 'THREE_MINT',
25
+ // Placeholder until the user pins their real $three CA. Override via
26
+ // the THREE_MINT env var when running the MCP.
27
+ '',
28
+ );
29
+
30
+ export const VIEWER_BASE = env('VIEWER_BASE', 'https://three.ws/viewer');
31
+ export const THREE_WS_BASE = env('THREE_WS_BASE', 'https://three.ws');
package/src/index.js ADDED
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ // @three-ws/avatar-agent — MCP server entry point.
3
+ //
4
+ // Boots a Model Context Protocol server over stdio that gives any AI
5
+ // assistant a 3D avatar, a Solana wallet, a voice, and full pump.fun
6
+ // powers (snapshots + Jupiter buys + atomic Jito-bundled launches +
7
+ // creator-fee collection).
8
+ //
9
+ // Run standalone:
10
+ // node packages/avatar-agent-mcp/src/index.js
11
+ //
12
+ // Or wire into Claude Desktop / Cursor — see README.md.
13
+
14
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
15
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
16
+
17
+ import { def as inspectGlb } from './tools/inspect-glb.js';
18
+ import { def as validateGlb } from './tools/validate-glb.js';
19
+ import { def as optimizeGlb } from './tools/optimize-glb.js';
20
+ import { def as thumbnailGlb } from './tools/thumbnail-glb.js';
21
+ import { def as viewerUrl } from './tools/viewer-url.js';
22
+ import { def as listAvatars } from './tools/list-avatars.js';
23
+ import { def as listAnimations } from './tools/list-animations.js';
24
+ import { def as spawnAvatar } from './tools/spawn-avatar.js';
25
+ import { def as dressAvatar } from './tools/dress-avatar.js';
26
+ import { def as renderAvatar } from './tools/render-avatar.js';
27
+ import { def as generateAvatar } from './tools/generate-avatar.js';
28
+ import { def as speak } from './tools/speak.js';
29
+ import { def as walletCreate } from './tools/wallet-create.js';
30
+ import { def as walletBalance } from './tools/wallet-balance.js';
31
+ import { def as walletSend } from './tools/wallet-send.js';
32
+ import { def as pumpSnapshot } from './tools/pump-snapshot.js';
33
+ import { def as pumpBuy } from './tools/pump-buy.js';
34
+ import { def as pumpLaunch } from './tools/pump-launch.js';
35
+ import { def as pumpCollect } from './tools/pump-collect.js';
36
+ import { def as ensSnsResolve } from './tools/ens-sns-resolve.js';
37
+
38
+ const TOOLS = [
39
+ // 3D toolkit — universal GLB / glTF tools
40
+ inspectGlb,
41
+ validateGlb,
42
+ optimizeGlb,
43
+ thumbnailGlb,
44
+ viewerUrl,
45
+ // Avatar
46
+ listAvatars,
47
+ listAnimations,
48
+ spawnAvatar,
49
+ dressAvatar,
50
+ renderAvatar,
51
+ generateAvatar,
52
+ speak,
53
+ // Wallet
54
+ walletCreate,
55
+ walletBalance,
56
+ walletSend,
57
+ // pump.fun
58
+ pumpSnapshot,
59
+ pumpBuy,
60
+ pumpLaunch,
61
+ pumpCollect,
62
+ // Identity
63
+ ensSnsResolve,
64
+ ];
65
+
66
+ async function main() {
67
+ const server = new McpServer(
68
+ { name: '3d-ai-agent-avatar', version: '1.0.0' },
69
+ {
70
+ capabilities: { tools: {} },
71
+ instructions:
72
+ '3D AI Agent Avatar — a complete 3D MCP toolkit plus a Solana-wallet-bearing, pump.fun-trading avatar agent. ' +
73
+ '3D tools (work on any GLB URL, no avatar required): inspect_glb returns mesh/material/animation/bbox stats; ' +
74
+ 'validate_glb runs the official Khronos validator; optimize_glb runs dedup/prune/weld/Draco and returns the ' +
75
+ 'smaller GLB inline; thumbnail_glb renders any GLB to a PNG via three.ws\'s hosted three-light rig (the same ' +
76
+ 'pipeline that generates OG cards); viewer_url builds a three.ws/viewer link + iframe embed. ' +
77
+ 'Avatar flow: list_avatars → list_animations → spawn_avatar (preset "default"/"cz" or any GLB) → dress_avatar → ' +
78
+ 'render_avatar (pose + camera orbit + ARKit-52 expression → real PNG) → speak. generate_avatar text/image-to-3D ' +
79
+ 'via Replicate. wallet_create (optional vanity grinder) gives the avatar a Solana wallet; wallet_balance, ' +
80
+ 'wallet_send for SOL ops. ' +
81
+ 'pump.fun: pump_snapshot for live market data (target="three" for $three); pump_buy via Jupiter, optional Jito ' +
82
+ 'bundle; pump_launch is an atomic Jito-bundled launch with separate funder + creator; pump_collect_fees drains ' +
83
+ 'pump.fun creator-fee vaults atomically. ens_sns_resolve for .eth / .sol names.',
84
+ },
85
+ );
86
+
87
+ for (const tool of TOOLS) {
88
+ server.registerTool(
89
+ tool.name,
90
+ {
91
+ title: tool.title,
92
+ description: tool.description,
93
+ inputSchema: tool.inputSchema,
94
+ },
95
+ async (args, extra) => {
96
+ try {
97
+ const result = await tool.handler(args, extra);
98
+ const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
99
+ return { content: [{ type: 'text', text }] };
100
+ } catch (err) {
101
+ const payload = {
102
+ ok: false,
103
+ error: err?.code || 'unhandled',
104
+ message: err?.message || String(err),
105
+ };
106
+ return {
107
+ content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }],
108
+ isError: true,
109
+ };
110
+ }
111
+ },
112
+ );
113
+ }
114
+
115
+ const transport = new StdioServerTransport();
116
+ await server.connect(transport);
117
+ console.error(`[3d-ai-agent-avatar] connected over stdio with ${TOOLS.length} tools`);
118
+ }
119
+
120
+ main().catch((err) => {
121
+ console.error('[3d-ai-agent-avatar] fatal:', err);
122
+ process.exit(1);
123
+ });
@@ -0,0 +1,141 @@
1
+ // Atomic pump.fun creator-fee collection via a Jito bundle.
2
+ //
3
+ // Ported from nirholas/atomic's collect-jito.js. Single tx in the bundle:
4
+ // 1. Funder pays fee + tip
5
+ // 2. Creator signs `collectCoinCreatorFee` (drains pump.fun's vault to creator)
6
+ // 3. Creator signs a SystemProgram transfer of the freshly-collected SOL
7
+ // to a safe DESTINATION wallet
8
+ //
9
+ // All three steps live in one tx, so even if the creator key is leaked /
10
+ // shared, no other holder of that key can interleave a tx between the
11
+ // collect and the drain.
12
+
13
+ import {
14
+ PublicKey,
15
+ SystemProgram,
16
+ TransactionMessage,
17
+ VersionedTransaction,
18
+ ComputeBudgetProgram,
19
+ LAMPORTS_PER_SOL,
20
+ } from '@solana/web3.js';
21
+
22
+ import { bs58encode, getConnection, keypairFromSecret } from './solana.js';
23
+ import { randomTipAccount, submitBundle, waitForSignatures } from './jito.js';
24
+
25
+ // Rent-exempt minimum for a bare system account. Leaving exactly this in
26
+ // the creator keeps the account alive without locking up unnecessary SOL.
27
+ const RENT_EXEMPT_LAMPORTS = 890_880;
28
+
29
+ export async function atomicCollect({
30
+ funderSecret,
31
+ creatorSecret,
32
+ destination,
33
+ jitoTipSol = 0.005,
34
+ priorityMicroLamports = 3_000_000,
35
+ bufferLamports = RENT_EXEMPT_LAMPORTS,
36
+ minVaultSol = 0.001,
37
+ }) {
38
+ if (!destination) throw new Error('atomicCollect: destination is required');
39
+ const destinationPk = new PublicKey(destination);
40
+ const funder = keypairFromSecret(funderSecret);
41
+ const creator = keypairFromSecret(creatorSecret);
42
+
43
+ const pumpSdkPkg = await import('@nirholas/pump-sdk');
44
+ const OnlinePumpSdk = pumpSdkPkg.OnlinePumpSdk || pumpSdkPkg.default?.OnlinePumpSdk;
45
+ if (!OnlinePumpSdk) {
46
+ throw new Error('@nirholas/pump-sdk: OnlinePumpSdk export missing');
47
+ }
48
+ const conn = getConnection();
49
+ const sdk = new OnlinePumpSdk(conn);
50
+
51
+ const vaultBalance = await sdk.getCreatorVaultBalance(creator.publicKey);
52
+ const vaultLamports = Number(vaultBalance);
53
+ if (vaultLamports < Math.floor(minVaultSol * LAMPORTS_PER_SOL)) {
54
+ return {
55
+ ok: false,
56
+ code: 'vault_too_small',
57
+ vaultSol: vaultLamports / LAMPORTS_PER_SOL,
58
+ minSol: minVaultSol,
59
+ };
60
+ }
61
+
62
+ const creatorPreBal = await conn.getBalance(creator.publicKey, 'confirmed');
63
+ const transferAmount = creatorPreBal + vaultLamports - bufferLamports;
64
+ if (transferAmount <= 0) {
65
+ return {
66
+ ok: false,
67
+ code: 'nothing_to_drain',
68
+ vaultSol: vaultLamports / LAMPORTS_PER_SOL,
69
+ creatorPreBalSol: creatorPreBal / LAMPORTS_PER_SOL,
70
+ };
71
+ }
72
+
73
+ const funderBal = await conn.getBalance(funder.publicKey, 'confirmed');
74
+ const needed = (jitoTipSol + 0.002) * LAMPORTS_PER_SOL;
75
+ if (funderBal < needed) {
76
+ const err = new Error(
77
+ `Funder needs >= ${(needed / LAMPORTS_PER_SOL).toFixed(4)} SOL; has ${(funderBal / LAMPORTS_PER_SOL).toFixed(4)} SOL.`,
78
+ );
79
+ err.code = 'insufficient_funds';
80
+ throw err;
81
+ }
82
+
83
+ const tipAccount = randomTipAccount();
84
+ const collectIxs = await sdk.collectCoinCreatorFeeInstructions(creator.publicKey, funder.publicKey);
85
+ const { blockhash } = await conn.getLatestBlockhash('confirmed');
86
+
87
+ const msg = new TransactionMessage({
88
+ payerKey: funder.publicKey,
89
+ recentBlockhash: blockhash,
90
+ instructions: [
91
+ ComputeBudgetProgram.setComputeUnitPrice({ microLamports: priorityMicroLamports }),
92
+ ComputeBudgetProgram.setComputeUnitLimit({ units: 100_000 }),
93
+ SystemProgram.transfer({
94
+ fromPubkey: funder.publicKey,
95
+ toPubkey: tipAccount,
96
+ lamports: Math.floor(jitoTipSol * LAMPORTS_PER_SOL),
97
+ }),
98
+ ...collectIxs,
99
+ SystemProgram.transfer({
100
+ fromPubkey: creator.publicKey,
101
+ toPubkey: destinationPk,
102
+ lamports: transferAmount,
103
+ }),
104
+ ],
105
+ }).compileToV0Message();
106
+
107
+ const tx = new VersionedTransaction(msg);
108
+ tx.sign([funder, creator]);
109
+
110
+ // Simulate first so we surface program errors before paying the tip.
111
+ const sim = await conn.simulateTransaction(tx, { sigVerify: false, replaceRecentBlockhash: false });
112
+ if (sim.value.err) {
113
+ return {
114
+ ok: false,
115
+ code: 'simulation_failed',
116
+ err: sim.value.err,
117
+ logs: sim.value.logs,
118
+ };
119
+ }
120
+
121
+ const bundle = [bs58encode(tx.serialize())];
122
+ const { bundleId, explorer } = await submitBundle(bundle);
123
+ const sig = bs58encode(tx.signatures[0]);
124
+ const wait = await waitForSignatures(conn, [sig], { timeoutMs: 60_000, intervalMs: 2_000 });
125
+
126
+ return {
127
+ ok: wait.ok,
128
+ bundleId,
129
+ bundleExplorer: explorer,
130
+ signature: sig,
131
+ statuses: wait.statuses,
132
+ err: wait.err,
133
+ creator: creator.publicKey.toBase58(),
134
+ destination,
135
+ drainedLamports: transferAmount,
136
+ drainedSol: transferAmount / LAMPORTS_PER_SOL,
137
+ vaultLamports,
138
+ vaultSol: vaultLamports / LAMPORTS_PER_SOL,
139
+ txExplorer: `https://solscan.io/tx/${sig}`,
140
+ };
141
+ }