@assistant-ui/mcp-docs-server 0.1.26 → 0.1.28

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.
Files changed (43) hide show
  1. package/.docs/organized/code-examples/waterfall.md +2 -2
  2. package/.docs/organized/code-examples/with-a2a.md +2 -2
  3. package/.docs/organized/code-examples/with-ag-ui.md +3 -3
  4. package/.docs/organized/code-examples/with-ai-sdk-v6.md +4 -4
  5. package/.docs/organized/code-examples/with-artifacts.md +4 -4
  6. package/.docs/organized/code-examples/with-assistant-transport.md +2 -2
  7. package/.docs/organized/code-examples/with-chain-of-thought.md +4 -4
  8. package/.docs/organized/code-examples/with-cloud-standalone.md +4 -4
  9. package/.docs/organized/code-examples/with-cloud.md +4 -4
  10. package/.docs/organized/code-examples/with-custom-thread-list.md +4 -4
  11. package/.docs/organized/code-examples/with-elevenlabs-conversational.md +511 -0
  12. package/.docs/organized/code-examples/with-elevenlabs-scribe.md +6 -6
  13. package/.docs/organized/code-examples/with-expo.md +17 -17
  14. package/.docs/organized/code-examples/with-external-store.md +2 -2
  15. package/.docs/organized/code-examples/with-ffmpeg.md +217 -63
  16. package/.docs/organized/code-examples/with-generative-ui.md +841 -0
  17. package/.docs/organized/code-examples/with-google-adk.md +3 -3
  18. package/.docs/organized/code-examples/with-heat-graph.md +2 -2
  19. package/.docs/organized/code-examples/with-interactables.md +67 -9
  20. package/.docs/organized/code-examples/with-langgraph.md +3 -3
  21. package/.docs/organized/code-examples/with-livekit.md +591 -0
  22. package/.docs/organized/code-examples/with-parent-id-grouping.md +3 -3
  23. package/.docs/organized/code-examples/with-react-hook-form.md +5 -5
  24. package/.docs/organized/code-examples/with-react-ink.md +1 -1
  25. package/.docs/organized/code-examples/with-react-router.md +7 -7
  26. package/.docs/organized/code-examples/with-store.md +8 -3
  27. package/.docs/organized/code-examples/with-tanstack.md +4 -4
  28. package/.docs/organized/code-examples/with-tap-runtime.md +2 -2
  29. package/.docs/raw/docs/(docs)/copilots/model-context.mdx +9 -1
  30. package/.docs/raw/docs/(docs)/guides/interactables.mdx +99 -37
  31. package/.docs/raw/docs/(docs)/guides/mentions.mdx +406 -0
  32. package/.docs/raw/docs/(docs)/guides/slash-commands.mdx +275 -0
  33. package/.docs/raw/docs/(docs)/guides/tool-ui.mdx +29 -0
  34. package/.docs/raw/docs/(docs)/guides/voice.mdx +333 -0
  35. package/.docs/raw/docs/(reference)/api-reference/primitives/message-part.mdx +23 -0
  36. package/.docs/raw/docs/primitives/composer.mdx +27 -4
  37. package/.docs/raw/docs/runtimes/a2a/index.mdx +4 -0
  38. package/.docs/raw/docs/runtimes/ai-sdk/v6.mdx +2 -2
  39. package/.docs/raw/docs/runtimes/assistant-transport.mdx +6 -2
  40. package/.docs/raw/docs/ui/context-display.mdx +2 -2
  41. package/.docs/raw/docs/ui/model-selector.mdx +1 -1
  42. package/.docs/raw/docs/ui/voice.mdx +172 -0
  43. package/package.json +5 -6
@@ -276,9 +276,11 @@ import {
276
276
  import { z } from "zod";
277
277
  import { FFmpeg } from "@ffmpeg/ffmpeg";
278
278
  import { toBlobURL } from "@ffmpeg/util";
279
- import { FC, useEffect, useRef, useState } from "react";
279
+ import { FC, useCallback, useEffect, useRef, useState } from "react";
280
280
  import {
281
281
  CircleCheckIcon,
282
+ DownloadIcon,
283
+ FileIcon,
282
284
  RefreshCcwIcon,
283
285
  TriangleAlertIcon,
284
286
  } from "lucide-react";
@@ -315,7 +317,9 @@ const FfmpegTool: FC<{ file: File }> = ({ file }) => {
315
317
  load();
316
318
  }, []);
317
319
 
318
- useAssistantInstructions(`The user has attached a file: ${file.name}`);
320
+ useAssistantInstructions(
321
+ `The user has attached a file: ${file.name}. To add text overlays, use the render_overlay tool to render HTML to a PNG image, then use run_ffmpeg with the "overlay" filter to composite it onto the video. Do NOT use the drawtext filter.`,
322
+ );
319
323
 
320
324
  useAssistantTool({
321
325
  toolName: "run_ffmpeg",
@@ -324,14 +328,6 @@ const FfmpegTool: FC<{ file: File }> = ({ file }) => {
324
328
  .string()
325
329
  .array()
326
330
  .describe("The ffmpeg command line arguments to provide"),
327
- outputFileName: z
328
- .string()
329
- .describe(
330
- "The name of the output file including extension, corresponding to the command provided",
331
- ),
332
- outputMimeType: z
333
- .string()
334
- .describe("The mime type of the output file, e.g. image/png"),
335
331
  }),
336
332
  execute: async ({ command }) => {
337
333
  const transcode = async () => {
@@ -359,57 +355,227 @@ const FfmpegTool: FC<{ file: File }> = ({ file }) => {
359
355
  success: code === 0,
360
356
  hint:
361
357
  code === 0
362
- ? "note: a download button is appearing in the chat for the user"
358
+ ? "Success. Now call display_file to show the output to the user."
363
359
  : `some error happened, logs: ${logs.join("\n")}`,
364
360
  };
365
361
  },
366
362
  render: function RenderFfmpeg({
367
- args: { command, outputFileName, outputMimeType },
363
+ args: { command },
368
364
  result: { success } = {},
369
365
  }) {
370
- const handleDownload = async () => {
366
+ return (
367
+ <div className="mb-2 flex flex-col gap-2 rounded-lg border px-5 py-4">
368
+ <div className="flex items-center gap-2">
369
+ {success == null && (
370
+ <RefreshCcwIcon className="size-4 animate-spin text-blue-600" />
371
+ )}
372
+ {success === false && (
373
+ <TriangleAlertIcon className="size-4 text-red-600" />
374
+ )}
375
+ {success === true && (
376
+ <CircleCheckIcon className="size-4 text-green-600" />
377
+ )}
378
+ <p>Running ffmpeg</p>
379
+ </div>
380
+ <pre className="overflow-y-scroll font-sm">
381
+ ffmpeg {command?.join(" ")}
382
+ </pre>
383
+ {success === false && (
384
+ <div className="mt-2 border-t border-dashed pt-3">
385
+ Encountered an error.
386
+ </div>
387
+ )}
388
+ </div>
389
+ );
390
+ },
391
+ });
392
+
393
+ useAssistantTool({
394
+ toolName: "render_overlay",
395
+ parameters: z.object({
396
+ html: z
397
+ .string()
398
+ .describe(
399
+ "HTML content to render. Use inline styles for all styling. The background is transparent by default.",
400
+ ),
401
+ width: z.number().describe("Width of the output image in pixels"),
402
+ height: z.number().describe("Height of the output image in pixels"),
403
+ fileName: z
404
+ .string()
405
+ .describe("Output filename in ffmpeg filesystem, e.g. overlay.png"),
406
+ }),
407
+ execute: async ({ html, width, height, fileName }) => {
408
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
409
+ <foreignObject width="100%" height="100%">
410
+ <div xmlns="http://www.w3.org/1999/xhtml">${html}</div>
411
+ </foreignObject>
412
+ </svg>`;
413
+
414
+ const img = new Image();
415
+ img.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
416
+
417
+ await new Promise<void>((resolve, reject) => {
418
+ img.onload = () => resolve();
419
+ img.onerror = () => reject(new Error("Failed to render HTML"));
420
+ });
421
+
422
+ const canvas = document.createElement("canvas");
423
+ canvas.width = width;
424
+ canvas.height = height;
425
+ const ctx = canvas.getContext("2d")!;
426
+ ctx.drawImage(img, 0, 0);
427
+
428
+ const blob = await new Promise<Blob>((resolve) =>
429
+ canvas.toBlob((b) => resolve(b!), "image/png"),
430
+ );
431
+ const data = new Uint8Array(await blob.arrayBuffer());
432
+
433
+ const ffmpeg = ffmpegRef.current;
434
+ await ffmpeg.writeFile(fileName, data);
435
+
436
+ return {
437
+ success: true,
438
+ hint: `Overlay image "${fileName}" (${width}x${height}) written to ffmpeg filesystem. Use it with the overlay filter in run_ffmpeg, e.g. -i ${fileName} -filter_complex "overlay=x=0:y=0"`,
439
+ };
440
+ },
441
+ render: function RenderOverlay({ args, result }) {
442
+ return (
443
+ <div className="mb-2 flex items-center gap-2 rounded-lg border px-5 py-4">
444
+ {!result && (
445
+ <RefreshCcwIcon className="size-4 animate-spin text-blue-600" />
446
+ )}
447
+ {result?.success && (
448
+ <CircleCheckIcon className="size-4 text-green-600" />
449
+ )}
450
+ <p>
451
+ {result?.success
452
+ ? `Rendered overlay → ${args.fileName}`
453
+ : "Rendering overlay..."}
454
+ </p>
455
+ </div>
456
+ );
457
+ },
458
+ });
459
+
460
+ useAssistantTool({
461
+ toolName: "display_file",
462
+ parameters: z.object({
463
+ fileName: z
464
+ .string()
465
+ .describe("The name of the file to display from the ffmpeg filesystem"),
466
+ mimeType: z
467
+ .string()
468
+ .describe("The mime type of the file, e.g. image/png, video/mp4"),
469
+ }),
470
+ execute: async ({ fileName }) => {
471
+ const ffmpeg = ffmpegRef.current;
472
+ try {
473
+ const data = (await ffmpeg.readFile(
474
+ fileName,
475
+ )) as Uint8Array<ArrayBuffer>;
476
+ return {
477
+ success: true,
478
+ size: data.byteLength,
479
+ hint: "A file preview and download button is now visible to the user. Do not describe the file or repeat its contents.",
480
+ };
481
+ } catch {
482
+ return {
483
+ success: false,
484
+ error: `File "${fileName}" not found in ffmpeg filesystem`,
485
+ };
486
+ }
487
+ },
488
+ render: function RenderDisplayFile({
489
+ args: { fileName, mimeType },
490
+ result,
491
+ }) {
492
+ const [blobUrl, setBlobUrl] = useState<string | null>(null);
493
+
494
+ const readFile = useCallback(async () => {
371
495
  const ffmpeg = ffmpegRef.current;
372
496
  const data = (await ffmpeg.readFile(
373
- outputFileName,
497
+ fileName,
374
498
  )) as Uint8Array<ArrayBuffer>;
375
- window.open(
376
- URL.createObjectURL(
377
- new Blob([data.buffer], { type: outputMimeType }),
378
- ),
379
- "_blank",
380
- );
499
+ return URL.createObjectURL(new Blob([data.buffer], { type: mimeType }));
500
+ }, [fileName, mimeType]);
501
+
502
+ useEffect(() => {
503
+ if (!result?.success) return;
504
+ let revoked = false;
505
+ readFile().then((url) => {
506
+ if (revoked) {
507
+ URL.revokeObjectURL(url);
508
+ return;
509
+ }
510
+ setBlobUrl(url);
511
+ });
512
+ return () => {
513
+ revoked = true;
514
+ setBlobUrl((prev) => {
515
+ if (prev) URL.revokeObjectURL(prev);
516
+ return null;
517
+ });
518
+ };
519
+ }, [result?.success, readFile]);
520
+
521
+ const handleDownload = () => {
522
+ if (!blobUrl) return;
523
+ const a = document.createElement("a");
524
+ a.href = blobUrl;
525
+ a.download = fileName;
526
+ a.click();
381
527
  };
382
- return (
383
- <div className="flex flex-col gap-2 rounded-lg border px-5 py-4">
384
- <div>
385
- <div className="flex items-center gap-2">
386
- {success == null && (
387
- <RefreshCcwIcon className="size-4 animate-spin text-blue-600" />
388
- )}
389
- {success === false && (
390
- <TriangleAlertIcon className="size-4 text-red-600" />
391
- )}
392
- {success === true && (
393
- <CircleCheckIcon className="size-4 text-green-600" />
394
- )}
395
- <p>Running ffmpeg</p>
396
- </div>
397
- <pre className="overflow-y-scroll font-sm">
398
- ffmpeg {command?.join(" ")}
399
- </pre>
528
+
529
+ if (!result) {
530
+ return (
531
+ <div className="flex items-center gap-2 rounded-lg border px-5 py-4">
532
+ <RefreshCcwIcon className="size-4 animate-spin text-blue-600" />
533
+ <p>Loading file...</p>
400
534
  </div>
401
- {!!success && (
402
- <div className="mt-2 border-t border-dashed pt-3">
403
- <button onClick={handleDownload}>
404
- Download {outputFileName}
405
- </button>
406
- </div>
535
+ );
536
+ }
537
+
538
+ if (!result.success) {
539
+ return (
540
+ <div className="flex items-center gap-2 rounded-lg border px-5 py-4">
541
+ <TriangleAlertIcon className="size-4 text-red-600" />
542
+ <p>File not found</p>
543
+ </div>
544
+ );
545
+ }
546
+
547
+ const isImage = mimeType?.startsWith("image/");
548
+ const isVideo = mimeType?.startsWith("video/");
549
+ const isAudio = mimeType?.startsWith("audio/");
550
+
551
+ return (
552
+ <div className="mb-2 flex flex-col gap-3 rounded-lg border px-5 py-4">
553
+ {blobUrl && isImage && (
554
+ <img
555
+ src={blobUrl}
556
+ alt={fileName}
557
+ className="max-h-64 w-fit rounded"
558
+ />
407
559
  )}
408
- {success === false && (
409
- <div className="mt-2 border-t border-dashed pt-3">
410
- Encountered an error.
560
+ {blobUrl && isVideo && (
561
+ // biome-ignore lint/a11y/useMediaCaption: generated output
562
+ <video src={blobUrl} controls className="max-h-64 w-fit rounded" />
563
+ )}
564
+ {/* biome-ignore lint/a11y/useMediaCaption: generated output */}
565
+ {blobUrl && isAudio && <audio src={blobUrl} controls />}
566
+ {blobUrl && !isImage && !isVideo && !isAudio && (
567
+ <div className="flex items-center gap-2 text-muted-foreground">
568
+ <FileIcon className="size-5" />
569
+ <span>{fileName}</span>
411
570
  </div>
412
571
  )}
572
+ <button
573
+ onClick={handleDownload}
574
+ className="flex w-fit items-center gap-2 rounded-md border bg-background px-4 py-2 text-sm hover:bg-accent"
575
+ >
576
+ <DownloadIcon className="size-4" />
577
+ Download {fileName}
578
+ </button>
413
579
  </div>
414
580
  );
415
581
  },
@@ -444,18 +610,6 @@ export default function Home() {
444
610
 
445
611
  return (
446
612
  <div className="flex h-full flex-col">
447
- <div className="border-b">
448
- <p className="my-4 ml-8 font-bold text-xl">
449
- ConvertGPT (built with{" "}
450
- <a
451
- href="https://github.com/assistant-ui/assistant-ui"
452
- className="underline"
453
- >
454
- assistant-ui
455
- </a>
456
- )
457
- </p>
458
- </div>
459
613
  <AuiProvider value={aui}>
460
614
  <Thread />
461
615
  </AuiProvider>
@@ -523,18 +677,18 @@ export default nextConfig;
523
677
  "start": "next start"
524
678
  },
525
679
  "dependencies": {
526
- "@ai-sdk/openai": "^3.0.48",
680
+ "@ai-sdk/openai": "^3.0.51",
527
681
  "@assistant-ui/react": "workspace:*",
528
682
  "@assistant-ui/react-ai-sdk": "workspace:*",
529
683
  "@assistant-ui/react-markdown": "workspace:*",
530
684
  "@assistant-ui/ui": "workspace:*",
531
685
  "@ffmpeg/ffmpeg": "^0.12.15",
532
686
  "@ffmpeg/util": "^0.12.2",
533
- "ai": "^6.0.138",
687
+ "ai": "^6.0.148",
534
688
  "class-variance-authority": "^0.7.1",
535
689
  "clsx": "^2.1.1",
536
690
  "lucide-react": "^1.7.0",
537
- "next": "^16.2.1",
691
+ "next": "^16.2.2",
538
692
  "react": "^19.2.4",
539
693
  "react-dom": "^19.2.4",
540
694
  "tailwind-merge": "^3.5.0",
@@ -543,7 +697,7 @@ export default nextConfig;
543
697
  "devDependencies": {
544
698
  "@assistant-ui/x-buildutils": "workspace:*",
545
699
  "@tailwindcss/postcss": "^4.2.2",
546
- "@types/node": "^25.5.0",
700
+ "@types/node": "^25.5.2",
547
701
  "@types/react": "^19.2.14",
548
702
  "@types/react-dom": "^19.2.3",
549
703
  "postcss": "^8.5.8",