@ourroadmaps/mcp 0.25.1 → 0.28.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/index.js +275 -6
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -526,7 +526,9 @@ var SLIDE_TYPES = [
|
|
|
526
526
|
"image",
|
|
527
527
|
"quote",
|
|
528
528
|
"code",
|
|
529
|
-
"thank_you"
|
|
529
|
+
"thank_you",
|
|
530
|
+
"mermaid",
|
|
531
|
+
"paragraph"
|
|
530
532
|
];
|
|
531
533
|
var slideTypeSchema = z12.enum(SLIDE_TYPES);
|
|
532
534
|
var titleContentSchema = z12.object({
|
|
@@ -581,6 +583,14 @@ var thankYouContentSchema = z12.object({
|
|
|
581
583
|
ctaText: z12.string().optional(),
|
|
582
584
|
ctaUrl: z12.string().optional()
|
|
583
585
|
});
|
|
586
|
+
var mermaidContentSchema = z12.object({
|
|
587
|
+
title: z12.string().optional(),
|
|
588
|
+
code: z12.string().default("")
|
|
589
|
+
});
|
|
590
|
+
var paragraphContentSchema = z12.object({
|
|
591
|
+
title: z12.string().optional(),
|
|
592
|
+
body: z12.string().default("")
|
|
593
|
+
});
|
|
584
594
|
var typedSlideContentSchema = z12.discriminatedUnion("type", [
|
|
585
595
|
z12.object({ type: z12.literal("title"), ...titleContentSchema.shape }),
|
|
586
596
|
z12.object({ type: z12.literal("section_header"), ...sectionHeaderContentSchema.shape }),
|
|
@@ -591,7 +601,9 @@ var typedSlideContentSchema = z12.discriminatedUnion("type", [
|
|
|
591
601
|
z12.object({ type: z12.literal("image"), ...imageContentSchema.shape }),
|
|
592
602
|
z12.object({ type: z12.literal("quote"), ...quoteContentSchema.shape }),
|
|
593
603
|
z12.object({ type: z12.literal("code"), ...codeContentSchema.shape }),
|
|
594
|
-
z12.object({ type: z12.literal("thank_you"), ...thankYouContentSchema.shape })
|
|
604
|
+
z12.object({ type: z12.literal("thank_you"), ...thankYouContentSchema.shape }),
|
|
605
|
+
z12.object({ type: z12.literal("mermaid"), ...mermaidContentSchema.shape }),
|
|
606
|
+
z12.object({ type: z12.literal("paragraph"), ...paragraphContentSchema.shape })
|
|
595
607
|
]);
|
|
596
608
|
var slideContentSchema = z12.object({
|
|
597
609
|
body: z12.string().optional()
|
|
@@ -1125,6 +1137,10 @@ class ApiClient {
|
|
|
1125
1137
|
});
|
|
1126
1138
|
return response.data;
|
|
1127
1139
|
}
|
|
1140
|
+
async getWorkflows() {
|
|
1141
|
+
const response = await this.request("/v1/workflows");
|
|
1142
|
+
return response.data;
|
|
1143
|
+
}
|
|
1128
1144
|
async updateReleaseDescription(roadmapId, description) {
|
|
1129
1145
|
const response = await this.request(`/v1/releases/roadmaps/${roadmapId}/release`, {
|
|
1130
1146
|
method: "PATCH",
|
|
@@ -1308,6 +1324,28 @@ class ApiClient {
|
|
|
1308
1324
|
});
|
|
1309
1325
|
return response.data;
|
|
1310
1326
|
}
|
|
1327
|
+
async createFeedbackSession(prototypeId, data) {
|
|
1328
|
+
const response = await this.request(`/v1/prototypes/${prototypeId}/feedback/sessions`, {
|
|
1329
|
+
method: "POST",
|
|
1330
|
+
body: JSON.stringify(data)
|
|
1331
|
+
});
|
|
1332
|
+
return response.data;
|
|
1333
|
+
}
|
|
1334
|
+
async listFeedbackSessions(prototypeId) {
|
|
1335
|
+
const response = await this.request(`/v1/prototypes/${prototypeId}/feedback/sessions`);
|
|
1336
|
+
return response.data;
|
|
1337
|
+
}
|
|
1338
|
+
async getFeedbackComments(prototypeId, sessionId, filter) {
|
|
1339
|
+
const params = filter && filter !== "all" ? `?filter=${filter}` : "";
|
|
1340
|
+
const response = await this.request(`/v1/prototypes/${prototypeId}/feedback/sessions/${sessionId}/comments${params}`);
|
|
1341
|
+
return response.data;
|
|
1342
|
+
}
|
|
1343
|
+
async resolveFeedbackComment(prototypeId, sessionId, commentId) {
|
|
1344
|
+
const response = await this.request(`/v1/prototypes/${prototypeId}/feedback/sessions/${sessionId}/comments/${commentId}/resolve`, {
|
|
1345
|
+
method: "PATCH"
|
|
1346
|
+
});
|
|
1347
|
+
return response.data;
|
|
1348
|
+
}
|
|
1311
1349
|
async getVariantPresentationId(variantId) {
|
|
1312
1350
|
const presentations = await this.listPresentations();
|
|
1313
1351
|
for (const presentation2 of presentations.items) {
|
|
@@ -1403,6 +1441,11 @@ function registerAllTools(server) {
|
|
|
1403
1441
|
registerUploadDesignWalkthrough(server);
|
|
1404
1442
|
registerUploadReleaseWalkthrough(server);
|
|
1405
1443
|
registerPublishPrototype(server);
|
|
1444
|
+
registerCreateFeedbackSession(server);
|
|
1445
|
+
registerListFeedbackSessions(server);
|
|
1446
|
+
registerGetFeedbackSummary(server);
|
|
1447
|
+
registerGetFeedbackDetail(server);
|
|
1448
|
+
registerResolveFeedbackComment(server);
|
|
1406
1449
|
registerSearchExports(server);
|
|
1407
1450
|
registerGetExport(server);
|
|
1408
1451
|
registerCreateExport(server);
|
|
@@ -1436,7 +1479,7 @@ function registerGetWorkflows(server) {
|
|
|
1436
1479
|
inputSchema: {}
|
|
1437
1480
|
}, async () => {
|
|
1438
1481
|
const client2 = getApiClient();
|
|
1439
|
-
const response = await client2.
|
|
1482
|
+
const response = await client2.getWorkflows();
|
|
1440
1483
|
return {
|
|
1441
1484
|
content: [
|
|
1442
1485
|
{
|
|
@@ -4307,7 +4350,7 @@ function registerGetSlide(server) {
|
|
|
4307
4350
|
}
|
|
4308
4351
|
function registerCreateSlide(server) {
|
|
4309
4352
|
server.registerTool("create_slide", {
|
|
4310
|
-
description: "Create a new slide in a variant. Slide types: title (title + subtitle), section_header (title + subtitle), bullets (title + items array), two_column (title + left/right text), comparison (leftLabel + leftContent + rightLabel + rightContent), timeline (title + steps array of {title, description}), image (title + imageUrl + caption), quote (quote + attribution), code (title + code + language), thank_you (title + subtitle).",
|
|
4353
|
+
description: "Create a new slide in a variant. Slide types: title (title + subtitle), section_header (title + subtitle), bullets (title + items array), two_column (title + left/right text), comparison (leftLabel + leftContent + rightLabel + rightContent), timeline (title + steps array of {title, description}), image (title + imageUrl + caption), quote (quote + attribution), code (title + code + language), thank_you (title + subtitle), mermaid (title + code with mermaid diagram syntax), paragraph (title + body with markdown text).",
|
|
4311
4354
|
inputSchema: {
|
|
4312
4355
|
variantId: z15.string().describe("The UUID of the variant"),
|
|
4313
4356
|
slideType: z15.enum([
|
|
@@ -4320,9 +4363,11 @@ function registerCreateSlide(server) {
|
|
|
4320
4363
|
"image",
|
|
4321
4364
|
"quote",
|
|
4322
4365
|
"code",
|
|
4323
|
-
"thank_you"
|
|
4366
|
+
"thank_you",
|
|
4367
|
+
"mermaid",
|
|
4368
|
+
"paragraph"
|
|
4324
4369
|
]).default("bullets").describe("The type of slide to create"),
|
|
4325
|
-
content: z15.record(z15.unknown()).optional().describe('Type-specific content object. For bullets: {title, items: ["..."]}. For title: {title, subtitle}. For two_column: {title, left, right}. For quote: {quote, attribution}. Etc.'),
|
|
4370
|
+
content: z15.record(z15.unknown()).optional().describe('Type-specific content object. For bullets: {title, items: ["..."]}. For title: {title, subtitle}. For two_column: {title, left, right}. For quote: {quote, attribution}. For mermaid: {title, code: "graph TD; A-->B"}. For paragraph: {title, body}. Etc.'),
|
|
4326
4371
|
speakerNotes: z15.string().nullable().optional().describe("Speaker notes for the slide")
|
|
4327
4372
|
}
|
|
4328
4373
|
}, async ({
|
|
@@ -4549,6 +4594,230 @@ function registerUploadSlideImage(server) {
|
|
|
4549
4594
|
};
|
|
4550
4595
|
});
|
|
4551
4596
|
}
|
|
4597
|
+
function registerCreateFeedbackSession(server) {
|
|
4598
|
+
server.registerTool("create_feedback_session", {
|
|
4599
|
+
description: "Create a feedback session for a prototype and get a shareable review link. Reviewers who open the link can optionally enter their name before leaving pin-based feedback.",
|
|
4600
|
+
inputSchema: {
|
|
4601
|
+
prototypeId: z15.string().uuid().describe("The UUID of the prototype to create a feedback session for"),
|
|
4602
|
+
name: z15.string().describe('Name for this feedback session (e.g., "Round 1 Review")'),
|
|
4603
|
+
expiresInDays: z15.number().int().min(1).max(90).optional().describe("Number of days until the session expires (default 7)")
|
|
4604
|
+
}
|
|
4605
|
+
}, async ({
|
|
4606
|
+
prototypeId,
|
|
4607
|
+
name,
|
|
4608
|
+
expiresInDays
|
|
4609
|
+
}) => {
|
|
4610
|
+
const client2 = getApiClient();
|
|
4611
|
+
const days = expiresInDays ?? 7;
|
|
4612
|
+
const expiresAt = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString();
|
|
4613
|
+
const session = await client2.createFeedbackSession(prototypeId, {
|
|
4614
|
+
name,
|
|
4615
|
+
expiresAt,
|
|
4616
|
+
reviewers: [{}]
|
|
4617
|
+
});
|
|
4618
|
+
const invite = session.invites[0];
|
|
4619
|
+
if (!invite) {
|
|
4620
|
+
return {
|
|
4621
|
+
content: [{ type: "text", text: "Error: No invite was created for the session." }]
|
|
4622
|
+
};
|
|
4623
|
+
}
|
|
4624
|
+
const reviewUrl = `https://app.ourroadmaps.com/prototype-review/${invite.token}`;
|
|
4625
|
+
const expiryDate = new Date(session.expiresAt).toLocaleDateString("en-US", {
|
|
4626
|
+
weekday: "long",
|
|
4627
|
+
year: "numeric",
|
|
4628
|
+
month: "long",
|
|
4629
|
+
day: "numeric"
|
|
4630
|
+
});
|
|
4631
|
+
return {
|
|
4632
|
+
content: [
|
|
4633
|
+
{
|
|
4634
|
+
type: "text",
|
|
4635
|
+
text: JSON.stringify({
|
|
4636
|
+
success: true,
|
|
4637
|
+
sessionId: session.id,
|
|
4638
|
+
sessionName: session.name,
|
|
4639
|
+
reviewUrl,
|
|
4640
|
+
expiresAt: session.expiresAt,
|
|
4641
|
+
expiryDate
|
|
4642
|
+
}, null, 2)
|
|
4643
|
+
}
|
|
4644
|
+
]
|
|
4645
|
+
};
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
function registerListFeedbackSessions(server) {
|
|
4649
|
+
server.registerTool("list_feedback_sessions", {
|
|
4650
|
+
description: "List all feedback sessions for a prototype with comment counts and status.",
|
|
4651
|
+
inputSchema: {
|
|
4652
|
+
prototypeId: z15.string().uuid().describe("The UUID of the prototype")
|
|
4653
|
+
}
|
|
4654
|
+
}, async ({ prototypeId }) => {
|
|
4655
|
+
const client2 = getApiClient();
|
|
4656
|
+
const sessions = await client2.listFeedbackSessions(prototypeId);
|
|
4657
|
+
return {
|
|
4658
|
+
content: [
|
|
4659
|
+
{
|
|
4660
|
+
type: "text",
|
|
4661
|
+
text: JSON.stringify({
|
|
4662
|
+
sessions: sessions.map((s) => ({
|
|
4663
|
+
id: s.id,
|
|
4664
|
+
name: s.name,
|
|
4665
|
+
status: s.status,
|
|
4666
|
+
totalComments: s.totalComments,
|
|
4667
|
+
unresolvedComments: s.unresolvedComments,
|
|
4668
|
+
inviteCount: s.inviteCount,
|
|
4669
|
+
expiresAt: s.expiresAt,
|
|
4670
|
+
createdAt: s.createdAt
|
|
4671
|
+
}))
|
|
4672
|
+
}, null, 2)
|
|
4673
|
+
}
|
|
4674
|
+
]
|
|
4675
|
+
};
|
|
4676
|
+
});
|
|
4677
|
+
}
|
|
4678
|
+
function formatElementDesc(pinData) {
|
|
4679
|
+
if (!pinData || typeof pinData !== "object")
|
|
4680
|
+
return "";
|
|
4681
|
+
const pd = pinData;
|
|
4682
|
+
const el = pd.element;
|
|
4683
|
+
if (!el)
|
|
4684
|
+
return "";
|
|
4685
|
+
const tag = el.tag || "";
|
|
4686
|
+
const text = el.text ? ` "${String(el.text).slice(0, 80)}"` : "";
|
|
4687
|
+
return ` → <${tag}>${text}`;
|
|
4688
|
+
}
|
|
4689
|
+
function formatCommentLine(c) {
|
|
4690
|
+
const pin = c.pinNumber != null ? `Pin #${c.pinNumber}` : "General";
|
|
4691
|
+
const status = c.resolved ? "[RESOLVED]" : "[OPEN]";
|
|
4692
|
+
const reviewer = c.reviewerName || "Anonymous";
|
|
4693
|
+
const elementDesc = formatElementDesc(c.pinData);
|
|
4694
|
+
return `- **${pin}** ${status} by ${reviewer}${elementDesc}
|
|
4695
|
+
${c.commentText}
|
|
4696
|
+
|
|
4697
|
+
`;
|
|
4698
|
+
}
|
|
4699
|
+
function registerGetFeedbackSummary(server) {
|
|
4700
|
+
server.registerTool("get_feedback_summary", {
|
|
4701
|
+
description: "Get a human-readable summary of feedback for a session, grouped by page. Shows pin numbers, reviewer names, comment text, and which element was pinned. Use this for a quick overview of feedback.",
|
|
4702
|
+
inputSchema: {
|
|
4703
|
+
prototypeId: z15.string().uuid().describe("The UUID of the prototype"),
|
|
4704
|
+
sessionId: z15.string().uuid().describe("The UUID of the feedback session"),
|
|
4705
|
+
filter: z15.enum(["all", "unresolved", "resolved"]).optional().describe("Filter comments by resolved status (default: unresolved)")
|
|
4706
|
+
}
|
|
4707
|
+
}, async ({
|
|
4708
|
+
prototypeId,
|
|
4709
|
+
sessionId,
|
|
4710
|
+
filter
|
|
4711
|
+
}) => {
|
|
4712
|
+
const client2 = getApiClient();
|
|
4713
|
+
const comments = await client2.getFeedbackComments(prototypeId, sessionId, filter ?? "unresolved");
|
|
4714
|
+
if (comments.length === 0) {
|
|
4715
|
+
return {
|
|
4716
|
+
content: [{ type: "text", text: "No comments found matching the filter." }]
|
|
4717
|
+
};
|
|
4718
|
+
}
|
|
4719
|
+
const byPage = new Map;
|
|
4720
|
+
for (const c of comments) {
|
|
4721
|
+
const page = c.pageUrl || "(no page)";
|
|
4722
|
+
if (!byPage.has(page))
|
|
4723
|
+
byPage.set(page, []);
|
|
4724
|
+
byPage.get(page).push(c);
|
|
4725
|
+
}
|
|
4726
|
+
let summary = `## Feedback Summary (${comments.length} comment${comments.length !== 1 ? "s" : ""})
|
|
4727
|
+
|
|
4728
|
+
`;
|
|
4729
|
+
for (const [page, pageComments] of byPage) {
|
|
4730
|
+
summary += `### Page: ${page}
|
|
4731
|
+
|
|
4732
|
+
`;
|
|
4733
|
+
for (const c of pageComments) {
|
|
4734
|
+
summary += formatCommentLine(c);
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4737
|
+
return {
|
|
4738
|
+
content: [{ type: "text", text: summary.trim() }]
|
|
4739
|
+
};
|
|
4740
|
+
});
|
|
4741
|
+
}
|
|
4742
|
+
function registerGetFeedbackDetail(server) {
|
|
4743
|
+
server.registerTool("get_feedback_detail", {
|
|
4744
|
+
description: "Get full raw pin data for feedback comments, including CSS selectors, bounding boxes, parent/sibling DOM context, and coordinates. Use this when you need exact element information to make targeted code changes.",
|
|
4745
|
+
inputSchema: {
|
|
4746
|
+
prototypeId: z15.string().uuid().describe("The UUID of the prototype"),
|
|
4747
|
+
sessionId: z15.string().uuid().describe("The UUID of the feedback session"),
|
|
4748
|
+
commentId: z15.string().uuid().optional().describe("Optional: get detail for a single comment"),
|
|
4749
|
+
filter: z15.enum(["all", "unresolved", "resolved"]).optional().describe("Filter comments by resolved status (default: all). Ignored if commentId is provided.")
|
|
4750
|
+
}
|
|
4751
|
+
}, async ({
|
|
4752
|
+
prototypeId,
|
|
4753
|
+
sessionId,
|
|
4754
|
+
commentId,
|
|
4755
|
+
filter
|
|
4756
|
+
}) => {
|
|
4757
|
+
const client2 = getApiClient();
|
|
4758
|
+
const comments = await client2.getFeedbackComments(prototypeId, sessionId, filter ?? "all");
|
|
4759
|
+
let filtered = comments;
|
|
4760
|
+
if (commentId) {
|
|
4761
|
+
filtered = comments.filter((c) => c.id === commentId);
|
|
4762
|
+
if (filtered.length === 0) {
|
|
4763
|
+
return {
|
|
4764
|
+
content: [
|
|
4765
|
+
{ type: "text", text: `Comment ${commentId} not found in this session.` }
|
|
4766
|
+
]
|
|
4767
|
+
};
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
return {
|
|
4771
|
+
content: [
|
|
4772
|
+
{
|
|
4773
|
+
type: "text",
|
|
4774
|
+
text: JSON.stringify({
|
|
4775
|
+
comments: filtered.map((c) => ({
|
|
4776
|
+
id: c.id,
|
|
4777
|
+
pinNumber: c.pinNumber,
|
|
4778
|
+
reviewerName: c.reviewerName,
|
|
4779
|
+
commentText: c.commentText,
|
|
4780
|
+
pageUrl: c.pageUrl,
|
|
4781
|
+
resolved: c.resolved,
|
|
4782
|
+
pinData: c.pinData,
|
|
4783
|
+
createdAt: c.createdAt
|
|
4784
|
+
}))
|
|
4785
|
+
}, null, 2)
|
|
4786
|
+
}
|
|
4787
|
+
]
|
|
4788
|
+
};
|
|
4789
|
+
});
|
|
4790
|
+
}
|
|
4791
|
+
function registerResolveFeedbackComment(server) {
|
|
4792
|
+
server.registerTool("resolve_feedback_comment", {
|
|
4793
|
+
description: "Toggle the resolved status on a feedback comment. If currently unresolved, marks it as resolved. If already resolved, marks it as unresolved.",
|
|
4794
|
+
inputSchema: {
|
|
4795
|
+
prototypeId: z15.string().uuid().describe("The UUID of the prototype"),
|
|
4796
|
+
sessionId: z15.string().uuid().describe("The UUID of the feedback session"),
|
|
4797
|
+
commentId: z15.string().uuid().describe("The UUID of the comment to resolve/unresolve")
|
|
4798
|
+
}
|
|
4799
|
+
}, async ({
|
|
4800
|
+
prototypeId,
|
|
4801
|
+
sessionId,
|
|
4802
|
+
commentId
|
|
4803
|
+
}) => {
|
|
4804
|
+
const client2 = getApiClient();
|
|
4805
|
+
const result = await client2.resolveFeedbackComment(prototypeId, sessionId, commentId);
|
|
4806
|
+
return {
|
|
4807
|
+
content: [
|
|
4808
|
+
{
|
|
4809
|
+
type: "text",
|
|
4810
|
+
text: JSON.stringify({
|
|
4811
|
+
success: true,
|
|
4812
|
+
commentId: result.id,
|
|
4813
|
+
resolved: result.resolved,
|
|
4814
|
+
resolvedAt: result.resolvedAt
|
|
4815
|
+
}, null, 2)
|
|
4816
|
+
}
|
|
4817
|
+
]
|
|
4818
|
+
};
|
|
4819
|
+
});
|
|
4820
|
+
}
|
|
4552
4821
|
|
|
4553
4822
|
// src/index.ts
|
|
4554
4823
|
async function main() {
|