@recapt/mcp 0.0.3-beta → 0.0.5-beta

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.
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Remediation tools for self-healing workflow.
3
+ *
4
+ * Provides fix proposal, deployment confirmation, evaluation, and history tracking.
5
+ */
6
+ import { z } from "zod";
7
+ import { apiGet, apiPost, apiPatch, isApiConfigured } from "../api/client.js";
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ export function registerRemediationTools(server) {
10
+ server.registerTool("propose_fix", {
11
+ description: "Propose a fix for an issue. Creates a remediation record with baseline metrics for later evaluation. Include detailed diagnosis and code suggestions when possible.",
12
+ inputSchema: z.object({
13
+ issue_id: z.string().describe("The ID of the issue to fix"),
14
+ diagnosis: z
15
+ .string()
16
+ .describe("Detailed analysis of the root cause of the issue"),
17
+ proposed_fix: z
18
+ .string()
19
+ .describe("Description of the proposed fix and how it addresses the root cause"),
20
+ code_snippet: z
21
+ .string()
22
+ .optional()
23
+ .describe("Suggested code changes (if applicable)"),
24
+ affected_files: z
25
+ .array(z.string())
26
+ .optional()
27
+ .describe("List of files that need to be modified"),
28
+ confidence: z
29
+ .number()
30
+ .min(0)
31
+ .max(1)
32
+ .describe("Confidence level in the fix (0-1). Use <0.7 when uncertain."),
33
+ }),
34
+ }, async ({ issue_id, diagnosis, proposed_fix, code_snippet, affected_files, confidence, }) => {
35
+ if (!isApiConfigured()) {
36
+ return {
37
+ content: [
38
+ {
39
+ type: "text",
40
+ text: JSON.stringify({ error: "API not configured" }),
41
+ },
42
+ ],
43
+ isError: true,
44
+ };
45
+ }
46
+ const { data, error } = await apiPost("/remediations", {
47
+ issue_id,
48
+ diagnosis,
49
+ proposed_fix,
50
+ code_snippet,
51
+ affected_files,
52
+ confidence,
53
+ });
54
+ if (error) {
55
+ return {
56
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
57
+ isError: true,
58
+ };
59
+ }
60
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
61
+ });
62
+ server.registerTool("list_pending_fixes", {
63
+ description: "List all proposed fixes awaiting deployment. Use to show the user what fixes are ready to be deployed.",
64
+ inputSchema: z.object({}),
65
+ }, async () => {
66
+ if (!isApiConfigured()) {
67
+ return {
68
+ content: [
69
+ {
70
+ type: "text",
71
+ text: JSON.stringify({ error: "API not configured" }),
72
+ },
73
+ ],
74
+ isError: true,
75
+ };
76
+ }
77
+ const { data, error } = await apiGet("/remediations/pending");
78
+ if (error) {
79
+ return {
80
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
81
+ isError: true,
82
+ };
83
+ }
84
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
85
+ });
86
+ server.registerTool("list_remediations", {
87
+ description: "List remediation records with optional filters. Use to review past and current fix attempts.",
88
+ inputSchema: z.object({
89
+ status: z
90
+ .enum([
91
+ "proposed",
92
+ "deployed",
93
+ "evaluating",
94
+ "succeeded",
95
+ "failed",
96
+ "reverted",
97
+ ])
98
+ .optional()
99
+ .describe("Filter by remediation status"),
100
+ issue_id: z.string().optional().describe("Filter by issue ID"),
101
+ page_path: z.string().optional().describe("Filter by page path"),
102
+ limit: z
103
+ .number()
104
+ .optional()
105
+ .default(20)
106
+ .describe("Maximum number of results (default: 20)"),
107
+ }),
108
+ }, async ({ status, issue_id, page_path, limit, }) => {
109
+ if (!isApiConfigured()) {
110
+ return {
111
+ content: [
112
+ {
113
+ type: "text",
114
+ text: JSON.stringify({ error: "API not configured" }),
115
+ },
116
+ ],
117
+ isError: true,
118
+ };
119
+ }
120
+ const { data, error } = await apiGet("/remediations", {
121
+ status,
122
+ issue_id,
123
+ page_path,
124
+ limit: limit ?? 20,
125
+ });
126
+ if (error) {
127
+ return {
128
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
129
+ isError: true,
130
+ };
131
+ }
132
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
133
+ });
134
+ server.registerTool("confirm_deployment", {
135
+ description: "Confirm that a proposed fix has been deployed to production. This starts the evaluation timer. Call this when the user confirms they have deployed the fix.",
136
+ inputSchema: z.object({
137
+ remediation_id: z
138
+ .string()
139
+ .describe("The ID of the remediation to mark as deployed"),
140
+ }),
141
+ }, async ({ remediation_id }) => {
142
+ if (!isApiConfigured()) {
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: JSON.stringify({ error: "API not configured" }),
148
+ },
149
+ ],
150
+ isError: true,
151
+ };
152
+ }
153
+ const { data, error } = await apiPatch(`/remediations/${remediation_id}/deploy`, {});
154
+ if (error) {
155
+ return {
156
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
157
+ isError: true,
158
+ };
159
+ }
160
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
161
+ });
162
+ server.registerTool("evaluate_fix", {
163
+ description: "Evaluate if a deployed fix improved metrics. Compares post-deployment metrics to baseline. Returns success/partial/failed outcome with detailed verdict. Wait at least 24 hours after deployment for reliable results.",
164
+ inputSchema: z.object({
165
+ remediation_id: z
166
+ .string()
167
+ .describe("The ID of the remediation to evaluate"),
168
+ min_hours: z
169
+ .number()
170
+ .optional()
171
+ .default(24)
172
+ .describe("Minimum hours since deployment required (default: 24)"),
173
+ }),
174
+ }, async ({ remediation_id, min_hours, }) => {
175
+ if (!isApiConfigured()) {
176
+ return {
177
+ content: [
178
+ {
179
+ type: "text",
180
+ text: JSON.stringify({ error: "API not configured" }),
181
+ },
182
+ ],
183
+ isError: true,
184
+ };
185
+ }
186
+ const { data, error } = await apiPost(`/remediations/${remediation_id}/evaluate`, { min_hours: min_hours ?? 24 });
187
+ if (error) {
188
+ return {
189
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
190
+ isError: true,
191
+ };
192
+ }
193
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
194
+ });
195
+ server.registerTool("get_fix_history", {
196
+ description: "Get the full remediation history for an issue. Shows all fix attempts, their outcomes, and current status. Use before proposing a new fix to learn from past attempts.",
197
+ inputSchema: z.object({
198
+ issue_id: z
199
+ .string()
200
+ .describe("The ID of the issue to get fix history for"),
201
+ }),
202
+ }, async ({ issue_id }) => {
203
+ if (!isApiConfigured()) {
204
+ return {
205
+ content: [
206
+ {
207
+ type: "text",
208
+ text: JSON.stringify({ error: "API not configured" }),
209
+ },
210
+ ],
211
+ isError: true,
212
+ };
213
+ }
214
+ const { data, error } = await apiGet(`/remediations/history/${issue_id}`);
215
+ if (error) {
216
+ return {
217
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
218
+ isError: true,
219
+ };
220
+ }
221
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
222
+ });
223
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Triage tools for self-healing workflow.
3
+ *
4
+ * Provides issue dismissal, marking intended behavior, and issue history.
5
+ */
6
+ export declare function registerTriageTools(server: any): void;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Triage tools for self-healing workflow.
3
+ *
4
+ * Provides issue dismissal, marking intended behavior, and issue history.
5
+ */
6
+ import { z } from "zod";
7
+ import { apiGet, apiPost, isApiConfigured } from "../api/client.js";
8
+ const DISMISS_REASONS = [
9
+ "stale",
10
+ "intended",
11
+ "duplicate",
12
+ "false_positive",
13
+ ];
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ export function registerTriageTools(server) {
16
+ server.registerTool("dismiss_issue", {
17
+ description: "Dismiss an issue as a false positive. Creates site knowledge to prevent re-flagging. Use when an issue is stale, intended behavior, a duplicate, or a false positive. Always confirm with user before dismissing.",
18
+ inputSchema: z.object({
19
+ issue_id: z.string().describe("The ID of the issue to dismiss"),
20
+ reason: z
21
+ .enum(DISMISS_REASONS)
22
+ .describe("Reason for dismissal: stale, intended, duplicate, or false_positive"),
23
+ explanation: z
24
+ .string()
25
+ .describe("Detailed explanation of why this issue is being dismissed"),
26
+ create_knowledge: z
27
+ .boolean()
28
+ .optional()
29
+ .default(true)
30
+ .describe("Whether to create site knowledge entry (default: true)"),
31
+ }),
32
+ }, async ({ issue_id, reason, explanation, create_knowledge, }) => {
33
+ if (!isApiConfigured()) {
34
+ return {
35
+ content: [
36
+ {
37
+ type: "text",
38
+ text: JSON.stringify({ error: "API not configured" }),
39
+ },
40
+ ],
41
+ isError: true,
42
+ };
43
+ }
44
+ const { data, error } = await apiPost(`/issues/${issue_id}/dismiss`, {
45
+ reason,
46
+ explanation,
47
+ create_knowledge: create_knowledge ?? true,
48
+ });
49
+ if (error) {
50
+ return {
51
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
52
+ isError: true,
53
+ };
54
+ }
55
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
56
+ });
57
+ server.registerTool("mark_intended_behavior", {
58
+ description: "Mark an issue as intended behavior. This is a shortcut for dismissing with reason 'intended' and always creates site knowledge. Use when the detected behavior is by design (e.g., disabled button clicks are expected).",
59
+ inputSchema: z.object({
60
+ issue_id: z
61
+ .string()
62
+ .describe("The ID of the issue to mark as intended"),
63
+ explanation: z
64
+ .string()
65
+ .describe("Explanation of why this behavior is intended"),
66
+ }),
67
+ }, async ({ issue_id, explanation, }) => {
68
+ if (!isApiConfigured()) {
69
+ return {
70
+ content: [
71
+ {
72
+ type: "text",
73
+ text: JSON.stringify({ error: "API not configured" }),
74
+ },
75
+ ],
76
+ isError: true,
77
+ };
78
+ }
79
+ const { data, error } = await apiPost(`/issues/${issue_id}/mark-intended`, { explanation });
80
+ if (error) {
81
+ return {
82
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
83
+ isError: true,
84
+ };
85
+ }
86
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
87
+ });
88
+ server.registerTool("get_issue_history", {
89
+ description: "Get the full history of an issue including status changes, remediation attempts, and outcomes. Use to understand past fix attempts before proposing new ones.",
90
+ inputSchema: z.object({
91
+ issue_id: z.string().describe("The ID of the issue to get history for"),
92
+ }),
93
+ }, async ({ issue_id }) => {
94
+ if (!isApiConfigured()) {
95
+ return {
96
+ content: [
97
+ {
98
+ type: "text",
99
+ text: JSON.stringify({ error: "API not configured" }),
100
+ },
101
+ ],
102
+ isError: true,
103
+ };
104
+ }
105
+ const { data, error } = await apiGet(`/issues/${issue_id}/history`);
106
+ if (error) {
107
+ return {
108
+ content: [{ type: "text", text: JSON.stringify({ error }) }],
109
+ isError: true,
110
+ };
111
+ }
112
+ return { content: [{ type: "text", text: JSON.stringify(data) }] };
113
+ });
114
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recapt/mcp",
3
- "version": "0.0.3-beta",
3
+ "version": "0.0.5-beta",
4
4
  "description": "MCP exposing recapt behavioral intelligence to AI coding agents",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,8 @@
11
11
  "dev": "tsx --watch src/index.ts",
12
12
  "start": "node dist/index.js",
13
13
  "prettify": "prettier --write .",
14
- "type-check": "tsc --noEmit"
14
+ "type-check": "tsc --noEmit",
15
+ "generate-catalog": "tsx scripts/generateCatalog.ts"
15
16
  },
16
17
  "bin": {
17
18
  "recapt-mcp": "./dist/index.js"
@@ -26,6 +27,9 @@
26
27
  "@modelcontextprotocol/sdk": "^1.12.0",
27
28
  "zod": "^3.24.0"
28
29
  },
30
+ "optionalDependencies": {
31
+ "@xenova/transformers": "^2.17.0"
32
+ },
29
33
  "peerDependencies": {
30
34
  "@types/node": "^24.3.1",
31
35
  "dotenv": "^17.2.2",