servherd 0.0.1 → 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.
Files changed (95) hide show
  1. package/CONTRIBUTING.md +250 -0
  2. package/LICENSE +21 -0
  3. package/README.md +653 -29
  4. package/dist/cli/commands/config.d.ts +35 -0
  5. package/dist/cli/commands/config.js +336 -0
  6. package/dist/cli/commands/info.d.ts +37 -0
  7. package/dist/cli/commands/info.js +98 -0
  8. package/dist/cli/commands/list.d.ts +26 -0
  9. package/dist/cli/commands/list.js +86 -0
  10. package/dist/cli/commands/logs.d.ts +46 -0
  11. package/dist/cli/commands/logs.js +292 -0
  12. package/dist/cli/commands/mcp.d.ts +5 -0
  13. package/dist/cli/commands/mcp.js +17 -0
  14. package/dist/cli/commands/refresh.d.ts +20 -0
  15. package/dist/cli/commands/refresh.js +139 -0
  16. package/dist/cli/commands/remove.d.ts +20 -0
  17. package/dist/cli/commands/remove.js +144 -0
  18. package/dist/cli/commands/restart.d.ts +25 -0
  19. package/dist/cli/commands/restart.js +177 -0
  20. package/dist/cli/commands/start.d.ts +37 -0
  21. package/dist/cli/commands/start.js +293 -0
  22. package/dist/cli/commands/stop.d.ts +20 -0
  23. package/dist/cli/commands/stop.js +108 -0
  24. package/dist/cli/index.d.ts +9 -0
  25. package/dist/cli/index.js +160 -0
  26. package/dist/cli/output/formatters.d.ts +117 -0
  27. package/dist/cli/output/formatters.js +454 -0
  28. package/dist/cli/output/json-formatter.d.ts +22 -0
  29. package/dist/cli/output/json-formatter.js +40 -0
  30. package/dist/index.d.ts +15 -0
  31. package/dist/index.js +25 -0
  32. package/dist/mcp/index.d.ts +14 -0
  33. package/dist/mcp/index.js +352 -0
  34. package/dist/mcp/resources/servers.d.ts +14 -0
  35. package/dist/mcp/resources/servers.js +128 -0
  36. package/dist/mcp/tools/config.d.ts +33 -0
  37. package/dist/mcp/tools/config.js +88 -0
  38. package/dist/mcp/tools/info.d.ts +36 -0
  39. package/dist/mcp/tools/info.js +65 -0
  40. package/dist/mcp/tools/list.d.ts +36 -0
  41. package/dist/mcp/tools/list.js +49 -0
  42. package/dist/mcp/tools/logs.d.ts +44 -0
  43. package/dist/mcp/tools/logs.js +55 -0
  44. package/dist/mcp/tools/refresh.d.ts +33 -0
  45. package/dist/mcp/tools/refresh.js +54 -0
  46. package/dist/mcp/tools/remove.d.ts +23 -0
  47. package/dist/mcp/tools/remove.js +43 -0
  48. package/dist/mcp/tools/restart.d.ts +23 -0
  49. package/dist/mcp/tools/restart.js +42 -0
  50. package/dist/mcp/tools/start.d.ts +38 -0
  51. package/dist/mcp/tools/start.js +73 -0
  52. package/dist/mcp/tools/stop.d.ts +23 -0
  53. package/dist/mcp/tools/stop.js +40 -0
  54. package/dist/services/config.service.d.ts +80 -0
  55. package/dist/services/config.service.js +227 -0
  56. package/dist/services/port.service.d.ts +82 -0
  57. package/dist/services/port.service.js +151 -0
  58. package/dist/services/process.service.d.ts +61 -0
  59. package/dist/services/process.service.js +220 -0
  60. package/dist/services/registry.service.d.ts +50 -0
  61. package/dist/services/registry.service.js +157 -0
  62. package/dist/types/config.d.ts +107 -0
  63. package/dist/types/config.js +44 -0
  64. package/dist/types/errors.d.ts +102 -0
  65. package/dist/types/errors.js +197 -0
  66. package/dist/types/pm2.d.ts +50 -0
  67. package/dist/types/pm2.js +4 -0
  68. package/dist/types/registry.d.ts +230 -0
  69. package/dist/types/registry.js +33 -0
  70. package/dist/utils/ci-detector.d.ts +31 -0
  71. package/dist/utils/ci-detector.js +68 -0
  72. package/dist/utils/config-drift.d.ts +71 -0
  73. package/dist/utils/config-drift.js +128 -0
  74. package/dist/utils/error-handler.d.ts +21 -0
  75. package/dist/utils/error-handler.js +38 -0
  76. package/dist/utils/log-follower.d.ts +10 -0
  77. package/dist/utils/log-follower.js +98 -0
  78. package/dist/utils/logger.d.ts +11 -0
  79. package/dist/utils/logger.js +24 -0
  80. package/dist/utils/names.d.ts +7 -0
  81. package/dist/utils/names.js +20 -0
  82. package/dist/utils/template.d.ts +88 -0
  83. package/dist/utils/template.js +180 -0
  84. package/dist/utils/time-parser.d.ts +19 -0
  85. package/dist/utils/time-parser.js +54 -0
  86. package/docs/ci-cd.md +408 -0
  87. package/docs/configuration.md +325 -0
  88. package/docs/mcp-integration.md +411 -0
  89. package/examples/basic-usage/README.md +187 -0
  90. package/examples/ci-github-actions/workflow.yml +195 -0
  91. package/examples/mcp-claude-code/README.md +213 -0
  92. package/examples/multi-server/README.md +270 -0
  93. package/examples/storybook/README.md +187 -0
  94. package/examples/vite-project/README.md +251 -0
  95. package/package.json +123 -6
@@ -0,0 +1,352 @@
1
+ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { startToolName, startToolDescription, startToolSchema, handleStartTool, } from "./tools/start.js";
4
+ import { stopToolName, stopToolDescription, stopToolSchema, handleStopTool, } from "./tools/stop.js";
5
+ import { restartToolName, restartToolDescription, restartToolSchema, handleRestartTool, } from "./tools/restart.js";
6
+ import { listToolName, listToolDescription, listToolSchema, handleListTool, } from "./tools/list.js";
7
+ import { infoToolName, infoToolDescription, infoToolSchema, handleInfoTool, } from "./tools/info.js";
8
+ import { logsToolName, logsToolDescription, logsToolSchema, handleLogsTool, } from "./tools/logs.js";
9
+ import { configToolName, configToolDescription, configToolSchema, handleConfigTool, } from "./tools/config.js";
10
+ import { removeToolName, removeToolDescription, removeToolSchema, handleRemoveTool, } from "./tools/remove.js";
11
+ import { refreshToolName, refreshToolDescription, refreshToolSchema, handleRefreshTool, } from "./tools/refresh.js";
12
+ import { listServerResources, readServerResource, } from "./resources/servers.js";
13
+ import { logger } from "../utils/logger.js";
14
+ /**
15
+ * Create and configure an MCP server for servherd
16
+ */
17
+ export function createMCPServer(options = {}) {
18
+ const name = options.name || "servherd";
19
+ const version = options.version || "0.1.0";
20
+ const server = new McpServer({ name, version }, {
21
+ capabilities: {
22
+ tools: {},
23
+ resources: {},
24
+ },
25
+ instructions: "servherd is a tool for managing development servers. " +
26
+ "Use servherd_start to start a server, servherd_stop to stop it, " +
27
+ "servherd_list to see all servers, and servherd_info to get details about a specific server.",
28
+ });
29
+ // Register tools
30
+ registerTools(server);
31
+ // Register resources
32
+ registerResources(server);
33
+ return server;
34
+ }
35
+ /**
36
+ * Register all tools with the MCP server
37
+ */
38
+ function registerTools(server) {
39
+ // Start tool
40
+ server.registerTool(startToolName, {
41
+ description: startToolDescription,
42
+ inputSchema: startToolSchema,
43
+ }, async (args) => {
44
+ try {
45
+ const result = await handleStartTool(args);
46
+ return {
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: JSON.stringify(result, null, 2),
51
+ },
52
+ ],
53
+ };
54
+ }
55
+ catch (error) {
56
+ const message = error instanceof Error ? error.message : String(error);
57
+ logger.error({ error }, "MCP start tool failed");
58
+ return {
59
+ content: [{ type: "text", text: `Error: ${message}` }],
60
+ isError: true,
61
+ };
62
+ }
63
+ });
64
+ // Stop tool
65
+ server.registerTool(stopToolName, {
66
+ description: stopToolDescription,
67
+ inputSchema: stopToolSchema,
68
+ }, async (args) => {
69
+ try {
70
+ const result = await handleStopTool(args);
71
+ return {
72
+ content: [
73
+ {
74
+ type: "text",
75
+ text: JSON.stringify(result, null, 2),
76
+ },
77
+ ],
78
+ };
79
+ }
80
+ catch (error) {
81
+ const message = error instanceof Error ? error.message : String(error);
82
+ logger.error({ error }, "MCP stop tool failed");
83
+ return {
84
+ content: [{ type: "text", text: `Error: ${message}` }],
85
+ isError: true,
86
+ };
87
+ }
88
+ });
89
+ // Restart tool
90
+ server.registerTool(restartToolName, {
91
+ description: restartToolDescription,
92
+ inputSchema: restartToolSchema,
93
+ }, async (args) => {
94
+ try {
95
+ const result = await handleRestartTool(args);
96
+ return {
97
+ content: [
98
+ {
99
+ type: "text",
100
+ text: JSON.stringify(result, null, 2),
101
+ },
102
+ ],
103
+ };
104
+ }
105
+ catch (error) {
106
+ const message = error instanceof Error ? error.message : String(error);
107
+ logger.error({ error }, "MCP restart tool failed");
108
+ return {
109
+ content: [{ type: "text", text: `Error: ${message}` }],
110
+ isError: true,
111
+ };
112
+ }
113
+ });
114
+ // List tool
115
+ server.registerTool(listToolName, {
116
+ description: listToolDescription,
117
+ inputSchema: listToolSchema,
118
+ }, async (args) => {
119
+ try {
120
+ const result = await handleListTool(args);
121
+ return {
122
+ content: [
123
+ {
124
+ type: "text",
125
+ text: JSON.stringify(result, null, 2),
126
+ },
127
+ ],
128
+ };
129
+ }
130
+ catch (error) {
131
+ const message = error instanceof Error ? error.message : String(error);
132
+ logger.error({ error }, "MCP list tool failed");
133
+ return {
134
+ content: [{ type: "text", text: `Error: ${message}` }],
135
+ isError: true,
136
+ };
137
+ }
138
+ });
139
+ // Info tool
140
+ server.registerTool(infoToolName, {
141
+ description: infoToolDescription,
142
+ inputSchema: infoToolSchema,
143
+ }, async (args) => {
144
+ try {
145
+ const result = await handleInfoTool(args);
146
+ return {
147
+ content: [
148
+ {
149
+ type: "text",
150
+ text: JSON.stringify(result, null, 2),
151
+ },
152
+ ],
153
+ };
154
+ }
155
+ catch (error) {
156
+ const message = error instanceof Error ? error.message : String(error);
157
+ logger.error({ error }, "MCP info tool failed");
158
+ return {
159
+ content: [{ type: "text", text: `Error: ${message}` }],
160
+ isError: true,
161
+ };
162
+ }
163
+ });
164
+ // Logs tool
165
+ server.registerTool(logsToolName, {
166
+ description: logsToolDescription,
167
+ inputSchema: logsToolSchema,
168
+ }, async (args) => {
169
+ try {
170
+ const result = await handleLogsTool(args);
171
+ return {
172
+ content: [
173
+ {
174
+ type: "text",
175
+ text: JSON.stringify(result, null, 2),
176
+ },
177
+ ],
178
+ };
179
+ }
180
+ catch (error) {
181
+ const message = error instanceof Error ? error.message : String(error);
182
+ logger.error({ error }, "MCP logs tool failed");
183
+ return {
184
+ content: [{ type: "text", text: `Error: ${message}` }],
185
+ isError: true,
186
+ };
187
+ }
188
+ });
189
+ // Config tool
190
+ server.registerTool(configToolName, {
191
+ description: configToolDescription,
192
+ inputSchema: configToolSchema,
193
+ }, async (args) => {
194
+ try {
195
+ const result = await handleConfigTool(args);
196
+ return {
197
+ content: [
198
+ {
199
+ type: "text",
200
+ text: JSON.stringify(result, null, 2),
201
+ },
202
+ ],
203
+ };
204
+ }
205
+ catch (error) {
206
+ const message = error instanceof Error ? error.message : String(error);
207
+ logger.error({ error }, "MCP config tool failed");
208
+ return {
209
+ content: [{ type: "text", text: `Error: ${message}` }],
210
+ isError: true,
211
+ };
212
+ }
213
+ });
214
+ // Remove tool
215
+ server.registerTool(removeToolName, {
216
+ description: removeToolDescription,
217
+ inputSchema: removeToolSchema,
218
+ }, async (args) => {
219
+ try {
220
+ const result = await handleRemoveTool(args);
221
+ return {
222
+ content: [
223
+ {
224
+ type: "text",
225
+ text: JSON.stringify(result, null, 2),
226
+ },
227
+ ],
228
+ };
229
+ }
230
+ catch (error) {
231
+ const message = error instanceof Error ? error.message : String(error);
232
+ logger.error({ error }, "MCP remove tool failed");
233
+ return {
234
+ content: [{ type: "text", text: `Error: ${message}` }],
235
+ isError: true,
236
+ };
237
+ }
238
+ });
239
+ // Refresh tool
240
+ server.registerTool(refreshToolName, {
241
+ description: refreshToolDescription,
242
+ inputSchema: refreshToolSchema,
243
+ }, async (args) => {
244
+ try {
245
+ const result = await handleRefreshTool(args);
246
+ return {
247
+ content: [
248
+ {
249
+ type: "text",
250
+ text: JSON.stringify(result, null, 2),
251
+ },
252
+ ],
253
+ };
254
+ }
255
+ catch (error) {
256
+ const message = error instanceof Error ? error.message : String(error);
257
+ logger.error({ error }, "MCP refresh tool failed");
258
+ return {
259
+ content: [{ type: "text", text: `Error: ${message}` }],
260
+ isError: true,
261
+ };
262
+ }
263
+ });
264
+ }
265
+ /**
266
+ * Register resources with the MCP server
267
+ */
268
+ function registerResources(server) {
269
+ // Register server resource template for individual servers
270
+ const serverTemplate = new ResourceTemplate("servherd://servers/{name}", {
271
+ list: async () => {
272
+ const resources = await listServerResources();
273
+ // Filter to only include server resources (not logs)
274
+ const serverResources = resources.filter((r) => !r.uri.endsWith("/logs"));
275
+ return {
276
+ resources: serverResources.map((r) => ({
277
+ uri: r.uri,
278
+ name: r.name,
279
+ description: r.description,
280
+ mimeType: r.mimeType,
281
+ })),
282
+ };
283
+ },
284
+ });
285
+ server.registerResource("Server Details", serverTemplate, {
286
+ description: "Get details about a specific managed server",
287
+ mimeType: "application/json",
288
+ }, async (uri) => {
289
+ const content = await readServerResource(uri.toString());
290
+ return {
291
+ contents: [
292
+ {
293
+ uri: uri.toString(),
294
+ mimeType: "application/json",
295
+ text: content,
296
+ },
297
+ ],
298
+ };
299
+ });
300
+ // Register server logs resource template
301
+ const logsTemplate = new ResourceTemplate("servherd://servers/{name}/logs", {
302
+ list: async () => {
303
+ const resources = await listServerResources();
304
+ // Filter to only include log resources
305
+ const logResources = resources.filter((r) => r.uri.endsWith("/logs"));
306
+ return {
307
+ resources: logResources.map((r) => ({
308
+ uri: r.uri,
309
+ name: r.name,
310
+ description: r.description,
311
+ mimeType: r.mimeType,
312
+ })),
313
+ };
314
+ },
315
+ });
316
+ server.registerResource("Server Logs", logsTemplate, {
317
+ description: "Get output logs from a managed server",
318
+ mimeType: "text/plain",
319
+ }, async (uri) => {
320
+ const content = await readServerResource(uri.toString());
321
+ return {
322
+ contents: [
323
+ {
324
+ uri: uri.toString(),
325
+ mimeType: "text/plain",
326
+ text: content,
327
+ },
328
+ ],
329
+ };
330
+ });
331
+ }
332
+ /**
333
+ * Start the MCP server in stdio mode
334
+ */
335
+ export async function startStdioServer() {
336
+ const server = createMCPServer();
337
+ const transport = new StdioServerTransport();
338
+ logger.info("Starting MCP server in stdio mode");
339
+ await server.connect(transport);
340
+ // Handle shutdown
341
+ process.on("SIGINT", async () => {
342
+ logger.info("Shutting down MCP server");
343
+ await server.close();
344
+ process.exit(0);
345
+ });
346
+ process.on("SIGTERM", async () => {
347
+ logger.info("Shutting down MCP server");
348
+ await server.close();
349
+ process.exit(0);
350
+ });
351
+ }
352
+ export { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -0,0 +1,14 @@
1
+ export interface ServerResource {
2
+ uri: string;
3
+ name: string;
4
+ description?: string;
5
+ mimeType?: string;
6
+ }
7
+ /**
8
+ * List all available server resources
9
+ */
10
+ export declare function listServerResources(): Promise<ServerResource[]>;
11
+ /**
12
+ * Read a server resource by URI
13
+ */
14
+ export declare function readServerResource(uri: string): Promise<string>;
@@ -0,0 +1,128 @@
1
+ import { RegistryService } from "../../services/registry.service.js";
2
+ import { ProcessService } from "../../services/process.service.js";
3
+ import { executeLogs } from "../../cli/commands/logs.js";
4
+ /**
5
+ * List all available server resources
6
+ */
7
+ export async function listServerResources() {
8
+ const registryService = new RegistryService();
9
+ try {
10
+ await registryService.load();
11
+ const servers = registryService.listServers();
12
+ const resources = [];
13
+ for (const server of servers) {
14
+ // Add server resource
15
+ resources.push({
16
+ uri: `servherd://servers/${server.name}`,
17
+ name: server.name,
18
+ description: server.description || `Server at ${server.cwd}`,
19
+ mimeType: "application/json",
20
+ });
21
+ // Add logs resource
22
+ resources.push({
23
+ uri: `servherd://servers/${server.name}/logs`,
24
+ name: `${server.name} logs`,
25
+ description: `Output logs for server ${server.name}`,
26
+ mimeType: "text/plain",
27
+ });
28
+ }
29
+ return resources;
30
+ }
31
+ catch {
32
+ // Return empty list if registry can't be loaded
33
+ return [];
34
+ }
35
+ }
36
+ /**
37
+ * Read a server resource by URI
38
+ */
39
+ export async function readServerResource(uri) {
40
+ // Parse URI
41
+ const match = uri.match(/^servherd:\/\/servers\/([^/]+)(\/logs)?$/);
42
+ if (!match) {
43
+ throw new Error(`Invalid resource URI: ${uri}`);
44
+ }
45
+ const serverName = match[1];
46
+ const isLogs = match[2] === "/logs";
47
+ if (isLogs) {
48
+ return readServerLogs(serverName);
49
+ }
50
+ return readServerDetails(serverName);
51
+ }
52
+ /**
53
+ * Read server details as JSON
54
+ */
55
+ async function readServerDetails(name) {
56
+ const registryService = new RegistryService();
57
+ const processService = new ProcessService();
58
+ try {
59
+ await registryService.load();
60
+ await processService.connect();
61
+ const server = registryService.findByName(name);
62
+ if (!server) {
63
+ throw new Error(`Server "${name}" not found`);
64
+ }
65
+ // Get process status
66
+ let status = "unknown";
67
+ let pid;
68
+ let uptime;
69
+ let memory;
70
+ let cpu;
71
+ try {
72
+ const procDesc = await processService.describe(server.pm2Name);
73
+ if (procDesc) {
74
+ const pm2Env = procDesc.pm2_env;
75
+ status = pm2Env.status === "online" ? "online"
76
+ : pm2Env.status === "stopped" || pm2Env.status === "stopping" ? "stopped"
77
+ : pm2Env.status === "errored" ? "errored"
78
+ : "unknown";
79
+ pid = procDesc.pid;
80
+ uptime = pm2Env.pm_uptime;
81
+ if (procDesc.monit) {
82
+ memory = procDesc.monit.memory;
83
+ cpu = procDesc.monit.cpu;
84
+ }
85
+ }
86
+ }
87
+ catch {
88
+ // Ignore PM2 errors, just report unknown status
89
+ }
90
+ const serverDetails = {
91
+ name: server.name,
92
+ status,
93
+ port: server.port,
94
+ url: `${server.protocol}://${server.hostname}:${server.port}`,
95
+ cwd: server.cwd,
96
+ command: server.command,
97
+ resolvedCommand: server.resolvedCommand,
98
+ hostname: server.hostname,
99
+ protocol: server.protocol,
100
+ tags: server.tags,
101
+ description: server.description,
102
+ env: server.env,
103
+ createdAt: server.createdAt,
104
+ pm2Name: server.pm2Name,
105
+ pid,
106
+ uptime,
107
+ memory,
108
+ cpu,
109
+ };
110
+ return JSON.stringify(serverDetails, null, 2);
111
+ }
112
+ finally {
113
+ processService.disconnect();
114
+ }
115
+ }
116
+ /**
117
+ * Read server logs
118
+ */
119
+ async function readServerLogs(name) {
120
+ const result = await executeLogs({
121
+ name,
122
+ lines: 100, // Get more lines for resource reading
123
+ });
124
+ if (!result.logs || result.logs.trim() === "") {
125
+ return "(no logs available)";
126
+ }
127
+ return result.logs;
128
+ }
@@ -0,0 +1,33 @@
1
+ import { z } from "zod";
2
+ export declare const configToolName = "servherd_config";
3
+ export declare const configToolDescription: string;
4
+ export declare const configToolSchema: z.ZodObject<{
5
+ show: z.ZodOptional<z.ZodBoolean>;
6
+ get: z.ZodOptional<z.ZodString>;
7
+ set: z.ZodOptional<z.ZodString>;
8
+ value: z.ZodOptional<z.ZodString>;
9
+ reset: z.ZodOptional<z.ZodBoolean>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ value?: string | undefined;
12
+ set?: string | undefined;
13
+ reset?: boolean | undefined;
14
+ show?: boolean | undefined;
15
+ get?: string | undefined;
16
+ }, {
17
+ value?: string | undefined;
18
+ set?: string | undefined;
19
+ reset?: boolean | undefined;
20
+ show?: boolean | undefined;
21
+ get?: string | undefined;
22
+ }>;
23
+ export type ConfigToolInput = z.infer<typeof configToolSchema>;
24
+ export interface ConfigToolResult {
25
+ action: "show" | "get" | "set" | "reset";
26
+ success: boolean;
27
+ config?: Record<string, unknown>;
28
+ key?: string;
29
+ value?: unknown;
30
+ error?: string;
31
+ message: string;
32
+ }
33
+ export declare function handleConfigTool(input: ConfigToolInput): Promise<ConfigToolResult>;
@@ -0,0 +1,88 @@
1
+ import { z } from "zod";
2
+ import { executeConfig } from "../../cli/commands/config.js";
3
+ export const configToolName = "servherd_config";
4
+ export const configToolDescription = "View or modify servherd global configuration settings. " +
5
+ "Use this tool to check current settings, change configuration values like hostname or port range, or reset to defaults. " +
6
+ "Can show all configuration values, get a specific setting by key, set a new value, or reset all settings to defaults. " +
7
+ "Returns the action performed, success status, relevant configuration data, and a descriptive message. " +
8
+ "Configuration changes persist across sessions and affect all future server operations.";
9
+ export const configToolSchema = z.object({
10
+ show: z.boolean().optional().describe("Set to true to show all configuration values"),
11
+ get: z.string().optional().describe("Get a specific configuration value by key, e.g., 'hostname', 'protocol', or 'portRange.min'"),
12
+ set: z.string().optional().describe("Configuration key to set, e.g., 'hostname' or 'portRange.max'. Requires 'value' parameter"),
13
+ value: z.string().optional().describe("Value to set for the key, e.g., '127.0.0.1' for hostname or '9999' for portRange.max"),
14
+ reset: z.boolean().optional().describe("Set to true to reset all configuration to default values"),
15
+ });
16
+ export async function handleConfigTool(input) {
17
+ // Determine action
18
+ let action = "show";
19
+ if (input.get) {
20
+ action = "get";
21
+ }
22
+ else if (input.set) {
23
+ action = "set";
24
+ }
25
+ else if (input.reset) {
26
+ action = "reset";
27
+ }
28
+ // For reset, we force it since MCP can't prompt
29
+ const result = await executeConfig({
30
+ show: action === "show",
31
+ get: input.get,
32
+ set: input.set,
33
+ value: input.value,
34
+ reset: input.reset,
35
+ force: input.reset ? true : undefined, // Force reset without prompting in MCP context
36
+ });
37
+ // Handle errors
38
+ if (result.error) {
39
+ return {
40
+ action,
41
+ success: false,
42
+ error: result.error,
43
+ message: result.error,
44
+ };
45
+ }
46
+ // Handle cancelled reset
47
+ if (result.cancelled) {
48
+ return {
49
+ action: "reset",
50
+ success: false,
51
+ message: "Reset was cancelled",
52
+ };
53
+ }
54
+ // Build response based on action
55
+ switch (action) {
56
+ case "get":
57
+ return {
58
+ action: "get",
59
+ success: true,
60
+ key: result.key,
61
+ value: result.value,
62
+ message: `${result.key} = ${JSON.stringify(result.value)}`,
63
+ };
64
+ case "set":
65
+ return {
66
+ action: "set",
67
+ success: result.updated === true,
68
+ key: result.key,
69
+ value: result.value,
70
+ message: result.updated ? `Set ${result.key} to ${JSON.stringify(result.value)}` : "Failed to set value",
71
+ };
72
+ case "reset":
73
+ return {
74
+ action: "reset",
75
+ success: result.reset === true,
76
+ config: result.config,
77
+ message: "Configuration reset to defaults",
78
+ };
79
+ case "show":
80
+ default:
81
+ return {
82
+ action: "show",
83
+ success: true,
84
+ config: result.config,
85
+ message: `Configuration loaded from ${result.configPath || "defaults"}`,
86
+ };
87
+ }
88
+ }
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ export declare const infoToolName = "servherd_info";
3
+ export declare const infoToolDescription: string;
4
+ export declare const infoToolSchema: z.ZodObject<{
5
+ name: z.ZodString;
6
+ }, "strip", z.ZodTypeAny, {
7
+ name: string;
8
+ }, {
9
+ name: string;
10
+ }>;
11
+ export type InfoToolInput = z.infer<typeof infoToolSchema>;
12
+ export interface InfoToolResult {
13
+ name: string;
14
+ status: string;
15
+ url: string;
16
+ cwd: string;
17
+ command: string;
18
+ resolvedCommand: string;
19
+ port: number;
20
+ hostname: string;
21
+ protocol: string;
22
+ pid?: number;
23
+ uptime?: number;
24
+ uptimeFormatted?: string;
25
+ restarts?: number;
26
+ memory?: number;
27
+ memoryFormatted?: string;
28
+ cpu?: number;
29
+ tags?: string[];
30
+ description?: string;
31
+ createdAt: string;
32
+ pm2Name: string;
33
+ outLogPath?: string;
34
+ errLogPath?: string;
35
+ }
36
+ export declare function handleInfoTool(input: InfoToolInput): Promise<InfoToolResult>;