circuit-mcp 1.0.17 → 2.0.1

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 +126 -37
  2. package/package.json +1 -1
  3. package/src/server.js +147 -185
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Connect [Circuit](https://withcircuit.com) to Cursor and Claude Code via MCP (Model Context Protocol).
4
4
 
5
- **Circuit** transforms customer feedback into engineering specs. The MCP brings your priorities and briefs directly into your AI coding assistant.
5
+ Circuit turns customer feedback into engineering specs. The MCP puts your priorities and briefs directly in your AI coding assistant — so you build what matters without switching tabs.
6
6
 
7
7
  ## Quick Start
8
8
 
@@ -27,71 +27,160 @@ Add to `~/.cursor/mcp.json`:
27
27
  claude mcp add circuit -- npx circuit-mcp
28
28
  ```
29
29
 
30
- ## First Run
30
+ ### First Run
31
31
 
32
32
  On first use, Circuit opens your browser to authenticate. Your token is cached at `~/.circuit/token.json`.
33
33
 
34
34
  ```
35
- Circuit MCP
35
+ Circuit MCP
36
36
 
37
- First time setup - let's connect your account.
38
- Opening browser to authenticate...
37
+ First time setup let's connect your account.
38
+ Opening browser to authenticate...
39
39
 
40
- Connected!
40
+ Connected!
41
41
  ```
42
42
 
43
- ## Available Tools
43
+ ---
44
44
 
45
- | Tool | Description |
46
- |------|-------------|
47
- | `get_priorities` | Get top customer priorities ranked by volume, urgency, or sentiment |
48
- | `get_brief` | Get the engineering spec for a priority (what to build, why, done criteria) |
49
- | `search_feedback` | Search raw customer feedback by keyword |
50
- | `start_building` | Mark a brief as "building" - you're working on it |
51
- | `mark_done` | Mark a brief as "done" - it shipped! |
45
+ ## Tools
52
46
 
53
- ## Example Usage
47
+ Four tools. Everything you need to go from "what should I build?" to "shipped."
54
48
 
55
- Ask your AI assistant:
49
+ ### `circuit.priorities` What should I work on?
56
50
 
57
- > "What are my top 5 priorities?"
51
+ Returns ranked priorities with trend data, confidence scores, and pattern matching from your shipping history.
58
52
 
59
- > "Get the brief for priority #1"
53
+ | Parameter | Description |
54
+ |-----------|-------------|
55
+ | `lens` | How to rank: `volume`, `urgency`, `revenue`, `retention`, `delight`, `feature` |
56
+ | `segment` | Filter by customer segment: `enterprise`, `smb`, `all` |
57
+ | `limit` | Number of priorities to return (default: 5, max: 20) |
58
+ | `category` | Filter: `Bug`, `Feature`, `Friction`, `Complaint`, `Praise` |
60
59
 
61
- > "Search feedback about login issues"
60
+ Each priority includes a `matches_pattern` flag — Circuit learns what you tend to ship and surfaces priorities that fit your instincts.
62
61
 
63
- > "Mark that brief as done"
62
+ **Try it:** *"What are my top priorities by revenue impact for enterprise customers?"*
63
+
64
+ ### `circuit.brief` — The full engineering spec
65
+
66
+ Returns the complete brief for a priority: **What** to build, **Why** it matters, **Voice of the customer**, **Files** to touch, and **Done** criteria. Includes customer quotes, version history, and context from previous ships.
67
+
68
+ | Parameter | Description |
69
+ |-----------|-------------|
70
+ | `priority_id` or `build_id` | Which priority to get the brief for |
71
+ | `include_history` | Include version history and related ship memory (default: true) |
72
+
73
+ Uses a 3-layer matching strategy to find the right brief, even with partial or ambiguous IDs.
74
+
75
+ **Try it:** *"Get the brief for priority #1"*
76
+
77
+ ### `circuit.act` — Take action
78
+
79
+ One tool, four actions. Move work forward without leaving your editor.
80
+
81
+ | Action | What it does |
82
+ |--------|-------------|
83
+ | `build` | Mark a brief as "building" — you're working on it |
84
+ | `ship` | Mark shipped. Auto-notifies customers who submitted feedback via the widget. Creates a ship memory so Circuit learns your patterns. |
85
+ | `correct` | Fix an AI classification that got it wrong. Writes a correction memory and recomputes pattern matching. |
86
+ | `submit` | Add new feedback directly via MCP |
87
+
88
+ **Try it:** *"Mark the login brief as building"* → build it → *"Ship it"*
89
+
90
+ ### `circuit.ask` — Search across everything
91
+
92
+ Semantic search across feedback, priorities, briefs, and help docs. Also returns your behavioral patterns — ship count, top categories, segment affinity — so your assistant has full context.
93
+
94
+ **Try it:** *"Search for feedback about onboarding friction"*
95
+
96
+ ---
97
+
98
+ ## Example Workflow
99
+
100
+ A typical session looks like this:
101
+
102
+ ```
103
+ You: "What should I work on?"
104
+ Circuit: → circuit.priorities (lens: urgency)
105
+ Returns top 5, #1 is "Login timeout on slow connections"
106
+
107
+ You: "Get me the brief for that"
108
+ Circuit: → circuit.brief (priority_id: ...)
109
+ Returns spec: what to build, affected files, done criteria
110
+
111
+ You: "I'm on it"
112
+ Circuit: → circuit.act (action: build)
113
+ Marked as building
114
+
115
+ ... you write the code ...
116
+
117
+ You: "Done, ship it"
118
+ Circuit: → circuit.act (action: ship)
119
+ Marked as shipped. 12 customers notified.
120
+ ```
121
+
122
+ Feedback in. Spec out. Code shipped. Customers notified. That's the circuit.
123
+
124
+ ---
64
125
 
65
126
  ## Commands
66
127
 
67
128
  ```bash
68
- npx circuit-mcp # Start MCP server (used by Cursor/Claude)
69
- npx circuit-mcp setup # Show setup instructions
70
- npx circuit-mcp auth # Re-authenticate
71
- npx circuit-mcp logout # Clear stored token
129
+ npx circuit-mcp # Start MCP server (used by Cursor/Claude Code)
130
+ npx circuit-mcp setup # Show setup instructions
131
+ npx circuit-mcp auth # Re-authenticate
132
+ npx circuit-mcp logout # Clear stored token
133
+ ```
134
+
135
+ ## Troubleshooting
136
+
137
+ **"Authentication failed" or token expired**
138
+ ```bash
139
+ npx circuit-mcp auth
72
140
  ```
141
+ Tokens last 30 days. Re-auth opens your browser — takes 5 seconds.
142
+
143
+ **Cursor not detecting the MCP server**
144
+ 1. Confirm `~/.cursor/mcp.json` has the config above
145
+ 2. Restart Cursor
146
+ 3. Check the MCP panel shows "circuit" as connected
147
+
148
+ **Claude Code not finding tools**
149
+ ```bash
150
+ claude mcp list # Verify circuit is registered
151
+ claude mcp remove circuit # Remove and re-add if needed
152
+ claude mcp add circuit -- npx circuit-mcp
153
+ ```
154
+
155
+ **"Priority not found" errors**
156
+
157
+ Circuit uses a 3-layer matching strategy, but if you're referencing old IDs, priorities may have been recomputed. Run `circuit.priorities` to get current IDs.
158
+
159
+ ---
73
160
 
74
161
  ## How It Works
75
162
 
76
163
  ```
164
+ Customer feedback
165
+
77
166
  Circuit (app.withcircuit.com)
78
-
79
- │ Feedback → Priorities → Briefs
80
-
81
-
82
- Circuit MCP ◄─── Cursor / Claude Code
83
-
84
- get_priorities, get_brief, etc.
85
-
86
-
87
- Your AI assistant has context
167
+ Feedback → Priorities → Specs
168
+
169
+ Circuit MCP ←── Your AI assistant (Cursor / Claude Code)
170
+
171
+ circuit.priorities → What to build
172
+ circuit.brief → How to build it
173
+ circuit.act → Ship it + notify customers
174
+ circuit.ask → Search everything
88
175
  ```
89
176
 
177
+ Auth uses OAuth 2.0 with PKCE. Tokens are SHA-256 hashed with 30-day expiry. All tools are memory-aware — Circuit learns from your corrections and shipping patterns to get smarter over time.
178
+
90
179
  ## Links
91
180
 
92
- - [Circuit](https://withcircuit.com) - Customer feedback intelligence
93
- - [MCP Protocol](https://modelcontextprotocol.io) - Model Context Protocol spec
181
+ - [Circuit](https://withcircuit.com) Customer feedback intelligence
182
+ - [MCP Protocol](https://modelcontextprotocol.io) Model Context Protocol spec
94
183
 
95
184
  ## License
96
185
 
97
- Proprietary - © Circuit (withcircuit.com)
186
+ Proprietary © Circuit ([withcircuit.com](https://withcircuit.com))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circuit-mcp",
3
- "version": "1.0.17",
3
+ "version": "2.0.1",
4
4
  "description": "Connect Circuit to Cursor and Claude Code - bring customer priorities and engineering briefs into your AI coding assistant",
5
5
  "type": "module",
6
6
  "bin": {
package/src/server.js CHANGED
@@ -74,7 +74,7 @@ async function handleMessage(message, token) {
74
74
  protocolVersion: '2024-11-05',
75
75
  serverInfo: {
76
76
  name: 'circuit-mcp',
77
- version: '1.0.0'
77
+ version: '2.0.0'
78
78
  },
79
79
  capabilities: {
80
80
  tools: {},
@@ -84,7 +84,6 @@ async function handleMessage(message, token) {
84
84
  };
85
85
 
86
86
  case 'initialized':
87
- // No response needed for notification
88
87
  return null;
89
88
 
90
89
  case 'tools/list':
@@ -94,111 +93,114 @@ async function handleMessage(message, token) {
94
93
  result: {
95
94
  tools: [
96
95
  {
97
- name: 'get_priorities',
98
- description: 'Get your top priorities from Circuit, ranked by customer volume. These are the features/fixes that matter most to your users.',
96
+ name: 'circuit.priorities',
97
+ description: 'What should I work on? Get ranked priorities with confidence indicators, trends, and memory context.',
99
98
  inputSchema: {
100
99
  type: 'object',
101
100
  properties: {
101
+ lens: {
102
+ type: 'string',
103
+ description: "Focus lens: 'volume', 'urgency', 'revenue', 'retention', 'delight', 'feature'",
104
+ enum: ['volume', 'urgency', 'revenue', 'retention', 'delight', 'feature'],
105
+ default: 'volume'
106
+ },
107
+ segment: {
108
+ type: 'string',
109
+ description: "Filter by customer segment: 'enterprise', 'smb', 'all'",
110
+ default: 'all'
111
+ },
102
112
  limit: {
103
113
  type: 'number',
104
- description: 'Number of priorities to return (default: 5, max: 20)'
114
+ description: 'Number of priorities (default: 5, max: 20)',
115
+ default: 5
105
116
  },
106
- sort_by: {
117
+ category: {
107
118
  type: 'string',
108
- description: 'Sort criterion: volume, urgency, revenue, negative, positive',
109
- enum: ['volume', 'urgency', 'revenue', 'negative', 'positive']
119
+ description: "Filter by category: 'Bug', 'Feature', 'Friction', 'Complaint', 'Praise'",
120
+ enum: ['Bug', 'Feature', 'Friction', 'Complaint', 'Praise']
110
121
  }
111
122
  }
112
123
  }
113
124
  },
114
125
  {
115
- name: 'get_brief',
116
- description: 'Get the full engineering brief for a specific priority. Includes what to build, why it matters, customer quotes, files to touch, and done criteria.',
126
+ name: 'circuit.brief',
127
+ description: 'Get the full engineering spec for a priority. Includes brief content, customer context, version history, and related memory (previous ships, outcomes).',
117
128
  inputSchema: {
118
129
  type: 'object',
119
130
  properties: {
120
131
  priority_id: {
121
132
  type: 'string',
122
- description: 'The priority ID (from get_priorities)'
133
+ description: 'The priority ID'
123
134
  },
124
135
  build_id: {
125
136
  type: 'string',
126
137
  description: 'The build ID directly (alternative to priority_id)'
138
+ },
139
+ include_history: {
140
+ type: 'boolean',
141
+ description: 'Include version history and related memory',
142
+ default: true
127
143
  }
128
144
  }
129
145
  }
130
146
  },
131
147
  {
132
- name: 'start_building',
133
- description: "Mark a brief as 'building' - you're actively working on this feature.",
148
+ name: 'circuit.act',
149
+ description: 'Take an action in Circuit. Ship a brief, start building, correct a classification, or submit new feedback.',
134
150
  inputSchema: {
135
151
  type: 'object',
136
152
  properties: {
137
- build_id: {
153
+ action: {
138
154
  type: 'string',
139
- description: 'The build ID to mark as building'
140
- }
141
- },
142
- required: ['build_id']
143
- }
144
- },
145
- {
146
- name: 'mark_shipped',
147
- description: "Mark a brief as 'shipped' - the feature has shipped! This closes the feedback loop and notifies customers.",
148
- inputSchema: {
149
- type: 'object',
150
- properties: {
151
- build_id: {
155
+ description: "Action to take: 'build' (start building), 'ship' (mark shipped), 'correct' (fix classification), 'submit' (add feedback)",
156
+ enum: ['build', 'ship', 'correct', 'submit']
157
+ },
158
+ brief_id: {
152
159
  type: 'string',
153
- description: 'The build ID to mark as shipped'
154
- }
155
- },
156
- required: ['build_id']
157
- }
158
- },
159
- {
160
- name: 'mark_done',
161
- description: "[Deprecated - use mark_shipped] Mark a brief as shipped.",
162
- inputSchema: {
163
- type: 'object',
164
- properties: {
165
- build_id: {
160
+ description: "Brief ID (for 'build' and 'ship' actions)"
161
+ },
162
+ priority_id: {
166
163
  type: 'string',
167
- description: 'The build ID to mark as shipped'
168
- }
169
- },
170
- required: ['build_id']
171
- }
172
- },
173
- {
174
- name: 'search_feedback',
175
- description: 'Search customer feedback by keyword. Useful for understanding pain points around specific features.',
176
- inputSchema: {
177
- type: 'object',
178
- properties: {
179
- query: {
164
+ description: "Priority ID (for 'correct' action)"
165
+ },
166
+ correction_type: {
180
167
  type: 'string',
181
- description: 'Search query (keywords or phrase)'
168
+ description: "What to correct (for 'correct' action): 'category'",
169
+ enum: ['category']
182
170
  },
183
- limit: {
184
- type: 'number',
185
- description: 'Max results (default: 10)'
171
+ original: {
172
+ type: 'string',
173
+ description: 'Original value being corrected'
174
+ },
175
+ corrected: {
176
+ type: 'string',
177
+ description: 'New corrected value'
178
+ },
179
+ feedback: {
180
+ type: 'string',
181
+ description: "Feedback text (for 'submit' action)"
182
+ },
183
+ source: {
184
+ type: 'string',
185
+ description: "Feedback source (for 'submit' action)",
186
+ default: 'mcp'
186
187
  }
187
188
  },
188
- required: ['query']
189
+ required: ['action']
189
190
  }
190
191
  },
191
192
  {
192
- name: 'get_insights',
193
- description: 'Get high-level insights and patterns across your priorities. Identifies themes, trends, and strategic recommendations.',
193
+ name: 'circuit.ask',
194
+ description: 'Ask anything about your feedback data. Searches across feedback, priorities, briefs, and help articles using semantic search.',
194
195
  inputSchema: {
195
196
  type: 'object',
196
197
  properties: {
197
- limit: {
198
- type: 'number',
199
- description: 'Number of priorities to analyze (default: 10)'
198
+ question: {
199
+ type: 'string',
200
+ description: "Natural language question (e.g., 'What are enterprise customers complaining about?', 'How do briefs work?')"
200
201
  }
201
- }
202
+ },
203
+ required: ['question']
202
204
  }
203
205
  }
204
206
  ]
@@ -232,7 +234,7 @@ async function handleMessage(message, token) {
232
234
  }
233
235
 
234
236
  /**
235
- * Format priorities for display (matches Circuit UI exactly)
237
+ * Format priorities response
236
238
  */
237
239
  function formatPriorities(data) {
238
240
  if (data.message) {
@@ -245,47 +247,49 @@ function formatPriorities(data) {
245
247
 
246
248
  const lines = [];
247
249
 
250
+ if (data.memory_applied) {
251
+ lines.push(`*Memory active: ${data.ships_count} ships tracked, segment: ${data.segment_affinity || 'mixed'}*\n`);
252
+ }
253
+
248
254
  for (const p of data.priorities) {
249
- // Format trend indicator like Circuit UI
250
255
  let trendText = '';
251
256
  if (p.trend === 'up') {
252
- trendText = ' ';
257
+ trendText = ` ↑${p.trend_percent ? ` ${p.trend_percent}%` : ''}`;
253
258
  } else if (p.trend === 'down') {
254
- trendText = ' ';
259
+ trendText = ` ↓${p.trend_percent ? ` ${p.trend_percent}%` : ''}`;
255
260
  }
256
261
 
257
- // Format: #Rank. Title
258
262
  lines.push(`**#${p.rank}. ${p.theme}**`);
259
263
 
260
- // Badges row: Category | X users | Trend | Status
261
264
  const badges = [];
262
265
  badges.push(p.category || 'Other');
263
266
  badges.push(`${p.volume} users`);
264
267
  if (trendText) badges.push(trendText.trim());
265
268
  if (p.brief_status && p.brief_status !== 'no_brief') {
266
- const statusLabel = { ready: 'Ready', building: 'Building', shipped: 'Shipped', done: 'Shipped' }[p.brief_status];
269
+ const statusLabel = { ready: 'Ready', building: 'Building', shipped: 'Shipped' }[p.brief_status];
267
270
  if (statusLabel) badges.push(statusLabel);
268
271
  }
272
+ if (p.matches_pattern) badges.push('matches pattern');
273
+ if (p.version) badges.push(p.version);
269
274
 
270
275
  lines.push(badges.join(' · '));
271
276
 
272
- // Key quote (customer voice)
273
277
  if (p.key_quote) {
274
278
  lines.push(`> "${p.key_quote}"`);
275
279
  }
276
280
 
277
- lines.push(`priority_id: \`${p.id}\``);
281
+ lines.push(`priority_id: \`${p.priority_id}\`${p.build_id ? ` · build_id: \`${p.build_id}\`` : ''}`);
278
282
  lines.push('');
279
283
  }
280
284
 
281
285
  lines.push('---');
282
- lines.push('Use `get_brief` with a priority_id to see the full engineering spec.');
286
+ lines.push('Use `circuit.brief` with a priority_id to see the full engineering spec.');
283
287
 
284
288
  return lines.join('\n');
285
289
  }
286
290
 
287
291
  /**
288
- * Format brief for display (matches Circuit UI format for Cursor/Claude)
292
+ * Format brief response
289
293
  */
290
294
  function formatBrief(data) {
291
295
  if (data.error) {
@@ -293,51 +297,31 @@ function formatBrief(data) {
293
297
  const p = data.priority;
294
298
  let output = `# ${p.theme || 'Priority'}\n\n`;
295
299
  output += `${p.category || 'Other'} · ${p.volume || 0} users\n\n`;
296
- output += `**No brief generated yet**\n\n`;
297
-
298
- if (p.summary) {
299
- output += `${p.summary}\n\n`;
300
+ output += `**No brief generated yet.**\n\n`;
301
+ if (data.suggestion) {
302
+ output += `${data.suggestion}\n`;
300
303
  }
301
-
302
- if (p.key_quote) {
303
- output += `> "${p.key_quote}"\n\n`;
304
- }
305
-
306
- output += `---\n\n`;
307
-
308
- // Include deep link to Circuit
309
- if (data.circuit_url) {
310
- output += `**Generate brief:** ${data.circuit_url}\n\n`;
311
- }
312
-
313
- output += `Or I can help you draft implementation notes based on the customer feedback above.`;
314
304
  return output;
315
305
  }
316
306
  return `Error: ${data.message || data.error}`;
317
307
  }
318
308
 
319
309
  const spec = data.spec_content || '';
320
-
321
- // Build header like Circuit UI exports
322
- // Format: # Title
323
- // Priority: #X | Mentions: Y | Type: Z
324
- // ---
325
- // {spec content}
326
-
327
310
  let output = '';
328
311
 
329
- // Status line (handle both 'shipped' and legacy 'done')
330
- const statusText = {
331
- 'ready': 'Ready',
332
- 'building': 'Building',
333
- 'shipped': 'Shipped',
334
- 'done': 'Shipped'
335
- }[data.status] || data.status;
312
+ const title = data.title || 'Engineering Brief';
313
+ output += `# ${title}\n\n`;
314
+
315
+ const statusText = { ready: 'Ready', building: 'Building', shipped: 'Shipped' }[data.status] || data.status;
316
+ const versionBadge = data.version_badge ? ` · ${data.version_badge}` : '';
317
+ output += `Status: ${statusText}${versionBadge}\n`;
336
318
 
337
- output += `Status: ${statusText}\n\n`;
319
+ if (data.customer_context) {
320
+ const ctx = data.customer_context;
321
+ output += `${ctx.category} · ${ctx.volume} users · ${ctx.paying_percent}% paying\n`;
322
+ }
323
+ output += '\n';
338
324
 
339
- // The spec_content should already be formatted markdown
340
- // Clean up any XML-style tags to proper headers
341
325
  let cleanSpec = spec
342
326
  .replace(/<what_to_build>/gi, '## WHAT TO BUILD\n')
343
327
  .replace(/<\/what_to_build>/gi, '\n')
@@ -352,6 +336,19 @@ function formatBrief(data) {
352
336
 
353
337
  output += cleanSpec;
354
338
 
339
+ if (data.related_memory && data.related_memory.length > 0) {
340
+ output += '\n## WHAT CIRCUIT REMEMBERS\n\n';
341
+ for (const mem of data.related_memory) {
342
+ if (mem.type === 'ship') {
343
+ output += `- Shipped: ${mem.theme || mem.summary || 'Related feature'}\n`;
344
+ } else if (mem.type === 'correction') {
345
+ output += `- Correction: ${mem.summary || 'Classification adjusted'}\n`;
346
+ } else {
347
+ output += `- ${mem.summary || JSON.stringify(mem)}\n`;
348
+ }
349
+ }
350
+ }
351
+
355
352
  output += `\n---\n`;
356
353
  output += `build_id: \`${data.build_id}\``;
357
354
 
@@ -359,103 +356,76 @@ function formatBrief(data) {
359
356
  }
360
357
 
361
358
  /**
362
- * Format search results for display
363
- */
364
- function formatSearchResults(data) {
365
- if (data.message) {
366
- return data.message;
367
- }
368
-
369
- if (!data.results || data.results.length === 0) {
370
- return 'No feedback found matching your search.';
371
- }
372
-
373
- const lines = [`**${data.total} results found**\n`];
374
-
375
- for (const f of data.results) {
376
- lines.push(`**${f.source}** · Urgency: ${f.urgency}/5`);
377
- lines.push(`> "${f.text}"`);
378
- lines.push('');
379
- }
380
-
381
- return lines.join('\n');
382
- }
383
-
384
- /**
385
- * Format status change response
359
+ * Format act response
386
360
  */
387
- function formatStatusChange(data) {
361
+ function formatAct(data) {
388
362
  if (data.error) {
389
363
  return `Error: ${data.error}`;
390
364
  }
391
365
 
392
366
  if (data.success) {
393
- return data.message;
367
+ let output = data.message;
368
+ if (data.memory_created) {
369
+ output += '\nShip memory recorded — Circuit will remember this for future briefs.';
370
+ }
371
+ return output;
394
372
  }
395
373
 
396
374
  return JSON.stringify(data, null, 2);
397
375
  }
398
376
 
399
377
  /**
400
- * Format insights for display
378
+ * Format ask response
401
379
  */
402
- function formatInsights(data) {
380
+ function formatAsk(data) {
403
381
  if (data.message) {
404
382
  return data.message;
405
383
  }
406
384
 
407
385
  const lines = [];
408
386
 
409
- lines.push(`## Insights from ${data.analyzed} priorities\n`);
410
- lines.push(`**Total feedback volume:** ${data.total_feedback_volume} mentions\n`);
411
-
412
- // Category breakdown
413
- if (data.category_breakdown) {
414
- lines.push('**Category breakdown:**');
415
- for (const [cat, count] of Object.entries(data.category_breakdown)) {
416
- lines.push(`- ${cat}: ${count}`);
387
+ if (data.help_articles && data.help_articles.length > 0) {
388
+ lines.push('**Help Articles:**');
389
+ for (const a of data.help_articles) {
390
+ lines.push(`- **${a.title}**: ${a.content}`);
417
391
  }
418
392
  lines.push('');
419
393
  }
420
394
 
421
- // Top 3 by volume
422
- if (data.top_3_by_volume && data.top_3_by_volume.length > 0) {
423
- lines.push('**Top priorities by volume:**');
424
- for (const p of data.top_3_by_volume) {
425
- lines.push(`- ${p.theme} (${p.volume} users, ${p.category})`);
395
+ if (data.priorities && data.priorities.length > 0) {
396
+ lines.push('**Related Priorities:**');
397
+ for (const p of data.priorities) {
398
+ lines.push(`- ${p.theme} (${p.category}, ${p.volume} users) — priority_id: \`${p.id}\``);
426
399
  }
427
400
  lines.push('');
428
401
  }
429
402
 
430
- // Trends
431
- if (data.trending_up && data.trending_up.length > 0) {
432
- lines.push(`**Trending up:** ${data.trending_up.join(', ')}`);
433
- }
434
- if (data.trending_down && data.trending_down.length > 0) {
435
- lines.push(`**Trending down:** ${data.trending_down.join(', ')}`);
436
- }
437
-
438
- // High urgency
439
- if (data.high_urgency && data.high_urgency.length > 0) {
440
- lines.push('\n**High urgency items:**');
441
- for (const h of data.high_urgency) {
442
- lines.push(`- ${h.theme} (urgency: ${h.urgency})`);
403
+ if (data.feedback && data.feedback.length > 0) {
404
+ lines.push('**Related Feedback:**');
405
+ for (const f of data.feedback) {
406
+ lines.push(`- [${f.source}] "${f.text}"`);
443
407
  }
408
+ lines.push('');
444
409
  }
445
410
 
446
- // Recommendations
447
- if (data.recommendations && data.recommendations.length > 0) {
448
- lines.push('\n**Recommendations:**');
449
- for (const r of data.recommendations) {
450
- lines.push(`- ${r}`);
411
+ if (data.your_patterns) {
412
+ const pat = data.your_patterns;
413
+ lines.push('**Your Patterns:**');
414
+ lines.push(`- Ships: ${pat.ships_count}`);
415
+ lines.push(`- Segment: ${pat.segment_affinity}`);
416
+ if (pat.top_categories) {
417
+ const cats = Object.entries(pat.top_categories).map(([k, v]) => `${k} (${Math.round(v * 100)}%)`).join(', ');
418
+ lines.push(`- Top categories: ${cats}`);
451
419
  }
420
+ lines.push('');
452
421
  }
453
422
 
454
- lines.push('\n---');
455
- if (data.circuit_url) {
456
- lines.push(`View full details: ${data.circuit_url}`);
423
+ if (lines.length === 0) {
424
+ return `No results found for "${data.question}". Try rephrasing.`;
457
425
  }
458
426
 
427
+ lines.push(`---\n${data.total} results found`);
428
+
459
429
  return lines.join('\n');
460
430
  }
461
431
 
@@ -466,29 +436,22 @@ async function handleToolCall(id, params, token) {
466
436
  const { name, arguments: args } = params;
467
437
 
468
438
  try {
469
- // Call the Circuit MCP backend
470
439
  const result = await callMcpApi(token, name, args || {});
471
440
 
472
- // Format response based on tool type
473
441
  let formattedText;
474
442
 
475
443
  switch (name) {
476
- case 'get_priorities':
444
+ case 'circuit.priorities':
477
445
  formattedText = formatPriorities(result);
478
446
  break;
479
- case 'get_brief':
447
+ case 'circuit.brief':
480
448
  formattedText = formatBrief(result);
481
449
  break;
482
- case 'search_feedback':
483
- formattedText = formatSearchResults(result);
450
+ case 'circuit.act':
451
+ formattedText = formatAct(result);
484
452
  break;
485
- case 'start_building':
486
- case 'mark_shipped':
487
- case 'mark_done':
488
- formattedText = formatStatusChange(result);
489
- break;
490
- case 'get_insights':
491
- formattedText = formatInsights(result);
453
+ case 'circuit.ask':
454
+ formattedText = formatAsk(result);
492
455
  break;
493
456
  default:
494
457
  formattedText = JSON.stringify(result, null, 2);
@@ -510,4 +473,3 @@ async function handleToolCall(id, params, token) {
510
473
  };
511
474
  }
512
475
  }
513
-