@fruition/fcp-mcp-server 1.5.0 → 1.6.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/dist/index.js +1490 -94
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -45,6 +45,12 @@ const UNROO_API_KEY = process.env.UNROO_API_KEY || '';
|
|
|
45
45
|
const USE_FCP_UNROO_PROXY = !UNROO_API_KEY;
|
|
46
46
|
// Helper to check if Unroo functionality is available (either mode)
|
|
47
47
|
const UNROO_AVAILABLE = UNROO_API_KEY || (USE_FCP_UNROO_PROXY && FCP_API_TOKEN);
|
|
48
|
+
// Unique instance ID for this MCP server process
|
|
49
|
+
// Combines hostname + PID + startup timestamp to ensure uniqueness across:
|
|
50
|
+
// - Multiple machines (hostname)
|
|
51
|
+
// - Multiple processes on same machine (PID)
|
|
52
|
+
// - Process restarts (timestamp)
|
|
53
|
+
const INSTANCE_ID = `${process.env.HOSTNAME || 'local'}-${process.pid}-${Date.now()}`;
|
|
48
54
|
let currentProject = null;
|
|
49
55
|
/**
|
|
50
56
|
* Detect the current git remote URL
|
|
@@ -173,12 +179,48 @@ class FCPClient {
|
|
|
173
179
|
async getLaunch(id) {
|
|
174
180
|
return this.fetch(`/api/launches/${id}`);
|
|
175
181
|
}
|
|
182
|
+
async createChecklistItem(launchId, input) {
|
|
183
|
+
return this.fetch(`/api/launches/${launchId}/checklist`, {
|
|
184
|
+
method: 'POST',
|
|
185
|
+
body: JSON.stringify(input),
|
|
186
|
+
});
|
|
187
|
+
}
|
|
176
188
|
async updateChecklistItem(launchId, itemId, updates) {
|
|
177
189
|
return this.fetch(`/api/launches/${launchId}/checklist/${itemId}`, {
|
|
178
190
|
method: 'PUT',
|
|
179
191
|
body: JSON.stringify(updates),
|
|
180
192
|
});
|
|
181
193
|
}
|
|
194
|
+
async deleteChecklistItem(launchId, itemId) {
|
|
195
|
+
return this.fetch(`/api/launches/${launchId}/checklist/${itemId}`, {
|
|
196
|
+
method: 'DELETE',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
async createLaunch(input) {
|
|
200
|
+
return this.fetch('/api/launches', {
|
|
201
|
+
method: 'POST',
|
|
202
|
+
body: JSON.stringify(input),
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
async updateLaunch(id, updates) {
|
|
206
|
+
return this.fetch(`/api/launches/${id}`, {
|
|
207
|
+
method: 'PUT',
|
|
208
|
+
body: JSON.stringify(updates),
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
async deleteLaunch(id) {
|
|
212
|
+
return this.fetch(`/api/launches/${id}`, {
|
|
213
|
+
method: 'DELETE',
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async getSuccessFactors(launchId, itemId) {
|
|
217
|
+
return this.fetch(`/api/launches/${launchId}/checklist/${itemId}/success-factors`);
|
|
218
|
+
}
|
|
219
|
+
async validateChecklistItem(launchId, itemId) {
|
|
220
|
+
return this.fetch(`/api/launches/${launchId}/checklist/${itemId}/validate`, {
|
|
221
|
+
method: 'POST',
|
|
222
|
+
});
|
|
223
|
+
}
|
|
182
224
|
async getClaudeMd(launchId) {
|
|
183
225
|
return this.fetch(`/api/launches/${launchId}/claude-md`);
|
|
184
226
|
}
|
|
@@ -188,6 +230,105 @@ class FCPClient {
|
|
|
188
230
|
body: JSON.stringify({ content }),
|
|
189
231
|
});
|
|
190
232
|
}
|
|
233
|
+
// FileSync methods
|
|
234
|
+
async listFileSyncConfigs(filters) {
|
|
235
|
+
const params = new URLSearchParams();
|
|
236
|
+
if (filters?.enabled !== undefined)
|
|
237
|
+
params.set('enabled', String(filters.enabled));
|
|
238
|
+
if (filters?.sync_direction)
|
|
239
|
+
params.set('sync_direction', filters.sync_direction);
|
|
240
|
+
const query = params.toString();
|
|
241
|
+
return this.fetch(`/api/filesync/configs${query ? `?${query}` : ''}`);
|
|
242
|
+
}
|
|
243
|
+
async getFileSyncJobs(filters) {
|
|
244
|
+
const params = new URLSearchParams();
|
|
245
|
+
if (filters?.config_id)
|
|
246
|
+
params.set('config_id', String(filters.config_id));
|
|
247
|
+
if (filters?.status)
|
|
248
|
+
params.set('status', filters.status);
|
|
249
|
+
if (filters?.limit)
|
|
250
|
+
params.set('limit', String(filters.limit));
|
|
251
|
+
const query = params.toString();
|
|
252
|
+
return this.fetch(`/api/filesync/jobs${query ? `?${query}` : ''}`);
|
|
253
|
+
}
|
|
254
|
+
async startFileSync(input) {
|
|
255
|
+
return this.fetch('/api/filesync/start', {
|
|
256
|
+
method: 'POST',
|
|
257
|
+
body: JSON.stringify(input),
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
async cancelFileSync(jobId) {
|
|
261
|
+
return this.fetch(`/api/filesync/cancel/${encodeURIComponent(jobId)}`, {
|
|
262
|
+
method: 'POST',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
async getFileSyncConfirmation(configId) {
|
|
266
|
+
return this.fetch('/api/filesync/confirm', {
|
|
267
|
+
method: 'POST',
|
|
268
|
+
body: JSON.stringify({ config_id: configId }),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
async triggerNucleiScan(websiteId, options) {
|
|
272
|
+
return this.fetch(`/api/sites/${websiteId}/nuclei-scan`, {
|
|
273
|
+
method: 'POST',
|
|
274
|
+
body: JSON.stringify(options || {}),
|
|
275
|
+
}, 30000);
|
|
276
|
+
}
|
|
277
|
+
async getNucleiResults(websiteId, options) {
|
|
278
|
+
// If scan_id provided, get detailed results for that specific scan
|
|
279
|
+
if (options?.scan_id) {
|
|
280
|
+
return this.fetch(`/api/sites/${websiteId}/nuclei-scans/${options.scan_id}`);
|
|
281
|
+
}
|
|
282
|
+
// Otherwise, get scan history list
|
|
283
|
+
return this.fetch(`/api/sites/${websiteId}/nuclei-scans`);
|
|
284
|
+
}
|
|
285
|
+
// Site/Website CRUD Methods
|
|
286
|
+
async listSites(filters) {
|
|
287
|
+
const params = new URLSearchParams();
|
|
288
|
+
if (filters?.account_id)
|
|
289
|
+
params.set('account_id', filters.account_id.toString());
|
|
290
|
+
if (filters?.cms)
|
|
291
|
+
params.set('cms', filters.cms);
|
|
292
|
+
if (filters?.environment)
|
|
293
|
+
params.set('environment', filters.environment);
|
|
294
|
+
if (filters?.fru_hosted !== undefined)
|
|
295
|
+
params.set('fru_hosted', String(filters.fru_hosted));
|
|
296
|
+
if (filters?.retired)
|
|
297
|
+
params.set('retired', filters.retired);
|
|
298
|
+
if (filters?.limit)
|
|
299
|
+
params.set('limit', filters.limit.toString());
|
|
300
|
+
if (filters?.offset)
|
|
301
|
+
params.set('offset', filters.offset.toString());
|
|
302
|
+
const query = params.toString();
|
|
303
|
+
return this.fetch(`/api/sites${query ? `?${query}` : ''}`);
|
|
304
|
+
}
|
|
305
|
+
async searchSites(query, limit) {
|
|
306
|
+
const params = new URLSearchParams({ q: query });
|
|
307
|
+
if (limit)
|
|
308
|
+
params.set('limit', limit.toString());
|
|
309
|
+
return this.fetch(`/api/sites/search?${params.toString()}`);
|
|
310
|
+
}
|
|
311
|
+
async getSite(siteId) {
|
|
312
|
+
return this.fetch(`/api/sites/${siteId}`);
|
|
313
|
+
}
|
|
314
|
+
async createSite(production, staging) {
|
|
315
|
+
return this.fetch('/api/projects/websites/create-with-staging', {
|
|
316
|
+
method: 'POST',
|
|
317
|
+
body: JSON.stringify({ production, staging: staging || [] }),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
async updateSite(siteId, updates) {
|
|
321
|
+
return this.fetch(`/api/sites/${siteId}`, {
|
|
322
|
+
method: 'PUT',
|
|
323
|
+
body: JSON.stringify(updates),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
async deleteSite(siteId, options) {
|
|
327
|
+
return this.fetch(`/api/sites/${siteId}`, {
|
|
328
|
+
method: 'DELETE',
|
|
329
|
+
body: JSON.stringify(options || {}),
|
|
330
|
+
});
|
|
331
|
+
}
|
|
191
332
|
}
|
|
192
333
|
// Unroo API Client
|
|
193
334
|
// Supports two modes:
|
|
@@ -287,10 +428,10 @@ class UnrooClient {
|
|
|
287
428
|
body: JSON.stringify({ action: 'start', ...input }),
|
|
288
429
|
});
|
|
289
430
|
}
|
|
290
|
-
async endSession() {
|
|
431
|
+
async endSession(input) {
|
|
291
432
|
return this.fetch('/api/external/fcp/sessions', {
|
|
292
433
|
method: 'POST',
|
|
293
|
-
body: JSON.stringify({ action: 'end' }),
|
|
434
|
+
body: JSON.stringify({ action: 'end', ...input }),
|
|
294
435
|
});
|
|
295
436
|
}
|
|
296
437
|
async sessionHeartbeat(input) {
|
|
@@ -418,7 +559,7 @@ class SessionTracker {
|
|
|
418
559
|
const sessionInput = {
|
|
419
560
|
task_id: this.currentTaskId || undefined,
|
|
420
561
|
source: 'claude-code-mcp',
|
|
421
|
-
machine_id: process
|
|
562
|
+
machine_id: INSTANCE_ID, // Unique per MCP server process to prevent session collision
|
|
422
563
|
};
|
|
423
564
|
// Add auto-detected project info
|
|
424
565
|
if (currentProject) {
|
|
@@ -459,6 +600,8 @@ class SessionTracker {
|
|
|
459
600
|
await this.unrooClient.sessionHeartbeat({
|
|
460
601
|
tool_calls_delta: this.toolCallCount,
|
|
461
602
|
activity: this.activities.slice(-10), // Send last 10 activities
|
|
603
|
+
machine_id: INSTANCE_ID, // Required for session key lookup
|
|
604
|
+
repo_name: currentProject ? `${currentProject.github.owner}/${currentProject.github.repo}` : undefined,
|
|
462
605
|
});
|
|
463
606
|
this.lastHeartbeat = new Date();
|
|
464
607
|
this.toolCallCount = 0;
|
|
@@ -483,8 +626,11 @@ class SessionTracker {
|
|
|
483
626
|
this.heartbeatInterval = null;
|
|
484
627
|
}
|
|
485
628
|
try {
|
|
486
|
-
// End the session first
|
|
487
|
-
const result = await this.unrooClient.endSession(
|
|
629
|
+
// End the session first - include machine_id and repo_name for session key lookup
|
|
630
|
+
const result = await this.unrooClient.endSession({
|
|
631
|
+
machine_id: INSTANCE_ID,
|
|
632
|
+
repo_name: currentProject ? `${currentProject.github.owner}/${currentProject.github.repo}` : undefined,
|
|
633
|
+
});
|
|
488
634
|
this.sessionActive = false;
|
|
489
635
|
console.error('[SessionTracker] Session ended');
|
|
490
636
|
// Log activity summary to the task if we have a current task
|
|
@@ -618,9 +764,72 @@ const TOOLS = [
|
|
|
618
764
|
required: ['launch_id'],
|
|
619
765
|
},
|
|
620
766
|
},
|
|
767
|
+
{
|
|
768
|
+
name: 'fcp_add_checklist_item',
|
|
769
|
+
description: 'Add a new checklist item to a launch. Requires title and category at minimum.',
|
|
770
|
+
inputSchema: {
|
|
771
|
+
type: 'object',
|
|
772
|
+
properties: {
|
|
773
|
+
launch_id: {
|
|
774
|
+
type: 'number',
|
|
775
|
+
description: 'The ID of the launch',
|
|
776
|
+
},
|
|
777
|
+
title: {
|
|
778
|
+
type: 'string',
|
|
779
|
+
description: 'Title of the checklist item',
|
|
780
|
+
},
|
|
781
|
+
category: {
|
|
782
|
+
type: 'string',
|
|
783
|
+
enum: ['pre_launch', 'content', 'technical', 'seo', 'testing', 'dns', 'monitoring', 'post_launch'],
|
|
784
|
+
description: 'Category for the checklist item',
|
|
785
|
+
},
|
|
786
|
+
description: {
|
|
787
|
+
type: 'string',
|
|
788
|
+
description: 'Detailed description of the item',
|
|
789
|
+
},
|
|
790
|
+
role: {
|
|
791
|
+
type: 'string',
|
|
792
|
+
description: 'Team role responsible: webops, devops, dev, seo, client',
|
|
793
|
+
},
|
|
794
|
+
environment: {
|
|
795
|
+
type: 'string',
|
|
796
|
+
description: 'Environment: production, staging, both',
|
|
797
|
+
},
|
|
798
|
+
assigned_to_id: {
|
|
799
|
+
type: 'number',
|
|
800
|
+
description: 'User ID to assign the item to',
|
|
801
|
+
},
|
|
802
|
+
due_date: {
|
|
803
|
+
type: 'string',
|
|
804
|
+
description: 'Due date in ISO format',
|
|
805
|
+
},
|
|
806
|
+
sort_order: {
|
|
807
|
+
type: 'number',
|
|
808
|
+
description: 'Sort order within the category',
|
|
809
|
+
},
|
|
810
|
+
depends_on_item_id: {
|
|
811
|
+
type: 'number',
|
|
812
|
+
description: 'ID of another checklist item this depends on',
|
|
813
|
+
},
|
|
814
|
+
is_required: {
|
|
815
|
+
type: 'boolean',
|
|
816
|
+
description: 'Whether this item is required for launch (default: true)',
|
|
817
|
+
},
|
|
818
|
+
is_blocking: {
|
|
819
|
+
type: 'boolean',
|
|
820
|
+
description: 'Whether this item blocks the launch (default: false)',
|
|
821
|
+
},
|
|
822
|
+
external_link: {
|
|
823
|
+
type: 'string',
|
|
824
|
+
description: 'External URL related to this item',
|
|
825
|
+
},
|
|
826
|
+
},
|
|
827
|
+
required: ['launch_id', 'title', 'category'],
|
|
828
|
+
},
|
|
829
|
+
},
|
|
621
830
|
{
|
|
622
831
|
name: 'fcp_update_checklist_item',
|
|
623
|
-
description: 'Update
|
|
832
|
+
description: 'Update a checklist item. Can change status, title, description, category, assignment, and other fields.',
|
|
624
833
|
inputSchema: {
|
|
625
834
|
type: 'object',
|
|
626
835
|
properties: {
|
|
@@ -639,10 +848,85 @@ const TOOLS = [
|
|
|
639
848
|
},
|
|
640
849
|
notes: {
|
|
641
850
|
type: 'string',
|
|
642
|
-
description: '
|
|
851
|
+
description: 'Notes about the status change (alias for completion_notes)',
|
|
852
|
+
},
|
|
853
|
+
title: {
|
|
854
|
+
type: 'string',
|
|
855
|
+
description: 'Updated title',
|
|
856
|
+
},
|
|
857
|
+
description: {
|
|
858
|
+
type: 'string',
|
|
859
|
+
description: 'Updated description (null to clear)',
|
|
860
|
+
},
|
|
861
|
+
category: {
|
|
862
|
+
type: 'string',
|
|
863
|
+
enum: ['pre_launch', 'content', 'technical', 'seo', 'testing', 'dns', 'monitoring', 'post_launch'],
|
|
864
|
+
description: 'Updated category',
|
|
865
|
+
},
|
|
866
|
+
role: {
|
|
867
|
+
type: 'string',
|
|
868
|
+
description: 'Updated team role: webops, devops, dev, seo, client',
|
|
869
|
+
},
|
|
870
|
+
environment: {
|
|
871
|
+
type: 'string',
|
|
872
|
+
description: 'Updated environment: production, staging, both',
|
|
873
|
+
},
|
|
874
|
+
assigned_to_id: {
|
|
875
|
+
type: 'number',
|
|
876
|
+
description: 'User ID to assign to (null to unassign)',
|
|
877
|
+
},
|
|
878
|
+
due_date: {
|
|
879
|
+
type: 'string',
|
|
880
|
+
description: 'Updated due date in ISO format (null to clear)',
|
|
881
|
+
},
|
|
882
|
+
sort_order: {
|
|
883
|
+
type: 'number',
|
|
884
|
+
description: 'Updated sort order',
|
|
885
|
+
},
|
|
886
|
+
depends_on_item_id: {
|
|
887
|
+
type: 'number',
|
|
888
|
+
description: 'Updated dependency item ID (null to clear)',
|
|
889
|
+
},
|
|
890
|
+
is_required: {
|
|
891
|
+
type: 'boolean',
|
|
892
|
+
description: 'Whether this item is required for launch',
|
|
893
|
+
},
|
|
894
|
+
is_blocking: {
|
|
895
|
+
type: 'boolean',
|
|
896
|
+
description: 'Whether this item blocks the launch',
|
|
897
|
+
},
|
|
898
|
+
external_link: {
|
|
899
|
+
type: 'string',
|
|
900
|
+
description: 'External URL (null to clear)',
|
|
901
|
+
},
|
|
902
|
+
evidence_url: {
|
|
903
|
+
type: 'string',
|
|
904
|
+
description: 'URL to evidence of completion (null to clear)',
|
|
905
|
+
},
|
|
906
|
+
jira_ticket_key: {
|
|
907
|
+
type: 'string',
|
|
908
|
+
description: 'Associated Jira ticket key (null to clear)',
|
|
909
|
+
},
|
|
910
|
+
},
|
|
911
|
+
required: ['launch_id', 'item_id'],
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
name: 'fcp_delete_checklist_item',
|
|
916
|
+
description: 'Delete a checklist item from a launch. This action cannot be undone.',
|
|
917
|
+
inputSchema: {
|
|
918
|
+
type: 'object',
|
|
919
|
+
properties: {
|
|
920
|
+
launch_id: {
|
|
921
|
+
type: 'number',
|
|
922
|
+
description: 'The ID of the launch',
|
|
923
|
+
},
|
|
924
|
+
item_id: {
|
|
925
|
+
type: 'number',
|
|
926
|
+
description: 'The ID of the checklist item to delete',
|
|
643
927
|
},
|
|
644
928
|
},
|
|
645
|
-
required: ['launch_id', 'item_id'
|
|
929
|
+
required: ['launch_id', 'item_id'],
|
|
646
930
|
},
|
|
647
931
|
},
|
|
648
932
|
{
|
|
@@ -677,109 +961,109 @@ const TOOLS = [
|
|
|
677
961
|
required: ['launch_id'],
|
|
678
962
|
},
|
|
679
963
|
},
|
|
680
|
-
//
|
|
681
|
-
{
|
|
682
|
-
name: 'unroo_list_projects',
|
|
683
|
-
description: 'List all Unroo projects mapped to FCP clients. Returns project mappings with organization and JIRA key info.',
|
|
684
|
-
inputSchema: {
|
|
685
|
-
type: 'object',
|
|
686
|
-
properties: {},
|
|
687
|
-
},
|
|
688
|
-
},
|
|
964
|
+
// Launch CRUD Tools
|
|
689
965
|
{
|
|
690
|
-
name: '
|
|
691
|
-
description: '
|
|
966
|
+
name: 'fcp_create_launch',
|
|
967
|
+
description: 'Create a new launch in FCP. Requires name, platform, launch_type, and target_launch_date. Optionally creates a default checklist.',
|
|
692
968
|
inputSchema: {
|
|
693
969
|
type: 'object',
|
|
694
970
|
properties: {
|
|
695
|
-
|
|
971
|
+
name: {
|
|
696
972
|
type: 'string',
|
|
697
|
-
description: '
|
|
973
|
+
description: 'Launch name (required)',
|
|
698
974
|
},
|
|
699
|
-
|
|
975
|
+
platform: {
|
|
700
976
|
type: 'string',
|
|
701
|
-
|
|
977
|
+
enum: ['wordpress', 'drupal', 'nextjs', 'other'],
|
|
978
|
+
description: 'Platform type (required)',
|
|
702
979
|
},
|
|
703
|
-
|
|
980
|
+
launch_type: {
|
|
704
981
|
type: 'string',
|
|
705
|
-
|
|
982
|
+
enum: ['new_site', 'migration', 'redesign', 'replatform', 'hosting_only', 'maintenance'],
|
|
983
|
+
description: 'Type of launch (required)',
|
|
706
984
|
},
|
|
707
|
-
|
|
985
|
+
target_launch_date: {
|
|
708
986
|
type: 'string',
|
|
709
|
-
description: '
|
|
987
|
+
description: 'Target launch date in ISO format (required)',
|
|
710
988
|
},
|
|
711
|
-
|
|
989
|
+
website_id: {
|
|
712
990
|
type: 'number',
|
|
713
|
-
description: '
|
|
991
|
+
description: 'Associated website ID',
|
|
714
992
|
},
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
{
|
|
719
|
-
name: 'unroo_create_task',
|
|
720
|
-
description: 'Create a new task in Unroo. Requires title and project_key. Use for follow-up tasks, discovered issues, or new work items.',
|
|
721
|
-
inputSchema: {
|
|
722
|
-
type: 'object',
|
|
723
|
-
properties: {
|
|
724
|
-
title: {
|
|
725
|
-
type: 'string',
|
|
726
|
-
description: 'Task title (required)',
|
|
993
|
+
account_id: {
|
|
994
|
+
type: 'number',
|
|
995
|
+
description: 'Associated account/client ID',
|
|
727
996
|
},
|
|
728
997
|
description: {
|
|
729
998
|
type: 'string',
|
|
730
|
-
description: '
|
|
999
|
+
description: 'Launch description',
|
|
731
1000
|
},
|
|
732
|
-
|
|
1001
|
+
soft_launch_date: {
|
|
733
1002
|
type: 'string',
|
|
734
|
-
description: '
|
|
1003
|
+
description: 'Soft launch date in ISO format',
|
|
735
1004
|
},
|
|
736
|
-
|
|
1005
|
+
kickoff_date: {
|
|
737
1006
|
type: 'string',
|
|
738
|
-
|
|
739
|
-
description: 'Task priority (default: medium)',
|
|
1007
|
+
description: 'Kickoff date in ISO format',
|
|
740
1008
|
},
|
|
741
|
-
|
|
1009
|
+
priority: {
|
|
742
1010
|
type: 'string',
|
|
743
|
-
|
|
1011
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
1012
|
+
description: 'Launch priority (default: medium)',
|
|
744
1013
|
},
|
|
745
|
-
|
|
1014
|
+
jira_project_key: {
|
|
746
1015
|
type: 'string',
|
|
747
|
-
description: '
|
|
1016
|
+
description: 'Jira project key',
|
|
748
1017
|
},
|
|
749
|
-
|
|
750
|
-
type: '
|
|
751
|
-
description: '
|
|
1018
|
+
unroo_project_id: {
|
|
1019
|
+
type: 'number',
|
|
1020
|
+
description: 'Unroo project ID to link',
|
|
752
1021
|
},
|
|
753
|
-
|
|
1022
|
+
webops_lead_id: {
|
|
754
1023
|
type: 'number',
|
|
755
|
-
description: '
|
|
1024
|
+
description: 'WebOps lead user ID',
|
|
756
1025
|
},
|
|
757
|
-
|
|
758
|
-
type: '
|
|
759
|
-
|
|
760
|
-
description: 'Labels/tags for the task',
|
|
1026
|
+
devops_lead_id: {
|
|
1027
|
+
type: 'number',
|
|
1028
|
+
description: 'DevOps lead user ID',
|
|
761
1029
|
},
|
|
762
|
-
|
|
1030
|
+
dev_lead_id: {
|
|
1031
|
+
type: 'number',
|
|
1032
|
+
description: 'Dev lead user ID',
|
|
1033
|
+
},
|
|
1034
|
+
seo_lead_id: {
|
|
1035
|
+
type: 'number',
|
|
1036
|
+
description: 'SEO lead user ID',
|
|
1037
|
+
},
|
|
1038
|
+
client_contact: {
|
|
763
1039
|
type: 'string',
|
|
764
|
-
description: '
|
|
1040
|
+
description: 'Client contact name',
|
|
1041
|
+
},
|
|
1042
|
+
client_contact_email: {
|
|
1043
|
+
type: 'string',
|
|
1044
|
+
description: 'Client contact email',
|
|
1045
|
+
},
|
|
1046
|
+
use_default_checklist: {
|
|
1047
|
+
type: 'boolean',
|
|
1048
|
+
description: 'Whether to create default checklist items (default: true)',
|
|
765
1049
|
},
|
|
766
1050
|
},
|
|
767
|
-
required: ['
|
|
1051
|
+
required: ['name', 'platform', 'launch_type', 'target_launch_date'],
|
|
768
1052
|
},
|
|
769
1053
|
},
|
|
770
1054
|
{
|
|
771
|
-
name: '
|
|
772
|
-
description: 'Update an existing
|
|
1055
|
+
name: 'fcp_update_launch',
|
|
1056
|
+
description: 'Update an existing launch. Only provided fields are updated. Can change status, dates, assignments, and other properties.',
|
|
773
1057
|
inputSchema: {
|
|
774
1058
|
type: 'object',
|
|
775
1059
|
properties: {
|
|
776
|
-
|
|
777
|
-
type: '
|
|
778
|
-
description: 'The ID of the
|
|
1060
|
+
launch_id: {
|
|
1061
|
+
type: 'number',
|
|
1062
|
+
description: 'The ID of the launch to update (required)',
|
|
779
1063
|
},
|
|
780
|
-
|
|
1064
|
+
name: {
|
|
781
1065
|
type: 'string',
|
|
782
|
-
description: 'Updated
|
|
1066
|
+
description: 'Updated launch name',
|
|
783
1067
|
},
|
|
784
1068
|
description: {
|
|
785
1069
|
type: 'string',
|
|
@@ -787,31 +1071,293 @@ const TOOLS = [
|
|
|
787
1071
|
},
|
|
788
1072
|
status: {
|
|
789
1073
|
type: 'string',
|
|
790
|
-
|
|
1074
|
+
enum: ['planning', 'in_progress', 'soft_launched', 'launched', 'on_hold', 'cancelled'],
|
|
1075
|
+
description: 'Updated status',
|
|
1076
|
+
},
|
|
1077
|
+
platform: {
|
|
1078
|
+
type: 'string',
|
|
1079
|
+
enum: ['wordpress', 'drupal', 'nextjs', 'other'],
|
|
1080
|
+
description: 'Updated platform',
|
|
1081
|
+
},
|
|
1082
|
+
launch_type: {
|
|
1083
|
+
type: 'string',
|
|
1084
|
+
enum: ['new_site', 'migration', 'redesign', 'replatform', 'hosting_only', 'maintenance'],
|
|
1085
|
+
description: 'Updated launch type',
|
|
1086
|
+
},
|
|
1087
|
+
target_launch_date: {
|
|
1088
|
+
type: 'string',
|
|
1089
|
+
description: 'Updated target launch date',
|
|
1090
|
+
},
|
|
1091
|
+
soft_launch_date: {
|
|
1092
|
+
type: 'string',
|
|
1093
|
+
description: 'Updated soft launch date (null to clear)',
|
|
1094
|
+
},
|
|
1095
|
+
actual_launch_date: {
|
|
1096
|
+
type: 'string',
|
|
1097
|
+
description: 'Actual launch date when launched',
|
|
1098
|
+
},
|
|
1099
|
+
kickoff_date: {
|
|
1100
|
+
type: 'string',
|
|
1101
|
+
description: 'Updated kickoff date (null to clear)',
|
|
791
1102
|
},
|
|
792
1103
|
priority: {
|
|
793
1104
|
type: 'string',
|
|
794
|
-
enum: ['
|
|
1105
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
795
1106
|
description: 'Updated priority',
|
|
796
1107
|
},
|
|
797
|
-
|
|
1108
|
+
jira_project_key: {
|
|
798
1109
|
type: 'string',
|
|
799
|
-
description: '
|
|
1110
|
+
description: 'Jira project key (null to clear)',
|
|
800
1111
|
},
|
|
801
|
-
|
|
1112
|
+
unroo_project_id: {
|
|
802
1113
|
type: 'number',
|
|
803
|
-
description: '
|
|
1114
|
+
description: 'Unroo project ID (null to unlink)',
|
|
804
1115
|
},
|
|
805
|
-
|
|
1116
|
+
webops_lead_id: {
|
|
1117
|
+
type: 'number',
|
|
1118
|
+
description: 'WebOps lead user ID (null to clear)',
|
|
1119
|
+
},
|
|
1120
|
+
devops_lead_id: {
|
|
1121
|
+
type: 'number',
|
|
1122
|
+
description: 'DevOps lead user ID (null to clear)',
|
|
1123
|
+
},
|
|
1124
|
+
dev_lead_id: {
|
|
1125
|
+
type: 'number',
|
|
1126
|
+
description: 'Dev lead user ID (null to clear)',
|
|
1127
|
+
},
|
|
1128
|
+
seo_lead_id: {
|
|
1129
|
+
type: 'number',
|
|
1130
|
+
description: 'SEO lead user ID (null to clear)',
|
|
1131
|
+
},
|
|
1132
|
+
client_contact: {
|
|
806
1133
|
type: 'string',
|
|
807
|
-
description: '
|
|
1134
|
+
description: 'Client contact name (null to clear)',
|
|
1135
|
+
},
|
|
1136
|
+
client_contact_email: {
|
|
1137
|
+
type: 'string',
|
|
1138
|
+
description: 'Client contact email (null to clear)',
|
|
808
1139
|
},
|
|
809
1140
|
},
|
|
810
|
-
required: ['
|
|
1141
|
+
required: ['launch_id'],
|
|
811
1142
|
},
|
|
812
1143
|
},
|
|
813
1144
|
{
|
|
814
|
-
name: '
|
|
1145
|
+
name: 'fcp_delete_launch',
|
|
1146
|
+
description: 'Delete a launch and all associated checklist items. This action cannot be undone.',
|
|
1147
|
+
inputSchema: {
|
|
1148
|
+
type: 'object',
|
|
1149
|
+
properties: {
|
|
1150
|
+
launch_id: {
|
|
1151
|
+
type: 'number',
|
|
1152
|
+
description: 'The ID of the launch to delete',
|
|
1153
|
+
},
|
|
1154
|
+
},
|
|
1155
|
+
required: ['launch_id'],
|
|
1156
|
+
},
|
|
1157
|
+
},
|
|
1158
|
+
// Validation / Success Factor Tools
|
|
1159
|
+
{
|
|
1160
|
+
name: 'fcp_get_success_factors',
|
|
1161
|
+
description: 'Get the success factors (validation criteria) for a checklist item. Shows what automated checks are configured.',
|
|
1162
|
+
inputSchema: {
|
|
1163
|
+
type: 'object',
|
|
1164
|
+
properties: {
|
|
1165
|
+
launch_id: {
|
|
1166
|
+
type: 'number',
|
|
1167
|
+
description: 'The ID of the launch',
|
|
1168
|
+
},
|
|
1169
|
+
item_id: {
|
|
1170
|
+
type: 'number',
|
|
1171
|
+
description: 'The ID of the checklist item',
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
required: ['launch_id', 'item_id'],
|
|
1175
|
+
},
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
name: 'fcp_validate_checklist_item',
|
|
1179
|
+
description: 'Run automated validation checks on a checklist item. Executes all configured success factors (HTTP checks, DNS checks, SSL checks, etc.) and returns results.',
|
|
1180
|
+
inputSchema: {
|
|
1181
|
+
type: 'object',
|
|
1182
|
+
properties: {
|
|
1183
|
+
launch_id: {
|
|
1184
|
+
type: 'number',
|
|
1185
|
+
description: 'The ID of the launch',
|
|
1186
|
+
},
|
|
1187
|
+
item_id: {
|
|
1188
|
+
type: 'number',
|
|
1189
|
+
description: 'The ID of the checklist item to validate',
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
required: ['launch_id', 'item_id'],
|
|
1193
|
+
},
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
name: 'fcp_get_unroo_section',
|
|
1197
|
+
description: 'Generate the Unroo task management section for a CLAUDE.md file. Auto-detects project key from git remote. Use this to add Unroo integration instructions to any project.',
|
|
1198
|
+
inputSchema: {
|
|
1199
|
+
type: 'object',
|
|
1200
|
+
properties: {
|
|
1201
|
+
repo_url: {
|
|
1202
|
+
type: 'string',
|
|
1203
|
+
description: 'Git remote URL (e.g., git@github.com:owner/repo.git). If not provided, uses current directory.',
|
|
1204
|
+
},
|
|
1205
|
+
project_key: {
|
|
1206
|
+
type: 'string',
|
|
1207
|
+
description: 'Manual project key override. Use if auto-detection fails.',
|
|
1208
|
+
},
|
|
1209
|
+
},
|
|
1210
|
+
},
|
|
1211
|
+
},
|
|
1212
|
+
// Unroo Task Management Tools
|
|
1213
|
+
{
|
|
1214
|
+
name: 'unroo_list_projects',
|
|
1215
|
+
description: 'List all Unroo projects mapped to FCP clients. Returns project mappings with organization and JIRA key info.',
|
|
1216
|
+
inputSchema: {
|
|
1217
|
+
type: 'object',
|
|
1218
|
+
properties: {},
|
|
1219
|
+
},
|
|
1220
|
+
},
|
|
1221
|
+
{
|
|
1222
|
+
name: 'unroo_list_tasks',
|
|
1223
|
+
description: 'List tasks from Unroo with optional filters. Use to find tasks for a project, by status, or by assignee.',
|
|
1224
|
+
inputSchema: {
|
|
1225
|
+
type: 'object',
|
|
1226
|
+
properties: {
|
|
1227
|
+
project_key: {
|
|
1228
|
+
type: 'string',
|
|
1229
|
+
description: 'Filter by JIRA project key or FCP-SITE-{id}',
|
|
1230
|
+
},
|
|
1231
|
+
status: {
|
|
1232
|
+
type: 'string',
|
|
1233
|
+
description: 'Filter by status: To Do, In Progress, Done, Blocked (comma-separated for multiple)',
|
|
1234
|
+
},
|
|
1235
|
+
assignee_email: {
|
|
1236
|
+
type: 'string',
|
|
1237
|
+
description: 'Filter by assignee email',
|
|
1238
|
+
},
|
|
1239
|
+
external_source_type: {
|
|
1240
|
+
type: 'string',
|
|
1241
|
+
description: 'Filter by source: fcp, fcp_checklist, fcp_launch',
|
|
1242
|
+
},
|
|
1243
|
+
limit: {
|
|
1244
|
+
type: 'number',
|
|
1245
|
+
description: 'Maximum number of tasks to return (default 100)',
|
|
1246
|
+
},
|
|
1247
|
+
},
|
|
1248
|
+
},
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
name: 'unroo_create_task',
|
|
1252
|
+
description: 'Create a new task in Unroo. Requires title and project_key. Use for follow-up tasks, discovered issues, or new work items.',
|
|
1253
|
+
inputSchema: {
|
|
1254
|
+
type: 'object',
|
|
1255
|
+
properties: {
|
|
1256
|
+
title: {
|
|
1257
|
+
type: 'string',
|
|
1258
|
+
description: 'Task title (required)',
|
|
1259
|
+
},
|
|
1260
|
+
description: {
|
|
1261
|
+
type: 'string',
|
|
1262
|
+
description: 'Detailed description of the task',
|
|
1263
|
+
},
|
|
1264
|
+
project_key: {
|
|
1265
|
+
type: 'string',
|
|
1266
|
+
description: 'JIRA project key or FCP-SITE-{id} (required)',
|
|
1267
|
+
},
|
|
1268
|
+
priority: {
|
|
1269
|
+
type: 'string',
|
|
1270
|
+
enum: ['urgent', 'high', 'medium', 'low'],
|
|
1271
|
+
description: 'Task priority (default: medium)',
|
|
1272
|
+
},
|
|
1273
|
+
status: {
|
|
1274
|
+
type: 'string',
|
|
1275
|
+
description: 'Initial status (default: To Do)',
|
|
1276
|
+
},
|
|
1277
|
+
assignee_email: {
|
|
1278
|
+
type: 'string',
|
|
1279
|
+
description: 'Email of person to assign the task to',
|
|
1280
|
+
},
|
|
1281
|
+
due_date: {
|
|
1282
|
+
type: 'string',
|
|
1283
|
+
description: 'Due date in ISO format',
|
|
1284
|
+
},
|
|
1285
|
+
hours_estimated: {
|
|
1286
|
+
type: 'number',
|
|
1287
|
+
description: 'Estimated hours to complete',
|
|
1288
|
+
},
|
|
1289
|
+
labels: {
|
|
1290
|
+
type: 'array',
|
|
1291
|
+
items: { type: 'string' },
|
|
1292
|
+
description: 'Labels/tags for the task',
|
|
1293
|
+
},
|
|
1294
|
+
parent_issue_id: {
|
|
1295
|
+
type: 'string',
|
|
1296
|
+
description: 'Parent task ID if this is a subtask',
|
|
1297
|
+
},
|
|
1298
|
+
},
|
|
1299
|
+
required: ['title', 'project_key'],
|
|
1300
|
+
},
|
|
1301
|
+
},
|
|
1302
|
+
{
|
|
1303
|
+
name: 'unroo_get_task',
|
|
1304
|
+
description: 'Get detailed information about a specific task by ID. Returns full task details including description, status, priority, labels, hours, and activity.',
|
|
1305
|
+
inputSchema: {
|
|
1306
|
+
type: 'object',
|
|
1307
|
+
properties: {
|
|
1308
|
+
task_id: {
|
|
1309
|
+
type: 'string',
|
|
1310
|
+
description: 'The ID of the task to retrieve (required)',
|
|
1311
|
+
},
|
|
1312
|
+
},
|
|
1313
|
+
required: ['task_id'],
|
|
1314
|
+
},
|
|
1315
|
+
},
|
|
1316
|
+
{
|
|
1317
|
+
name: 'unroo_update_task',
|
|
1318
|
+
description: 'Update an existing task in Unroo. Use to change status, log hours, update priority, or reassign.',
|
|
1319
|
+
inputSchema: {
|
|
1320
|
+
type: 'object',
|
|
1321
|
+
properties: {
|
|
1322
|
+
task_id: {
|
|
1323
|
+
type: 'string',
|
|
1324
|
+
description: 'The ID of the task to update (required)',
|
|
1325
|
+
},
|
|
1326
|
+
title: {
|
|
1327
|
+
type: 'string',
|
|
1328
|
+
description: 'Updated title',
|
|
1329
|
+
},
|
|
1330
|
+
description: {
|
|
1331
|
+
type: 'string',
|
|
1332
|
+
description: 'Updated description',
|
|
1333
|
+
},
|
|
1334
|
+
status: {
|
|
1335
|
+
type: 'string',
|
|
1336
|
+
description: 'New status: To Do, In Progress, Done, Blocked',
|
|
1337
|
+
},
|
|
1338
|
+
priority: {
|
|
1339
|
+
type: 'string',
|
|
1340
|
+
enum: ['urgent', 'high', 'medium', 'low'],
|
|
1341
|
+
description: 'Updated priority',
|
|
1342
|
+
},
|
|
1343
|
+
assignee_email: {
|
|
1344
|
+
type: 'string',
|
|
1345
|
+
description: 'New assignee email (null to unassign)',
|
|
1346
|
+
},
|
|
1347
|
+
hours_logged: {
|
|
1348
|
+
type: 'number',
|
|
1349
|
+
description: 'Total hours logged on the task',
|
|
1350
|
+
},
|
|
1351
|
+
resolution: {
|
|
1352
|
+
type: 'string',
|
|
1353
|
+
description: 'Resolution when marking as Done',
|
|
1354
|
+
},
|
|
1355
|
+
},
|
|
1356
|
+
required: ['task_id'],
|
|
1357
|
+
},
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
name: 'unroo_get_my_tasks',
|
|
815
1361
|
description: 'Get tasks assigned to the current user (based on API key). Useful for finding your work items.',
|
|
816
1362
|
inputSchema: {
|
|
817
1363
|
type: 'object',
|
|
@@ -994,17 +1540,399 @@ const TOOLS = [
|
|
|
994
1540
|
type: 'string',
|
|
995
1541
|
description: 'The ID of the parking lot item to convert (required)',
|
|
996
1542
|
},
|
|
997
|
-
priority: {
|
|
1543
|
+
priority: {
|
|
1544
|
+
type: 'string',
|
|
1545
|
+
enum: ['Urgent', 'High', 'Medium', 'Low'],
|
|
1546
|
+
description: 'Priority for the backlog item (optional, keeps original if not specified)',
|
|
1547
|
+
},
|
|
1548
|
+
notes: {
|
|
1549
|
+
type: 'string',
|
|
1550
|
+
description: 'Notes about the conversion decision',
|
|
1551
|
+
},
|
|
1552
|
+
},
|
|
1553
|
+
required: ['id'],
|
|
1554
|
+
},
|
|
1555
|
+
},
|
|
1556
|
+
// FileSync Tools
|
|
1557
|
+
{
|
|
1558
|
+
name: 'fcp_filesync_list_configs',
|
|
1559
|
+
description: 'List all file sync configurations with current status. Shows prod/staging PVC pairs, scheduling, sync direction, and last sync info.',
|
|
1560
|
+
inputSchema: {
|
|
1561
|
+
type: 'object',
|
|
1562
|
+
properties: {
|
|
1563
|
+
enabled: {
|
|
1564
|
+
type: 'boolean',
|
|
1565
|
+
description: 'Filter by enabled status',
|
|
1566
|
+
},
|
|
1567
|
+
search: {
|
|
1568
|
+
type: 'string',
|
|
1569
|
+
description: 'Search across site names, clusters, namespaces',
|
|
1570
|
+
},
|
|
1571
|
+
sync_direction: {
|
|
1572
|
+
type: 'string',
|
|
1573
|
+
enum: ['prod_to_staging', 'staging_to_prod'],
|
|
1574
|
+
description: 'Filter by sync direction',
|
|
1575
|
+
},
|
|
1576
|
+
},
|
|
1577
|
+
},
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
name: 'fcp_filesync_get_config',
|
|
1581
|
+
description: 'Get detailed file sync config with recent job history. Returns full config, last 5 jobs, and scheduling info.',
|
|
1582
|
+
inputSchema: {
|
|
1583
|
+
type: 'object',
|
|
1584
|
+
properties: {
|
|
1585
|
+
config_id: {
|
|
1586
|
+
type: 'number',
|
|
1587
|
+
description: 'The config ID to retrieve',
|
|
1588
|
+
},
|
|
1589
|
+
},
|
|
1590
|
+
required: ['config_id'],
|
|
1591
|
+
},
|
|
1592
|
+
},
|
|
1593
|
+
{
|
|
1594
|
+
name: 'fcp_filesync_start_sync',
|
|
1595
|
+
description: 'Trigger a file sync immediately. For prod-to-staging syncs, executes directly. For staging-to-prod syncs, requires a confirmation_token from fcp_filesync_get_confirmation.',
|
|
1596
|
+
inputSchema: {
|
|
1597
|
+
type: 'object',
|
|
1598
|
+
properties: {
|
|
1599
|
+
config_id: {
|
|
1600
|
+
type: 'number',
|
|
1601
|
+
description: 'The config ID to sync',
|
|
1602
|
+
},
|
|
1603
|
+
confirmation_token: {
|
|
1604
|
+
type: 'string',
|
|
1605
|
+
description: 'Required for staging-to-prod syncs. Get from fcp_filesync_get_confirmation.',
|
|
1606
|
+
},
|
|
1607
|
+
},
|
|
1608
|
+
required: ['config_id'],
|
|
1609
|
+
},
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
name: 'fcp_filesync_get_job_status',
|
|
1613
|
+
description: 'Get the status of a file sync job. Returns progress, current step, rsync stats, and timing info. Call in a loop to monitor a running sync.',
|
|
1614
|
+
inputSchema: {
|
|
1615
|
+
type: 'object',
|
|
1616
|
+
properties: {
|
|
1617
|
+
config_id: {
|
|
1618
|
+
type: 'number',
|
|
1619
|
+
description: 'The config ID to check jobs for',
|
|
1620
|
+
},
|
|
1621
|
+
status: {
|
|
1622
|
+
type: 'string',
|
|
1623
|
+
description: 'Filter by job status (e.g. pending, syncing, completed, failed)',
|
|
1624
|
+
},
|
|
1625
|
+
limit: {
|
|
1626
|
+
type: 'number',
|
|
1627
|
+
description: 'Number of jobs to return (default 5)',
|
|
1628
|
+
},
|
|
1629
|
+
},
|
|
1630
|
+
required: ['config_id'],
|
|
1631
|
+
},
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
name: 'fcp_filesync_cancel_sync',
|
|
1635
|
+
description: 'Cancel a running file sync. Kills the K8s Job and marks the job as cancelled.',
|
|
1636
|
+
inputSchema: {
|
|
1637
|
+
type: 'object',
|
|
1638
|
+
properties: {
|
|
1639
|
+
job_id: {
|
|
1640
|
+
type: 'string',
|
|
1641
|
+
description: 'The job ID (UUID) to cancel',
|
|
1642
|
+
},
|
|
1643
|
+
},
|
|
1644
|
+
required: ['job_id'],
|
|
1645
|
+
},
|
|
1646
|
+
},
|
|
1647
|
+
{
|
|
1648
|
+
name: 'fcp_filesync_get_confirmation',
|
|
1649
|
+
description: 'Generate a confirmation token for staging-to-prod syncs. Token is valid for 5 minutes. Use before fcp_filesync_start_sync for staging_to_prod direction.',
|
|
1650
|
+
inputSchema: {
|
|
1651
|
+
type: 'object',
|
|
1652
|
+
properties: {
|
|
1653
|
+
config_id: {
|
|
1654
|
+
type: 'number',
|
|
1655
|
+
description: 'The config ID requiring confirmation',
|
|
1656
|
+
},
|
|
1657
|
+
},
|
|
1658
|
+
required: ['config_id'],
|
|
1659
|
+
},
|
|
1660
|
+
},
|
|
1661
|
+
// Nuclei Security Scanning
|
|
1662
|
+
{
|
|
1663
|
+
name: 'fcp_trigger_nuclei_scan',
|
|
1664
|
+
description: 'Trigger a Nuclei security scan for a website. Creates a Kubernetes Job that runs the scan and stores results in the FCP database.',
|
|
1665
|
+
inputSchema: {
|
|
1666
|
+
type: 'object',
|
|
1667
|
+
properties: {
|
|
1668
|
+
website_id: {
|
|
1669
|
+
type: 'number',
|
|
1670
|
+
description: 'The website ID to scan (required)',
|
|
1671
|
+
},
|
|
1672
|
+
url: {
|
|
1673
|
+
type: 'string',
|
|
1674
|
+
description: 'Override target URL (optional, auto-resolved from website record if not provided)',
|
|
1675
|
+
},
|
|
1676
|
+
severity: {
|
|
1677
|
+
type: 'string',
|
|
1678
|
+
description: 'Comma-separated severity levels to scan for (default: critical,high,medium)',
|
|
1679
|
+
},
|
|
1680
|
+
templates: {
|
|
1681
|
+
type: 'string',
|
|
1682
|
+
description: 'Comma-separated Nuclei template categories (default: cves,exposures,misconfiguration). Options: cves, vulnerabilities, exposures, misconfiguration, technologies',
|
|
1683
|
+
},
|
|
1684
|
+
},
|
|
1685
|
+
required: ['website_id'],
|
|
1686
|
+
},
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
name: 'fcp_get_nuclei_results',
|
|
1690
|
+
description: 'Get Nuclei security scan results for a website. Returns scan history with findings summary and individual vulnerability details.',
|
|
1691
|
+
inputSchema: {
|
|
1692
|
+
type: 'object',
|
|
1693
|
+
properties: {
|
|
1694
|
+
website_id: {
|
|
1695
|
+
type: 'number',
|
|
1696
|
+
description: 'The website ID to get results for (required)',
|
|
1697
|
+
},
|
|
1698
|
+
scan_id: {
|
|
1699
|
+
type: 'string',
|
|
1700
|
+
description: 'Specific scan ID to get detailed results for (optional)',
|
|
1701
|
+
},
|
|
1702
|
+
limit: {
|
|
1703
|
+
type: 'number',
|
|
1704
|
+
description: 'Maximum number of scans to return (default: 5)',
|
|
1705
|
+
},
|
|
1706
|
+
status: {
|
|
1707
|
+
type: 'string',
|
|
1708
|
+
description: 'Filter findings by status: active, fixed, reopened, false_positive',
|
|
1709
|
+
},
|
|
1710
|
+
},
|
|
1711
|
+
required: ['website_id'],
|
|
1712
|
+
},
|
|
1713
|
+
},
|
|
1714
|
+
// Site/Website CRUD Tools
|
|
1715
|
+
{
|
|
1716
|
+
name: 'fcp_list_sites',
|
|
1717
|
+
description: 'List websites managed by FCP with optional filters. Returns paginated results with account info.',
|
|
1718
|
+
inputSchema: {
|
|
1719
|
+
type: 'object',
|
|
1720
|
+
properties: {
|
|
1721
|
+
account_id: {
|
|
1722
|
+
type: 'number',
|
|
1723
|
+
description: 'Filter by account/client ID',
|
|
1724
|
+
},
|
|
1725
|
+
cms: {
|
|
1726
|
+
type: 'string',
|
|
1727
|
+
description: 'Filter by CMS type: WordPress, Drupal, Strapi, Other',
|
|
1728
|
+
},
|
|
1729
|
+
environment: {
|
|
1730
|
+
type: 'string',
|
|
1731
|
+
description: 'Filter by environment: production, staging, development',
|
|
1732
|
+
},
|
|
1733
|
+
retired: {
|
|
1734
|
+
type: 'string',
|
|
1735
|
+
description: 'Filter retired sites: "true" (only retired), "all" (include retired), omit for active only',
|
|
1736
|
+
},
|
|
1737
|
+
limit: {
|
|
1738
|
+
type: 'number',
|
|
1739
|
+
description: 'Maximum results to return (default: 50, max: 200)',
|
|
1740
|
+
},
|
|
1741
|
+
offset: {
|
|
1742
|
+
type: 'number',
|
|
1743
|
+
description: 'Offset for pagination (default: 0)',
|
|
1744
|
+
},
|
|
1745
|
+
},
|
|
1746
|
+
},
|
|
1747
|
+
},
|
|
1748
|
+
{
|
|
1749
|
+
name: 'fcp_search_sites',
|
|
1750
|
+
description: 'Search websites by domain name, account name, or URL. Returns matching sites ranked by relevance.',
|
|
1751
|
+
inputSchema: {
|
|
1752
|
+
type: 'object',
|
|
1753
|
+
properties: {
|
|
1754
|
+
query: {
|
|
1755
|
+
type: 'string',
|
|
1756
|
+
description: 'Search query (min 2 characters) - matches against domain, account name, and URL',
|
|
1757
|
+
},
|
|
1758
|
+
limit: {
|
|
1759
|
+
type: 'number',
|
|
1760
|
+
description: 'Maximum results to return (default: 10)',
|
|
1761
|
+
},
|
|
1762
|
+
},
|
|
1763
|
+
required: ['query'],
|
|
1764
|
+
},
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
name: 'fcp_get_site',
|
|
1768
|
+
description: 'Get detailed information about a specific website including account info, infrastructure details, and lead developer.',
|
|
1769
|
+
inputSchema: {
|
|
1770
|
+
type: 'object',
|
|
1771
|
+
properties: {
|
|
1772
|
+
website_id: {
|
|
1773
|
+
type: 'number',
|
|
1774
|
+
description: 'The website ID to retrieve',
|
|
1775
|
+
},
|
|
1776
|
+
},
|
|
1777
|
+
required: ['website_id'],
|
|
1778
|
+
},
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
name: 'fcp_create_site',
|
|
1782
|
+
description: 'Create a new website with optional staging environments. Production site is created first, then staging sites are linked to it.',
|
|
1783
|
+
inputSchema: {
|
|
1784
|
+
type: 'object',
|
|
1785
|
+
properties: {
|
|
1786
|
+
account_id: {
|
|
1787
|
+
type: 'number',
|
|
1788
|
+
description: 'Account/client ID the site belongs to (required)',
|
|
1789
|
+
},
|
|
1790
|
+
domain: {
|
|
1791
|
+
type: 'string',
|
|
1792
|
+
description: 'Primary domain name, e.g. "example.com" (required)',
|
|
1793
|
+
},
|
|
1794
|
+
cms: {
|
|
1795
|
+
type: 'string',
|
|
1796
|
+
description: 'CMS type: WordPress, Drupal, Strapi, Other (required)',
|
|
1797
|
+
},
|
|
1798
|
+
url_full: {
|
|
1799
|
+
type: 'string',
|
|
1800
|
+
description: 'Full URL including protocol (default: https://<domain>)',
|
|
1801
|
+
},
|
|
1802
|
+
git_provider: {
|
|
1803
|
+
type: 'string',
|
|
1804
|
+
description: 'Git provider: GitHub, GitLab, Bitbucket (default: GitHub)',
|
|
1805
|
+
},
|
|
1806
|
+
git_link: {
|
|
1807
|
+
type: 'string',
|
|
1808
|
+
description: 'URL to the git repository',
|
|
1809
|
+
},
|
|
1810
|
+
hosting_provider: {
|
|
1811
|
+
type: 'string',
|
|
1812
|
+
description: 'Hosting provider name',
|
|
1813
|
+
},
|
|
1814
|
+
fru_hosted: {
|
|
1815
|
+
type: 'boolean',
|
|
1816
|
+
description: 'Whether the site is hosted on FruCloud (default: false)',
|
|
1817
|
+
},
|
|
1818
|
+
k8s_cluster: {
|
|
1819
|
+
type: 'string',
|
|
1820
|
+
description: 'Kubernetes cluster name if FruCloud hosted',
|
|
1821
|
+
},
|
|
1822
|
+
k8s_namespace: {
|
|
1823
|
+
type: 'string',
|
|
1824
|
+
description: 'Kubernetes namespace if FruCloud hosted',
|
|
1825
|
+
},
|
|
1826
|
+
staging: {
|
|
1827
|
+
type: 'array',
|
|
1828
|
+
items: {
|
|
1829
|
+
type: 'object',
|
|
1830
|
+
properties: {
|
|
1831
|
+
domain: { type: 'string', description: 'Staging domain' },
|
|
1832
|
+
k8s_namespace: { type: 'string', description: 'Staging K8s namespace' },
|
|
1833
|
+
},
|
|
1834
|
+
required: ['domain', 'k8s_namespace'],
|
|
1835
|
+
},
|
|
1836
|
+
description: 'Optional staging environments to create',
|
|
1837
|
+
},
|
|
1838
|
+
},
|
|
1839
|
+
required: ['account_id', 'domain', 'cms'],
|
|
1840
|
+
},
|
|
1841
|
+
},
|
|
1842
|
+
{
|
|
1843
|
+
name: 'fcp_update_site',
|
|
1844
|
+
description: 'Update properties of an existing website. Only provided fields are updated.',
|
|
1845
|
+
inputSchema: {
|
|
1846
|
+
type: 'object',
|
|
1847
|
+
properties: {
|
|
1848
|
+
website_id: {
|
|
1849
|
+
type: 'number',
|
|
1850
|
+
description: 'The website ID to update (required)',
|
|
1851
|
+
},
|
|
1852
|
+
domain: {
|
|
1853
|
+
type: 'string',
|
|
1854
|
+
description: 'Updated domain name',
|
|
1855
|
+
},
|
|
1856
|
+
url_full: {
|
|
1857
|
+
type: 'string',
|
|
1858
|
+
description: 'Updated full URL',
|
|
1859
|
+
},
|
|
1860
|
+
cms: {
|
|
1861
|
+
type: 'string',
|
|
1862
|
+
description: 'Updated CMS type',
|
|
1863
|
+
},
|
|
1864
|
+
git_provider: {
|
|
1865
|
+
type: 'string',
|
|
1866
|
+
description: 'Updated git provider',
|
|
1867
|
+
},
|
|
1868
|
+
git_link: {
|
|
1869
|
+
type: 'string',
|
|
1870
|
+
description: 'Updated git repository URL',
|
|
1871
|
+
},
|
|
1872
|
+
staging_url: {
|
|
1873
|
+
type: 'string',
|
|
1874
|
+
description: 'Updated staging URL',
|
|
1875
|
+
},
|
|
1876
|
+
hosting_provider: {
|
|
1877
|
+
type: 'string',
|
|
1878
|
+
description: 'Updated hosting provider',
|
|
1879
|
+
},
|
|
1880
|
+
fru_hosted: {
|
|
1881
|
+
type: 'boolean',
|
|
1882
|
+
description: 'Updated FruCloud hosting flag',
|
|
1883
|
+
},
|
|
1884
|
+
k8s_cluster: {
|
|
1885
|
+
type: 'string',
|
|
1886
|
+
description: 'Updated K8s cluster',
|
|
1887
|
+
},
|
|
1888
|
+
k8s_namespace: {
|
|
1889
|
+
type: 'string',
|
|
1890
|
+
description: 'Updated K8s namespace',
|
|
1891
|
+
},
|
|
1892
|
+
environment: {
|
|
1893
|
+
type: 'string',
|
|
1894
|
+
description: 'Updated environment: production, staging, development',
|
|
1895
|
+
},
|
|
1896
|
+
lead_developer: {
|
|
1897
|
+
type: 'number',
|
|
1898
|
+
description: 'Updated lead developer user ID',
|
|
1899
|
+
},
|
|
1900
|
+
frucare_site: {
|
|
1901
|
+
type: 'boolean',
|
|
1902
|
+
description: 'Updated FruCare maintenance flag',
|
|
1903
|
+
},
|
|
1904
|
+
backup_enabled: {
|
|
1905
|
+
type: 'boolean',
|
|
1906
|
+
description: 'Updated backup enabled flag',
|
|
1907
|
+
},
|
|
1908
|
+
backup_schedule: {
|
|
1909
|
+
type: 'string',
|
|
1910
|
+
description: 'Updated backup cron schedule',
|
|
1911
|
+
},
|
|
1912
|
+
},
|
|
1913
|
+
required: ['website_id'],
|
|
1914
|
+
},
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
name: 'fcp_delete_site',
|
|
1918
|
+
description: 'Soft-delete (retire) a website. Sets retired flag and preserves all data. Admin only.',
|
|
1919
|
+
inputSchema: {
|
|
1920
|
+
type: 'object',
|
|
1921
|
+
properties: {
|
|
1922
|
+
website_id: {
|
|
1923
|
+
type: 'number',
|
|
1924
|
+
description: 'The website ID to retire (required)',
|
|
1925
|
+
},
|
|
1926
|
+
reason: {
|
|
998
1927
|
type: 'string',
|
|
999
|
-
|
|
1000
|
-
description: 'Priority for the backlog item (optional, keeps original if not specified)',
|
|
1928
|
+
description: 'Reason for retiring the site',
|
|
1001
1929
|
},
|
|
1002
|
-
|
|
1930
|
+
forward_url: {
|
|
1003
1931
|
type: 'string',
|
|
1004
|
-
description: '
|
|
1932
|
+
description: 'URL to forward the domain to after retirement',
|
|
1005
1933
|
},
|
|
1006
1934
|
},
|
|
1007
|
-
required: ['
|
|
1935
|
+
required: ['website_id'],
|
|
1008
1936
|
},
|
|
1009
1937
|
},
|
|
1010
1938
|
];
|
|
@@ -1096,11 +2024,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1096
2024
|
],
|
|
1097
2025
|
};
|
|
1098
2026
|
}
|
|
1099
|
-
case '
|
|
1100
|
-
const { launch_id,
|
|
1101
|
-
const result = await client.
|
|
1102
|
-
|
|
1103
|
-
|
|
2027
|
+
case 'fcp_add_checklist_item': {
|
|
2028
|
+
const { launch_id, title, category, ...rest } = args;
|
|
2029
|
+
const result = await client.createChecklistItem(launch_id, {
|
|
2030
|
+
title,
|
|
2031
|
+
category,
|
|
2032
|
+
...rest,
|
|
1104
2033
|
});
|
|
1105
2034
|
return {
|
|
1106
2035
|
content: [
|
|
@@ -1108,13 +2037,49 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1108
2037
|
type: 'text',
|
|
1109
2038
|
text: JSON.stringify({
|
|
1110
2039
|
success: true,
|
|
1111
|
-
message: `Checklist item
|
|
2040
|
+
message: `Checklist item created: "${title}"`,
|
|
2041
|
+
item: result,
|
|
2042
|
+
}, null, 2),
|
|
2043
|
+
},
|
|
2044
|
+
],
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
case 'fcp_update_checklist_item': {
|
|
2048
|
+
const { launch_id, item_id, notes, ...updates } = args;
|
|
2049
|
+
// Map 'notes' to 'completion_notes' for the API
|
|
2050
|
+
const payload = { ...updates };
|
|
2051
|
+
if (notes !== undefined) {
|
|
2052
|
+
payload.completion_notes = notes;
|
|
2053
|
+
}
|
|
2054
|
+
const result = await client.updateChecklistItem(launch_id, item_id, payload);
|
|
2055
|
+
return {
|
|
2056
|
+
content: [
|
|
2057
|
+
{
|
|
2058
|
+
type: 'text',
|
|
2059
|
+
text: JSON.stringify({
|
|
2060
|
+
success: true,
|
|
2061
|
+
message: `Checklist item ${item_id} updated`,
|
|
1112
2062
|
item: result,
|
|
1113
2063
|
}, null, 2),
|
|
1114
2064
|
},
|
|
1115
2065
|
],
|
|
1116
2066
|
};
|
|
1117
2067
|
}
|
|
2068
|
+
case 'fcp_delete_checklist_item': {
|
|
2069
|
+
const { launch_id, item_id } = args;
|
|
2070
|
+
await client.deleteChecklistItem(launch_id, item_id);
|
|
2071
|
+
return {
|
|
2072
|
+
content: [
|
|
2073
|
+
{
|
|
2074
|
+
type: 'text',
|
|
2075
|
+
text: JSON.stringify({
|
|
2076
|
+
success: true,
|
|
2077
|
+
message: `Checklist item ${item_id} deleted from launch ${launch_id}`,
|
|
2078
|
+
}, null, 2),
|
|
2079
|
+
},
|
|
2080
|
+
],
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
1118
2083
|
case 'fcp_add_progress_note': {
|
|
1119
2084
|
const { launch_id, content } = args;
|
|
1120
2085
|
const result = await client.addNote(launch_id, content);
|
|
@@ -1143,6 +2108,244 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1143
2108
|
],
|
|
1144
2109
|
};
|
|
1145
2110
|
}
|
|
2111
|
+
// Nuclei Security Scanning Handlers
|
|
2112
|
+
case 'fcp_trigger_nuclei_scan': {
|
|
2113
|
+
const { website_id, url, severity, templates } = args;
|
|
2114
|
+
const result = await client.triggerNucleiScan(website_id, { url, severity, templates });
|
|
2115
|
+
return {
|
|
2116
|
+
content: [
|
|
2117
|
+
{
|
|
2118
|
+
type: 'text',
|
|
2119
|
+
text: JSON.stringify(result, null, 2),
|
|
2120
|
+
},
|
|
2121
|
+
],
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
case 'fcp_get_nuclei_results': {
|
|
2125
|
+
const { website_id, scan_id, limit, status } = args;
|
|
2126
|
+
const result = await client.getNucleiResults(website_id, { scan_id, limit, status });
|
|
2127
|
+
return {
|
|
2128
|
+
content: [
|
|
2129
|
+
{
|
|
2130
|
+
type: 'text',
|
|
2131
|
+
text: JSON.stringify(result, null, 2),
|
|
2132
|
+
},
|
|
2133
|
+
],
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
// Site/Website CRUD Handlers
|
|
2137
|
+
case 'fcp_list_sites': {
|
|
2138
|
+
const filters = args;
|
|
2139
|
+
const result = await client.listSites(filters);
|
|
2140
|
+
return {
|
|
2141
|
+
content: [
|
|
2142
|
+
{
|
|
2143
|
+
type: 'text',
|
|
2144
|
+
text: JSON.stringify(result, null, 2),
|
|
2145
|
+
},
|
|
2146
|
+
],
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
case 'fcp_search_sites': {
|
|
2150
|
+
const { query, limit } = args;
|
|
2151
|
+
const result = await client.searchSites(query, limit);
|
|
2152
|
+
return {
|
|
2153
|
+
content: [
|
|
2154
|
+
{
|
|
2155
|
+
type: 'text',
|
|
2156
|
+
text: JSON.stringify(result, null, 2),
|
|
2157
|
+
},
|
|
2158
|
+
],
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
case 'fcp_get_site': {
|
|
2162
|
+
const { website_id } = args;
|
|
2163
|
+
const result = await client.getSite(website_id);
|
|
2164
|
+
return {
|
|
2165
|
+
content: [
|
|
2166
|
+
{
|
|
2167
|
+
type: 'text',
|
|
2168
|
+
text: JSON.stringify(result, null, 2),
|
|
2169
|
+
},
|
|
2170
|
+
],
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
case 'fcp_create_site': {
|
|
2174
|
+
const { account_id, domain, cms, url_full, git_provider, git_link, hosting_provider, fru_hosted, k8s_cluster, k8s_namespace, staging, } = args;
|
|
2175
|
+
const result = await client.createSite({ account_id, domain, cms, url_full, git_provider, git_link, hosting_provider, fru_hosted, k8s_cluster, k8s_namespace }, staging);
|
|
2176
|
+
return {
|
|
2177
|
+
content: [
|
|
2178
|
+
{
|
|
2179
|
+
type: 'text',
|
|
2180
|
+
text: JSON.stringify(result, null, 2),
|
|
2181
|
+
},
|
|
2182
|
+
],
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
case 'fcp_update_site': {
|
|
2186
|
+
const { website_id, ...updates } = args;
|
|
2187
|
+
const result = await client.updateSite(website_id, updates);
|
|
2188
|
+
return {
|
|
2189
|
+
content: [
|
|
2190
|
+
{
|
|
2191
|
+
type: 'text',
|
|
2192
|
+
text: JSON.stringify(result, null, 2),
|
|
2193
|
+
},
|
|
2194
|
+
],
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
case 'fcp_delete_site': {
|
|
2198
|
+
const { website_id, reason, forward_url } = args;
|
|
2199
|
+
const result = await client.deleteSite(website_id, { reason, forward_url });
|
|
2200
|
+
return {
|
|
2201
|
+
content: [
|
|
2202
|
+
{
|
|
2203
|
+
type: 'text',
|
|
2204
|
+
text: JSON.stringify(result, null, 2),
|
|
2205
|
+
},
|
|
2206
|
+
],
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
// Launch CRUD Handlers
|
|
2210
|
+
case 'fcp_create_launch': {
|
|
2211
|
+
const { name, platform, launch_type, target_launch_date, ...rest } = args;
|
|
2212
|
+
const result = await client.createLaunch({
|
|
2213
|
+
name,
|
|
2214
|
+
platform,
|
|
2215
|
+
launch_type,
|
|
2216
|
+
target_launch_date,
|
|
2217
|
+
...rest,
|
|
2218
|
+
});
|
|
2219
|
+
return {
|
|
2220
|
+
content: [
|
|
2221
|
+
{
|
|
2222
|
+
type: 'text',
|
|
2223
|
+
text: JSON.stringify({
|
|
2224
|
+
success: true,
|
|
2225
|
+
message: `Launch created: "${name}" (ID: ${result.launch.id})`,
|
|
2226
|
+
launch: result.launch,
|
|
2227
|
+
}, null, 2),
|
|
2228
|
+
},
|
|
2229
|
+
],
|
|
2230
|
+
};
|
|
2231
|
+
}
|
|
2232
|
+
case 'fcp_update_launch': {
|
|
2233
|
+
const { launch_id, ...updates } = args;
|
|
2234
|
+
const result = await client.updateLaunch(launch_id, updates);
|
|
2235
|
+
return {
|
|
2236
|
+
content: [
|
|
2237
|
+
{
|
|
2238
|
+
type: 'text',
|
|
2239
|
+
text: JSON.stringify({
|
|
2240
|
+
success: true,
|
|
2241
|
+
message: `Launch ${launch_id} updated`,
|
|
2242
|
+
launch: result.launch || result,
|
|
2243
|
+
}, null, 2),
|
|
2244
|
+
},
|
|
2245
|
+
],
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
2248
|
+
case 'fcp_delete_launch': {
|
|
2249
|
+
const { launch_id } = args;
|
|
2250
|
+
await client.deleteLaunch(launch_id);
|
|
2251
|
+
return {
|
|
2252
|
+
content: [
|
|
2253
|
+
{
|
|
2254
|
+
type: 'text',
|
|
2255
|
+
text: JSON.stringify({
|
|
2256
|
+
success: true,
|
|
2257
|
+
message: `Launch ${launch_id} deleted`,
|
|
2258
|
+
}, null, 2),
|
|
2259
|
+
},
|
|
2260
|
+
],
|
|
2261
|
+
};
|
|
2262
|
+
}
|
|
2263
|
+
// Validation / Success Factor Handlers
|
|
2264
|
+
case 'fcp_get_success_factors': {
|
|
2265
|
+
const { launch_id, item_id } = args;
|
|
2266
|
+
const result = await client.getSuccessFactors(launch_id, item_id);
|
|
2267
|
+
return {
|
|
2268
|
+
content: [
|
|
2269
|
+
{
|
|
2270
|
+
type: 'text',
|
|
2271
|
+
text: JSON.stringify({
|
|
2272
|
+
checklist_item_id: item_id,
|
|
2273
|
+
total: result.factors?.length || 0,
|
|
2274
|
+
factors: result.factors || [],
|
|
2275
|
+
}, null, 2),
|
|
2276
|
+
},
|
|
2277
|
+
],
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
case 'fcp_validate_checklist_item': {
|
|
2281
|
+
const { launch_id, item_id } = args;
|
|
2282
|
+
const result = await client.validateChecklistItem(launch_id, item_id);
|
|
2283
|
+
return {
|
|
2284
|
+
content: [
|
|
2285
|
+
{
|
|
2286
|
+
type: 'text',
|
|
2287
|
+
text: JSON.stringify(result, null, 2),
|
|
2288
|
+
},
|
|
2289
|
+
],
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
case 'fcp_get_unroo_section': {
|
|
2293
|
+
const { repo_url, project_key } = args;
|
|
2294
|
+
// Use provided repo_url or detect from current directory
|
|
2295
|
+
const repoUrlToUse = repo_url || detectGitRemote();
|
|
2296
|
+
if (!repoUrlToUse && !project_key) {
|
|
2297
|
+
return {
|
|
2298
|
+
content: [
|
|
2299
|
+
{
|
|
2300
|
+
type: 'text',
|
|
2301
|
+
text: JSON.stringify({
|
|
2302
|
+
error: 'Could not detect git remote and no project_key provided. Either run this from a git repository or provide a project_key.',
|
|
2303
|
+
}, null, 2),
|
|
2304
|
+
},
|
|
2305
|
+
],
|
|
2306
|
+
isError: true,
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
// Build query params
|
|
2310
|
+
const sectionParams = new URLSearchParams();
|
|
2311
|
+
if (repoUrlToUse)
|
|
2312
|
+
sectionParams.set('repo_url', repoUrlToUse);
|
|
2313
|
+
if (project_key)
|
|
2314
|
+
sectionParams.set('project_key', project_key);
|
|
2315
|
+
// Call FCP API to get the section
|
|
2316
|
+
const sectionUrl = `${FCP_API_URL}/api/mcp/claude-md-section?${sectionParams}`;
|
|
2317
|
+
const sectionHeaders = {
|
|
2318
|
+
'Content-Type': 'application/json',
|
|
2319
|
+
};
|
|
2320
|
+
if (FCP_API_TOKEN && FCP_API_TOKEN !== 'dev_bypass') {
|
|
2321
|
+
sectionHeaders['X-API-Key'] = FCP_API_TOKEN;
|
|
2322
|
+
}
|
|
2323
|
+
else if (FCP_API_TOKEN === 'dev_bypass') {
|
|
2324
|
+
sectionHeaders['X-Dev-Bypass'] = 'true';
|
|
2325
|
+
}
|
|
2326
|
+
const response = await fetch(sectionUrl, { headers: sectionHeaders });
|
|
2327
|
+
const data = await response.json();
|
|
2328
|
+
if (!response.ok) {
|
|
2329
|
+
return {
|
|
2330
|
+
content: [
|
|
2331
|
+
{
|
|
2332
|
+
type: 'text',
|
|
2333
|
+
text: JSON.stringify({ error: data.error || 'Failed to get section' }, null, 2),
|
|
2334
|
+
},
|
|
2335
|
+
],
|
|
2336
|
+
isError: true,
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
// Return the markdown content with metadata
|
|
2340
|
+
return {
|
|
2341
|
+
content: [
|
|
2342
|
+
{
|
|
2343
|
+
type: 'text',
|
|
2344
|
+
text: `Project Key: ${data.project_key}\nSource: ${data.source}\n${data.warning ? `Warning: ${data.warning}\n` : ''}\n---\n\n${data.content}`,
|
|
2345
|
+
},
|
|
2346
|
+
],
|
|
2347
|
+
};
|
|
2348
|
+
}
|
|
1146
2349
|
// Unroo Task Management Handlers
|
|
1147
2350
|
case 'unroo_list_projects': {
|
|
1148
2351
|
const result = await unrooClient.listProjects();
|
|
@@ -1190,6 +2393,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1190
2393
|
],
|
|
1191
2394
|
};
|
|
1192
2395
|
}
|
|
2396
|
+
case 'unroo_get_task': {
|
|
2397
|
+
const { task_id } = args;
|
|
2398
|
+
const result = await unrooClient.getTask(task_id);
|
|
2399
|
+
return {
|
|
2400
|
+
content: [
|
|
2401
|
+
{
|
|
2402
|
+
type: 'text',
|
|
2403
|
+
text: JSON.stringify({
|
|
2404
|
+
success: true,
|
|
2405
|
+
task: result.task,
|
|
2406
|
+
}, null, 2),
|
|
2407
|
+
},
|
|
2408
|
+
],
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
1193
2411
|
case 'unroo_update_task': {
|
|
1194
2412
|
const { task_id, ...updates } = args;
|
|
1195
2413
|
const result = await unrooClient.updateTask(task_id, updates);
|
|
@@ -1250,9 +2468,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1250
2468
|
};
|
|
1251
2469
|
}
|
|
1252
2470
|
case 'unroo_end_session': {
|
|
1253
|
-
// End
|
|
2471
|
+
// End the auto-tracked session (clears heartbeat interval, logs activity)
|
|
1254
2472
|
await sessionTracker.endSession();
|
|
1255
|
-
|
|
2473
|
+
// End session via API to get the final session state
|
|
2474
|
+
// Note: If sessionTracker already ended it, Unroo returns the existing completed session
|
|
2475
|
+
// (protected against duplicate work log creation server-side)
|
|
2476
|
+
const result = await unrooClient.endSession({
|
|
2477
|
+
machine_id: INSTANCE_ID,
|
|
2478
|
+
repo_name: currentProject ? `${currentProject.github.owner}/${currentProject.github.repo}` : undefined,
|
|
2479
|
+
});
|
|
1256
2480
|
return {
|
|
1257
2481
|
content: [
|
|
1258
2482
|
{
|
|
@@ -1380,6 +2604,178 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1380
2604
|
],
|
|
1381
2605
|
};
|
|
1382
2606
|
}
|
|
2607
|
+
// FileSync Handlers
|
|
2608
|
+
case 'fcp_filesync_list_configs': {
|
|
2609
|
+
const filters = args;
|
|
2610
|
+
const result = await client.listFileSyncConfigs(filters);
|
|
2611
|
+
// If search is provided, filter client-side (API doesn't have text search)
|
|
2612
|
+
let configs = result.data || [];
|
|
2613
|
+
if (filters.search) {
|
|
2614
|
+
const term = filters.search.toLowerCase();
|
|
2615
|
+
configs = configs.filter((c) => (c.prod_namespace || '').toLowerCase().includes(term) ||
|
|
2616
|
+
(c.staging_namespace || '').toLowerCase().includes(term) ||
|
|
2617
|
+
(c.prod_cluster || '').toLowerCase().includes(term) ||
|
|
2618
|
+
(c.staging_cluster || '').toLowerCase().includes(term) ||
|
|
2619
|
+
(c.prod_pvc_name || '').toLowerCase().includes(term) ||
|
|
2620
|
+
(c.staging_pvc_name || '').toLowerCase().includes(term));
|
|
2621
|
+
}
|
|
2622
|
+
return {
|
|
2623
|
+
content: [
|
|
2624
|
+
{
|
|
2625
|
+
type: 'text',
|
|
2626
|
+
text: JSON.stringify({
|
|
2627
|
+
total: configs.length,
|
|
2628
|
+
configs: configs.map((c) => ({
|
|
2629
|
+
id: c.id,
|
|
2630
|
+
prod_namespace: c.prod_namespace,
|
|
2631
|
+
staging_namespace: c.staging_namespace,
|
|
2632
|
+
prod_cluster: c.prod_cluster,
|
|
2633
|
+
staging_cluster: c.staging_cluster,
|
|
2634
|
+
sync_direction: c.sync_direction,
|
|
2635
|
+
sync_method: c.sync_method,
|
|
2636
|
+
enabled: c.enabled,
|
|
2637
|
+
schedule_enabled: c.schedule_enabled,
|
|
2638
|
+
schedule_cron: c.schedule_cron,
|
|
2639
|
+
last_sync_at: c.last_sync_at,
|
|
2640
|
+
last_sync_status: c.last_sync_status,
|
|
2641
|
+
})),
|
|
2642
|
+
}, null, 2),
|
|
2643
|
+
},
|
|
2644
|
+
],
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
case 'fcp_filesync_get_config': {
|
|
2648
|
+
const { config_id } = args;
|
|
2649
|
+
// Get config from list (filtered by looking through all)
|
|
2650
|
+
const configsResult = await client.listFileSyncConfigs();
|
|
2651
|
+
const config = (configsResult.data || []).find((c) => c.id === config_id);
|
|
2652
|
+
if (!config) {
|
|
2653
|
+
throw new Error(`Config not found: ${config_id}`);
|
|
2654
|
+
}
|
|
2655
|
+
// Get recent jobs for this config
|
|
2656
|
+
const jobsResult = await client.getFileSyncJobs({ config_id, limit: 5 });
|
|
2657
|
+
return {
|
|
2658
|
+
content: [
|
|
2659
|
+
{
|
|
2660
|
+
type: 'text',
|
|
2661
|
+
text: JSON.stringify({
|
|
2662
|
+
config,
|
|
2663
|
+
recent_jobs: (jobsResult.data || []).map((j) => ({
|
|
2664
|
+
id: j.id,
|
|
2665
|
+
job_id: j.job_id,
|
|
2666
|
+
status: j.status,
|
|
2667
|
+
trigger_type: j.trigger_type,
|
|
2668
|
+
progress_percent: j.progress_percent,
|
|
2669
|
+
current_step: j.current_step,
|
|
2670
|
+
started_at: j.started_at,
|
|
2671
|
+
completed_at: j.completed_at,
|
|
2672
|
+
duration_seconds: j.duration_seconds,
|
|
2673
|
+
files_transferred: j.files_transferred,
|
|
2674
|
+
bytes_transferred: j.bytes_transferred,
|
|
2675
|
+
error_message: j.error_message,
|
|
2676
|
+
})),
|
|
2677
|
+
}, null, 2),
|
|
2678
|
+
},
|
|
2679
|
+
],
|
|
2680
|
+
};
|
|
2681
|
+
}
|
|
2682
|
+
case 'fcp_filesync_start_sync': {
|
|
2683
|
+
const { config_id, confirmation_token } = args;
|
|
2684
|
+
console.error(`[MCP] Starting file sync for config ${config_id}`);
|
|
2685
|
+
const result = await client.startFileSync({
|
|
2686
|
+
config_id,
|
|
2687
|
+
trigger_type: 'api',
|
|
2688
|
+
triggered_by: 'claude-code-mcp',
|
|
2689
|
+
confirmation_token,
|
|
2690
|
+
});
|
|
2691
|
+
return {
|
|
2692
|
+
content: [
|
|
2693
|
+
{
|
|
2694
|
+
type: 'text',
|
|
2695
|
+
text: JSON.stringify({
|
|
2696
|
+
success: result.success,
|
|
2697
|
+
message: result.message,
|
|
2698
|
+
job: result.data ? {
|
|
2699
|
+
id: result.data.id,
|
|
2700
|
+
job_id: result.data.job_id,
|
|
2701
|
+
status: result.data.status,
|
|
2702
|
+
current_step: result.data.current_step,
|
|
2703
|
+
} : null,
|
|
2704
|
+
}, null, 2),
|
|
2705
|
+
},
|
|
2706
|
+
],
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
case 'fcp_filesync_get_job_status': {
|
|
2710
|
+
const { config_id, status, limit } = args;
|
|
2711
|
+
const result = await client.getFileSyncJobs({
|
|
2712
|
+
config_id,
|
|
2713
|
+
status,
|
|
2714
|
+
limit: limit || 5,
|
|
2715
|
+
});
|
|
2716
|
+
return {
|
|
2717
|
+
content: [
|
|
2718
|
+
{
|
|
2719
|
+
type: 'text',
|
|
2720
|
+
text: JSON.stringify({
|
|
2721
|
+
total: (result.data || []).length,
|
|
2722
|
+
jobs: (result.data || []).map((j) => ({
|
|
2723
|
+
id: j.id,
|
|
2724
|
+
job_id: j.job_id,
|
|
2725
|
+
status: j.status,
|
|
2726
|
+
trigger_type: j.trigger_type,
|
|
2727
|
+
progress_percent: j.progress_percent,
|
|
2728
|
+
current_step: j.current_step,
|
|
2729
|
+
started_at: j.started_at,
|
|
2730
|
+
completed_at: j.completed_at,
|
|
2731
|
+
duration_seconds: j.duration_seconds,
|
|
2732
|
+
files_transferred: j.files_transferred,
|
|
2733
|
+
files_deleted: j.files_deleted,
|
|
2734
|
+
bytes_transferred: j.bytes_transferred,
|
|
2735
|
+
error_message: j.error_message,
|
|
2736
|
+
rsync_stats: j.rsync_stats,
|
|
2737
|
+
})),
|
|
2738
|
+
}, null, 2),
|
|
2739
|
+
},
|
|
2740
|
+
],
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
case 'fcp_filesync_cancel_sync': {
|
|
2744
|
+
const { job_id } = args;
|
|
2745
|
+
console.error(`[MCP] Cancelling file sync job ${job_id}`);
|
|
2746
|
+
const result = await client.cancelFileSync(job_id);
|
|
2747
|
+
return {
|
|
2748
|
+
content: [
|
|
2749
|
+
{
|
|
2750
|
+
type: 'text',
|
|
2751
|
+
text: JSON.stringify({
|
|
2752
|
+
success: result.success,
|
|
2753
|
+
message: result.message,
|
|
2754
|
+
}, null, 2),
|
|
2755
|
+
},
|
|
2756
|
+
],
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
case 'fcp_filesync_get_confirmation': {
|
|
2760
|
+
const { config_id } = args;
|
|
2761
|
+
console.error(`[MCP] Getting confirmation token for config ${config_id}`);
|
|
2762
|
+
const result = await client.getFileSyncConfirmation(config_id);
|
|
2763
|
+
return {
|
|
2764
|
+
content: [
|
|
2765
|
+
{
|
|
2766
|
+
type: 'text',
|
|
2767
|
+
text: JSON.stringify({
|
|
2768
|
+
success: result.success,
|
|
2769
|
+
confirmation_token: result.data.token,
|
|
2770
|
+
config_id: result.data.config_id,
|
|
2771
|
+
expires_at: result.data.expires_at,
|
|
2772
|
+
ttl_seconds: result.data.ttl_seconds,
|
|
2773
|
+
warnings: result.data.warnings,
|
|
2774
|
+
}, null, 2),
|
|
2775
|
+
},
|
|
2776
|
+
],
|
|
2777
|
+
};
|
|
2778
|
+
}
|
|
1383
2779
|
default:
|
|
1384
2780
|
throw new Error(`Unknown tool: ${name}`);
|
|
1385
2781
|
}
|