@fruition/fcp-mcp-server 1.15.0 → 1.16.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 +210 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -41,6 +41,9 @@ const FCP_API_TOKEN = process.env.FCP_API_TOKEN || '';
|
|
|
41
41
|
// When UNROO_API_KEY is not set, Unroo calls go through FCP's proxy at /api/mcp/unroo/*
|
|
42
42
|
const UNROO_API_URL = process.env.UNROO_API_URL || 'https://app.unroo.io';
|
|
43
43
|
const UNROO_API_KEY = process.env.UNROO_API_KEY || '';
|
|
44
|
+
// User identity for Unroo attribution
|
|
45
|
+
// Without this, all actions are attributed to whoever created the FCP API key
|
|
46
|
+
const FCP_USER_EMAIL = process.env.FCP_USER_EMAIL || '';
|
|
44
47
|
// If no Unroo key, use FCP as proxy (unified key setup)
|
|
45
48
|
const USE_FCP_UNROO_PROXY = !UNROO_API_KEY;
|
|
46
49
|
// Helper to check if Unroo functionality is available (either mode)
|
|
@@ -466,6 +469,35 @@ class FCPClient {
|
|
|
466
469
|
body: JSON.stringify({ s3Key }),
|
|
467
470
|
});
|
|
468
471
|
}
|
|
472
|
+
// Clone to Staging
|
|
473
|
+
async cloneToStagingConfirm(productionSiteId, stagingSiteId) {
|
|
474
|
+
return this.fetch('/api/clone-staging/confirm', {
|
|
475
|
+
method: 'POST',
|
|
476
|
+
body: JSON.stringify({ productionSiteId, stagingSiteId }),
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
async cloneToStaging(params) {
|
|
480
|
+
return this.fetch('/api/clone-staging', {
|
|
481
|
+
method: 'POST',
|
|
482
|
+
body: JSON.stringify(params),
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
async getCloneStatus(cloneId) {
|
|
486
|
+
return this.fetch(`/api/clone-staging/${encodeURIComponent(cloneId)}`);
|
|
487
|
+
}
|
|
488
|
+
async listCloneOperations(params) {
|
|
489
|
+
const query = new URLSearchParams();
|
|
490
|
+
if (params?.productionSiteId)
|
|
491
|
+
query.set('productionSiteId', String(params.productionSiteId));
|
|
492
|
+
if (params?.stagingSiteId)
|
|
493
|
+
query.set('stagingSiteId', String(params.stagingSiteId));
|
|
494
|
+
if (params?.limit)
|
|
495
|
+
query.set('limit', String(params.limit));
|
|
496
|
+
return this.fetch(`/api/clone-staging?${query.toString()}`);
|
|
497
|
+
}
|
|
498
|
+
async getDevEnvironmentInfo(siteId) {
|
|
499
|
+
return this.fetch(`/api/sites/${siteId}/dev-info`);
|
|
500
|
+
}
|
|
469
501
|
}
|
|
470
502
|
// Unroo API Client
|
|
471
503
|
// Supports two modes:
|
|
@@ -508,6 +540,11 @@ class UnrooClient {
|
|
|
508
540
|
else if (this.fcpToken === 'dev_bypass') {
|
|
509
541
|
headers['X-Dev-Bypass'] = 'true';
|
|
510
542
|
}
|
|
543
|
+
// Pass actual user identity so Unroo attributes actions correctly
|
|
544
|
+
// (otherwise all actions show as the FCP API key creator)
|
|
545
|
+
if (FCP_USER_EMAIL) {
|
|
546
|
+
headers['X-Acting-User-Email'] = FCP_USER_EMAIL;
|
|
547
|
+
}
|
|
511
548
|
}
|
|
512
549
|
else {
|
|
513
550
|
// Direct Unroo API call
|
|
@@ -1460,6 +1497,14 @@ const TOOLS = [
|
|
|
1460
1497
|
type: 'string',
|
|
1461
1498
|
description: 'Parent task ID if this is a subtask',
|
|
1462
1499
|
},
|
|
1500
|
+
github_branch: {
|
|
1501
|
+
type: 'string',
|
|
1502
|
+
description: 'Git branch name associated with this task',
|
|
1503
|
+
},
|
|
1504
|
+
github_repo: {
|
|
1505
|
+
type: 'string',
|
|
1506
|
+
description: 'GitHub repository (owner/repo format)',
|
|
1507
|
+
},
|
|
1463
1508
|
},
|
|
1464
1509
|
required: ['title', 'project_key'],
|
|
1465
1510
|
},
|
|
@@ -1517,6 +1562,22 @@ const TOOLS = [
|
|
|
1517
1562
|
type: 'string',
|
|
1518
1563
|
description: 'Resolution when marking as Done',
|
|
1519
1564
|
},
|
|
1565
|
+
github_branch: {
|
|
1566
|
+
type: 'string',
|
|
1567
|
+
description: 'Git branch name associated with this task',
|
|
1568
|
+
},
|
|
1569
|
+
github_pr_url: {
|
|
1570
|
+
type: 'string',
|
|
1571
|
+
description: 'GitHub pull request URL',
|
|
1572
|
+
},
|
|
1573
|
+
github_pr_number: {
|
|
1574
|
+
type: 'number',
|
|
1575
|
+
description: 'GitHub pull request number',
|
|
1576
|
+
},
|
|
1577
|
+
github_repo: {
|
|
1578
|
+
type: 'string',
|
|
1579
|
+
description: 'GitHub repository (owner/repo format)',
|
|
1580
|
+
},
|
|
1520
1581
|
},
|
|
1521
1582
|
required: ['task_id'],
|
|
1522
1583
|
},
|
|
@@ -2584,6 +2645,108 @@ const TOOLS = [
|
|
|
2584
2645
|
required: ['s3Key'],
|
|
2585
2646
|
},
|
|
2586
2647
|
},
|
|
2648
|
+
// Clone to Staging (unified prod → staging with files + database + search/replace)
|
|
2649
|
+
{
|
|
2650
|
+
name: 'fcp_clone_to_staging',
|
|
2651
|
+
description: 'Clone production to staging environment. Combines file sync (from DO snapshots) and database sync (from S3 backups pulled from RO replicas) into a single tracked operation. Requires a confirmation token from fcp_clone_confirm first, unless triggerType is "mcp".',
|
|
2652
|
+
inputSchema: {
|
|
2653
|
+
type: 'object',
|
|
2654
|
+
properties: {
|
|
2655
|
+
productionSiteId: {
|
|
2656
|
+
type: 'number',
|
|
2657
|
+
description: 'Production site ID',
|
|
2658
|
+
},
|
|
2659
|
+
stagingSiteId: {
|
|
2660
|
+
type: 'number',
|
|
2661
|
+
description: 'Staging site ID to clone into',
|
|
2662
|
+
},
|
|
2663
|
+
includeFiles: {
|
|
2664
|
+
type: 'boolean',
|
|
2665
|
+
description: 'Whether to sync files from production snapshot (default: true)',
|
|
2666
|
+
},
|
|
2667
|
+
includeDatabase: {
|
|
2668
|
+
type: 'boolean',
|
|
2669
|
+
description: 'Whether to sync database from latest backup (default: true)',
|
|
2670
|
+
},
|
|
2671
|
+
runSearchReplace: {
|
|
2672
|
+
type: 'boolean',
|
|
2673
|
+
description: 'Whether to run domain search/replace after DB sync (default: true)',
|
|
2674
|
+
},
|
|
2675
|
+
backupId: {
|
|
2676
|
+
type: 'string',
|
|
2677
|
+
description: 'Specific backup ID to restore from (optional, defaults to latest)',
|
|
2678
|
+
},
|
|
2679
|
+
},
|
|
2680
|
+
required: ['productionSiteId', 'stagingSiteId'],
|
|
2681
|
+
},
|
|
2682
|
+
},
|
|
2683
|
+
{
|
|
2684
|
+
name: 'fcp_clone_confirm',
|
|
2685
|
+
description: 'Generate a safety confirmation token for a clone-to-staging operation. Returns token (5-min TTL) plus site info and latest backup age.',
|
|
2686
|
+
inputSchema: {
|
|
2687
|
+
type: 'object',
|
|
2688
|
+
properties: {
|
|
2689
|
+
productionSiteId: {
|
|
2690
|
+
type: 'number',
|
|
2691
|
+
description: 'Production site ID',
|
|
2692
|
+
},
|
|
2693
|
+
stagingSiteId: {
|
|
2694
|
+
type: 'number',
|
|
2695
|
+
description: 'Staging site ID',
|
|
2696
|
+
},
|
|
2697
|
+
},
|
|
2698
|
+
required: ['productionSiteId', 'stagingSiteId'],
|
|
2699
|
+
},
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
name: 'fcp_clone_status',
|
|
2703
|
+
description: 'Get the status of a clone-to-staging operation. Returns progress, step statuses (files, database, search/replace), and errors.',
|
|
2704
|
+
inputSchema: {
|
|
2705
|
+
type: 'object',
|
|
2706
|
+
properties: {
|
|
2707
|
+
cloneId: {
|
|
2708
|
+
type: 'string',
|
|
2709
|
+
description: 'Clone operation ID (e.g., clone_abc12345_lxyz)',
|
|
2710
|
+
},
|
|
2711
|
+
},
|
|
2712
|
+
required: ['cloneId'],
|
|
2713
|
+
},
|
|
2714
|
+
},
|
|
2715
|
+
{
|
|
2716
|
+
name: 'fcp_clone_list',
|
|
2717
|
+
description: 'List recent clone-to-staging operations for a site. Shows history of all clone operations with status.',
|
|
2718
|
+
inputSchema: {
|
|
2719
|
+
type: 'object',
|
|
2720
|
+
properties: {
|
|
2721
|
+
productionSiteId: {
|
|
2722
|
+
type: 'number',
|
|
2723
|
+
description: 'Filter by production site ID',
|
|
2724
|
+
},
|
|
2725
|
+
stagingSiteId: {
|
|
2726
|
+
type: 'number',
|
|
2727
|
+
description: 'Filter by staging site ID',
|
|
2728
|
+
},
|
|
2729
|
+
limit: {
|
|
2730
|
+
type: 'number',
|
|
2731
|
+
description: 'Max results (default: 20)',
|
|
2732
|
+
},
|
|
2733
|
+
},
|
|
2734
|
+
},
|
|
2735
|
+
},
|
|
2736
|
+
{
|
|
2737
|
+
name: 'fcp_get_dev_environment_info',
|
|
2738
|
+
description: 'Get developer environment info for a site. Shows what capabilities are available (clone to staging, download database, file sync, local setup), current state (latest backup age, clone status), staging sites, and quick action URLs. Use this to help developers understand what they can do with a site.',
|
|
2739
|
+
inputSchema: {
|
|
2740
|
+
type: 'object',
|
|
2741
|
+
properties: {
|
|
2742
|
+
siteId: {
|
|
2743
|
+
type: 'number',
|
|
2744
|
+
description: 'The production site ID to get dev environment info for',
|
|
2745
|
+
},
|
|
2746
|
+
},
|
|
2747
|
+
required: ['siteId'],
|
|
2748
|
+
},
|
|
2749
|
+
},
|
|
2587
2750
|
];
|
|
2588
2751
|
// Register tool handlers
|
|
2589
2752
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -3664,6 +3827,53 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3664
3827
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
3665
3828
|
};
|
|
3666
3829
|
}
|
|
3830
|
+
// Clone to Staging tools
|
|
3831
|
+
case 'fcp_clone_to_staging': {
|
|
3832
|
+
const { productionSiteId, stagingSiteId, includeFiles, includeDatabase, runSearchReplace, backupId } = args;
|
|
3833
|
+
// For MCP callers, auto-generate confirmation token (bypasses type-to-confirm UI)
|
|
3834
|
+
const confirmResult = await client.cloneToStagingConfirm(productionSiteId, stagingSiteId);
|
|
3835
|
+
const token = confirmResult.token;
|
|
3836
|
+
const result = await client.cloneToStaging({
|
|
3837
|
+
productionSiteId,
|
|
3838
|
+
stagingSiteId,
|
|
3839
|
+
includeFiles: includeFiles !== false,
|
|
3840
|
+
includeDatabase: includeDatabase !== false,
|
|
3841
|
+
runSearchReplace: runSearchReplace !== false,
|
|
3842
|
+
confirmationToken: token,
|
|
3843
|
+
backupId,
|
|
3844
|
+
});
|
|
3845
|
+
return {
|
|
3846
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
3847
|
+
};
|
|
3848
|
+
}
|
|
3849
|
+
case 'fcp_clone_confirm': {
|
|
3850
|
+
const { productionSiteId, stagingSiteId } = args;
|
|
3851
|
+
const result = await client.cloneToStagingConfirm(productionSiteId, stagingSiteId);
|
|
3852
|
+
return {
|
|
3853
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
3854
|
+
};
|
|
3855
|
+
}
|
|
3856
|
+
case 'fcp_clone_status': {
|
|
3857
|
+
const { cloneId } = args;
|
|
3858
|
+
const result = await client.getCloneStatus(cloneId);
|
|
3859
|
+
return {
|
|
3860
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
3861
|
+
};
|
|
3862
|
+
}
|
|
3863
|
+
case 'fcp_clone_list': {
|
|
3864
|
+
const { productionSiteId, stagingSiteId, limit } = args;
|
|
3865
|
+
const result = await client.listCloneOperations({ productionSiteId, stagingSiteId, limit });
|
|
3866
|
+
return {
|
|
3867
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
case 'fcp_get_dev_environment_info': {
|
|
3871
|
+
const { siteId } = args;
|
|
3872
|
+
const result = await client.getDevEnvironmentInfo(siteId);
|
|
3873
|
+
return {
|
|
3874
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
3875
|
+
};
|
|
3876
|
+
}
|
|
3667
3877
|
default:
|
|
3668
3878
|
throw new Error(`Unknown tool: ${name}`);
|
|
3669
3879
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fruition/fcp-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.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",
|