@loupekit/mcp 0.10.8 → 0.10.9-next.54
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/index.js +28 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ element, its state, and the page.
|
|
|
51
51
|
|
|
52
52
|
| Tool | Description |
|
|
53
53
|
| --- | --- |
|
|
54
|
-
| `list_comments(status?, url?)` | List comments, optionally filtered by stage (`queue` / `todo` / `in_progress` / `in_review` / `resolved`) or page URL. The legacy names `open` / `done` are accepted too. |
|
|
54
|
+
| `list_comments(status?, priority?, changeType?, url?)` | List comments, optionally filtered by stage (`queue` / `todo` / `in_progress` / `in_review` / `resolved`), priority (`critical` / `high` / `medium` / `low`) or change type (`frontend` / `backend` / `api` / `other`) or page URL. The legacy status names `open` / `done` are accepted too. |
|
|
55
55
|
| `get_comment(id)` | The full Claude-ready package for one comment: request + element HTML + computed styles + the **screenshot as an image** + any screen-recording URL. |
|
|
56
56
|
| `propose_change(id, html, css?, notes?)` | Write your **modified HTML/CSS** back onto the comment. The dashboard renders it as code plus a live before/after preview for the dev team. |
|
|
57
57
|
| `update_status(id, status)` | Move a comment along the board (`queue` → `todo` → `in_progress` → `in_review` → `resolved`) so triage state stays in sync. Move to `in_review` when a change is ready — only a person resolves. |
|
package/dist/index.js
CHANGED
|
@@ -8,8 +8,20 @@ import { realpathSync } from "fs";
|
|
|
8
8
|
import { argv } from "process";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { Buffer } from "buffer";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
CHANGE_TYPE_LABELS,
|
|
13
|
+
CHANGE_TYPES,
|
|
14
|
+
COMMENT_PRIORITIES,
|
|
15
|
+
COMMENT_STAGES,
|
|
16
|
+
normalizeChangeType,
|
|
17
|
+
normalizePriority,
|
|
18
|
+
normalizeStatus,
|
|
19
|
+
PRIORITY_LABELS,
|
|
20
|
+
STAGE_LABELS
|
|
21
|
+
} from "@loupekit/shared";
|
|
12
22
|
var STAGE_ARG = z.string().describe(`Stage: ${COMMENT_STAGES.join(" / ")}. Legacy "open" and "done" are accepted too.`);
|
|
23
|
+
var PRIORITY_ARG = z.string().describe(`Priority, most urgent first: ${COMMENT_PRIORITIES.join(" / ")}.`);
|
|
24
|
+
var CHANGE_TYPE_ARG = z.string().describe(`Change type: ${CHANGE_TYPES.join(" / ")}.`);
|
|
13
25
|
var API = (process.env.LOUPE_API || "http://localhost:8787").replace(/\/$/, "");
|
|
14
26
|
var PROJECT_KEY = process.env.LOUPE_PROJECT_KEY || "pk_demo_acme";
|
|
15
27
|
var ADMIN = process.env.LOUPE_ADMIN_KEY || "";
|
|
@@ -52,7 +64,7 @@ async function imageBlocks(attachments) {
|
|
|
52
64
|
return out;
|
|
53
65
|
}
|
|
54
66
|
var attachmentLines = (c) => (c.attachments ?? []).length ? ["", "**Attachments:**", ...(c.attachments ?? []).map((a) => `- ${a.kind === "video" ? "\u{1F3AC}" : "\u{1F5BC}"} ${a.name ?? "attachment"} \u2014 ${a.url}`)] : [];
|
|
55
|
-
async function listComments({ status, url }) {
|
|
67
|
+
async function listComments({ status, priority, changeType, url }) {
|
|
56
68
|
const q = new URLSearchParams({ projectKey: PROJECT_KEY });
|
|
57
69
|
if (url) q.set("url", url);
|
|
58
70
|
let comments = await api(`/v1/comments?${q}`);
|
|
@@ -60,9 +72,17 @@ async function listComments({ status, url }) {
|
|
|
60
72
|
const want = normalizeStatus(status);
|
|
61
73
|
comments = comments.filter((c) => normalizeStatus(c.status) === want);
|
|
62
74
|
}
|
|
75
|
+
if (priority) {
|
|
76
|
+
const want = normalizePriority(priority);
|
|
77
|
+
comments = comments.filter((c) => normalizePriority(c.priority) === want);
|
|
78
|
+
}
|
|
79
|
+
if (changeType) {
|
|
80
|
+
const want = normalizeChangeType(changeType);
|
|
81
|
+
comments = comments.filter((c) => normalizeChangeType(c.changeType) === want);
|
|
82
|
+
}
|
|
63
83
|
if (!comments.length) return wrap("No comments match.");
|
|
64
84
|
const lines = comments.map(
|
|
65
|
-
(c) => `- [${STAGE_LABELS[normalizeStatus(c.status)]}] #${c.id} \u2014 ${titleOf(c)}: ${c.body}
|
|
85
|
+
(c) => `- [${STAGE_LABELS[normalizeStatus(c.status)]}] ${PRIORITY_LABELS[normalizePriority(c.priority)]} \xB7 ${CHANGE_TYPE_LABELS[normalizeChangeType(c.changeType)]} \xB7 #${c.id} \u2014 ${titleOf(c)}: ${c.body}
|
|
66
86
|
\u21B3 ${targetOf(c)} on ${c.url} (by ${c.author.name})`
|
|
67
87
|
);
|
|
68
88
|
return wrap(`${comments.length} comment(s):
|
|
@@ -77,6 +97,7 @@ async function getComment({ id }) {
|
|
|
77
97
|
const text2 = [
|
|
78
98
|
`# Feedback #${c.id} from ${c.author.name} (${STAGE_LABELS[normalizeStatus(c.status)]})`,
|
|
79
99
|
``,
|
|
100
|
+
`**Priority:** ${PRIORITY_LABELS[normalizePriority(c.priority)]} \xB7 **Change type:** ${CHANGE_TYPE_LABELS[normalizeChangeType(c.changeType)]}`,
|
|
80
101
|
`**Title:** ${titleOf(c)}`,
|
|
81
102
|
`**Note:** ${c.body}`,
|
|
82
103
|
`**Page:** ${c.url}`,
|
|
@@ -142,12 +163,14 @@ async function proposeChange({ id, html, css, notes }) {
|
|
|
142
163
|
await api(`/v1/comments/${encodeURIComponent(id)}`, { method: "PATCH", body: JSON.stringify({ proposal }) });
|
|
143
164
|
return wrap(`Proposal saved for #${id}. The dev team can now review your modified HTML/CSS in the dashboard.`);
|
|
144
165
|
}
|
|
145
|
-
var server = new McpServer({ name: "loupe", version: "0.10.
|
|
166
|
+
var server = new McpServer({ name: "loupe", version: "0.10.9" });
|
|
146
167
|
server.tool(
|
|
147
168
|
"list_comments",
|
|
148
|
-
"List Loupe product-feedback comments for the project as a task backlog. Use this to see what a PM has flagged, then work through the items.",
|
|
169
|
+
"List Loupe product-feedback comments for the project as a task backlog. Each item carries its board stage, priority and change type, so you can start with the most urgent. Use this to see what a PM has flagged, then work through the items.",
|
|
149
170
|
{
|
|
150
171
|
status: STAGE_ARG.optional().describe(`Filter by stage. Omit for all.`),
|
|
172
|
+
priority: PRIORITY_ARG.optional().describe("Filter by priority."),
|
|
173
|
+
changeType: CHANGE_TYPE_ARG.optional().describe("Filter by change type."),
|
|
151
174
|
url: z.string().optional().describe("Filter to a single page path, e.g. /checkout.")
|
|
152
175
|
},
|
|
153
176
|
listComments
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"email": "m.ashraf.saed@gmail.com",
|
|
6
6
|
"url": "https://www.linkedin.com/in/mohamedashrafelsaed/"
|
|
7
7
|
},
|
|
8
|
-
"version": "0.10.
|
|
8
|
+
"version": "0.10.9-next.54",
|
|
9
9
|
"description": "MCP server that exposes Loupe comments to Claude Code as an actionable, fully-contextualized backlog.",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"loupe",
|
|
@@ -46,6 +46,6 @@
|
|
|
46
46
|
"zod": "^3.24.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@loupekit/shared": "0.10.
|
|
49
|
+
"@loupekit/shared": "0.10.9-next.54"
|
|
50
50
|
}
|
|
51
51
|
}
|