@vibeframe/mcp-server 0.109.1 → 0.110.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.
Files changed (3) hide show
  1. package/README.md +15 -0
  2. package/dist/index.js +134 -16
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -125,6 +125,21 @@ Claude Code drives `vibe` natively via shell + the scaffolded `AGENTS.md` / `CLA
125
125
  claude mcp add vibeframe --scope project -- npx -y @vibeframe/mcp-server
126
126
  ```
127
127
 
128
+ ## Human-in-the-Loop Choices
129
+
130
+ The server prefers asking over silently picking defaults:
131
+
132
+ - On hosts that support **MCP elicitation** (Claude Code 2.1.76+), the `build`
133
+ tool opens a native form before the build starts for any choice the call
134
+ left unspecified: narration provider (Kokoro free/local vs ElevenLabs),
135
+ backdrop image generation (skip vs paid OpenAI), and a max cost cap.
136
+ Declining cancels the build before any provider spend. Set
137
+ `VIBE_MCP_ELICIT=off` in the server env to disable the form (headless and
138
+ automation setups).
139
+ - On hosts without elicitation (Claude Desktop today), the server
140
+ instructions tell the agent to present the same choices in chat and wait
141
+ for your answer.
142
+
128
143
  ## What You Can Do
129
144
 
130
145
  Once connected, your MCP host can resolve prompts like these into typed tool calls:
package/dist/index.js CHANGED
@@ -469537,6 +469537,7 @@ function isComposerProvider(value) {
469537
469537
  return value === "claude" || value === "openai" || value === "gemini";
469538
469538
  }
469539
469539
  function resolveComposer(explicit) {
469540
+ loadEnv();
469540
469541
  if (explicit) {
469541
469542
  const key2 = readKey(explicit);
469542
469543
  if (!key2) {
@@ -469561,6 +469562,7 @@ var ENV_VAR, PROVIDER_LABEL, ComposerResolveError;
469561
469562
  var init_composer_resolve = __esm({
469562
469563
  "../cli/src/commands/_shared/composer-resolve.ts"() {
469563
469564
  "use strict";
469565
+ init_api_key();
469564
469566
  ENV_VAR = {
469565
469567
  claude: "ANTHROPIC_API_KEY",
469566
469568
  openai: "OPENAI_API_KEY",
@@ -486484,6 +486486,76 @@ function pickOutputPath(data) {
486484
486486
  return typeof candidate === "string" ? candidate : void 0;
486485
486487
  }
486486
486488
 
486489
+ // ../cli/src/tools/_shared/elicit.ts
486490
+ var NARRATION_CHOICES = ["kokoro", "elevenlabs"];
486491
+ var BACKDROP_CHOICES = ["skip", "openai"];
486492
+ function planBuildElicitation(args) {
486493
+ const assetsWillRun = args.stage === void 0 || args.stage === "all" || args.stage === "assets";
486494
+ if (!assetsWillRun) return null;
486495
+ const properties = {};
486496
+ const required = [];
486497
+ const narrationUndecided = !args.skipNarration && (args.ttsProvider === void 0 || args.ttsProvider === "auto");
486498
+ if (narrationUndecided) {
486499
+ properties.narration = {
486500
+ type: "string",
486501
+ title: "Narration voice-over",
486502
+ description: "Which TTS engine narrates the video?",
486503
+ enum: [...NARRATION_CHOICES],
486504
+ enumNames: [
486505
+ "Kokoro \u2014 free, runs locally on this machine",
486506
+ "ElevenLabs \u2014 cloud TTS, uses your ElevenLabs credits"
486507
+ ]
486508
+ };
486509
+ required.push("narration");
486510
+ }
486511
+ if (args.skipBackdrop === void 0 && args.imageProvider === void 0) {
486512
+ properties.backdrop_images = {
486513
+ type: "string",
486514
+ title: "Backdrop images",
486515
+ description: "Generate AI background images for each scene?",
486516
+ enum: [...BACKDROP_CHOICES],
486517
+ enumNames: [
486518
+ "Skip \u2014 free, pure typographic scenes",
486519
+ "OpenAI \u2014 paid image generation (roughly $1-3 per video)"
486520
+ ]
486521
+ };
486522
+ required.push("backdrop_images");
486523
+ }
486524
+ if (args.maxCostUsd === void 0) {
486525
+ properties.max_cost_usd = {
486526
+ type: "number",
486527
+ title: "Max provider spend (USD)",
486528
+ description: "Abort before any paid provider call when the estimate exceeds this cap. Leave empty for no cap.",
486529
+ minimum: 0
486530
+ };
486531
+ }
486532
+ if (Object.keys(properties).length === 0) return null;
486533
+ return {
486534
+ message: "VibeFrame is about to build this video. Choose how the assets are generated:",
486535
+ requestedSchema: { type: "object", properties, required }
486536
+ };
486537
+ }
486538
+ function applyElicitationAnswers(args, content) {
486539
+ if (!content) return args;
486540
+ const next = { ...args };
486541
+ const narration = content.narration;
486542
+ if (typeof narration === "string" && NARRATION_CHOICES.includes(narration)) {
486543
+ next.ttsProvider = narration;
486544
+ }
486545
+ const backdrop = content.backdrop_images;
486546
+ if (backdrop === "skip") {
486547
+ next.skipBackdrop = true;
486548
+ } else if (backdrop === "openai") {
486549
+ next.skipBackdrop = false;
486550
+ next.imageProvider = "openai";
486551
+ }
486552
+ const cap = content.max_cost_usd;
486553
+ if (typeof cap === "number" && Number.isFinite(cap) && cap >= 0) {
486554
+ next.maxCostUsd = cap;
486555
+ }
486556
+ return next;
486557
+ }
486558
+
486487
486559
  // ../cli/src/commands/_shared/visual-styles.ts
486488
486560
  var STYLES = [
486489
486561
  {
@@ -488722,9 +488794,32 @@ var sceneBuildTool = defineTool({
488722
488794
  name: "build",
488723
488795
  category: "scene",
488724
488796
  cost: "high",
488725
- description: "v0.60 one-shot orchestrator: read STORYBOARD.md per-beat YAML cues (narration / backdrop / duration), dispatch TTS + image generation per beat, compose scene HTML via the compose-scenes-with-skills pipeline, then render to MP4. Use this instead of chaining init + scene_add + render manually. Caches by SHA256 of (DESIGN.md + cue body) so re-runs are idempotent and cheap. Long runs: over MCP, calls exceeding ~45s return immediately with { promoted: true, jobId, status: 'running' } \u2014 poll status_job every 15-30s until completed/failed instead of re-invoking build. Emits MCP progress notifications when the client supplies a progressToken.",
488797
+ description: "v0.60 one-shot orchestrator: read STORYBOARD.md per-beat YAML cues (narration / backdrop / duration), dispatch TTS + image generation per beat, compose scene HTML via the compose-scenes-with-skills pipeline, then render to MP4. Use this instead of chaining init + scene_add + render manually. Caches by SHA256 of (DESIGN.md + cue body) so re-runs are idempotent and cheap. Long runs: over MCP, calls exceeding ~45s return immediately with { promoted: true, jobId, status: 'running' } \u2014 poll status_job every 15-30s until completed/failed instead of re-invoking build. Emits MCP progress notifications when the client supplies a progressToken. When the MCP host supports elicitation, unspecified asset choices (narration provider, backdrop images, cost cap) are confirmed with the user via a form before the build starts.",
488726
488798
  schema: sceneBuildSchema,
488727
488799
  async execute(args, ctx) {
488800
+ let warnings;
488801
+ if (ctx.surface === "mcp" && ctx.elicit) {
488802
+ const form = planBuildElicitation(args);
488803
+ if (form) {
488804
+ const outcome = await ctx.elicit(form).catch(() => null);
488805
+ if (outcome === null) {
488806
+ warnings = ["Asset-choice elicitation failed or timed out; proceeding with defaults."];
488807
+ } else if (outcome.action === "accept") {
488808
+ args = applyElicitationAnswers(args, outcome.content);
488809
+ } else {
488810
+ return {
488811
+ success: false,
488812
+ error: "Build cancelled \u2014 the user declined the asset choices.",
488813
+ data: {
488814
+ cancelled: true,
488815
+ retryWith: [
488816
+ `build { projectDir: "${args.projectDir ?? "."}", ttsProvider: "kokoro", skipBackdrop: true }`
488817
+ ]
488818
+ }
488819
+ };
488820
+ }
488821
+ }
488822
+ }
488728
488823
  const projectDir = args.projectDir ? resolve34(ctx.workingDirectory, args.projectDir) : ctx.workingDirectory;
488729
488824
  const runBuild = (report) => executeSceneBuild({
488730
488825
  projectDir,
@@ -488752,7 +488847,9 @@ var sceneBuildTool = defineTool({
488752
488847
  ctx.onProgress?.({ message: update2.message ?? update2.stage });
488753
488848
  report(update2);
488754
488849
  }
488755
- }).then(mapBuildResultToToolResult);
488850
+ }).then(mapBuildResultToToolResult).then(
488851
+ (result) => warnings === void 0 ? result : { ...result, data: { ...result.data, elicitationWarnings: warnings } }
488852
+ );
488756
488853
  if (ctx.surface !== "mcp") {
488757
488854
  return runBuild(() => void 0);
488758
488855
  }
@@ -497250,7 +497347,8 @@ function buildMcpDispatcher(manifest2) {
497250
497347
  () => tool.execute(parsed.data, {
497251
497348
  workingDirectory: process.cwd(),
497252
497349
  surface: "mcp",
497253
- ...extra?.onProgress ? { onProgress: sink.send } : {}
497350
+ ...extra?.onProgress ? { onProgress: sink.send } : {},
497351
+ ...extra?.elicit ? { elicit: extra.elicit } : {}
497254
497352
  })
497255
497353
  );
497256
497354
  const text = result.success ? JSON.stringify({ success: true, ...result.data }) : result.data ? JSON.stringify({
@@ -497745,10 +497843,22 @@ function buildServerInstructions(cwd = resolveServerWorkspaceRoot()) {
497745
497843
  "For args named dir, projectDir, output, videoPath, or source, prefer workspace-relative paths unless the user explicitly provides an absolute path.",
497746
497844
  "When creating projects with init, use a project name or workspace-relative path under the workspace root.",
497747
497845
  "Do not create projects in /tmp, /home/claude, /workspace, or other synthetic roots unless the user explicitly asks.",
497748
- "Before high or very-high cost provider work, run a dry-run or plan when available and ask the user to confirm provider spend."
497846
+ "Before high or very-high cost provider work, run a dry-run or plan when available and ask the user to confirm provider spend.",
497847
+ "When the user's request leaves them unspecified, ask the user before building instead of silently picking defaults: narration provider (kokoro = free local, elevenlabs = cloud credits), backdrop image generation (paid; skipping is free), and visual style (list options via scene_list_styles). Present the choices and wait for the answer."
497749
497848
  ].join("\n");
497750
497849
  }
497751
497850
 
497851
+ // src/elicit.ts
497852
+ var ELICIT_TIMEOUT_MS = 6e5;
497853
+ function makeElicitFn(server3, env4 = process.env) {
497854
+ if ((env4.VIBE_MCP_ELICIT ?? "").toLowerCase() === "off") return void 0;
497855
+ if (!server3.getClientCapabilities()?.elicitation) return void 0;
497856
+ return (form) => server3.elicitInput(
497857
+ { mode: "form", message: form.message, requestedSchema: form.requestedSchema },
497858
+ { timeout: ELICIT_TIMEOUT_MS }
497859
+ );
497860
+ }
497861
+
497752
497862
  // src/index.ts
497753
497863
  console.log = (...args) => console.error(...args);
497754
497864
  console.info = (...args) => console.error(...args);
@@ -497759,7 +497869,7 @@ var server2 = new Server(
497759
497869
  {
497760
497870
  name: "vibeframe",
497761
497871
  // Stamped with the package version by build.js at bundle time.
497762
- version: "0.109.1"
497872
+ version: "0.110.0"
497763
497873
  },
497764
497874
  {
497765
497875
  capabilities: {
@@ -497776,20 +497886,28 @@ server2.setRequestHandler(ListToolsRequestSchema, async () => {
497776
497886
  server2.setRequestHandler(CallToolRequestSchema, async (request5, extra) => {
497777
497887
  const { name, arguments: args } = request5.params;
497778
497888
  const progressToken = request5.params._meta?.progressToken;
497889
+ const elicit = makeElicitFn(server2);
497779
497890
  return handleToolCall(
497780
497891
  name,
497781
497892
  args || {},
497782
- progressToken === void 0 ? void 0 : {
497783
- onProgress: ({ progress, total, message }) => {
497784
- void extra.sendNotification({
497785
- method: "notifications/progress",
497786
- params: {
497787
- progressToken,
497788
- progress: progress ?? 0,
497789
- ...total !== void 0 ? { total } : {},
497790
- ...message ? { message } : {}
497791
- }
497792
- }).catch(() => void 0);
497893
+ progressToken === void 0 && elicit === void 0 ? void 0 : {
497894
+ ...elicit ? { elicit } : {},
497895
+ ...progressToken === void 0 ? {} : {
497896
+ onProgress: ({
497897
+ progress,
497898
+ total,
497899
+ message
497900
+ }) => {
497901
+ void extra.sendNotification({
497902
+ method: "notifications/progress",
497903
+ params: {
497904
+ progressToken,
497905
+ progress: progress ?? 0,
497906
+ ...total !== void 0 ? { total } : {},
497907
+ ...message ? { message } : {}
497908
+ }
497909
+ }).catch(() => void 0);
497910
+ }
497793
497911
  }
497794
497912
  }
497795
497913
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibeframe/mcp-server",
3
- "version": "0.109.1",
3
+ "version": "0.110.0",
4
4
  "description": "VibeFrame MCP Server - AI-native video editing via Model Context Protocol",
5
5
  "type": "module",
6
6
  "bin": {
@@ -60,8 +60,8 @@
60
60
  "tsx": "^4.21.0",
61
61
  "typescript": "^5.3.3",
62
62
  "vitest": "^1.2.2",
63
- "@vibeframe/cli": "0.109.1",
64
- "@vibeframe/core": "0.109.1"
63
+ "@vibeframe/core": "0.110.0",
64
+ "@vibeframe/cli": "0.110.0"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">=20"