@vibescope/mcp-server 0.4.4 → 0.4.6

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 (204) hide show
  1. package/CHANGELOG.md +84 -84
  2. package/README.md +194 -194
  3. package/dist/api-client/bodies-of-work.d.ts +125 -0
  4. package/dist/api-client/bodies-of-work.js +78 -0
  5. package/dist/api-client/chat.d.ts +26 -0
  6. package/dist/api-client/chat.js +20 -0
  7. package/dist/api-client/connectors.d.ts +104 -0
  8. package/dist/api-client/connectors.js +46 -0
  9. package/dist/api-client/deployment.d.ts +190 -0
  10. package/dist/api-client/deployment.js +113 -0
  11. package/dist/api-client/file-checkouts.d.ts +71 -0
  12. package/dist/api-client/file-checkouts.js +43 -0
  13. package/dist/api-client/git-issues.d.ts +55 -0
  14. package/dist/api-client/git-issues.js +34 -0
  15. package/dist/api-client/index.d.ts +619 -1
  16. package/dist/api-client/index.js +148 -0
  17. package/dist/api-client/organizations.d.ts +101 -0
  18. package/dist/api-client/organizations.js +86 -0
  19. package/dist/api-client/progress.d.ts +61 -0
  20. package/dist/api-client/progress.js +34 -0
  21. package/dist/api-client/project.d.ts +1 -0
  22. package/dist/api-client/requests.d.ts +28 -0
  23. package/dist/api-client/requests.js +28 -0
  24. package/dist/api-client/sprints.d.ts +153 -0
  25. package/dist/api-client/sprints.js +82 -0
  26. package/dist/api-client/subtasks.d.ts +37 -0
  27. package/dist/api-client/subtasks.js +23 -0
  28. package/dist/api-client.d.ts +23 -0
  29. package/dist/api-client.js +15 -0
  30. package/dist/cli-init.js +21 -21
  31. package/dist/cli.js +26 -26
  32. package/dist/handlers/blockers.js +4 -0
  33. package/dist/handlers/chat.d.ts +23 -0
  34. package/dist/handlers/chat.js +84 -0
  35. package/dist/handlers/deployment.d.ts +3 -0
  36. package/dist/handlers/deployment.js +23 -0
  37. package/dist/handlers/discovery.js +13 -0
  38. package/dist/handlers/index.d.ts +1 -0
  39. package/dist/handlers/index.js +3 -0
  40. package/dist/handlers/project.js +4 -2
  41. package/dist/handlers/session.js +7 -0
  42. package/dist/handlers/tasks.js +7 -0
  43. package/dist/handlers/tool-docs.js +1204 -1131
  44. package/dist/index.js +73 -73
  45. package/dist/templates/agent-guidelines.d.ts +1 -1
  46. package/dist/templates/agent-guidelines.js +205 -187
  47. package/dist/templates/help-content.js +1621 -1621
  48. package/dist/tools/bodies-of-work.js +6 -6
  49. package/dist/tools/chat.d.ts +1 -0
  50. package/dist/tools/chat.js +24 -0
  51. package/dist/tools/cloud-agents.js +22 -22
  52. package/dist/tools/deployment.js +13 -0
  53. package/dist/tools/features.d.ts +13 -0
  54. package/dist/tools/features.js +151 -0
  55. package/dist/tools/index.d.ts +3 -1
  56. package/dist/tools/index.js +4 -1
  57. package/dist/tools/milestones.js +2 -2
  58. package/dist/tools/project.js +4 -0
  59. package/dist/tools/requests.js +1 -1
  60. package/dist/tools/session.js +11 -11
  61. package/dist/tools/sprints.js +9 -9
  62. package/dist/tools/tasks.js +35 -35
  63. package/dist/tools/worktrees.js +14 -14
  64. package/dist/tools.d.ts +2 -0
  65. package/dist/tools.js +3602 -0
  66. package/dist/utils.js +11 -11
  67. package/docs/TOOLS.md +2663 -2545
  68. package/package.json +53 -53
  69. package/scripts/generate-docs.ts +212 -212
  70. package/scripts/version-bump.ts +203 -203
  71. package/src/api-client/blockers.ts +86 -86
  72. package/src/api-client/bodies-of-work.ts +194 -0
  73. package/src/api-client/chat.ts +50 -0
  74. package/src/api-client/connectors.ts +152 -0
  75. package/src/api-client/cost.ts +185 -185
  76. package/src/api-client/decisions.ts +87 -87
  77. package/src/api-client/deployment.ts +313 -0
  78. package/src/api-client/discovery.ts +81 -81
  79. package/src/api-client/fallback.ts +52 -52
  80. package/src/api-client/file-checkouts.ts +115 -0
  81. package/src/api-client/findings.ts +100 -100
  82. package/src/api-client/git-issues.ts +88 -0
  83. package/src/api-client/ideas.ts +112 -112
  84. package/src/api-client/index.ts +592 -426
  85. package/src/api-client/milestones.ts +83 -83
  86. package/src/api-client/organizations.ts +185 -0
  87. package/src/api-client/progress.ts +94 -0
  88. package/src/api-client/project.ts +180 -179
  89. package/src/api-client/requests.ts +54 -0
  90. package/src/api-client/session.ts +220 -220
  91. package/src/api-client/sprints.ts +227 -0
  92. package/src/api-client/subtasks.ts +57 -0
  93. package/src/api-client/tasks.ts +450 -450
  94. package/src/api-client/types.ts +32 -32
  95. package/src/api-client/validation.ts +60 -60
  96. package/src/api-client/worktrees.ts +53 -53
  97. package/src/api-client.test.ts +847 -850
  98. package/src/api-client.ts +2707 -2672
  99. package/src/cli-init.ts +557 -557
  100. package/src/cli.test.ts +284 -284
  101. package/src/cli.ts +204 -204
  102. package/src/handlers/__test-setup__.ts +240 -236
  103. package/src/handlers/__test-utils__.ts +89 -89
  104. package/src/handlers/blockers.test.ts +468 -468
  105. package/src/handlers/blockers.ts +172 -163
  106. package/src/handlers/bodies-of-work.test.ts +704 -704
  107. package/src/handlers/bodies-of-work.ts +526 -526
  108. package/src/handlers/chat.test.ts +185 -0
  109. package/src/handlers/chat.ts +101 -0
  110. package/src/handlers/cloud-agents.test.ts +438 -438
  111. package/src/handlers/cloud-agents.ts +156 -156
  112. package/src/handlers/connectors.test.ts +834 -834
  113. package/src/handlers/connectors.ts +229 -229
  114. package/src/handlers/cost.test.ts +462 -462
  115. package/src/handlers/cost.ts +285 -285
  116. package/src/handlers/decisions.test.ts +382 -382
  117. package/src/handlers/decisions.ts +153 -153
  118. package/src/handlers/deployment.test.ts +551 -551
  119. package/src/handlers/deployment.ts +570 -541
  120. package/src/handlers/discovery.test.ts +206 -206
  121. package/src/handlers/discovery.ts +427 -414
  122. package/src/handlers/fallback.test.ts +537 -537
  123. package/src/handlers/fallback.ts +194 -194
  124. package/src/handlers/file-checkouts.test.ts +750 -750
  125. package/src/handlers/file-checkouts.ts +185 -185
  126. package/src/handlers/findings.test.ts +633 -633
  127. package/src/handlers/findings.ts +239 -239
  128. package/src/handlers/git-issues.test.ts +631 -631
  129. package/src/handlers/git-issues.ts +136 -136
  130. package/src/handlers/ideas.test.ts +644 -644
  131. package/src/handlers/ideas.ts +207 -207
  132. package/src/handlers/index.ts +93 -90
  133. package/src/handlers/milestones.test.ts +475 -475
  134. package/src/handlers/milestones.ts +180 -180
  135. package/src/handlers/organizations.test.ts +826 -826
  136. package/src/handlers/organizations.ts +315 -315
  137. package/src/handlers/progress.test.ts +269 -269
  138. package/src/handlers/progress.ts +77 -77
  139. package/src/handlers/project.test.ts +546 -546
  140. package/src/handlers/project.ts +242 -239
  141. package/src/handlers/requests.test.ts +303 -303
  142. package/src/handlers/requests.ts +99 -99
  143. package/src/handlers/roles.test.ts +305 -305
  144. package/src/handlers/roles.ts +219 -219
  145. package/src/handlers/session.test.ts +998 -998
  146. package/src/handlers/session.ts +1105 -1093
  147. package/src/handlers/sprints.test.ts +732 -732
  148. package/src/handlers/sprints.ts +537 -537
  149. package/src/handlers/tasks.test.ts +931 -931
  150. package/src/handlers/tasks.ts +1133 -1121
  151. package/src/handlers/tool-categories.test.ts +66 -66
  152. package/src/handlers/tool-docs.test.ts +511 -511
  153. package/src/handlers/tool-docs.ts +1571 -1491
  154. package/src/handlers/types.test.ts +259 -259
  155. package/src/handlers/types.ts +176 -176
  156. package/src/handlers/validation.test.ts +582 -582
  157. package/src/handlers/validation.ts +164 -164
  158. package/src/handlers/version.ts +63 -63
  159. package/src/index.test.ts +674 -674
  160. package/src/index.ts +807 -807
  161. package/src/setup.test.ts +233 -233
  162. package/src/setup.ts +404 -404
  163. package/src/templates/agent-guidelines.ts +233 -215
  164. package/src/templates/help-content.ts +1751 -1751
  165. package/src/token-tracking.test.ts +463 -463
  166. package/src/token-tracking.ts +167 -167
  167. package/src/tools/blockers.ts +122 -122
  168. package/src/tools/bodies-of-work.ts +283 -283
  169. package/src/tools/chat.ts +72 -46
  170. package/src/tools/cloud-agents.ts +101 -101
  171. package/src/tools/connectors.ts +191 -191
  172. package/src/tools/cost.ts +111 -111
  173. package/src/tools/decisions.ts +111 -111
  174. package/src/tools/deployment.ts +455 -442
  175. package/src/tools/discovery.ts +76 -76
  176. package/src/tools/fallback.ts +111 -111
  177. package/src/tools/features.ts +154 -0
  178. package/src/tools/file-checkouts.ts +145 -145
  179. package/src/tools/findings.ts +101 -101
  180. package/src/tools/git-issues.ts +130 -130
  181. package/src/tools/ideas.ts +162 -162
  182. package/src/tools/index.ts +141 -137
  183. package/src/tools/milestones.ts +118 -118
  184. package/src/tools/organizations.ts +224 -224
  185. package/src/tools/progress.ts +73 -73
  186. package/src/tools/project.ts +206 -202
  187. package/src/tools/requests.ts +68 -68
  188. package/src/tools/roles.ts +112 -112
  189. package/src/tools/session.ts +181 -181
  190. package/src/tools/sprints.ts +298 -298
  191. package/src/tools/tasks.ts +550 -550
  192. package/src/tools/tools.test.ts +222 -222
  193. package/src/tools/types.ts +9 -9
  194. package/src/tools/validation.ts +75 -75
  195. package/src/tools/version.ts +34 -34
  196. package/src/tools/worktrees.ts +66 -66
  197. package/src/tools.test.ts +416 -416
  198. package/src/utils.test.ts +1014 -1014
  199. package/src/utils.ts +586 -586
  200. package/src/validators.test.ts +223 -223
  201. package/src/validators.ts +249 -249
  202. package/src/version.ts +109 -109
  203. package/tsconfig.json +16 -16
  204. package/vitest.config.ts +14 -14
package/src/index.ts CHANGED
@@ -1,807 +1,807 @@
1
- #!/usr/bin/env node
2
-
3
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
- import {
6
- CallToolRequestSchema,
7
- ListToolsRequestSchema,
8
- type Tool,
9
- } from '@modelcontextprotocol/sdk/types.js';
10
- import { randomUUID } from 'crypto';
11
- import { initApiClient, getApiClient } from './api-client.js';
12
- import {
13
- ValidationError,
14
- validateRequired,
15
- validateUUID,
16
- validateTaskStatus,
17
- validateProjectStatus,
18
- validatePriority,
19
- validateProgressPercentage,
20
- validateEstimatedMinutes,
21
- validateEnvironment,
22
- VALID_TASK_STATUSES,
23
- VALID_PROJECT_STATUSES,
24
- VALID_BLOCKER_STATUSES,
25
- VALID_DEPLOYMENT_STATUSES,
26
- VALID_ENVIRONMENTS,
27
- } from './validators.js';
28
- import {
29
- AGENT_PERSONAS,
30
- FALLBACK_ACTIVITIES,
31
- getRandomFallbackActivity,
32
- selectPersona,
33
- RateLimiter,
34
- extractProjectNameFromGitUrl,
35
- isValidStatusTransition,
36
- getErrorMessage,
37
- } from './utils.js';
38
- import { buildHandlerRegistry, type HandlerContext } from './handlers/index.js';
39
- import { tools } from './tools/index.js';
40
- import {
41
- createTokenUsage,
42
- trackTokenUsage as trackTokens,
43
- setCurrentModel,
44
- type TokenUsage,
45
- } from './token-tracking.js';
46
- import { getUpdateWarning } from './version.js';
47
-
48
- // ============================================================================
49
- // Agent Instance Tracking
50
- // ============================================================================
51
-
52
- // Unique identifier for this agent instance (survives tool calls within same session)
53
- const INSTANCE_ID = randomUUID();
54
-
55
- // Current session ID (set when start_work_session is called)
56
- let currentSessionId: string | null = null;
57
-
58
- // Assigned persona for this agent instance
59
- let currentPersona: string | null = null;
60
-
61
- // Current role for this agent instance
62
- let currentRole: 'developer' | 'validator' | 'deployer' | 'reviewer' | 'maintainer' | null = null;
63
-
64
- // Current project ID for this agent instance
65
- let currentProjectId: string | null = null;
66
-
67
- // Token usage tracking for this session (using token-tracking module)
68
- let sessionTokenUsage: TokenUsage = createTokenUsage();
69
-
70
- // Wrapper function to track token usage with the session's usage object
71
- function trackTokenUsage(toolName: string, args: unknown, response: unknown): void {
72
- trackTokens(sessionTokenUsage, toolName, args, response);
73
- }
74
-
75
- // Global rate limiter instance (60 requests per minute per API key)
76
- const rateLimiter = new RateLimiter(60, 60000);
77
-
78
- // Cleanup expired entries every 5 minutes (store interval ID for cleanup)
79
- const rateLimiterCleanupInterval = setInterval(() => rateLimiter.cleanup(), 5 * 60 * 1000);
80
-
81
- // Cleanup on process exit to prevent memory leaks
82
- process.on('exit', () => clearInterval(rateLimiterCleanupInterval));
83
- process.on('SIGINT', () => { clearInterval(rateLimiterCleanupInterval); process.exit(0); });
84
- process.on('SIGTERM', () => { clearInterval(rateLimiterCleanupInterval); process.exit(0); });
85
-
86
- // Build handler registry from modular handlers
87
- const handlerRegistry = buildHandlerRegistry();
88
-
89
- // ============================================================================
90
- // Embedded Knowledge Base (on-demand help topics)
91
- // ============================================================================
92
-
93
- const KNOWLEDGE_BASE: Record<string, string> = {
94
- getting_started: `# Getting Started
95
- 1. Call start_work_session(git_url, model: "your-model-name") to initialize
96
- - IMPORTANT: Pass your model for accurate cost tracking (e.g., "opus", "sonnet", "gemini", "gpt-4o")
97
- - Check your system prompt for "You are powered by the model named..." to find it
98
- 2. Response includes next_task - start working on it immediately
99
- 3. Use update_task to mark in_progress and track progress
100
- 4. Call complete_task when done - it returns your next task
101
- 5. Use get_help(topic) when you need guidance on specific workflows`,
102
-
103
- tasks: `# Task Workflow
104
- - Mark task in_progress with update_task before starting
105
- - Update progress_percentage regularly (every 15-20% progress)
106
- - Include progress_note to auto-log milestones
107
- - One task at a time - complete current before starting another
108
- - complete_task returns next_task and context counts (validation, blockers, deployment)
109
- - Priority: 1=highest, 5=lowest`,
110
-
111
- validation: `# Task Validation
112
- Completed tasks need validation before deployment. PRIORITIZE validation over new tasks.
113
- 1. Check: get_tasks_awaiting_validation(project_id)
114
- 2. Claim: claim_validation(task_id) - marks "being reviewed" on dashboard
115
- 3. Review code changes and run tests
116
- 4. Complete: validate_task(task_id, approved: true/false, validation_notes: "...")
117
- Self-validation allowed when single-agent or to unblock deployment.`,
118
-
119
- deployment: `# Deployment Workflow
120
- 1. Ensure all completed tasks are validated first
121
- 2. request_deployment(project_id, environment: "production")
122
- 3. claim_deployment_validation(project_id) - claim for validation
123
- 4. Run: pnpm build && pnpm test
124
- 5. report_validation(project_id, build_passed: true, tests_passed: true)
125
- 6. start_deployment(project_id) - returns project's deployment_instructions
126
- 7. Follow the instructions (e.g., push to main, run deploy command)
127
- 8. complete_deployment(project_id, success: true, summary: "...")`,
128
-
129
- git: `# Git Workflow
130
- Call get_git_workflow(project_id) for project-specific config.
131
- Workflows: none, trunk-based, github-flow, git-flow
132
- - trunk-based: commit directly to main
133
- - github-flow: feature branches, merge via PR
134
- - git-flow: develop/release branches
135
- Update task git_branch when working on a branch.`,
136
-
137
- blockers: `# Working with Blockers
138
- When stuck and need human input:
139
- 1. add_blocker(project_id, description: "What's blocking")
140
- 2. Ask your question to the user
141
- 3. resolve_blocker(blocker_id, resolution_note: "How resolved")
142
- Only use for genuine blockers requiring decisions - not routine questions.`,
143
-
144
- milestones: `# Task Milestones
145
- For complex tasks, break into milestones:
146
- 1. add_milestone(task_id, title: "Design schema")
147
- 2. add_milestone(task_id, title: "Implement API")
148
- 3. Update with complete_milestone(milestone_id) as you go
149
- Dashboard shows progress bar with completed/total.`,
150
-
151
- fallback: `# Fallback Activities
152
- When no tasks available, get_next_task suggests activities:
153
- - feature_ideation, code_review, performance_audit
154
- - security_review, test_coverage, documentation_review
155
- 1. start_fallback_activity(project_id, activity: "code_review")
156
- 2. Do the work, use add_finding for issues, add_idea for improvements
157
- 3. stop_fallback_activity(project_id, summary: "...")`,
158
-
159
- session: `# Session Management
160
- - start_work_session(git_url, model) initializes and returns next_task
161
- - ALWAYS pass model parameter for cost tracking (e.g., "opus", "sonnet", "gemini", "gpt-4o")
162
- - Use mode:'lite' (default) or mode:'full' for complete context
163
- - heartbeat every 30-60 seconds maintains active status
164
- - end_work_session releases claimed tasks and returns summary
165
- - NEVER STOP: After completing a task, immediately start the next one
166
- - When context grows large: /clear then start_work_session to continue fresh
167
- - Your progress is saved to the dashboard - nothing is lost on /clear`,
168
-
169
- tokens: `# Token Efficiency
170
- Be mindful of token costs - every tool call has a cost.
171
- - Use mode:'lite' (default) - saves ~85% vs full mode
172
- - Call get_token_usage() to check consumption
173
- - /compact when context grows large
174
- - Batch related updates when possible
175
- - Trust lite mode; only use full mode for initial exploration`,
176
-
177
- topics: `# Available Help Topics
178
- - getting_started: Basic workflow overview
179
- - tasks: Working on tasks, progress tracking
180
- - validation: Cross-agent task validation
181
- - deployment: Deployment coordination
182
- - git: Git workflow configuration
183
- - blockers: Handling blockers
184
- - milestones: Breaking down complex tasks
185
- - fallback: Background activities when idle
186
- - session: Session management
187
- - tokens: Token efficiency tips`,
188
- };
189
-
190
-
191
- // ============================================================================
192
- // Types
193
- // ============================================================================
194
-
195
- interface Database {
196
- public: {
197
- Tables: {
198
- api_keys: {
199
- Row: {
200
- id: string;
201
- user_id: string;
202
- key_hash: string;
203
- name: string;
204
- last_used_at: string | null;
205
- };
206
- Insert: {
207
- id?: string;
208
- user_id: string;
209
- key_hash: string;
210
- name: string;
211
- last_used_at?: string | null;
212
- };
213
- Update: {
214
- id?: string;
215
- user_id?: string;
216
- key_hash?: string;
217
- name?: string;
218
- last_used_at?: string | null;
219
- };
220
- };
221
- projects: {
222
- Row: {
223
- id: string;
224
- user_id: string;
225
- name: string;
226
- description: string | null;
227
- goal: string | null;
228
- status: string;
229
- git_url: string | null;
230
- agent_instructions: string | null;
231
- tech_stack: string[] | null;
232
- git_workflow: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
233
- git_main_branch: string;
234
- git_develop_branch: string | null;
235
- git_auto_branch: boolean;
236
- git_auto_tag: boolean;
237
- created_at: string;
238
- updated_at: string;
239
- };
240
- Insert: {
241
- id?: string;
242
- user_id: string;
243
- name: string;
244
- description?: string | null;
245
- goal?: string | null;
246
- status?: string;
247
- git_url?: string | null;
248
- agent_instructions?: string | null;
249
- tech_stack?: string[] | null;
250
- git_workflow?: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
251
- git_main_branch?: string;
252
- git_develop_branch?: string | null;
253
- git_auto_branch?: boolean;
254
- git_auto_tag?: boolean;
255
- created_at?: string;
256
- updated_at?: string;
257
- };
258
- Update: {
259
- id?: string;
260
- user_id?: string;
261
- name?: string;
262
- description?: string | null;
263
- goal?: string | null;
264
- status?: string;
265
- git_url?: string | null;
266
- agent_instructions?: string | null;
267
- tech_stack?: string[] | null;
268
- git_workflow?: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
269
- git_main_branch?: string;
270
- git_develop_branch?: string | null;
271
- git_auto_branch?: boolean;
272
- git_auto_tag?: boolean;
273
- created_at?: string;
274
- updated_at?: string;
275
- };
276
- };
277
- tasks: {
278
- Row: {
279
- id: string;
280
- project_id: string;
281
- title: string;
282
- description: string | null;
283
- priority: number;
284
- status: string;
285
- created_by: string;
286
- created_at: string;
287
- completed_at: string | null;
288
- progress_percentage: number;
289
- estimated_minutes: number | null;
290
- started_at: string | null;
291
- working_agent_session_id: string | null;
292
- git_branch: string | null;
293
- references: { url: string; label?: string }[] | null;
294
- };
295
- Insert: {
296
- id?: string;
297
- project_id: string;
298
- title: string;
299
- description?: string | null;
300
- priority?: number;
301
- status?: string;
302
- created_by?: string;
303
- created_at?: string;
304
- completed_at?: string | null;
305
- progress_percentage?: number;
306
- estimated_minutes?: number | null;
307
- started_at?: string | null;
308
- working_agent_session_id?: string | null;
309
- git_branch?: string | null;
310
- references?: { url: string; label?: string }[] | null;
311
- };
312
- Update: {
313
- id?: string;
314
- project_id?: string;
315
- title?: string;
316
- description?: string | null;
317
- priority?: number;
318
- status?: string;
319
- created_by?: string;
320
- created_at?: string;
321
- completed_at?: string | null;
322
- progress_percentage?: number;
323
- estimated_minutes?: number | null;
324
- started_at?: string | null;
325
- working_agent_session_id?: string | null;
326
- git_branch?: string | null;
327
- references?: { url: string; label?: string }[] | null;
328
- };
329
- };
330
- progress_logs: {
331
- Row: {
332
- id: string;
333
- project_id: string;
334
- task_id: string | null;
335
- summary: string;
336
- details: string | null;
337
- created_by: string;
338
- created_at: string;
339
- };
340
- Insert: {
341
- id?: string;
342
- project_id: string;
343
- task_id?: string | null;
344
- summary: string;
345
- details?: string | null;
346
- created_by?: string;
347
- created_at?: string;
348
- };
349
- Update: {
350
- id?: string;
351
- project_id?: string;
352
- task_id?: string | null;
353
- summary?: string;
354
- details?: string | null;
355
- created_by?: string;
356
- created_at?: string;
357
- };
358
- };
359
- blockers: {
360
- Row: {
361
- id: string;
362
- project_id: string;
363
- description: string;
364
- status: string;
365
- created_by: string;
366
- created_at: string;
367
- resolved_at: string | null;
368
- resolution_note: string | null;
369
- };
370
- Insert: {
371
- id?: string;
372
- project_id: string;
373
- description: string;
374
- status?: string;
375
- created_by?: string;
376
- created_at?: string;
377
- resolved_at?: string | null;
378
- resolution_note?: string | null;
379
- };
380
- Update: {
381
- id?: string;
382
- project_id?: string;
383
- description?: string;
384
- status?: string;
385
- created_by?: string;
386
- created_at?: string;
387
- resolved_at?: string | null;
388
- resolution_note?: string | null;
389
- };
390
- };
391
- ideas: {
392
- Row: {
393
- id: string;
394
- project_id: string;
395
- title: string;
396
- description: string | null;
397
- created_by: string;
398
- created_at: string;
399
- };
400
- Insert: {
401
- id?: string;
402
- project_id: string;
403
- title: string;
404
- description?: string | null;
405
- created_by?: string;
406
- created_at?: string;
407
- };
408
- Update: {
409
- id?: string;
410
- project_id?: string;
411
- title?: string;
412
- description?: string | null;
413
- created_by?: string;
414
- created_at?: string;
415
- };
416
- };
417
- decisions: {
418
- Row: {
419
- id: string;
420
- project_id: string;
421
- title: string;
422
- description: string;
423
- rationale: string | null;
424
- alternatives_considered: string[] | null;
425
- created_by: string;
426
- created_at: string;
427
- };
428
- Insert: {
429
- id?: string;
430
- project_id: string;
431
- title: string;
432
- description: string;
433
- rationale?: string | null;
434
- alternatives_considered?: string[] | null;
435
- created_by?: string;
436
- created_at?: string;
437
- };
438
- Update: {
439
- id?: string;
440
- project_id?: string;
441
- title?: string;
442
- description?: string;
443
- rationale?: string | null;
444
- alternatives_considered?: string[] | null;
445
- created_by?: string;
446
- created_at?: string;
447
- };
448
- };
449
- agent_sessions: {
450
- Row: {
451
- id: string;
452
- api_key_id: string;
453
- project_id: string;
454
- last_synced_at: string;
455
- agent_name: string | null;
456
- agent_version: string | null;
457
- instance_id: string | null;
458
- current_task_id: string | null;
459
- status: string;
460
- };
461
- Insert: {
462
- id?: string;
463
- api_key_id: string;
464
- project_id: string;
465
- last_synced_at?: string;
466
- agent_name?: string | null;
467
- agent_version?: string | null;
468
- instance_id?: string | null;
469
- current_task_id?: string | null;
470
- status?: string;
471
- };
472
- Update: {
473
- id?: string;
474
- api_key_id?: string;
475
- project_id?: string;
476
- last_synced_at?: string;
477
- agent_name?: string | null;
478
- agent_version?: string | null;
479
- instance_id?: string | null;
480
- current_task_id?: string | null;
481
- status?: string;
482
- };
483
- };
484
- agent_heartbeats: {
485
- Row: {
486
- id: string;
487
- session_id: string;
488
- created_at: string;
489
- };
490
- Insert: {
491
- id?: string;
492
- session_id: string;
493
- created_at?: string;
494
- };
495
- Update: {
496
- id?: string;
497
- session_id?: string;
498
- created_at?: string;
499
- };
500
- };
501
- task_milestones: {
502
- Row: {
503
- id: string;
504
- task_id: string;
505
- title: string;
506
- description: string | null;
507
- order_index: number;
508
- status: string;
509
- created_by: string;
510
- created_at: string;
511
- completed_at: string | null;
512
- created_by_session_id: string | null;
513
- };
514
- Insert: {
515
- id?: string;
516
- task_id: string;
517
- title: string;
518
- description?: string | null;
519
- order_index?: number;
520
- status?: string;
521
- created_by?: string;
522
- created_at?: string;
523
- completed_at?: string | null;
524
- created_by_session_id?: string | null;
525
- };
526
- Update: {
527
- id?: string;
528
- task_id?: string;
529
- title?: string;
530
- description?: string | null;
531
- order_index?: number;
532
- status?: string;
533
- created_by?: string;
534
- created_at?: string;
535
- completed_at?: string | null;
536
- created_by_session_id?: string | null;
537
- };
538
- };
539
- };
540
- Views: Record<string, never>;
541
- Functions: Record<string, never>;
542
- Enums: Record<string, never>;
543
- };
544
- }
545
-
546
- interface AuthContext {
547
- userId: string;
548
- apiKeyId: string;
549
- organizationId?: string;
550
- scope: 'personal' | 'organization';
551
- }
552
-
553
- interface UserUpdates {
554
- tasks: Array<{ id: string; title: string; created_at: string }>;
555
- blockers: Array<{ id: string; description: string; created_at: string }>;
556
- ideas: Array<{ id: string; title: string; created_at: string }>;
557
- }
558
-
559
- // ============================================================================
560
- // Configuration
561
- // ============================================================================
562
-
563
- const API_KEY = process.env.VIBESCOPE_API_KEY;
564
-
565
- if (!API_KEY) {
566
- console.error('Missing required environment variable: VIBESCOPE_API_KEY');
567
- process.exit(1);
568
- }
569
-
570
- // Initialize the API client
571
- initApiClient({ apiKey: API_KEY });
572
-
573
- // ============================================================================
574
- // Authentication
575
- // ============================================================================
576
-
577
- async function validateApiKey(): Promise<AuthContext | null> {
578
- const apiClient = getApiClient();
579
- const response = await apiClient.validateAuth();
580
-
581
- if (!response.ok || !response.data?.valid) {
582
- return null;
583
- }
584
-
585
- return {
586
- userId: response.data.user_id,
587
- apiKeyId: response.data.api_key_id,
588
- scope: 'personal', // API handles authorization, scope not needed locally
589
- };
590
- }
591
-
592
- // Tool definitions imported from tools.ts
593
-
594
-
595
- // ============================================================================
596
- // Tool Handlers
597
- // ============================================================================
598
-
599
- async function handleTool(
600
- auth: AuthContext,
601
- name: string,
602
- args: Record<string, unknown>
603
- ): Promise<{ result: unknown; user_updates?: UserUpdates; reminder?: string }> {
604
- // Update session on every tool call via API:
605
- // - last_synced_at: keeps session alive and visible as "active" on dashboard
606
- // - token tracking: synced periodically for cost monitoring
607
- // Skip for start_work_session since it handles its own session setup
608
- if (currentSessionId && name !== 'start_work_session') {
609
- const apiClient = getApiClient();
610
- // Fire and forget - don't block tool execution on session sync
611
- apiClient.syncSession(currentSessionId).catch(() => {
612
- // Silently ignore sync errors - session tracking is non-critical
613
- });
614
- }
615
-
616
- // Check if handler exists in the modular registry
617
- const handler = handlerRegistry[name];
618
- if (handler) {
619
- // Build handler context
620
- const ctx: HandlerContext = {
621
- auth,
622
- session: {
623
- instanceId: INSTANCE_ID,
624
- currentSessionId,
625
- currentPersona,
626
- currentRole,
627
- currentProjectId,
628
- tokenUsage: sessionTokenUsage,
629
- },
630
- updateSession: (updates) => {
631
- if (updates.currentSessionId !== undefined) currentSessionId = updates.currentSessionId;
632
- if (updates.currentPersona !== undefined) currentPersona = updates.currentPersona;
633
- if (updates.currentRole !== undefined) currentRole = updates.currentRole;
634
- if (updates.currentProjectId !== undefined) currentProjectId = updates.currentProjectId;
635
- if (updates.tokenUsage !== undefined) sessionTokenUsage = updates.tokenUsage;
636
- },
637
- };
638
-
639
- const handlerResult = await handler(args, ctx);
640
- return handlerResult as { result: unknown; user_updates?: UserUpdates; reminder?: string };
641
- }
642
-
643
- throw new Error(`Unknown tool: ${name}`);
644
- }
645
-
646
- // ============================================================================
647
- // Server Setup
648
- // ============================================================================
649
-
650
- async function main() {
651
- // Validate API key on startup via API
652
- const auth = await validateApiKey();
653
- if (!auth) {
654
- console.error('Invalid API key');
655
- process.exit(1);
656
- }
657
-
658
- // Check for updates (non-blocking, with timeout)
659
- const updateWarning = await getUpdateWarning();
660
-
661
- const serverInstructions = updateWarning
662
- ? `${updateWarning}\n\nVibescope MCP server - AI project tracking and coordination tools.`
663
- : 'Vibescope MCP server - AI project tracking and coordination tools.';
664
-
665
- const server = new Server(
666
- {
667
- name: 'vibescope',
668
- version: '0.1.0',
669
- },
670
- {
671
- capabilities: {
672
- tools: {},
673
- },
674
- instructions: serverInstructions,
675
- }
676
- );
677
-
678
- // List available tools
679
- server.setRequestHandler(ListToolsRequestSchema, async () => {
680
- return { tools };
681
- });
682
-
683
- // Get reminder nudge - only for critical workflow reminders to save context
684
- function getReminder(toolName: string): string | null {
685
- // Most reminders removed to reduce context bloat - instructions are in CLAUDE.md
686
- // Only keep critical continuation reminder for complete_task
687
- const reminders: Record<string, string> = {
688
- 'complete_task': 'CONTINUE WORKING: Call get_next_task or start the next_task. If awaiting_validation tasks exist, validate them FIRST before new work. If context is large, run /clear then start_work_session to refresh.',
689
- 'batch_complete_tasks': 'CONTINUE WORKING: Call get_next_task. If awaiting_validation tasks exist, validate them FIRST before new work.',
690
- };
691
- return reminders[toolName] ?? null;
692
- }
693
-
694
- // Handle tool calls
695
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
696
- const { name, arguments: args } = request.params;
697
-
698
- // Check rate limit
699
- const rateCheck = rateLimiter.check(auth.apiKeyId);
700
- if (!rateCheck.allowed) {
701
- const resetSeconds = Math.ceil(rateCheck.resetIn / 1000);
702
- return {
703
- content: [
704
- {
705
- type: 'text',
706
- text: `Rate limit exceeded. Too many requests. Please wait ${resetSeconds} seconds before trying again. (Limit: 60 requests per minute)`,
707
- },
708
- ],
709
- isError: true,
710
- };
711
- }
712
-
713
- try {
714
- const toolArgs = (args as Record<string, unknown>) || {};
715
- const { result, user_updates } = await handleTool(
716
- auth,
717
- name,
718
- toolArgs
719
- );
720
-
721
- // Track token usage for this call (both input args and output result)
722
- trackTokenUsage(name, toolArgs, result);
723
-
724
- const content: { type: 'text'; text: string }[] = [
725
- {
726
- type: 'text',
727
- text: JSON.stringify(result, null, 2),
728
- },
729
- ];
730
-
731
- // Include reminder nudge if applicable
732
- const reminder = getReminder(name);
733
- if (reminder) {
734
- content.push({
735
- type: 'text',
736
- text: `\n--- ${reminder} ---`,
737
- });
738
- }
739
-
740
- // Include user updates only if there are new items (null means no updates)
741
- if (user_updates) {
742
- content.push({
743
- type: 'text',
744
- text: `\n--- New User Updates ---\n${JSON.stringify(user_updates, null, 2)}`,
745
- });
746
- }
747
-
748
- // Add rate limit warning if getting low
749
- if (rateCheck.remaining < 10) {
750
- content.push({
751
- type: 'text',
752
- text: `\n--- Rate Limit Warning: ${rateCheck.remaining} requests remaining this minute ---`,
753
- });
754
- }
755
-
756
- return { content };
757
- } catch (error) {
758
- // Handle validation errors with structured output
759
- if (error instanceof ValidationError) {
760
- return {
761
- content: [
762
- {
763
- type: 'text',
764
- text: JSON.stringify(error.toJSON(), null, 2),
765
- },
766
- ],
767
- isError: true,
768
- };
769
- }
770
-
771
- // Handle database errors with better context
772
- const errorMessage = getErrorMessage(error);
773
- let hint: string | undefined;
774
-
775
- if (errorMessage.includes('violates foreign key constraint')) {
776
- hint = 'The referenced ID does not exist. Check that the project_id, task_id, or other IDs are correct.';
777
- } else if (errorMessage.includes('duplicate key')) {
778
- hint = 'A record with this identifier already exists.';
779
- } else if (errorMessage.includes('not found') || errorMessage.includes('no rows')) {
780
- hint = 'The requested resource was not found. Verify the ID is correct.';
781
- }
782
-
783
- return {
784
- content: [
785
- {
786
- type: 'text',
787
- text: JSON.stringify({
788
- error: 'operation_failed',
789
- message: errorMessage,
790
- hint,
791
- }, null, 2),
792
- },
793
- ],
794
- isError: true,
795
- };
796
- }
797
- });
798
-
799
- // Start server
800
- const transport = new StdioServerTransport();
801
- await server.connect(transport);
802
- }
803
-
804
- main().catch((error) => {
805
- console.error('Fatal error:', error);
806
- process.exit(1);
807
- });
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ type Tool,
9
+ } from '@modelcontextprotocol/sdk/types.js';
10
+ import { randomUUID } from 'crypto';
11
+ import { initApiClient, getApiClient } from './api-client.js';
12
+ import {
13
+ ValidationError,
14
+ validateRequired,
15
+ validateUUID,
16
+ validateTaskStatus,
17
+ validateProjectStatus,
18
+ validatePriority,
19
+ validateProgressPercentage,
20
+ validateEstimatedMinutes,
21
+ validateEnvironment,
22
+ VALID_TASK_STATUSES,
23
+ VALID_PROJECT_STATUSES,
24
+ VALID_BLOCKER_STATUSES,
25
+ VALID_DEPLOYMENT_STATUSES,
26
+ VALID_ENVIRONMENTS,
27
+ } from './validators.js';
28
+ import {
29
+ AGENT_PERSONAS,
30
+ FALLBACK_ACTIVITIES,
31
+ getRandomFallbackActivity,
32
+ selectPersona,
33
+ RateLimiter,
34
+ extractProjectNameFromGitUrl,
35
+ isValidStatusTransition,
36
+ getErrorMessage,
37
+ } from './utils.js';
38
+ import { buildHandlerRegistry, type HandlerContext } from './handlers/index.js';
39
+ import { tools } from './tools/index.js';
40
+ import {
41
+ createTokenUsage,
42
+ trackTokenUsage as trackTokens,
43
+ setCurrentModel,
44
+ type TokenUsage,
45
+ } from './token-tracking.js';
46
+ import { getUpdateWarning } from './version.js';
47
+
48
+ // ============================================================================
49
+ // Agent Instance Tracking
50
+ // ============================================================================
51
+
52
+ // Unique identifier for this agent instance (survives tool calls within same session)
53
+ const INSTANCE_ID = randomUUID();
54
+
55
+ // Current session ID (set when start_work_session is called)
56
+ let currentSessionId: string | null = null;
57
+
58
+ // Assigned persona for this agent instance
59
+ let currentPersona: string | null = null;
60
+
61
+ // Current role for this agent instance
62
+ let currentRole: 'developer' | 'validator' | 'deployer' | 'reviewer' | 'maintainer' | null = null;
63
+
64
+ // Current project ID for this agent instance
65
+ let currentProjectId: string | null = null;
66
+
67
+ // Token usage tracking for this session (using token-tracking module)
68
+ let sessionTokenUsage: TokenUsage = createTokenUsage();
69
+
70
+ // Wrapper function to track token usage with the session's usage object
71
+ function trackTokenUsage(toolName: string, args: unknown, response: unknown): void {
72
+ trackTokens(sessionTokenUsage, toolName, args, response);
73
+ }
74
+
75
+ // Global rate limiter instance (60 requests per minute per API key)
76
+ const rateLimiter = new RateLimiter(60, 60000);
77
+
78
+ // Cleanup expired entries every 5 minutes (store interval ID for cleanup)
79
+ const rateLimiterCleanupInterval = setInterval(() => rateLimiter.cleanup(), 5 * 60 * 1000);
80
+
81
+ // Cleanup on process exit to prevent memory leaks
82
+ process.on('exit', () => clearInterval(rateLimiterCleanupInterval));
83
+ process.on('SIGINT', () => { clearInterval(rateLimiterCleanupInterval); process.exit(0); });
84
+ process.on('SIGTERM', () => { clearInterval(rateLimiterCleanupInterval); process.exit(0); });
85
+
86
+ // Build handler registry from modular handlers
87
+ const handlerRegistry = buildHandlerRegistry();
88
+
89
+ // ============================================================================
90
+ // Embedded Knowledge Base (on-demand help topics)
91
+ // ============================================================================
92
+
93
+ const KNOWLEDGE_BASE: Record<string, string> = {
94
+ getting_started: `# Getting Started
95
+ 1. Call start_work_session(git_url, model: "your-model-name") to initialize
96
+ - IMPORTANT: Pass your model for accurate cost tracking (e.g., "opus", "sonnet", "gemini", "gpt-4o")
97
+ - Check your system prompt for "You are powered by the model named..." to find it
98
+ 2. Response includes next_task - start working on it immediately
99
+ 3. Use update_task to mark in_progress and track progress
100
+ 4. Call complete_task when done - it returns your next task
101
+ 5. Use get_help(topic) when you need guidance on specific workflows`,
102
+
103
+ tasks: `# Task Workflow
104
+ - Mark task in_progress with update_task before starting
105
+ - Update progress_percentage regularly (every 15-20% progress)
106
+ - Include progress_note to auto-log milestones
107
+ - One task at a time - complete current before starting another
108
+ - complete_task returns next_task and context counts (validation, blockers, deployment)
109
+ - Priority: 1=highest, 5=lowest`,
110
+
111
+ validation: `# Task Validation
112
+ Completed tasks need validation before deployment. PRIORITIZE validation over new tasks.
113
+ 1. Check: get_tasks_awaiting_validation(project_id)
114
+ 2. Claim: claim_validation(task_id) - marks "being reviewed" on dashboard
115
+ 3. Review code changes and run tests
116
+ 4. Complete: validate_task(task_id, approved: true/false, validation_notes: "...")
117
+ Self-validation allowed when single-agent or to unblock deployment.`,
118
+
119
+ deployment: `# Deployment Workflow
120
+ 1. Ensure all completed tasks are validated first
121
+ 2. request_deployment(project_id, environment: "production")
122
+ 3. claim_deployment_validation(project_id) - claim for validation
123
+ 4. Run: pnpm build && pnpm test
124
+ 5. report_validation(project_id, build_passed: true, tests_passed: true)
125
+ 6. start_deployment(project_id) - returns project's deployment_instructions
126
+ 7. Follow the instructions (e.g., push to main, run deploy command)
127
+ 8. complete_deployment(project_id, success: true, summary: "...")`,
128
+
129
+ git: `# Git Workflow
130
+ Call get_git_workflow(project_id) for project-specific config.
131
+ Workflows: none, trunk-based, github-flow, git-flow
132
+ - trunk-based: commit directly to main
133
+ - github-flow: feature branches, merge via PR
134
+ - git-flow: develop/release branches
135
+ Update task git_branch when working on a branch.`,
136
+
137
+ blockers: `# Working with Blockers
138
+ When stuck and need human input:
139
+ 1. add_blocker(project_id, description: "What's blocking")
140
+ 2. Ask your question to the user
141
+ 3. resolve_blocker(blocker_id, resolution_note: "How resolved")
142
+ Only use for genuine blockers requiring decisions - not routine questions.`,
143
+
144
+ milestones: `# Task Milestones
145
+ For complex tasks, break into milestones:
146
+ 1. add_milestone(task_id, title: "Design schema")
147
+ 2. add_milestone(task_id, title: "Implement API")
148
+ 3. Update with complete_milestone(milestone_id) as you go
149
+ Dashboard shows progress bar with completed/total.`,
150
+
151
+ fallback: `# Fallback Activities
152
+ When no tasks available, get_next_task suggests activities:
153
+ - feature_ideation, code_review, performance_audit
154
+ - security_review, test_coverage, documentation_review
155
+ 1. start_fallback_activity(project_id, activity: "code_review")
156
+ 2. Do the work, use add_finding for issues, add_idea for improvements
157
+ 3. stop_fallback_activity(project_id, summary: "...")`,
158
+
159
+ session: `# Session Management
160
+ - start_work_session(git_url, model) initializes and returns next_task
161
+ - ALWAYS pass model parameter for cost tracking (e.g., "opus", "sonnet", "gemini", "gpt-4o")
162
+ - Use mode:'lite' (default) or mode:'full' for complete context
163
+ - heartbeat every 30-60 seconds maintains active status
164
+ - end_work_session releases claimed tasks and returns summary
165
+ - NEVER STOP: After completing a task, immediately start the next one
166
+ - When context grows large: /clear then start_work_session to continue fresh
167
+ - Your progress is saved to the dashboard - nothing is lost on /clear`,
168
+
169
+ tokens: `# Token Efficiency
170
+ Be mindful of token costs - every tool call has a cost.
171
+ - Use mode:'lite' (default) - saves ~85% vs full mode
172
+ - Call get_token_usage() to check consumption
173
+ - /compact when context grows large
174
+ - Batch related updates when possible
175
+ - Trust lite mode; only use full mode for initial exploration`,
176
+
177
+ topics: `# Available Help Topics
178
+ - getting_started: Basic workflow overview
179
+ - tasks: Working on tasks, progress tracking
180
+ - validation: Cross-agent task validation
181
+ - deployment: Deployment coordination
182
+ - git: Git workflow configuration
183
+ - blockers: Handling blockers
184
+ - milestones: Breaking down complex tasks
185
+ - fallback: Background activities when idle
186
+ - session: Session management
187
+ - tokens: Token efficiency tips`,
188
+ };
189
+
190
+
191
+ // ============================================================================
192
+ // Types
193
+ // ============================================================================
194
+
195
+ interface Database {
196
+ public: {
197
+ Tables: {
198
+ api_keys: {
199
+ Row: {
200
+ id: string;
201
+ user_id: string;
202
+ key_hash: string;
203
+ name: string;
204
+ last_used_at: string | null;
205
+ };
206
+ Insert: {
207
+ id?: string;
208
+ user_id: string;
209
+ key_hash: string;
210
+ name: string;
211
+ last_used_at?: string | null;
212
+ };
213
+ Update: {
214
+ id?: string;
215
+ user_id?: string;
216
+ key_hash?: string;
217
+ name?: string;
218
+ last_used_at?: string | null;
219
+ };
220
+ };
221
+ projects: {
222
+ Row: {
223
+ id: string;
224
+ user_id: string;
225
+ name: string;
226
+ description: string | null;
227
+ goal: string | null;
228
+ status: string;
229
+ git_url: string | null;
230
+ agent_instructions: string | null;
231
+ tech_stack: string[] | null;
232
+ git_workflow: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
233
+ git_main_branch: string;
234
+ git_develop_branch: string | null;
235
+ git_auto_branch: boolean;
236
+ git_auto_tag: boolean;
237
+ created_at: string;
238
+ updated_at: string;
239
+ };
240
+ Insert: {
241
+ id?: string;
242
+ user_id: string;
243
+ name: string;
244
+ description?: string | null;
245
+ goal?: string | null;
246
+ status?: string;
247
+ git_url?: string | null;
248
+ agent_instructions?: string | null;
249
+ tech_stack?: string[] | null;
250
+ git_workflow?: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
251
+ git_main_branch?: string;
252
+ git_develop_branch?: string | null;
253
+ git_auto_branch?: boolean;
254
+ git_auto_tag?: boolean;
255
+ created_at?: string;
256
+ updated_at?: string;
257
+ };
258
+ Update: {
259
+ id?: string;
260
+ user_id?: string;
261
+ name?: string;
262
+ description?: string | null;
263
+ goal?: string | null;
264
+ status?: string;
265
+ git_url?: string | null;
266
+ agent_instructions?: string | null;
267
+ tech_stack?: string[] | null;
268
+ git_workflow?: 'none' | 'trunk-based' | 'github-flow' | 'git-flow';
269
+ git_main_branch?: string;
270
+ git_develop_branch?: string | null;
271
+ git_auto_branch?: boolean;
272
+ git_auto_tag?: boolean;
273
+ created_at?: string;
274
+ updated_at?: string;
275
+ };
276
+ };
277
+ tasks: {
278
+ Row: {
279
+ id: string;
280
+ project_id: string;
281
+ title: string;
282
+ description: string | null;
283
+ priority: number;
284
+ status: string;
285
+ created_by: string;
286
+ created_at: string;
287
+ completed_at: string | null;
288
+ progress_percentage: number;
289
+ estimated_minutes: number | null;
290
+ started_at: string | null;
291
+ working_agent_session_id: string | null;
292
+ git_branch: string | null;
293
+ references: { url: string; label?: string }[] | null;
294
+ };
295
+ Insert: {
296
+ id?: string;
297
+ project_id: string;
298
+ title: string;
299
+ description?: string | null;
300
+ priority?: number;
301
+ status?: string;
302
+ created_by?: string;
303
+ created_at?: string;
304
+ completed_at?: string | null;
305
+ progress_percentage?: number;
306
+ estimated_minutes?: number | null;
307
+ started_at?: string | null;
308
+ working_agent_session_id?: string | null;
309
+ git_branch?: string | null;
310
+ references?: { url: string; label?: string }[] | null;
311
+ };
312
+ Update: {
313
+ id?: string;
314
+ project_id?: string;
315
+ title?: string;
316
+ description?: string | null;
317
+ priority?: number;
318
+ status?: string;
319
+ created_by?: string;
320
+ created_at?: string;
321
+ completed_at?: string | null;
322
+ progress_percentage?: number;
323
+ estimated_minutes?: number | null;
324
+ started_at?: string | null;
325
+ working_agent_session_id?: string | null;
326
+ git_branch?: string | null;
327
+ references?: { url: string; label?: string }[] | null;
328
+ };
329
+ };
330
+ progress_logs: {
331
+ Row: {
332
+ id: string;
333
+ project_id: string;
334
+ task_id: string | null;
335
+ summary: string;
336
+ details: string | null;
337
+ created_by: string;
338
+ created_at: string;
339
+ };
340
+ Insert: {
341
+ id?: string;
342
+ project_id: string;
343
+ task_id?: string | null;
344
+ summary: string;
345
+ details?: string | null;
346
+ created_by?: string;
347
+ created_at?: string;
348
+ };
349
+ Update: {
350
+ id?: string;
351
+ project_id?: string;
352
+ task_id?: string | null;
353
+ summary?: string;
354
+ details?: string | null;
355
+ created_by?: string;
356
+ created_at?: string;
357
+ };
358
+ };
359
+ blockers: {
360
+ Row: {
361
+ id: string;
362
+ project_id: string;
363
+ description: string;
364
+ status: string;
365
+ created_by: string;
366
+ created_at: string;
367
+ resolved_at: string | null;
368
+ resolution_note: string | null;
369
+ };
370
+ Insert: {
371
+ id?: string;
372
+ project_id: string;
373
+ description: string;
374
+ status?: string;
375
+ created_by?: string;
376
+ created_at?: string;
377
+ resolved_at?: string | null;
378
+ resolution_note?: string | null;
379
+ };
380
+ Update: {
381
+ id?: string;
382
+ project_id?: string;
383
+ description?: string;
384
+ status?: string;
385
+ created_by?: string;
386
+ created_at?: string;
387
+ resolved_at?: string | null;
388
+ resolution_note?: string | null;
389
+ };
390
+ };
391
+ ideas: {
392
+ Row: {
393
+ id: string;
394
+ project_id: string;
395
+ title: string;
396
+ description: string | null;
397
+ created_by: string;
398
+ created_at: string;
399
+ };
400
+ Insert: {
401
+ id?: string;
402
+ project_id: string;
403
+ title: string;
404
+ description?: string | null;
405
+ created_by?: string;
406
+ created_at?: string;
407
+ };
408
+ Update: {
409
+ id?: string;
410
+ project_id?: string;
411
+ title?: string;
412
+ description?: string | null;
413
+ created_by?: string;
414
+ created_at?: string;
415
+ };
416
+ };
417
+ decisions: {
418
+ Row: {
419
+ id: string;
420
+ project_id: string;
421
+ title: string;
422
+ description: string;
423
+ rationale: string | null;
424
+ alternatives_considered: string[] | null;
425
+ created_by: string;
426
+ created_at: string;
427
+ };
428
+ Insert: {
429
+ id?: string;
430
+ project_id: string;
431
+ title: string;
432
+ description: string;
433
+ rationale?: string | null;
434
+ alternatives_considered?: string[] | null;
435
+ created_by?: string;
436
+ created_at?: string;
437
+ };
438
+ Update: {
439
+ id?: string;
440
+ project_id?: string;
441
+ title?: string;
442
+ description?: string;
443
+ rationale?: string | null;
444
+ alternatives_considered?: string[] | null;
445
+ created_by?: string;
446
+ created_at?: string;
447
+ };
448
+ };
449
+ agent_sessions: {
450
+ Row: {
451
+ id: string;
452
+ api_key_id: string;
453
+ project_id: string;
454
+ last_synced_at: string;
455
+ agent_name: string | null;
456
+ agent_version: string | null;
457
+ instance_id: string | null;
458
+ current_task_id: string | null;
459
+ status: string;
460
+ };
461
+ Insert: {
462
+ id?: string;
463
+ api_key_id: string;
464
+ project_id: string;
465
+ last_synced_at?: string;
466
+ agent_name?: string | null;
467
+ agent_version?: string | null;
468
+ instance_id?: string | null;
469
+ current_task_id?: string | null;
470
+ status?: string;
471
+ };
472
+ Update: {
473
+ id?: string;
474
+ api_key_id?: string;
475
+ project_id?: string;
476
+ last_synced_at?: string;
477
+ agent_name?: string | null;
478
+ agent_version?: string | null;
479
+ instance_id?: string | null;
480
+ current_task_id?: string | null;
481
+ status?: string;
482
+ };
483
+ };
484
+ agent_heartbeats: {
485
+ Row: {
486
+ id: string;
487
+ session_id: string;
488
+ created_at: string;
489
+ };
490
+ Insert: {
491
+ id?: string;
492
+ session_id: string;
493
+ created_at?: string;
494
+ };
495
+ Update: {
496
+ id?: string;
497
+ session_id?: string;
498
+ created_at?: string;
499
+ };
500
+ };
501
+ task_milestones: {
502
+ Row: {
503
+ id: string;
504
+ task_id: string;
505
+ title: string;
506
+ description: string | null;
507
+ order_index: number;
508
+ status: string;
509
+ created_by: string;
510
+ created_at: string;
511
+ completed_at: string | null;
512
+ created_by_session_id: string | null;
513
+ };
514
+ Insert: {
515
+ id?: string;
516
+ task_id: string;
517
+ title: string;
518
+ description?: string | null;
519
+ order_index?: number;
520
+ status?: string;
521
+ created_by?: string;
522
+ created_at?: string;
523
+ completed_at?: string | null;
524
+ created_by_session_id?: string | null;
525
+ };
526
+ Update: {
527
+ id?: string;
528
+ task_id?: string;
529
+ title?: string;
530
+ description?: string | null;
531
+ order_index?: number;
532
+ status?: string;
533
+ created_by?: string;
534
+ created_at?: string;
535
+ completed_at?: string | null;
536
+ created_by_session_id?: string | null;
537
+ };
538
+ };
539
+ };
540
+ Views: Record<string, never>;
541
+ Functions: Record<string, never>;
542
+ Enums: Record<string, never>;
543
+ };
544
+ }
545
+
546
+ interface AuthContext {
547
+ userId: string;
548
+ apiKeyId: string;
549
+ organizationId?: string;
550
+ scope: 'personal' | 'organization';
551
+ }
552
+
553
+ interface UserUpdates {
554
+ tasks: Array<{ id: string; title: string; created_at: string }>;
555
+ blockers: Array<{ id: string; description: string; created_at: string }>;
556
+ ideas: Array<{ id: string; title: string; created_at: string }>;
557
+ }
558
+
559
+ // ============================================================================
560
+ // Configuration
561
+ // ============================================================================
562
+
563
+ const API_KEY = process.env.VIBESCOPE_API_KEY;
564
+
565
+ if (!API_KEY) {
566
+ console.error('Missing required environment variable: VIBESCOPE_API_KEY');
567
+ process.exit(1);
568
+ }
569
+
570
+ // Initialize the API client
571
+ initApiClient({ apiKey: API_KEY });
572
+
573
+ // ============================================================================
574
+ // Authentication
575
+ // ============================================================================
576
+
577
+ async function validateApiKey(): Promise<AuthContext | null> {
578
+ const apiClient = getApiClient();
579
+ const response = await apiClient.validateAuth();
580
+
581
+ if (!response.ok || !response.data?.valid) {
582
+ return null;
583
+ }
584
+
585
+ return {
586
+ userId: response.data.user_id,
587
+ apiKeyId: response.data.api_key_id,
588
+ scope: 'personal', // API handles authorization, scope not needed locally
589
+ };
590
+ }
591
+
592
+ // Tool definitions imported from tools.ts
593
+
594
+
595
+ // ============================================================================
596
+ // Tool Handlers
597
+ // ============================================================================
598
+
599
+ async function handleTool(
600
+ auth: AuthContext,
601
+ name: string,
602
+ args: Record<string, unknown>
603
+ ): Promise<{ result: unknown; user_updates?: UserUpdates; reminder?: string }> {
604
+ // Update session on every tool call via API:
605
+ // - last_synced_at: keeps session alive and visible as "active" on dashboard
606
+ // - token tracking: synced periodically for cost monitoring
607
+ // Skip for start_work_session since it handles its own session setup
608
+ if (currentSessionId && name !== 'start_work_session') {
609
+ const apiClient = getApiClient();
610
+ // Fire and forget - don't block tool execution on session sync
611
+ apiClient.syncSession(currentSessionId).catch(() => {
612
+ // Silently ignore sync errors - session tracking is non-critical
613
+ });
614
+ }
615
+
616
+ // Check if handler exists in the modular registry
617
+ const handler = handlerRegistry[name];
618
+ if (handler) {
619
+ // Build handler context
620
+ const ctx: HandlerContext = {
621
+ auth,
622
+ session: {
623
+ instanceId: INSTANCE_ID,
624
+ currentSessionId,
625
+ currentPersona,
626
+ currentRole,
627
+ currentProjectId,
628
+ tokenUsage: sessionTokenUsage,
629
+ },
630
+ updateSession: (updates) => {
631
+ if (updates.currentSessionId !== undefined) currentSessionId = updates.currentSessionId;
632
+ if (updates.currentPersona !== undefined) currentPersona = updates.currentPersona;
633
+ if (updates.currentRole !== undefined) currentRole = updates.currentRole;
634
+ if (updates.currentProjectId !== undefined) currentProjectId = updates.currentProjectId;
635
+ if (updates.tokenUsage !== undefined) sessionTokenUsage = updates.tokenUsage;
636
+ },
637
+ };
638
+
639
+ const handlerResult = await handler(args, ctx);
640
+ return handlerResult as { result: unknown; user_updates?: UserUpdates; reminder?: string };
641
+ }
642
+
643
+ throw new Error(`Unknown tool: ${name}`);
644
+ }
645
+
646
+ // ============================================================================
647
+ // Server Setup
648
+ // ============================================================================
649
+
650
+ async function main() {
651
+ // Validate API key on startup via API
652
+ const auth = await validateApiKey();
653
+ if (!auth) {
654
+ console.error('Invalid API key');
655
+ process.exit(1);
656
+ }
657
+
658
+ // Check for updates (non-blocking, with timeout)
659
+ const updateWarning = await getUpdateWarning();
660
+
661
+ const serverInstructions = updateWarning
662
+ ? `${updateWarning}\n\nVibescope MCP server - AI project tracking and coordination tools.`
663
+ : 'Vibescope MCP server - AI project tracking and coordination tools.';
664
+
665
+ const server = new Server(
666
+ {
667
+ name: 'vibescope',
668
+ version: '0.1.0',
669
+ },
670
+ {
671
+ capabilities: {
672
+ tools: {},
673
+ },
674
+ instructions: serverInstructions,
675
+ }
676
+ );
677
+
678
+ // List available tools
679
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
680
+ return { tools };
681
+ });
682
+
683
+ // Get reminder nudge - only for critical workflow reminders to save context
684
+ function getReminder(toolName: string): string | null {
685
+ // Most reminders removed to reduce context bloat - instructions are in CLAUDE.md
686
+ // Only keep critical continuation reminder for complete_task
687
+ const reminders: Record<string, string> = {
688
+ 'complete_task': 'CONTINUE WORKING: Call get_next_task or start the next_task. If awaiting_validation tasks exist, validate them FIRST before new work. If context is large, run /clear then start_work_session to refresh.',
689
+ 'batch_complete_tasks': 'CONTINUE WORKING: Call get_next_task. If awaiting_validation tasks exist, validate them FIRST before new work.',
690
+ };
691
+ return reminders[toolName] ?? null;
692
+ }
693
+
694
+ // Handle tool calls
695
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
696
+ const { name, arguments: args } = request.params;
697
+
698
+ // Check rate limit
699
+ const rateCheck = rateLimiter.check(auth.apiKeyId);
700
+ if (!rateCheck.allowed) {
701
+ const resetSeconds = Math.ceil(rateCheck.resetIn / 1000);
702
+ return {
703
+ content: [
704
+ {
705
+ type: 'text',
706
+ text: `Rate limit exceeded. Too many requests. Please wait ${resetSeconds} seconds before trying again. (Limit: 60 requests per minute)`,
707
+ },
708
+ ],
709
+ isError: true,
710
+ };
711
+ }
712
+
713
+ try {
714
+ const toolArgs = (args as Record<string, unknown>) || {};
715
+ const { result, user_updates } = await handleTool(
716
+ auth,
717
+ name,
718
+ toolArgs
719
+ );
720
+
721
+ // Track token usage for this call (both input args and output result)
722
+ trackTokenUsage(name, toolArgs, result);
723
+
724
+ const content: { type: 'text'; text: string }[] = [
725
+ {
726
+ type: 'text',
727
+ text: JSON.stringify(result, null, 2),
728
+ },
729
+ ];
730
+
731
+ // Include reminder nudge if applicable
732
+ const reminder = getReminder(name);
733
+ if (reminder) {
734
+ content.push({
735
+ type: 'text',
736
+ text: `\n--- ${reminder} ---`,
737
+ });
738
+ }
739
+
740
+ // Include user updates only if there are new items (null means no updates)
741
+ if (user_updates) {
742
+ content.push({
743
+ type: 'text',
744
+ text: `\n--- New User Updates ---\n${JSON.stringify(user_updates, null, 2)}`,
745
+ });
746
+ }
747
+
748
+ // Add rate limit warning if getting low
749
+ if (rateCheck.remaining < 10) {
750
+ content.push({
751
+ type: 'text',
752
+ text: `\n--- Rate Limit Warning: ${rateCheck.remaining} requests remaining this minute ---`,
753
+ });
754
+ }
755
+
756
+ return { content };
757
+ } catch (error) {
758
+ // Handle validation errors with structured output
759
+ if (error instanceof ValidationError) {
760
+ return {
761
+ content: [
762
+ {
763
+ type: 'text',
764
+ text: JSON.stringify(error.toJSON(), null, 2),
765
+ },
766
+ ],
767
+ isError: true,
768
+ };
769
+ }
770
+
771
+ // Handle database errors with better context
772
+ const errorMessage = getErrorMessage(error);
773
+ let hint: string | undefined;
774
+
775
+ if (errorMessage.includes('violates foreign key constraint')) {
776
+ hint = 'The referenced ID does not exist. Check that the project_id, task_id, or other IDs are correct.';
777
+ } else if (errorMessage.includes('duplicate key')) {
778
+ hint = 'A record with this identifier already exists.';
779
+ } else if (errorMessage.includes('not found') || errorMessage.includes('no rows')) {
780
+ hint = 'The requested resource was not found. Verify the ID is correct.';
781
+ }
782
+
783
+ return {
784
+ content: [
785
+ {
786
+ type: 'text',
787
+ text: JSON.stringify({
788
+ error: 'operation_failed',
789
+ message: errorMessage,
790
+ hint,
791
+ }, null, 2),
792
+ },
793
+ ],
794
+ isError: true,
795
+ };
796
+ }
797
+ });
798
+
799
+ // Start server
800
+ const transport = new StdioServerTransport();
801
+ await server.connect(transport);
802
+ }
803
+
804
+ main().catch((error) => {
805
+ console.error('Fatal error:', error);
806
+ process.exit(1);
807
+ });