@fruition/fcp-mcp-server 1.29.0 → 1.30.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 CHANGED
@@ -43,6 +43,18 @@ MCP (Model Context Protocol) server that gives Claude Code direct access to the
43
43
  | `fcp_approve_job` | Approve merge for a job needing human sign-off |
44
44
  | `fcp_override_merge_window` | Force-allow an auto-merge outside its deploy window (super_admin) |
45
45
 
46
+ ### Site Ops Tools
47
+
48
+ | Tool | Description |
49
+ |------|-------------|
50
+ | `fcp_dns_current` | Current live DNS records (A/CNAME) for a site |
51
+ | `fcp_dns_history` | DNS snapshot history with diffs (filter recordType/limit/since) |
52
+ | `fcp_list_deployments` | Deployments (GitHub Actions runs) across sites + stats |
53
+ | `fcp_get_deployment_status` | Real-time k8s deployment + pod health for a site |
54
+ | `fcp_purge_cache` | Purge edge cache (Varnish + DynamoDB + CloudFront + Cloudflare) (admin) |
55
+ | `fcp_list_maintenance_windows` | List maintenance windows (all, or by-site with `websiteId`) |
56
+ | `fcp_create_maintenance_window` | Create a recurring maintenance window (admin) |
57
+
46
58
  ### Unroo Task Management Tools
47
59
 
48
60
  | Tool | Description |
package/dist/index.d.ts CHANGED
@@ -360,6 +360,62 @@ 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>;
363
419
  listCertificates(filters?: {
364
420
  status?: string;
365
421
  cluster?: string;
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',
@@ -166,6 +168,11 @@ const TOOL_PERMISSIONS = {
166
168
  fcp_trusted_ip_export: 'viewer',
167
169
  fcp_list_freezes: 'viewer',
168
170
  fcp_list_certificates: 'viewer',
171
+ fcp_dns_current: 'viewer',
172
+ fcp_dns_history: 'viewer',
173
+ fcp_list_deployments: 'viewer',
174
+ fcp_get_deployment_status: 'viewer',
175
+ fcp_list_maintenance_windows: 'viewer',
169
176
  fcp_get_certificate: 'viewer',
170
177
  fcp_list_jobs: 'viewer',
171
178
  fcp_get_job_status: 'viewer',
@@ -837,6 +844,82 @@ export class FCPClient {
837
844
  });
838
845
  }
839
846
  // ============================================================================
847
+ // Site Ops (DNS / Deploy / Maintenance) Methods
848
+ // ============================================================================
849
+ async getDnsCurrent(websiteId) {
850
+ return this.fetch(`/api/dns-history/${encodeURIComponent(String(websiteId))}/current`);
851
+ }
852
+ async getDnsHistory(websiteId, opts) {
853
+ const p = new URLSearchParams();
854
+ if (opts?.recordType)
855
+ p.append('record_type', opts.recordType);
856
+ if (opts?.limit != null)
857
+ p.append('limit', String(opts.limit));
858
+ if (opts?.since)
859
+ p.append('since', opts.since);
860
+ const qs = p.toString();
861
+ return this.fetch(`/api/dns-history/${encodeURIComponent(String(websiteId))}/history${qs ? `?${qs}` : ''}`);
862
+ }
863
+ async listDeployments(filters) {
864
+ const p = new URLSearchParams();
865
+ if (filters?.websiteIds)
866
+ p.append('websiteIds', filters.websiteIds);
867
+ if (filters?.branches)
868
+ p.append('branches', filters.branches);
869
+ if (filters?.statuses)
870
+ p.append('statuses', filters.statuses);
871
+ if (filters?.conclusions)
872
+ p.append('conclusions', filters.conclusions);
873
+ if (filters?.events)
874
+ p.append('events', filters.events);
875
+ if (filters?.actor)
876
+ p.append('actor', filters.actor);
877
+ if (filters?.startDate)
878
+ p.append('startDate', filters.startDate);
879
+ if (filters?.endDate)
880
+ p.append('endDate', filters.endDate);
881
+ if (filters?.sortBy)
882
+ p.append('sortBy', filters.sortBy);
883
+ if (filters?.sortOrder)
884
+ p.append('sortOrder', filters.sortOrder);
885
+ if (filters?.limit != null)
886
+ p.append('limit', String(filters.limit));
887
+ if (filters?.offset != null)
888
+ p.append('offset', String(filters.offset));
889
+ const qs = p.toString();
890
+ return this.fetch(`/api/deployments${qs ? `?${qs}` : ''}`);
891
+ }
892
+ async getDeploymentStatus(siteId) {
893
+ return this.fetch(`/api/sites/${encodeURIComponent(String(siteId))}/deployment-status`);
894
+ }
895
+ async purgeCache(siteId, body) {
896
+ return this.fetch(`/api/sites/${encodeURIComponent(String(siteId))}/cache-purge`, {
897
+ method: 'POST',
898
+ body: JSON.stringify(body || {}),
899
+ });
900
+ }
901
+ async listMaintenanceWindows(opts) {
902
+ // When a websiteId is provided, use the by-site view (site + account windows).
903
+ if (opts?.websiteId != null) {
904
+ return this.fetch(`/api/maintenance/windows/by-site?website_id=${encodeURIComponent(String(opts.websiteId))}`);
905
+ }
906
+ const p = new URLSearchParams();
907
+ if (opts?.accountId != null)
908
+ p.append('account_id', String(opts.accountId));
909
+ if (opts?.k8sCluster)
910
+ p.append('k8s_cluster', opts.k8sCluster);
911
+ if (opts?.enabledOnly)
912
+ p.append('enabled', 'true');
913
+ const qs = p.toString();
914
+ return this.fetch(`/api/maintenance/windows${qs ? `?${qs}` : ''}`);
915
+ }
916
+ async createMaintenanceWindow(input) {
917
+ return this.fetch('/api/maintenance/windows', {
918
+ method: 'POST',
919
+ body: JSON.stringify(input),
920
+ });
921
+ }
922
+ // ============================================================================
840
923
  // Certificate Methods
841
924
  // ============================================================================
842
925
  async listCertificates(filters) {
@@ -3240,6 +3323,133 @@ const TOOLS = [
3240
3323
  },
3241
3324
  },
3242
3325
  // ============================================================================
3326
+ // Site Ops (DNS / Deploy / Maintenance) Tools
3327
+ // ============================================================================
3328
+ {
3329
+ name: 'fcp_dns_current',
3330
+ 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.',
3331
+ inputSchema: {
3332
+ type: 'object',
3333
+ properties: {
3334
+ websiteId: { type: 'number', description: 'FCP website id' },
3335
+ },
3336
+ required: ['websiteId'],
3337
+ },
3338
+ },
3339
+ {
3340
+ name: 'fcp_dns_history',
3341
+ 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).',
3342
+ inputSchema: {
3343
+ type: 'object',
3344
+ properties: {
3345
+ websiteId: { type: 'number', description: 'FCP website id' },
3346
+ recordType: { type: 'string', enum: ['A', 'CNAME'], description: 'Filter by record type' },
3347
+ limit: { type: 'number', description: 'Max number of snapshots to return' },
3348
+ since: { type: 'string', description: 'ISO date; only snapshots on/after this time' },
3349
+ },
3350
+ required: ['websiteId'],
3351
+ },
3352
+ },
3353
+ {
3354
+ name: 'fcp_list_deployments',
3355
+ 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.',
3356
+ inputSchema: {
3357
+ type: 'object',
3358
+ properties: {
3359
+ websiteIds: { type: 'string', description: 'Comma-separated website ids' },
3360
+ branches: { type: 'string', description: 'Comma-separated branch names' },
3361
+ statuses: { type: 'string', description: 'Comma-separated statuses' },
3362
+ conclusions: { type: 'string', description: 'Comma-separated conclusions (success|failure|...)' },
3363
+ events: { type: 'string', description: "Comma-separated events; 'all' to include PR builds" },
3364
+ actor: { type: 'string', description: 'Filter by GitHub actor' },
3365
+ startDate: { type: 'string', description: 'ISO date lower bound' },
3366
+ endDate: { type: 'string', description: 'ISO date upper bound' },
3367
+ sortBy: { type: 'string', enum: ['created_at', 'completed_at', 'duration'], description: 'Sort field' },
3368
+ sortOrder: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default desc)' },
3369
+ limit: { type: 'number', description: 'Page size (default 50)' },
3370
+ offset: { type: 'number', description: 'Offset (default 0)' },
3371
+ },
3372
+ },
3373
+ },
3374
+ {
3375
+ name: 'fcp_get_deployment_status',
3376
+ 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.',
3377
+ inputSchema: {
3378
+ type: 'object',
3379
+ properties: {
3380
+ siteId: { type: 'number', description: 'FCP site (website) id' },
3381
+ },
3382
+ required: ['siteId'],
3383
+ },
3384
+ },
3385
+ {
3386
+ name: 'fcp_purge_cache',
3387
+ 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.',
3388
+ inputSchema: {
3389
+ type: 'object',
3390
+ properties: {
3391
+ siteId: { type: 'number', description: 'FCP site (website) id' },
3392
+ purgeType: {
3393
+ type: 'string',
3394
+ enum: ['all', 'urls', 'single', 'cloudfront-only', 'cloudflare-only', 'dynamodb-only', 'varnish-only'],
3395
+ description: "Which layers/scope to purge (default 'all')",
3396
+ },
3397
+ paths: { type: 'array', items: { type: 'string' }, description: "Paths to purge (default ['/*'])" },
3398
+ urls: { type: 'array', items: { type: 'string' }, description: 'Specific full URLs to purge (urls/single)' },
3399
+ languages: { type: 'array', items: { type: 'string' }, description: 'Language codes for translation cache' },
3400
+ warmCache: { type: 'boolean', description: 'Warm cache after a successful purge' },
3401
+ warmPages: { type: 'array', items: { type: 'string' }, description: 'Pages to warm if warmCache=true' },
3402
+ verifyAfterPurge: { type: 'boolean', description: 'Verify the site after purge' },
3403
+ },
3404
+ required: ['siteId'],
3405
+ },
3406
+ },
3407
+ {
3408
+ name: 'fcp_list_maintenance_windows',
3409
+ 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.',
3410
+ inputSchema: {
3411
+ type: 'object',
3412
+ properties: {
3413
+ websiteId: { type: 'number', description: 'Site id — switches to the by-site view (site + account windows)' },
3414
+ accountId: { type: 'number', description: 'Filter by account id (list view)' },
3415
+ k8sCluster: { type: 'string', description: 'Filter by k8s cluster (list view)' },
3416
+ enabledOnly: { type: 'boolean', description: 'Only enabled windows (list view)' },
3417
+ },
3418
+ },
3419
+ },
3420
+ {
3421
+ name: 'fcp_create_maintenance_window',
3422
+ 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.',
3423
+ inputSchema: {
3424
+ type: 'object',
3425
+ properties: {
3426
+ name: { type: 'string', description: 'Window name' },
3427
+ recurrence_type: {
3428
+ type: 'string',
3429
+ enum: ['weekly', 'biweekly', 'bimonthly', 'monthly', 'custom'],
3430
+ description: 'Recurrence type',
3431
+ },
3432
+ start_time: { type: 'string', description: 'Start time HH:MM[:SS]' },
3433
+ end_time: { type: 'string', description: 'End time HH:MM[:SS]' },
3434
+ website_id: { type: 'number', description: 'Scope: site id' },
3435
+ account_id: { type: 'number', description: 'Scope: account id' },
3436
+ k8s_cluster: { type: 'string', description: 'Scope: k8s cluster (INFRA-wide)' },
3437
+ day_of_week: { type: 'number', description: 'Day of week 0=Sun..6=Sat (required for weekly/biweekly/bimonthly/monthly)' },
3438
+ weeks_of_month: { type: 'array', items: { type: 'number' }, description: 'Weeks of month for bimonthly (e.g. [1,3])' },
3439
+ week_of_month: { type: 'number', description: 'Week of month for monthly' },
3440
+ cron_expression: { type: 'string', description: 'Cron expression for custom recurrence' },
3441
+ description: { type: 'string', description: 'Optional description' },
3442
+ timezone: { type: 'string', description: "IANA tz (default 'America/Denver')" },
3443
+ default_severity: { type: 'string', description: "Default event severity (default 'low')" },
3444
+ auto_create_events: { type: 'boolean', description: 'Auto-create events for occurrences' },
3445
+ auto_create_lead_time: { type: 'number', description: 'Hours of lead time for auto-created events (default 24)' },
3446
+ enabled: { type: 'boolean', description: 'Enabled (default true)' },
3447
+ created_by: { type: 'string', description: 'Creator identifier' },
3448
+ },
3449
+ required: ['name', 'recurrence_type', 'start_time', 'end_time'],
3450
+ },
3451
+ },
3452
+ // ============================================================================
3243
3453
  // Certificate Tools
3244
3454
  // ============================================================================
3245
3455
  {
@@ -5225,6 +5435,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5225
5435
  };
5226
5436
  }
5227
5437
  // ============================================================================
5438
+ // Site Ops (DNS / Deploy / Maintenance) Handlers
5439
+ // ============================================================================
5440
+ case 'fcp_dns_current': {
5441
+ const { websiteId } = args;
5442
+ const result = await client.getDnsCurrent(websiteId);
5443
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5444
+ }
5445
+ case 'fcp_dns_history': {
5446
+ const { websiteId, recordType, limit, since } = args;
5447
+ const result = await client.getDnsHistory(websiteId, { recordType, limit, since });
5448
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5449
+ }
5450
+ case 'fcp_list_deployments': {
5451
+ const result = await client.listDeployments(args);
5452
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5453
+ }
5454
+ case 'fcp_get_deployment_status': {
5455
+ const { siteId } = args;
5456
+ const result = await client.getDeploymentStatus(siteId);
5457
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5458
+ }
5459
+ case 'fcp_purge_cache': {
5460
+ const { siteId, ...body } = args;
5461
+ const result = await client.purgeCache(siteId, body);
5462
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5463
+ }
5464
+ case 'fcp_list_maintenance_windows': {
5465
+ const result = await client.listMaintenanceWindows(args);
5466
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5467
+ }
5468
+ case 'fcp_create_maintenance_window': {
5469
+ const result = await client.createMaintenanceWindow(args);
5470
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5471
+ }
5472
+ // ============================================================================
5228
5473
  // Certificate Handlers
5229
5474
  // ============================================================================
5230
5475
  case 'fcp_list_certificates': {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fruition/fcp-mcp-server",
3
- "version": "1.29.0",
3
+ "version": "1.30.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",