@myna-sh/mcp 0.13.0 → 0.15.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/README.md +1 -1
- package/dist/main.d.ts +12 -0
- package/dist/main.js +411 -2
- package/dist/main.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @myna-sh/mcp
|
|
2
2
|
|
|
3
|
-
Model Context Protocol server for
|
|
3
|
+
Model Context Protocol server for operating [Myna](https://myna.sh) content and feedback from coding agents and MCP clients.
|
|
4
4
|
|
|
5
5
|
> **Connecting Claude, ChatGPT, or another remote agent?** You do not need this package. Myna hosts the same server at `https://api.myna.sh/mcp` — add it as a custom connector and authorize it with OAuth. See [connecting a remote agent](https://docs.myna.sh/mcp/hosted). This package is for local coding agents that run the server over stdio.
|
|
6
6
|
|
package/dist/main.d.ts
CHANGED
|
@@ -89,6 +89,18 @@ interface ToolOptions {
|
|
|
89
89
|
* remote client must never be able to read the server's filesystem.
|
|
90
90
|
*/
|
|
91
91
|
allowPathUploads: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Myna products the target project runs.
|
|
94
|
+
*
|
|
95
|
+
* Tools for a product are registered only when the project has it enabled.
|
|
96
|
+
* Omitting this registers the Content tools and nothing else — the
|
|
97
|
+
* conservative default, and the one that matters: a customer's saved agent
|
|
98
|
+
* configuration must not change shape because Myna shipped a second product.
|
|
99
|
+
* There are already 45+ tools on this server, and every one added degrades
|
|
100
|
+
* selection for all of them, so a project that does not run Feedback should
|
|
101
|
+
* never be shown Feedback's.
|
|
102
|
+
*/
|
|
103
|
+
products?: readonly string[];
|
|
92
104
|
}
|
|
93
105
|
|
|
94
106
|
declare const SERVER_NAME = "myna";
|
package/dist/main.js
CHANGED
|
@@ -536,6 +536,147 @@ var ManagementClient = class {
|
|
|
536
536
|
body
|
|
537
537
|
)
|
|
538
538
|
};
|
|
539
|
+
// --- Feedback: boards -----------------------------------------------------
|
|
540
|
+
/**
|
|
541
|
+
* Boards collect reports. A board's `formFields` is its intake form, declared
|
|
542
|
+
* with the same field DSL as a collection schema — so a QA board that needs a
|
|
543
|
+
* build number says so there, and gets the same validation as anything else
|
|
544
|
+
* in Myna.
|
|
545
|
+
*/
|
|
546
|
+
boards = {
|
|
547
|
+
list: (project, signal) => this.get(`/projects/${enc(project)}/boards`, void 0, signal),
|
|
548
|
+
get: (project, board, signal) => this.get(`/projects/${enc(project)}/boards/${enc(board)}`, void 0, signal),
|
|
549
|
+
create: (project, body) => this.mutate("POST", `/projects/${enc(project)}/boards`, body),
|
|
550
|
+
update: (project, board, body) => this.mutate("PATCH", `/projects/${enc(project)}/boards/${enc(board)}`, body),
|
|
551
|
+
delete: (project, board) => this.mutate("DELETE", `/projects/${enc(project)}/boards/${enc(board)}`)
|
|
552
|
+
};
|
|
553
|
+
// --- Feedback: reports ----------------------------------------------------
|
|
554
|
+
/**
|
|
555
|
+
* Bug reports, QA findings, feedback, and feature requests.
|
|
556
|
+
*
|
|
557
|
+
* `get` deliberately returns everything at once — body, board guidance,
|
|
558
|
+
* custom fields, developer-supplied context, the full timeline with resolved
|
|
559
|
+
* actors, the attachment manifest with short-lived URLs, and the links.
|
|
560
|
+
* Assembling a bug report from six calls is the problem this product exists
|
|
561
|
+
* to remove, so its own client does not make you do it.
|
|
562
|
+
*
|
|
563
|
+
* A report reference may be an id (`rep_…`), a number, or `#number`.
|
|
564
|
+
*/
|
|
565
|
+
reports = {
|
|
566
|
+
list: (project, opts = {}) => {
|
|
567
|
+
const { board, status, type, priority, assignee, labels, q, since, ...rest } = opts;
|
|
568
|
+
return this.page(`/projects/${enc(project)}/reports`, rest, {
|
|
569
|
+
board,
|
|
570
|
+
status: Array.isArray(status) ? status.join(",") : status,
|
|
571
|
+
type,
|
|
572
|
+
priority,
|
|
573
|
+
assignee,
|
|
574
|
+
labels: labels?.join(","),
|
|
575
|
+
q,
|
|
576
|
+
since
|
|
577
|
+
});
|
|
578
|
+
},
|
|
579
|
+
get: (project, report, signal) => this.get(
|
|
580
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}`,
|
|
581
|
+
void 0,
|
|
582
|
+
signal
|
|
583
|
+
),
|
|
584
|
+
create: (project, body) => this.mutate("POST", `/projects/${enc(project)}/reports`, body),
|
|
585
|
+
update: (project, report, body) => this.mutate(
|
|
586
|
+
"PATCH",
|
|
587
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}`,
|
|
588
|
+
body
|
|
589
|
+
),
|
|
590
|
+
comment: (project, report, body) => this.mutate(
|
|
591
|
+
"POST",
|
|
592
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/comments`,
|
|
593
|
+
body
|
|
594
|
+
),
|
|
595
|
+
resolve: (project, report, body = {}) => this.mutate(
|
|
596
|
+
"POST",
|
|
597
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/resolve`,
|
|
598
|
+
body
|
|
599
|
+
),
|
|
600
|
+
reopen: (project, report, body = {}) => this.mutate(
|
|
601
|
+
"POST",
|
|
602
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/reopen`,
|
|
603
|
+
body
|
|
604
|
+
),
|
|
605
|
+
/** Ask whoever reported it to confirm a fix. The human half of the loop. */
|
|
606
|
+
requestRetest: (project, report, body = {}) => this.mutate(
|
|
607
|
+
"POST",
|
|
608
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/retest`,
|
|
609
|
+
body
|
|
610
|
+
),
|
|
611
|
+
merge: (project, report, body) => this.mutate(
|
|
612
|
+
"POST",
|
|
613
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/merge`,
|
|
614
|
+
body
|
|
615
|
+
),
|
|
616
|
+
/**
|
|
617
|
+
* What has come in since a time or a release.
|
|
618
|
+
*
|
|
619
|
+
* The call to make before deciding what to work on: counts, groupings, and
|
|
620
|
+
* the reports themselves already ordered by priority then recency.
|
|
621
|
+
*/
|
|
622
|
+
digest: (project, opts = {}, signal) => this.get(
|
|
623
|
+
`/projects/${enc(project)}/reports/digest`,
|
|
624
|
+
{ since: opts.since, release: opts.release, limit: opts.limit },
|
|
625
|
+
signal
|
|
626
|
+
),
|
|
627
|
+
similar: (project, report, signal) => this.get(
|
|
628
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/similar`,
|
|
629
|
+
void 0,
|
|
630
|
+
signal
|
|
631
|
+
),
|
|
632
|
+
/** What publishing this report to a board would expose. Read before moving. */
|
|
633
|
+
exposure: (project, report, board, signal) => this.get(
|
|
634
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/exposure`,
|
|
635
|
+
{ board },
|
|
636
|
+
signal
|
|
637
|
+
),
|
|
638
|
+
/** Move between boards. A public destination needs `confirm: true`. */
|
|
639
|
+
move: (project, report, body) => this.mutate(
|
|
640
|
+
"POST",
|
|
641
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/move`,
|
|
642
|
+
body
|
|
643
|
+
),
|
|
644
|
+
link: (project, report, body) => this.mutate(
|
|
645
|
+
"POST",
|
|
646
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/links`,
|
|
647
|
+
body
|
|
648
|
+
),
|
|
649
|
+
/** One attachment, with a fresh short-lived URL. */
|
|
650
|
+
attachment: (project, report, attachment, signal) => this.get(
|
|
651
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/attachments/${enc(attachment)}`,
|
|
652
|
+
void 0,
|
|
653
|
+
signal
|
|
654
|
+
),
|
|
655
|
+
createUpload: (project, body) => this.mutate(
|
|
656
|
+
"POST",
|
|
657
|
+
`/projects/${enc(project)}/reports/attachments/uploads`,
|
|
658
|
+
body
|
|
659
|
+
),
|
|
660
|
+
attach: (project, report, uploadIds) => this.mutate(
|
|
661
|
+
"POST",
|
|
662
|
+
`/projects/${enc(project)}/reports/${enc(String(report))}/attachments`,
|
|
663
|
+
{ uploadIds }
|
|
664
|
+
)
|
|
665
|
+
};
|
|
666
|
+
// --- Feedback: ingest keys ------------------------------------------------
|
|
667
|
+
/**
|
|
668
|
+
* Publishable keys that let a website submit reports.
|
|
669
|
+
*
|
|
670
|
+
* Unlike every other credential here, one of these is *meant* to be readable
|
|
671
|
+
* by every visitor to a customer's page. What makes that safe lives on the
|
|
672
|
+
* server: the key may only file on one board, only from an origin the project
|
|
673
|
+
* registered, and only within its own rate and quota budget.
|
|
674
|
+
*/
|
|
675
|
+
ingestKeys = {
|
|
676
|
+
list: (project, signal) => this.get(`/projects/${enc(project)}/ingest-keys`, void 0, signal),
|
|
677
|
+
create: (project, body) => this.mutate("POST", `/projects/${enc(project)}/ingest-keys`, body),
|
|
678
|
+
revoke: (project, key) => this.mutate("DELETE", `/projects/${enc(project)}/ingest-keys/${enc(key)}`)
|
|
679
|
+
};
|
|
539
680
|
// --- Declared external checks ---------------------------------------------
|
|
540
681
|
checks = {
|
|
541
682
|
list: (project, signal) => this.get(`/projects/${enc(project)}/checks`, void 0, signal),
|
|
@@ -892,7 +1033,16 @@ var TOOL_CAPABILITY = {
|
|
|
892
1033
|
myna_list_assets: "assets:read",
|
|
893
1034
|
myna_get_asset: "assets:read",
|
|
894
1035
|
myna_upload_asset: "assets:write",
|
|
895
|
-
myna_update_asset: "assets:write"
|
|
1036
|
+
myna_update_asset: "assets:write",
|
|
1037
|
+
myna_list_reports: "feedback:read",
|
|
1038
|
+
myna_get_report: "feedback:read",
|
|
1039
|
+
myna_get_report_attachment: "feedback:read",
|
|
1040
|
+
myna_find_similar_reports: "feedback:read",
|
|
1041
|
+
myna_report_digest: "feedback:read",
|
|
1042
|
+
myna_reply_to_report: "feedback:write",
|
|
1043
|
+
myna_update_report: "feedback:write",
|
|
1044
|
+
myna_resolve_report: "feedback:write",
|
|
1045
|
+
myna_request_retest: "feedback:write"
|
|
896
1046
|
};
|
|
897
1047
|
function registerTools(server, registry, options = { allowPathUploads: true }) {
|
|
898
1048
|
server.registerTool(
|
|
@@ -1964,6 +2114,265 @@ function registerTools(server, registry, options = { allowPathUploads: true }) {
|
|
|
1964
2114
|
}
|
|
1965
2115
|
}
|
|
1966
2116
|
);
|
|
2117
|
+
if (options.products?.includes("feedback")) registerFeedbackTools(server, registry);
|
|
2118
|
+
}
|
|
2119
|
+
var reportArg = {
|
|
2120
|
+
...projectArg,
|
|
2121
|
+
report: z.string().describe("Report id (rep_...), number, or #number.")
|
|
2122
|
+
};
|
|
2123
|
+
function registerFeedbackTools(server, registry) {
|
|
2124
|
+
server.registerTool(
|
|
2125
|
+
"myna_list_reports",
|
|
2126
|
+
{
|
|
2127
|
+
title: "List reports",
|
|
2128
|
+
description: "List bug reports, QA findings, and feature requests, most recently active first. Filter by board, status, type, priority, assignee, labels, free-text search over title and body, and last-activity time. Returns compact rows; call myna_get_report for the whole picture.",
|
|
2129
|
+
inputSchema: {
|
|
2130
|
+
...projectArg,
|
|
2131
|
+
board: z.string().optional().describe("Board key, e.g. qa."),
|
|
2132
|
+
status: z.string().optional().describe("Comma-separated: triage, open, in_progress, needs_retest, resolved, closed, duplicate, wont_fix, spam."),
|
|
2133
|
+
type: z.string().optional().describe("bug, qa, feedback, or feature_request."),
|
|
2134
|
+
priority: z.string().optional().describe("low, normal, high, or urgent."),
|
|
2135
|
+
labels: z.string().optional().describe("Comma-separated labels; matches any."),
|
|
2136
|
+
q: z.string().optional().describe("Full-text search over title and body."),
|
|
2137
|
+
since: z.string().optional().describe("ISO timestamp; only reports active at or after it."),
|
|
2138
|
+
limit: z.number().optional()
|
|
2139
|
+
},
|
|
2140
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2141
|
+
},
|
|
2142
|
+
async (args) => {
|
|
2143
|
+
try {
|
|
2144
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2145
|
+
const page = await client.reports.list(project, {
|
|
2146
|
+
board: args.board,
|
|
2147
|
+
status: args.status,
|
|
2148
|
+
type: args.type,
|
|
2149
|
+
priority: args.priority,
|
|
2150
|
+
labels: args.labels?.split(","),
|
|
2151
|
+
q: args.q,
|
|
2152
|
+
since: args.since,
|
|
2153
|
+
limit: args.limit
|
|
2154
|
+
});
|
|
2155
|
+
return ok(`${page.data.length} report(s).`, { reports: page.data, nextCursor: page.nextCursor });
|
|
2156
|
+
} catch (error) {
|
|
2157
|
+
return fail(error);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
);
|
|
2161
|
+
server.registerTool(
|
|
2162
|
+
"myna_get_report",
|
|
2163
|
+
{
|
|
2164
|
+
title: "Get report",
|
|
2165
|
+
description: "Everything known about one report, in a single call: the description, the board's guidance for answering it, custom fields, the environment the application supplied, the full timeline with named actors, an attachment manifest, and linked commits or pull requests. Use this before trying to reproduce or fix anything.",
|
|
2166
|
+
inputSchema: { ...reportArg },
|
|
2167
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2168
|
+
},
|
|
2169
|
+
async (args) => {
|
|
2170
|
+
try {
|
|
2171
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2172
|
+
const report = await client.reports.get(project, args.report);
|
|
2173
|
+
const summary = [
|
|
2174
|
+
`#${report.number} ${report.title}`,
|
|
2175
|
+
`${report.status} / ${report.priority} on board ${report.boardKey}`,
|
|
2176
|
+
`${report.attachments.length} attachment(s), ${report.timeline.length} timeline entr(ies)`
|
|
2177
|
+
].join(" \u2014 ");
|
|
2178
|
+
return ok(summary, report);
|
|
2179
|
+
} catch (error) {
|
|
2180
|
+
return fail(error);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
);
|
|
2184
|
+
server.registerTool(
|
|
2185
|
+
"myna_get_report_attachment",
|
|
2186
|
+
{
|
|
2187
|
+
title: "Get report attachment",
|
|
2188
|
+
description: "Fetch one attachment from a report. Images come back as image content the model can actually look at; parsed logs come back structured. Attachment ids are listed by myna_get_report.",
|
|
2189
|
+
inputSchema: { ...reportArg, attachment: z.string().describe("Attachment id (rat_...).") },
|
|
2190
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2191
|
+
},
|
|
2192
|
+
async (args) => {
|
|
2193
|
+
try {
|
|
2194
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2195
|
+
const attachment = await client.reports.attachment(project, args.report, args.attachment);
|
|
2196
|
+
if (attachment.kind === "image" && attachment.url) {
|
|
2197
|
+
const response = await fetch(attachment.url);
|
|
2198
|
+
if (response.ok) {
|
|
2199
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
2200
|
+
return {
|
|
2201
|
+
content: [
|
|
2202
|
+
{ type: "text", text: `${attachment.filename} (${attachment.byteSize} bytes)` },
|
|
2203
|
+
{ type: "image", data: buffer.toString("base64"), mimeType: attachment.contentType }
|
|
2204
|
+
]
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
if (attachment.structured) {
|
|
2209
|
+
return ok(`${attachment.filename} (parsed).`, { attachment });
|
|
2210
|
+
}
|
|
2211
|
+
return ok(
|
|
2212
|
+
`${attachment.filename} \u2014 ${attachment.kind}, ${attachment.byteSize} bytes. Download it with the url field; it is short-lived.`,
|
|
2213
|
+
{ attachment }
|
|
2214
|
+
);
|
|
2215
|
+
} catch (error) {
|
|
2216
|
+
return fail(error);
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
);
|
|
2220
|
+
server.registerTool(
|
|
2221
|
+
"myna_reply_to_report",
|
|
2222
|
+
{
|
|
2223
|
+
title: "Reply to a report",
|
|
2224
|
+
description: "Post a message on a report. visibility 'public' reaches whoever filed it; 'internal' is a note for the team and is the default. Say what changed and why, not that you are an AI.",
|
|
2225
|
+
inputSchema: {
|
|
2226
|
+
...reportArg,
|
|
2227
|
+
message: z.string(),
|
|
2228
|
+
visibility: z.enum(["internal", "public"]).optional().describe("Defaults to internal. Use public to answer the reporter.")
|
|
2229
|
+
},
|
|
2230
|
+
annotations: { openWorldHint: true }
|
|
2231
|
+
},
|
|
2232
|
+
async (args) => {
|
|
2233
|
+
try {
|
|
2234
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2235
|
+
const report = await client.reports.comment(project, args.report, {
|
|
2236
|
+
body: args.message,
|
|
2237
|
+
visibility: args.visibility ?? "internal"
|
|
2238
|
+
});
|
|
2239
|
+
return ok(`Posted ${args.visibility ?? "internal"} reply on #${report.number}.`, { report });
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
return fail(error);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
);
|
|
2245
|
+
server.registerTool(
|
|
2246
|
+
"myna_update_report",
|
|
2247
|
+
{
|
|
2248
|
+
title: "Triage a report",
|
|
2249
|
+
description: "Set status, priority, assignee, labels, title, or body on a report. One tool rather than five, because triage is usually several of these at once.",
|
|
2250
|
+
inputSchema: {
|
|
2251
|
+
...reportArg,
|
|
2252
|
+
status: z.string().optional(),
|
|
2253
|
+
priority: z.string().optional(),
|
|
2254
|
+
assigneeId: z.string().nullable().optional().describe("User id, or null to unassign."),
|
|
2255
|
+
labels: z.array(z.string()).optional().describe("Replaces the label set."),
|
|
2256
|
+
title: z.string().optional(),
|
|
2257
|
+
body: z.string().optional()
|
|
2258
|
+
},
|
|
2259
|
+
annotations: { openWorldHint: true }
|
|
2260
|
+
},
|
|
2261
|
+
async (args) => {
|
|
2262
|
+
try {
|
|
2263
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2264
|
+
const report = await client.reports.update(project, args.report, {
|
|
2265
|
+
status: args.status,
|
|
2266
|
+
priority: args.priority,
|
|
2267
|
+
assigneeId: args.assigneeId,
|
|
2268
|
+
labels: args.labels,
|
|
2269
|
+
title: args.title,
|
|
2270
|
+
body: args.body
|
|
2271
|
+
});
|
|
2272
|
+
return ok(`#${report.number} is now ${report.status} (${report.priority}).`, { report });
|
|
2273
|
+
} catch (error) {
|
|
2274
|
+
return fail(error);
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
);
|
|
2278
|
+
server.registerTool(
|
|
2279
|
+
"myna_resolve_report",
|
|
2280
|
+
{
|
|
2281
|
+
title: "Resolve a report",
|
|
2282
|
+
description: "Mark a report resolved, link the commit or pull request that fixed it, and reply to the reporter \u2014 in one call. Set requestRetest to ask them to confirm the fix instead of closing it, which is usually the better move when you could not reproduce the problem yourself.",
|
|
2283
|
+
inputSchema: {
|
|
2284
|
+
...reportArg,
|
|
2285
|
+
reply: z.string().optional().describe("Public reply explaining what changed."),
|
|
2286
|
+
commit: z.string().optional(),
|
|
2287
|
+
pullRequest: z.string().optional(),
|
|
2288
|
+
requestRetest: z.boolean().optional()
|
|
2289
|
+
},
|
|
2290
|
+
annotations: { openWorldHint: true }
|
|
2291
|
+
},
|
|
2292
|
+
async (args) => {
|
|
2293
|
+
try {
|
|
2294
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2295
|
+
const report = await client.reports.resolve(project, args.report, {
|
|
2296
|
+
reply: args.reply,
|
|
2297
|
+
commit: args.commit,
|
|
2298
|
+
pullRequest: args.pullRequest,
|
|
2299
|
+
requestRetest: args.requestRetest
|
|
2300
|
+
});
|
|
2301
|
+
return ok(
|
|
2302
|
+
args.requestRetest ? `#${report.number} is awaiting the reporter's confirmation.` : `#${report.number} resolved.`,
|
|
2303
|
+
{ report }
|
|
2304
|
+
);
|
|
2305
|
+
} catch (error) {
|
|
2306
|
+
return fail(error);
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
);
|
|
2310
|
+
server.registerTool(
|
|
2311
|
+
"myna_request_retest",
|
|
2312
|
+
{
|
|
2313
|
+
title: "Ask for a retest",
|
|
2314
|
+
description: "Move a report to needs_retest and ask whoever filed it to confirm the fix. This is how the loop closes back to a human; prefer it over silently closing something you could not verify.",
|
|
2315
|
+
inputSchema: { ...reportArg, note: z.string().optional().describe("What they should check.") },
|
|
2316
|
+
annotations: { openWorldHint: true }
|
|
2317
|
+
},
|
|
2318
|
+
async (args) => {
|
|
2319
|
+
try {
|
|
2320
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2321
|
+
const report = await client.reports.requestRetest(project, args.report, { note: args.note });
|
|
2322
|
+
return ok(`Asked for a retest on #${report.number}.`, { report });
|
|
2323
|
+
} catch (error) {
|
|
2324
|
+
return fail(error);
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
);
|
|
2328
|
+
server.registerTool(
|
|
2329
|
+
"myna_report_digest",
|
|
2330
|
+
{
|
|
2331
|
+
title: "Report digest",
|
|
2332
|
+
description: "What has been reported since a time or a release: counts by status, board and type, plus the reports themselves already ordered by priority then recency. Call this before deciding what to work on \u2014 it answers in one request what would otherwise be several pages of myna_list_reports.",
|
|
2333
|
+
inputSchema: {
|
|
2334
|
+
...projectArg,
|
|
2335
|
+
since: z.string().optional().describe("ISO timestamp. Defaults to the last 24 hours."),
|
|
2336
|
+
release: z.number().optional().describe("Since this release number shipped."),
|
|
2337
|
+
limit: z.number().optional()
|
|
2338
|
+
},
|
|
2339
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2340
|
+
},
|
|
2341
|
+
async (args) => {
|
|
2342
|
+
try {
|
|
2343
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2344
|
+
const digest = await client.reports.digest(project, {
|
|
2345
|
+
since: args.since,
|
|
2346
|
+
release: args.release,
|
|
2347
|
+
limit: args.limit
|
|
2348
|
+
});
|
|
2349
|
+
return ok(
|
|
2350
|
+
`${digest.total} report(s) since ${digest.sinceLabel}; ${digest.stillOpen} still open.`,
|
|
2351
|
+
digest
|
|
2352
|
+
);
|
|
2353
|
+
} catch (error) {
|
|
2354
|
+
return fail(error);
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
);
|
|
2358
|
+
server.registerTool(
|
|
2359
|
+
"myna_find_similar_reports",
|
|
2360
|
+
{
|
|
2361
|
+
title: "Find similar reports",
|
|
2362
|
+
description: "Reports that might describe the same problem, by wording and by the route or URL the application reported. A ranking aid, not a verdict \u2014 each candidate says why it surfaced. Use it before fixing something twice, and to merge duplicates with myna_update_report.",
|
|
2363
|
+
inputSchema: { ...reportArg },
|
|
2364
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2365
|
+
},
|
|
2366
|
+
async (args) => {
|
|
2367
|
+
try {
|
|
2368
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2369
|
+
const result = await client.reports.similar(project, args.report);
|
|
2370
|
+
return ok(`${result.reports.length} candidate(s).`, result);
|
|
2371
|
+
} catch (error) {
|
|
2372
|
+
return fail(error);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
);
|
|
1967
2376
|
}
|
|
1968
2377
|
|
|
1969
2378
|
// src/resources.ts
|
|
@@ -2038,7 +2447,7 @@ function registerResources(server, registry) {
|
|
|
2038
2447
|
}
|
|
2039
2448
|
|
|
2040
2449
|
// src/version.ts
|
|
2041
|
-
var VERSION = true ? "0.
|
|
2450
|
+
var VERSION = true ? "0.15.0" : "0.0.0-dev";
|
|
2042
2451
|
|
|
2043
2452
|
// src/server.ts
|
|
2044
2453
|
var SERVER_NAME = "myna";
|