@ourroadmaps/mcp 0.11.0 → 0.13.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 +30 -0
- package/dist/index.js +135 -6
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# @ourroadmaps/mcp
|
|
2
|
+
|
|
3
|
+
MCP server for OurRoadmaps - manage roadmaps, features, and ideas from Claude Code.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
claude mcp add roadmaps \
|
|
9
|
+
--env ROADMAPS_API_URL=https://api.ourroadmaps.com \
|
|
10
|
+
--env ROADMAPS_API_KEY=your-key-here \
|
|
11
|
+
-- npx -y @ourroadmaps/mcp
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Get your API key from **Organization Settings → API Keys** in the web app.
|
|
15
|
+
|
|
16
|
+
## Development
|
|
17
|
+
|
|
18
|
+
This package is published to npm. Changes require publishing:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bun run build
|
|
22
|
+
# bump version in package.json
|
|
23
|
+
npm publish
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The server runs from npm, not local files. Users fetch via `npx -y @ourroadmaps/mcp`.
|
|
27
|
+
|
|
28
|
+
## Available Tools
|
|
29
|
+
|
|
30
|
+
See the main [README](../../README.md#mcp-server) for the full list of tools.
|
package/dist/index.js
CHANGED
|
@@ -794,6 +794,34 @@ class ApiClient {
|
|
|
794
794
|
method: "DELETE"
|
|
795
795
|
});
|
|
796
796
|
}
|
|
797
|
+
async getDesignWalkthroughUploadUrl(data) {
|
|
798
|
+
const response = await this.request("/v1/designs/walkthrough/upload-url", {
|
|
799
|
+
method: "POST",
|
|
800
|
+
body: JSON.stringify(data)
|
|
801
|
+
});
|
|
802
|
+
return response.data;
|
|
803
|
+
}
|
|
804
|
+
async updateDesignWalkthrough(roadmapId, walkthroughVideoUrl) {
|
|
805
|
+
const response = await this.request(`/v1/designs/roadmaps/${roadmapId}/design`, {
|
|
806
|
+
method: "PATCH",
|
|
807
|
+
body: JSON.stringify({ walkthroughVideoUrl })
|
|
808
|
+
});
|
|
809
|
+
return response.data;
|
|
810
|
+
}
|
|
811
|
+
async getReleaseWalkthroughUploadUrl(data) {
|
|
812
|
+
const response = await this.request("/v1/releases/walkthrough/upload-url", {
|
|
813
|
+
method: "POST",
|
|
814
|
+
body: JSON.stringify(data)
|
|
815
|
+
});
|
|
816
|
+
return response.data;
|
|
817
|
+
}
|
|
818
|
+
async updateReleaseWalkthrough(roadmapId, walkthroughVideoUrl) {
|
|
819
|
+
const response = await this.request(`/v1/releases/roadmaps/${roadmapId}/release`, {
|
|
820
|
+
method: "PATCH",
|
|
821
|
+
body: JSON.stringify({ walkthroughVideoUrl })
|
|
822
|
+
});
|
|
823
|
+
return response.data;
|
|
824
|
+
}
|
|
797
825
|
async getProductDesignUploadUrl(productId, data) {
|
|
798
826
|
const response = await this.request(`/v1/products/${productId}/design-media/upload-url`, {
|
|
799
827
|
method: "POST",
|
|
@@ -922,6 +950,8 @@ function registerAllTools(server) {
|
|
|
922
950
|
registerUploadProductDesignMedia(server);
|
|
923
951
|
registerUpdateProductDesignMedia(server);
|
|
924
952
|
registerDeleteProductDesignMedia(server);
|
|
953
|
+
registerUploadDesignWalkthrough(server);
|
|
954
|
+
registerUploadReleaseWalkthrough(server);
|
|
925
955
|
}
|
|
926
956
|
function registerSearchRoadmaps(server) {
|
|
927
957
|
server.registerTool("search_roadmaps", {
|
|
@@ -1290,7 +1320,8 @@ function registerUpdateRoadmapItem(server) {
|
|
|
1290
1320
|
designDescription: z10.string().nullable().optional().describe("Description for the Design phase"),
|
|
1291
1321
|
planDescription: z10.string().nullable().optional().describe("Description for the Planning phase"),
|
|
1292
1322
|
buildDescription: z10.string().nullable().optional().describe("Description for the Build phase"),
|
|
1293
|
-
releaseDescription: z10.string().nullable().optional().describe("Description for the Release phase")
|
|
1323
|
+
releaseDescription: z10.string().nullable().optional().describe("Description for the Release phase"),
|
|
1324
|
+
prompt: z10.string().nullable().optional().describe("Build prompt for the roadmap item")
|
|
1294
1325
|
}
|
|
1295
1326
|
}, async ({
|
|
1296
1327
|
id,
|
|
@@ -1303,7 +1334,8 @@ function registerUpdateRoadmapItem(server) {
|
|
|
1303
1334
|
designDescription,
|
|
1304
1335
|
planDescription,
|
|
1305
1336
|
buildDescription,
|
|
1306
|
-
releaseDescription
|
|
1337
|
+
releaseDescription,
|
|
1338
|
+
prompt
|
|
1307
1339
|
}) => {
|
|
1308
1340
|
const client2 = getApiClient();
|
|
1309
1341
|
const updates = {};
|
|
@@ -1319,7 +1351,7 @@ function registerUpdateRoadmapItem(server) {
|
|
|
1319
1351
|
updates.effort = effort;
|
|
1320
1352
|
if (order !== undefined)
|
|
1321
1353
|
updates.order = order;
|
|
1322
|
-
const roadmap2 = await client2.updateRoadmap(id, updates);
|
|
1354
|
+
const roadmap2 = Object.keys(updates).length > 0 ? await client2.updateRoadmap(id, updates) : await client2.getRoadmap(id);
|
|
1323
1355
|
const phaseUpdates = {};
|
|
1324
1356
|
if (designDescription !== undefined) {
|
|
1325
1357
|
await client2.updateDesignDescription(id, designDescription);
|
|
@@ -1329,9 +1361,17 @@ function registerUpdateRoadmapItem(server) {
|
|
|
1329
1361
|
await client2.updatePlanDescription(id, planDescription);
|
|
1330
1362
|
phaseUpdates.planDescription = planDescription;
|
|
1331
1363
|
}
|
|
1332
|
-
if (buildDescription !== undefined) {
|
|
1333
|
-
|
|
1334
|
-
|
|
1364
|
+
if (buildDescription !== undefined || prompt !== undefined) {
|
|
1365
|
+
const buildData = {};
|
|
1366
|
+
if (buildDescription !== undefined)
|
|
1367
|
+
buildData.description = buildDescription;
|
|
1368
|
+
if (prompt !== undefined)
|
|
1369
|
+
buildData.prompt = prompt;
|
|
1370
|
+
await client2.updateBuildDescription(id, buildData);
|
|
1371
|
+
if (buildDescription !== undefined)
|
|
1372
|
+
phaseUpdates.buildDescription = buildDescription;
|
|
1373
|
+
if (prompt !== undefined)
|
|
1374
|
+
phaseUpdates.prompt = prompt;
|
|
1335
1375
|
}
|
|
1336
1376
|
if (releaseDescription !== undefined) {
|
|
1337
1377
|
await client2.updateReleaseDescription(id, releaseDescription);
|
|
@@ -2685,6 +2725,95 @@ function registerDeleteProductDesignMedia(server) {
|
|
|
2685
2725
|
};
|
|
2686
2726
|
});
|
|
2687
2727
|
}
|
|
2728
|
+
function isVideoContentType(contentType) {
|
|
2729
|
+
return contentType?.startsWith("video/") ?? false;
|
|
2730
|
+
}
|
|
2731
|
+
function registerUploadDesignWalkthrough(server) {
|
|
2732
|
+
server.registerTool("upload_design_walkthrough", {
|
|
2733
|
+
description: "Upload a walkthrough video to a roadmap item's design phase. Returns a curl command to execute for the upload.",
|
|
2734
|
+
inputSchema: {
|
|
2735
|
+
roadmapId: z10.string().describe("The UUID of the roadmap item"),
|
|
2736
|
+
filePath: z10.string().describe("Absolute path to the video file on disk")
|
|
2737
|
+
}
|
|
2738
|
+
}, async ({ roadmapId, filePath }) => {
|
|
2739
|
+
const client2 = getApiClient();
|
|
2740
|
+
const filename = filePath.split("/").pop() || "video.mp4";
|
|
2741
|
+
const contentType = inferContentType(filename);
|
|
2742
|
+
if (!isVideoContentType(contentType)) {
|
|
2743
|
+
return {
|
|
2744
|
+
content: [
|
|
2745
|
+
{
|
|
2746
|
+
type: "text",
|
|
2747
|
+
text: JSON.stringify({
|
|
2748
|
+
error: "Unsupported video format. Use MP4, WebM, or MOV."
|
|
2749
|
+
})
|
|
2750
|
+
}
|
|
2751
|
+
]
|
|
2752
|
+
};
|
|
2753
|
+
}
|
|
2754
|
+
const { uploadUrl, videoUrl } = await client2.getDesignWalkthroughUploadUrl({
|
|
2755
|
+
filename,
|
|
2756
|
+
contentType
|
|
2757
|
+
});
|
|
2758
|
+
await client2.updateDesignWalkthrough(roadmapId, videoUrl);
|
|
2759
|
+
const curlCommand = `curl -X PUT -H "Content-Type: ${contentType}" --data-binary @"${filePath}" "${uploadUrl}"`;
|
|
2760
|
+
return {
|
|
2761
|
+
content: [
|
|
2762
|
+
{
|
|
2763
|
+
type: "text",
|
|
2764
|
+
text: JSON.stringify({
|
|
2765
|
+
videoUrl,
|
|
2766
|
+
curlCommand,
|
|
2767
|
+
instructions: "Execute the curlCommand to upload the video. The design record has been updated and will display once uploaded."
|
|
2768
|
+
}, null, 2)
|
|
2769
|
+
}
|
|
2770
|
+
]
|
|
2771
|
+
};
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
function registerUploadReleaseWalkthrough(server) {
|
|
2775
|
+
server.registerTool("upload_release_walkthrough", {
|
|
2776
|
+
description: "Upload a walkthrough video to a roadmap item's release phase. Returns a curl command to execute for the upload.",
|
|
2777
|
+
inputSchema: {
|
|
2778
|
+
roadmapId: z10.string().describe("The UUID of the roadmap item"),
|
|
2779
|
+
filePath: z10.string().describe("Absolute path to the video file on disk")
|
|
2780
|
+
}
|
|
2781
|
+
}, async ({ roadmapId, filePath }) => {
|
|
2782
|
+
const client2 = getApiClient();
|
|
2783
|
+
const filename = filePath.split("/").pop() || "video.mp4";
|
|
2784
|
+
const contentType = inferContentType(filename);
|
|
2785
|
+
if (!isVideoContentType(contentType)) {
|
|
2786
|
+
return {
|
|
2787
|
+
content: [
|
|
2788
|
+
{
|
|
2789
|
+
type: "text",
|
|
2790
|
+
text: JSON.stringify({
|
|
2791
|
+
error: "Unsupported video format. Use MP4, WebM, or MOV."
|
|
2792
|
+
})
|
|
2793
|
+
}
|
|
2794
|
+
]
|
|
2795
|
+
};
|
|
2796
|
+
}
|
|
2797
|
+
const { uploadUrl, videoUrl } = await client2.getReleaseWalkthroughUploadUrl({
|
|
2798
|
+
filename,
|
|
2799
|
+
contentType
|
|
2800
|
+
});
|
|
2801
|
+
await client2.updateReleaseWalkthrough(roadmapId, videoUrl);
|
|
2802
|
+
const curlCommand = `curl -X PUT -H "Content-Type: ${contentType}" --data-binary @"${filePath}" "${uploadUrl}"`;
|
|
2803
|
+
return {
|
|
2804
|
+
content: [
|
|
2805
|
+
{
|
|
2806
|
+
type: "text",
|
|
2807
|
+
text: JSON.stringify({
|
|
2808
|
+
videoUrl,
|
|
2809
|
+
curlCommand,
|
|
2810
|
+
instructions: "Execute the curlCommand to upload the video. The release record has been updated and will display once uploaded."
|
|
2811
|
+
}, null, 2)
|
|
2812
|
+
}
|
|
2813
|
+
]
|
|
2814
|
+
};
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2688
2817
|
|
|
2689
2818
|
// src/index.ts
|
|
2690
2819
|
async function main() {
|