realtime-avatar-mcp 0.9.0 → 0.10.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 +6 -8
- package/dist/server.d.ts +1 -1
- package/dist/server.js +18 -51
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -41,7 +41,6 @@ With `REALTIME_AVATAR_ALLOW_WRITES=1`:
|
|
|
41
41
|
| --- | --- | --- |
|
|
42
42
|
| `set_loop` | Re-direct her resting loop from a new description | no |
|
|
43
43
|
| `set_clip_library` | Declare an avatar's full clip set as JSON — the platform renders it | no |
|
|
44
|
-
| `sync_clips` | **Deprecated.** The external-URL tier. Use `set_clip_library` | no |
|
|
45
44
|
| `upload_asset` | Upload a file **from this machine's disk**, get a public URL | no |
|
|
46
45
|
| `create_remote_asset` | Register a file already on the internet — no local copy | no |
|
|
47
46
|
| `create_avatar_from_image` | Build an avatar from ONE still — the loop is generated | no |
|
|
@@ -54,7 +53,7 @@ The five tools above are **read-only**, and each carries `readOnlyHint` so a hos
|
|
|
54
53
|
the annotation rather than on a name it has to recognise. Pointed at a production key, this
|
|
55
54
|
server is no more dangerous than a dashboard you left open.
|
|
56
55
|
|
|
57
|
-
`REALTIME_AVATAR_ALLOW_WRITES=1`
|
|
56
|
+
`REALTIME_AVATAR_ALLOW_WRITES=1` enables the write tools. Only `start_call` costs credits,
|
|
58
57
|
and it **refuses a `tic_live_` key outright** — an operator who armed writes against a test
|
|
59
58
|
key and later swapped in a production one should not discover it by being billed. Two
|
|
60
59
|
independent gates, because one is a single mistake away from being none.
|
|
@@ -64,12 +63,11 @@ Two things to understand before arming writes:
|
|
|
64
63
|
- **`upload_asset` reads local disk.** It opens whatever absolute path the agent names, on the
|
|
65
64
|
machine running the server. Relative paths are refused rather than resolved against
|
|
66
65
|
whatever directory the host spawned it in.
|
|
67
|
-
- **The clip
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`
|
|
72
|
-
that takes URLs on your own storage.
|
|
66
|
+
- **The clip declaration retires by omission.** `set_clip_library` takes the COMPLETE set;
|
|
67
|
+
anything missing from it stops being served. Its plan reports kept/queued/retired clips.
|
|
68
|
+
Declare `source.motionPrompt` or `source.assetId`; the platform hosts and prepares media.
|
|
69
|
+
The avatar's stored source is the implicit rest state — never declared, so the id
|
|
70
|
+
`primary` is reserved.
|
|
73
71
|
|
|
74
72
|
## Why an MCP server at all
|
|
75
73
|
|
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.
|
|
8
|
+
export declare const MCP_VERSION = "0.10.0";
|
|
9
9
|
/**
|
|
10
10
|
* The Realtime Avatar MCP server.
|
|
11
11
|
*
|
package/dist/server.js
CHANGED
|
@@ -3,14 +3,15 @@ import { stat } from "node:fs/promises";
|
|
|
3
3
|
import { basename, extname, isAbsolute, resolve } from "node:path";
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import { RealtimeAvatar, isQueued } from "realtime-avatar";
|
|
6
|
+
import { RealtimeAvatar, isQueued, clipLibraryDeclarationSchema, } from "realtime-avatar";
|
|
7
7
|
/**
|
|
8
8
|
* Mirrors this package's `version`. It reaches the wire twice — as the MCP server's own
|
|
9
9
|
* identity, and inside the `User-Agent` — so a stale value misattributes real traffic.
|
|
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.
|
|
13
|
+
export const MCP_VERSION = "0.10.0";
|
|
14
|
+
const clipLibraryInputSchema = clipLibraryDeclarationSchema.safeExtend({ avatarId: z.string() });
|
|
14
15
|
const MICROS_PER_CREDIT = 1_000_000;
|
|
15
16
|
/** Extension -> asset kind, so a caller does not have to restate what the filename says. */
|
|
16
17
|
const KIND_BY_EXT = {
|
|
@@ -120,7 +121,7 @@ export function createServer(options) {
|
|
|
120
121
|
title: "List an avatar's clip library",
|
|
121
122
|
description: "The declared clip library: every non-retired clip with its render status, plus " +
|
|
122
123
|
"the library revision (pass it to set_clip_library as expectedRevision), the pose " +
|
|
123
|
-
"anchor and eligibility. status is the render JOB, not serveability — a clip " +
|
|
124
|
+
"anchor, default source, idle and event behavior, and eligibility. status is the render JOB, not serveability — a clip " +
|
|
124
125
|
"re-rendering keeps serving its previous take.",
|
|
125
126
|
inputSchema: { avatarId: z.string() },
|
|
126
127
|
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
@@ -175,26 +176,19 @@ export function createServer(options) {
|
|
|
175
176
|
"unchanged clips are kept, new or changed ones are queued to render, omitted ones " +
|
|
176
177
|
"are retired. The 202 is acceptance, not readiness — poll list_clips until no row " +
|
|
177
178
|
"is queued or generating. Pass expectedRevision from list_clips so a concurrent " +
|
|
178
|
-
"writer surfaces as a 409 instead of a lost update.
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
]).describe("motionPrompt renders motion; assetId uploads a clip that must start AND end on the rest pose"),
|
|
189
|
-
durationSeconds: z.number().optional(),
|
|
190
|
-
reroll: z.boolean().optional().describe("Set true to force a re-render of the same prompt"),
|
|
191
|
-
})).max(20).describe("The COMPLETE library (≤20 clips, ≤8 idle, ≤2 listen). Omitted clips are retired."),
|
|
192
|
-
expectedRevision: z.number().int().optional()
|
|
193
|
-
.describe("CAS: the revision you last read. Omit to declare unconditionally."),
|
|
194
|
-
},
|
|
179
|
+
"writer surfaces as a 409 instead of a lost update. clips maps stable ids to sources; " +
|
|
180
|
+
"'primary' is reserved — the avatar's stored source is the implicit rest state, " +
|
|
181
|
+
"weight 1, and the one clip that may repeat. idle.clips are variations on resting, " +
|
|
182
|
+
"drawn uniformly and never twice running; idle.weight says how often a variation " +
|
|
183
|
+
"plays instead of resting. on.userSpeechStarted plays one listening reaction per " +
|
|
184
|
+
"speech episode; actions are requestable by description, never automatic. Clip " +
|
|
185
|
+
"references are checked before sending. New or changed sources can incur generation " +
|
|
186
|
+
"or pose-validation charges; behavior-only edits reuse media. Do not submit " +
|
|
187
|
+
"speculative declarations.",
|
|
188
|
+
inputSchema: clipLibraryInputSchema,
|
|
195
189
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
196
|
-
}, async ({ avatarId,
|
|
197
|
-
const update = await rta.setClipLibrary(avatarId,
|
|
190
|
+
}, async ({ avatarId, ...declaration }) => {
|
|
191
|
+
const update = await rta.setClipLibrary(avatarId, declaration);
|
|
198
192
|
const bucket = (label, ids) => `${label} (${ids.length})${ids.length ? ` ${ids.join(", ")}` : ""}`;
|
|
199
193
|
return text(`revision ${update.revision}\n` +
|
|
200
194
|
[
|
|
@@ -209,8 +203,8 @@ export function createServer(options) {
|
|
|
209
203
|
server.registerTool("set_loop", {
|
|
210
204
|
title: "Re-direct the resting loop",
|
|
211
205
|
description: "Re-generate the RESTING LOOP — the video she plays when nothing else is happening — " +
|
|
212
|
-
"from a new one-sentence description.
|
|
213
|
-
"
|
|
206
|
+
"from a new one-sentence description. This replaces the avatar's stored source, " +
|
|
207
|
+
"which is the implicit rest state her declared idle variations are weighted against. " +
|
|
214
208
|
"Accepted immediately (202) and rendered over minutes; she stays ready and keeps " +
|
|
215
209
|
"serving her previous loop the whole time, and the clip library is untouched. Bills " +
|
|
216
210
|
"one video generation per call, so do not send it speculatively.",
|
|
@@ -228,33 +222,6 @@ export function createServer(options) {
|
|
|
228
222
|
`She is still playing the previous loop until the new one is ready:\n ${result.servingUrl ?? "—"}\n\n` +
|
|
229
223
|
"Poll get_avatar; the swap publishes in one step. Her clips are unaffected.");
|
|
230
224
|
});
|
|
231
|
-
server.registerTool("sync_clips", {
|
|
232
|
-
title: "Sync an avatar's clips",
|
|
233
|
-
description: "DEPRECATED — this serves the sunsetting external-URL clip tier; declare the " +
|
|
234
|
-
"library with set_clip_library instead. Clips are prepared once and cached by URL " +
|
|
235
|
-
"hash, and " +
|
|
236
|
-
"the serve path only LOADS that cache — so a clip you added but never synced does " +
|
|
237
|
-
"nothing at all on the next call, silently. Idempotent: call it after every clip " +
|
|
238
|
-
"change. Pass the complete set you want live; anything omitted is retired. This does " +
|
|
239
|
-
"not spend credits.",
|
|
240
|
-
inputSchema: {
|
|
241
|
-
avatarId: z.string(),
|
|
242
|
-
clipUrls: z.array(z.string().url()).max(64)
|
|
243
|
-
.describe("The COMPLETE set that should be live. Omitted clips are retired."),
|
|
244
|
-
},
|
|
245
|
-
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true },
|
|
246
|
-
}, async ({ avatarId, clipUrls }) => {
|
|
247
|
-
const result = await rta.syncClips(avatarId, clipUrls);
|
|
248
|
-
const line = (label, urls) => urls.length ? `${label} (${urls.length})\n ${urls.join("\n ")}` : `${label} (0)`;
|
|
249
|
-
return text([
|
|
250
|
-
line("queued — preparing now", result.queued),
|
|
251
|
-
line("ready — already cached", result.ready),
|
|
252
|
-
line("retired — no longer live", result.retired),
|
|
253
|
-
].join("\n") +
|
|
254
|
-
(result.queued.length
|
|
255
|
-
? "\n\nQueued clips are not usable until they finish preparing."
|
|
256
|
-
: ""));
|
|
257
|
-
});
|
|
258
225
|
server.registerTool("upload_asset", {
|
|
259
226
|
title: "Upload a local file",
|
|
260
227
|
description: "Upload a file FROM THIS MACHINE'S DISK and get back a public URL usable as a clip " +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "realtime-avatar-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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.
|
|
58
|
+
"realtime-avatar": "0.10.0",
|
|
59
59
|
"zod": "^4.4.3"
|
|
60
60
|
}
|
|
61
61
|
}
|