@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.
- package/LICENSE +13 -0
- package/README.md +534 -0
- package/build/http-server.js +286 -0
- package/build/index.js +35 -0
- package/build/mcp/tools/application/applicationCleanQueues.js +23 -0
- package/build/mcp/tools/application/applicationCreate.js +39 -0
- package/build/mcp/tools/application/applicationDelete.js +21 -0
- package/build/mcp/tools/application/applicationDeploy.js +21 -0
- package/build/mcp/tools/application/applicationMarkRunning.js +23 -0
- package/build/mcp/tools/application/applicationMove.js +22 -0
- package/build/mcp/tools/application/applicationOne.js +26 -0
- package/build/mcp/tools/application/applicationReadAppMonitoring.js +26 -0
- package/build/mcp/tools/application/applicationReadTraefikConfig.js +26 -0
- package/build/mcp/tools/application/applicationRedeploy.js +23 -0
- package/build/mcp/tools/application/applicationRefreshToken.js +23 -0
- package/build/mcp/tools/application/applicationReload.js +22 -0
- package/build/mcp/tools/application/applicationSaveBitbucketProvider.js +49 -0
- package/build/mcp/tools/application/applicationSaveBuildType.js +56 -0
- package/build/mcp/tools/application/applicationSaveDockerProvider.js +43 -0
- package/build/mcp/tools/application/applicationSaveEnvironment.js +33 -0
- package/build/mcp/tools/application/applicationSaveGitProvider.js +49 -0
- package/build/mcp/tools/application/applicationSaveGiteaProvider.js +43 -0
- package/build/mcp/tools/application/applicationSaveGithubProvider.js +51 -0
- package/build/mcp/tools/application/applicationSaveGitlabProvider.js +48 -0
- package/build/mcp/tools/application/applicationStart.js +21 -0
- package/build/mcp/tools/application/applicationStop.js +21 -0
- package/build/mcp/tools/application/applicationUpdate.js +319 -0
- package/build/mcp/tools/application/applicationUpdateTraefikConfig.js +26 -0
- package/build/mcp/tools/application/index.js +24 -0
- package/build/mcp/tools/compose/composeCreate.js +31 -0
- package/build/mcp/tools/compose/composeDeploy.js +26 -0
- package/build/mcp/tools/compose/composeOne.js +24 -0
- package/build/mcp/tools/compose/composeReload.js +28 -0
- package/build/mcp/tools/compose/composeRemove.js +26 -0
- package/build/mcp/tools/compose/composeSaveEnvironment.js +28 -0
- package/build/mcp/tools/compose/composeStart.js +26 -0
- package/build/mcp/tools/compose/composeStop.js +26 -0
- package/build/mcp/tools/compose/composeUpdate.js +34 -0
- package/build/mcp/tools/compose/index.js +9 -0
- package/build/mcp/tools/index.js +12 -0
- package/build/mcp/tools/mysql/index.js +13 -0
- package/build/mcp/tools/mysql/mysqlChangeStatus.js +24 -0
- package/build/mcp/tools/mysql/mysqlCreate.js +50 -0
- package/build/mcp/tools/mysql/mysqlDeploy.js +21 -0
- package/build/mcp/tools/mysql/mysqlMove.js +24 -0
- package/build/mcp/tools/mysql/mysqlOne.js +23 -0
- package/build/mcp/tools/mysql/mysqlRebuild.js +21 -0
- package/build/mcp/tools/mysql/mysqlReload.js +22 -0
- package/build/mcp/tools/mysql/mysqlRemove.js +21 -0
- package/build/mcp/tools/mysql/mysqlSaveEnvironment.js +26 -0
- package/build/mcp/tools/mysql/mysqlSaveExternalPort.js +25 -0
- package/build/mcp/tools/mysql/mysqlStart.js +21 -0
- package/build/mcp/tools/mysql/mysqlStop.js +21 -0
- package/build/mcp/tools/mysql/mysqlUpdate.js +103 -0
- package/build/mcp/tools/postgres/index.js +13 -0
- package/build/mcp/tools/postgres/postgresChangeStatus.js +26 -0
- package/build/mcp/tools/postgres/postgresCreate.js +48 -0
- package/build/mcp/tools/postgres/postgresDeploy.js +23 -0
- package/build/mcp/tools/postgres/postgresMove.js +26 -0
- package/build/mcp/tools/postgres/postgresOne.js +26 -0
- package/build/mcp/tools/postgres/postgresRebuild.js +23 -0
- package/build/mcp/tools/postgres/postgresReload.js +26 -0
- package/build/mcp/tools/postgres/postgresRemove.js +23 -0
- package/build/mcp/tools/postgres/postgresSaveEnvironment.js +28 -0
- package/build/mcp/tools/postgres/postgresSaveExternalPort.js +27 -0
- package/build/mcp/tools/postgres/postgresStart.js +23 -0
- package/build/mcp/tools/postgres/postgresStop.js +23 -0
- package/build/mcp/tools/postgres/postgresUpdate.js +97 -0
- package/build/mcp/tools/project/index.js +6 -0
- package/build/mcp/tools/project/projectAll.js +68 -0
- package/build/mcp/tools/project/projectCreate.js +30 -0
- package/build/mcp/tools/project/projectDuplicate.js +54 -0
- package/build/mcp/tools/project/projectOne.js +24 -0
- package/build/mcp/tools/project/projectRemove.js +21 -0
- package/build/mcp/tools/project/projectUpdate.js +39 -0
- package/build/mcp/tools/toolFactory.js +60 -0
- package/build/server.js +12 -0
- package/build/types/platform.js +3 -0
- package/build/utils/apiClient.js +126 -0
- package/build/utils/clientConfig.js +37 -0
- package/build/utils/logger.js +38 -0
- package/build/utils/responseFormatter.js +33 -0
- 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
|
+
}
|
package/build/server.js
ADDED
|
@@ -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,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;
|