ralph-hero-mcp-server 2.4.11 → 2.4.13
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/routing-types.js +163 -0
- package/dist/tools/routing-tools.js +84 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { registerDashboardTools } from "./tools/dashboard-tools.js";
|
|
|
19
19
|
import { registerBatchTools } from "./tools/batch-tools.js";
|
|
20
20
|
import { registerProjectManagementTools } from "./tools/project-management-tools.js";
|
|
21
21
|
import { registerHygieneTools } from "./tools/hygiene-tools.js";
|
|
22
|
+
import { registerRoutingTools } from "./tools/routing-tools.js";
|
|
22
23
|
/**
|
|
23
24
|
* Initialize the GitHub client from environment variables.
|
|
24
25
|
*/
|
|
@@ -248,6 +249,8 @@ async function main() {
|
|
|
248
249
|
registerProjectManagementTools(server, client, fieldCache);
|
|
249
250
|
// Hygiene reporting tools
|
|
250
251
|
registerHygieneTools(server, client, fieldCache);
|
|
252
|
+
// Routing config management tools
|
|
253
|
+
registerRoutingTools(server, client, fieldCache);
|
|
251
254
|
// Connect via stdio transport
|
|
252
255
|
const transport = new StdioServerTransport();
|
|
253
256
|
await server.connect(transport);
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routing rules config schema and TypeScript types.
|
|
3
|
+
*
|
|
4
|
+
* Defines the structure for `.ralph-routing.yml` config files used by
|
|
5
|
+
* the issue routing engine. Zod schemas provide runtime validation;
|
|
6
|
+
* TypeScript types are derived via z.infer<> for compile-time safety.
|
|
7
|
+
*
|
|
8
|
+
* Consumers:
|
|
9
|
+
* - #167 matching engine: imports MatchCriteria, RoutingRule types
|
|
10
|
+
* - #168 config loader: imports RoutingConfigSchema for YAML validation
|
|
11
|
+
* - #178 CRUD tool: imports schemas + types for inline validation
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Match Criteria
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* Defines conditions for matching an issue to a routing rule.
|
|
19
|
+
*
|
|
20
|
+
* At least one criterion must be specified. Multiple criteria are AND'd:
|
|
21
|
+
* all specified conditions must match for the rule to apply.
|
|
22
|
+
*
|
|
23
|
+
* Example YAML:
|
|
24
|
+
* match:
|
|
25
|
+
* repo: "cdubiel08/ralph-hero"
|
|
26
|
+
* labels:
|
|
27
|
+
* any: ["enhancement", "bug"]
|
|
28
|
+
*/
|
|
29
|
+
export const MatchCriteriaSchema = z
|
|
30
|
+
.object({
|
|
31
|
+
repo: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Repository glob pattern (e.g., 'my-org/*', 'owner/repo')"),
|
|
35
|
+
labels: z
|
|
36
|
+
.object({
|
|
37
|
+
any: z
|
|
38
|
+
.array(z.string())
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("Match if issue has ANY of these labels"),
|
|
41
|
+
all: z
|
|
42
|
+
.array(z.string())
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Match if issue has ALL of these labels"),
|
|
45
|
+
})
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Label matching criteria"),
|
|
48
|
+
issueType: z
|
|
49
|
+
.enum(["issue", "pull_request", "draft_issue"])
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Match by item type"),
|
|
52
|
+
negate: z
|
|
53
|
+
.boolean()
|
|
54
|
+
.optional()
|
|
55
|
+
.default(false)
|
|
56
|
+
.describe("Invert match result — true means 'NOT matching'"),
|
|
57
|
+
})
|
|
58
|
+
.refine((d) => d.repo || d.labels || d.issueType, {
|
|
59
|
+
message: "At least one match criterion must be specified (repo, labels, or issueType)",
|
|
60
|
+
});
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Routing Action
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/**
|
|
65
|
+
* Defines what happens when a rule matches.
|
|
66
|
+
*
|
|
67
|
+
* At least one action must be specified. Multiple actions execute together.
|
|
68
|
+
*
|
|
69
|
+
* Example YAML:
|
|
70
|
+
* action:
|
|
71
|
+
* projectNumber: 3
|
|
72
|
+
* workflowState: "Backlog"
|
|
73
|
+
* labels: ["triaged"]
|
|
74
|
+
*/
|
|
75
|
+
export const RoutingActionSchema = z
|
|
76
|
+
.object({
|
|
77
|
+
projectNumber: z
|
|
78
|
+
.number()
|
|
79
|
+
.optional()
|
|
80
|
+
.describe("Add issue to this project (shorthand for single project)"),
|
|
81
|
+
projectNumbers: z
|
|
82
|
+
.array(z.number())
|
|
83
|
+
.optional()
|
|
84
|
+
.describe("Add issue to multiple projects"),
|
|
85
|
+
workflowState: z
|
|
86
|
+
.string()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Set workflow state after routing"),
|
|
89
|
+
labels: z
|
|
90
|
+
.array(z.string())
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("Add these labels to the issue"),
|
|
93
|
+
})
|
|
94
|
+
.refine((d) => d.projectNumber || d.projectNumbers || d.workflowState || d.labels, {
|
|
95
|
+
message: "At least one action must be specified (projectNumber, projectNumbers, workflowState, or labels)",
|
|
96
|
+
});
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Routing Rule
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
/**
|
|
101
|
+
* A single routing rule: match criteria + action.
|
|
102
|
+
*
|
|
103
|
+
* Example YAML:
|
|
104
|
+
* - name: "Route MCP server issues"
|
|
105
|
+
* match:
|
|
106
|
+
* repo: "cdubiel08/ralph-hero"
|
|
107
|
+
* action:
|
|
108
|
+
* projectNumber: 3
|
|
109
|
+
*/
|
|
110
|
+
export const RoutingRuleSchema = z.object({
|
|
111
|
+
name: z
|
|
112
|
+
.string()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe("Human-readable rule name for debugging and audit trail"),
|
|
115
|
+
match: MatchCriteriaSchema,
|
|
116
|
+
action: RoutingActionSchema,
|
|
117
|
+
enabled: z
|
|
118
|
+
.boolean()
|
|
119
|
+
.optional()
|
|
120
|
+
.default(true)
|
|
121
|
+
.describe("Toggle rule on/off without removing it"),
|
|
122
|
+
});
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Routing Config
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
/**
|
|
127
|
+
* Top-level routing configuration.
|
|
128
|
+
*
|
|
129
|
+
* Example YAML:
|
|
130
|
+
* version: 1
|
|
131
|
+
* stopOnFirstMatch: true
|
|
132
|
+
* rules:
|
|
133
|
+
* - name: "Route bugs"
|
|
134
|
+
* match:
|
|
135
|
+
* labels:
|
|
136
|
+
* any: ["bug"]
|
|
137
|
+
* action:
|
|
138
|
+
* projectNumber: 3
|
|
139
|
+
*/
|
|
140
|
+
export const RoutingConfigSchema = z.object({
|
|
141
|
+
version: z
|
|
142
|
+
.literal(1)
|
|
143
|
+
.describe("Schema version for forward compatibility"),
|
|
144
|
+
stopOnFirstMatch: z
|
|
145
|
+
.boolean()
|
|
146
|
+
.optional()
|
|
147
|
+
.default(true)
|
|
148
|
+
.describe("Stop evaluating rules after first match (default: true)"),
|
|
149
|
+
rules: z
|
|
150
|
+
.array(RoutingRuleSchema)
|
|
151
|
+
.describe("Ordered list of routing rules — evaluated top to bottom"),
|
|
152
|
+
});
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Convenience Functions
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
/**
|
|
157
|
+
* Validate and parse a routing config object (e.g., from YAML parse output).
|
|
158
|
+
* Throws ZodError with detailed messages on validation failure.
|
|
159
|
+
*/
|
|
160
|
+
export function validateRoutingConfig(data) {
|
|
161
|
+
return RoutingConfigSchema.parse(data);
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=routing-types.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool for managing routing rules in .ralph-routing.yml.
|
|
3
|
+
*
|
|
4
|
+
* Provides a single `ralph_hero__configure_routing` tool with four
|
|
5
|
+
* CRUD operations: list_rules, add_rule, update_rule, remove_rule.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "node:fs/promises";
|
|
8
|
+
import { parse, stringify } from "yaml";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { toolSuccess, toolError } from "../types.js";
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Register routing tools
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
export function registerRoutingTools(server, _client, _fieldCache) {
|
|
15
|
+
server.tool("ralph_hero__configure_routing", "Manage routing rules in .ralph-routing.yml. CRUD operations: list, add, update, remove rules. Config path: configPath arg > RALPH_ROUTING_CONFIG env var > .ralph-routing.yml. Returns: updated rule list and configPath.", {
|
|
16
|
+
operation: z
|
|
17
|
+
.enum(["list_rules", "add_rule", "update_rule", "remove_rule"])
|
|
18
|
+
.describe("CRUD operation to perform"),
|
|
19
|
+
configPath: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Path to routing config file. Defaults to RALPH_ROUTING_CONFIG env var or .ralph-routing.yml"),
|
|
23
|
+
rule: z
|
|
24
|
+
.object({
|
|
25
|
+
match: z.object({
|
|
26
|
+
labels: z.array(z.string()).optional(),
|
|
27
|
+
repo: z.string().optional(),
|
|
28
|
+
}),
|
|
29
|
+
action: z.object({
|
|
30
|
+
workflowState: z.string().optional(),
|
|
31
|
+
projectNumber: z.number().optional(),
|
|
32
|
+
}),
|
|
33
|
+
})
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Rule definition (required for add_rule, update_rule)"),
|
|
36
|
+
ruleIndex: z
|
|
37
|
+
.number()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Zero-based rule index (required for update_rule, remove_rule)"),
|
|
40
|
+
}, async (args) => {
|
|
41
|
+
const configPath = args.configPath ??
|
|
42
|
+
process.env.RALPH_ROUTING_CONFIG ??
|
|
43
|
+
".ralph-routing.yml";
|
|
44
|
+
try {
|
|
45
|
+
const raw = await fs.readFile(configPath, "utf-8").catch(() => "");
|
|
46
|
+
const config = raw
|
|
47
|
+
? parse(raw)
|
|
48
|
+
: { rules: [] };
|
|
49
|
+
if (!config.rules)
|
|
50
|
+
config.rules = [];
|
|
51
|
+
switch (args.operation) {
|
|
52
|
+
case "list_rules":
|
|
53
|
+
return toolSuccess({ rules: config.rules, configPath });
|
|
54
|
+
case "add_rule":
|
|
55
|
+
if (!args.rule)
|
|
56
|
+
return toolError("rule is required for add_rule operation");
|
|
57
|
+
config.rules = [...config.rules, args.rule];
|
|
58
|
+
await fs.writeFile(configPath, stringify(config, { lineWidth: 0 }));
|
|
59
|
+
return toolSuccess({ rules: config.rules, configPath });
|
|
60
|
+
case "update_rule":
|
|
61
|
+
if (args.ruleIndex == null || !args.rule)
|
|
62
|
+
return toolError("ruleIndex and rule are required for update_rule operation");
|
|
63
|
+
if (args.ruleIndex < 0 || args.ruleIndex >= config.rules.length)
|
|
64
|
+
return toolError(`Rule index ${args.ruleIndex} out of range (0-${config.rules.length - 1})`);
|
|
65
|
+
config.rules[args.ruleIndex] = args.rule;
|
|
66
|
+
await fs.writeFile(configPath, stringify(config, { lineWidth: 0 }));
|
|
67
|
+
return toolSuccess({ rules: config.rules, configPath });
|
|
68
|
+
case "remove_rule":
|
|
69
|
+
if (args.ruleIndex == null)
|
|
70
|
+
return toolError("ruleIndex is required for remove_rule operation");
|
|
71
|
+
if (args.ruleIndex < 0 || args.ruleIndex >= config.rules.length)
|
|
72
|
+
return toolError(`Rule index ${args.ruleIndex} out of range (0-${config.rules.length - 1})`);
|
|
73
|
+
config.rules = config.rules.filter((_, i) => i !== args.ruleIndex);
|
|
74
|
+
await fs.writeFile(configPath, stringify(config, { lineWidth: 0 }));
|
|
75
|
+
return toolSuccess({ rules: config.rules, configPath });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
+
return toolError(`Failed to configure routing: ${message}`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=routing-tools.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-hero-mcp-server",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.13",
|
|
4
4
|
"description": "MCP server for GitHub Projects V2 - Ralph workflow automation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
21
21
|
"@octokit/graphql": "^9.0.3",
|
|
22
22
|
"@octokit/plugin-paginate-graphql": "^6.0.0",
|
|
23
|
+
"yaml": "^2.7.0",
|
|
23
24
|
"zod": "^3.25.0"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|