@beaconcue/mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +194 -0
  2. package/package.json +43 -0
package/dist/index.js ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BeaconCue MCP Server
4
+ *
5
+ * Environment variables:
6
+ * BEACONCUE_API_KEY — your bck_... API key (from Settings → API Keys)
7
+ * BEACONCUE_BASE_URL — optional, defaults to https://beaconcue.com
8
+ *
9
+ * Usage (stdio transport — for Claude Desktop):
10
+ * node dist/index.js
11
+ */
12
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
13
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
+ import { z } from 'zod';
15
+ const API_KEY = process.env.BEACONCUE_API_KEY;
16
+ const BASE_URL = (process.env.BEACONCUE_BASE_URL || 'https://beaconcue.com').replace(/\/$/, '');
17
+ if (!API_KEY) {
18
+ console.error('BEACONCUE_API_KEY environment variable is required');
19
+ process.exit(1);
20
+ }
21
+ async function api(path, options = {}) {
22
+ const url = `${BASE_URL}/api/mcp${path}`;
23
+ const res = await fetch(url, {
24
+ ...options,
25
+ headers: {
26
+ 'Content-Type': 'application/json',
27
+ Authorization: `Bearer ${API_KEY}`,
28
+ ...(options.headers ?? {}),
29
+ },
30
+ });
31
+ const json = await res.json();
32
+ if (!res.ok)
33
+ throw new Error(json.error || `API error ${res.status}`);
34
+ return json;
35
+ }
36
+ const server = new McpServer({
37
+ name: 'beaconcue',
38
+ version: '1.0.0',
39
+ });
40
+ // ── Employees ─────────────────────────────────────────────────────────────────
41
+ server.tool('list_employees', 'List all employees in the BeaconCue workspace', {}, async () => {
42
+ const data = await api('/employees');
43
+ return { content: [{ type: 'text', text: JSON.stringify(data.employees, null, 2) }] };
44
+ });
45
+ server.tool('add_employee', 'Add a new employee to the workspace', {
46
+ name: z.string().describe('Full name'),
47
+ email: z.string().email().describe('Work email'),
48
+ role: z.string().optional().describe('Job title e.g. "Backend Engineer"'),
49
+ department: z.string().optional().describe('Department e.g. "Engineering"'),
50
+ location: z.string().optional().describe('Office location name'),
51
+ joining_date: z.string().optional().describe('ISO date e.g. 2026-07-01'),
52
+ manager_id: z.string().optional().describe('Employee ID of direct manager'),
53
+ }, async (args) => {
54
+ const data = await api('/employees', {
55
+ method: 'POST',
56
+ body: JSON.stringify(args),
57
+ });
58
+ return { content: [{ type: 'text', text: JSON.stringify(data.employee, null, 2) }] };
59
+ });
60
+ server.tool('update_employee', 'Update an existing employee record', {
61
+ id: z.string().describe('Employee ID'),
62
+ name: z.string().optional(),
63
+ role: z.string().optional().describe('Job title'),
64
+ department: z.string().optional(),
65
+ location: z.string().optional(),
66
+ status: z.enum(['active', 'suspended']).optional(),
67
+ joining_date: z.string().optional(),
68
+ manager_id: z.string().nullable().optional(),
69
+ }, async (args) => {
70
+ const data = await api('/employees', {
71
+ method: 'PATCH',
72
+ body: JSON.stringify(args),
73
+ });
74
+ return { content: [{ type: 'text', text: JSON.stringify(data.employee, null, 2) }] };
75
+ });
76
+ // ── Tickets ───────────────────────────────────────────────────────────────────
77
+ server.tool('list_tickets', 'List tickets in the workspace. Filter by status, assigneeId, or releaseId.', {
78
+ status: z.enum(['backlog', 'todo', 'in_progress', 'in_review', 'done']).optional(),
79
+ assigneeId: z.string().optional().describe('Employee ID to filter by assignee'),
80
+ releaseId: z.string().optional().describe('Release ID to filter by release'),
81
+ }, async (args) => {
82
+ const params = new URLSearchParams();
83
+ if (args.status)
84
+ params.set('status', args.status);
85
+ if (args.assigneeId)
86
+ params.set('assigneeId', args.assigneeId);
87
+ if (args.releaseId)
88
+ params.set('releaseId', args.releaseId);
89
+ const data = await api(`/tickets?${params}`);
90
+ return { content: [{ type: 'text', text: JSON.stringify(data.tickets, null, 2) }] };
91
+ });
92
+ server.tool('create_ticket', 'Create a new ticket', {
93
+ title: z.string().describe('Ticket title'),
94
+ status: z.enum(['backlog', 'todo', 'in_progress', 'in_review', 'done']).optional(),
95
+ priority: z.enum(['low', 'medium', 'high', 'urgent']).optional(),
96
+ ticket_type: z.enum(['story', 'feature', 'bug', 'customer_request']).optional(),
97
+ assignee_id: z.string().optional().describe('Employee ID'),
98
+ story_points: z.number().optional(),
99
+ release_id: z.string().optional(),
100
+ description: z.string().optional(),
101
+ }, async (args) => {
102
+ const data = await api('/tickets', {
103
+ method: 'POST',
104
+ body: JSON.stringify(args),
105
+ });
106
+ return { content: [{ type: 'text', text: JSON.stringify(data.ticket, null, 2) }] };
107
+ });
108
+ server.tool('update_ticket', 'Update a ticket (status, priority, assignee, etc.)', {
109
+ id: z.string().describe('Ticket ID'),
110
+ title: z.string().optional(),
111
+ status: z.enum(['backlog', 'todo', 'in_progress', 'in_review', 'done']).optional(),
112
+ priority: z.enum(['low', 'medium', 'high', 'urgent']).optional(),
113
+ ticket_type: z.enum(['story', 'feature', 'bug', 'customer_request']).optional(),
114
+ assignee_id: z.string().nullable().optional(),
115
+ story_points: z.number().nullable().optional(),
116
+ release_id: z.string().nullable().optional(),
117
+ }, async (args) => {
118
+ const data = await api('/tickets', {
119
+ method: 'PATCH',
120
+ body: JSON.stringify(args),
121
+ });
122
+ return { content: [{ type: 'text', text: JSON.stringify(data.ticket, null, 2) }] };
123
+ });
124
+ // ── OKRs ──────────────────────────────────────────────────────────────────────
125
+ server.tool('list_okrs', 'List all objectives and key results in the workspace', {}, async () => {
126
+ const data = await api('/okrs');
127
+ return {
128
+ content: [{
129
+ type: 'text',
130
+ text: JSON.stringify({ objectives: data.objectives, keyResults: data.keyResults }, null, 2),
131
+ }],
132
+ };
133
+ });
134
+ server.tool('update_kr_progress', 'Update the current value (progress) of a key result', {
135
+ id: z.string().describe('Key Result ID'),
136
+ current_value: z.number().describe('New current value'),
137
+ }, async (args) => {
138
+ const data = await api('/okrs', {
139
+ method: 'PATCH',
140
+ body: JSON.stringify(args),
141
+ });
142
+ return { content: [{ type: 'text', text: JSON.stringify(data.keyResult, null, 2) }] };
143
+ });
144
+ // ── Releases ──────────────────────────────────────────────────────────────────
145
+ server.tool('list_releases', 'List all releases in the workspace', {}, async () => {
146
+ const data = await api('/releases');
147
+ return { content: [{ type: 'text', text: JSON.stringify(data.releases, null, 2) }] };
148
+ });
149
+ // ── Members ───────────────────────────────────────────────────────────────────
150
+ server.tool('list_members', 'List workspace members with their system roles', {}, async () => {
151
+ const data = await api('/members');
152
+ return { content: [{ type: 'text', text: JSON.stringify(data.members, null, 2) }] };
153
+ });
154
+ // ── Recognition ───────────────────────────────────────────────────────────────
155
+ server.tool('list_recognitions', 'List recent recognitions (shout-outs)', {
156
+ limit: z.number().optional().describe('Max results, default 20'),
157
+ }, async (args) => {
158
+ const params = new URLSearchParams();
159
+ if (args.limit)
160
+ params.set('limit', String(args.limit));
161
+ const data = await api(`/recognition?${params}`);
162
+ return { content: [{ type: 'text', text: JSON.stringify(data.recognitions, null, 2) }] };
163
+ });
164
+ server.tool('send_recognition', 'Send a recognition (ecard) from one employee to another', {
165
+ from_employee_id: z.string().describe('Employee ID of the sender'),
166
+ to_employee_id: z.string().describe('Employee ID of the recipient'),
167
+ ecard_type: z.enum(['above_beyond', 'savior', 'fixer', 'team_player', 'achiever']),
168
+ message: z.string().optional().describe('Personal message'),
169
+ }, async (args) => {
170
+ const data = await api('/recognition', {
171
+ method: 'POST',
172
+ body: JSON.stringify(args),
173
+ });
174
+ return { content: [{ type: 'text', text: JSON.stringify(data.recognition, null, 2) }] };
175
+ });
176
+ // ── Time off ──────────────────────────────────────────────────────────────────
177
+ server.tool('list_timeoff', 'List leave/time-off requests', {
178
+ employeeId: z.string().optional().describe('Filter by employee ID'),
179
+ status: z.enum(['pending', 'approved', 'rejected', 'cancelled']).optional(),
180
+ year: z.string().optional().describe('4-digit year e.g. "2026"'),
181
+ }, async (args) => {
182
+ const params = new URLSearchParams();
183
+ if (args.employeeId)
184
+ params.set('employeeId', args.employeeId);
185
+ if (args.status)
186
+ params.set('status', args.status);
187
+ if (args.year)
188
+ params.set('year', args.year);
189
+ const data = await api(`/timeoff?${params}`);
190
+ return { content: [{ type: 'text', text: JSON.stringify(data.leaves, null, 2) }] };
191
+ });
192
+ // ── Start server ──────────────────────────────────────────────────────────────
193
+ const transport = new StdioServerTransport();
194
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@beaconcue/mcp",
3
+ "version": "1.0.0",
4
+ "description": "BeaconCue MCP server — control your BeaconCue workspace from Claude",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "beaconcue-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "engines": {
14
+ "node": ">=18"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc",
18
+ "start": "node dist/index.js",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "keywords": [
25
+ "beaconcue",
26
+ "mcp",
27
+ "model-context-protocol",
28
+ "claude",
29
+ "work-management",
30
+ "hr",
31
+ "okr"
32
+ ],
33
+ "author": "BeaconCue",
34
+ "license": "MIT",
35
+ "homepage": "https://beaconcue.com",
36
+ "dependencies": {
37
+ "@modelcontextprotocol/sdk": "^1.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "^5.0.0",
41
+ "@types/node": "^20.0.0"
42
+ }
43
+ }