ralph-hero-mcp-server 2.5.149 → 2.5.171
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/dist/index.js +3 -0
- package/dist/lib/kubectl-exec.js +70 -0
- package/dist/lib/telemetry.js +5 -5
- package/dist/tools/sre-tools.js +334 -0
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ import { registerPlanGraphTools } from "./tools/plan-graph-tools.js";
|
|
|
33
33
|
import { registerActivityTools } from "./tools/activity-tools.js";
|
|
34
34
|
import { registerDelegationTools } from "./tools/delegation-tools.js";
|
|
35
35
|
import { registerTrendsTools } from "./tools/trends-tools.js";
|
|
36
|
+
import { registerSreTools } from "./tools/sre-tools.js";
|
|
36
37
|
/**
|
|
37
38
|
* Initialize the GitHub client from environment variables.
|
|
38
39
|
*/
|
|
@@ -450,6 +451,8 @@ async function main() {
|
|
|
450
451
|
registerDelegationTools(server);
|
|
451
452
|
// Trends tools (capture_snapshot — JSONL persistence under ~/.ralph-hero/snapshots/)
|
|
452
453
|
registerTrendsTools(server, client, fieldCache);
|
|
454
|
+
// SRE operation tools (kubectl autoremediation — typed argv, no-shell invariant)
|
|
455
|
+
registerSreTools(server, client, fieldCache);
|
|
453
456
|
// Debug tools (only when RALPH_DEBUG=true)
|
|
454
457
|
if (process.env.RALPH_DEBUG === 'true') {
|
|
455
458
|
registerDebugTools(server, client);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared kubectl execution helper for all SRE operation tools.
|
|
3
|
+
*
|
|
4
|
+
* INVARIANT: This module NEVER invokes a shell. All kubectl calls go through
|
|
5
|
+
* `child_process.execFile` with `shell: false` (the execFile default), which
|
|
6
|
+
* passes argv directly to execve(2). String-interpolated shell commands and
|
|
7
|
+
* `exec()` are explicitly forbidden here.
|
|
8
|
+
*
|
|
9
|
+
* The {@link FORBIDDEN_FLAGS} list provides a defense-in-depth check on top of
|
|
10
|
+
* the typed Zod schemas in sre-tools.ts. The typed schemas should already make
|
|
11
|
+
* these flags unreachable, but the helper enforces a hard floor so a future
|
|
12
|
+
* regression (e.g., a new typed param that happens to produce a forbidden flag)
|
|
13
|
+
* is caught at the exec layer, not silently allowed.
|
|
14
|
+
*/
|
|
15
|
+
import { execFile as _execFile } from "node:child_process";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
const execFileAsync = promisify(_execFile);
|
|
18
|
+
/**
|
|
19
|
+
* Flags that are unconditionally forbidden in kubectl argv.
|
|
20
|
+
*
|
|
21
|
+
* These represent destructive or resource-exhausting operations that the
|
|
22
|
+
* sre-fixit agent must never perform. They are defense-in-depth: the typed
|
|
23
|
+
* Zod schemas in sre-tools.ts make them structurally unreachable, but this
|
|
24
|
+
* list catches future regressions where a new typed param happens to produce
|
|
25
|
+
* one of these strings.
|
|
26
|
+
*/
|
|
27
|
+
export const FORBIDDEN_FLAGS = [
|
|
28
|
+
"--force",
|
|
29
|
+
"--cascade=foreground",
|
|
30
|
+
"--grace-period=0",
|
|
31
|
+
"--delete-emptydir-data",
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* Execute kubectl with the given argv array.
|
|
35
|
+
*
|
|
36
|
+
* Uses `child_process.execFile` (shell: false by default) so the argv is
|
|
37
|
+
* passed directly to execve(2) — no shell interpretation, no metacharacter
|
|
38
|
+
* expansion, no glob expansion.
|
|
39
|
+
*
|
|
40
|
+
* @param args - Typed argv array (e.g. `["scale", "--namespace", "default", ...]`).
|
|
41
|
+
* Never accepts a string command.
|
|
42
|
+
* @throws {Error} If any element of `args` matches a {@link FORBIDDEN_FLAGS} entry.
|
|
43
|
+
* @returns A typed result with `stdout`, `stderr`, and `exitCode`.
|
|
44
|
+
*/
|
|
45
|
+
export async function runKubectl(args) {
|
|
46
|
+
// Defense-in-depth: reject any argv element that matches a forbidden flag.
|
|
47
|
+
for (const arg of args) {
|
|
48
|
+
if (FORBIDDEN_FLAGS.includes(arg)) {
|
|
49
|
+
throw new Error(`kubectl argv contains forbidden flag: ${arg}. ` +
|
|
50
|
+
`Forbidden flags: ${FORBIDDEN_FLAGS.join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const { stdout, stderr } = await execFileAsync("kubectl", args, {
|
|
55
|
+
shell: false,
|
|
56
|
+
});
|
|
57
|
+
return { stdout, stderr, exitCode: 0 };
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
// execFile rejects when the process exits with a non-zero code.
|
|
61
|
+
// The error object carries stdout/stderr from the failed run.
|
|
62
|
+
const e = err;
|
|
63
|
+
return {
|
|
64
|
+
stdout: e.stdout ?? "",
|
|
65
|
+
stderr: e.stderr ?? "",
|
|
66
|
+
exitCode: typeof e.code === "number" ? e.code : 1,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=kubectl-exec.js.map
|
package/dist/lib/telemetry.js
CHANGED
|
@@ -132,16 +132,16 @@ export async function initTelemetry() {
|
|
|
132
132
|
// is unset, none of these modules are loaded into memory.
|
|
133
133
|
const { NodeSDK } = await import("@opentelemetry/sdk-node");
|
|
134
134
|
const { OTLPTraceExporter } = await import("@opentelemetry/exporter-trace-otlp-http");
|
|
135
|
-
const {
|
|
136
|
-
const {
|
|
135
|
+
const { resourceFromAttributes } = await import("@opentelemetry/resources");
|
|
136
|
+
const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } = await import("@opentelemetry/semantic-conventions");
|
|
137
137
|
const { BatchSpanProcessor } = await import("@opentelemetry/sdk-trace-base");
|
|
138
138
|
const endpoint = process.env.OTEL_EXPORTER_OTLP_ENDPOINT ??
|
|
139
139
|
"http://localhost:3100/api/public/otel/v1/traces";
|
|
140
140
|
const exporter = new OTLPTraceExporter({ url: endpoint });
|
|
141
141
|
const sdk = new NodeSDK({
|
|
142
|
-
resource:
|
|
143
|
-
[
|
|
144
|
-
[
|
|
142
|
+
resource: resourceFromAttributes({
|
|
143
|
+
[ATTR_SERVICE_NAME]: "ralph-hero",
|
|
144
|
+
[ATTR_SERVICE_VERSION]: resolveServiceVersion(),
|
|
145
145
|
}),
|
|
146
146
|
spanProcessors: [
|
|
147
147
|
new TokenScrubbingSpanProcessor(),
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SRE operation tools for kubectl autoremediation.
|
|
3
|
+
*
|
|
4
|
+
* This module registers the `ralph_hero__sre__*` family of typed MCP tools.
|
|
5
|
+
* Each tool accepts explicit, narrowly-typed parameters and delegates to the
|
|
6
|
+
* shared {@link runKubectl} helper from `../lib/kubectl-exec.ts`.
|
|
7
|
+
*
|
|
8
|
+
* INVARIANT (no-shell): All kubectl invocations go through `runKubectl`, which
|
|
9
|
+
* uses `child_process.execFile` with `shell: false`. There are NO string-
|
|
10
|
+
* interpolated shell commands, NO `exec()` calls, and NO `Bash` tool usage in
|
|
11
|
+
* this module or the agent that consumes it. Argv is always a plain array
|
|
12
|
+
* literal — never built via template strings, string concat, or user-controlled
|
|
13
|
+
* flag pass-through.
|
|
14
|
+
*
|
|
15
|
+
* Phases 2-5 of the GH-1285 plan add the four operation tool registrations
|
|
16
|
+
* (sre__scale, sre__rollout_restart, sre__delete_pod, sre__drain) inside
|
|
17
|
+
* `registerSreTools`. Phase 1 (GH-1287) establishes the module skeleton and
|
|
18
|
+
* wires the registration call in index.ts.
|
|
19
|
+
*/
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { runKubectl } from "../lib/kubectl-exec.js";
|
|
22
|
+
import { toolSuccess, toolError } from "../types.js";
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Shared Zod field schemas
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* RFC 1123 label regex for Kubernetes namespace and deployment names.
|
|
28
|
+
* Intentionally rejects shell metacharacters, slashes, newlines, and empty
|
|
29
|
+
* strings as a single Zod check.
|
|
30
|
+
*/
|
|
31
|
+
const k8sLabelSchema = z.string().min(1).regex(/^[a-z0-9-]+$/);
|
|
32
|
+
/**
|
|
33
|
+
* Bounded replica count. Ceiling is 50 — configurable in a follow-up; the
|
|
34
|
+
* explicit ceiling prevents runaway scale-out from an LLM miscalculation.
|
|
35
|
+
*/
|
|
36
|
+
export const REPLICA_CEILING = 50;
|
|
37
|
+
const replicasSchema = z.number().int().min(0).max(REPLICA_CEILING);
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// sre__scale schemas
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
/**
|
|
42
|
+
* Raw Zod shape for the sre__scale tool.
|
|
43
|
+
* Passed to `server.tool()` which expects a `ZodRawShape` (plain object of
|
|
44
|
+
* Zod field schemas).
|
|
45
|
+
*/
|
|
46
|
+
const sreScaleShape = {
|
|
47
|
+
namespace: k8sLabelSchema.describe("Kubernetes namespace (RFC 1123 label: lowercase alphanumeric and hyphens only)."),
|
|
48
|
+
deployment: k8sLabelSchema.describe("Deployment name (RFC 1123 label: lowercase alphanumeric and hyphens only)."),
|
|
49
|
+
replicas: replicasSchema.describe(`Target replica count (integer, 0–${REPLICA_CEILING}).`),
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Strict Zod object schema for sre__scale parameters.
|
|
53
|
+
*
|
|
54
|
+
* Exported so adversarial-input tests (sre-tools.test.ts) can call
|
|
55
|
+
* `.safeParse()` directly. `.strict()` ensures unknown keys are rejected —
|
|
56
|
+
* any attempt to pass extra fields (e.g., a `flags` bypass) is caught at
|
|
57
|
+
* the schema level.
|
|
58
|
+
*/
|
|
59
|
+
export const sreScaleSchema = z.object(sreScaleShape).strict();
|
|
60
|
+
/**
|
|
61
|
+
* Build the kubectl argv array for a scale operation.
|
|
62
|
+
*
|
|
63
|
+
* Exported for unit-testing the argv shape independently of the MCP server.
|
|
64
|
+
* The argv is a plain array literal — no string interpolation, no concat.
|
|
65
|
+
*
|
|
66
|
+
* @param namespace - Validated Kubernetes namespace.
|
|
67
|
+
* @param deployment - Validated deployment name.
|
|
68
|
+
* @param replicas - Validated replica count.
|
|
69
|
+
*/
|
|
70
|
+
export function buildScaleArgv(namespace, deployment, replicas) {
|
|
71
|
+
return [
|
|
72
|
+
"scale",
|
|
73
|
+
"--namespace",
|
|
74
|
+
namespace,
|
|
75
|
+
"deployment",
|
|
76
|
+
deployment,
|
|
77
|
+
"--replicas",
|
|
78
|
+
String(replicas),
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// sre__rollout_restart schemas (Phase 3 / GH-1289)
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
/**
|
|
85
|
+
* Raw Zod shape for the sre__rollout_restart tool.
|
|
86
|
+
*/
|
|
87
|
+
const sreRolloutRestartShape = {
|
|
88
|
+
namespace: k8sLabelSchema.describe("Kubernetes namespace (RFC 1123 label: lowercase alphanumeric and hyphens only)."),
|
|
89
|
+
deployment: k8sLabelSchema.describe("Deployment name (RFC 1123 label: lowercase alphanumeric and hyphens only)."),
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Strict Zod object schema for sre__rollout_restart parameters.
|
|
93
|
+
*
|
|
94
|
+
* Exported so adversarial-input tests (sre-tools.test.ts) can call
|
|
95
|
+
* `.safeParse()` directly. `.strict()` ensures unknown keys are rejected.
|
|
96
|
+
*/
|
|
97
|
+
export const sreRolloutRestartSchema = z.object(sreRolloutRestartShape).strict();
|
|
98
|
+
/**
|
|
99
|
+
* Build the kubectl argv array for a rollout restart operation.
|
|
100
|
+
*
|
|
101
|
+
* Exported for unit-testing the argv shape independently of the MCP server.
|
|
102
|
+
*
|
|
103
|
+
* NOTE — deliberate template-literal exception: This is the only argv builder
|
|
104
|
+
* in the sre__* family that uses a template literal. The `deployment/<name>`
|
|
105
|
+
* form is specific to `kubectl rollout restart`'s resource-qualified argument
|
|
106
|
+
* syntax. The interpolation is safe by construction: the `deployment` Zod
|
|
107
|
+
* schema (`/^[a-z0-9-]+$/`) forbids `/`, newlines, and shell metacharacters —
|
|
108
|
+
* the only characters that could escape the literal prefix. Do NOT generalise
|
|
109
|
+
* this pattern to other phases; phases 2, 4, and 5 keep plain array literals.
|
|
110
|
+
*
|
|
111
|
+
* @param namespace - Validated Kubernetes namespace.
|
|
112
|
+
* @param deployment - Validated deployment name.
|
|
113
|
+
*/
|
|
114
|
+
export function buildRolloutRestartArgv(namespace, deployment) {
|
|
115
|
+
return [
|
|
116
|
+
"rollout",
|
|
117
|
+
"restart",
|
|
118
|
+
"--namespace",
|
|
119
|
+
namespace,
|
|
120
|
+
`deployment/${deployment}`,
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// sre__delete_pod schemas (Phase 4 / GH-1290)
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
/**
|
|
127
|
+
* Raw Zod shape for the sre__delete_pod tool.
|
|
128
|
+
*
|
|
129
|
+
* The schema has exactly two fields — `namespace` and `pod`. There is no
|
|
130
|
+
* label-selector field, no `--force` field, and no `--grace-period` field.
|
|
131
|
+
* The absence of these fields is the primary typed-surface guarantee: there is
|
|
132
|
+
* no way to express bulk deletion, forced deletion, or grace-period override
|
|
133
|
+
* through this schema. `.strict()` rejects any extra keys the caller might
|
|
134
|
+
* try to pass.
|
|
135
|
+
*/
|
|
136
|
+
const sreDeletePodShape = {
|
|
137
|
+
namespace: k8sLabelSchema.describe("Kubernetes namespace (RFC 1123 label: lowercase alphanumeric and hyphens only)."),
|
|
138
|
+
pod: k8sLabelSchema.describe("Pod name to delete (RFC 1123 label: lowercase alphanumeric and hyphens only). " +
|
|
139
|
+
"Single pod only — no label selector, no wildcard."),
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Strict Zod object schema for sre__delete_pod parameters.
|
|
143
|
+
*
|
|
144
|
+
* Exported so adversarial-input tests (sre-tools.test.ts) can call
|
|
145
|
+
* `.safeParse()` directly, including the `.strict()` no-label-selector
|
|
146
|
+
* assertion. `.strict()` ensures unknown keys (e.g., `selector: "app=foo"`)
|
|
147
|
+
* are rejected at the schema level.
|
|
148
|
+
*/
|
|
149
|
+
export const sreDeletePodSchema = z.object(sreDeletePodShape).strict();
|
|
150
|
+
/**
|
|
151
|
+
* Build the kubectl argv array for a delete-pod operation.
|
|
152
|
+
*
|
|
153
|
+
* Exported for unit-testing the argv shape independently of the MCP server.
|
|
154
|
+
* The argv is a plain array literal — no string interpolation, no concat.
|
|
155
|
+
*
|
|
156
|
+
* @param namespace - Validated Kubernetes namespace.
|
|
157
|
+
* @param pod - Validated pod name (single pod, no label selector).
|
|
158
|
+
*/
|
|
159
|
+
export function buildDeletePodArgv(namespace, pod) {
|
|
160
|
+
return ["delete", "pod", "--namespace", namespace, pod];
|
|
161
|
+
}
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
// sre__drain schemas (Phase 5 / GH-1291)
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
/**
|
|
166
|
+
* Node name regex — slightly looser than the RFC 1123 label regex because
|
|
167
|
+
* Kubernetes node names can be in FQDN form (e.g., "node-1.us-east-1.example.com").
|
|
168
|
+
* The dot (`.`) is the only addition over the namespace/deployment regex.
|
|
169
|
+
* Intentionally rejects shell metacharacters, slashes, newlines, and empty
|
|
170
|
+
* strings as a single Zod check.
|
|
171
|
+
*/
|
|
172
|
+
const k8sNodeNameSchema = z.string().min(1).regex(/^[a-z0-9.-]+$/);
|
|
173
|
+
/**
|
|
174
|
+
* Bounded grace period in seconds. Minimum is 1 — gracePeriodSeconds=0 is
|
|
175
|
+
* equivalent to --force (immediate kill) and is explicitly forbidden by the
|
|
176
|
+
* plan's Shared Constraint #3. Maximum is 3600 (one hour).
|
|
177
|
+
*/
|
|
178
|
+
const gracePeriodSecondsSchema = z.number().int().min(1).max(3600);
|
|
179
|
+
/**
|
|
180
|
+
* Bounded timeout in seconds. Minimum is 1. Maximum is 3600 (one hour).
|
|
181
|
+
*/
|
|
182
|
+
const timeoutSecondsSchema = z.number().int().min(1).max(3600);
|
|
183
|
+
/**
|
|
184
|
+
* Raw Zod shape for the sre__drain tool.
|
|
185
|
+
*
|
|
186
|
+
* `--namespace` is intentionally absent: `kubectl drain` targets a node, which
|
|
187
|
+
* is a cluster-scoped resource. Do not add a namespace field — its absence is
|
|
188
|
+
* correct per the plan's Phase 5 note.
|
|
189
|
+
*
|
|
190
|
+
* `--ignore-daemonsets` is hard-coded into argv by the builder; it is NOT a
|
|
191
|
+
* user-controllable parameter. `--force` and `--delete-emptydir-data` are
|
|
192
|
+
* structurally unreachable through this schema.
|
|
193
|
+
*/
|
|
194
|
+
const sreDrainShape = {
|
|
195
|
+
node: k8sNodeNameSchema.describe("Node name to drain (RFC 1123 / FQDN form: lowercase alphanumeric, hyphens, and dots only)."),
|
|
196
|
+
gracePeriodSeconds: gracePeriodSecondsSchema.optional().describe("Grace period for evicting pods in seconds (integer, 1–3600). " +
|
|
197
|
+
"Omit to use the pod's default. " +
|
|
198
|
+
"0 is explicitly forbidden (equivalent to --force)."),
|
|
199
|
+
timeoutSeconds: timeoutSecondsSchema.optional().describe("Timeout for the drain operation in seconds (integer, 1–3600). " +
|
|
200
|
+
"Omit to use kubectl's default."),
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Strict Zod object schema for sre__drain parameters.
|
|
204
|
+
*
|
|
205
|
+
* Exported so adversarial-input tests (sre-tools.test.ts) can call
|
|
206
|
+
* `.safeParse()` directly. `.strict()` ensures unknown keys are rejected —
|
|
207
|
+
* any attempt to pass extra fields (e.g., a `force` bypass) is caught at
|
|
208
|
+
* the schema level.
|
|
209
|
+
*/
|
|
210
|
+
export const sreDrainSchema = z.object(sreDrainShape).strict();
|
|
211
|
+
/**
|
|
212
|
+
* Build the kubectl argv array for a drain operation.
|
|
213
|
+
*
|
|
214
|
+
* Exported for unit-testing the argv shape independently of the MCP server.
|
|
215
|
+
* The argv is a plain array literal — no string interpolation, no concat
|
|
216
|
+
* (drain follows the same plain-array-literal pattern as phases 2 and 4).
|
|
217
|
+
*
|
|
218
|
+
* INVARIANTS (enforced by construction):
|
|
219
|
+
* - `--ignore-daemonsets` is always present (hard-coded, not user-controlled).
|
|
220
|
+
* - `--force` is never present (no schema field; helper also rejects it).
|
|
221
|
+
* - `--delete-emptydir-data` is never present (no schema field).
|
|
222
|
+
* - `--grace-period=0` is never present (gracePeriodSeconds min is 1 via Zod).
|
|
223
|
+
*
|
|
224
|
+
* @param node - Validated node name.
|
|
225
|
+
* @param gracePeriodSeconds - Optional validated grace period (seconds, >= 1).
|
|
226
|
+
* @param timeoutSeconds - Optional validated timeout (seconds, >= 1).
|
|
227
|
+
*/
|
|
228
|
+
export function buildDrainArgv(node, gracePeriodSeconds, timeoutSeconds) {
|
|
229
|
+
const argv = ["drain", node, "--ignore-daemonsets"];
|
|
230
|
+
if (gracePeriodSeconds !== undefined) {
|
|
231
|
+
argv.push("--grace-period", String(gracePeriodSeconds));
|
|
232
|
+
}
|
|
233
|
+
if (timeoutSeconds !== undefined) {
|
|
234
|
+
argv.push("--timeout", `${timeoutSeconds}s`);
|
|
235
|
+
}
|
|
236
|
+
return argv;
|
|
237
|
+
}
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
/**
|
|
240
|
+
* Register all SRE operation tools on the MCP server.
|
|
241
|
+
*
|
|
242
|
+
* The `client` and `fieldCache` parameters are accepted for API consistency
|
|
243
|
+
* with other `register*Tools` functions. They are unused in Phase 1 (scaffold
|
|
244
|
+
* only); Phases 2-5 may use `client` for issue context lookups if needed.
|
|
245
|
+
*
|
|
246
|
+
* @param server - The MCP server instance to register tools on.
|
|
247
|
+
* @param client - GitHub client (reserved for future phases).
|
|
248
|
+
* @param fieldCache - Field option cache (reserved for future phases).
|
|
249
|
+
*/
|
|
250
|
+
export function registerSreTools(server,
|
|
251
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
252
|
+
_client,
|
|
253
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
254
|
+
_fieldCache) {
|
|
255
|
+
// -------------------------------------------------------------------------
|
|
256
|
+
// ralph_hero__sre__scale (Phase 2 / GH-1288)
|
|
257
|
+
// -------------------------------------------------------------------------
|
|
258
|
+
server.tool("ralph_hero__sre__scale", "Scale a Kubernetes deployment to a specified replica count. " +
|
|
259
|
+
"Typed parameters only — no shell, no flag pass-through. " +
|
|
260
|
+
`Replica ceiling: ${REPLICA_CEILING}.`, sreScaleShape, async ({ namespace, deployment, replicas }) => {
|
|
261
|
+
const argv = buildScaleArgv(namespace, deployment, replicas);
|
|
262
|
+
try {
|
|
263
|
+
const result = await runKubectl(argv);
|
|
264
|
+
if (result.exitCode !== 0) {
|
|
265
|
+
return toolError(`kubectl scale failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
|
|
266
|
+
}
|
|
267
|
+
return toolSuccess(result);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
271
|
+
return toolError(`kubectl scale failed: ${message}`);
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
// -------------------------------------------------------------------------
|
|
275
|
+
// ralph_hero__sre__rollout_restart (Phase 3 / GH-1289)
|
|
276
|
+
// -------------------------------------------------------------------------
|
|
277
|
+
server.tool("ralph_hero__sre__rollout_restart", "Trigger a rolling restart of a Kubernetes deployment. " +
|
|
278
|
+
"Typed parameters only — no shell, no flag pass-through. " +
|
|
279
|
+
"Equivalent to: kubectl rollout restart deployment/<name> -n <namespace>.", sreRolloutRestartShape, async ({ namespace, deployment }) => {
|
|
280
|
+
const argv = buildRolloutRestartArgv(namespace, deployment);
|
|
281
|
+
try {
|
|
282
|
+
const result = await runKubectl(argv);
|
|
283
|
+
if (result.exitCode !== 0) {
|
|
284
|
+
return toolError(`kubectl rollout restart failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
|
|
285
|
+
}
|
|
286
|
+
return toolSuccess(result);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
290
|
+
return toolError(`kubectl rollout restart failed: ${message}`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
// -------------------------------------------------------------------------
|
|
294
|
+
// ralph_hero__sre__delete_pod (Phase 4 / GH-1290)
|
|
295
|
+
// -------------------------------------------------------------------------
|
|
296
|
+
server.tool("ralph_hero__sre__delete_pod", "Delete a single named pod in a Kubernetes namespace. " +
|
|
297
|
+
"Single pod by name only — no label selector, no --force, no --grace-period=0. " +
|
|
298
|
+
"Typed parameters only — no shell, no flag pass-through.", sreDeletePodShape, async ({ namespace, pod }) => {
|
|
299
|
+
const argv = buildDeletePodArgv(namespace, pod);
|
|
300
|
+
try {
|
|
301
|
+
const result = await runKubectl(argv);
|
|
302
|
+
if (result.exitCode !== 0) {
|
|
303
|
+
return toolError(`kubectl delete pod failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
|
|
304
|
+
}
|
|
305
|
+
return toolSuccess(result);
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
309
|
+
return toolError(`kubectl delete pod failed: ${message}`);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
// -------------------------------------------------------------------------
|
|
313
|
+
// ralph_hero__sre__drain (Phase 5 / GH-1291)
|
|
314
|
+
// -------------------------------------------------------------------------
|
|
315
|
+
server.tool("ralph_hero__sre__drain", "Drain a Kubernetes node by evicting all non-daemonset pods. " +
|
|
316
|
+
"--ignore-daemonsets is hard-coded on (always present). " +
|
|
317
|
+
"--force, --delete-emptydir-data, and --grace-period=0 are structurally unreachable. " +
|
|
318
|
+
"Cluster-scoped operation — no --namespace flag. " +
|
|
319
|
+
"Typed parameters only — no shell, no flag pass-through.", sreDrainShape, async ({ node, gracePeriodSeconds, timeoutSeconds }) => {
|
|
320
|
+
const argv = buildDrainArgv(node, gracePeriodSeconds, timeoutSeconds);
|
|
321
|
+
try {
|
|
322
|
+
const result = await runKubectl(argv);
|
|
323
|
+
if (result.exitCode !== 0) {
|
|
324
|
+
return toolError(`kubectl drain failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
|
|
325
|
+
}
|
|
326
|
+
return toolSuccess(result);
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
330
|
+
return toolError(`kubectl drain failed: ${message}`);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=sre-tools.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-hero-mcp-server",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.171",
|
|
4
4
|
"description": "MCP server for GitHub Projects V2 - Ralph workflow automation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"@octokit/graphql": "^9.0.3",
|
|
22
22
|
"@octokit/plugin-paginate-graphql": "^6.0.0",
|
|
23
23
|
"@opentelemetry/api": "^1.9.0",
|
|
24
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
25
|
-
"@opentelemetry/resources": "^
|
|
26
|
-
"@opentelemetry/sdk-node": "^0.
|
|
27
|
-
"@opentelemetry/sdk-trace-base": "^
|
|
28
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
24
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
|
|
25
|
+
"@opentelemetry/resources": "^2.7.1",
|
|
26
|
+
"@opentelemetry/sdk-node": "^0.218.0",
|
|
27
|
+
"@opentelemetry/sdk-trace-base": "^2.7.1",
|
|
28
|
+
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
29
29
|
"yaml": "^2.7.0",
|
|
30
30
|
"zod": "^3.25.0"
|
|
31
31
|
},
|