@fruition/fcp-mcp-server 1.4.0 → 1.5.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.
- package/README.md +14 -1
- package/dist/index.js +321 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ MCP (Model Context Protocol) server that gives Claude Code direct access to the
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
### Tools
|
|
7
|
+
### FCP Launch Management Tools
|
|
8
8
|
|
|
9
9
|
| Tool | Description |
|
|
10
10
|
|------|-------------|
|
|
@@ -16,6 +16,19 @@ MCP (Model Context Protocol) server that gives Claude Code direct access to the
|
|
|
16
16
|
| `fcp_add_progress_note` | Add progress notes to document work done |
|
|
17
17
|
| `fcp_get_claude_md` | Generate CLAUDE.md content for a launch |
|
|
18
18
|
|
|
19
|
+
### Unroo Task Management Tools
|
|
20
|
+
|
|
21
|
+
| Tool | Description |
|
|
22
|
+
|------|-------------|
|
|
23
|
+
| `unroo_list_projects` | List all Unroo projects mapped to FCP clients |
|
|
24
|
+
| `unroo_list_tasks` | Query tasks with filters (status, project, assignee) |
|
|
25
|
+
| `unroo_create_task` | Create new tasks for discovered issues or follow-ups |
|
|
26
|
+
| `unroo_update_task` | Update task status, hours logged, priority |
|
|
27
|
+
| `unroo_get_my_tasks` | Get tasks assigned to current user |
|
|
28
|
+
| `unroo_start_session` | Start work session for time tracking |
|
|
29
|
+
| `unroo_end_session` | End work session and log time |
|
|
30
|
+
| `unroo_create_follow_up` | Create follow-up task linked to a parent |
|
|
31
|
+
|
|
19
32
|
### Resources
|
|
20
33
|
|
|
21
34
|
- `fcp://launches` - List of all launches
|
package/dist/index.js
CHANGED
|
@@ -37,8 +37,14 @@ import { execSync } from 'child_process';
|
|
|
37
37
|
// Configuration
|
|
38
38
|
const FCP_API_URL = process.env.FCP_API_URL || 'https://fcp.fru.io';
|
|
39
39
|
const FCP_API_TOKEN = process.env.FCP_API_TOKEN || '';
|
|
40
|
+
// Unroo API can be called directly (legacy) or proxied through FCP (recommended)
|
|
41
|
+
// When UNROO_API_KEY is not set, Unroo calls go through FCP's proxy at /api/mcp/unroo/*
|
|
40
42
|
const UNROO_API_URL = process.env.UNROO_API_URL || 'https://chat.frugpt.com';
|
|
41
43
|
const UNROO_API_KEY = process.env.UNROO_API_KEY || '';
|
|
44
|
+
// If no Unroo key, use FCP as proxy (unified key setup)
|
|
45
|
+
const USE_FCP_UNROO_PROXY = !UNROO_API_KEY;
|
|
46
|
+
// Helper to check if Unroo functionality is available (either mode)
|
|
47
|
+
const UNROO_AVAILABLE = UNROO_API_KEY || (USE_FCP_UNROO_PROXY && FCP_API_TOKEN);
|
|
42
48
|
let currentProject = null;
|
|
43
49
|
/**
|
|
44
50
|
* Detect the current git remote URL
|
|
@@ -119,11 +125,12 @@ async function initializeProjectDetection() {
|
|
|
119
125
|
class FCPClient {
|
|
120
126
|
baseUrl;
|
|
121
127
|
token;
|
|
128
|
+
defaultTimeout = 15000; // 15 seconds
|
|
122
129
|
constructor(baseUrl, token) {
|
|
123
130
|
this.baseUrl = baseUrl;
|
|
124
131
|
this.token = token;
|
|
125
132
|
}
|
|
126
|
-
async fetch(path, options = {}) {
|
|
133
|
+
async fetch(path, options = {}, timeoutMs) {
|
|
127
134
|
const url = `${this.baseUrl}${path}`;
|
|
128
135
|
const headers = {
|
|
129
136
|
'Content-Type': 'application/json',
|
|
@@ -142,6 +149,7 @@ class FCPClient {
|
|
|
142
149
|
const response = await fetch(url, {
|
|
143
150
|
...options,
|
|
144
151
|
headers,
|
|
152
|
+
signal: AbortSignal.timeout(timeoutMs || this.defaultTimeout),
|
|
145
153
|
});
|
|
146
154
|
if (!response.ok) {
|
|
147
155
|
const error = await response.text();
|
|
@@ -182,27 +190,61 @@ class FCPClient {
|
|
|
182
190
|
}
|
|
183
191
|
}
|
|
184
192
|
// Unroo API Client
|
|
193
|
+
// Supports two modes:
|
|
194
|
+
// 1. Direct: Uses UNROO_API_KEY to call Unroo directly (legacy)
|
|
195
|
+
// 2. Proxy: Routes through FCP at /api/mcp/unroo/* using FCP_API_TOKEN (recommended)
|
|
185
196
|
class UnrooClient {
|
|
186
197
|
baseUrl;
|
|
187
198
|
apiKey;
|
|
188
|
-
|
|
199
|
+
useProxy;
|
|
200
|
+
fcpUrl;
|
|
201
|
+
fcpToken;
|
|
202
|
+
defaultTimeout = 15000; // 15 seconds
|
|
203
|
+
constructor(baseUrl, apiKey, useProxy = false, fcpUrl = '', fcpToken = '') {
|
|
189
204
|
this.baseUrl = baseUrl;
|
|
190
205
|
this.apiKey = apiKey;
|
|
206
|
+
this.useProxy = useProxy;
|
|
207
|
+
this.fcpUrl = fcpUrl;
|
|
208
|
+
this.fcpToken = fcpToken;
|
|
209
|
+
if (this.useProxy) {
|
|
210
|
+
console.error('[UnrooClient] Using FCP proxy mode (unified key)');
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
console.error('[UnrooClient] Using direct Unroo API mode');
|
|
214
|
+
}
|
|
191
215
|
}
|
|
192
|
-
async fetch(path, options = {}) {
|
|
193
|
-
|
|
216
|
+
async fetch(path, options = {}, timeoutMs) {
|
|
217
|
+
let url;
|
|
194
218
|
const headers = {
|
|
195
219
|
'Content-Type': 'application/json',
|
|
196
|
-
'X-API-Key': this.apiKey,
|
|
197
220
|
...(options.headers || {}),
|
|
198
221
|
};
|
|
222
|
+
if (this.useProxy) {
|
|
223
|
+
// Route through FCP proxy: /api/external/fcp/X -> /api/mcp/unroo/X
|
|
224
|
+
const proxyPath = path.replace('/api/external/fcp/', '/api/mcp/unroo/');
|
|
225
|
+
url = `${this.fcpUrl}${proxyPath}`;
|
|
226
|
+
// Use FCP API token
|
|
227
|
+
if (this.fcpToken && this.fcpToken !== 'dev_bypass') {
|
|
228
|
+
headers['X-API-Key'] = this.fcpToken;
|
|
229
|
+
}
|
|
230
|
+
else if (this.fcpToken === 'dev_bypass') {
|
|
231
|
+
headers['X-Dev-Bypass'] = 'true';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Direct Unroo API call
|
|
236
|
+
url = `${this.baseUrl}${path}`;
|
|
237
|
+
headers['X-API-Key'] = this.apiKey;
|
|
238
|
+
}
|
|
199
239
|
const response = await fetch(url, {
|
|
200
240
|
...options,
|
|
201
241
|
headers,
|
|
242
|
+
signal: AbortSignal.timeout(timeoutMs || this.defaultTimeout),
|
|
202
243
|
});
|
|
203
244
|
if (!response.ok) {
|
|
204
245
|
const error = await response.text();
|
|
205
|
-
|
|
246
|
+
const source = this.useProxy ? 'FCP Proxy' : 'Unroo API';
|
|
247
|
+
throw new Error(`${source} error (${response.status}): ${error}`);
|
|
206
248
|
}
|
|
207
249
|
return response.json();
|
|
208
250
|
}
|
|
@@ -270,6 +312,51 @@ class UnrooClient {
|
|
|
270
312
|
}),
|
|
271
313
|
});
|
|
272
314
|
}
|
|
315
|
+
// ============================================================================
|
|
316
|
+
// Parking Lot / Backlog APIs
|
|
317
|
+
// ============================================================================
|
|
318
|
+
async logFutureWork(input) {
|
|
319
|
+
return this.fetch('/api/external/fcp/future-work', {
|
|
320
|
+
method: 'POST',
|
|
321
|
+
body: JSON.stringify(input),
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
async getFutureWork(filters) {
|
|
325
|
+
const params = new URLSearchParams();
|
|
326
|
+
if (filters.project_key)
|
|
327
|
+
params.set('project_key', filters.project_key);
|
|
328
|
+
if (filters.account_id)
|
|
329
|
+
params.set('account_id', filters.account_id);
|
|
330
|
+
if (filters.status)
|
|
331
|
+
params.set('status', filters.status);
|
|
332
|
+
if (filters.destination)
|
|
333
|
+
params.set('destination', filters.destination);
|
|
334
|
+
if (filters.limit)
|
|
335
|
+
params.set('limit', filters.limit.toString());
|
|
336
|
+
if (filters.offset)
|
|
337
|
+
params.set('offset', filters.offset.toString());
|
|
338
|
+
const query = params.toString();
|
|
339
|
+
return this.fetch(`/api/external/fcp/future-work${query ? `?${query}` : ''}`);
|
|
340
|
+
}
|
|
341
|
+
async updateFutureWork(id, updates) {
|
|
342
|
+
return this.fetch(`/api/external/fcp/future-work/${encodeURIComponent(id)}`, {
|
|
343
|
+
method: 'PUT',
|
|
344
|
+
body: JSON.stringify(updates),
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
async getBacklog(filters) {
|
|
348
|
+
const params = new URLSearchParams();
|
|
349
|
+
if (filters.project_key)
|
|
350
|
+
params.set('project_key', filters.project_key);
|
|
351
|
+
if (filters.priority)
|
|
352
|
+
params.set('priority', filters.priority);
|
|
353
|
+
if (filters.limit)
|
|
354
|
+
params.set('limit', filters.limit.toString());
|
|
355
|
+
if (filters.offset)
|
|
356
|
+
params.set('offset', filters.offset.toString());
|
|
357
|
+
const query = params.toString();
|
|
358
|
+
return this.fetch(`/api/external/fcp/backlog${query ? `?${query}` : ''}`);
|
|
359
|
+
}
|
|
273
360
|
}
|
|
274
361
|
// ============================================================================
|
|
275
362
|
// Auto Session Tracking
|
|
@@ -282,6 +369,8 @@ class SessionTracker {
|
|
|
282
369
|
activities = [];
|
|
283
370
|
heartbeatInterval = null;
|
|
284
371
|
currentTaskId = null;
|
|
372
|
+
consecutiveHeartbeatFailures = 0;
|
|
373
|
+
static MAX_HEARTBEAT_FAILURES = 3;
|
|
285
374
|
constructor(unrooClient) {
|
|
286
375
|
this.unrooClient = unrooClient;
|
|
287
376
|
}
|
|
@@ -300,7 +389,8 @@ class SessionTracker {
|
|
|
300
389
|
this.activities = this.activities.slice(-50);
|
|
301
390
|
}
|
|
302
391
|
// Auto-start session on first tool call
|
|
303
|
-
|
|
392
|
+
// Session tracking works with either direct Unroo key OR FCP proxy mode
|
|
393
|
+
if (!this.sessionActive && UNROO_AVAILABLE) {
|
|
304
394
|
await this.startSession();
|
|
305
395
|
}
|
|
306
396
|
// Send heartbeat every 30 tool calls or every 5 minutes
|
|
@@ -320,7 +410,7 @@ class SessionTracker {
|
|
|
320
410
|
* Start a new session
|
|
321
411
|
*/
|
|
322
412
|
async startSession() {
|
|
323
|
-
if (!
|
|
413
|
+
if (!UNROO_AVAILABLE) {
|
|
324
414
|
return;
|
|
325
415
|
}
|
|
326
416
|
try {
|
|
@@ -340,7 +430,12 @@ class SessionTracker {
|
|
|
340
430
|
this.lastHeartbeat = new Date();
|
|
341
431
|
// Set up periodic heartbeat (every 5 minutes)
|
|
342
432
|
this.heartbeatInterval = setInterval(() => {
|
|
343
|
-
this.sendHeartbeat().catch(() => {
|
|
433
|
+
this.sendHeartbeat().catch((err) => {
|
|
434
|
+
this.consecutiveHeartbeatFailures++;
|
|
435
|
+
if (this.consecutiveHeartbeatFailures >= SessionTracker.MAX_HEARTBEAT_FAILURES) {
|
|
436
|
+
console.error(`[SessionTracker] Heartbeat failed ${this.consecutiveHeartbeatFailures}x - session tracking may not work`);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
344
439
|
}, 5 * 60 * 1000);
|
|
345
440
|
if (currentProject) {
|
|
346
441
|
console.error(`[SessionTracker] Session started for project: ${currentProject.domain}`);
|
|
@@ -357,7 +452,7 @@ class SessionTracker {
|
|
|
357
452
|
* Send a heartbeat to keep session alive
|
|
358
453
|
*/
|
|
359
454
|
async sendHeartbeat() {
|
|
360
|
-
if (!this.sessionActive || !
|
|
455
|
+
if (!this.sessionActive || !UNROO_AVAILABLE) {
|
|
361
456
|
return;
|
|
362
457
|
}
|
|
363
458
|
try {
|
|
@@ -367,17 +462,20 @@ class SessionTracker {
|
|
|
367
462
|
});
|
|
368
463
|
this.lastHeartbeat = new Date();
|
|
369
464
|
this.toolCallCount = 0;
|
|
465
|
+
this.consecutiveHeartbeatFailures = 0; // Reset on success
|
|
370
466
|
console.error('[SessionTracker] Heartbeat sent');
|
|
371
467
|
}
|
|
372
468
|
catch (error) {
|
|
373
|
-
|
|
469
|
+
this.consecutiveHeartbeatFailures++;
|
|
470
|
+
console.error(`[SessionTracker] Failed to send heartbeat (attempt ${this.consecutiveHeartbeatFailures}):`, error);
|
|
471
|
+
throw error; // Re-throw for interval handler
|
|
374
472
|
}
|
|
375
473
|
}
|
|
376
474
|
/**
|
|
377
475
|
* End the current session and log activity summary to task
|
|
378
476
|
*/
|
|
379
477
|
async endSession() {
|
|
380
|
-
if (!this.sessionActive || !
|
|
478
|
+
if (!this.sessionActive || !UNROO_AVAILABLE) {
|
|
381
479
|
return;
|
|
382
480
|
}
|
|
383
481
|
if (this.heartbeatInterval) {
|
|
@@ -441,7 +539,7 @@ const server = new Server({
|
|
|
441
539
|
},
|
|
442
540
|
});
|
|
443
541
|
const client = new FCPClient(FCP_API_URL, FCP_API_TOKEN);
|
|
444
|
-
const unrooClient = new UnrooClient(UNROO_API_URL, UNROO_API_KEY);
|
|
542
|
+
const unrooClient = new UnrooClient(UNROO_API_URL, UNROO_API_KEY, USE_FCP_UNROO_PROXY, FCP_API_URL, FCP_API_TOKEN);
|
|
445
543
|
const sessionTracker = new SessionTracker(unrooClient);
|
|
446
544
|
// Tool definitions
|
|
447
545
|
const TOOLS = [
|
|
@@ -789,6 +887,126 @@ const TOOLS = [
|
|
|
789
887
|
required: ['parent_task_id', 'title'],
|
|
790
888
|
},
|
|
791
889
|
},
|
|
890
|
+
// Parking Lot / Backlog Tools
|
|
891
|
+
{
|
|
892
|
+
name: 'unroo_log_future_work',
|
|
893
|
+
description: 'Log future work items to parking lot or backlog. Use when you discover work that should be done later - bugs, tech debt, features, documentation needs, etc.',
|
|
894
|
+
inputSchema: {
|
|
895
|
+
type: 'object',
|
|
896
|
+
properties: {
|
|
897
|
+
title: {
|
|
898
|
+
type: 'string',
|
|
899
|
+
description: 'Title of the future work item (required)',
|
|
900
|
+
},
|
|
901
|
+
project_key: {
|
|
902
|
+
type: 'string',
|
|
903
|
+
description: 'JIRA project key or FCP-SITE-{id} (required)',
|
|
904
|
+
},
|
|
905
|
+
description: {
|
|
906
|
+
type: 'string',
|
|
907
|
+
description: 'Detailed description of the work needed',
|
|
908
|
+
},
|
|
909
|
+
priority: {
|
|
910
|
+
type: 'string',
|
|
911
|
+
enum: ['Urgent', 'High', 'Medium', 'Low'],
|
|
912
|
+
description: 'Priority level (default: Medium)',
|
|
913
|
+
},
|
|
914
|
+
task_type: {
|
|
915
|
+
type: 'string',
|
|
916
|
+
enum: ['bug', 'tech_debt', 'feature', 'documentation', 'security', 'performance'],
|
|
917
|
+
description: 'Type of work item',
|
|
918
|
+
},
|
|
919
|
+
estimated_hours: {
|
|
920
|
+
type: 'number',
|
|
921
|
+
description: 'Estimated hours to complete',
|
|
922
|
+
},
|
|
923
|
+
launch_id: {
|
|
924
|
+
type: 'number',
|
|
925
|
+
description: 'FCP launch ID if related to a launch',
|
|
926
|
+
},
|
|
927
|
+
checklist_item_id: {
|
|
928
|
+
type: 'number',
|
|
929
|
+
description: 'FCP checklist item ID if discovered during checklist work',
|
|
930
|
+
},
|
|
931
|
+
notes: {
|
|
932
|
+
type: 'string',
|
|
933
|
+
description: 'Additional notes or context',
|
|
934
|
+
},
|
|
935
|
+
destination: {
|
|
936
|
+
type: 'string',
|
|
937
|
+
enum: ['parking_lot', 'backlog'],
|
|
938
|
+
description: 'Where to put the item: parking_lot (needs review) or backlog (ready for sprint). Default: parking_lot',
|
|
939
|
+
},
|
|
940
|
+
},
|
|
941
|
+
required: ['title', 'project_key'],
|
|
942
|
+
},
|
|
943
|
+
},
|
|
944
|
+
{
|
|
945
|
+
name: 'unroo_get_parking_lot',
|
|
946
|
+
description: 'Get items from the parking lot - discovered work that needs review before being added to backlog.',
|
|
947
|
+
inputSchema: {
|
|
948
|
+
type: 'object',
|
|
949
|
+
properties: {
|
|
950
|
+
project_key: {
|
|
951
|
+
type: 'string',
|
|
952
|
+
description: 'Filter by JIRA project key or FCP-SITE-{id}',
|
|
953
|
+
},
|
|
954
|
+
status: {
|
|
955
|
+
type: 'string',
|
|
956
|
+
description: 'Filter by status: pending, approved, rejected, converted (default: pending)',
|
|
957
|
+
},
|
|
958
|
+
limit: {
|
|
959
|
+
type: 'number',
|
|
960
|
+
description: 'Maximum number of items to return (default: 100)',
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
name: 'unroo_get_backlog',
|
|
967
|
+
description: 'Get backlog items - tasks ready to be scheduled into sprints.',
|
|
968
|
+
inputSchema: {
|
|
969
|
+
type: 'object',
|
|
970
|
+
properties: {
|
|
971
|
+
project_key: {
|
|
972
|
+
type: 'string',
|
|
973
|
+
description: 'Filter by JIRA project key or FCP-SITE-{id}',
|
|
974
|
+
},
|
|
975
|
+
priority: {
|
|
976
|
+
type: 'string',
|
|
977
|
+
enum: ['Urgent', 'High', 'Medium', 'Low'],
|
|
978
|
+
description: 'Filter by priority',
|
|
979
|
+
},
|
|
980
|
+
limit: {
|
|
981
|
+
type: 'number',
|
|
982
|
+
description: 'Maximum number of items to return (default: 100)',
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
},
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
name: 'unroo_convert_to_backlog',
|
|
989
|
+
description: 'Convert a parking lot item to backlog. Use after reviewing a discovered item and deciding it should be done.',
|
|
990
|
+
inputSchema: {
|
|
991
|
+
type: 'object',
|
|
992
|
+
properties: {
|
|
993
|
+
id: {
|
|
994
|
+
type: 'string',
|
|
995
|
+
description: 'The ID of the parking lot item to convert (required)',
|
|
996
|
+
},
|
|
997
|
+
priority: {
|
|
998
|
+
type: 'string',
|
|
999
|
+
enum: ['Urgent', 'High', 'Medium', 'Low'],
|
|
1000
|
+
description: 'Priority for the backlog item (optional, keeps original if not specified)',
|
|
1001
|
+
},
|
|
1002
|
+
notes: {
|
|
1003
|
+
type: 'string',
|
|
1004
|
+
description: 'Notes about the conversion decision',
|
|
1005
|
+
},
|
|
1006
|
+
},
|
|
1007
|
+
required: ['id'],
|
|
1008
|
+
},
|
|
1009
|
+
},
|
|
792
1010
|
];
|
|
793
1011
|
// Register tool handlers
|
|
794
1012
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -1079,6 +1297,89 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1079
1297
|
],
|
|
1080
1298
|
};
|
|
1081
1299
|
}
|
|
1300
|
+
// Parking Lot / Backlog Handlers
|
|
1301
|
+
case 'unroo_log_future_work': {
|
|
1302
|
+
const input = args;
|
|
1303
|
+
const result = await unrooClient.logFutureWork({
|
|
1304
|
+
...input,
|
|
1305
|
+
discovered_by: 'claude-code-mcp',
|
|
1306
|
+
});
|
|
1307
|
+
return {
|
|
1308
|
+
content: [
|
|
1309
|
+
{
|
|
1310
|
+
type: 'text',
|
|
1311
|
+
text: JSON.stringify({
|
|
1312
|
+
success: true,
|
|
1313
|
+
message: result.message,
|
|
1314
|
+
id: result.id,
|
|
1315
|
+
destination: result.destination,
|
|
1316
|
+
}, null, 2),
|
|
1317
|
+
},
|
|
1318
|
+
],
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
case 'unroo_get_parking_lot': {
|
|
1322
|
+
const { project_key, status, limit } = args;
|
|
1323
|
+
const result = await unrooClient.getFutureWork({
|
|
1324
|
+
project_key,
|
|
1325
|
+
status: status || 'pending',
|
|
1326
|
+
destination: 'parking_lot',
|
|
1327
|
+
limit: limit || 100,
|
|
1328
|
+
});
|
|
1329
|
+
return {
|
|
1330
|
+
content: [
|
|
1331
|
+
{
|
|
1332
|
+
type: 'text',
|
|
1333
|
+
text: JSON.stringify({
|
|
1334
|
+
success: true,
|
|
1335
|
+
total: result.items.length,
|
|
1336
|
+
stats: result.stats,
|
|
1337
|
+
items: result.items,
|
|
1338
|
+
}, null, 2),
|
|
1339
|
+
},
|
|
1340
|
+
],
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
case 'unroo_get_backlog': {
|
|
1344
|
+
const { project_key, priority, limit } = args;
|
|
1345
|
+
const result = await unrooClient.getBacklog({
|
|
1346
|
+
project_key,
|
|
1347
|
+
priority,
|
|
1348
|
+
limit: limit || 100,
|
|
1349
|
+
});
|
|
1350
|
+
return {
|
|
1351
|
+
content: [
|
|
1352
|
+
{
|
|
1353
|
+
type: 'text',
|
|
1354
|
+
text: JSON.stringify({
|
|
1355
|
+
success: true,
|
|
1356
|
+
total: result.total,
|
|
1357
|
+
items: result.items,
|
|
1358
|
+
}, null, 2),
|
|
1359
|
+
},
|
|
1360
|
+
],
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
case 'unroo_convert_to_backlog': {
|
|
1364
|
+
const { id, priority, notes } = args;
|
|
1365
|
+
const result = await unrooClient.updateFutureWork(id, {
|
|
1366
|
+
convert_to_backlog: true,
|
|
1367
|
+
priority,
|
|
1368
|
+
notes,
|
|
1369
|
+
});
|
|
1370
|
+
return {
|
|
1371
|
+
content: [
|
|
1372
|
+
{
|
|
1373
|
+
type: 'text',
|
|
1374
|
+
text: JSON.stringify({
|
|
1375
|
+
success: true,
|
|
1376
|
+
message: `Parking lot item ${id} converted to backlog`,
|
|
1377
|
+
item: result.item,
|
|
1378
|
+
}, null, 2),
|
|
1379
|
+
},
|
|
1380
|
+
],
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1082
1383
|
default:
|
|
1083
1384
|
throw new Error(`Unknown tool: ${name}`);
|
|
1084
1385
|
}
|
|
@@ -1192,6 +1493,13 @@ async function main() {
|
|
|
1192
1493
|
const transport = new StdioServerTransport();
|
|
1193
1494
|
await server.connect(transport);
|
|
1194
1495
|
console.error(`FCP MCP Server v${MCP_SERVER_VERSION} running on stdio`);
|
|
1496
|
+
console.error(` FCP API: ${FCP_API_URL}`);
|
|
1497
|
+
if (USE_FCP_UNROO_PROXY) {
|
|
1498
|
+
console.error(' Unroo: via FCP proxy (unified key mode)');
|
|
1499
|
+
}
|
|
1500
|
+
else {
|
|
1501
|
+
console.error(` Unroo: direct (${UNROO_API_URL})`);
|
|
1502
|
+
}
|
|
1195
1503
|
// Auto-detect project from git remote (non-blocking)
|
|
1196
1504
|
initializeProjectDetection();
|
|
1197
1505
|
// Check for updates (non-blocking)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fruition/fcp-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "MCP Server for FCP Launch Coordination System - enables Claude Code to interact with FCP launches and track development time",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|