aixischeck-mcp 0.4.0 → 0.5.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 +9 -0
- package/dist/index.js +28 -2
- package/dist/lib.js +21 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,15 @@ upload. Generate an HTML one-pager in chat, and it lands in your workspace, rend
|
|
|
21
21
|
(base_version derived from the request); records what was done with each comment
|
|
22
22
|
(incorporated / partial / skipped / not_addressed). The write-back to Postgres is one
|
|
23
23
|
atomic RPC (concurrency + idempotency guaranteed).
|
|
24
|
+
- **`create_comments`** — `{ doc_id, origin, items[] }`. Bulk-creates review comments,
|
|
25
|
+
e.g. from a transcript of a reviewer narrating feedback while reading (such as a
|
|
26
|
+
Granola recording). Each item needs `section_index` (0-based, from `get_doc`'s
|
|
27
|
+
`sections`) and an EXACT verbatim `selected_text` quote from that section —
|
|
28
|
+
paraphrased quotes are rejected, not force-matched. `origin` is `'ai-transcribed'`
|
|
29
|
+
(matched from a human transcript) or `'ai-generated'`. Up to 20 items per call;
|
|
30
|
+
each item succeeds or fails independently (`created`/`rejected`, with a reason).
|
|
31
|
+
Every created comment lands **unconfirmed** — the reviewer must confirm it in the
|
|
32
|
+
AIxisCheck sidebar before it counts as a real review comment.
|
|
24
33
|
|
|
25
34
|
## Setup
|
|
26
35
|
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// AIXIS_URL — Supabase project URL, e.g. https://<ref>.supabase.co
|
|
13
13
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
14
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
-
import { buildCreateDocBody, createDocShape, createRevisionRequestShape, edgeErrorMessage, functionUrl, getDocShape, listCommentsShape, listDocsShape, updateDocShape, } from "./lib.js";
|
|
15
|
+
import { buildCreateDocBody, createCommentsShape, createDocShape, createRevisionRequestShape, edgeErrorMessage, functionUrl, getDocShape, listCommentsShape, listDocsShape, updateDocShape, } from "./lib.js";
|
|
16
16
|
const AIXIS_TOKEN = process.env.AIXIS_TOKEN ?? "";
|
|
17
17
|
const AIXIS_URL = process.env.AIXIS_URL ?? "";
|
|
18
18
|
if (!AIXIS_TOKEN || !AIXIS_URL) {
|
|
@@ -43,7 +43,7 @@ function errorResult(e) {
|
|
|
43
43
|
const message = e instanceof Error ? e.message : String(e);
|
|
44
44
|
return { content: [{ type: "text", text: message }], isError: true };
|
|
45
45
|
}
|
|
46
|
-
const server = new McpServer({ name: "aixischeck-mcp", version: "0.
|
|
46
|
+
const server = new McpServer({ name: "aixischeck-mcp", version: "0.5.0" });
|
|
47
47
|
server.registerTool("list_workspaces", {
|
|
48
48
|
description: "List the AIxisCheck workspaces you can write to. Returns each workspace's id, name, and your role. Call this first to get a workspace_id for create_doc.",
|
|
49
49
|
inputSchema: {},
|
|
@@ -157,5 +157,31 @@ server.registerTool("update_doc", {
|
|
|
157
157
|
return errorResult(e);
|
|
158
158
|
}
|
|
159
159
|
});
|
|
160
|
+
server.registerTool("create_comments", {
|
|
161
|
+
description: "Bulk-create review comments on an AIxisCheck doc — e.g. from a transcript of a reviewer narrating feedback while reading (such as a Granola recording). Call get_doc FIRST to read the doc's actual content and section list: each item's selected_text must be an EXACT, verbatim substring of the target section — do not paraphrase. Set origin:'ai-transcribed' when the feedback came from a human transcript, or 'ai-generated' otherwise. Up to 20 items per call. Items that don't match verbatim (or match ambiguously — the same phrase appears more than once in that section) are rejected individually; the rest of the batch still succeeds. Every created comment lands UNCONFIRMED in the sidebar — the reviewer must confirm it there before it counts as a real review comment.",
|
|
162
|
+
inputSchema: createCommentsShape,
|
|
163
|
+
}, async (args) => {
|
|
164
|
+
try {
|
|
165
|
+
const data = await callEdge("mcp-create-comments", "POST", {
|
|
166
|
+
doc_id: args.doc_id,
|
|
167
|
+
origin: args.origin,
|
|
168
|
+
items: args.items,
|
|
169
|
+
});
|
|
170
|
+
const created = Array.isArray(data?.created) ? data.created : [];
|
|
171
|
+
const rejected = Array.isArray(data?.rejected) ? data.rejected : [];
|
|
172
|
+
const parts = [`Created ${created.length} comment(s).`];
|
|
173
|
+
if (rejected.length) {
|
|
174
|
+
const detail = rejected
|
|
175
|
+
.map((r) => `item ${r.item_index} (${r.reason})`)
|
|
176
|
+
.join(", ");
|
|
177
|
+
parts.push(`${rejected.length} rejected: ${detail}. Re-check each quote against the section's actual ` +
|
|
178
|
+
"text (via get_doc) and retry those items if needed.");
|
|
179
|
+
}
|
|
180
|
+
return { content: [{ type: "text", text: parts.join(" ") }] };
|
|
181
|
+
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
return errorResult(e);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
160
186
|
const transport = new StdioServerTransport();
|
|
161
187
|
await server.connect(transport);
|
package/dist/lib.js
CHANGED
|
@@ -47,6 +47,27 @@ export const updateDocShape = {
|
|
|
47
47
|
export const createRevisionRequestShape = {
|
|
48
48
|
doc_id: z.string().min(1, "doc_id is required"),
|
|
49
49
|
};
|
|
50
|
+
// create_comments (bulk comment import, 2026-07-09): write a batch of
|
|
51
|
+
// AI-matched comments onto a doc's existing sections. section_index is the
|
|
52
|
+
// 0-based index into get_doc's `sections` array (its sections[].id is this
|
|
53
|
+
// same index, stringified) — NOT a free-form id. selected_text must be an
|
|
54
|
+
// EXACT substring of that section's content; the edge function rejects
|
|
55
|
+
// items that don't match verbatim (or match ambiguously) rather than
|
|
56
|
+
// guessing. Every item in one call shares an `origin`, which becomes the
|
|
57
|
+
// created annotation's `source` and determines nothing else server-side —
|
|
58
|
+
// all AI-created comments land unconfirmed regardless of origin.
|
|
59
|
+
export const createCommentsShape = {
|
|
60
|
+
doc_id: z.string().min(1, "doc_id is required"),
|
|
61
|
+
origin: z.enum(["ai-transcribed", "ai-generated"]).describe("'ai-transcribed' if these comments were matched from a human transcript (e.g. a Granola recording of the reviewer narrating feedback while reading); 'ai-generated' if there was no human transcript source."),
|
|
62
|
+
items: z.array(z.object({
|
|
63
|
+
section_index: z.number().int().min(0).describe("0-based index into the sections array returned by get_doc (sections[].id is this same index, stringified)."),
|
|
64
|
+
selected_text: z.string().min(1).describe("An EXACT, verbatim substring of that section's content — read the doc with get_doc first; do not paraphrase."),
|
|
65
|
+
prefix_context: z.string().optional().describe("A few characters immediately before selected_text, to disambiguate if the phrase repeats in the section."),
|
|
66
|
+
suffix_context: z.string().optional().describe("A few characters immediately after selected_text, to disambiguate if the phrase repeats in the section."),
|
|
67
|
+
note: z.string().min(1, "note is required"),
|
|
68
|
+
})).min(1).max(20, "at most 20 comments per call — split larger batches into multiple calls"),
|
|
69
|
+
};
|
|
70
|
+
export const CreateCommentsSchema = z.object(createCommentsShape);
|
|
50
71
|
// Shape the POST body for the mcp-create-doc edge function, applying
|
|
51
72
|
// the doc_type='html' default and normalizing an absent title to ''.
|
|
52
73
|
export function buildCreateDocBody(input) {
|