@hanzo/platform-mcp 1.4.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.
Files changed (83) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +534 -0
  3. package/build/http-server.js +286 -0
  4. package/build/index.js +35 -0
  5. package/build/mcp/tools/application/applicationCleanQueues.js +23 -0
  6. package/build/mcp/tools/application/applicationCreate.js +39 -0
  7. package/build/mcp/tools/application/applicationDelete.js +21 -0
  8. package/build/mcp/tools/application/applicationDeploy.js +21 -0
  9. package/build/mcp/tools/application/applicationMarkRunning.js +23 -0
  10. package/build/mcp/tools/application/applicationMove.js +22 -0
  11. package/build/mcp/tools/application/applicationOne.js +26 -0
  12. package/build/mcp/tools/application/applicationReadAppMonitoring.js +26 -0
  13. package/build/mcp/tools/application/applicationReadTraefikConfig.js +26 -0
  14. package/build/mcp/tools/application/applicationRedeploy.js +23 -0
  15. package/build/mcp/tools/application/applicationRefreshToken.js +23 -0
  16. package/build/mcp/tools/application/applicationReload.js +22 -0
  17. package/build/mcp/tools/application/applicationSaveBitbucketProvider.js +49 -0
  18. package/build/mcp/tools/application/applicationSaveBuildType.js +56 -0
  19. package/build/mcp/tools/application/applicationSaveDockerProvider.js +43 -0
  20. package/build/mcp/tools/application/applicationSaveEnvironment.js +33 -0
  21. package/build/mcp/tools/application/applicationSaveGitProvider.js +49 -0
  22. package/build/mcp/tools/application/applicationSaveGiteaProvider.js +43 -0
  23. package/build/mcp/tools/application/applicationSaveGithubProvider.js +51 -0
  24. package/build/mcp/tools/application/applicationSaveGitlabProvider.js +48 -0
  25. package/build/mcp/tools/application/applicationStart.js +21 -0
  26. package/build/mcp/tools/application/applicationStop.js +21 -0
  27. package/build/mcp/tools/application/applicationUpdate.js +319 -0
  28. package/build/mcp/tools/application/applicationUpdateTraefikConfig.js +26 -0
  29. package/build/mcp/tools/application/index.js +24 -0
  30. package/build/mcp/tools/compose/composeCreate.js +31 -0
  31. package/build/mcp/tools/compose/composeDeploy.js +26 -0
  32. package/build/mcp/tools/compose/composeOne.js +24 -0
  33. package/build/mcp/tools/compose/composeReload.js +28 -0
  34. package/build/mcp/tools/compose/composeRemove.js +26 -0
  35. package/build/mcp/tools/compose/composeSaveEnvironment.js +28 -0
  36. package/build/mcp/tools/compose/composeStart.js +26 -0
  37. package/build/mcp/tools/compose/composeStop.js +26 -0
  38. package/build/mcp/tools/compose/composeUpdate.js +34 -0
  39. package/build/mcp/tools/compose/index.js +9 -0
  40. package/build/mcp/tools/index.js +12 -0
  41. package/build/mcp/tools/mysql/index.js +13 -0
  42. package/build/mcp/tools/mysql/mysqlChangeStatus.js +24 -0
  43. package/build/mcp/tools/mysql/mysqlCreate.js +50 -0
  44. package/build/mcp/tools/mysql/mysqlDeploy.js +21 -0
  45. package/build/mcp/tools/mysql/mysqlMove.js +24 -0
  46. package/build/mcp/tools/mysql/mysqlOne.js +23 -0
  47. package/build/mcp/tools/mysql/mysqlRebuild.js +21 -0
  48. package/build/mcp/tools/mysql/mysqlReload.js +22 -0
  49. package/build/mcp/tools/mysql/mysqlRemove.js +21 -0
  50. package/build/mcp/tools/mysql/mysqlSaveEnvironment.js +26 -0
  51. package/build/mcp/tools/mysql/mysqlSaveExternalPort.js +25 -0
  52. package/build/mcp/tools/mysql/mysqlStart.js +21 -0
  53. package/build/mcp/tools/mysql/mysqlStop.js +21 -0
  54. package/build/mcp/tools/mysql/mysqlUpdate.js +103 -0
  55. package/build/mcp/tools/postgres/index.js +13 -0
  56. package/build/mcp/tools/postgres/postgresChangeStatus.js +26 -0
  57. package/build/mcp/tools/postgres/postgresCreate.js +48 -0
  58. package/build/mcp/tools/postgres/postgresDeploy.js +23 -0
  59. package/build/mcp/tools/postgres/postgresMove.js +26 -0
  60. package/build/mcp/tools/postgres/postgresOne.js +26 -0
  61. package/build/mcp/tools/postgres/postgresRebuild.js +23 -0
  62. package/build/mcp/tools/postgres/postgresReload.js +26 -0
  63. package/build/mcp/tools/postgres/postgresRemove.js +23 -0
  64. package/build/mcp/tools/postgres/postgresSaveEnvironment.js +28 -0
  65. package/build/mcp/tools/postgres/postgresSaveExternalPort.js +27 -0
  66. package/build/mcp/tools/postgres/postgresStart.js +23 -0
  67. package/build/mcp/tools/postgres/postgresStop.js +23 -0
  68. package/build/mcp/tools/postgres/postgresUpdate.js +97 -0
  69. package/build/mcp/tools/project/index.js +6 -0
  70. package/build/mcp/tools/project/projectAll.js +68 -0
  71. package/build/mcp/tools/project/projectCreate.js +30 -0
  72. package/build/mcp/tools/project/projectDuplicate.js +54 -0
  73. package/build/mcp/tools/project/projectOne.js +24 -0
  74. package/build/mcp/tools/project/projectRemove.js +21 -0
  75. package/build/mcp/tools/project/projectUpdate.js +39 -0
  76. package/build/mcp/tools/toolFactory.js +60 -0
  77. package/build/server.js +12 -0
  78. package/build/types/platform.js +3 -0
  79. package/build/utils/apiClient.js +126 -0
  80. package/build/utils/clientConfig.js +37 -0
  81. package/build/utils/logger.js +38 -0
  82. package/build/utils/responseFormatter.js +33 -0
  83. package/package.json +63 -0
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
4
+ import { createTool } from "../toolFactory.js";
5
+ export const postgresSaveEnvironment = createTool({
6
+ name: "postgres-saveEnvironment",
7
+ description: "Saves environment variables for a PostgreSQL database in Platform.",
8
+ schema: z.object({
9
+ postgresId: z
10
+ .string()
11
+ .describe("The ID of the PostgreSQL database to save environment for."),
12
+ env: z
13
+ .string()
14
+ .nullable()
15
+ .optional()
16
+ .describe("Environment variables to save for the PostgreSQL database."),
17
+ }),
18
+ annotations: {
19
+ title: "Save PostgreSQL Environment",
20
+ destructiveHint: true,
21
+ idempotentHint: false,
22
+ openWorldHint: true,
23
+ },
24
+ handler: async (input) => {
25
+ const response = await apiClient.post("/postgres.saveEnvironment", input);
26
+ return ResponseFormatter.success(`Environment variables for PostgreSQL database "${input.postgresId}" saved successfully`, response.data);
27
+ },
28
+ });
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
4
+ import { createTool } from "../toolFactory.js";
5
+ export const postgresSaveExternalPort = createTool({
6
+ name: "postgres-saveExternalPort",
7
+ description: "Saves external port configuration for a PostgreSQL database in Platform.",
8
+ schema: z.object({
9
+ postgresId: z
10
+ .string()
11
+ .describe("The ID of the PostgreSQL database to configure."),
12
+ externalPort: z
13
+ .number()
14
+ .nullable()
15
+ .describe("The external port number to expose the database on."),
16
+ }),
17
+ annotations: {
18
+ title: "Save PostgreSQL External Port",
19
+ destructiveHint: true,
20
+ idempotentHint: false,
21
+ openWorldHint: true,
22
+ },
23
+ handler: async (input) => {
24
+ const response = await apiClient.post("/postgres.saveExternalPort", input);
25
+ return ResponseFormatter.success(`External port for PostgreSQL database "${input.postgresId}" saved successfully`, response.data);
26
+ },
27
+ });
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
4
+ import { createTool } from "../toolFactory.js";
5
+ export const postgresStart = createTool({
6
+ name: "postgres-start",
7
+ description: "Starts a PostgreSQL database in Platform.",
8
+ schema: z.object({
9
+ postgresId: z
10
+ .string()
11
+ .describe("The ID of the PostgreSQL database to start."),
12
+ }),
13
+ annotations: {
14
+ title: "Start PostgreSQL Database",
15
+ destructiveHint: true,
16
+ idempotentHint: false,
17
+ openWorldHint: true,
18
+ },
19
+ handler: async (input) => {
20
+ const response = await apiClient.post("/postgres.start", input);
21
+ return ResponseFormatter.success(`PostgreSQL database "${input.postgresId}" started successfully`, response.data);
22
+ },
23
+ });
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
4
+ import { createTool } from "../toolFactory.js";
5
+ export const postgresStop = createTool({
6
+ name: "postgres-stop",
7
+ description: "Stops a PostgreSQL database in Platform.",
8
+ schema: z.object({
9
+ postgresId: z
10
+ .string()
11
+ .describe("The ID of the PostgreSQL database to stop."),
12
+ }),
13
+ annotations: {
14
+ title: "Stop PostgreSQL Database",
15
+ destructiveHint: true,
16
+ idempotentHint: false,
17
+ openWorldHint: true,
18
+ },
19
+ handler: async (input) => {
20
+ const response = await apiClient.post("/postgres.stop", input);
21
+ return ResponseFormatter.success(`PostgreSQL database "${input.postgresId}" stopped successfully`, response.data);
22
+ },
23
+ });
@@ -0,0 +1,97 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
4
+ import { createTool } from "../toolFactory.js";
5
+ export const postgresUpdate = createTool({
6
+ name: "postgres-update",
7
+ description: "Updates an existing PostgreSQL database in Platform.",
8
+ schema: z.object({
9
+ postgresId: z
10
+ .string()
11
+ .min(1)
12
+ .describe("The ID of the PostgreSQL database to update."),
13
+ name: z
14
+ .string()
15
+ .min(1)
16
+ .optional()
17
+ .describe("The new name of the PostgreSQL database."),
18
+ appName: z
19
+ .string()
20
+ .optional()
21
+ .describe("The new app name of the PostgreSQL database."),
22
+ databaseName: z
23
+ .string()
24
+ .min(1)
25
+ .optional()
26
+ .describe("The new database name."),
27
+ databaseUser: z
28
+ .string()
29
+ .min(1)
30
+ .optional()
31
+ .describe("The new database username."),
32
+ databasePassword: z
33
+ .string()
34
+ .optional()
35
+ .describe("The new database password."),
36
+ description: z
37
+ .string()
38
+ .nullable()
39
+ .optional()
40
+ .describe("The new description for the PostgreSQL database."),
41
+ dockerImage: z
42
+ .string()
43
+ .optional()
44
+ .describe("The new Docker image for PostgreSQL."),
45
+ command: z
46
+ .string()
47
+ .nullable()
48
+ .optional()
49
+ .describe("Custom command to run the PostgreSQL database."),
50
+ env: z
51
+ .string()
52
+ .nullable()
53
+ .optional()
54
+ .describe("Environment variables for the PostgreSQL database."),
55
+ memoryReservation: z
56
+ .string()
57
+ .nullable()
58
+ .optional()
59
+ .describe("Memory reservation for the PostgreSQL database."),
60
+ externalPort: z
61
+ .number()
62
+ .nullable()
63
+ .optional()
64
+ .describe("External port for the PostgreSQL database."),
65
+ memoryLimit: z
66
+ .string()
67
+ .nullable()
68
+ .optional()
69
+ .describe("Memory limit for the PostgreSQL database."),
70
+ cpuReservation: z
71
+ .string()
72
+ .nullable()
73
+ .optional()
74
+ .describe("CPU reservation for the PostgreSQL database."),
75
+ cpuLimit: z
76
+ .string()
77
+ .nullable()
78
+ .optional()
79
+ .describe("CPU limit for the PostgreSQL database."),
80
+ applicationStatus: z
81
+ .enum(["idle", "running", "done", "error"])
82
+ .optional()
83
+ .describe("Application status."),
84
+ createdAt: z.string().optional().describe("Creation date."),
85
+ projectId: z.string().optional().describe("Project ID."),
86
+ }),
87
+ annotations: {
88
+ title: "Update PostgreSQL Database",
89
+ destructiveHint: true,
90
+ idempotentHint: false,
91
+ openWorldHint: true,
92
+ },
93
+ handler: async (input) => {
94
+ const response = await apiClient.post("/postgres.update", input);
95
+ return ResponseFormatter.success(`PostgreSQL database "${input.postgresId}" updated successfully`, response.data);
96
+ },
97
+ });
@@ -0,0 +1,6 @@
1
+ export { projectAll } from "./projectAll.js";
2
+ export { projectOne } from "./projectOne.js";
3
+ export { projectCreate } from "./projectCreate.js";
4
+ export { projectUpdate } from "./projectUpdate.js";
5
+ export { projectDuplicate } from "./projectDuplicate.js";
6
+ export { projectRemove } from "./projectRemove.js";
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
4
+ import { createTool } from "../toolFactory.js";
5
+ export const projectAll = createTool({
6
+ name: "project-all",
7
+ description: "Lists all projects in Platform with optimized response size suitable for LLM consumption. Returns summary data including project info, service counts, and basic service details.",
8
+ schema: z.object({}),
9
+ annotations: {
10
+ title: "List All Projects",
11
+ readOnlyHint: true,
12
+ idempotentHint: true,
13
+ openWorldHint: true,
14
+ },
15
+ handler: async () => {
16
+ const response = await apiClient.get("/project.all");
17
+ if (!response?.data) {
18
+ return ResponseFormatter.error("Failed to fetch projects", "No response data received");
19
+ }
20
+ const projects = response.data;
21
+ // Always return optimized summary data
22
+ const optimizedProjects = projects.map((project) => ({
23
+ projectId: project.projectId,
24
+ name: project.name,
25
+ description: project.description,
26
+ createdAt: project.createdAt,
27
+ organizationId: project.organizationId,
28
+ // Service counts instead of full data
29
+ serviceCounts: {
30
+ applications: project.applications?.length || 0,
31
+ postgres: project.postgres?.length || 0,
32
+ mysql: project.mysql?.length || 0,
33
+ mariadb: project.mariadb?.length || 0,
34
+ mongo: project.mongo?.length || 0,
35
+ redis: project.redis?.length || 0,
36
+ compose: project.compose?.length || 0,
37
+ },
38
+ // Basic service summaries (only essential info)
39
+ services: {
40
+ applications: project.applications?.map((app) => ({
41
+ applicationId: app.applicationId,
42
+ name: app.name,
43
+ appName: app.appName,
44
+ applicationStatus: app.applicationStatus,
45
+ sourceType: app.sourceType,
46
+ buildType: app.buildType,
47
+ domainCount: app.domains?.length || 0,
48
+ })) || [],
49
+ postgres: project.postgres?.map((db) => ({
50
+ postgresId: db.postgresId,
51
+ name: db.name,
52
+ appName: db.appName,
53
+ applicationStatus: db.applicationStatus,
54
+ databaseName: db.databaseName,
55
+ })) || [],
56
+ compose: project.compose?.map((comp) => ({
57
+ composeId: comp.composeId,
58
+ name: comp.name,
59
+ appName: comp.appName,
60
+ composeStatus: comp.composeStatus,
61
+ sourceType: comp.sourceType,
62
+ domainCount: comp.domains?.length || 0,
63
+ })) || [],
64
+ },
65
+ }));
66
+ return ResponseFormatter.success("Successfully fetched projects summary", optimizedProjects);
67
+ },
68
+ });
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { createTool } from "../toolFactory.js";
4
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
5
+ export const projectCreate = createTool({
6
+ name: "project-create",
7
+ description: "Creates a new project in Platform.",
8
+ schema: z.object({
9
+ name: z.string().min(1).describe("The name of the project."),
10
+ description: z
11
+ .string()
12
+ .nullable()
13
+ .optional()
14
+ .describe("An optional description for the project."),
15
+ env: z
16
+ .string()
17
+ .optional()
18
+ .describe("Optional environment variables for the project."),
19
+ }),
20
+ annotations: {
21
+ title: "Create Project",
22
+ destructiveHint: false,
23
+ idempotentHint: false,
24
+ openWorldHint: true,
25
+ },
26
+ handler: async (input) => {
27
+ const response = await apiClient.post("/project.create", input);
28
+ return ResponseFormatter.success(`Project "${input.name}" created successfully`, response.data);
29
+ },
30
+ });
@@ -0,0 +1,54 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
4
+ import { createTool } from "../toolFactory.js";
5
+ const serviceSchema = z.object({
6
+ id: z.string().describe("The ID of the service."),
7
+ type: z
8
+ .enum([
9
+ "application",
10
+ "postgres",
11
+ "mariadb",
12
+ "mongo",
13
+ "mysql",
14
+ "redis",
15
+ "compose",
16
+ ])
17
+ .describe("The type of the service."),
18
+ });
19
+ export const projectDuplicate = createTool({
20
+ name: "project-duplicate",
21
+ description: "Duplicates an existing project in Platform with optional service selection.",
22
+ schema: z.object({
23
+ sourceProjectId: z
24
+ .string()
25
+ .min(1)
26
+ .describe("The ID of the source project to duplicate."),
27
+ name: z
28
+ .string()
29
+ .min(1)
30
+ .describe("The name for the new duplicated project."),
31
+ description: z
32
+ .string()
33
+ .optional()
34
+ .describe("An optional description for the duplicated project."),
35
+ includeServices: z
36
+ .boolean()
37
+ .default(true)
38
+ .describe("Whether to include services in the duplication. Defaults to true."),
39
+ selectedServices: z
40
+ .array(serviceSchema)
41
+ .optional()
42
+ .describe("Array of specific services to include. When includeServices is true and this is not provided, you MUST first retrieve all services from the source project and include ALL of them in this array. Services are not automatically included - you must explicitly list each service with its ID and type."),
43
+ }),
44
+ annotations: {
45
+ title: "Duplicate Project",
46
+ destructiveHint: false,
47
+ idempotentHint: false,
48
+ openWorldHint: true,
49
+ },
50
+ handler: async (input) => {
51
+ const response = await apiClient.post("/project.duplicate", input);
52
+ return ResponseFormatter.success(`Project "${input.name}" duplicated successfully from source project "${input.sourceProjectId}"`, response.data);
53
+ },
54
+ });
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { createTool } from "../toolFactory.js";
4
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
5
+ export const projectOne = createTool({
6
+ name: "project-one",
7
+ description: "Gets a specific project by its ID in Platform.",
8
+ schema: z.object({
9
+ projectId: z.string().describe("The ID of the project to retrieve."),
10
+ }),
11
+ annotations: {
12
+ title: "Get Project Details",
13
+ readOnlyHint: true,
14
+ idempotentHint: true,
15
+ openWorldHint: true,
16
+ },
17
+ handler: async (input) => {
18
+ const project = await apiClient.get(`/project.one?projectId=${input.projectId}`);
19
+ if (!project?.data) {
20
+ return ResponseFormatter.error("Failed to fetch project", `Project with ID "${input.projectId}" not found`);
21
+ }
22
+ return ResponseFormatter.success(`Successfully fetched project "${input.projectId}"`, project.data);
23
+ },
24
+ });
@@ -0,0 +1,21 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { createTool } from "../toolFactory.js";
4
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
5
+ export const projectRemove = createTool({
6
+ name: "project-remove",
7
+ description: "Removes/deletes an existing project in Platform.",
8
+ schema: z.object({
9
+ projectId: z.string().min(1).describe("The ID of the project to remove."),
10
+ }),
11
+ annotations: {
12
+ title: "Remove Project",
13
+ destructiveHint: true,
14
+ idempotentHint: false,
15
+ openWorldHint: true,
16
+ },
17
+ handler: async (input) => {
18
+ const response = await apiClient.post("/project.remove", input);
19
+ return ResponseFormatter.success(`Project "${input.projectId}" removed successfully`, response.data);
20
+ },
21
+ });
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ import apiClient from "../../../utils/apiClient.js";
3
+ import { createTool } from "../toolFactory.js";
4
+ import { ResponseFormatter } from "../../../utils/responseFormatter.js";
5
+ export const projectUpdate = createTool({
6
+ name: "project-update",
7
+ description: "Updates an existing project in Platform.",
8
+ schema: z.object({
9
+ projectId: z.string().min(1).describe("The ID of the project to update."),
10
+ name: z.string().min(1).optional().describe("The new name of the project."),
11
+ description: z
12
+ .string()
13
+ .nullable()
14
+ .optional()
15
+ .describe("The new description for the project."),
16
+ createdAt: z
17
+ .string()
18
+ .optional()
19
+ .describe("The creation date of the project."),
20
+ organizationId: z
21
+ .string()
22
+ .optional()
23
+ .describe("The organization ID of the project."),
24
+ env: z
25
+ .string()
26
+ .optional()
27
+ .describe("Environment variables for the project."),
28
+ }),
29
+ annotations: {
30
+ title: "Update Project",
31
+ destructiveHint: true,
32
+ idempotentHint: false,
33
+ openWorldHint: true,
34
+ },
35
+ handler: async (input) => {
36
+ const response = await apiClient.post("/project.update", input);
37
+ return ResponseFormatter.success(`Project "${input.projectId}" updated successfully`, response.data);
38
+ },
39
+ });
@@ -0,0 +1,60 @@
1
+ import apiClient from "../../utils/apiClient.js";
2
+ import { createLogger } from "../../utils/logger.js";
3
+ import { ResponseFormatter } from "../../utils/responseFormatter.js";
4
+ const logger = createLogger("ToolFactory");
5
+ export function createToolContext() {
6
+ return {
7
+ apiClient,
8
+ logger,
9
+ };
10
+ }
11
+ export function createTool(definition) {
12
+ return {
13
+ ...definition,
14
+ handler: async (input) => {
15
+ const context = createToolContext();
16
+ try {
17
+ // Validate input against schema
18
+ const validationResult = definition.schema.safeParse(input);
19
+ if (!validationResult.success) {
20
+ context.logger.warn(`Input validation failed for tool: ${definition.name}`, {
21
+ errors: validationResult.error.errors,
22
+ input,
23
+ });
24
+ const errorMessages = validationResult.error.errors
25
+ .map((err) => `${err.path.join(".")}: ${err.message}`)
26
+ .join(", ");
27
+ return ResponseFormatter.error(`Invalid input for tool: ${definition.name}`, `Validation errors: ${errorMessages}`);
28
+ }
29
+ context.logger.info(`Executing tool: ${definition.name}`, {
30
+ input: validationResult.data,
31
+ });
32
+ const result = await definition.handler(validationResult.data);
33
+ context.logger.info(`Tool executed successfully: ${definition.name}`);
34
+ return result;
35
+ }
36
+ catch (error) {
37
+ context.logger.error(`Tool execution failed: ${definition.name}`, {
38
+ error: error instanceof Error ? error.message : "Unknown error",
39
+ input,
40
+ });
41
+ // More specific error handling based on error types
42
+ if (error instanceof Error) {
43
+ if (error.message.includes("401") ||
44
+ error.message.includes("Unauthorized")) {
45
+ return ResponseFormatter.error(`Authentication failed for tool: ${definition.name}`, "Please check your PLATFORM_API_KEY configuration");
46
+ }
47
+ if (error.message.includes("404") ||
48
+ error.message.includes("Not Found")) {
49
+ return ResponseFormatter.error(`Resource not found`, `The requested resource for ${definition.name} could not be found`);
50
+ }
51
+ if (error.message.includes("500") ||
52
+ error.message.includes("Internal Server Error")) {
53
+ return ResponseFormatter.error(`Server error occurred`, `Platform server encountered an internal error while processing ${definition.name}`);
54
+ }
55
+ }
56
+ return ResponseFormatter.error(`Failed to execute tool: ${definition.name}`, `Error: ${error instanceof Error ? error.message : "Unknown error occurred"}`);
57
+ }
58
+ },
59
+ };
60
+ }
@@ -0,0 +1,12 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { allTools } from "./mcp/tools/index.js";
3
+ export function createServer() {
4
+ const server = new McpServer({
5
+ name: "platform",
6
+ version: "1.0.0",
7
+ });
8
+ for (const tool of allTools) {
9
+ server.tool(tool.name, tool.description, tool.schema.shape, tool.handler);
10
+ }
11
+ return server;
12
+ }
@@ -0,0 +1,3 @@
1
+ // Basic types for Platform API responses
2
+ // These are minimal interfaces to avoid 'any' usage while maintaining flexibility
3
+ export {};
@@ -0,0 +1,126 @@
1
+ import axios from "axios";
2
+ import { getClientConfig } from "./clientConfig.js";
3
+ import { createLogger } from "./logger.js";
4
+ // Create logger instance for axios client
5
+ const logger = createLogger("AxiosClient");
6
+ // Get configuration from existing client config
7
+ const config = getClientConfig();
8
+ // Default headers for MCP server context
9
+ const DEFAULT_HEADERS = {
10
+ "Content-Type": "application/json",
11
+ Accept: "application/json",
12
+ "x-api-key": config.authToken, // Use the same auth mechanism as httpClient
13
+ };
14
+ // Create axios instance with configuration from clientConfig
15
+ const apiClient = axios.create({
16
+ baseURL: config.platformUrl,
17
+ timeout: config.timeout,
18
+ headers: DEFAULT_HEADERS,
19
+ });
20
+ // Request interceptor - Add request timing and logging
21
+ apiClient.interceptors.request.use((config) => {
22
+ // Add request timestamp for performance monitoring
23
+ config.metadata = { startTime: Date.now() };
24
+ logger.debug("Making API request", {
25
+ method: config.method?.toUpperCase(),
26
+ url: config.url,
27
+ baseURL: config.baseURL,
28
+ hasData: !!config.data,
29
+ });
30
+ return config;
31
+ }, (error) => {
32
+ logger.error("Request interceptor error", { error: error.message });
33
+ return Promise.reject(error);
34
+ });
35
+ // Response interceptor - Handle responses and errors with proper logging
36
+ apiClient.interceptors.response.use((response) => {
37
+ // Log response time for performance monitoring
38
+ const metadata = response.config.metadata;
39
+ const startTime = metadata?.startTime;
40
+ if (startTime) {
41
+ const duration = Date.now() - startTime;
42
+ logger.info("API request completed", {
43
+ method: response.config.method?.toUpperCase(),
44
+ url: response.config.url,
45
+ status: response.status,
46
+ duration: `${duration}ms`,
47
+ });
48
+ }
49
+ return response;
50
+ }, (error) => {
51
+ const { response, request, config } = error;
52
+ // Handle different error scenarios with proper logging
53
+ if (response) {
54
+ // Server responded with error status
55
+ handleServerError(response);
56
+ }
57
+ else if (request) {
58
+ // Request was made but no response received
59
+ handleNetworkError(request, config);
60
+ }
61
+ else {
62
+ // Something else happened
63
+ handleUnknownError(error);
64
+ }
65
+ return Promise.reject(error);
66
+ });
67
+ // Helper Functions
68
+ function handleServerError(response) {
69
+ const { status, data, config } = response;
70
+ const errorContext = {
71
+ status,
72
+ method: config?.method?.toUpperCase(),
73
+ url: config?.url,
74
+ message: data?.message || data?.error || "Unknown server error",
75
+ };
76
+ switch (status) {
77
+ case 401:
78
+ logger.error("Authentication failed - Invalid or expired API key", errorContext);
79
+ break;
80
+ case 403:
81
+ logger.error("Access forbidden - Insufficient permissions", errorContext);
82
+ break;
83
+ case 404:
84
+ logger.error("Resource not found", errorContext);
85
+ break;
86
+ case 422:
87
+ logger.error("Validation error", {
88
+ ...errorContext,
89
+ errors: data?.errors,
90
+ });
91
+ break;
92
+ case 500:
93
+ logger.error("Internal server error", errorContext);
94
+ break;
95
+ default:
96
+ logger.error(`Server error (${status})`, errorContext);
97
+ }
98
+ }
99
+ function handleNetworkError(_request, config) {
100
+ logger.error("Network error - Request failed", {
101
+ method: config?.method?.toUpperCase(),
102
+ url: config?.url || "Unknown URL",
103
+ timeout: config?.timeout || "No timeout set",
104
+ error: "No response received from server",
105
+ });
106
+ }
107
+ function handleUnknownError(error) {
108
+ logger.error("Unknown error occurred", {
109
+ error: error.message,
110
+ stack: error.stack,
111
+ });
112
+ }
113
+ // Utility function to update auth token (for MCP context)
114
+ export function setAuthToken(token) {
115
+ // Update default headers for future requests
116
+ apiClient.defaults.headers.common["x-api-key"] = token;
117
+ logger.info("Auth token updated for API client");
118
+ }
119
+ // Utility function to clear auth token
120
+ export function clearAuthToken() {
121
+ // Remove authorization header
122
+ delete apiClient.defaults.headers.common["x-api-key"];
123
+ logger.info("Auth token cleared from API client");
124
+ }
125
+ // Export the configured axios instance
126
+ export default apiClient;