headlesshost-mcp-server 1.0.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/build/index.js ADDED
@@ -0,0 +1,1766 @@
1
+ #!/usr/bin/env node
2
+ // Suppress console output during dotenv loading
3
+ const originalConsoleLog = console.log;
4
+ console.log = () => { };
5
+ import dotenv from "dotenv";
6
+ dotenv.config();
7
+ // Restore console.log for debugging purposes
8
+ console.log = originalConsoleLog;
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import { z } from "zod";
12
+ import axios from "axios";
13
+ // Configuration
14
+ const API_BASE_URL = "https://api.headlesshost.com";
15
+ const API_KEY = process.env.HEADLESSHOST_API_KEY || "YOUR API KEY HERE";
16
+ // Create axios instance with default config
17
+ const apiClient = axios.create({
18
+ baseURL: API_BASE_URL,
19
+ headers: {
20
+ "Content-Type": "application/json",
21
+ ...(API_KEY && { Authorization: `Bearer ${API_KEY}` }),
22
+ },
23
+ timeout: 30000,
24
+ });
25
+ const validClaims = [
26
+ "Administrator",
27
+ "PageCreator",
28
+ "PageEditor",
29
+ "PageDeleter",
30
+ "PageMover",
31
+ "SectionCreator",
32
+ "SectionEditor",
33
+ "SectionDeleter",
34
+ "SectionMover",
35
+ "ContentDesigner",
36
+ "Publisher",
37
+ "BusinessDeleter",
38
+ "BusinessEditor",
39
+ "BusinessCreator",
40
+ "PublishApproval",
41
+ "PublishDeleter",
42
+ "Super",
43
+ "StageCreator",
44
+ "StageDeleter",
45
+ "SiteMerger",
46
+ "CatalogCreator",
47
+ "CatalogEditor",
48
+ "CatalogDeleter",
49
+ "BusinessUserCreator",
50
+ "BusinessUserEditor",
51
+ //wrap
52
+ "BusinessUserDeleter",
53
+ ];
54
+ // Create MCP server
55
+ const server = new McpServer({
56
+ name: "headlesshost-tools-server",
57
+ version: "1.0.0",
58
+ capabilities: {
59
+ tools: {},
60
+ resources: {},
61
+ },
62
+ });
63
+ // Helper function to handle API errors
64
+ function handleApiError(error) {
65
+ if (error.response) {
66
+ return `API Error ${error.response.status}: ${error.response.data?.message || error.response.statusText}`;
67
+ }
68
+ else if (error.request) {
69
+ return "Network Error: Unable to reach API server";
70
+ }
71
+ else {
72
+ return `Error: ${error.message}`;
73
+ }
74
+ }
75
+ // ========== GENERAL TOOLS ENDPOINTS ==========
76
+ // Ping - Test authentication and connection
77
+ server.registerTool("ping", {
78
+ title: "Ping API",
79
+ description: "Test authentication and connection to the Headlesshost API",
80
+ inputSchema: {},
81
+ }, async () => {
82
+ try {
83
+ const response = await apiClient.get("/tools/ping");
84
+ return {
85
+ content: [
86
+ {
87
+ type: "text",
88
+ text: JSON.stringify(response.data, null, 2),
89
+ },
90
+ ],
91
+ };
92
+ }
93
+ catch (error) {
94
+ return {
95
+ content: [
96
+ {
97
+ type: "text",
98
+ text: handleApiError(error),
99
+ },
100
+ ],
101
+ isError: true,
102
+ };
103
+ }
104
+ });
105
+ // Health - Check API health status
106
+ server.registerTool("health", {
107
+ title: "Health Check",
108
+ description: "Check the health status of the Headlesshost API",
109
+ inputSchema: {},
110
+ }, async () => {
111
+ try {
112
+ const response = await apiClient.get("/tools/health");
113
+ return {
114
+ content: [
115
+ {
116
+ type: "text",
117
+ text: JSON.stringify(response.data, null, 2),
118
+ },
119
+ ],
120
+ };
121
+ }
122
+ catch (error) {
123
+ return {
124
+ content: [
125
+ {
126
+ type: "text",
127
+ text: handleApiError(error),
128
+ },
129
+ ],
130
+ isError: true,
131
+ };
132
+ }
133
+ });
134
+ // Get Reference Data
135
+ server.registerTool("get_ref_data", {
136
+ title: "Get Reference Data",
137
+ description: "Get system reference data and lookups",
138
+ inputSchema: {
139
+ category: z.string().optional().describe("Reference data category to filter by"),
140
+ },
141
+ }, async ({ category }) => {
142
+ try {
143
+ const params = new URLSearchParams();
144
+ if (category)
145
+ params.append("category", category);
146
+ const response = await apiClient.get(`/tools/system/refdata?${params}`);
147
+ return {
148
+ content: [
149
+ {
150
+ type: "text",
151
+ text: JSON.stringify(response.data, null, 2),
152
+ },
153
+ ],
154
+ };
155
+ }
156
+ catch (error) {
157
+ return {
158
+ content: [
159
+ {
160
+ type: "text",
161
+ text: handleApiError(error),
162
+ },
163
+ ],
164
+ isError: true,
165
+ };
166
+ }
167
+ });
168
+ // ========== MEMBERSHIP MANAGEMENT TOOLS ==========
169
+ // Register User
170
+ server.registerTool("register_user", {
171
+ title: "Register User",
172
+ description: "Register a new user in the system with account creation",
173
+ inputSchema: {
174
+ email: z.string().email().describe("User email address"),
175
+ password: z.string().describe("User password"),
176
+ firstName: z.string().optional().describe("User first name"),
177
+ lastName: z.string().optional().describe("User last name"),
178
+ accountName: z.string().optional().describe("Account name to create"),
179
+ },
180
+ }, async ({ email, password, firstName, lastName, accountName }) => {
181
+ try {
182
+ const payload = { email, password, firstName, lastName, accountName };
183
+ const response = await apiClient.post("/tools/membership/register", payload);
184
+ return {
185
+ content: [
186
+ {
187
+ type: "text",
188
+ text: JSON.stringify(response.data, null, 2),
189
+ },
190
+ ],
191
+ };
192
+ }
193
+ catch (error) {
194
+ return {
195
+ content: [
196
+ {
197
+ type: "text",
198
+ text: handleApiError(error),
199
+ },
200
+ ],
201
+ isError: true,
202
+ };
203
+ }
204
+ });
205
+ // Create User
206
+ server.registerTool("create_user", {
207
+ title: "Create User",
208
+ description: "Create a new user in the current account",
209
+ inputSchema: {
210
+ email: z.string().email().describe("User email address"),
211
+ firstName: z.string().describe("User first name"),
212
+ lastName: z.string().describe("User last name"),
213
+ password: z.string().optional().describe("User password"),
214
+ claims: z
215
+ .union([z.string(), z.array(z.string())])
216
+ .optional()
217
+ .describe("User roles/claims (string or array of strings)"),
218
+ },
219
+ }, async ({ email, firstName, lastName, password, claims }) => {
220
+ try {
221
+ // Build payload object more carefully
222
+ const payload = {
223
+ email,
224
+ firstName,
225
+ lastName,
226
+ };
227
+ // Only add password if provided
228
+ if (password) {
229
+ payload.password = password;
230
+ }
231
+ // Only add claims if provided and convert to array format
232
+ if (claims !== undefined && claims !== null) {
233
+ if (typeof claims === "string") {
234
+ payload.claims = [claims];
235
+ }
236
+ else if (Array.isArray(claims) && claims.length > 0) {
237
+ payload.claims = claims;
238
+ }
239
+ // If claims is an empty array or invalid, don't include it in payload
240
+ }
241
+ console.error("Create user payload:", JSON.stringify(payload, null, 2)); // Debug log
242
+ const response = await apiClient.post("/tools/membership/users", payload);
243
+ return {
244
+ content: [
245
+ {
246
+ type: "text",
247
+ text: JSON.stringify(response.data, null, 2),
248
+ },
249
+ ],
250
+ };
251
+ }
252
+ catch (error) {
253
+ return {
254
+ content: [
255
+ {
256
+ type: "text",
257
+ text: handleApiError(error),
258
+ },
259
+ ],
260
+ isError: true,
261
+ };
262
+ }
263
+ });
264
+ // Get User
265
+ server.registerTool("get_user", {
266
+ title: "Get User",
267
+ description: "Get user details by ID",
268
+ inputSchema: {
269
+ id: z.string().describe("User ID"),
270
+ },
271
+ }, async ({ id }) => {
272
+ try {
273
+ const response = await apiClient.get(`/tools/membership/users/${id}`);
274
+ return {
275
+ content: [
276
+ {
277
+ type: "text",
278
+ text: JSON.stringify(response.data, null, 2),
279
+ },
280
+ ],
281
+ };
282
+ }
283
+ catch (error) {
284
+ return {
285
+ content: [
286
+ {
287
+ type: "text",
288
+ text: handleApiError(error),
289
+ },
290
+ ],
291
+ isError: true,
292
+ };
293
+ }
294
+ });
295
+ // Update User
296
+ server.registerTool("update_user", {
297
+ title: "Update User",
298
+ description: "Update user information",
299
+ inputSchema: {
300
+ id: z.string().describe("User ID"),
301
+ email: z.string().email().optional().describe("User email"),
302
+ firstName: z.string().optional().describe("First name"),
303
+ lastName: z.string().optional().describe("Last name"),
304
+ claims: z
305
+ .union([
306
+ z.enum(validClaims), // single claim
307
+ z.array(z.enum(validClaims)), // multiple claims
308
+ ])
309
+ .optional()
310
+ .describe(`User roles/claims (choose from: ${validClaims.join(", ")})`),
311
+ },
312
+ }, async ({ id, email, firstName, lastName, claims }) => {
313
+ try {
314
+ const payload = {};
315
+ if (email)
316
+ payload.email = email;
317
+ if (firstName)
318
+ payload.firstName = firstName;
319
+ if (lastName)
320
+ payload.lastName = lastName;
321
+ if (claims !== undefined && claims !== null) {
322
+ // Normalize to array
323
+ payload.claims = Array.isArray(claims) ? claims : [claims];
324
+ }
325
+ const response = await apiClient.put(`/tools/membership/users/${id}`, payload);
326
+ return {
327
+ content: [
328
+ {
329
+ type: "text",
330
+ text: JSON.stringify(response.data, null, 2),
331
+ },
332
+ ],
333
+ };
334
+ }
335
+ catch (error) {
336
+ return {
337
+ content: [
338
+ {
339
+ type: "text",
340
+ text: handleApiError(error),
341
+ },
342
+ ],
343
+ isError: true,
344
+ };
345
+ }
346
+ });
347
+ // Delete User
348
+ server.registerTool("delete_user", {
349
+ title: "Delete User",
350
+ description: "Delete a user from the system",
351
+ inputSchema: {
352
+ id: z.string().describe("User ID"),
353
+ reason: z.string().optional().describe("Reason for deletion"),
354
+ },
355
+ }, async ({ id, reason }) => {
356
+ try {
357
+ const payload = { reason };
358
+ const response = await apiClient.delete(`/tools/membership/users/${id}`, {
359
+ data: payload,
360
+ });
361
+ return {
362
+ content: [
363
+ {
364
+ type: "text",
365
+ text: JSON.stringify(response.data, null, 2),
366
+ },
367
+ ],
368
+ };
369
+ }
370
+ catch (error) {
371
+ return {
372
+ content: [
373
+ {
374
+ type: "text",
375
+ text: handleApiError(error),
376
+ },
377
+ ],
378
+ isError: true,
379
+ };
380
+ }
381
+ });
382
+ // Get Account
383
+ server.registerTool("get_account", {
384
+ title: "Get Account",
385
+ description: "Get current account information",
386
+ inputSchema: {},
387
+ }, async () => {
388
+ try {
389
+ const response = await apiClient.get(`/tools/membership/account`);
390
+ return {
391
+ content: [
392
+ {
393
+ type: "text",
394
+ text: JSON.stringify(response.data, null, 2),
395
+ },
396
+ ],
397
+ };
398
+ }
399
+ catch (error) {
400
+ return {
401
+ content: [
402
+ {
403
+ type: "text",
404
+ text: handleApiError(error),
405
+ },
406
+ ],
407
+ isError: true,
408
+ };
409
+ }
410
+ });
411
+ // Update Account
412
+ server.registerTool("update_account", {
413
+ title: "Update Account",
414
+ description: "Update account information",
415
+ inputSchema: {
416
+ name: z.string().optional().describe("Account name"),
417
+ },
418
+ }, async ({ name }) => {
419
+ try {
420
+ const payload = {};
421
+ if (name)
422
+ payload.name = name;
423
+ const response = await apiClient.put("/tools/membership/account", payload);
424
+ return {
425
+ content: [
426
+ {
427
+ type: "text",
428
+ text: JSON.stringify(response.data, null, 2),
429
+ },
430
+ ],
431
+ };
432
+ }
433
+ catch (error) {
434
+ return {
435
+ content: [
436
+ {
437
+ type: "text",
438
+ text: handleApiError(error),
439
+ },
440
+ ],
441
+ isError: true,
442
+ };
443
+ }
444
+ });
445
+ // ========== FILE MANAGEMENT TOOLS ==========
446
+ // Upload User Profile Image
447
+ server.registerTool("upload_user_profile_image", {
448
+ title: "Upload User Profile Image",
449
+ description: "Upload a profile image for a user",
450
+ inputSchema: {
451
+ userId: z.string().describe("User ID"),
452
+ image: z.string().describe("Base64 encoded file data"),
453
+ },
454
+ }, async ({ userId, image }) => {
455
+ try {
456
+ const payload = { image };
457
+ const response = await apiClient.post(`/tools/files/users/${userId}/profile-image`, payload);
458
+ return {
459
+ content: [
460
+ {
461
+ type: "text",
462
+ text: JSON.stringify(response.data, null, 2),
463
+ },
464
+ ],
465
+ };
466
+ }
467
+ catch (error) {
468
+ return {
469
+ content: [
470
+ {
471
+ type: "text",
472
+ text: handleApiError(error),
473
+ },
474
+ ],
475
+ isError: true,
476
+ };
477
+ }
478
+ });
479
+ // Upload Staging Site File
480
+ server.registerTool("upload_staging_site_file", {
481
+ title: "Upload Staging Site File",
482
+ description: "Upload a file to a staging site",
483
+ inputSchema: {
484
+ contentSiteId: z.string().describe("Content Site ID"),
485
+ stagingSiteId: z.string().describe("Staging Site ID"),
486
+ file: z.string().describe("Base64 encoded file data or file path"),
487
+ filename: z.string().optional().describe("Original filename"),
488
+ mimetype: z.string().optional().describe("File MIME type"),
489
+ },
490
+ }, async ({ contentSiteId, stagingSiteId, file, filename, mimetype }) => {
491
+ try {
492
+ const payload = { file, filename, mimetype };
493
+ const response = await apiClient.post(`/tools/files/contentSite/${contentSiteId}/staging-sites/${stagingSiteId}/files`, payload);
494
+ return {
495
+ content: [
496
+ {
497
+ type: "text",
498
+ text: JSON.stringify(response.data, null, 2),
499
+ },
500
+ ],
501
+ };
502
+ }
503
+ catch (error) {
504
+ return {
505
+ content: [
506
+ {
507
+ type: "text",
508
+ text: handleApiError(error),
509
+ },
510
+ ],
511
+ isError: true,
512
+ };
513
+ }
514
+ });
515
+ // ========== CONTENT SITE MANAGEMENT TOOLS ==========
516
+ // Create Content Site
517
+ server.registerTool("create_content_site", {
518
+ title: "Create Content Site",
519
+ description: "Create a new content site in the current account",
520
+ inputSchema: {
521
+ name: z.string().describe("Content site name"),
522
+ sampleDataId: z.string().optional().describe("Optional sample data ID to initialize the site. The sample data list can be found in the ref data."),
523
+ },
524
+ }, async ({ name, sampleDataId }) => {
525
+ try {
526
+ const payload = { name, sampleDataId };
527
+ const response = await apiClient.post("/tools/content-sites", payload);
528
+ return {
529
+ content: [
530
+ {
531
+ type: "text",
532
+ text: JSON.stringify(response.data, null, 2),
533
+ },
534
+ ],
535
+ };
536
+ }
537
+ catch (error) {
538
+ return {
539
+ content: [
540
+ {
541
+ type: "text",
542
+ text: handleApiError(error),
543
+ },
544
+ ],
545
+ isError: true,
546
+ };
547
+ }
548
+ });
549
+ // Get Content Sites
550
+ server.registerTool("get_content_sites", {
551
+ title: "Get Content Sites",
552
+ description: "Get all content sites in the current account",
553
+ inputSchema: {},
554
+ }, async ({}) => {
555
+ try {
556
+ const response = await apiClient.get(`/tools/content-sites`);
557
+ return {
558
+ content: [
559
+ {
560
+ type: "text",
561
+ text: JSON.stringify(response.data, null, 2),
562
+ },
563
+ ],
564
+ };
565
+ }
566
+ catch (error) {
567
+ return {
568
+ content: [
569
+ {
570
+ type: "text",
571
+ text: handleApiError(error),
572
+ },
573
+ ],
574
+ isError: true,
575
+ };
576
+ }
577
+ });
578
+ // Get Content Site
579
+ server.registerTool("get_content_site", {
580
+ title: "Get Content Site",
581
+ description: "Get content site details by ID",
582
+ inputSchema: {
583
+ contentSiteId: z.string().describe("Content Site ID"),
584
+ },
585
+ }, async ({ contentSiteId }) => {
586
+ try {
587
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}`);
588
+ return {
589
+ content: [
590
+ {
591
+ type: "text",
592
+ text: JSON.stringify(response.data, null, 2),
593
+ },
594
+ ],
595
+ };
596
+ }
597
+ catch (error) {
598
+ return {
599
+ content: [
600
+ {
601
+ type: "text",
602
+ text: handleApiError(error),
603
+ },
604
+ ],
605
+ isError: true,
606
+ };
607
+ }
608
+ });
609
+ // Update Content Site
610
+ server.registerTool("update_content_site", {
611
+ title: "Update Staging Site",
612
+ description: "Update staging site information",
613
+ inputSchema: {
614
+ contentSiteId: z.string().describe("Content Site ID"),
615
+ name: z.string().optional().describe("Content Site Name"),
616
+ contactEmail: z.string().optional().describe("Content Site Contact Email"),
617
+ billingEmail: z.string().optional().describe("Content Site Billing Email"),
618
+ addressLine1: z.string().optional().describe("Content Site Address Line 1"),
619
+ addressLine2: z.string().optional().describe("Content Site Address Line 2"),
620
+ city: z.string().optional().describe("Content Site City"),
621
+ state: z.string().optional().describe("Content Site State"),
622
+ country: z.string().optional().describe("Content Site Country"),
623
+ postalCode: z.string().optional().describe("Content Site Postal Code"),
624
+ productionUrl: z.string().optional().describe("Content Site Production URL"),
625
+ repoUrl: z.string().optional().describe("Content Site Repository URL"),
626
+ },
627
+ }, async ({ contentSiteId, name, contactEmail, billingEmail, addressLine1, addressLine2, city, state, country, postalCode, productionUrl, repoUrl }) => {
628
+ try {
629
+ const payload = {};
630
+ if (name)
631
+ payload.name = name;
632
+ if (contactEmail)
633
+ payload.contactEmail = contactEmail;
634
+ if (billingEmail)
635
+ payload.billingEmail = billingEmail;
636
+ if (addressLine1)
637
+ payload.addressLine1 = addressLine1;
638
+ if (addressLine2)
639
+ payload.addressLine2 = addressLine2;
640
+ if (city)
641
+ payload.city = city;
642
+ if (state)
643
+ payload.state = state;
644
+ if (country)
645
+ payload.country = country;
646
+ if (postalCode)
647
+ payload.postalCode = postalCode;
648
+ if (productionUrl)
649
+ payload.productionUrl = productionUrl;
650
+ if (repoUrl)
651
+ payload.repoUrl = repoUrl;
652
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}`, payload);
653
+ return {
654
+ content: [
655
+ {
656
+ type: "text",
657
+ text: JSON.stringify(response.data, null, 2),
658
+ },
659
+ ],
660
+ };
661
+ }
662
+ catch (error) {
663
+ return {
664
+ content: [
665
+ {
666
+ type: "text",
667
+ text: handleApiError(error),
668
+ },
669
+ ],
670
+ isError: true,
671
+ };
672
+ }
673
+ });
674
+ // Delete Content Site
675
+ server.registerTool("delete_content_site", {
676
+ title: "Delete Content Site",
677
+ description: "Delete a content site",
678
+ inputSchema: {
679
+ contentSiteId: z.string().describe("Content Site ID"),
680
+ reason: z.string().optional().describe("Reason for deletion"),
681
+ },
682
+ }, async ({ contentSiteId, reason }) => {
683
+ try {
684
+ const payload = { reason };
685
+ const response = await apiClient.delete(`/tools/content-sites/${contentSiteId}`, {
686
+ data: payload,
687
+ });
688
+ return {
689
+ content: [
690
+ {
691
+ type: "text",
692
+ text: JSON.stringify(response.data, null, 2),
693
+ },
694
+ ],
695
+ };
696
+ }
697
+ catch (error) {
698
+ return {
699
+ content: [
700
+ {
701
+ type: "text",
702
+ text: handleApiError(error),
703
+ },
704
+ ],
705
+ isError: true,
706
+ };
707
+ }
708
+ });
709
+ // ========== STAGING SITE MANAGEMENT TOOLS ==========
710
+ // Update Staging Site
711
+ server.registerTool("update_staging_site", {
712
+ title: "Update Staging Site",
713
+ description: "Update staging site information",
714
+ inputSchema: {
715
+ contentSiteId: z.string().describe("Content Site ID"),
716
+ stagingSiteId: z.string().describe("Staging Site ID"),
717
+ name: z.string().optional().describe("Staging site name"),
718
+ locale: z.string().optional().describe("Staging site default locale"),
719
+ isHead: z.boolean().describe("Is the default staging site"),
720
+ content: z.record(z.any()).optional().describe("Staging site content"),
721
+ stageUrl: z.string().optional().describe("Staging site stage URL"),
722
+ guideUrl: z.string().optional().describe("Staging site guide URL"),
723
+ },
724
+ }, async ({ contentSiteId, stagingSiteId, name, locale, isHead, content, stageUrl, guideUrl }) => {
725
+ try {
726
+ const payload = {};
727
+ if (name)
728
+ payload.name = name;
729
+ if (locale)
730
+ payload.locale = locale;
731
+ if (isHead)
732
+ payload.isHead = isHead;
733
+ if (content)
734
+ payload.content = content;
735
+ if (stageUrl)
736
+ payload.stageUrl = stageUrl;
737
+ if (guideUrl)
738
+ payload.guideUrl = guideUrl;
739
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}`, payload);
740
+ return {
741
+ content: [
742
+ {
743
+ type: "text",
744
+ text: JSON.stringify(response.data, null, 2),
745
+ },
746
+ ],
747
+ };
748
+ }
749
+ catch (error) {
750
+ return {
751
+ content: [
752
+ {
753
+ type: "text",
754
+ text: handleApiError(error),
755
+ },
756
+ ],
757
+ isError: true,
758
+ };
759
+ }
760
+ });
761
+ // Delete Staging Site
762
+ server.registerTool("delete_staging_site", {
763
+ title: "Delete Staging Site",
764
+ description: "Delete a staging site",
765
+ inputSchema: {
766
+ contentSiteId: z.string().describe("Content Site ID"),
767
+ stagingSiteId: z.string().describe("Staging Site ID"),
768
+ reason: z.string().optional().describe("Reason for deletion"),
769
+ },
770
+ }, async ({ contentSiteId, stagingSiteId, reason }) => {
771
+ try {
772
+ const payload = { reason };
773
+ const response = await apiClient.delete(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}`, {
774
+ data: payload,
775
+ });
776
+ return {
777
+ content: [
778
+ {
779
+ type: "text",
780
+ text: JSON.stringify(response.data, null, 2),
781
+ },
782
+ ],
783
+ };
784
+ }
785
+ catch (error) {
786
+ return {
787
+ content: [
788
+ {
789
+ type: "text",
790
+ text: handleApiError(error),
791
+ },
792
+ ],
793
+ isError: true,
794
+ };
795
+ }
796
+ });
797
+ // Publish Staging Site
798
+ server.registerTool("publish_staging_site", {
799
+ title: "Publish Staging Site",
800
+ description: "Publish a staging site to make it live",
801
+ inputSchema: {
802
+ contentSiteId: z.string().describe("Content Site ID"),
803
+ stagingSiteId: z.string().describe("Staging Site ID"),
804
+ comments: z.string().describe("Message for this publish"),
805
+ isApproved: z.boolean().describe("Is the publish approved"),
806
+ publishAt: z.date().optional().describe("When to publish the staging site"),
807
+ previewUrl: z.string().optional().describe("Preview URL for the staging site"),
808
+ },
809
+ }, async ({ contentSiteId, stagingSiteId, comments, isApproved, publishAt, previewUrl }) => {
810
+ try {
811
+ const payload = { comments, isApproved, publishAt, previewUrl };
812
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/publish`, payload);
813
+ return {
814
+ content: [
815
+ {
816
+ type: "text",
817
+ text: JSON.stringify(response.data, null, 2),
818
+ },
819
+ ],
820
+ };
821
+ }
822
+ catch (error) {
823
+ return {
824
+ content: [
825
+ {
826
+ type: "text",
827
+ text: handleApiError(error),
828
+ },
829
+ ],
830
+ isError: true,
831
+ };
832
+ }
833
+ });
834
+ // Get Staging Site
835
+ server.registerTool("get_staging_site", {
836
+ title: "Get Staging Site",
837
+ description: "Get staging site details by ID",
838
+ inputSchema: {
839
+ contentSiteId: z.string().describe("Content site ID"),
840
+ stagingSiteId: z.string().describe("Staging site ID"),
841
+ },
842
+ }, async ({ contentSiteId, stagingSiteId }) => {
843
+ try {
844
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}`);
845
+ return {
846
+ content: [
847
+ {
848
+ type: "text",
849
+ text: JSON.stringify(response.data, null, 2),
850
+ },
851
+ ],
852
+ };
853
+ }
854
+ catch (error) {
855
+ return {
856
+ content: [
857
+ {
858
+ type: "text",
859
+ text: handleApiError(error),
860
+ },
861
+ ],
862
+ isError: true,
863
+ };
864
+ }
865
+ });
866
+ // Get Staging Site Pages
867
+ server.registerTool("get_staging_site_pages", {
868
+ title: "Get Staging Site Pages",
869
+ description: "Get staging site pages by ID",
870
+ inputSchema: {
871
+ contentSiteId: z.string().describe("Content site ID"),
872
+ stagingSiteId: z.string().describe("Staging site ID"),
873
+ },
874
+ }, async ({ contentSiteId, stagingSiteId }) => {
875
+ try {
876
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages`);
877
+ return {
878
+ content: [
879
+ {
880
+ type: "text",
881
+ text: JSON.stringify(response.data, null, 2),
882
+ },
883
+ ],
884
+ };
885
+ }
886
+ catch (error) {
887
+ return {
888
+ content: [
889
+ {
890
+ type: "text",
891
+ text: handleApiError(error),
892
+ },
893
+ ],
894
+ isError: true,
895
+ };
896
+ }
897
+ });
898
+ // Get Staging Site Configuration
899
+ server.registerTool("get_staging_site_configuration", {
900
+ title: "Get Staging Site Configuration",
901
+ description: "Get staging site configuration by ID",
902
+ inputSchema: {
903
+ contentSiteId: z.string().describe("Content site ID"),
904
+ stagingSiteId: z.string().describe("Staging site ID"),
905
+ },
906
+ }, async ({ contentSiteId, stagingSiteId }) => {
907
+ try {
908
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/configuration`);
909
+ return {
910
+ content: [
911
+ {
912
+ type: "text",
913
+ text: JSON.stringify(response.data, null, 2),
914
+ },
915
+ ],
916
+ };
917
+ }
918
+ catch (error) {
919
+ return {
920
+ content: [
921
+ {
922
+ type: "text",
923
+ text: handleApiError(error),
924
+ },
925
+ ],
926
+ isError: true,
927
+ };
928
+ }
929
+ });
930
+ // Get Staging Site Logs
931
+ server.registerTool("get_staging_site_logs", {
932
+ title: "Get Staging Site Logs",
933
+ description: "Change logs since last publish",
934
+ inputSchema: {
935
+ contentSiteId: z.string().describe("Content site ID"),
936
+ stagingSiteId: z.string().describe("Staging site ID"),
937
+ },
938
+ }, async ({ contentSiteId, stagingSiteId }) => {
939
+ try {
940
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/logs`);
941
+ return {
942
+ content: [
943
+ {
944
+ type: "text",
945
+ text: JSON.stringify(response.data, null, 2),
946
+ },
947
+ ],
948
+ };
949
+ }
950
+ catch (error) {
951
+ return {
952
+ content: [
953
+ {
954
+ type: "text",
955
+ text: handleApiError(error),
956
+ },
957
+ ],
958
+ isError: true,
959
+ };
960
+ }
961
+ });
962
+ // Get Published Sites
963
+ server.registerTool("get_published_sites", {
964
+ title: "Get Published Sites",
965
+ description: "Get published sites for a content site",
966
+ inputSchema: {
967
+ contentSiteId: z.string().describe("Content site ID"),
968
+ },
969
+ }, async ({ contentSiteId }) => {
970
+ try {
971
+ const params = new URLSearchParams();
972
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/published-sites`);
973
+ return {
974
+ content: [
975
+ {
976
+ type: "text",
977
+ text: JSON.stringify(response.data, null, 2),
978
+ },
979
+ ],
980
+ };
981
+ }
982
+ catch (error) {
983
+ return {
984
+ content: [
985
+ {
986
+ type: "text",
987
+ text: handleApiError(error),
988
+ },
989
+ ],
990
+ isError: true,
991
+ };
992
+ }
993
+ });
994
+ // Revert Staging Site
995
+ server.registerTool("revert_staging_site", {
996
+ title: "Revert Staging Site",
997
+ description: "Revert a staging site to a previous state",
998
+ inputSchema: {
999
+ contentSiteId: z.string().describe("Content site ID"),
1000
+ stagingSiteId: z.string().describe("Staging site ID"),
1001
+ reason: z.string().optional().describe("Reason for reversion"),
1002
+ },
1003
+ }, async ({ contentSiteId, stagingSiteId, reason }) => {
1004
+ try {
1005
+ const payload = { reason };
1006
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/revert`, payload);
1007
+ return {
1008
+ content: [
1009
+ {
1010
+ type: "text",
1011
+ text: JSON.stringify(response.data, null, 2),
1012
+ },
1013
+ ],
1014
+ };
1015
+ }
1016
+ catch (error) {
1017
+ return {
1018
+ content: [
1019
+ {
1020
+ type: "text",
1021
+ text: handleApiError(error),
1022
+ },
1023
+ ],
1024
+ isError: true,
1025
+ };
1026
+ }
1027
+ });
1028
+ // Clone Staging Site
1029
+ server.registerTool("clone_staging_site", {
1030
+ title: "Clone Staging Site",
1031
+ description: "Clone a staging site",
1032
+ inputSchema: {
1033
+ contentSiteId: z.string().describe("Content site ID"),
1034
+ stagingSiteId: z.string().describe("Staging site ID"),
1035
+ newName: z.string().optional().describe("Name for the cloned site"),
1036
+ },
1037
+ }, async ({ contentSiteId, stagingSiteId, newName }) => {
1038
+ try {
1039
+ const payload = { newName };
1040
+ const response = await apiClient.post(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/clone`, payload);
1041
+ return {
1042
+ content: [
1043
+ {
1044
+ type: "text",
1045
+ text: JSON.stringify(response.data, null, 2),
1046
+ },
1047
+ ],
1048
+ };
1049
+ }
1050
+ catch (error) {
1051
+ return {
1052
+ content: [
1053
+ {
1054
+ type: "text",
1055
+ text: handleApiError(error),
1056
+ },
1057
+ ],
1058
+ isError: true,
1059
+ };
1060
+ }
1061
+ });
1062
+ // ========== WORKING SITE SECTION MANAGEMENT TOOLS ==========
1063
+ // Create Staging Site Section
1064
+ server.registerTool("create_staging_site_section", {
1065
+ title: "Create Staging Site Section",
1066
+ description: "Create a new section in a staging site page",
1067
+ inputSchema: {
1068
+ contentSiteId: z.string().describe("Content site ID"),
1069
+ stagingSiteId: z.string().describe("Staging site ID"),
1070
+ pageId: z.string().describe("Page ID"),
1071
+ sectionType: z.string().describe("Section type"),
1072
+ content: z.record(z.any()).optional().describe("Section content"),
1073
+ },
1074
+ }, async ({ contentSiteId, stagingSiteId, pageId, sectionType, content }) => {
1075
+ try {
1076
+ const payload = { sectionType, content };
1077
+ const response = await apiClient.post(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/sections`, payload);
1078
+ return {
1079
+ content: [
1080
+ {
1081
+ type: "text",
1082
+ text: JSON.stringify(response.data, null, 2),
1083
+ },
1084
+ ],
1085
+ };
1086
+ }
1087
+ catch (error) {
1088
+ return {
1089
+ content: [
1090
+ {
1091
+ type: "text",
1092
+ text: handleApiError(error),
1093
+ },
1094
+ ],
1095
+ isError: true,
1096
+ };
1097
+ }
1098
+ });
1099
+ // Get Staging Site Section
1100
+ server.registerTool("get_staging_site_section", {
1101
+ title: "Get Staging Site Section",
1102
+ description: "Get details of a staging site section",
1103
+ inputSchema: {
1104
+ contentSiteId: z.string().describe("Content site ID"),
1105
+ stagingSiteId: z.string().describe("Staging site ID"),
1106
+ pageId: z.string().describe("Page ID"),
1107
+ sectionId: z.string().describe("Section ID"),
1108
+ },
1109
+ }, async ({ contentSiteId, stagingSiteId, pageId, sectionId }) => {
1110
+ try {
1111
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/sections/${sectionId}`);
1112
+ return {
1113
+ content: [
1114
+ {
1115
+ type: "text",
1116
+ text: JSON.stringify(response.data, null, 2),
1117
+ },
1118
+ ],
1119
+ };
1120
+ }
1121
+ catch (error) {
1122
+ return {
1123
+ content: [
1124
+ {
1125
+ type: "text",
1126
+ text: handleApiError(error),
1127
+ },
1128
+ ],
1129
+ isError: true,
1130
+ };
1131
+ }
1132
+ });
1133
+ // Update Staging Site Section
1134
+ server.registerTool("update_staging_site_section", {
1135
+ title: "Update Staging Site Section",
1136
+ description: "Update a staging site section",
1137
+ inputSchema: {
1138
+ contentSiteId: z.string().describe("Content site ID"),
1139
+ stagingSiteId: z.string().describe("Staging site ID"),
1140
+ pageId: z.string().describe("Page ID"),
1141
+ sectionId: z.string().describe("Section ID"),
1142
+ content: z.record(z.any()).optional().describe("Section content"),
1143
+ order: z.number().optional().describe("Section order"),
1144
+ },
1145
+ }, async ({ contentSiteId, stagingSiteId, pageId, sectionId, content, order }) => {
1146
+ try {
1147
+ const payload = {};
1148
+ if (content)
1149
+ payload.content = content;
1150
+ if (order !== undefined)
1151
+ payload.order = order;
1152
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/sections/${sectionId}`, payload);
1153
+ return {
1154
+ content: [
1155
+ {
1156
+ type: "text",
1157
+ text: JSON.stringify(response.data, null, 2),
1158
+ },
1159
+ ],
1160
+ };
1161
+ }
1162
+ catch (error) {
1163
+ return {
1164
+ content: [
1165
+ {
1166
+ type: "text",
1167
+ text: handleApiError(error),
1168
+ },
1169
+ ],
1170
+ isError: true,
1171
+ };
1172
+ }
1173
+ });
1174
+ // Delete Staging Site Section
1175
+ server.registerTool("delete_staging_site_section", {
1176
+ title: "Delete Staging Site Section",
1177
+ description: "Delete a staging site section",
1178
+ inputSchema: {
1179
+ contentSiteId: z.string().describe("Content site ID"),
1180
+ stagingSiteId: z.string().describe("Staging site ID"),
1181
+ pageId: z.string().describe("Page ID"),
1182
+ sectionId: z.string().describe("Section ID"),
1183
+ },
1184
+ }, async ({ contentSiteId, stagingSiteId, pageId, sectionId }) => {
1185
+ try {
1186
+ const response = await apiClient.delete(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/sections/${sectionId}`);
1187
+ return {
1188
+ content: [
1189
+ {
1190
+ type: "text",
1191
+ text: JSON.stringify(response.data, null, 2),
1192
+ },
1193
+ ],
1194
+ };
1195
+ }
1196
+ catch (error) {
1197
+ return {
1198
+ content: [
1199
+ {
1200
+ type: "text",
1201
+ text: handleApiError(error),
1202
+ },
1203
+ ],
1204
+ isError: true,
1205
+ };
1206
+ }
1207
+ });
1208
+ // Publish Staging Site Section
1209
+ server.registerTool("publish_staging_site_section", {
1210
+ title: "Publish Staging Site Section",
1211
+ description: "Publish a staging site section",
1212
+ inputSchema: {
1213
+ contentSiteId: z.string().describe("Content site ID"),
1214
+ stagingSiteId: z.string().describe("Staging site ID"),
1215
+ pageId: z.string().describe("Page ID"),
1216
+ sectionId: z.string().describe("Section ID"),
1217
+ publishMessage: z.string().optional().describe("Message for this publish"),
1218
+ },
1219
+ }, async ({ contentSiteId, stagingSiteId, pageId, sectionId, publishMessage }) => {
1220
+ try {
1221
+ const payload = { publishMessage };
1222
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/sections/${sectionId}/publish`, payload);
1223
+ return {
1224
+ content: [
1225
+ {
1226
+ type: "text",
1227
+ text: JSON.stringify(response.data, null, 2),
1228
+ },
1229
+ ],
1230
+ };
1231
+ }
1232
+ catch (error) {
1233
+ return {
1234
+ content: [
1235
+ {
1236
+ type: "text",
1237
+ text: handleApiError(error),
1238
+ },
1239
+ ],
1240
+ isError: true,
1241
+ };
1242
+ }
1243
+ });
1244
+ // Revert Staging Site Section
1245
+ server.registerTool("revert_staging_site_section", {
1246
+ title: "Revert Staging Site Section",
1247
+ description: "Revert a staging site section to a previous state",
1248
+ inputSchema: {
1249
+ contentSiteId: z.string().describe("Content site ID"),
1250
+ stagingSiteId: z.string().describe("Staging site ID"),
1251
+ pageId: z.string().describe("Page ID"),
1252
+ sectionId: z.string().describe("Section ID"),
1253
+ },
1254
+ }, async ({ contentSiteId, stagingSiteId, pageId, sectionId }) => {
1255
+ try {
1256
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/sections/${sectionId}/revert`);
1257
+ return {
1258
+ content: [
1259
+ {
1260
+ type: "text",
1261
+ text: JSON.stringify(response.data, null, 2),
1262
+ },
1263
+ ],
1264
+ };
1265
+ }
1266
+ catch (error) {
1267
+ return {
1268
+ content: [
1269
+ {
1270
+ type: "text",
1271
+ text: handleApiError(error),
1272
+ },
1273
+ ],
1274
+ isError: true,
1275
+ };
1276
+ }
1277
+ });
1278
+ // Get Staging Site Section Logs
1279
+ server.registerTool("get_staging_site_section_logs", {
1280
+ title: "Get Staging Site Section Logs",
1281
+ description: "Get logs for a staging site section since the last publish",
1282
+ inputSchema: {
1283
+ contentSiteId: z.string().describe("Content site ID"),
1284
+ stagingSiteId: z.string().describe("Staging site ID"),
1285
+ pageId: z.string().describe("Page ID"),
1286
+ sectionId: z.string().describe("Section Common ID (CID)"),
1287
+ },
1288
+ }, async ({ contentSiteId, stagingSiteId, pageId, sectionId }) => {
1289
+ try {
1290
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/sections/${sectionId}/logs`);
1291
+ return {
1292
+ content: [
1293
+ {
1294
+ type: "text",
1295
+ text: JSON.stringify(response.data, null, 2),
1296
+ },
1297
+ ],
1298
+ };
1299
+ }
1300
+ catch (error) {
1301
+ return {
1302
+ content: [
1303
+ {
1304
+ type: "text",
1305
+ text: handleApiError(error),
1306
+ },
1307
+ ],
1308
+ isError: true,
1309
+ };
1310
+ }
1311
+ });
1312
+ // ========== WORKING SITE PAGE MANAGEMENT TOOLS ==========
1313
+ // Create Staging Site Page
1314
+ server.registerTool("create_staging_site_page", {
1315
+ title: "Create Staging Site Page",
1316
+ description: "Create a new page in a staging site",
1317
+ inputSchema: {
1318
+ contentSiteId: z.string().describe("Content site ID"),
1319
+ stagingSiteId: z.string().describe("Staging site ID"),
1320
+ title: z.string().describe("Page title"),
1321
+ identifier: z.string().describe("Page identifier"),
1322
+ content: z.record(z.any()).optional().describe("Page content"),
1323
+ },
1324
+ }, async ({ contentSiteId, stagingSiteId, title, identifier, content }) => {
1325
+ try {
1326
+ const payload = { title, identifier, content };
1327
+ const response = await apiClient.post(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages`, payload);
1328
+ return {
1329
+ content: [
1330
+ {
1331
+ type: "text",
1332
+ text: JSON.stringify(response.data, null, 2),
1333
+ },
1334
+ ],
1335
+ };
1336
+ }
1337
+ catch (error) {
1338
+ return {
1339
+ content: [
1340
+ {
1341
+ type: "text",
1342
+ text: handleApiError(error),
1343
+ },
1344
+ ],
1345
+ isError: true,
1346
+ };
1347
+ }
1348
+ });
1349
+ // Get Staging Site Page
1350
+ server.registerTool("get_staging_site_page", {
1351
+ title: "Get Staging Site Page",
1352
+ description: "Get details of a staging site page",
1353
+ inputSchema: {
1354
+ contentSiteId: z.string().describe("Content site ID"),
1355
+ stagingSiteId: z.string().describe("Staging site ID"),
1356
+ pageId: z.string().describe("Page ID"),
1357
+ includeSections: z.boolean().describe("Include page sections"),
1358
+ },
1359
+ }, async ({ contentSiteId, stagingSiteId, pageId, includeSections }) => {
1360
+ try {
1361
+ const params = new URLSearchParams();
1362
+ if (includeSections)
1363
+ params.append("includeSections", "true");
1364
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}?${params}`);
1365
+ return {
1366
+ content: [
1367
+ {
1368
+ type: "text",
1369
+ text: JSON.stringify(response.data, null, 2),
1370
+ },
1371
+ ],
1372
+ };
1373
+ }
1374
+ catch (error) {
1375
+ return {
1376
+ content: [
1377
+ {
1378
+ type: "text",
1379
+ text: handleApiError(error),
1380
+ },
1381
+ ],
1382
+ isError: true,
1383
+ };
1384
+ }
1385
+ });
1386
+ // Update Staging Site Page
1387
+ server.registerTool("update_staging_site_page", {
1388
+ title: "Update Staging Site Page",
1389
+ description: "Update a staging site page",
1390
+ inputSchema: {
1391
+ contentSiteId: z.string().describe("Content site ID"),
1392
+ stagingSiteId: z.string().describe("Staging site ID"),
1393
+ pageId: z.string().describe("Page ID"),
1394
+ title: z.string().describe("Page title"),
1395
+ identifier: z.string().describe("Page identifier"),
1396
+ order: z.number().optional().describe("Page order"),
1397
+ },
1398
+ }, async ({ contentSiteId, stagingSiteId, pageId, title, identifier, order }) => {
1399
+ try {
1400
+ const payload = {};
1401
+ if (title)
1402
+ payload.title = title;
1403
+ if (identifier)
1404
+ payload.identifier = identifier;
1405
+ if (order !== undefined)
1406
+ payload.order = order;
1407
+ const response = await apiClient.put(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}`, payload);
1408
+ return {
1409
+ content: [
1410
+ {
1411
+ type: "text",
1412
+ text: JSON.stringify(response.data, null, 2),
1413
+ },
1414
+ ],
1415
+ };
1416
+ }
1417
+ catch (error) {
1418
+ return {
1419
+ content: [
1420
+ {
1421
+ type: "text",
1422
+ text: handleApiError(error),
1423
+ },
1424
+ ],
1425
+ isError: true,
1426
+ };
1427
+ }
1428
+ });
1429
+ // Delete Staging Site Page
1430
+ server.registerTool("delete_staging_site_page", {
1431
+ title: "Delete Staging Site Page",
1432
+ description: "Delete a staging site page",
1433
+ inputSchema: {
1434
+ contentSiteId: z.string().describe("Content site ID"),
1435
+ stagingSiteId: z.string().describe("Staging site ID"),
1436
+ pageId: z.string().describe("Page ID"),
1437
+ },
1438
+ }, async ({ contentSiteId, stagingSiteId, pageId }) => {
1439
+ try {
1440
+ const response = await apiClient.delete(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}`);
1441
+ return {
1442
+ content: [
1443
+ {
1444
+ type: "text",
1445
+ text: JSON.stringify(response.data, null, 2),
1446
+ },
1447
+ ],
1448
+ };
1449
+ }
1450
+ catch (error) {
1451
+ return {
1452
+ content: [
1453
+ {
1454
+ type: "text",
1455
+ text: handleApiError(error),
1456
+ },
1457
+ ],
1458
+ isError: true,
1459
+ };
1460
+ }
1461
+ });
1462
+ // Get Staging Site Page Logs
1463
+ server.registerTool("get_staging_site_page_logs", {
1464
+ title: "Get Staging Site Page Logs",
1465
+ description: "Get the change logs for a staging site page since the last publish",
1466
+ inputSchema: {
1467
+ contentSiteId: z.string().describe("Content site ID"),
1468
+ stagingSiteId: z.string().describe("Staging site ID"),
1469
+ pageId: z.string().describe("Page common ID (CID)"),
1470
+ },
1471
+ }, async ({ contentSiteId, stagingSiteId, pageId }) => {
1472
+ try {
1473
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/staging-sites/${stagingSiteId}/pages/${pageId}/logs`);
1474
+ return {
1475
+ content: [
1476
+ {
1477
+ type: "text",
1478
+ text: JSON.stringify(response.data, null, 2),
1479
+ },
1480
+ ],
1481
+ };
1482
+ }
1483
+ catch (error) {
1484
+ return {
1485
+ content: [
1486
+ {
1487
+ type: "text",
1488
+ text: handleApiError(error),
1489
+ },
1490
+ ],
1491
+ isError: true,
1492
+ };
1493
+ }
1494
+ });
1495
+ // ========== ContentSite ANALYTICS & ADMIN TOOLS ==========
1496
+ // Get ContentSite Logs
1497
+ server.registerTool("get_content_site_logs", {
1498
+ title: "Get ContentSite Logs",
1499
+ description: "Get the last 15 activity logs for a content site",
1500
+ inputSchema: {
1501
+ contentSiteId: z.string().describe("Content site ID"),
1502
+ },
1503
+ }, async ({ contentSiteId }) => {
1504
+ try {
1505
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/logs`);
1506
+ return {
1507
+ content: [
1508
+ {
1509
+ type: "text",
1510
+ text: JSON.stringify(response.data, null, 2),
1511
+ },
1512
+ ],
1513
+ };
1514
+ }
1515
+ catch (error) {
1516
+ return {
1517
+ content: [
1518
+ {
1519
+ type: "text",
1520
+ text: handleApiError(error),
1521
+ },
1522
+ ],
1523
+ isError: true,
1524
+ };
1525
+ }
1526
+ });
1527
+ // Get ContentSite Hits (Analytics)
1528
+ server.registerTool("get_content_site_hits", {
1529
+ title: "Get ContentSite Hits",
1530
+ description: "Get daily hits for a content site",
1531
+ inputSchema: {
1532
+ contentSiteId: z.string().describe("Content site ID"),
1533
+ },
1534
+ }, async ({ contentSiteId }) => {
1535
+ try {
1536
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/hits`);
1537
+ return {
1538
+ content: [
1539
+ {
1540
+ type: "text",
1541
+ text: JSON.stringify(response.data, null, 2),
1542
+ },
1543
+ ],
1544
+ };
1545
+ }
1546
+ catch (error) {
1547
+ return {
1548
+ content: [
1549
+ {
1550
+ type: "text",
1551
+ text: handleApiError(error),
1552
+ },
1553
+ ],
1554
+ isError: true,
1555
+ };
1556
+ }
1557
+ });
1558
+ // Get ContentSite Users
1559
+ server.registerTool("get_content_site_accounts", {
1560
+ title: "Get ContentSite Accounts",
1561
+ description: "Get accounts associated with a content site",
1562
+ inputSchema: {
1563
+ contentSiteId: z.string().describe("Content site ID"),
1564
+ },
1565
+ }, async ({ contentSiteId }) => {
1566
+ try {
1567
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/accounts`);
1568
+ return {
1569
+ content: [
1570
+ {
1571
+ type: "text",
1572
+ text: JSON.stringify(response.data, null, 2),
1573
+ },
1574
+ ],
1575
+ };
1576
+ }
1577
+ catch (error) {
1578
+ return {
1579
+ content: [
1580
+ {
1581
+ type: "text",
1582
+ text: handleApiError(error),
1583
+ },
1584
+ ],
1585
+ isError: true,
1586
+ };
1587
+ }
1588
+ });
1589
+ // Get Content site Claims
1590
+ server.registerTool("get_content_site_claims", {
1591
+ title: "Get Content site Claims",
1592
+ description: "Get current user claims for the content site",
1593
+ inputSchema: {
1594
+ contentSiteId: z.string().describe("Content site ID"),
1595
+ },
1596
+ }, async ({ contentSiteId }) => {
1597
+ try {
1598
+ const response = await apiClient.get(`/tools/content-sites/${contentSiteId}/claims`);
1599
+ return {
1600
+ content: [
1601
+ {
1602
+ type: "text",
1603
+ text: JSON.stringify(response.data, null, 2),
1604
+ },
1605
+ ],
1606
+ };
1607
+ }
1608
+ catch (error) {
1609
+ return {
1610
+ content: [
1611
+ {
1612
+ type: "text",
1613
+ text: handleApiError(error),
1614
+ },
1615
+ ],
1616
+ isError: true,
1617
+ };
1618
+ }
1619
+ });
1620
+ // ========== RESOURCES ==========
1621
+ // Register a resource for API configuration
1622
+ server.registerResource("api_config", "config://api", {
1623
+ title: "Headlesshost API Configuration",
1624
+ description: "Current Headlesshost API configuration and available endpoints",
1625
+ mimeType: "application/json",
1626
+ }, async () => {
1627
+ const config = {
1628
+ baseUrl: API_BASE_URL,
1629
+ hasApiKey: !!API_KEY,
1630
+ endpoints: {
1631
+ general: {
1632
+ ping: "GET /tools/ping - Test authentication and connection",
1633
+ },
1634
+ membership: {
1635
+ register: "POST /tools/membership/register - Register new user",
1636
+ createUser: "POST /tools/membership/users - Create user",
1637
+ getUser: "GET /tools/membership/users/:id - Get user details",
1638
+ updateUser: "PUT /tools/membership/users/:id - Update user",
1639
+ deleteUser: "DELETE /tools/membership/users/:id - Delete user",
1640
+ getAccount: "GET /tools/membership/account - Get account info",
1641
+ updateAccount: "PUT /tools/membership/account - Update account",
1642
+ },
1643
+ contentSite: {
1644
+ createContentSite: "POST /tools/content-sites - Create content site",
1645
+ getContentSites: "GET /tools/content-sites - Get content sites",
1646
+ getContentSite: "GET /tools/content-sites/:id - Get content site details",
1647
+ getContentSiteLogs: "GET /tools/content-sites/:contentSiteId/logs - Get content site logs",
1648
+ getContentSiteHits: "GET /tools/content-sites/:contentSiteId/hits - Get content site analytics",
1649
+ getContentSiteUsers: "GET /tools/content-sites/:contentSiteId/users - Get content site users",
1650
+ getContentSiteClaims: "GET /tools/content-sites/:contentSiteId/claims - Get content site claims",
1651
+ },
1652
+ stagingSites: {
1653
+ createStagingSite: "POST /tools/content-sites/:contentSiteId/staging-sites - Create staging site",
1654
+ getStagingSite: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId - Get staging site details",
1655
+ getStagingSitePages: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages - Get staging site pages",
1656
+ getStagingSiteConfiguration: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/configuration - Get staging site configuration, including section types (BusinessSections) available for pages",
1657
+ updateStagingSite: "PUT /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId - Update staging site",
1658
+ deleteStagingSite: "DELETE /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId - Delete staging site",
1659
+ publishStagingSite: "PUT /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/publish - Publish staging site",
1660
+ revertStagingSite: "PUT /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/revert - Revert staging site",
1661
+ cloneStagingSite: "POST /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/clone - Clone staging site",
1662
+ getStagingSiteLogs: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/logs - Get staging site logs",
1663
+ getPublishedSites: "GET /tools/content-sites/:contentSiteId/published-sites - Get published sites",
1664
+ },
1665
+ stagingSitePages: {
1666
+ createStagingSitePage: "POST /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages - Create staging site page",
1667
+ getStagingSitePage: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId - Get staging site page",
1668
+ updateStagingSitePage: "PUT /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId - Update staging site page",
1669
+ deleteStagingSitePage: "DELETE /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId - Delete staging site page",
1670
+ getStagingSitePageLogs: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/logs - Get staging site page logs",
1671
+ },
1672
+ stagingSiteSections: {
1673
+ createStagingSiteSection: "POST /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/sections - Create staging site section",
1674
+ getStagingSiteSection: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/sections/:sectionId - Get staging site section",
1675
+ updateStagingSiteSection: "PUT /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/sections/:sectionId - Update staging site section",
1676
+ deleteStagingSiteSection: "DELETE /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/sections/:sectionId - Delete staging site section",
1677
+ publishStagingSiteSection: "PUT /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/sections/:sectionId/publish - Publish staging site section",
1678
+ revertStagingSiteSection: "PUT /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/sections/:sectionId/revert - Revert staging site section",
1679
+ getStagingSiteSectionLogs: "GET /tools/content-sites/:contentSiteId/staging-sites/:stagingSiteId/pages/:pageId/sections/:sectionId/logs - Get staging site section logs",
1680
+ },
1681
+ system: {
1682
+ getRefData: "GET /tools/system/refdata - Get reference data",
1683
+ },
1684
+ },
1685
+ };
1686
+ return {
1687
+ contents: [
1688
+ {
1689
+ uri: "config://api",
1690
+ text: JSON.stringify(config, null, 2),
1691
+ mimeType: "application/json",
1692
+ },
1693
+ ],
1694
+ };
1695
+ });
1696
+ // Register a resource for API health status
1697
+ server.registerResource("api_health", "health://api", {
1698
+ title: "Headlesshost API Health Status",
1699
+ description: "Current health status and connectivity information for the Headlesshost API",
1700
+ mimeType: "application/json",
1701
+ }, async () => {
1702
+ try {
1703
+ const startTime = Date.now();
1704
+ const response = await apiClient.get("/tools/ping");
1705
+ const endTime = Date.now();
1706
+ const responseTime = endTime - startTime;
1707
+ const healthInfo = {
1708
+ status: "healthy",
1709
+ responseTime: `${responseTime}ms`,
1710
+ timestamp: new Date().toISOString(),
1711
+ apiUrl: API_BASE_URL,
1712
+ authenticated: response.status === 200,
1713
+ serverResponse: response.data,
1714
+ };
1715
+ return {
1716
+ contents: [
1717
+ {
1718
+ uri: "health://api",
1719
+ text: JSON.stringify(healthInfo, null, 2),
1720
+ mimeType: "application/json",
1721
+ },
1722
+ ],
1723
+ };
1724
+ }
1725
+ catch (error) {
1726
+ const healthInfo = {
1727
+ status: "unhealthy",
1728
+ timestamp: new Date().toISOString(),
1729
+ apiUrl: API_BASE_URL,
1730
+ error: error instanceof Error ? error.message : "Unknown error",
1731
+ authenticated: false,
1732
+ };
1733
+ return {
1734
+ contents: [
1735
+ {
1736
+ uri: "health://api",
1737
+ text: JSON.stringify(healthInfo, null, 2),
1738
+ mimeType: "application/json",
1739
+ },
1740
+ ],
1741
+ };
1742
+ }
1743
+ });
1744
+ // Start the server
1745
+ async function main() {
1746
+ const transport = new StdioServerTransport();
1747
+ await server.connect(transport);
1748
+ // Log to stderr so it doesn't interfere with the JSON-RPC protocol on stdout
1749
+ console.error("Headlesshost MCP Server started successfully");
1750
+ console.error(`API Base URL: ${API_BASE_URL}`);
1751
+ console.error(`API Key configured: ${!!API_KEY}`);
1752
+ }
1753
+ main().catch((error) => {
1754
+ console.error("Unhandled error in main:", error);
1755
+ process.exit(1);
1756
+ });
1757
+ // Handle graceful shutdown
1758
+ process.on("SIGINT", async () => {
1759
+ console.error("Shutting down Headlesshost MCP Server...");
1760
+ process.exit(0);
1761
+ });
1762
+ process.on("SIGTERM", async () => {
1763
+ console.error("Shutting down Headlesshost MCP Server...");
1764
+ process.exit(0);
1765
+ });
1766
+ //# sourceMappingURL=index.js.map