@drawpro/mcp 0.6.0 → 0.6.3
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/server.js +88 -17
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -1411,7 +1411,7 @@ function api() {
|
|
|
1411
1411
|
return clientInstance;
|
|
1412
1412
|
}
|
|
1413
1413
|
var SESSION_ID = Math.random().toString(36).slice(2, 10);
|
|
1414
|
-
var VERSION = "0.6.
|
|
1414
|
+
var VERSION = "0.6.3";
|
|
1415
1415
|
var inFlight = [];
|
|
1416
1416
|
var cachedUser = null;
|
|
1417
1417
|
async function currentUser() {
|
|
@@ -1426,7 +1426,14 @@ async function unlockedKey() {
|
|
|
1426
1426
|
const key = loadKey(user.email);
|
|
1427
1427
|
if (!key) {
|
|
1428
1428
|
return {
|
|
1429
|
-
error:
|
|
1429
|
+
error: `This account (${user.email}) is locked, so names and contents cannot be read.
|
|
1430
|
+
|
|
1431
|
+
Ask the user to run this in a terminal:
|
|
1432
|
+
npx -y @drawpro/mcp login
|
|
1433
|
+
|
|
1434
|
+
It prompts for their passcode and stores the derived key in the OS keychain. They do NOT need to restart \u2014 once it completes, simply try the same tool again and it will work.
|
|
1435
|
+
|
|
1436
|
+
Never ask the user for their passcode here. It is the account's master secret and must not pass through this conversation.`
|
|
1430
1437
|
};
|
|
1431
1438
|
}
|
|
1432
1439
|
return { key };
|
|
@@ -1615,11 +1622,11 @@ tool(
|
|
|
1615
1622
|
);
|
|
1616
1623
|
function buildOrExplain(spec) {
|
|
1617
1624
|
const { scene, issues } = buildDiagram(spec);
|
|
1618
|
-
const
|
|
1619
|
-
if (
|
|
1625
|
+
const errors2 = issues.filter((i) => i.level === "error");
|
|
1626
|
+
if (errors2.length > 0) {
|
|
1620
1627
|
return {
|
|
1621
1628
|
ok: false,
|
|
1622
|
-
error: "The diagram was not created. Fix the spec and try again:\n" +
|
|
1629
|
+
error: "The diagram was not created. Fix the spec and try again:\n" + errors2.map((i) => ` error: ${i.message}`).join("\n")
|
|
1623
1630
|
};
|
|
1624
1631
|
}
|
|
1625
1632
|
const warnings = issues.filter((i) => i.level === "warning");
|
|
@@ -1719,7 +1726,9 @@ tool(
|
|
|
1719
1726
|
sheet_id: import_zod.z.string(),
|
|
1720
1727
|
edits: import_zod.z.array(
|
|
1721
1728
|
import_zod.z.object({
|
|
1722
|
-
find: import_zod.z.string().describe(
|
|
1729
|
+
find: import_zod.z.string().describe(
|
|
1730
|
+
"The element's current text, as read_sheet reports it. Whitespace is matched loosely, so the flattened single-line form read_sheet prints works even when the element itself contains line breaks."
|
|
1731
|
+
),
|
|
1723
1732
|
replace: import_zod.z.string()
|
|
1724
1733
|
})
|
|
1725
1734
|
).describe("Applied all-or-nothing: if any find has no match, nothing is written.")
|
|
@@ -1729,13 +1738,14 @@ tool(
|
|
|
1729
1738
|
if ("error" in unlocked) return text(unlocked.error);
|
|
1730
1739
|
const scene = await api().readSheet(workspace_id, sheet_id, unlocked.key);
|
|
1731
1740
|
const elements = scene.elements;
|
|
1741
|
+
const collapse = (t) => t.replace(/\s+/g, " ").trim();
|
|
1732
1742
|
const counts = /* @__PURE__ */ new Map();
|
|
1733
1743
|
const changedTextIds = /* @__PURE__ */ new Set();
|
|
1734
1744
|
for (const el of elements) {
|
|
1735
1745
|
if (el.type !== "text") continue;
|
|
1736
1746
|
const current = (el.originalText ?? el.text)?.trim();
|
|
1737
1747
|
if (current === void 0) continue;
|
|
1738
|
-
const edit = edits.find((e) => e.find.trim() === current);
|
|
1748
|
+
const edit = edits.find((e) => e.find.trim() === current) ?? edits.find((e) => collapse(e.find) === collapse(current));
|
|
1739
1749
|
if (!edit) continue;
|
|
1740
1750
|
el.text = edit.replace;
|
|
1741
1751
|
el.originalText = edit.replace;
|
|
@@ -1754,7 +1764,7 @@ tool(
|
|
|
1754
1764
|
const missed = edits.filter((e) => !counts.has(e.find));
|
|
1755
1765
|
if (missed.length > 0) {
|
|
1756
1766
|
return text(
|
|
1757
|
-
"Nothing was written. These strings matched no text element:\n" + missed.map((e) => ` ${JSON.stringify(e.find)}`).join("\n") + "\n\nRun read_sheet and copy the text
|
|
1767
|
+
"Nothing was written. These strings matched no text element:\n" + missed.map((e) => ` ${JSON.stringify(e.find)}`).join("\n") + "\n\nRun read_sheet and copy the text as it appears there. Line breaks and repeated spaces do not need to match \u2014 only the words do."
|
|
1758
1768
|
);
|
|
1759
1769
|
}
|
|
1760
1770
|
const user = await currentUser();
|
|
@@ -1854,6 +1864,44 @@ function summariseLog(file) {
|
|
|
1854
1864
|
}))
|
|
1855
1865
|
};
|
|
1856
1866
|
}
|
|
1867
|
+
function errors(file) {
|
|
1868
|
+
let rows;
|
|
1869
|
+
try {
|
|
1870
|
+
rows = (0, import_node_fs3.readFileSync)(file, "utf8").split("\n").filter(Boolean).flatMap((line) => {
|
|
1871
|
+
try {
|
|
1872
|
+
return [JSON.parse(line)];
|
|
1873
|
+
} catch {
|
|
1874
|
+
return [];
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
} catch {
|
|
1878
|
+
console.error(`Could not read ${file}`);
|
|
1879
|
+
process.exit(2);
|
|
1880
|
+
}
|
|
1881
|
+
const failures = rows.filter((r) => r.ok === false || r.refused);
|
|
1882
|
+
if (failures.length === 0) {
|
|
1883
|
+
console.log("No failures or refusals recorded.");
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1887
|
+
for (const r of failures) {
|
|
1888
|
+
const reason = String(r.error ?? "refused (the tool declined, see its message)").slice(0, 120);
|
|
1889
|
+
const acc = grouped.get(reason) ?? { n: 0, tools: /* @__PURE__ */ new Set(), last: "" };
|
|
1890
|
+
acc.n++;
|
|
1891
|
+
acc.tools.add(String(r.tool));
|
|
1892
|
+
acc.last = String(r.ts);
|
|
1893
|
+
grouped.set(reason, acc);
|
|
1894
|
+
}
|
|
1895
|
+
console.log(`${failures.length} of ${rows.length} calls failed or were refused
|
|
1896
|
+
`);
|
|
1897
|
+
for (const [reason, a] of [...grouped.entries()].sort((x, y) => y[1].n - x[1].n)) {
|
|
1898
|
+
console.log(` ${a.n}x ${[...a.tools].join(", ")}`);
|
|
1899
|
+
console.log(` ${reason}`);
|
|
1900
|
+
console.log(` last seen ${a.last}
|
|
1901
|
+
`);
|
|
1902
|
+
}
|
|
1903
|
+
console.log(" This reads your local log and sends nothing.");
|
|
1904
|
+
}
|
|
1857
1905
|
function stats(path, asJson) {
|
|
1858
1906
|
const file = path ?? logPath();
|
|
1859
1907
|
if (!file) {
|
|
@@ -1885,7 +1933,7 @@ function stats(path, asJson) {
|
|
|
1885
1933
|
console.log(
|
|
1886
1934
|
"\n No ids, account details, or diagram content above \u2014 safe to paste into a bug report."
|
|
1887
1935
|
);
|
|
1888
|
-
console.log(" Add --json for a machine-readable copy.");
|
|
1936
|
+
console.log(" Add --json for a machine-readable copy, or --errors to see why calls failed.");
|
|
1889
1937
|
}
|
|
1890
1938
|
function buildReport() {
|
|
1891
1939
|
const file = logPath();
|
|
@@ -1922,15 +1970,29 @@ async function telemetry(action) {
|
|
|
1922
1970
|
if (action === "on") {
|
|
1923
1971
|
writeConfig({ telemetry: "on" });
|
|
1924
1972
|
console.log(`Telemetry on. Usage is recorded to ${logPath()}`);
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1973
|
+
const built = buildReport();
|
|
1974
|
+
if (!built) {
|
|
1975
|
+
console.log("\nNothing recorded yet. The first report goes out once you have used the tools.");
|
|
1976
|
+
console.log("Turn it off any time with: drawpro-mcp telemetry off");
|
|
1977
|
+
return;
|
|
1978
|
+
}
|
|
1979
|
+
console.log("\nSending this now, and roughly once a day after:\n");
|
|
1980
|
+
console.log(JSON.stringify(built, null, 2));
|
|
1981
|
+
const { ok, detail } = await sendReport(built);
|
|
1982
|
+
if (ok) {
|
|
1983
|
+
writeConfig({ lastReportAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
1984
|
+
console.log("\nSent.");
|
|
1985
|
+
} else {
|
|
1986
|
+
console.log(`
|
|
1987
|
+
Could not send it (${detail}). Telemetry is still on and it will retry.`);
|
|
1988
|
+
}
|
|
1989
|
+
console.log("Turn it off any time with: drawpro-mcp telemetry off");
|
|
1930
1990
|
return;
|
|
1931
1991
|
}
|
|
1932
|
-
|
|
1933
|
-
`);
|
|
1992
|
+
const last = readConfig().lastReportAt;
|
|
1993
|
+
console.log(`Telemetry is ${telemetryEnabled() ? "ON" : "OFF"}.`);
|
|
1994
|
+
console.log(last ? `Last report sent ${last}.
|
|
1995
|
+
` : "No report has been sent yet.\n");
|
|
1934
1996
|
console.log("If enabled, this is the entire payload \u2014 tool counts and timings,");
|
|
1935
1997
|
console.log("no account, no token, no workspace or sheet ids, nothing drawn:\n");
|
|
1936
1998
|
console.log(
|
|
@@ -2094,7 +2156,16 @@ async function main() {
|
|
|
2094
2156
|
}
|
|
2095
2157
|
if (command === "stats") {
|
|
2096
2158
|
const rest = process.argv.slice(3);
|
|
2097
|
-
|
|
2159
|
+
const file = rest.find((a) => !a.startsWith("--")) ?? logPath();
|
|
2160
|
+
if (rest.includes("--errors")) {
|
|
2161
|
+
if (!file) {
|
|
2162
|
+
console.error("No usage is being recorded, so there is nothing to explain.");
|
|
2163
|
+
process.exit(2);
|
|
2164
|
+
}
|
|
2165
|
+
errors(file);
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
stats(file, rest.includes("--json"));
|
|
2098
2169
|
return;
|
|
2099
2170
|
}
|
|
2100
2171
|
if (command && command !== "serve") {
|