@bifocal/mcp 0.1.6 → 0.1.8

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.
@@ -183,3 +183,15 @@ export async function importPrototypeConfirm(projectId, prototypeId, contextId,
183
183
  }
184
184
  return response.json();
185
185
  }
186
+ export async function screenshotPrototype(prototypeId, path, interactionSteps) {
187
+ const response = await fetch(`${API_URL}/api/prototypes/${prototypeId}/screenshot`, {
188
+ method: 'POST',
189
+ headers: headers(),
190
+ body: JSON.stringify({ path, interaction_steps: interactionSteps }),
191
+ });
192
+ if (!response.ok) {
193
+ const error = await response.json().catch(() => ({}));
194
+ throw new Error(error.error || `Request failed: ${response.status}`);
195
+ }
196
+ return response.json();
197
+ }
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
- import { createProject, updateProject, listProjects, importPrototypeUploadUrl, importPrototypeConfirm, addFeedbackText, generateSolution, getInsights, getQuotes, getSolutions, getSolution, getPrototypes, getPrototype, exportPrototype, generatePrototype, updatePrototype, getContexts, createContext, createSolution, updateSolution } from './bifocalClient.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { createProject, updateProject, listProjects, importPrototypeUploadUrl, importPrototypeConfirm, addFeedbackText, generateSolution, getInsights, getQuotes, getSolutions, getSolution, getPrototypes, getPrototype, exportPrototype, generatePrototype, updatePrototype, getContexts, createContext, createSolution, updateSolution, screenshotPrototype } from './bifocalClient.js';
6
6
  import { writeFile } from 'fs/promises';
7
7
  import { join } from 'path';
8
- const server = new Server({ name: 'bifocal', version: '0.1.0' }, { capabilities: { tools: {}, resources: {} } });
8
+ const server = new Server({ name: 'bifocal', version: '0.1.0' }, { capabilities: { tools: {} } });
9
9
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
10
10
  tools: [
11
11
  {
@@ -316,6 +316,34 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
316
316
  required: ['project_id', 'prototype_id'],
317
317
  },
318
318
  },
319
+ {
320
+ name: 'screenshot_prototype',
321
+ description: 'Capture a screenshot of a specific page or UI state in a prototype and render it inline.\n\nBefore calling, make sure you know what path to capture — get valid paths from the prototype\'s sitemap by calling get_prototypes with the prototype_id if you haven\'t already.\n\n**Choosing whether to pass interaction_steps:**\nPass interaction steps when the screen you want to capture requires user interaction to reach — this includes: a specific step in a multi-page flow that can\'t be reached by URL alone, a UI state triggered by a selection or toggle (e.g. a plan option selected, a modal open, a tab active), or any meaningful variation of a screen that differs from its default loaded state. If navigating directly to the path shows the right screen, no steps are needed.\n\nIf you need interaction steps but aren\'t sure what selectors to use, read the relevant page or component source — export the prototype first via export_prototype if you don\'t have the source locally.\n\n**IMPORTANT**: Only call this when you actually need to see something — after an edit, to evaluate a specific screen, or to compare before and after. Don\'t call it speculatively.',
322
+ inputSchema: {
323
+ type: 'object',
324
+ properties: {
325
+ project_id: { type: 'string', description: 'The ID of the project the prototype belongs to.' },
326
+ prototype_id: { type: 'string', description: 'The ID of the prototype to screenshot.' },
327
+ path: { type: 'string', description: 'The URL path to capture (e.g. "/onboarding/step-13"). Must be a real path from the prototype sitemap.' },
328
+ interaction_steps: {
329
+ type: 'array',
330
+ description: 'Optional steps to execute before screenshotting, to reach a specific UI state.',
331
+ items: {
332
+ type: 'object',
333
+ properties: {
334
+ action: { type: 'string', enum: ['click', 'hover', 'waitForSelector', 'waitForTimeout', 'select', 'type'] },
335
+ selector: { type: 'string', description: 'CSS selector for the target element. Required for all actions except waitForTimeout.' },
336
+ ms: { type: 'number', description: 'Milliseconds to wait. Required for waitForTimeout.' },
337
+ value: { type: 'string', description: 'Value to select. Required for select.' },
338
+ text: { type: 'string', description: 'Text to type. Required for type.' },
339
+ },
340
+ required: ['action'],
341
+ },
342
+ },
343
+ },
344
+ required: ['project_id', 'prototype_id', 'path'],
345
+ },
346
+ },
319
347
  ],
320
348
  }));
321
349
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -583,6 +611,11 @@ Bifocal requires a Vite + React single-page app containing only the experience b
583
611
  await writeFile(filePath, buffer);
584
612
  return { content: [{ type: 'text', text: `Saved to ${filePath}` }] };
585
613
  }
614
+ if (name === 'screenshot_prototype') {
615
+ const { prototype_id, path, interaction_steps } = args;
616
+ const { screenshot, mime_type } = await screenshotPrototype(prototype_id, path, interaction_steps);
617
+ return { content: [{ type: 'image', data: screenshot, mimeType: mime_type }] };
618
+ }
586
619
  return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
587
620
  }
588
621
  catch (error) {
@@ -592,117 +625,6 @@ Bifocal requires a Vite + React single-page app containing only the experience b
592
625
  };
593
626
  }
594
627
  });
595
- const PRIMER = `# Bifocal — What It Is and How to Use It
596
-
597
- ## What is Bifocal?
598
- Bifocal is a product discovery platform. It takes raw user research (interview transcripts, usability session notes, feedback documents) and turns it into structured insights, then uses those insights to generate and iterate on interactive prototypes — all without writing code manually.
599
-
600
- The typical use case: a product team has run user research sessions. They upload the raw feedback into Bifocal, which analyzes it and extracts structured insights (barriers, goals, mention counts). They then use those insights to generate solution hypotheses and working prototypes to test with users.
601
-
602
- ## Core Concepts
603
-
604
- **Project**
605
- The top-level container. Represents a product area or research initiative (e.g. "Onboarding Flow Redesign"). Holds all the feedback, insights, solutions, and prototypes for that initiative.
606
-
607
- **Feedback**
608
- Raw user research uploaded to a project — interview transcripts, session notes, usability findings. Bifocal processes this into structured insights.
609
-
610
- **Insight**
611
- A structured finding extracted from feedback. Each insight has a title, description, category, user goal, specific barriers, affected pages, and a mention count (how many feedback sessions surfaced this issue). Insights are ordered by mention count — higher count = more critical.
612
-
613
- **Solution**
614
- A hypothesis for how to address one or more insights. Contains a set of interventions — each intervention targets a specific barrier and describes exact UI/UX changes to make (which pages to modify, what to change, why). Solutions are generated from insights but can also be created manually.
615
-
616
- **Prototype**
617
- A working, deployed web app (Vite + React) built from a solution. Accessible at a public URL. Prototypes are either generated by Bifocal's coding agent from a solution spec, or built locally and imported. Each prototype forks from a base prototype and only applies the changes described in its solution.
618
-
619
- **Context**
620
- Reusable reference material attached to a project — design tokens, component specs, brand guidelines, page layouts. Contexts are injected into solution and prototype generation to ensure consistency.
621
-
622
- ## Data Model
623
- \`\`\`
624
- Project
625
- └── Feedback (raw research)
626
- └── Insights (structured findings, ordered by mention_count)
627
- └── Contexts (design system, brand guidelines)
628
- └── Solutions (hypotheses built from insights)
629
- └── Prototype (deployed web app)
630
- \`\`\`
631
-
632
- ## Standard Workflow
633
-
634
- 1. **Orient** — call \`list_projects\` to find the project, then \`get_insights\` to understand what the research says. Insights with high mention counts are the most critical to address.
635
-
636
- 2. **Plan** — select 1–3 insights to address. Consider the user's goal and any constraints. Optionally call \`get_contexts\` to understand design constraints.
637
-
638
- 3. **Generate a solution** — call \`generate_solution\` with the selected insight IDs, a goal, and any constraints. This is async — the solution appears in \`get_solutions\` within 1–2 minutes. Always call \`get_solution\` after to read the full intervention detail.
639
-
640
- 4. **Build a prototype** — call \`generate_prototype\` with the solution ID. Bifocal's coding agent builds a working app. This is async — poll \`get_prototype\` until status is \`ready\` (typically 3–5 minutes). The \`published_url\` is the live prototype.
641
-
642
- 5. **Iterate** — use \`update_prototype\` to refine a prototype with specific edit instructions, or generate a new solution from different insights. Use \`add_feedback\` to log what you learn from testing.
643
-
644
- ## Tool Reference by Category
645
-
646
- **Finding your project**
647
- - \`list_projects\` — start here, always
648
-
649
- **Understanding the research**
650
- - \`get_insights\` — structured findings ordered by mention count; read these before generating anything
651
- - \`get_quotes\` — raw supporting quotes for a specific insight; use when you need more evidence
652
-
653
- **Design system / constraints**
654
- - \`get_contexts\` — read design tokens, component specs, brand guidelines for the project
655
- - \`create_context\` — add new context (design system, brand guidelines, etc.)
656
-
657
- **Solutions**
658
- - \`generate_solution\` — async; requires insight IDs, optional goal + constraints; always elicit these from the user first
659
- - \`get_solutions\` — list all solutions; use to check if generation is complete
660
- - \`get_solution\` — full detail including interventions and page changes; always call after generation
661
- - \`create_solution\` — manually specify a solution without using the generator
662
- - \`update_solution\` — modify an existing solution
663
-
664
- **Prototypes**
665
- - \`generate_prototype\` — async; builds from a solution; use \`coding_agent: "bifocal"\` (default) or \`"client"\` (to build locally)
666
- - \`get_prototype\` — full detail including sitemap; use to poll for ready status
667
- - \`get_prototypes\` — list all prototypes for a project
668
- - \`update_prototype\` — async edit instruction to an existing prototype; use for targeted refinements
669
- - \`export_prototype\` — download source as ZIP for local editing
670
- - \`import_prototype\` — upload a locally built or edited ZIP; always call \`get_import_instructions\` first
671
-
672
- **Feedback**
673
- - \`add_feedback\` — add text or PDF research to a project, linked to a prototype
674
-
675
- ## Key Behaviors to Know
676
-
677
- - **Async operations**: \`generate_solution\`, \`generate_prototype\`, and \`update_prototype\` are all async. After calling them, poll the corresponding getter (\`get_solutions\`/\`get_prototype\`) until the result appears. Do not assume they complete immediately.
678
-
679
- - **Base prototype**: Every project has a base prototype that all solution prototypes fork from. The base prototype ID is resolved automatically by \`generate_prototype\` — you do not need to specify it.
680
-
681
- - **Client vs Bifocal coding agent**: \`generate_prototype\` and \`update_prototype\` both support \`coding_agent: "client"\`. Use this when you want to build or edit the prototype locally (exports the spec + base code, you implement and import back). Use \`"bifocal"\` (default) when you want Bifocal to build it automatically.
682
-
683
- - **Solution elicitation**: Before calling \`generate_solution\`, always show the user the available insights and ask which to prioritize, what the goal is, and whether there are constraints. The tool description spells this out explicitly.
684
-
685
- - **Prototype forking**: Prototypes are built from a base, so each one only contains the changes from its solution — not the entire app. When comparing prototypes, they share the same baseline.
686
- `;
687
- server.setRequestHandler(ListResourcesRequestSchema, async () => ({
688
- resources: [
689
- {
690
- uri: 'bifocal://primer',
691
- name: 'Bifocal Primer',
692
- description: 'Start here — overview of what Bifocal is, core concepts, data model, standard workflow, and tool reference. Read this before using any tools.',
693
- mimeType: 'text/markdown',
694
- },
695
- ],
696
- }));
697
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
698
- const { uri } = request.params;
699
- if (uri === 'bifocal://primer') {
700
- return {
701
- contents: [{ uri, mimeType: 'text/markdown', text: PRIMER }],
702
- };
703
- }
704
- throw new Error(`Unknown resource: ${uri}`);
705
- });
706
628
  async function main() {
707
629
  const transport = new StdioServerTransport();
708
630
  await server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bifocal/mcp",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Bifocal MCP server — access projects, insights, and prototypes from Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",