@fruition/fcp-mcp-server 1.28.0 → 1.29.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
@@ -25,6 +25,24 @@ MCP (Model Context Protocol) server that gives Claude Code direct access to the
25
25
  | `fcp_update_freeze` | Edit an existing freeze's window or reason |
26
26
  | `fcp_unfreeze` | Remove a freeze by id, or clear all freezes for a repo |
27
27
 
28
+ ### Certificate Tools
29
+
30
+ | Tool | Description |
31
+ |------|-------------|
32
+ | `fcp_list_certificates` | Fleet TLS certs with status/expiry/cluster filters |
33
+ | `fcp_get_certificate` | Single certificate detail (issuer, SANs, expiry) |
34
+ | `fcp_scan_certificates` | Trigger a cert-monitor scan run |
35
+
36
+ ### Tasker Job Control Tools
37
+
38
+ | Tool | Description |
39
+ |------|-------------|
40
+ | `fcp_spawn_task_job` | Spawn an automated code-change job for an Unroo task |
41
+ | `fcp_get_job_status` | Job status by job_id/task_id (or all + stats) |
42
+ | `fcp_list_jobs` | List the current Tasker job queue |
43
+ | `fcp_approve_job` | Approve merge for a job needing human sign-off |
44
+ | `fcp_override_merge_window` | Force-allow an auto-merge outside its deploy window (super_admin) |
45
+
28
46
  ### Unroo Task Management Tools
29
47
 
30
48
  | Tool | Description |
package/dist/index.d.ts CHANGED
@@ -360,6 +360,41 @@ export declare class FCPClient {
360
360
  id?: number;
361
361
  repo?: string;
362
362
  }): Promise<any>;
363
+ listCertificates(filters?: {
364
+ status?: string;
365
+ cluster?: string;
366
+ namespace?: string;
367
+ certType?: string;
368
+ expiringWithinDays?: number;
369
+ sortBy?: string;
370
+ sortDir?: string;
371
+ page?: number;
372
+ limit?: number;
373
+ }): Promise<any>;
374
+ getCertificate(certId: string | number): Promise<any>;
375
+ scanCertificates(input?: {
376
+ dryRun?: boolean;
377
+ }): Promise<any>;
378
+ spawnTaskJob(input: {
379
+ task_id: string;
380
+ task_title: string;
381
+ project_key: string;
382
+ task_description?: string;
383
+ labels?: string[];
384
+ }): Promise<any>;
385
+ getJobStatus(opts: {
386
+ job_id?: string;
387
+ task_id?: string;
388
+ }): Promise<any>;
389
+ listJobs(opts?: {
390
+ refresh?: boolean;
391
+ }): Promise<any>;
392
+ approveJob(id: string): Promise<any>;
393
+ overrideMergeWindow(id: string, input: {
394
+ action: 'set' | 'clear';
395
+ reason?: string;
396
+ ttl_minutes?: number;
397
+ }): Promise<any>;
363
398
  backupListSites(): Promise<any>;
364
399
  backupGetConfig(): Promise<any>;
365
400
  backupListEligible(): Promise<any>;
package/dist/index.js CHANGED
@@ -99,6 +99,7 @@ const TOOL_PERMISSIONS = {
99
99
  fcp_backup_delete_pairing: 'super_admin',
100
100
  fcp_filesync_cancel_sync: 'super_admin',
101
101
  fcp_unfreeze: 'super_admin', // lifting a freeze releases a held deploy
102
+ fcp_override_merge_window: 'super_admin', // bypasses the deploy-window safety gate
102
103
  // --- admin+: mutating ops with real-world side effects ---
103
104
  fcp_create_launch: 'admin',
104
105
  fcp_update_launch: 'admin',
@@ -111,6 +112,9 @@ const TOOL_PERMISSIONS = {
111
112
  fcp_trusted_ip_update_range: 'admin',
112
113
  fcp_freeze_repo: 'admin',
113
114
  fcp_update_freeze: 'admin',
115
+ fcp_scan_certificates: 'admin',
116
+ fcp_spawn_task_job: 'admin',
117
+ fcp_approve_job: 'admin',
114
118
  fcp_backup_enable: 'admin',
115
119
  fcp_backup_trigger: 'admin',
116
120
  fcp_backup_check_trigger: 'admin',
@@ -161,6 +165,10 @@ const TOOL_PERMISSIONS = {
161
165
  fcp_trusted_ip_list_ranges: 'viewer',
162
166
  fcp_trusted_ip_export: 'viewer',
163
167
  fcp_list_freezes: 'viewer',
168
+ fcp_list_certificates: 'viewer',
169
+ fcp_get_certificate: 'viewer',
170
+ fcp_list_jobs: 'viewer',
171
+ fcp_get_job_status: 'viewer',
164
172
  fcp_backup_list_sites: 'viewer',
165
173
  fcp_backup_get_config: 'viewer',
166
174
  fcp_backup_list_eligible: 'viewer',
@@ -829,6 +837,79 @@ export class FCPClient {
829
837
  });
830
838
  }
831
839
  // ============================================================================
840
+ // Certificate Methods
841
+ // ============================================================================
842
+ async listCertificates(filters) {
843
+ const p = new URLSearchParams();
844
+ if (filters?.status)
845
+ p.append('status', filters.status);
846
+ if (filters?.cluster)
847
+ p.append('cluster', filters.cluster);
848
+ if (filters?.namespace)
849
+ p.append('namespace', filters.namespace);
850
+ if (filters?.certType)
851
+ p.append('certType', filters.certType);
852
+ if (filters?.expiringWithinDays != null)
853
+ p.append('expiringWithinDays', String(filters.expiringWithinDays));
854
+ if (filters?.sortBy)
855
+ p.append('sortBy', filters.sortBy);
856
+ if (filters?.sortDir)
857
+ p.append('sortDir', filters.sortDir);
858
+ if (filters?.page != null)
859
+ p.append('page', String(filters.page));
860
+ if (filters?.limit != null)
861
+ p.append('limit', String(filters.limit));
862
+ const qs = p.toString();
863
+ return this.fetch(`/api/certificates${qs ? `?${qs}` : ''}`);
864
+ }
865
+ async getCertificate(certId) {
866
+ return this.fetch(`/api/certificates/${encodeURIComponent(String(certId))}`);
867
+ }
868
+ async scanCertificates(input) {
869
+ return this.fetch('/api/certificates/scan', {
870
+ method: 'POST',
871
+ body: JSON.stringify(input || {}),
872
+ });
873
+ }
874
+ // ============================================================================
875
+ // Tasker Job Control Methods
876
+ // ============================================================================
877
+ async spawnTaskJob(input) {
878
+ return this.fetch('/api/ticket-workflow/spawn', {
879
+ method: 'POST',
880
+ body: JSON.stringify(input),
881
+ });
882
+ }
883
+ async getJobStatus(opts) {
884
+ const p = new URLSearchParams();
885
+ if (opts.job_id)
886
+ p.append('job_id', opts.job_id);
887
+ if (opts.task_id)
888
+ p.append('task_id', opts.task_id);
889
+ const qs = p.toString();
890
+ return this.fetch(`/api/ticket-workflow/status${qs ? `?${qs}` : ''}`);
891
+ }
892
+ async listJobs(opts) {
893
+ const qs = opts?.refresh ? '?refresh=true' : '';
894
+ return this.fetch(`/api/ticket-workflow/queue${qs}`);
895
+ }
896
+ async approveJob(id) {
897
+ return this.fetch(`/api/ticket-workflow/jobs/${encodeURIComponent(id)}/approve`, {
898
+ method: 'POST',
899
+ body: JSON.stringify({}),
900
+ });
901
+ }
902
+ async overrideMergeWindow(id, input) {
903
+ const path = `/api/ticket-workflow/jobs/${encodeURIComponent(id)}/override`;
904
+ if (input.action === 'clear') {
905
+ return this.fetch(path, { method: 'DELETE' });
906
+ }
907
+ const body = { reason: input.reason };
908
+ if (input.ttl_minutes != null)
909
+ body.ttl_minutes = input.ttl_minutes;
910
+ return this.fetch(path, { method: 'POST', body: JSON.stringify(body) });
911
+ }
912
+ // ============================================================================
832
913
  // Backup Management Methods
833
914
  // ============================================================================
834
915
  async backupListSites() {
@@ -3159,6 +3240,104 @@ const TOOLS = [
3159
3240
  },
3160
3241
  },
3161
3242
  // ============================================================================
3243
+ // Certificate Tools
3244
+ // ============================================================================
3245
+ {
3246
+ name: 'fcp_list_certificates',
3247
+ description: 'List TLS certificates across the fleet (cert-monitor). Filter by status (valid|warning|critical|expired), expiringWithinDays, cluster, namespace, certType; sort and paginate. Use to find certs about to expire before a migration/cutover.',
3248
+ inputSchema: {
3249
+ type: 'object',
3250
+ properties: {
3251
+ status: { type: 'string', description: 'valid | warning | critical | expired | unknown' },
3252
+ expiringWithinDays: { type: 'number', description: 'Only certs expiring within N days' },
3253
+ cluster: { type: 'string', description: 'Filter by k8s cluster' },
3254
+ namespace: { type: 'string', description: 'Filter by namespace' },
3255
+ certType: { type: 'string', description: 'Filter by certificate type' },
3256
+ sortBy: { type: 'string', description: 'name | cluster | namespace | certType | status | expiry | subjectCn' },
3257
+ sortDir: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction (default asc)' },
3258
+ page: { type: 'number', description: 'Page number (1-based)' },
3259
+ limit: { type: 'number', description: 'Page size' },
3260
+ },
3261
+ },
3262
+ },
3263
+ {
3264
+ name: 'fcp_get_certificate',
3265
+ description: 'Get a single certificate by id, including issuer, SANs, expiry, and history.',
3266
+ inputSchema: {
3267
+ type: 'object',
3268
+ properties: { certId: { type: 'string', description: 'Certificate id' } },
3269
+ required: ['certId'],
3270
+ },
3271
+ },
3272
+ {
3273
+ name: 'fcp_scan_certificates',
3274
+ description: 'Trigger a cert-monitor scan run across clusters to refresh certificate inventory and expiry. Pass dryRun=true to compute without persisting.',
3275
+ inputSchema: {
3276
+ type: 'object',
3277
+ properties: { dryRun: { type: 'boolean', description: 'Compute without writing results (default false)' } },
3278
+ },
3279
+ },
3280
+ // ============================================================================
3281
+ // Tasker Job Control Tools
3282
+ // ============================================================================
3283
+ {
3284
+ name: 'fcp_spawn_task_job',
3285
+ description: 'Spawn a Tasker automated code-change job for an Unroo task (Claude Code in K8s -> PR). Requires task_id, task_title, project_key. Rejects if a job is already in progress for the task.',
3286
+ inputSchema: {
3287
+ type: 'object',
3288
+ properties: {
3289
+ task_id: { type: 'string', description: 'Unroo task id' },
3290
+ task_title: { type: 'string', description: 'Task title' },
3291
+ project_key: { type: 'string', description: 'Project key (resolves the repo)' },
3292
+ task_description: { type: 'string', description: 'Optional task description / instructions' },
3293
+ labels: { type: 'array', items: { type: 'string' }, description: 'Optional labels' },
3294
+ },
3295
+ required: ['task_id', 'task_title', 'project_key'],
3296
+ },
3297
+ },
3298
+ {
3299
+ name: 'fcp_get_job_status',
3300
+ description: 'Get Tasker job status. Pass job_id or task_id for a specific job; omit both for all jobs + stats.',
3301
+ inputSchema: {
3302
+ type: 'object',
3303
+ properties: {
3304
+ job_id: { type: 'string', description: 'Tasker job id' },
3305
+ task_id: { type: 'string', description: 'Unroo task id (alternative to job_id)' },
3306
+ },
3307
+ },
3308
+ },
3309
+ {
3310
+ name: 'fcp_list_jobs',
3311
+ description: 'List the current Tasker job queue. Pass refresh=true to re-sync statuses from K8s first.',
3312
+ inputSchema: {
3313
+ type: 'object',
3314
+ properties: { refresh: { type: 'boolean', description: 'Re-sync from K8s before listing (default false)' } },
3315
+ },
3316
+ },
3317
+ {
3318
+ name: 'fcp_approve_job',
3319
+ description: 'Approve the merge for a Tasker job that requires human sign-off (e.g. HIGH/CRITICAL security PRs). The job still respects its deploy window unless overridden.',
3320
+ inputSchema: {
3321
+ type: 'object',
3322
+ properties: { id: { type: 'string', description: 'Tasker job id' } },
3323
+ required: ['id'],
3324
+ },
3325
+ },
3326
+ {
3327
+ name: 'fcp_override_merge_window',
3328
+ description: 'Force-allow a Tasker auto-merge OUTSIDE its configured deploy window (super_admin). action=set grants an override for ttl_minutes (reason required); action=clear cancels it. Bypasses the safety gate \u2014 use deliberately.',
3329
+ inputSchema: {
3330
+ type: 'object',
3331
+ properties: {
3332
+ id: { type: 'string', description: 'Tasker job id' },
3333
+ action: { type: 'string', enum: ['set', 'clear'], description: 'set = grant override, clear = cancel' },
3334
+ reason: { type: 'string', description: 'Justification (required when action=set)' },
3335
+ ttl_minutes: { type: 'number', description: 'How long the override lasts (when action=set)' },
3336
+ },
3337
+ required: ['id', 'action'],
3338
+ },
3339
+ },
3340
+ // ============================================================================
3162
3341
  // Backup Management Tools
3163
3342
  // ============================================================================
3164
3343
  {
@@ -5046,6 +5225,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5046
5225
  };
5047
5226
  }
5048
5227
  // ============================================================================
5228
+ // Certificate Handlers
5229
+ // ============================================================================
5230
+ case 'fcp_list_certificates': {
5231
+ const result = await client.listCertificates(args);
5232
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5233
+ }
5234
+ case 'fcp_get_certificate': {
5235
+ const { certId } = args;
5236
+ const result = await client.getCertificate(certId);
5237
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5238
+ }
5239
+ case 'fcp_scan_certificates': {
5240
+ const { dryRun } = args;
5241
+ const result = await client.scanCertificates({ dryRun });
5242
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5243
+ }
5244
+ // ============================================================================
5245
+ // Tasker Job Control Handlers
5246
+ // ============================================================================
5247
+ case 'fcp_spawn_task_job': {
5248
+ const { task_id, task_title, project_key, task_description, labels } = args;
5249
+ const result = await client.spawnTaskJob({ task_id, task_title, project_key, task_description, labels });
5250
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5251
+ }
5252
+ case 'fcp_get_job_status': {
5253
+ const { job_id, task_id } = args;
5254
+ const result = await client.getJobStatus({ job_id, task_id });
5255
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5256
+ }
5257
+ case 'fcp_list_jobs': {
5258
+ const { refresh } = args;
5259
+ const result = await client.listJobs({ refresh });
5260
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5261
+ }
5262
+ case 'fcp_approve_job': {
5263
+ const { id } = args;
5264
+ const result = await client.approveJob(id);
5265
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5266
+ }
5267
+ case 'fcp_override_merge_window': {
5268
+ const { id, action, reason, ttl_minutes } = args;
5269
+ if (action === 'set' && (!reason || !reason.trim())) {
5270
+ throw new Error('fcp_override_merge_window: reason is required when action=set');
5271
+ }
5272
+ const result = await client.overrideMergeWindow(id, { action, reason, ttl_minutes });
5273
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
5274
+ }
5275
+ // ============================================================================
5049
5276
  // Backup Management Handlers
5050
5277
  // ============================================================================
5051
5278
  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.28.0",
3
+ "version": "1.29.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",