@vibescope/mcp-server 0.2.8 → 0.2.9

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.
package/dist/tools.js CHANGED
@@ -26,13 +26,11 @@ Use mode:'full' for complete context, mode:'lite' (default) for minimal tokens.`
26
26
  },
27
27
  model: {
28
28
  type: 'string',
29
- enum: ['opus', 'sonnet', 'haiku'],
30
- description: 'Claude model being used (for accurate cost tracking). E.g., "opus", "sonnet", "haiku".',
29
+ description: 'Model being used for cost tracking. E.g., "opus", "sonnet", "haiku" for Claude, "gemini" for Gemini, "gpt-4o" for OpenAI.',
31
30
  },
32
31
  role: {
33
32
  type: 'string',
34
- enum: ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'],
35
- description: 'Agent role: developer (general work, default), validator (task validation), deployer (deployments), reviewer (code review), maintainer (housekeeping)',
33
+ description: 'Agent role (e.g., "developer", "validator", "deployer", "reviewer", "maintainer"). Custom roles accepted.',
36
34
  },
37
35
  hostname: {
38
36
  type: 'string',
@@ -40,8 +38,7 @@ Use mode:'full' for complete context, mode:'lite' (default) for minimal tokens.`
40
38
  },
41
39
  agent_type: {
42
40
  type: 'string',
43
- enum: ['claude', 'gemini', 'cursor', 'windsurf', 'other'],
44
- description: 'Agent type for onboarding. When a new agent type connects to an existing project, setup instructions are returned.',
41
+ description: 'Agent type (e.g., "claude", "gemini", "cursor", "windsurf"). Custom agent types accepted.',
45
42
  },
46
43
  },
47
44
  },
@@ -60,8 +57,7 @@ This marks your agent type as fully onboarded to the project, so you won't recei
60
57
  },
61
58
  agent_type: {
62
59
  type: 'string',
63
- enum: ['claude', 'gemini', 'cursor', 'windsurf', 'other'],
64
- description: 'Agent type that completed setup',
60
+ description: 'Agent type that completed setup (e.g., "claude", "gemini", "cursor"). Custom agent types accepted.',
65
61
  },
66
62
  },
67
63
  required: ['project_id', 'agent_type'],
@@ -110,7 +106,6 @@ The Claude API response includes 'usage.input_tokens' and 'usage.output_tokens'
110
106
  },
111
107
  model: {
112
108
  type: 'string',
113
- enum: ['opus', 'sonnet', 'haiku'],
114
109
  description: 'Model used for this API call (optional, uses session model if not provided)',
115
110
  },
116
111
  },
@@ -2904,8 +2899,7 @@ Only returns tasks where all dependencies are completed.`,
2904
2899
  },
2905
2900
  role: {
2906
2901
  type: 'string',
2907
- enum: ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'],
2908
- description: 'Role to configure',
2902
+ description: 'Role to configure (e.g., "developer", "validator", "deployer"). Custom roles accepted.',
2909
2903
  },
2910
2904
  enabled: {
2911
2905
  type: 'boolean',
@@ -2949,8 +2943,7 @@ Only returns tasks where all dependencies are completed.`,
2949
2943
  properties: {
2950
2944
  role: {
2951
2945
  type: 'string',
2952
- enum: ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'],
2953
- description: 'Role to assign to this session',
2946
+ description: 'Role to assign to this session (e.g., "developer", "validator", "deployer"). Custom roles accepted.',
2954
2947
  },
2955
2948
  role_config: {
2956
2949
  type: 'object',
package/package.json CHANGED
@@ -1,51 +1,51 @@
1
- {
2
- "name": "@vibescope/mcp-server",
3
- "version": "0.2.8",
4
- "description": "MCP server for Vibescope - AI project tracking tools",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "bin": {
9
- "vibescope-mcp": "dist/index.js",
10
- "vibescope-cli": "dist/cli.js"
11
- },
12
- "repository": {
13
- "type": "git",
14
- "url": "https://github.com/Nonatomic/Vibescope.git",
15
- "directory": "packages/mcp-server"
16
- },
17
- "publishConfig": {
18
- "access": "public"
19
- },
20
- "scripts": {
21
- "build": "tsc",
22
- "dev": "tsc --watch",
23
- "test": "vitest run",
24
- "test:watch": "vitest",
25
- "docs:generate": "npx tsx scripts/generate-docs.ts --output=docs/TOOLS.md",
26
- "docs:preview": "npx tsx scripts/generate-docs.ts",
27
- "version:check": "npx tsx scripts/version-bump.ts --dry-run",
28
- "version:bump": "npx tsx scripts/version-bump.ts",
29
- "version:patch": "npx tsx scripts/version-bump.ts --patch",
30
- "version:minor": "npx tsx scripts/version-bump.ts --minor",
31
- "version:major": "npx tsx scripts/version-bump.ts --major",
32
- "prepublishOnly": "npm run build"
33
- },
34
- "keywords": [
35
- "mcp",
36
- "model-context-protocol",
37
- "vibescope",
38
- "ai",
39
- "project-management"
40
- ],
41
- "license": "MIT",
42
- "devDependencies": {
43
- "@types/node": "^22.10.0",
44
- "tsx": "^4.7.0",
45
- "typescript": "^5.7.0",
46
- "vitest": "^4.0.17"
47
- },
48
- "dependencies": {
49
- "@modelcontextprotocol/sdk": "^1.25.2"
50
- }
51
- }
1
+ {
2
+ "name": "@vibescope/mcp-server",
3
+ "version": "0.2.9",
4
+ "description": "MCP server for Vibescope - AI project tracking tools",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "vibescope-mcp": "dist/index.js",
10
+ "vibescope-cli": "dist/cli.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/Nonatomic/Vibescope.git",
15
+ "directory": "packages/mcp-server"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "dev": "tsc --watch",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "docs:generate": "npx tsx scripts/generate-docs.ts --output=docs/TOOLS.md",
26
+ "docs:preview": "npx tsx scripts/generate-docs.ts",
27
+ "version:check": "npx tsx scripts/version-bump.ts --dry-run",
28
+ "version:bump": "npx tsx scripts/version-bump.ts",
29
+ "version:patch": "npx tsx scripts/version-bump.ts --patch",
30
+ "version:minor": "npx tsx scripts/version-bump.ts --minor",
31
+ "version:major": "npx tsx scripts/version-bump.ts --major",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+ "keywords": [
35
+ "mcp",
36
+ "model-context-protocol",
37
+ "vibescope",
38
+ "ai",
39
+ "project-management"
40
+ ],
41
+ "license": "MIT",
42
+ "devDependencies": {
43
+ "@types/node": "^22.10.0",
44
+ "tsx": "^4.7.0",
45
+ "typescript": "^5.7.0",
46
+ "vitest": "^4.0.17"
47
+ },
48
+ "dependencies": {
49
+ "@modelcontextprotocol/sdk": "^1.25.2"
50
+ }
51
+ }
package/src/api-client.ts CHANGED
@@ -83,10 +83,10 @@ export class VibescopeApiClient {
83
83
  project_id?: string;
84
84
  git_url?: string;
85
85
  mode?: 'lite' | 'full';
86
- model?: 'opus' | 'sonnet' | 'haiku';
87
- role?: 'developer' | 'validator' | 'deployer' | 'reviewer' | 'maintainer';
86
+ model?: string; // Open-ended - any model name accepted
87
+ role?: string; // Open-ended - any role name accepted
88
88
  hostname?: string; // Machine hostname for worktree tracking
89
- agent_type?: 'claude' | 'gemini' | 'cursor' | 'windsurf' | 'other'; // Agent type for onboarding
89
+ agent_type?: string; // Open-ended - any agent type accepted
90
90
  }): Promise<ApiResponse<{
91
91
  session_started: boolean;
92
92
  session_id?: string;
@@ -1429,7 +1429,7 @@ export class VibescopeApiClient {
1429
1429
  async reportTokenUsage(sessionId: string, params: {
1430
1430
  input_tokens: number;
1431
1431
  output_tokens: number;
1432
- model?: 'opus' | 'sonnet' | 'haiku';
1432
+ model?: string; // Open-ended - any model name accepted
1433
1433
  }): Promise<ApiResponse<{
1434
1434
  success: boolean;
1435
1435
  reported: {
@@ -2467,7 +2467,7 @@ export class VibescopeApiClient {
2467
2467
  * Confirm that agent setup is complete for a project.
2468
2468
  * This marks the agent type as onboarded so future sessions don't receive setup instructions.
2469
2469
  */
2470
- async confirmAgentSetup(projectId: string, agentType: 'claude' | 'gemini' | 'cursor' | 'windsurf' | 'other'): Promise<ApiResponse<{
2470
+ async confirmAgentSetup(projectId: string, agentType: string): Promise<ApiResponse<{
2471
2471
  success: boolean;
2472
2472
  project_id: string;
2473
2473
  agent_type: string;
@@ -166,13 +166,15 @@ describe('setSessionRole', () => {
166
166
  });
167
167
  });
168
168
 
169
- it('should return error for invalid role', async () => {
169
+ it('should accept custom roles (open-ended)', async () => {
170
170
  const ctx = createMockContext();
171
171
 
172
- const result = await setSessionRole({ role: 'invalid_role' }, ctx);
172
+ const result = await setSessionRole({ role: 'custom_role' }, ctx);
173
173
 
174
+ // Roles are now open-ended - any role name is accepted
174
175
  expect(result.result).toMatchObject({
175
- error: expect.stringContaining('Invalid role: invalid_role'),
176
+ success: true,
177
+ role: 'custom_role',
176
178
  });
177
179
  });
178
180
 
@@ -116,14 +116,7 @@ export const setSessionRole: Handler = async (args, ctx) => {
116
116
  };
117
117
  }
118
118
 
119
- const validRoles: AgentRole[] = ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'];
120
- if (!validRoles.includes(role)) {
121
- return {
122
- result: {
123
- error: `Invalid role: ${role}. Must be one of: ${validRoles.join(', ')}`,
124
- },
125
- };
126
- }
119
+ // Role is now open-ended - any role name accepted
127
120
 
128
121
  // Update local session state
129
122
  updateSession({ currentRole: role });
@@ -14,29 +14,28 @@ import type { Handler, HandlerRegistry, TokenUsage } from './types.js';
14
14
  import { parseArgs, createEnumValidator } from '../validators.js';
15
15
  import { getApiClient } from '../api-client.js';
16
16
  import { getAgentGuidelinesTemplate, getAgentGuidelinesSummary } from '../templates/agent-guidelines.js';
17
+ import { getFallbackHelpContent, getAvailableHelpTopics } from '../templates/help-content.js';
17
18
 
18
19
  // Auto-detect machine hostname for worktree tracking
19
20
  const MACHINE_HOSTNAME = os.hostname();
20
21
 
21
22
  const VALID_MODES = ['lite', 'full'] as const;
22
- const VALID_MODELS = ['opus', 'sonnet', 'haiku'] as const;
23
- const VALID_ROLES = ['developer', 'validator', 'deployer', 'reviewer', 'maintainer'] as const;
24
- const VALID_AGENT_TYPES = ['claude', 'gemini', 'cursor', 'windsurf', 'other'] as const;
23
+ // Model, role, and agent_type are now open-ended - any string is accepted
25
24
 
26
25
  type SessionMode = typeof VALID_MODES[number];
27
- type SessionModel = typeof VALID_MODELS[number];
28
- type SessionRole = typeof VALID_ROLES[number];
29
- type AgentType = typeof VALID_AGENT_TYPES[number];
26
+ type SessionModel = string; // Open-ended - any model name accepted
27
+ type SessionRole = string; // Open-ended - any role name accepted
28
+ type AgentType = string; // Open-ended - any agent type accepted
30
29
 
31
30
  // Argument schemas for type-safe parsing
32
31
  const startWorkSessionSchema = {
33
32
  project_id: { type: 'string' as const },
34
33
  git_url: { type: 'string' as const },
35
34
  mode: { type: 'string' as const, default: 'lite', validate: createEnumValidator(VALID_MODES) },
36
- model: { type: 'string' as const, validate: createEnumValidator(VALID_MODELS) },
37
- role: { type: 'string' as const, default: 'developer', validate: createEnumValidator(VALID_ROLES) },
35
+ model: { type: 'string' as const }, // Open-ended - any model name accepted
36
+ role: { type: 'string' as const, default: 'developer' }, // Open-ended - any role name accepted
38
37
  hostname: { type: 'string' as const }, // Machine hostname for worktree tracking
39
- agent_type: { type: 'string' as const, validate: createEnumValidator(VALID_AGENT_TYPES) }, // Agent type for onboarding
38
+ agent_type: { type: 'string' as const }, // Open-ended - any agent type accepted
40
39
  };
41
40
 
42
41
  const heartbeatSchema = {
@@ -62,10 +61,8 @@ export const startWorkSession: Handler = async (args, ctx) => {
62
61
  const { session, updateSession } = ctx;
63
62
 
64
63
  // Reset token tracking for new session with model info
64
+ // Model is now open-ended - use as-is (normalize Claude model names for consistency)
65
65
  const normalizedModel = model ? model.toLowerCase().replace(/^claude[- ]*/i, '') : null;
66
- const validModel = normalizedModel && VALID_MODELS.includes(normalizedModel as SessionModel)
67
- ? normalizedModel
68
- : null;
69
66
 
70
67
  updateSession({
71
68
  tokenUsage: {
@@ -73,7 +70,7 @@ export const startWorkSession: Handler = async (args, ctx) => {
73
70
  totalTokens: 0,
74
71
  byTool: {},
75
72
  byModel: {},
76
- currentModel: validModel,
73
+ currentModel: normalizedModel,
77
74
  },
78
75
  });
79
76
 
@@ -396,31 +393,26 @@ export const getHelp: Handler = async (args, _ctx) => {
396
393
  const apiClient = getApiClient();
397
394
  const response = await apiClient.getHelpTopic(topic);
398
395
 
399
- if (!response.ok) {
400
- // If database fetch fails, return error
401
- return {
402
- result: {
403
- error: response.error || `Failed to fetch help topic: ${topic}`,
404
- },
405
- };
396
+ // Try database content first
397
+ if (response.ok && response.data?.content) {
398
+ return { result: { topic, content: response.data.content } };
406
399
  }
407
400
 
408
- if (!response.data) {
409
- // Topic not found - fetch available topics
410
- const topicsResponse = await apiClient.getHelpTopics();
411
- const available = topicsResponse.ok && topicsResponse.data
412
- ? topicsResponse.data.map(t => t.slug)
413
- : ['getting_started', 'tasks', 'validation', 'deployment', 'git', 'blockers', 'milestones', 'fallback', 'session', 'tokens', 'sprints', 'topics'];
414
-
415
- return {
416
- result: {
417
- error: `Unknown topic: ${topic}`,
418
- available,
419
- },
420
- };
401
+ // Fall back to local content if database is empty or unavailable
402
+ const fallback = getFallbackHelpContent(topic);
403
+ if (fallback) {
404
+ return { result: { topic, content: fallback.content } };
421
405
  }
422
406
 
423
- return { result: { topic, content: response.data.content } };
407
+ // Topic not found in either source - show available topics
408
+ const available = getAvailableHelpTopics();
409
+
410
+ return {
411
+ result: {
412
+ error: `Unknown topic: ${topic}`,
413
+ available,
414
+ },
415
+ };
424
416
  };
425
417
 
426
418
  // Model pricing rates (USD per 1M tokens) by pricing tier
@@ -572,12 +564,12 @@ export const getTokenUsage: Handler = async (_args, ctx) => {
572
564
  const reportTokenUsageSchema = {
573
565
  input_tokens: { type: 'number' as const, required: true as const },
574
566
  output_tokens: { type: 'number' as const, required: true as const },
575
- model: { type: 'string' as const, validate: createEnumValidator(VALID_MODELS) },
567
+ model: { type: 'string' as const }, // Open-ended - any model name accepted
576
568
  };
577
569
 
578
570
  const confirmAgentSetupSchema = {
579
571
  project_id: { type: 'string' as const, required: true as const },
580
- agent_type: { type: 'string' as const, required: true as const, validate: createEnumValidator(VALID_AGENT_TYPES) },
572
+ agent_type: { type: 'string' as const, required: true as const }, // Open-ended - any agent type accepted
581
573
  };
582
574
 
583
575
  /**
@@ -704,7 +696,7 @@ export const confirmAgentSetup: Handler = async (args, _ctx) => {
704
696
  }
705
697
 
706
698
  const apiClient = getApiClient();
707
- const response = await apiClient.confirmAgentSetup(project_id, agent_type as AgentType);
699
+ const response = await apiClient.confirmAgentSetup(project_id, agent_type);
708
700
 
709
701
  if (!response.ok) {
710
702
  return {
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { Handler, HandlerRegistry } from './types.js';
11
+ import { error, success } from './types.js';
11
12
  import { parseArgs, uuidValidator } from '../validators.js';
12
13
  import { getApiClient } from '../api-client.js';
13
14
 
@@ -34,10 +35,16 @@ export const getTasksAwaitingValidation: Handler = async (args, _ctx) => {
34
35
  const response = await apiClient.getTasksAwaitingValidation(project_id);
35
36
 
36
37
  if (!response.ok) {
37
- return { result: { error: response.error || 'Failed to fetch tasks awaiting validation' }, isError: true };
38
+ return error(response.error || 'Failed to fetch tasks awaiting validation');
38
39
  }
39
40
 
40
- return { result: response.data };
41
+ // Check for application-level errors (200 OK but error in body)
42
+ if (response.data && typeof response.data === 'object' && 'error' in response.data) {
43
+ const errMsg = typeof response.data.error === 'string' ? response.data.error : 'Unknown error';
44
+ return error(errMsg);
45
+ }
46
+
47
+ return success(response.data);
41
48
  };
42
49
 
43
50
  export const claimValidation: Handler = async (args, ctx) => {
@@ -50,10 +57,16 @@ export const claimValidation: Handler = async (args, ctx) => {
50
57
  const response = await apiClient.claimValidation(task_id, currentSessionId || undefined);
51
58
 
52
59
  if (!response.ok) {
53
- return { result: { error: response.error || 'Failed to claim task for validation' }, isError: true };
60
+ return error(response.error || 'Failed to claim task for validation');
61
+ }
62
+
63
+ // Check for application-level errors (200 OK but error in body)
64
+ if (response.data && typeof response.data === 'object' && 'error' in response.data) {
65
+ const errMsg = typeof response.data.error === 'string' ? response.data.error : 'Unknown error';
66
+ return error(errMsg);
54
67
  }
55
68
 
56
- return { result: response.data };
69
+ return success(response.data);
57
70
  };
58
71
 
59
72
  export const validateTask: Handler = async (args, ctx) => {
@@ -72,19 +85,22 @@ export const validateTask: Handler = async (args, ctx) => {
72
85
  if (!response.ok) {
73
86
  // Handle PR required error specially
74
87
  if (response.error === 'pr_required') {
75
- return {
76
- result: {
77
- error: 'pr_required',
78
- message: response.data?.message || 'A Pull Request is required before validation approval. Create a PR and add it via add_task_reference.',
79
- workflow: response.data?.workflow,
80
- action_required: 'Create a PR for this task and add it via add_task_reference(task_id, pr_url, label: "Pull Request")',
81
- },
82
- };
88
+ return error('pr_required', {
89
+ message: response.data?.message || 'A Pull Request is required before validation approval. Create a PR and add it via add_task_reference.',
90
+ workflow: response.data?.workflow,
91
+ action_required: 'Create a PR for this task and add it via add_task_reference(task_id, pr_url, label: "Pull Request")',
92
+ });
83
93
  }
84
- return { result: { error: response.error || 'Failed to validate task' }, isError: true };
94
+ return error(response.error || 'Failed to validate task');
95
+ }
96
+
97
+ // Check for application-level errors (200 OK but error in body)
98
+ if (response.data && typeof response.data === 'object' && 'error' in response.data) {
99
+ const errMsg = typeof response.data.error === 'string' ? response.data.error : 'Unknown error';
100
+ return error(errMsg);
85
101
  }
86
102
 
87
- return { result: response.data };
103
+ return success(response.data);
88
104
  };
89
105
 
90
106
  /**
package/src/index.ts CHANGED
@@ -87,8 +87,8 @@ const handlerRegistry = buildHandlerRegistry();
87
87
 
88
88
  const KNOWLEDGE_BASE: Record<string, string> = {
89
89
  getting_started: `# Getting Started
90
- 1. Call start_work_session(git_url, model: "opus"|"sonnet"|"haiku") to initialize
91
- - IMPORTANT: Pass your model for accurate cost tracking
90
+ 1. Call start_work_session(git_url, model: "your-model-name") to initialize
91
+ - IMPORTANT: Pass your model for accurate cost tracking (e.g., "opus", "sonnet", "gemini", "gpt-4o")
92
92
  - Check your system prompt for "You are powered by the model named..." to find it
93
93
  2. Response includes next_task - start working on it immediately
94
94
  3. Use update_task to mark in_progress and track progress
@@ -153,7 +153,7 @@ When no tasks available, get_next_task suggests activities:
153
153
 
154
154
  session: `# Session Management
155
155
  - start_work_session(git_url, model) initializes and returns next_task
156
- - ALWAYS pass model parameter ("opus", "sonnet", "haiku") for cost tracking
156
+ - ALWAYS pass model parameter for cost tracking (e.g., "opus", "sonnet", "gemini", "gpt-4o")
157
157
  - Use mode:'lite' (default) or mode:'full' for complete context
158
158
  - heartbeat every 30-60 seconds maintains active status
159
159
  - end_work_session releases claimed tasks and returns summary