@fruition/fcp-mcp-server 1.29.0 → 1.31.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 +19 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +510 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,13 @@ MCP (Model Context Protocol) server that gives Claude Code direct access to the
|
|
|
33
33
|
| `fcp_get_certificate` | Single certificate detail (issuer, SANs, expiry) |
|
|
34
34
|
| `fcp_scan_certificates` | Trigger a cert-monitor scan run |
|
|
35
35
|
|
|
36
|
+
### Cluster Gateway Tools
|
|
37
|
+
|
|
38
|
+
| Tool | Description |
|
|
39
|
+
|------|-------------|
|
|
40
|
+
| `fcp_db_query` | Run a read-only SQL query (SELECT/SHOW/EXPLAIN/DESCRIBE) against a site's read-only DB replica |
|
|
41
|
+
| `fcp_cluster_http` | GET/HEAD an in-cluster Kubernetes Service URL through FCP (devs firewalled off the cluster) |
|
|
42
|
+
|
|
36
43
|
### Tasker Job Control Tools
|
|
37
44
|
|
|
38
45
|
| Tool | Description |
|
|
@@ -43,6 +50,18 @@ MCP (Model Context Protocol) server that gives Claude Code direct access to the
|
|
|
43
50
|
| `fcp_approve_job` | Approve merge for a job needing human sign-off |
|
|
44
51
|
| `fcp_override_merge_window` | Force-allow an auto-merge outside its deploy window (super_admin) |
|
|
45
52
|
|
|
53
|
+
### Site Ops Tools
|
|
54
|
+
|
|
55
|
+
| Tool | Description |
|
|
56
|
+
|------|-------------|
|
|
57
|
+
| `fcp_dns_current` | Current live DNS records (A/CNAME) for a site |
|
|
58
|
+
| `fcp_dns_history` | DNS snapshot history with diffs (filter recordType/limit/since) |
|
|
59
|
+
| `fcp_list_deployments` | Deployments (GitHub Actions runs) across sites + stats |
|
|
60
|
+
| `fcp_get_deployment_status` | Real-time k8s deployment + pod health for a site |
|
|
61
|
+
| `fcp_purge_cache` | Purge edge cache (Varnish + DynamoDB + CloudFront + Cloudflare) (admin) |
|
|
62
|
+
| `fcp_list_maintenance_windows` | List maintenance windows (all, or by-site with `websiteId`) |
|
|
63
|
+
| `fcp_create_maintenance_window` | Create a recurring maintenance window (admin) |
|
|
64
|
+
|
|
46
65
|
### Unroo Task Management Tools
|
|
47
66
|
|
|
48
67
|
| Tool | Description |
|
package/dist/index.d.ts
CHANGED
|
@@ -360,6 +360,76 @@ export declare class FCPClient {
|
|
|
360
360
|
id?: number;
|
|
361
361
|
repo?: string;
|
|
362
362
|
}): Promise<any>;
|
|
363
|
+
getDnsCurrent(websiteId: string | number): Promise<any>;
|
|
364
|
+
getDnsHistory(websiteId: string | number, opts?: {
|
|
365
|
+
recordType?: string;
|
|
366
|
+
limit?: number;
|
|
367
|
+
since?: string;
|
|
368
|
+
}): Promise<any>;
|
|
369
|
+
listDeployments(filters?: {
|
|
370
|
+
websiteIds?: string;
|
|
371
|
+
branches?: string;
|
|
372
|
+
statuses?: string;
|
|
373
|
+
conclusions?: string;
|
|
374
|
+
events?: string;
|
|
375
|
+
actor?: string;
|
|
376
|
+
startDate?: string;
|
|
377
|
+
endDate?: string;
|
|
378
|
+
sortBy?: string;
|
|
379
|
+
sortOrder?: string;
|
|
380
|
+
limit?: number;
|
|
381
|
+
offset?: number;
|
|
382
|
+
}): Promise<any>;
|
|
383
|
+
getDeploymentStatus(siteId: string | number): Promise<any>;
|
|
384
|
+
purgeCache(siteId: string | number, body?: {
|
|
385
|
+
purgeType?: string;
|
|
386
|
+
paths?: string[];
|
|
387
|
+
urls?: string[];
|
|
388
|
+
languages?: string[];
|
|
389
|
+
warmCache?: boolean;
|
|
390
|
+
warmPages?: string[];
|
|
391
|
+
verifyAfterPurge?: boolean;
|
|
392
|
+
}): Promise<any>;
|
|
393
|
+
listMaintenanceWindows(opts?: {
|
|
394
|
+
websiteId?: string | number;
|
|
395
|
+
accountId?: string | number;
|
|
396
|
+
k8sCluster?: string;
|
|
397
|
+
enabledOnly?: boolean;
|
|
398
|
+
}): Promise<any>;
|
|
399
|
+
createMaintenanceWindow(input: {
|
|
400
|
+
name: string;
|
|
401
|
+
recurrence_type: string;
|
|
402
|
+
start_time: string;
|
|
403
|
+
end_time: string;
|
|
404
|
+
website_id?: number;
|
|
405
|
+
account_id?: number;
|
|
406
|
+
k8s_cluster?: string;
|
|
407
|
+
day_of_week?: number;
|
|
408
|
+
weeks_of_month?: number[];
|
|
409
|
+
week_of_month?: number;
|
|
410
|
+
cron_expression?: string;
|
|
411
|
+
description?: string;
|
|
412
|
+
timezone?: string;
|
|
413
|
+
default_severity?: string;
|
|
414
|
+
auto_create_events?: boolean;
|
|
415
|
+
auto_create_lead_time?: number;
|
|
416
|
+
enabled?: boolean;
|
|
417
|
+
created_by?: string;
|
|
418
|
+
}): Promise<any>;
|
|
419
|
+
clusterDbQuery(input: {
|
|
420
|
+
site?: number | string;
|
|
421
|
+
domain?: string;
|
|
422
|
+
sql: string;
|
|
423
|
+
limit?: number;
|
|
424
|
+
}): Promise<any>;
|
|
425
|
+
clusterHttp(input: {
|
|
426
|
+
cluster: string;
|
|
427
|
+
namespace: string;
|
|
428
|
+
service: string;
|
|
429
|
+
port: number;
|
|
430
|
+
path?: string;
|
|
431
|
+
method?: string;
|
|
432
|
+
}): Promise<any>;
|
|
363
433
|
listCertificates(filters?: {
|
|
364
434
|
status?: string;
|
|
365
435
|
cluster?: string;
|
|
@@ -395,6 +465,46 @@ export declare class FCPClient {
|
|
|
395
465
|
reason?: string;
|
|
396
466
|
ttl_minutes?: number;
|
|
397
467
|
}): Promise<any>;
|
|
468
|
+
clusterPods(input: {
|
|
469
|
+
site?: string;
|
|
470
|
+
domain?: string;
|
|
471
|
+
cluster?: string;
|
|
472
|
+
namespace?: string;
|
|
473
|
+
labelSelector?: string;
|
|
474
|
+
}): Promise<any>;
|
|
475
|
+
clusterLogs(input: {
|
|
476
|
+
pod: string;
|
|
477
|
+
site?: string;
|
|
478
|
+
domain?: string;
|
|
479
|
+
cluster?: string;
|
|
480
|
+
namespace?: string;
|
|
481
|
+
}): Promise<any>;
|
|
482
|
+
clusterIngress(input: {
|
|
483
|
+
site?: string;
|
|
484
|
+
domain?: string;
|
|
485
|
+
cluster?: string;
|
|
486
|
+
namespace?: string;
|
|
487
|
+
}): Promise<any>;
|
|
488
|
+
clusterDeployments(input: {
|
|
489
|
+
site?: string;
|
|
490
|
+
domain?: string;
|
|
491
|
+
cluster?: string;
|
|
492
|
+
namespace?: string;
|
|
493
|
+
}): Promise<any>;
|
|
494
|
+
clusterEvents(input: {
|
|
495
|
+
site?: string;
|
|
496
|
+
domain?: string;
|
|
497
|
+
cluster?: string;
|
|
498
|
+
namespace?: string;
|
|
499
|
+
}): Promise<any>;
|
|
500
|
+
clusterDescribe(input: {
|
|
501
|
+
kind: string;
|
|
502
|
+
name: string;
|
|
503
|
+
site?: string;
|
|
504
|
+
domain?: string;
|
|
505
|
+
cluster?: string;
|
|
506
|
+
namespace?: string;
|
|
507
|
+
}): Promise<any>;
|
|
398
508
|
backupListSites(): Promise<any>;
|
|
399
509
|
backupGetConfig(): Promise<any>;
|
|
400
510
|
backupListEligible(): Promise<any>;
|
package/dist/index.js
CHANGED
|
@@ -114,6 +114,8 @@ const TOOL_PERMISSIONS = {
|
|
|
114
114
|
fcp_update_freeze: 'admin',
|
|
115
115
|
fcp_scan_certificates: 'admin',
|
|
116
116
|
fcp_spawn_task_job: 'admin',
|
|
117
|
+
fcp_purge_cache: 'admin',
|
|
118
|
+
fcp_create_maintenance_window: 'admin',
|
|
117
119
|
fcp_approve_job: 'admin',
|
|
118
120
|
fcp_backup_enable: 'admin',
|
|
119
121
|
fcp_backup_trigger: 'admin',
|
|
@@ -165,7 +167,20 @@ const TOOL_PERMISSIONS = {
|
|
|
165
167
|
fcp_trusted_ip_list_ranges: 'viewer',
|
|
166
168
|
fcp_trusted_ip_export: 'viewer',
|
|
167
169
|
fcp_list_freezes: 'viewer',
|
|
170
|
+
fcp_db_query: 'viewer',
|
|
171
|
+
fcp_cluster_http: 'viewer',
|
|
168
172
|
fcp_list_certificates: 'viewer',
|
|
173
|
+
fcp_cluster_pods: 'viewer',
|
|
174
|
+
fcp_cluster_logs: 'viewer',
|
|
175
|
+
fcp_cluster_ingress: 'viewer',
|
|
176
|
+
fcp_cluster_deployments: 'viewer',
|
|
177
|
+
fcp_cluster_events: 'viewer',
|
|
178
|
+
fcp_cluster_describe: 'viewer',
|
|
179
|
+
fcp_dns_current: 'viewer',
|
|
180
|
+
fcp_dns_history: 'viewer',
|
|
181
|
+
fcp_list_deployments: 'viewer',
|
|
182
|
+
fcp_get_deployment_status: 'viewer',
|
|
183
|
+
fcp_list_maintenance_windows: 'viewer',
|
|
169
184
|
fcp_get_certificate: 'viewer',
|
|
170
185
|
fcp_list_jobs: 'viewer',
|
|
171
186
|
fcp_get_job_status: 'viewer',
|
|
@@ -837,6 +852,97 @@ export class FCPClient {
|
|
|
837
852
|
});
|
|
838
853
|
}
|
|
839
854
|
// ============================================================================
|
|
855
|
+
// Site Ops (DNS / Deploy / Maintenance) Methods
|
|
856
|
+
// ============================================================================
|
|
857
|
+
async getDnsCurrent(websiteId) {
|
|
858
|
+
return this.fetch(`/api/dns-history/${encodeURIComponent(String(websiteId))}/current`);
|
|
859
|
+
}
|
|
860
|
+
async getDnsHistory(websiteId, opts) {
|
|
861
|
+
const p = new URLSearchParams();
|
|
862
|
+
if (opts?.recordType)
|
|
863
|
+
p.append('record_type', opts.recordType);
|
|
864
|
+
if (opts?.limit != null)
|
|
865
|
+
p.append('limit', String(opts.limit));
|
|
866
|
+
if (opts?.since)
|
|
867
|
+
p.append('since', opts.since);
|
|
868
|
+
const qs = p.toString();
|
|
869
|
+
return this.fetch(`/api/dns-history/${encodeURIComponent(String(websiteId))}/history${qs ? `?${qs}` : ''}`);
|
|
870
|
+
}
|
|
871
|
+
async listDeployments(filters) {
|
|
872
|
+
const p = new URLSearchParams();
|
|
873
|
+
if (filters?.websiteIds)
|
|
874
|
+
p.append('websiteIds', filters.websiteIds);
|
|
875
|
+
if (filters?.branches)
|
|
876
|
+
p.append('branches', filters.branches);
|
|
877
|
+
if (filters?.statuses)
|
|
878
|
+
p.append('statuses', filters.statuses);
|
|
879
|
+
if (filters?.conclusions)
|
|
880
|
+
p.append('conclusions', filters.conclusions);
|
|
881
|
+
if (filters?.events)
|
|
882
|
+
p.append('events', filters.events);
|
|
883
|
+
if (filters?.actor)
|
|
884
|
+
p.append('actor', filters.actor);
|
|
885
|
+
if (filters?.startDate)
|
|
886
|
+
p.append('startDate', filters.startDate);
|
|
887
|
+
if (filters?.endDate)
|
|
888
|
+
p.append('endDate', filters.endDate);
|
|
889
|
+
if (filters?.sortBy)
|
|
890
|
+
p.append('sortBy', filters.sortBy);
|
|
891
|
+
if (filters?.sortOrder)
|
|
892
|
+
p.append('sortOrder', filters.sortOrder);
|
|
893
|
+
if (filters?.limit != null)
|
|
894
|
+
p.append('limit', String(filters.limit));
|
|
895
|
+
if (filters?.offset != null)
|
|
896
|
+
p.append('offset', String(filters.offset));
|
|
897
|
+
const qs = p.toString();
|
|
898
|
+
return this.fetch(`/api/deployments${qs ? `?${qs}` : ''}`);
|
|
899
|
+
}
|
|
900
|
+
async getDeploymentStatus(siteId) {
|
|
901
|
+
return this.fetch(`/api/sites/${encodeURIComponent(String(siteId))}/deployment-status`);
|
|
902
|
+
}
|
|
903
|
+
async purgeCache(siteId, body) {
|
|
904
|
+
return this.fetch(`/api/sites/${encodeURIComponent(String(siteId))}/cache-purge`, {
|
|
905
|
+
method: 'POST',
|
|
906
|
+
body: JSON.stringify(body || {}),
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
async listMaintenanceWindows(opts) {
|
|
910
|
+
// When a websiteId is provided, use the by-site view (site + account windows).
|
|
911
|
+
if (opts?.websiteId != null) {
|
|
912
|
+
return this.fetch(`/api/maintenance/windows/by-site?website_id=${encodeURIComponent(String(opts.websiteId))}`);
|
|
913
|
+
}
|
|
914
|
+
const p = new URLSearchParams();
|
|
915
|
+
if (opts?.accountId != null)
|
|
916
|
+
p.append('account_id', String(opts.accountId));
|
|
917
|
+
if (opts?.k8sCluster)
|
|
918
|
+
p.append('k8s_cluster', opts.k8sCluster);
|
|
919
|
+
if (opts?.enabledOnly)
|
|
920
|
+
p.append('enabled', 'true');
|
|
921
|
+
const qs = p.toString();
|
|
922
|
+
return this.fetch(`/api/maintenance/windows${qs ? `?${qs}` : ''}`);
|
|
923
|
+
}
|
|
924
|
+
async createMaintenanceWindow(input) {
|
|
925
|
+
return this.fetch('/api/maintenance/windows', {
|
|
926
|
+
method: 'POST',
|
|
927
|
+
body: JSON.stringify(input),
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
// ============================================================================
|
|
931
|
+
// Cluster DB + HTTP Methods
|
|
932
|
+
// ============================================================================
|
|
933
|
+
async clusterDbQuery(input) {
|
|
934
|
+
return this.fetch('/api/cluster/db-query', {
|
|
935
|
+
method: 'POST',
|
|
936
|
+
body: JSON.stringify(input),
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
async clusterHttp(input) {
|
|
940
|
+
return this.fetch('/api/cluster/http', {
|
|
941
|
+
method: 'POST',
|
|
942
|
+
body: JSON.stringify(input),
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
// ============================================================================
|
|
840
946
|
// Certificate Methods
|
|
841
947
|
// ============================================================================
|
|
842
948
|
async listCertificates(filters) {
|
|
@@ -910,6 +1016,90 @@ export class FCPClient {
|
|
|
910
1016
|
return this.fetch(path, { method: 'POST', body: JSON.stringify(body) });
|
|
911
1017
|
}
|
|
912
1018
|
// ============================================================================
|
|
1019
|
+
// Cluster Info (read-only) Methods
|
|
1020
|
+
// ============================================================================
|
|
1021
|
+
async clusterPods(input) {
|
|
1022
|
+
const p = new URLSearchParams();
|
|
1023
|
+
if (input.site)
|
|
1024
|
+
p.append('site', input.site);
|
|
1025
|
+
if (input.domain)
|
|
1026
|
+
p.append('domain', input.domain);
|
|
1027
|
+
if (input.cluster)
|
|
1028
|
+
p.append('cluster', input.cluster);
|
|
1029
|
+
if (input.namespace)
|
|
1030
|
+
p.append('namespace', input.namespace);
|
|
1031
|
+
if (input.labelSelector)
|
|
1032
|
+
p.append('labelSelector', input.labelSelector);
|
|
1033
|
+
const qs = p.toString();
|
|
1034
|
+
return this.fetch(`/api/cluster/info/pods${qs ? `?${qs}` : ''}`);
|
|
1035
|
+
}
|
|
1036
|
+
async clusterLogs(input) {
|
|
1037
|
+
const p = new URLSearchParams();
|
|
1038
|
+
p.append('pod', input.pod);
|
|
1039
|
+
if (input.site)
|
|
1040
|
+
p.append('site', input.site);
|
|
1041
|
+
if (input.domain)
|
|
1042
|
+
p.append('domain', input.domain);
|
|
1043
|
+
if (input.cluster)
|
|
1044
|
+
p.append('cluster', input.cluster);
|
|
1045
|
+
if (input.namespace)
|
|
1046
|
+
p.append('namespace', input.namespace);
|
|
1047
|
+
return this.fetch(`/api/cluster/info/logs?${p.toString()}`);
|
|
1048
|
+
}
|
|
1049
|
+
async clusterIngress(input) {
|
|
1050
|
+
const p = new URLSearchParams();
|
|
1051
|
+
if (input.site)
|
|
1052
|
+
p.append('site', input.site);
|
|
1053
|
+
if (input.domain)
|
|
1054
|
+
p.append('domain', input.domain);
|
|
1055
|
+
if (input.cluster)
|
|
1056
|
+
p.append('cluster', input.cluster);
|
|
1057
|
+
if (input.namespace)
|
|
1058
|
+
p.append('namespace', input.namespace);
|
|
1059
|
+
const qs = p.toString();
|
|
1060
|
+
return this.fetch(`/api/cluster/info/ingress${qs ? `?${qs}` : ''}`);
|
|
1061
|
+
}
|
|
1062
|
+
async clusterDeployments(input) {
|
|
1063
|
+
const p = new URLSearchParams();
|
|
1064
|
+
if (input.site)
|
|
1065
|
+
p.append('site', input.site);
|
|
1066
|
+
if (input.domain)
|
|
1067
|
+
p.append('domain', input.domain);
|
|
1068
|
+
if (input.cluster)
|
|
1069
|
+
p.append('cluster', input.cluster);
|
|
1070
|
+
if (input.namespace)
|
|
1071
|
+
p.append('namespace', input.namespace);
|
|
1072
|
+
const qs = p.toString();
|
|
1073
|
+
return this.fetch(`/api/cluster/info/deployments${qs ? `?${qs}` : ''}`);
|
|
1074
|
+
}
|
|
1075
|
+
async clusterEvents(input) {
|
|
1076
|
+
const p = new URLSearchParams();
|
|
1077
|
+
if (input.site)
|
|
1078
|
+
p.append('site', input.site);
|
|
1079
|
+
if (input.domain)
|
|
1080
|
+
p.append('domain', input.domain);
|
|
1081
|
+
if (input.cluster)
|
|
1082
|
+
p.append('cluster', input.cluster);
|
|
1083
|
+
if (input.namespace)
|
|
1084
|
+
p.append('namespace', input.namespace);
|
|
1085
|
+
const qs = p.toString();
|
|
1086
|
+
return this.fetch(`/api/cluster/info/events${qs ? `?${qs}` : ''}`);
|
|
1087
|
+
}
|
|
1088
|
+
async clusterDescribe(input) {
|
|
1089
|
+
const p = new URLSearchParams();
|
|
1090
|
+
p.append('kind', input.kind);
|
|
1091
|
+
p.append('name', input.name);
|
|
1092
|
+
if (input.site)
|
|
1093
|
+
p.append('site', input.site);
|
|
1094
|
+
if (input.domain)
|
|
1095
|
+
p.append('domain', input.domain);
|
|
1096
|
+
if (input.cluster)
|
|
1097
|
+
p.append('cluster', input.cluster);
|
|
1098
|
+
if (input.namespace)
|
|
1099
|
+
p.append('namespace', input.namespace);
|
|
1100
|
+
return this.fetch(`/api/cluster/info/describe?${p.toString()}`);
|
|
1101
|
+
}
|
|
1102
|
+
// ============================================================================
|
|
913
1103
|
// Backup Management Methods
|
|
914
1104
|
// ============================================================================
|
|
915
1105
|
async backupListSites() {
|
|
@@ -3240,6 +3430,166 @@ const TOOLS = [
|
|
|
3240
3430
|
},
|
|
3241
3431
|
},
|
|
3242
3432
|
// ============================================================================
|
|
3433
|
+
// Site Ops (DNS / Deploy / Maintenance) Tools
|
|
3434
|
+
// ============================================================================
|
|
3435
|
+
{
|
|
3436
|
+
name: 'fcp_dns_current',
|
|
3437
|
+
description: 'Get the current live DNS records (A / CNAME) for a site via a fresh lookup. Use before/after a migration or DNS cutover to confirm where a domain resolves now.',
|
|
3438
|
+
inputSchema: {
|
|
3439
|
+
type: 'object',
|
|
3440
|
+
properties: {
|
|
3441
|
+
websiteId: { type: 'number', description: 'FCP website id' },
|
|
3442
|
+
},
|
|
3443
|
+
required: ['websiteId'],
|
|
3444
|
+
},
|
|
3445
|
+
},
|
|
3446
|
+
{
|
|
3447
|
+
name: 'fcp_dns_history',
|
|
3448
|
+
description: 'Get the DNS snapshot history for a site, including diffs where records changed. Optionally filter by recordType (A|CNAME), limit, and since (ISO date).',
|
|
3449
|
+
inputSchema: {
|
|
3450
|
+
type: 'object',
|
|
3451
|
+
properties: {
|
|
3452
|
+
websiteId: { type: 'number', description: 'FCP website id' },
|
|
3453
|
+
recordType: { type: 'string', enum: ['A', 'CNAME'], description: 'Filter by record type' },
|
|
3454
|
+
limit: { type: 'number', description: 'Max number of snapshots to return' },
|
|
3455
|
+
since: { type: 'string', description: 'ISO date; only snapshots on/after this time' },
|
|
3456
|
+
},
|
|
3457
|
+
required: ['websiteId'],
|
|
3458
|
+
},
|
|
3459
|
+
},
|
|
3460
|
+
{
|
|
3461
|
+
name: 'fcp_list_deployments',
|
|
3462
|
+
description: 'List deployments (GitHub Actions workflow runs) across all sites, with overall stats. Filter by websiteIds, branches, statuses, conclusions, events, actor, date range; paginate and sort.',
|
|
3463
|
+
inputSchema: {
|
|
3464
|
+
type: 'object',
|
|
3465
|
+
properties: {
|
|
3466
|
+
websiteIds: { type: 'string', description: 'Comma-separated website ids' },
|
|
3467
|
+
branches: { type: 'string', description: 'Comma-separated branch names' },
|
|
3468
|
+
statuses: { type: 'string', description: 'Comma-separated statuses' },
|
|
3469
|
+
conclusions: { type: 'string', description: 'Comma-separated conclusions (success|failure|...)' },
|
|
3470
|
+
events: { type: 'string', description: "Comma-separated events; 'all' to include PR builds" },
|
|
3471
|
+
actor: { type: 'string', description: 'Filter by GitHub actor' },
|
|
3472
|
+
startDate: { type: 'string', description: 'ISO date lower bound' },
|
|
3473
|
+
endDate: { type: 'string', description: 'ISO date upper bound' },
|
|
3474
|
+
sortBy: { type: 'string', enum: ['created_at', 'completed_at', 'duration'], description: 'Sort field' },
|
|
3475
|
+
sortOrder: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default desc)' },
|
|
3476
|
+
limit: { type: 'number', description: 'Page size (default 50)' },
|
|
3477
|
+
offset: { type: 'number', description: 'Offset (default 0)' },
|
|
3478
|
+
},
|
|
3479
|
+
},
|
|
3480
|
+
},
|
|
3481
|
+
{
|
|
3482
|
+
name: 'fcp_get_deployment_status',
|
|
3483
|
+
description: 'Get real-time Kubernetes deployment + pod health for a site (replicas, pod status, image, conditions, latest GitHub run). Use to confirm a deploy rolled out cleanly.',
|
|
3484
|
+
inputSchema: {
|
|
3485
|
+
type: 'object',
|
|
3486
|
+
properties: {
|
|
3487
|
+
siteId: { type: 'number', description: 'FCP site (website) id' },
|
|
3488
|
+
},
|
|
3489
|
+
required: ['siteId'],
|
|
3490
|
+
},
|
|
3491
|
+
},
|
|
3492
|
+
{
|
|
3493
|
+
name: 'fcp_purge_cache',
|
|
3494
|
+
description: 'Purge a site cache across configured edge layers (Varnish + DynamoDB + CloudFront + Cloudflare). Defaults to purging everything; pass paths/urls + purgeType for a targeted purge, languages for translation cache, and warmCache/verifyAfterPurge to follow up. Edge infrastructure must be configured for the site.',
|
|
3495
|
+
inputSchema: {
|
|
3496
|
+
type: 'object',
|
|
3497
|
+
properties: {
|
|
3498
|
+
siteId: { type: 'number', description: 'FCP site (website) id' },
|
|
3499
|
+
purgeType: {
|
|
3500
|
+
type: 'string',
|
|
3501
|
+
enum: ['all', 'urls', 'single', 'cloudfront-only', 'cloudflare-only', 'dynamodb-only', 'varnish-only'],
|
|
3502
|
+
description: "Which layers/scope to purge (default 'all')",
|
|
3503
|
+
},
|
|
3504
|
+
paths: { type: 'array', items: { type: 'string' }, description: "Paths to purge (default ['/*'])" },
|
|
3505
|
+
urls: { type: 'array', items: { type: 'string' }, description: 'Specific full URLs to purge (urls/single)' },
|
|
3506
|
+
languages: { type: 'array', items: { type: 'string' }, description: 'Language codes for translation cache' },
|
|
3507
|
+
warmCache: { type: 'boolean', description: 'Warm cache after a successful purge' },
|
|
3508
|
+
warmPages: { type: 'array', items: { type: 'string' }, description: 'Pages to warm if warmCache=true' },
|
|
3509
|
+
verifyAfterPurge: { type: 'boolean', description: 'Verify the site after purge' },
|
|
3510
|
+
},
|
|
3511
|
+
required: ['siteId'],
|
|
3512
|
+
},
|
|
3513
|
+
},
|
|
3514
|
+
{
|
|
3515
|
+
name: 'fcp_list_maintenance_windows',
|
|
3516
|
+
description: 'List recurring maintenance windows. With no args lists all windows; pass websiteId to get the windows that apply to a specific site (direct + account-scoped, with next occurrence). Also filter by accountId, k8sCluster, enabledOnly.',
|
|
3517
|
+
inputSchema: {
|
|
3518
|
+
type: 'object',
|
|
3519
|
+
properties: {
|
|
3520
|
+
websiteId: { type: 'number', description: 'Site id — switches to the by-site view (site + account windows)' },
|
|
3521
|
+
accountId: { type: 'number', description: 'Filter by account id (list view)' },
|
|
3522
|
+
k8sCluster: { type: 'string', description: 'Filter by k8s cluster (list view)' },
|
|
3523
|
+
enabledOnly: { type: 'boolean', description: 'Only enabled windows (list view)' },
|
|
3524
|
+
},
|
|
3525
|
+
},
|
|
3526
|
+
},
|
|
3527
|
+
{
|
|
3528
|
+
name: 'fcp_create_maintenance_window',
|
|
3529
|
+
description: 'Create a recurring maintenance window. Requires name, recurrence_type, start_time, end_time, and a scope (one of website_id, account_id, k8s_cluster). day_of_week is required for weekly/biweekly/bimonthly/monthly. timezone defaults to America/Denver.',
|
|
3530
|
+
inputSchema: {
|
|
3531
|
+
type: 'object',
|
|
3532
|
+
properties: {
|
|
3533
|
+
name: { type: 'string', description: 'Window name' },
|
|
3534
|
+
recurrence_type: {
|
|
3535
|
+
type: 'string',
|
|
3536
|
+
enum: ['weekly', 'biweekly', 'bimonthly', 'monthly', 'custom'],
|
|
3537
|
+
description: 'Recurrence type',
|
|
3538
|
+
},
|
|
3539
|
+
start_time: { type: 'string', description: 'Start time HH:MM[:SS]' },
|
|
3540
|
+
end_time: { type: 'string', description: 'End time HH:MM[:SS]' },
|
|
3541
|
+
website_id: { type: 'number', description: 'Scope: site id' },
|
|
3542
|
+
account_id: { type: 'number', description: 'Scope: account id' },
|
|
3543
|
+
k8s_cluster: { type: 'string', description: 'Scope: k8s cluster (INFRA-wide)' },
|
|
3544
|
+
day_of_week: { type: 'number', description: 'Day of week 0=Sun..6=Sat (required for weekly/biweekly/bimonthly/monthly)' },
|
|
3545
|
+
weeks_of_month: { type: 'array', items: { type: 'number' }, description: 'Weeks of month for bimonthly (e.g. [1,3])' },
|
|
3546
|
+
week_of_month: { type: 'number', description: 'Week of month for monthly' },
|
|
3547
|
+
cron_expression: { type: 'string', description: 'Cron expression for custom recurrence' },
|
|
3548
|
+
description: { type: 'string', description: 'Optional description' },
|
|
3549
|
+
timezone: { type: 'string', description: "IANA tz (default 'America/Denver')" },
|
|
3550
|
+
default_severity: { type: 'string', description: "Default event severity (default 'low')" },
|
|
3551
|
+
auto_create_events: { type: 'boolean', description: 'Auto-create events for occurrences' },
|
|
3552
|
+
auto_create_lead_time: { type: 'number', description: 'Hours of lead time for auto-created events (default 24)' },
|
|
3553
|
+
enabled: { type: 'boolean', description: 'Enabled (default true)' },
|
|
3554
|
+
created_by: { type: 'string', description: 'Creator identifier' },
|
|
3555
|
+
},
|
|
3556
|
+
required: ['name', 'recurrence_type', 'start_time', 'end_time'],
|
|
3557
|
+
},
|
|
3558
|
+
},
|
|
3559
|
+
// ============================================================================
|
|
3560
|
+
// Cluster DB + HTTP Tools
|
|
3561
|
+
// ============================================================================
|
|
3562
|
+
{
|
|
3563
|
+
name: 'fcp_db_query',
|
|
3564
|
+
description: 'Run a READ-ONLY SQL query against a site\'s read-only DB replica. Provide either site (numeric id) or domain, plus a single SELECT/SHOW/EXPLAIN/DESCRIBE statement. Writes/DDL/multi-statement are rejected. Rows are capped (default 200). Use to inspect site data without cluster access.',
|
|
3565
|
+
inputSchema: {
|
|
3566
|
+
type: 'object',
|
|
3567
|
+
properties: {
|
|
3568
|
+
site: { type: 'number', description: 'Site id (provide site OR domain)' },
|
|
3569
|
+
domain: { type: 'string', description: 'Site domain (provide site OR domain)' },
|
|
3570
|
+
sql: { type: 'string', description: 'Single read-only statement (SELECT/SHOW/EXPLAIN/DESCRIBE)' },
|
|
3571
|
+
limit: { type: 'number', description: 'Max rows to return (default 200, max 1000)' },
|
|
3572
|
+
},
|
|
3573
|
+
required: ['sql'],
|
|
3574
|
+
},
|
|
3575
|
+
},
|
|
3576
|
+
{
|
|
3577
|
+
name: 'fcp_cluster_http',
|
|
3578
|
+
description: 'Fetch an in-cluster Kubernetes Service URL through FCP (devs are firewalled off the cluster). Builds http://<service>.<namespace>:<port>/<path>. Only GET/HEAD allowed; response body is size-capped. Use to hit health/metrics/internal endpoints.',
|
|
3579
|
+
inputSchema: {
|
|
3580
|
+
type: 'object',
|
|
3581
|
+
properties: {
|
|
3582
|
+
cluster: { type: 'string', description: 'Target cluster name (recorded for audit)' },
|
|
3583
|
+
namespace: { type: 'string', description: 'Kubernetes namespace' },
|
|
3584
|
+
service: { type: 'string', description: 'Kubernetes Service name' },
|
|
3585
|
+
port: { type: 'number', description: 'Service port' },
|
|
3586
|
+
path: { type: 'string', description: 'Request path (default /)' },
|
|
3587
|
+
method: { type: 'string', enum: ['GET', 'HEAD'], description: 'HTTP method (default GET)' },
|
|
3588
|
+
},
|
|
3589
|
+
required: ['cluster', 'namespace', 'service', 'port'],
|
|
3590
|
+
},
|
|
3591
|
+
},
|
|
3592
|
+
// ============================================================================
|
|
3243
3593
|
// Certificate Tools
|
|
3244
3594
|
// ============================================================================
|
|
3245
3595
|
{
|
|
@@ -3338,6 +3688,93 @@ const TOOLS = [
|
|
|
3338
3688
|
},
|
|
3339
3689
|
},
|
|
3340
3690
|
// ============================================================================
|
|
3691
|
+
// Cluster Info (read-only) Tools
|
|
3692
|
+
// ============================================================================
|
|
3693
|
+
{
|
|
3694
|
+
name: 'fcp_cluster_pods',
|
|
3695
|
+
description: 'List pods in a namespace (read-only cluster gateway for firewalled devs). Target a namespace by EITHER a site (\"site\"/\"domain\") OR an explicit \"cluster\"+\"namespace\" pair. Optional labelSelector.',
|
|
3696
|
+
inputSchema: {
|
|
3697
|
+
type: 'object',
|
|
3698
|
+
properties: {
|
|
3699
|
+
site: { type: 'string', description: 'Website id or domain (resolves cluster+namespace)' },
|
|
3700
|
+
domain: { type: 'string', description: 'Alias for site: a domain to resolve' },
|
|
3701
|
+
cluster: { type: 'string', description: 'Explicit cluster name (with namespace)' },
|
|
3702
|
+
namespace: { type: 'string', description: 'Explicit namespace (with cluster)' },
|
|
3703
|
+
labelSelector: { type: 'string', description: 'Optional label selector (e.g. app=web)' },
|
|
3704
|
+
},
|
|
3705
|
+
},
|
|
3706
|
+
},
|
|
3707
|
+
{
|
|
3708
|
+
name: 'fcp_cluster_logs',
|
|
3709
|
+
description: 'Tail logs (last 1000 lines) for a pod (read-only cluster gateway). Requires pod. Target a namespace by EITHER a site (\"site\"/\"domain\") OR an explicit \"cluster\"+\"namespace\" pair.',
|
|
3710
|
+
inputSchema: {
|
|
3711
|
+
type: 'object',
|
|
3712
|
+
properties: {
|
|
3713
|
+
pod: { type: 'string', description: 'Pod name to read logs from' },
|
|
3714
|
+
site: { type: 'string', description: 'Website id or domain (resolves cluster+namespace)' },
|
|
3715
|
+
domain: { type: 'string', description: 'Alias for site: a domain to resolve' },
|
|
3716
|
+
cluster: { type: 'string', description: 'Explicit cluster name (with namespace)' },
|
|
3717
|
+
namespace: { type: 'string', description: 'Explicit namespace (with cluster)' },
|
|
3718
|
+
},
|
|
3719
|
+
required: ['pod'],
|
|
3720
|
+
},
|
|
3721
|
+
},
|
|
3722
|
+
{
|
|
3723
|
+
name: 'fcp_cluster_ingress',
|
|
3724
|
+
description: 'List ingresses in a namespace (read-only cluster gateway). Target a namespace by EITHER a site (\"site\"/\"domain\") OR an explicit \"cluster\"+\"namespace\" pair.',
|
|
3725
|
+
inputSchema: {
|
|
3726
|
+
type: 'object',
|
|
3727
|
+
properties: {
|
|
3728
|
+
site: { type: 'string', description: 'Website id or domain (resolves cluster+namespace)' },
|
|
3729
|
+
domain: { type: 'string', description: 'Alias for site: a domain to resolve' },
|
|
3730
|
+
cluster: { type: 'string', description: 'Explicit cluster name (with namespace)' },
|
|
3731
|
+
namespace: { type: 'string', description: 'Explicit namespace (with cluster)' },
|
|
3732
|
+
},
|
|
3733
|
+
},
|
|
3734
|
+
},
|
|
3735
|
+
{
|
|
3736
|
+
name: 'fcp_cluster_deployments',
|
|
3737
|
+
description: 'List deployments in a namespace (read-only cluster gateway). Target a namespace by EITHER a site (\"site\"/\"domain\") OR an explicit \"cluster\"+\"namespace\" pair.',
|
|
3738
|
+
inputSchema: {
|
|
3739
|
+
type: 'object',
|
|
3740
|
+
properties: {
|
|
3741
|
+
site: { type: 'string', description: 'Website id or domain (resolves cluster+namespace)' },
|
|
3742
|
+
domain: { type: 'string', description: 'Alias for site: a domain to resolve' },
|
|
3743
|
+
cluster: { type: 'string', description: 'Explicit cluster name (with namespace)' },
|
|
3744
|
+
namespace: { type: 'string', description: 'Explicit namespace (with cluster)' },
|
|
3745
|
+
},
|
|
3746
|
+
},
|
|
3747
|
+
},
|
|
3748
|
+
{
|
|
3749
|
+
name: 'fcp_cluster_events',
|
|
3750
|
+
description: 'List namespace events sorted by lastTimestamp (read-only cluster gateway). Target a namespace by EITHER a site (\"site\"/\"domain\") OR an explicit \"cluster\"+\"namespace\" pair.',
|
|
3751
|
+
inputSchema: {
|
|
3752
|
+
type: 'object',
|
|
3753
|
+
properties: {
|
|
3754
|
+
site: { type: 'string', description: 'Website id or domain (resolves cluster+namespace)' },
|
|
3755
|
+
domain: { type: 'string', description: 'Alias for site: a domain to resolve' },
|
|
3756
|
+
cluster: { type: 'string', description: 'Explicit cluster name (with namespace)' },
|
|
3757
|
+
namespace: { type: 'string', description: 'Explicit namespace (with cluster)' },
|
|
3758
|
+
},
|
|
3759
|
+
},
|
|
3760
|
+
},
|
|
3761
|
+
{
|
|
3762
|
+
name: 'fcp_cluster_describe',
|
|
3763
|
+
description: 'kubectl describe <kind> <name> in a namespace (read-only cluster gateway). Requires kind + name. Target a namespace by EITHER a site (\"site\"/\"domain\") OR an explicit \"cluster\"+\"namespace\" pair.',
|
|
3764
|
+
inputSchema: {
|
|
3765
|
+
type: 'object',
|
|
3766
|
+
properties: {
|
|
3767
|
+
kind: { type: 'string', description: 'Resource kind (e.g. pod, deployment, ingress)' },
|
|
3768
|
+
name: { type: 'string', description: 'Resource name to describe' },
|
|
3769
|
+
site: { type: 'string', description: 'Website id or domain (resolves cluster+namespace)' },
|
|
3770
|
+
domain: { type: 'string', description: 'Alias for site: a domain to resolve' },
|
|
3771
|
+
cluster: { type: 'string', description: 'Explicit cluster name (with namespace)' },
|
|
3772
|
+
namespace: { type: 'string', description: 'Explicit namespace (with cluster)' },
|
|
3773
|
+
},
|
|
3774
|
+
required: ['kind', 'name'],
|
|
3775
|
+
},
|
|
3776
|
+
},
|
|
3777
|
+
// ============================================================================
|
|
3341
3778
|
// Backup Management Tools
|
|
3342
3779
|
// ============================================================================
|
|
3343
3780
|
{
|
|
@@ -5225,6 +5662,52 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5225
5662
|
};
|
|
5226
5663
|
}
|
|
5227
5664
|
// ============================================================================
|
|
5665
|
+
// Site Ops (DNS / Deploy / Maintenance) Handlers
|
|
5666
|
+
// ============================================================================
|
|
5667
|
+
case 'fcp_dns_current': {
|
|
5668
|
+
const { websiteId } = args;
|
|
5669
|
+
const result = await client.getDnsCurrent(websiteId);
|
|
5670
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5671
|
+
}
|
|
5672
|
+
case 'fcp_dns_history': {
|
|
5673
|
+
const { websiteId, recordType, limit, since } = args;
|
|
5674
|
+
const result = await client.getDnsHistory(websiteId, { recordType, limit, since });
|
|
5675
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5676
|
+
}
|
|
5677
|
+
case 'fcp_list_deployments': {
|
|
5678
|
+
const result = await client.listDeployments(args);
|
|
5679
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5680
|
+
}
|
|
5681
|
+
case 'fcp_get_deployment_status': {
|
|
5682
|
+
const { siteId } = args;
|
|
5683
|
+
const result = await client.getDeploymentStatus(siteId);
|
|
5684
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5685
|
+
}
|
|
5686
|
+
case 'fcp_purge_cache': {
|
|
5687
|
+
const { siteId, ...body } = args;
|
|
5688
|
+
const result = await client.purgeCache(siteId, body);
|
|
5689
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5690
|
+
}
|
|
5691
|
+
case 'fcp_list_maintenance_windows': {
|
|
5692
|
+
const result = await client.listMaintenanceWindows(args);
|
|
5693
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5694
|
+
}
|
|
5695
|
+
case 'fcp_create_maintenance_window': {
|
|
5696
|
+
const result = await client.createMaintenanceWindow(args);
|
|
5697
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5698
|
+
}
|
|
5699
|
+
// ============================================================================
|
|
5700
|
+
// Cluster DB + HTTP Handlers
|
|
5701
|
+
// ============================================================================
|
|
5702
|
+
case 'fcp_db_query': {
|
|
5703
|
+
const result = await client.clusterDbQuery(args);
|
|
5704
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5705
|
+
}
|
|
5706
|
+
case 'fcp_cluster_http': {
|
|
5707
|
+
const result = await client.clusterHttp(args);
|
|
5708
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5709
|
+
}
|
|
5710
|
+
// ============================================================================
|
|
5228
5711
|
// Certificate Handlers
|
|
5229
5712
|
// ============================================================================
|
|
5230
5713
|
case 'fcp_list_certificates': {
|
|
@@ -5273,6 +5756,33 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
5273
5756
|
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5274
5757
|
}
|
|
5275
5758
|
// ============================================================================
|
|
5759
|
+
// Cluster Info (read-only) Handlers
|
|
5760
|
+
// ============================================================================
|
|
5761
|
+
case 'fcp_cluster_pods': {
|
|
5762
|
+
const result = await client.clusterPods(args);
|
|
5763
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5764
|
+
}
|
|
5765
|
+
case 'fcp_cluster_logs': {
|
|
5766
|
+
const result = await client.clusterLogs(args);
|
|
5767
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5768
|
+
}
|
|
5769
|
+
case 'fcp_cluster_ingress': {
|
|
5770
|
+
const result = await client.clusterIngress(args);
|
|
5771
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5772
|
+
}
|
|
5773
|
+
case 'fcp_cluster_deployments': {
|
|
5774
|
+
const result = await client.clusterDeployments(args);
|
|
5775
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5776
|
+
}
|
|
5777
|
+
case 'fcp_cluster_events': {
|
|
5778
|
+
const result = await client.clusterEvents(args);
|
|
5779
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5780
|
+
}
|
|
5781
|
+
case 'fcp_cluster_describe': {
|
|
5782
|
+
const result = await client.clusterDescribe(args);
|
|
5783
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
5784
|
+
}
|
|
5785
|
+
// ============================================================================
|
|
5276
5786
|
// Backup Management Handlers
|
|
5277
5787
|
// ============================================================================
|
|
5278
5788
|
case 'fcp_backup_list_sites': {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fruition/fcp-mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.31.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",
|