@loupekit/mcp 0.4.3 → 0.5.0-next.17

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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/index.ts +20 -2
  3. package/package.json +31 -8
package/README.md CHANGED
@@ -18,6 +18,7 @@ as an actionable, fully-contextualized backlog — request + element HTML + styl
18
18
 
19
19
  <p>
20
20
  <a href="https://mohamed-ashraf-elsaed.github.io/loupe/"><b>Website</b></a> ·
21
+ <a href="https://mohamed-ashraf-elsaed.github.io/loupe/guide/"><b>Docs</b></a> ·
21
22
  <a href="https://github.com/mohamed-ashraf-elsaed/loupe"><b>GitHub</b></a> ·
22
23
  <a href="https://github.com/mohamed-ashraf-elsaed/loupe/blob/main/CHANGELOG.md"><b>Changelog</b></a> ·
23
24
  <a href="https://www.npmjs.com/package/@loupekit/sdk"><b>SDK</b></a>
package/index.ts CHANGED
@@ -41,6 +41,12 @@ async function api(path: string, init?: RequestInit): Promise<any> {
41
41
 
42
42
  const wrap = (text: string) => ({ content: [{ type: "text" as const, text }] });
43
43
 
44
+ /** How to name a comment's target in a one-liner. Free notes have no element. */
45
+ const targetOf = (c: Comment) =>
46
+ c.kind === "free"
47
+ ? "page-level note"
48
+ : c.anchor.testid ? `[data-testid="${c.anchor.testid}"]` : c.anchor.cssPath;
49
+
44
50
  // Tool handlers are exported so they can be unit-tested in-process (the stdio
45
51
  // transport below only runs when this file is the entrypoint).
46
52
 
@@ -52,13 +58,25 @@ export async function listComments({ status, url }: { status?: string; url?: str
52
58
  if (!comments.length) return wrap("No comments match.");
53
59
  const lines = comments.map(
54
60
  (c) =>
55
- `- [${c.status}] #${c.id} — ${c.body}\n ↳ ${c.anchor.testid ? `[data-testid="${c.anchor.testid}"]` : c.anchor.cssPath} on ${c.url} (by ${c.author.name})`,
61
+ `- [${c.status}] #${c.id} — ${c.body}\n ↳ ${targetOf(c)} on ${c.url} (by ${c.author.name})`,
56
62
  );
57
63
  return wrap(`${comments.length} comment(s):\n\n${lines.join("\n")}\n\nUse get_comment(id) for the full element context.`);
58
64
  }
59
65
 
60
66
  export async function getComment({ id }: { id: string }) {
61
67
  const c = (await api(`/v1/comments/${encodeURIComponent(id)}`)) as Comment;
68
+ // Free notes aren't tied to an element — skip the element HTML/styles sections.
69
+ if (c.kind === "free") {
70
+ return wrap(
71
+ [
72
+ `# Feedback #${c.id} from ${c.author.name} (${c.status})`,
73
+ ``,
74
+ `**Note:** ${c.body}`,
75
+ `**Page:** ${c.url}`,
76
+ `**Type:** Free note — a page-level comment, not tied to a specific element (no screenshot).`,
77
+ ].join("\n"),
78
+ );
79
+ }
62
80
  return wrap(
63
81
  [
64
82
  `# Feedback #${c.id} from ${c.author.name} (${c.status})`,
@@ -86,7 +104,7 @@ export async function updateStatus({ id, status }: { id: string; status: string
86
104
  return wrap(`#${id} → ${status}`);
87
105
  }
88
106
 
89
- const server = new McpServer({ name: "loupe", version: "0.2.0" });
107
+ const server = new McpServer({ name: "loupe", version: "0.5.0" });
90
108
  server.tool(
91
109
  "list_comments",
92
110
  "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.",
package/package.json CHANGED
@@ -1,22 +1,45 @@
1
1
  {
2
2
  "name": "@loupekit/mcp",
3
- "author": { "name": "Mohamed Ashraf Elsaed", "email": "m.ashraf.saed@gmail.com", "url": "https://www.linkedin.com/in/mohamedashrafelsaed/" },
4
- "version": "0.4.3",
3
+ "author": {
4
+ "name": "Mohamed Ashraf Elsaed",
5
+ "email": "m.ashraf.saed@gmail.com",
6
+ "url": "https://www.linkedin.com/in/mohamedashrafelsaed/"
7
+ },
8
+ "version": "0.5.0-next.17",
5
9
  "description": "MCP server that exposes Loupe comments to Claude Code as an actionable, fully-contextualized backlog.",
6
- "keywords": ["loupe", "mcp", "model-context-protocol", "claude", "claude-code", "visual-feedback", "product-management", "ai"],
10
+ "keywords": [
11
+ "loupe",
12
+ "mcp",
13
+ "model-context-protocol",
14
+ "claude",
15
+ "claude-code",
16
+ "visual-feedback",
17
+ "product-management",
18
+ "ai"
19
+ ],
7
20
  "license": "MIT",
8
- "repository": { "type": "git", "url": "https://github.com/mohamed-ashraf-elsaed/loupe.git", "directory": "packages/mcp" },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/mohamed-ashraf-elsaed/loupe.git",
24
+ "directory": "packages/mcp"
25
+ },
9
26
  "homepage": "https://mohamed-ashraf-elsaed.github.io/loupe/",
10
- "publishConfig": { "access": "public" },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
11
30
  "type": "module",
12
- "files": ["index.ts"],
13
- "bin": { "loupe-mcp": "./index.ts" },
31
+ "files": [
32
+ "index.ts"
33
+ ],
34
+ "bin": {
35
+ "loupe-mcp": "./index.ts"
36
+ },
14
37
  "scripts": {
15
38
  "start": "node index.ts",
16
39
  "test:client": "node test-client.ts"
17
40
  },
18
41
  "dependencies": {
19
- "@loupekit/shared": "^0.2.0",
42
+ "@loupekit/shared": "0.5.0-next.17",
20
43
  "@modelcontextprotocol/sdk": "^1.12.0",
21
44
  "zod": "^3.24.1"
22
45
  }