realtime-avatar-mcp 0.4.0 → 0.5.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/README.md CHANGED
@@ -1,27 +1,26 @@
1
1
  # realtime-avatar-mcp
2
2
 
3
- **Not published yet** — the package is marked `private: true`, so `npm publish` skips it rather than relying on nobody running the command. Build it and point your agent at the local path.
3
+ Published on npm as [`realtime-avatar-mcp`](https://www.npmjs.com/package/realtime-avatar-mcp) — run it with `npx`, never installed into an app.
4
4
 
5
5
  An MCP server for Realtime Avatar. [`AGENTS.md`](../../AGENTS.md) tells a coding agent what
6
6
  the API is; this lets it *look* — at your avatars, your balance, your bill — instead of
7
7
  guessing ids and inventing shapes.
8
8
 
9
- ```bash
10
- npm run build
11
- ```
12
-
13
9
  ```jsonc
14
10
  {
15
11
  "mcpServers": {
16
12
  "realtime-avatar": {
17
- "command": "node",
18
- "args": ["/abs/path/to/realtime-avatar-sdk/libs/mcp/dist/bin.js"],
13
+ "command": "npx",
14
+ "args": ["-y", "realtime-avatar-mcp"],
19
15
  "env": { "REALTIME_AVATAR_API_KEY": "tic_test_…" }
20
16
  }
21
17
  }
22
18
  }
23
19
  ```
24
20
 
21
+ Working on it in this repo? `npm run build`, then point `command` at `node` with
22
+ `args: ["/abs/path/to/realtime-avatar-sdk/libs/mcp/dist/bin.js"]`.
23
+
25
24
  ## Tools
26
25
 
27
26
  | Tool | |
package/dist/server.d.ts CHANGED
@@ -5,7 +5,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  * `test/server.test.ts` asserts the two stay equal; the equivalent constant in
6
6
  * `realtime-avatar` had that guard and this one did not, which is how it drifted.
7
7
  */
8
- export declare const MCP_VERSION = "0.4.0";
8
+ export declare const MCP_VERSION = "0.5.0";
9
9
  /**
10
10
  * The Realtime Avatar MCP server.
11
11
  *
package/dist/server.js CHANGED
@@ -10,7 +10,7 @@ import { RealtimeAvatar, isQueued } from "realtime-avatar";
10
10
  * `test/server.test.ts` asserts the two stay equal; the equivalent constant in
11
11
  * `realtime-avatar` had that guard and this one did not, which is how it drifted.
12
12
  */
13
- export const MCP_VERSION = "0.4.0";
13
+ export const MCP_VERSION = "0.5.0";
14
14
  const MICROS_PER_CREDIT = 1_000_000;
15
15
  /** Extension -> asset kind, so a caller does not have to restate what the filename says. */
16
16
  const KIND_BY_EXT = {
@@ -109,6 +109,15 @@ export function createServer(options) {
109
109
  `total ${credits(total)}` +
110
110
  (page.nextCursor ? "\n(more pages available)" : ""));
111
111
  });
112
+ server.registerTool("list_clips", {
113
+ title: "List an avatar's clip library",
114
+ description: "The declared clip library: every non-retired clip with its render status, plus " +
115
+ "the library revision (pass it to set_clip_library as expectedRevision), the pose " +
116
+ "anchor and eligibility. status is the render JOB, not serveability — a clip " +
117
+ "re-rendering keeps serving its previous take.",
118
+ inputSchema: { avatarId: z.string() },
119
+ annotations: { readOnlyHint: true, openWorldHint: true },
120
+ }, async ({ avatarId }) => text(JSON.stringify(await rta.listClips(avatarId), null, 2)));
112
121
  if (!options.allowWrites)
113
122
  return server;
114
123
  // ── writes, opt-in only ────────────────────────────────────────────────────
@@ -153,9 +162,48 @@ export function createServer(options) {
153
162
  "Relay this payload to the browser byte-for-byte — the client SDK validates it " +
154
163
  "strictly and rejects a reshaped object.");
155
164
  });
165
+ server.registerTool("set_clip_library", {
166
+ title: "Declare an avatar's clip library",
167
+ description: "Declare the avatar's FULL desired clip library — a declaration, not a delta: " +
168
+ "unchanged clips are kept, new or changed ones are queued to render, omitted ones " +
169
+ "are retired. The 202 is acceptance, not readiness — poll list_clips until no row " +
170
+ "is queued or generating. Pass expectedRevision from list_clips so a concurrent " +
171
+ "writer surfaces as a 409 instead of a lost update. Does not spend credits.",
172
+ inputSchema: {
173
+ avatarId: z.string(),
174
+ clips: z.array(z.object({
175
+ clipId: z.string().describe("Stable id you choose; same id + same source = kept"),
176
+ role: z.enum(["idle", "listen", "gesture"]),
177
+ whenHint: z.string().optional().describe("Briefed to the character, like an actor"),
178
+ source: z.union([
179
+ z.object({ motionPrompt: z.string() }),
180
+ z.object({ assetId: z.string() }),
181
+ ]).describe("motionPrompt renders motion; assetId uploads a clip that must start AND end on the rest pose"),
182
+ durationSeconds: z.number().optional(),
183
+ reroll: z.boolean().optional().describe("Set true to force a re-render of the same prompt"),
184
+ })).max(12).describe("The COMPLETE library. Omitted clips are retired."),
185
+ expectedRevision: z.number().int().optional()
186
+ .describe("CAS: the revision you last read. Omit to declare unconditionally."),
187
+ },
188
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
189
+ }, async ({ avatarId, clips, expectedRevision }) => {
190
+ const update = await rta.setClipLibrary(avatarId, { clips, expectedRevision });
191
+ const bucket = (label, ids) => `${label} (${ids.length})${ids.length ? ` ${ids.join(", ")}` : ""}`;
192
+ return text(`revision ${update.revision}\n` +
193
+ [
194
+ bucket("kept — still serving", update.plan.kept),
195
+ bucket("queued — rendering now", update.plan.queued),
196
+ bucket("retired — no longer live", update.plan.retired),
197
+ ].join("\n") +
198
+ (update.plan.queued.length
199
+ ? "\n\nQueued clips render in the background — poll list_clips until no row is queued or generating."
200
+ : ""));
201
+ });
156
202
  server.registerTool("sync_clips", {
157
203
  title: "Sync an avatar's clips",
158
- description: "Prepare an avatar's clip set. Clips are prepared once and cached by URL hash, and " +
204
+ description: "DEPRECATED — this serves the sunsetting external-URL clip tier; declare the " +
205
+ "library with set_clip_library instead. Clips are prepared once and cached by URL " +
206
+ "hash, and " +
159
207
  "the serve path only LOADS that cache — so a clip you added but never synced does " +
160
208
  "nothing at all on the next call, silently. Idempotent: call it after every clip " +
161
209
  "change. Pass the complete set you want live; anything omitted is retired. This does " +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "realtime-avatar-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server for Realtime Avatar — let a coding agent read your avatars, balance and bill directly.",
5
5
  "license": "MIT",
6
6
  "author": "The Influence Company",
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@modelcontextprotocol/sdk": "^1.30.0",
58
- "realtime-avatar": "0.4.0",
58
+ "realtime-avatar": "0.5.0",
59
59
  "zod": "^4.4.3"
60
60
  }
61
61
  }