@myna-sh/mcp 0.15.0 → 0.16.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/dist/main.js +185 -14
- package/dist/main.js.map +1 -1
- package/package.json +2 -2
package/dist/main.js
CHANGED
|
@@ -564,7 +564,7 @@ var ManagementClient = class {
|
|
|
564
564
|
*/
|
|
565
565
|
reports = {
|
|
566
566
|
list: (project, opts = {}) => {
|
|
567
|
-
const { board, status, type, priority, assignee, labels, q, since, ...rest } = opts;
|
|
567
|
+
const { board, status, type, priority, assignee, labels, q, since, reporter, property, ...rest } = opts;
|
|
568
568
|
return this.page(`/projects/${enc(project)}/reports`, rest, {
|
|
569
569
|
board,
|
|
570
570
|
status: Array.isArray(status) ? status.join(",") : status,
|
|
@@ -573,7 +573,9 @@ var ManagementClient = class {
|
|
|
573
573
|
assignee,
|
|
574
574
|
labels: labels?.join(","),
|
|
575
575
|
q,
|
|
576
|
-
since
|
|
576
|
+
since,
|
|
577
|
+
reporter,
|
|
578
|
+
property
|
|
577
579
|
});
|
|
578
580
|
},
|
|
579
581
|
get: (project, report, signal) => this.get(
|
|
@@ -629,13 +631,7 @@ var ManagementClient = class {
|
|
|
629
631
|
void 0,
|
|
630
632
|
signal
|
|
631
633
|
),
|
|
632
|
-
/**
|
|
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`. */
|
|
634
|
+
/** Move a report to another board. Ordinary triage; every board is private. */
|
|
639
635
|
move: (project, report, body) => this.mutate(
|
|
640
636
|
"POST",
|
|
641
637
|
`/projects/${enc(project)}/reports/${enc(String(report))}/move`,
|
|
@@ -669,14 +665,58 @@ var ManagementClient = class {
|
|
|
669
665
|
*
|
|
670
666
|
* Unlike every other credential here, one of these is *meant* to be readable
|
|
671
667
|
* by every visitor to a customer's page. What makes that safe lives on the
|
|
672
|
-
* server: the key
|
|
673
|
-
*
|
|
668
|
+
* server: the key can only file a report and read back what the presenting
|
|
669
|
+
* reporter wrote, only from an origin the project registered, and only within
|
|
670
|
+
* its own rate and quota budget.
|
|
671
|
+
*
|
|
672
|
+
* Both `rotate` calls return the new secret and leave the old one working for
|
|
673
|
+
* 24 hours, so a frontend can be rebuilt and redeployed in between.
|
|
674
674
|
*/
|
|
675
675
|
ingestKeys = {
|
|
676
676
|
list: (project, signal) => this.get(`/projects/${enc(project)}/ingest-keys`, void 0, signal),
|
|
677
677
|
create: (project, body) => this.mutate("POST", `/projects/${enc(project)}/ingest-keys`, body),
|
|
678
|
+
rotate: (project, key) => this.mutate(
|
|
679
|
+
"POST",
|
|
680
|
+
`/projects/${enc(project)}/ingest-keys/${enc(key)}/rotate`
|
|
681
|
+
),
|
|
682
|
+
rotateIdentitySecret: (project, key) => this.mutate(
|
|
683
|
+
"POST",
|
|
684
|
+
`/projects/${enc(project)}/ingest-keys/${enc(key)}/rotate-identity-secret`
|
|
685
|
+
),
|
|
678
686
|
revoke: (project, key) => this.mutate("DELETE", `/projects/${enc(project)}/ingest-keys/${enc(key)}`)
|
|
679
687
|
};
|
|
688
|
+
// --- Feedback: reporters --------------------------------------------------
|
|
689
|
+
/**
|
|
690
|
+
* The people who filed the reports.
|
|
691
|
+
*
|
|
692
|
+
* `verified` is the only field Myna stands behind: a signature the project's
|
|
693
|
+
* own backend produced. `properties` is what that backend said about its own
|
|
694
|
+
* user — true of their system, not checked here.
|
|
695
|
+
*/
|
|
696
|
+
reporters = {
|
|
697
|
+
list: (project, opts = {}) => {
|
|
698
|
+
const { q, blocked, ...rest } = opts;
|
|
699
|
+
return this.page(`/projects/${enc(project)}/reporters`, rest, {
|
|
700
|
+
q,
|
|
701
|
+
blocked: blocked === void 0 ? void 0 : String(blocked)
|
|
702
|
+
});
|
|
703
|
+
},
|
|
704
|
+
/** By id (`rpr_…`), by the customer's own user id, or by email. */
|
|
705
|
+
get: (project, reporter, signal) => this.get(
|
|
706
|
+
`/projects/${enc(project)}/reporters/${enc(reporter)}`,
|
|
707
|
+
void 0,
|
|
708
|
+
signal
|
|
709
|
+
),
|
|
710
|
+
/**
|
|
711
|
+
* Block or unblock. Blocking stops notifications and closes read-back; it
|
|
712
|
+
* never deletes what they already filed.
|
|
713
|
+
*/
|
|
714
|
+
setBlocked: (project, reporter, body) => this.mutate(
|
|
715
|
+
"PATCH",
|
|
716
|
+
`/projects/${enc(project)}/reporters/${enc(reporter)}`,
|
|
717
|
+
body
|
|
718
|
+
)
|
|
719
|
+
};
|
|
680
720
|
// --- Declared external checks ---------------------------------------------
|
|
681
721
|
checks = {
|
|
682
722
|
list: (project, signal) => this.get(`/projects/${enc(project)}/checks`, void 0, signal),
|
|
@@ -1039,10 +1079,15 @@ var TOOL_CAPABILITY = {
|
|
|
1039
1079
|
myna_get_report_attachment: "feedback:read",
|
|
1040
1080
|
myna_find_similar_reports: "feedback:read",
|
|
1041
1081
|
myna_report_digest: "feedback:read",
|
|
1082
|
+
myna_list_reporters: "feedback:read",
|
|
1083
|
+
myna_get_reporter: "feedback:read",
|
|
1042
1084
|
myna_reply_to_report: "feedback:write",
|
|
1043
1085
|
myna_update_report: "feedback:write",
|
|
1044
1086
|
myna_resolve_report: "feedback:write",
|
|
1045
|
-
myna_request_retest: "feedback:write"
|
|
1087
|
+
myna_request_retest: "feedback:write",
|
|
1088
|
+
myna_merge_report: "feedback:write",
|
|
1089
|
+
myna_move_report: "feedback:write",
|
|
1090
|
+
myna_block_reporter: "feedback:manage"
|
|
1046
1091
|
};
|
|
1047
1092
|
function registerTools(server, registry, options = { allowPathUploads: true }) {
|
|
1048
1093
|
server.registerTool(
|
|
@@ -2359,7 +2404,7 @@ function registerFeedbackTools(server, registry) {
|
|
|
2359
2404
|
"myna_find_similar_reports",
|
|
2360
2405
|
{
|
|
2361
2406
|
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
|
|
2407
|
+
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_merge_report.",
|
|
2363
2408
|
inputSchema: { ...reportArg },
|
|
2364
2409
|
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2365
2410
|
},
|
|
@@ -2373,6 +2418,132 @@ function registerFeedbackTools(server, registry) {
|
|
|
2373
2418
|
}
|
|
2374
2419
|
}
|
|
2375
2420
|
);
|
|
2421
|
+
server.registerTool(
|
|
2422
|
+
"myna_merge_report",
|
|
2423
|
+
{
|
|
2424
|
+
title: "Merge a duplicate report",
|
|
2425
|
+
description: "Mark a report as a duplicate of another. Nothing is deleted: the duplicate keeps its own timeline and points at the canonical report, because the second person to hit a bug usually said something the first did not. Confirm with myna_find_similar_reports first \u2014 a wrong merge buries a distinct problem.",
|
|
2426
|
+
inputSchema: {
|
|
2427
|
+
...reportArg,
|
|
2428
|
+
into: z.string().describe("The canonical report: an id, a number, or #number."),
|
|
2429
|
+
note: z.string().optional().describe("Explains the merge on both timelines.")
|
|
2430
|
+
},
|
|
2431
|
+
annotations: { openWorldHint: true }
|
|
2432
|
+
},
|
|
2433
|
+
async (args) => {
|
|
2434
|
+
try {
|
|
2435
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2436
|
+
const report = await client.reports.merge(project, args.report, {
|
|
2437
|
+
into: args.into,
|
|
2438
|
+
note: args.note
|
|
2439
|
+
});
|
|
2440
|
+
return ok(`#${report.number} marked as a duplicate of ${args.into}.`, { report });
|
|
2441
|
+
} catch (error) {
|
|
2442
|
+
return fail(error);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
);
|
|
2446
|
+
server.registerTool(
|
|
2447
|
+
"myna_move_report",
|
|
2448
|
+
{
|
|
2449
|
+
title: "Move a report to another board",
|
|
2450
|
+
description: "Move a report to a different board on the same project \u2014 a bug filed on the ideas board, a QA finding that belongs with the bugs. Every board is private to the project, so this changes which queue the report sits in and nothing about who can read it.",
|
|
2451
|
+
inputSchema: { ...reportArg, board: z.string().describe("Destination board key.") },
|
|
2452
|
+
annotations: { openWorldHint: true }
|
|
2453
|
+
},
|
|
2454
|
+
async (args) => {
|
|
2455
|
+
try {
|
|
2456
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2457
|
+
const report = await client.reports.move(project, args.report, { board: args.board });
|
|
2458
|
+
return ok(`#${report.number} moved to ${report.boardKey}.`, { report });
|
|
2459
|
+
} catch (error) {
|
|
2460
|
+
return fail(error);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
);
|
|
2464
|
+
server.registerTool(
|
|
2465
|
+
"myna_list_reporters",
|
|
2466
|
+
{
|
|
2467
|
+
title: "List reporters",
|
|
2468
|
+
description: "The people who have filed reports on this project: how many each has filed, when they were last active, whether their identity was signed by the application's own backend, and the properties that backend attached. Use it to see whether a wave of reports is one person or twenty.",
|
|
2469
|
+
inputSchema: {
|
|
2470
|
+
...projectArg,
|
|
2471
|
+
query: z.string().optional().describe("Match an email, display name, or external id."),
|
|
2472
|
+
blocked: z.boolean().optional().describe("Only blocked reporters."),
|
|
2473
|
+
limit: z.number().optional(),
|
|
2474
|
+
cursor: z.string().optional()
|
|
2475
|
+
},
|
|
2476
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2477
|
+
},
|
|
2478
|
+
async (args) => {
|
|
2479
|
+
try {
|
|
2480
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2481
|
+
const page = await client.reporters.list(project, {
|
|
2482
|
+
q: args.query,
|
|
2483
|
+
blocked: args.blocked,
|
|
2484
|
+
limit: args.limit,
|
|
2485
|
+
cursor: args.cursor
|
|
2486
|
+
});
|
|
2487
|
+
return ok(`${page.data.length} reporter(s).`, {
|
|
2488
|
+
reporters: page.data,
|
|
2489
|
+
nextCursor: page.nextCursor
|
|
2490
|
+
});
|
|
2491
|
+
} catch (error) {
|
|
2492
|
+
return fail(error);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
);
|
|
2496
|
+
server.registerTool(
|
|
2497
|
+
"myna_get_reporter",
|
|
2498
|
+
{
|
|
2499
|
+
title: "Get a reporter",
|
|
2500
|
+
description: "One reporter and everything they have filed, in one call. Reference them by Myna id (rpr_...), by the application's own user id, or by email address. Read this before answering somebody who has reported the same area three times.",
|
|
2501
|
+
inputSchema: {
|
|
2502
|
+
...projectArg,
|
|
2503
|
+
reporter: z.string().describe("Reporter id (rpr_...), your own user id, or an email address.")
|
|
2504
|
+
},
|
|
2505
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
2506
|
+
},
|
|
2507
|
+
async (args) => {
|
|
2508
|
+
try {
|
|
2509
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2510
|
+
const reporter = await client.reporters.get(project, args.reporter);
|
|
2511
|
+
return ok(
|
|
2512
|
+
`${reporter.displayName ?? reporter.email ?? reporter.id}: ${reporter.reportCount} report(s), ${reporter.verified ? "verified" : "unverified"}.`,
|
|
2513
|
+
reporter
|
|
2514
|
+
);
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
return fail(error);
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
);
|
|
2520
|
+
server.registerTool(
|
|
2521
|
+
"myna_block_reporter",
|
|
2522
|
+
{
|
|
2523
|
+
title: "Block or unblock a reporter",
|
|
2524
|
+
description: "Stop a reporter filing, reading their reports back, or being emailed \u2014 or let them back in. Nothing they already filed is deleted or hidden. This is moderation, not triage: use it for abuse, not for somebody who reports too much.",
|
|
2525
|
+
inputSchema: {
|
|
2526
|
+
...projectArg,
|
|
2527
|
+
reporter: z.string().describe("Reporter id (rpr_...), your own user id, or an email address."),
|
|
2528
|
+
blocked: z.boolean().describe("True to block, false to unblock.")
|
|
2529
|
+
},
|
|
2530
|
+
annotations: { destructiveHint: true, openWorldHint: true }
|
|
2531
|
+
},
|
|
2532
|
+
async (args) => {
|
|
2533
|
+
try {
|
|
2534
|
+
const { client, project } = registry.clientFor(args.project);
|
|
2535
|
+
const reporter = await client.reporters.setBlocked(project, args.reporter, {
|
|
2536
|
+
blocked: args.blocked
|
|
2537
|
+
});
|
|
2538
|
+
return ok(
|
|
2539
|
+
`${reporter.displayName ?? reporter.email ?? reporter.id} is now ${reporter.blocked ? "blocked" : "active"}.`,
|
|
2540
|
+
reporter
|
|
2541
|
+
);
|
|
2542
|
+
} catch (error) {
|
|
2543
|
+
return fail(error);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
);
|
|
2376
2547
|
}
|
|
2377
2548
|
|
|
2378
2549
|
// src/resources.ts
|
|
@@ -2447,7 +2618,7 @@ function registerResources(server, registry) {
|
|
|
2447
2618
|
}
|
|
2448
2619
|
|
|
2449
2620
|
// src/version.ts
|
|
2450
|
-
var VERSION = true ? "0.
|
|
2621
|
+
var VERSION = true ? "0.16.0" : "0.0.0-dev";
|
|
2451
2622
|
|
|
2452
2623
|
// src/server.ts
|
|
2453
2624
|
var SERVER_NAME = "myna";
|