things-api 0.17.0 → 0.19.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 +16 -9
- package/deputy/AppIcon.icns +0 -0
- package/deputy/VERSION +1 -0
- package/deputy/helpers-Info.plist +27 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/CodeResources +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Info.plist +22 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/MacOS/things-reader +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/Resources/AppIcon.icns +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Helpers/things-reader.app/Contents/_CodeSignature/CodeResources +128 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Info.plist +27 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/MacOS/things-deputy +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/Resources/AppIcon.icns +0 -0
- package/deputy/prebuilt/Things API Helper.app/Contents/_CodeSignature/CodeResources +137 -0
- package/deputy/reader/Info.plist +22 -0
- package/deputy/reader/entitlements.plist +45 -0
- package/deputy/reader/main.swift +580 -0
- package/deputy/src/main.swift +133 -0
- package/deputy/src/osascript.swift +102 -0
- package/deputy/src/server.swift +373 -0
- package/deputy/src/sqlite.swift +121 -0
- package/deputy/src/tcc.swift +58 -0
- package/dist/audit/schema.d.ts +13 -1
- package/dist/audit/schema.js.map +1 -1
- package/dist/capability.d.ts +160 -0
- package/dist/capability.js +416 -0
- package/dist/capability.js.map +1 -0
- package/dist/cli/commands/doctor.js +181 -2
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/helpers.d.ts +17 -0
- package/dist/cli/commands/helpers.js +275 -0
- package/dist/cli/commands/helpers.js.map +1 -0
- package/dist/cli/commands/op-result.d.ts +15 -0
- package/dist/cli/commands/op-result.js +45 -0
- package/dist/cli/commands/op-result.js.map +1 -0
- package/dist/cli/commands/reads.js +47 -1
- package/dist/cli/commands/reads.js.map +1 -1
- package/dist/cli/commands/setup.d.ts +9 -0
- package/dist/cli/commands/setup.js +63 -83
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/writes.d.ts +1 -1
- package/dist/cli/commands/writes.js +262 -109
- package/dist/cli/commands/writes.js.map +1 -1
- package/dist/cli/dry-run.d.ts +1 -1
- package/dist/cli/dry-run.js +1 -1
- package/dist/cli/glyphs.d.ts +23 -4
- package/dist/cli/glyphs.js +46 -19
- package/dist/cli/glyphs.js.map +1 -1
- package/dist/cli/help.d.ts +1 -1
- package/dist/cli/help.js +53 -27
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/helpers-check.d.ts +10 -0
- package/dist/cli/helpers-check.js +59 -0
- package/dist/cli/helpers-check.js.map +1 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/period.d.ts +3 -1
- package/dist/cli/period.js +1 -1
- package/dist/cli/period.js.map +1 -1
- package/dist/cli/read-driver.js +26 -1
- package/dist/cli/read-driver.js.map +1 -1
- package/dist/cli/render.d.ts +31 -4
- package/dist/cli/render.js +127 -15
- package/dist/cli/render.js.map +1 -1
- package/dist/client.d.ts +22 -2
- package/dist/client.js +52 -15
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +31 -0
- package/dist/config.js +9 -0
- package/dist/config.js.map +1 -1
- package/dist/contracts.d.ts +6 -2
- package/dist/contracts.js +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/db/baselines/db-v26.js +8 -4
- package/dist/db/baselines/db-v26.js.map +1 -1
- package/dist/db/baselines/db-v27.d.ts +42 -0
- package/dist/db/baselines/db-v27.js +47 -0
- package/dist/db/baselines/db-v27.js.map +1 -0
- package/dist/db/baselines/index.js +2 -1
- package/dist/db/baselines/index.js.map +1 -1
- package/dist/db/locate.d.ts +4 -1
- package/dist/db/locate.js +3 -1
- package/dist/db/locate.js.map +1 -1
- package/dist/db/schema.d.ts +1 -1
- package/dist/db/schema.js +9 -0
- package/dist/db/schema.js.map +1 -1
- package/dist/deputy/bridge-worker.d.ts +1 -0
- package/dist/deputy/bridge-worker.js +79 -0
- package/dist/deputy/bridge-worker.js.map +1 -0
- package/dist/deputy/bridge.d.ts +14 -0
- package/dist/deputy/bridge.js +70 -0
- package/dist/deputy/bridge.js.map +1 -0
- package/dist/deputy/client.d.ts +19 -0
- package/dist/deputy/client.js +104 -0
- package/dist/deputy/client.js.map +1 -0
- package/dist/deputy/db-facade.d.ts +18 -0
- package/dist/deputy/db-facade.js +71 -0
- package/dist/deputy/db-facade.js.map +1 -0
- package/dist/deputy/files.d.ts +1 -0
- package/dist/deputy/files.js +16 -0
- package/dist/deputy/files.js.map +1 -0
- package/dist/deputy/install.d.ts +319 -0
- package/dist/deputy/install.js +1239 -0
- package/dist/deputy/install.js.map +1 -0
- package/dist/deputy/notice.d.ts +6 -0
- package/dist/deputy/notice.js +28 -0
- package/dist/deputy/notice.js.map +1 -0
- package/dist/deputy/notices.d.ts +31 -0
- package/dist/deputy/notices.js +94 -0
- package/dist/deputy/notices.js.map +1 -0
- package/dist/deputy/osa.d.ts +21 -0
- package/dist/deputy/osa.js +72 -0
- package/dist/deputy/osa.js.map +1 -0
- package/dist/deputy/protocol.d.ts +140 -0
- package/dist/deputy/protocol.js +143 -0
- package/dist/deputy/protocol.js.map +1 -0
- package/dist/deputy/routing.d.ts +84 -0
- package/dist/deputy/routing.js +422 -0
- package/dist/deputy/routing.js.map +1 -0
- package/dist/deputy/shortcuts-exec.d.ts +10 -0
- package/dist/deputy/shortcuts-exec.js +50 -0
- package/dist/deputy/shortcuts-exec.js.map +1 -0
- package/dist/diagnose.d.ts +57 -2
- package/dist/diagnose.js +105 -3
- package/dist/diagnose.js.map +1 -1
- package/dist/direct-setup.d.ts +78 -0
- package/dist/direct-setup.js +484 -0
- package/dist/direct-setup.js.map +1 -0
- package/dist/host-access.d.ts +63 -0
- package/dist/host-access.js +165 -0
- package/dist/host-access.js.map +1 -0
- package/dist/index.d.ts +31 -5
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +27 -1
- package/dist/mcp/server.js +300 -97
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/mappers.d.ts +12 -3
- package/dist/model/mappers.js +6 -1
- package/dist/model/mappers.js.map +1 -1
- package/dist/model/recurrence.d.ts +5 -1
- package/dist/model/recurrence.js +19 -1
- package/dist/model/recurrence.js.map +1 -1
- package/dist/model/template-projection.d.ts +26 -0
- package/dist/model/template-projection.js +113 -0
- package/dist/model/template-projection.js.map +1 -0
- package/dist/op-result.d.ts +55 -0
- package/dist/op-result.js +161 -0
- package/dist/op-result.js.map +1 -0
- package/dist/read/detail.js +7 -1
- package/dist/read/detail.js.map +1 -1
- package/dist/read/filter-contract.d.ts +1 -1
- package/dist/read/filter-contract.js +4 -0
- package/dist/read/filter-contract.js.map +1 -1
- package/dist/read/queries.js +8 -1
- package/dist/read/queries.js.map +1 -1
- package/dist/read/shape.js +4 -0
- package/dist/read/shape.js.map +1 -1
- package/dist/read/views.d.ts +35 -4
- package/dist/read/views.js +120 -18
- package/dist/read/views.js.map +1 -1
- package/dist/session-grant.d.ts +48 -0
- package/dist/session-grant.js +170 -0
- package/dist/session-grant.js.map +1 -0
- package/dist/surface-copy.d.ts +8 -0
- package/dist/surface-copy.js +8 -0
- package/dist/surface-copy.js.map +1 -1
- package/dist/sync-health.js +6 -2
- package/dist/sync-health.js.map +1 -1
- package/dist/wizard.d.ts +58 -0
- package/dist/wizard.js +167 -0
- package/dist/wizard.js.map +1 -0
- package/dist/write/accessibility-probe.js +4 -2
- package/dist/write/accessibility-probe.js.map +1 -1
- package/dist/write/automation-probe.js +5 -1
- package/dist/write/automation-probe.js.map +1 -1
- package/dist/write/availability.d.ts +1 -1
- package/dist/write/availability.js +10 -6
- package/dist/write/availability.js.map +1 -1
- package/dist/write/batch.d.ts +20 -0
- package/dist/write/batch.js +94 -15
- package/dist/write/batch.js.map +1 -1
- package/dist/write/capabilities.d.ts +9 -0
- package/dist/write/capabilities.js +2 -0
- package/dist/write/capabilities.js.map +1 -1
- package/dist/write/clear-reminder.js +1 -1
- package/dist/write/clear-reminder.js.map +1 -1
- package/dist/write/commands.d.ts +12 -1
- package/dist/write/commands.js +170 -237
- package/dist/write/commands.js.map +1 -1
- package/dist/write/experimental.d.ts +19 -0
- package/dist/write/experimental.js +50 -0
- package/dist/write/experimental.js.map +1 -1
- package/dist/write/failure-hints.d.ts +3 -1
- package/dist/write/failure-hints.js.map +1 -1
- package/dist/write/guards.js +19 -2
- package/dist/write/guards.js.map +1 -1
- package/dist/write/lock.d.ts +16 -13
- package/dist/write/lock.js +55 -0
- package/dist/write/lock.js.map +1 -1
- package/dist/write/make-repeating-project.js +9 -13
- package/dist/write/make-repeating-project.js.map +1 -1
- package/dist/write/move.js +14 -6
- package/dist/write/move.js.map +1 -1
- package/dist/write/operations.d.ts +64 -25
- package/dist/write/operations.js +7 -0
- package/dist/write/operations.js.map +1 -1
- package/dist/write/opid.d.ts +14 -0
- package/dist/write/opid.js +22 -0
- package/dist/write/opid.js.map +1 -1
- package/dist/write/param-schema.d.ts +124 -0
- package/dist/write/param-schema.js +747 -0
- package/dist/write/param-schema.js.map +1 -0
- package/dist/write/pipeline.d.ts +78 -2
- package/dist/write/pipeline.js +249 -4
- package/dist/write/pipeline.js.map +1 -1
- package/dist/write/pre-state.d.ts +8 -0
- package/dist/write/pre-state.js +62 -17
- package/dist/write/pre-state.js.map +1 -1
- package/dist/write/promote-clone.js +635 -367
- package/dist/write/promote-clone.js.map +1 -1
- package/dist/write/reorder.js +95 -29
- package/dist/write/reorder.js.map +1 -1
- package/dist/write/repeat-anchor.d.ts +30 -0
- package/dist/write/repeat-anchor.js +166 -1
- package/dist/write/repeat-anchor.js.map +1 -1
- package/dist/write/repeat-rule.d.ts +28 -1
- package/dist/write/repeat-rule.js +64 -4
- package/dist/write/repeat-rule.js.map +1 -1
- package/dist/write/resolution-timestamps.d.ts +11 -0
- package/dist/write/resolution-timestamps.js +83 -38
- package/dist/write/resolution-timestamps.js.map +1 -1
- package/dist/write/reversibility.js +4 -0
- package/dist/write/reversibility.js.map +1 -1
- package/dist/write/template-mutation.d.ts +137 -0
- package/dist/write/template-mutation.js +451 -0
- package/dist/write/template-mutation.js.map +1 -0
- package/dist/write/undo.js +10 -20
- package/dist/write/undo.js.map +1 -1
- package/dist/write/update-fields.d.ts +151 -0
- package/dist/write/update-fields.js +427 -0
- package/dist/write/update-fields.js.map +1 -0
- package/dist/write/vectors/applescript.js +10 -16
- package/dist/write/vectors/applescript.js.map +1 -1
- package/dist/write/vectors/registry.d.ts +1 -1
- package/dist/write/vectors/registry.js +10 -2
- package/dist/write/vectors/registry.js.map +1 -1
- package/dist/write/vectors/session-reachability.d.ts +55 -0
- package/dist/write/vectors/session-reachability.js +56 -8
- package/dist/write/vectors/session-reachability.js.map +1 -1
- package/dist/write/vectors/shortcuts.js +7 -16
- package/dist/write/vectors/shortcuts.js.map +1 -1
- package/dist/write/vectors/simulator.d.ts +16 -1
- package/dist/write/vectors/simulator.js +68 -12
- package/dist/write/vectors/simulator.js.map +1 -1
- package/dist/write/vectors/types.d.ts +124 -1
- package/dist/write/vectors/ui-recipes.d.ts +19 -0
- package/dist/write/vectors/ui-recipes.js +158 -38
- package/dist/write/vectors/ui-recipes.js.map +1 -1
- package/dist/write/vectors/ui.d.ts +89 -0
- package/dist/write/vectors/ui.js +504 -49
- package/dist/write/vectors/ui.js.map +1 -1
- package/dist/write/verify/delta.d.ts +21 -0
- package/dist/write/verify/delta.js.map +1 -1
- package/dist/write/verify/poller.d.ts +13 -0
- package/dist/write/verify/poller.js +38 -0
- package/dist/write/verify/poller.js.map +1 -1
- package/package.json +9 -1
- package/schema/envelope.schema.json +5 -3
- package/scripts/build-helpers.sh +109 -0
- package/scripts/deputy-cert-setup.sh +60 -0
- package/skills/things-cli/SKILL.md +12 -5
- package/skills/things-cli/references/contracts.md +1 -0
- package/skills/things-cli/references/data-model.md +3 -0
- package/skills/things-cli/references/errors.md +5 -1
- package/skills/things-cli/references/gui.md +1 -0
- package/skills/things-cli/references/repeating.md +75 -0
- package/skills/things-cli/references/tag-cleanup.md +64 -0
package/dist/mcp/server.js
CHANGED
|
@@ -16,7 +16,64 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
-
import { ALL_DESC, AREA_FILTER_DESC, AREA_LIMIT_DESC, AREA_PREVIEW_LIMIT, blockedCode, capabilitiesTable, DATE_FORMAT, DEFAULT_LIST_LIMIT, diagnose, FILTER_CONTRACT, FULL_DESC, hasTagPresence, isLooseRef, isValidTimeZone, LIMIT_DESC, looseShadowNotice, MCP_WHEN_LABELS, mutationWireData, noUuidMatch, omitEmpty, OMIT_EMPTY_NOTE, OP_ID_RE, OPERATION_KINDS, openThings, PKG_VERSION, PROJECT_LIMIT_DESC, PROJECT_PREVIEW_LIMIT, REF_FORMAT, REF_RULE_NOTE, ReferenceResolutionError, REMINDER_FORMAT, RESOLUTION_DATE_FORMAT, schemaWarnings, shapeReadPayload, withTodayBucketTotals, withAreaBucketTotals, withSectionTotals, withUpcomingBlockTotals, splitWhenSugar, tagFilterFields, tagFlagConflict, validateViewArgs, verifyFailedCode, WHEN_VALUES, } from "../index.js";
|
|
19
|
+
import { ALL_DESC, AREA_FILTER_DESC, AREA_LIMIT_DESC, AREA_PREVIEW_LIMIT, blockedCode, buildUpdatePatch, capabilitiesTable, DATE_FORMAT, DEFAULT_LIST_LIMIT, diagnose, FILTER_CONTRACT, FULL_DESC, hasTagPresence, isLooseRef, isValidTimeZone, LIMIT_DESC, looseShadowNotice, MCP_UPDATE_LABELS, MCP_WHEN_LABELS, mutationWireData, NOTES_FORMAT, noUuidMatch, omitEmpty, OMIT_EMPTY_NOTE, OP_ID_RE, opResult, OPERATION_KINDS, openThings, readAllowed, readCapability, uiAllowed, uiCapability, writeAllowed, writeCapability, PKG_VERSION, PROJECT_LIMIT_DESC, PROJECT_PREVIEW_LIMIT, REF_FORMAT, REF_RULE_NOTE, ReferenceResolutionError, REMINDER_FORMAT, RESOLUTION_DATE_FORMAT, schemaWarnings, shapeReadPayload, withTodayBucketTotals, withAreaBucketTotals, withSectionTotals, withUpcomingBlockTotals, splitWhenSugar, tagFilterFields, tagFlagConflict, validateViewArgs, verifyFailedCode, WHEN_VALUES, } from "../index.js";
|
|
20
|
+
/** Tools that mutate through the app. Everything unlisted is a plain read. */
|
|
21
|
+
const WRITE_TOOLS = new Set([
|
|
22
|
+
"add_todo",
|
|
23
|
+
"update",
|
|
24
|
+
"set_status",
|
|
25
|
+
"move_todo",
|
|
26
|
+
"set_tags",
|
|
27
|
+
"edit_checklist",
|
|
28
|
+
"delete",
|
|
29
|
+
"restore_item",
|
|
30
|
+
"heading",
|
|
31
|
+
"clear_reminder",
|
|
32
|
+
"duplicate_item",
|
|
33
|
+
"clone_item",
|
|
34
|
+
"add_project",
|
|
35
|
+
"move_project",
|
|
36
|
+
"add_area",
|
|
37
|
+
"add_tag",
|
|
38
|
+
"log_now",
|
|
39
|
+
"run_operation",
|
|
40
|
+
"batch",
|
|
41
|
+
"reorder",
|
|
42
|
+
"undo",
|
|
43
|
+
]);
|
|
44
|
+
/**
|
|
45
|
+
* Tools with NO headless spelling at all — every path through them drives the
|
|
46
|
+
* Things window. A tool that merely *can* reach a GUI op (`heading`'s promote,
|
|
47
|
+
* `reorder`'s areas) is NOT listed: those have working headless branches, and
|
|
48
|
+
* the pipeline's own Article IV gate refuses the GUI ones per call.
|
|
49
|
+
*/
|
|
50
|
+
const UI_TOOLS = new Set(["repeat", "convert_to_project"]);
|
|
51
|
+
/** The always-available diagnostics. */
|
|
52
|
+
const DIAGNOSTIC_TOOLS = new Set(["doctor", "capabilities", "op_result"]);
|
|
53
|
+
function toolClass(name) {
|
|
54
|
+
if (DIAGNOSTIC_TOOLS.has(name))
|
|
55
|
+
return "none";
|
|
56
|
+
if (UI_TOOLS.has(name))
|
|
57
|
+
return "ui";
|
|
58
|
+
if (WRITE_TOOLS.has(name))
|
|
59
|
+
return "write";
|
|
60
|
+
return "read";
|
|
61
|
+
}
|
|
62
|
+
/** The one remediation every baked refusal ends with. */
|
|
63
|
+
const RESTART_REMEDIATION = "this server surveyed its permissions once at startup and does not re-check them, because a " +
|
|
64
|
+
"grant made now would attach to whoever launched it: stop this server, run the setup command " +
|
|
65
|
+
"above at the machine, then start it again.";
|
|
66
|
+
/** The vector classes this server can actually serve, for the instructions. */
|
|
67
|
+
function availableClasses(capability) {
|
|
68
|
+
const available = [];
|
|
69
|
+
if (readAllowed(capability.read))
|
|
70
|
+
available.push("read");
|
|
71
|
+
if (writeAllowed(capability.write))
|
|
72
|
+
available.push("write");
|
|
73
|
+
if (uiAllowed(capability.ui))
|
|
74
|
+
available.push("gui-driving");
|
|
75
|
+
return available;
|
|
76
|
+
}
|
|
20
77
|
function jsonResult(data) {
|
|
21
78
|
return { content: [{ type: "text", text: JSON.stringify(data) }] };
|
|
22
79
|
}
|
|
@@ -402,7 +459,7 @@ const tagLabel = (t) => t.parent === null ? t.title : `${t.parent} > ${t.title}`
|
|
|
402
459
|
* names without a discovery round-trip. Degrades to conventions-only when
|
|
403
460
|
* the database is not readable.
|
|
404
461
|
*/
|
|
405
|
-
function buildInstructions(getClient) {
|
|
462
|
+
function buildInstructions(getClient, capability) {
|
|
406
463
|
const lines = [
|
|
407
464
|
"This server reads and modifies the user's Things 3 data: to-dos, projects, areas, and tags.",
|
|
408
465
|
"",
|
|
@@ -418,6 +475,8 @@ function buildInstructions(getClient) {
|
|
|
418
475
|
`- Scheduling vocabulary: when = ${WHEN_VALUES}; deadlines are ${DATE_FORMAT}; reminders ` +
|
|
419
476
|
`are ${REMINDER_FORMAT}. Resolve relative calendar phrases against the Calendar context ` +
|
|
420
477
|
`below (or a date-sensitive read result's meta.clock.today), then pass the explicit date.`,
|
|
478
|
+
`- Notes vocabulary: the notes field is ${NOTES_FORMAT}. Newlines are kept as written, so ` +
|
|
479
|
+
`pass a multi-line body directly rather than flattening it into one line.`,
|
|
421
480
|
"- Every write tool accepts dry_run: true to preview the change without applying it. " +
|
|
422
481
|
"A preview creates no state, so later calls cannot reference an item that only appeared in " +
|
|
423
482
|
"a dry-run result. Operations with cascading or permanent effects require the explicit " +
|
|
@@ -430,6 +489,27 @@ function buildInstructions(getClient) {
|
|
|
430
489
|
"- For capped reads, pass limit to cap rows or all: true for everything; if both are set, all wins.",
|
|
431
490
|
`- Read results are compact: ${OMIT_EMPTY_NOTE}`,
|
|
432
491
|
];
|
|
492
|
+
// What this server may do, decided once at startup and true for its whole
|
|
493
|
+
// life (permissions doctrine, Article II). Stated up front so an agent plans
|
|
494
|
+
// around the real surface instead of discovering it one refusal at a time.
|
|
495
|
+
const available = availableClasses(capability);
|
|
496
|
+
const missing = [];
|
|
497
|
+
if (!readAllowed(capability.read))
|
|
498
|
+
missing.push(`reads — ${capability.read.detail}`);
|
|
499
|
+
if (!writeAllowed(capability.write))
|
|
500
|
+
missing.push(`changes — ${capability.write.detail}`);
|
|
501
|
+
if (!uiAllowed(capability.ui))
|
|
502
|
+
missing.push(`GUI-driven operations — ${capability.ui.detail}`);
|
|
503
|
+
lines.push("", "Permissions (checked once at server start, fixed for this server's lifetime):", `- Available here: ${available.length > 0 ? available.join(", ") : "nothing — every tool below refuses"}.`, ...(missing.length > 0
|
|
504
|
+
? [
|
|
505
|
+
`- Unavailable here: ${missing.join("; ")}.`,
|
|
506
|
+
"- Tools needing an unavailable capability refuse immediately rather than trying and " +
|
|
507
|
+
"raising a macOS permission dialog nobody is present to answer. Remediation for each " +
|
|
508
|
+
"is in its refusal. These grants attach to whichever process launched this server, so " +
|
|
509
|
+
"they cannot change while it runs: the fix is always stop the server, run the named " +
|
|
510
|
+
"setup command at the machine, start it again.",
|
|
511
|
+
]
|
|
512
|
+
: ["- Nothing is missing; no tool will refuse for want of a permission."]));
|
|
433
513
|
try {
|
|
434
514
|
const c = getClient();
|
|
435
515
|
// Under a container scope the inventory below is already limited to in-scope
|
|
@@ -499,7 +579,83 @@ export function createThingsMcpServer(options = {}) {
|
|
|
499
579
|
});
|
|
500
580
|
return client;
|
|
501
581
|
};
|
|
502
|
-
|
|
582
|
+
// THE STARTUP BAKE (permissions doctrine, Article II). One prompt-free survey
|
|
583
|
+
// before the transport is connected; the verdict then shapes the instructions
|
|
584
|
+
// and gates the tools, and is never re-taken.
|
|
585
|
+
const capability = options.capability ?? {
|
|
586
|
+
read: readCapability(options.dbPath !== undefined ? { dbPath: options.dbPath } : {}),
|
|
587
|
+
write: writeCapability(),
|
|
588
|
+
ui: uiCapability(),
|
|
589
|
+
};
|
|
590
|
+
const warn = options.onStartupWarning ?? ((line) => process.stderr.write(`${line}\n`));
|
|
591
|
+
const unavailable = [
|
|
592
|
+
...(readAllowed(capability.read) ? [] : ["reads"]),
|
|
593
|
+
...(writeAllowed(capability.write) ? [] : ["changes"]),
|
|
594
|
+
...(uiAllowed(capability.ui) ? [] : ["GUI-driven operations"]),
|
|
595
|
+
];
|
|
596
|
+
if (unavailable.length > 0) {
|
|
597
|
+
warn(`things-api MCP: ${unavailable.join(", ")} unavailable on this machine — ` +
|
|
598
|
+
`${capability.read.detail}; ${capability.write.detail}; ${capability.ui.detail}. ` +
|
|
599
|
+
"Tools needing them refuse; restart this server after running setup.");
|
|
600
|
+
}
|
|
601
|
+
const server = new McpServer({ name: "things-api", version: PKG_VERSION }, { instructions: buildInstructions(getClient, capability) });
|
|
602
|
+
/**
|
|
603
|
+
* The baked refusal for one tool class, or null when the class is served.
|
|
604
|
+
* Article II in one function: the answer is the STARTUP verdict, quoted, with
|
|
605
|
+
* the setup command and the restart rule — never a fresh probe, and never a
|
|
606
|
+
* dispatch that could put a dialog on screen.
|
|
607
|
+
*/
|
|
608
|
+
const bakedRefusal = (name) => {
|
|
609
|
+
switch (toolClass(name)) {
|
|
610
|
+
case "none":
|
|
611
|
+
return null;
|
|
612
|
+
case "read":
|
|
613
|
+
return readAllowed(capability.read)
|
|
614
|
+
? null
|
|
615
|
+
: errorResult({
|
|
616
|
+
code: "environment",
|
|
617
|
+
message: `this tool reads the Things database, and ${capability.read.detail}`,
|
|
618
|
+
remediation: [...capability.read.remediation, RESTART_REMEDIATION].join("; "),
|
|
619
|
+
});
|
|
620
|
+
case "write":
|
|
621
|
+
// A write read-verifies, so it needs both classes; name the one that
|
|
622
|
+
// is actually missing rather than a generic "cannot write".
|
|
623
|
+
if (!readAllowed(capability.read)) {
|
|
624
|
+
return errorResult({
|
|
625
|
+
code: "environment",
|
|
626
|
+
message: `this tool changes Things data and verifies the result by reading it back, and ${capability.read.detail}`,
|
|
627
|
+
remediation: [...capability.read.remediation, RESTART_REMEDIATION].join("; "),
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
return writeAllowed(capability.write)
|
|
631
|
+
? null
|
|
632
|
+
: errorResult({
|
|
633
|
+
code: "environment",
|
|
634
|
+
message: `this tool changes Things data by driving the app, and ${capability.write.detail}`,
|
|
635
|
+
remediation: [...capability.write.remediation, RESTART_REMEDIATION].join("; "),
|
|
636
|
+
});
|
|
637
|
+
case "ui":
|
|
638
|
+
return uiAllowed(capability.ui)
|
|
639
|
+
? null
|
|
640
|
+
: errorResult({
|
|
641
|
+
code: "environment",
|
|
642
|
+
message: `this tool is delivered by driving the Things window, and ${capability.ui.detail}`,
|
|
643
|
+
remediation: [...capability.ui.remediation, RESTART_REMEDIATION].join("; "),
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
/**
|
|
648
|
+
* `server.registerTool` with the baked gate in front of every callback. Every
|
|
649
|
+
* registration below goes through this, so a tool cannot be added without a
|
|
650
|
+
* class — {@link toolClass} defaults an unlisted name to `read`, the weakest
|
|
651
|
+
* useful assumption, rather than to ungated.
|
|
652
|
+
*/
|
|
653
|
+
const registerTool = ((name, config, cb) => server.registerTool(name, config, ((...args) => {
|
|
654
|
+
const refusal = bakedRefusal(name);
|
|
655
|
+
if (refusal !== null)
|
|
656
|
+
return refusal;
|
|
657
|
+
return cb(...args);
|
|
658
|
+
})));
|
|
503
659
|
// The audit author for every write on this connection, derived once from the
|
|
504
660
|
// client's handshake identity (clientInfo.name). Read per call: clientInfo is
|
|
505
661
|
// populated when the initialize handshake completes, before any tool can run.
|
|
@@ -608,14 +764,18 @@ export function createThingsMcpServer(options = {}) {
|
|
|
608
764
|
return item.type;
|
|
609
765
|
};
|
|
610
766
|
// ------------------------------------------------------------------ reads
|
|
611
|
-
|
|
767
|
+
registerTool("read_view", {
|
|
612
768
|
description: "Read a Things list as the app presents it: today (two children buckets — " +
|
|
613
769
|
"children.today and children.evening, evening expires daily; the whole-view " +
|
|
614
770
|
"count due/overdue vs. other rides the result's second block), inbox, anytime, " +
|
|
615
771
|
"upcoming, someday, logbook, trash, or deadlines (a flat items list of everything " +
|
|
616
772
|
"carrying a deadline — to-dos and projects — in deadline order, most-overdue first; " +
|
|
617
773
|
"repeating items appear at their next occurrence's projected deadline; scope with " +
|
|
618
|
-
"today/overdue/project/area/tag)
|
|
774
|
+
"today/overdue/project/area/tag), or repeaters (every repeating series in the " +
|
|
775
|
+
"library — to-dos and projects — each carrying its decoded rule under " +
|
|
776
|
+
"repeating.rule; series appear in no other view, which show the occurrences a " +
|
|
777
|
+
"series spawns instead; paused and ended series are included; ordered by next " +
|
|
778
|
+
"occurrence, the ones with none last). For upcoming, " +
|
|
619
779
|
"horizon > 1 also includes future occurrences of repeating items (up to 10 each). " +
|
|
620
780
|
"anytime/someday return sections in canonical order (area + items; null area = the " +
|
|
621
781
|
"top-level block); children of someday/future-scheduled projects are excluded " +
|
|
@@ -639,6 +799,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
639
799
|
"logbook",
|
|
640
800
|
"trash",
|
|
641
801
|
"deadlines",
|
|
802
|
+
"repeaters",
|
|
642
803
|
]),
|
|
643
804
|
...tagFilterShape,
|
|
644
805
|
...tzShape,
|
|
@@ -854,10 +1015,14 @@ export function createThingsMcpServer(options = {}) {
|
|
|
854
1015
|
});
|
|
855
1016
|
return truncatedResult(shapeReadPayload("deadlines", items, full, c.refPromoter()), truncation);
|
|
856
1017
|
}
|
|
1018
|
+
case "repeaters": {
|
|
1019
|
+
const { items, truncation } = c.read.repeaters({ ...filter, ...zone, limit });
|
|
1020
|
+
return truncatedResult(shapeReadPayload("repeaters", items, full, c.refPromoter()), truncation);
|
|
1021
|
+
}
|
|
857
1022
|
}
|
|
858
1023
|
}, args.tz, () => (filterMeta !== undefined ? { filter: filterMeta } : undefined), () => looseAreaWarnings(getClient(), args.area));
|
|
859
1024
|
});
|
|
860
|
-
|
|
1025
|
+
registerTool("search", {
|
|
861
1026
|
description: "Find items by title/notes substring. Returns open, untrashed items by default; " +
|
|
862
1027
|
"include more with logged/trashed/all. Scope with project/area/tag — scope " +
|
|
863
1028
|
"references must name existing items. " +
|
|
@@ -920,7 +1085,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
920
1085
|
});
|
|
921
1086
|
return truncatedResult(shapeReadPayload("search", items, args.full === true, getClient().refPromoter()), truncation);
|
|
922
1087
|
}, args.tz, undefined, () => looseAreaWarnings(getClient(), args.area)));
|
|
923
|
-
|
|
1088
|
+
registerTool("changes_since", {
|
|
924
1089
|
description: "List items created or modified since a moment — including trashed, logged, and " +
|
|
925
1090
|
"repeating items (inspect each item's fields to tell them apart). Edits to tags, " +
|
|
926
1091
|
"areas, and checklist items do not mark the containing item as modified. " +
|
|
@@ -950,7 +1115,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
950
1115
|
});
|
|
951
1116
|
return truncatedResult(shapeReadPayload("changes", items, args.full === true, getClient().refPromoter()), truncation);
|
|
952
1117
|
}, args.tz));
|
|
953
|
-
|
|
1118
|
+
registerTool("get_item", {
|
|
954
1119
|
description: "Full detail for one item by uuid: notes, schedule, reminder, deadline, tags " +
|
|
955
1120
|
"(direct and inherited), checklist with per-item state, repeat schedule, and its " +
|
|
956
1121
|
"project/area/heading. " +
|
|
@@ -965,7 +1130,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
965
1130
|
? errorResult({ code: "not-found", message: noUuidMatch("item", args.uuid) })
|
|
966
1131
|
: readResult(shapeReadPayload("detail", item, false, getClient().refPromoter()));
|
|
967
1132
|
}));
|
|
968
|
-
|
|
1133
|
+
registerTool("get_project", {
|
|
969
1134
|
description: "One project's full contents: metadata plus its to-dos grouped under their headings. " +
|
|
970
1135
|
"Open headings (and archived ones not yet swept to the logbook) stay live, carrying " +
|
|
971
1136
|
"archived once archived; a swept archived heading moves into the logged region as a " +
|
|
@@ -997,7 +1162,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
997
1162
|
...(args.tz !== undefined && { zone: args.tz }),
|
|
998
1163
|
}), args.full === true, getClient().refPromoter()));
|
|
999
1164
|
}, args.tz));
|
|
1000
|
-
|
|
1165
|
+
registerTool("get_area", {
|
|
1001
1166
|
description: "One area's contents: metadata plus its direct to-dos (active first), its " +
|
|
1002
1167
|
"projects in canonical order, and later (scheduled/repeating/someday). The area " +
|
|
1003
1168
|
"logbook is not returned here — read it with read_view logbook + area; trashed " +
|
|
@@ -1061,7 +1226,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1061
1226
|
return groupedResult(withAreaBucketTotals(shapeReadPayload("area-view", view, args.full === true, getClient().refPromoter()), totals), truncation);
|
|
1062
1227
|
}, args.tz, undefined, () => (areaNotice !== undefined ? [areaNotice] : undefined));
|
|
1063
1228
|
});
|
|
1064
|
-
|
|
1229
|
+
registerTool("list_collections", {
|
|
1065
1230
|
description: "List every project, area, or tag (tags include their parent-tag nesting). Use to " +
|
|
1066
1231
|
"refresh the inventory summarized in the server instructions. The tag filters scope " +
|
|
1067
1232
|
"the projects list by each project's own tags (areas/tags reject them). " +
|
|
@@ -1116,7 +1281,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1116
1281
|
}, args.tz));
|
|
1117
1282
|
// ---------------------------------------------------------------- to-dos
|
|
1118
1283
|
const whenSchema = z.string().optional().describe(WHEN_VALUES);
|
|
1119
|
-
|
|
1284
|
+
registerTool("add_todo", {
|
|
1120
1285
|
description: "Create a to-do and return its uuid. Optionally schedule it, set a reminder or " +
|
|
1121
1286
|
"deadline, tag it, give it a checklist, and place it in a project or area " +
|
|
1122
1287
|
"(optionally under a heading within that project). A reminder " +
|
|
@@ -1124,7 +1289,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1124
1289
|
"project reopens that project — pass acknowledge_project_reopen to confirm.",
|
|
1125
1290
|
inputSchema: {
|
|
1126
1291
|
title: z.string(),
|
|
1127
|
-
notes: z.string().optional(),
|
|
1292
|
+
notes: z.string().optional().describe(`Notes body — ${NOTES_FORMAT}`),
|
|
1128
1293
|
when: whenSchema,
|
|
1129
1294
|
reminder: z.string().optional().describe(REMINDER_FORMAT),
|
|
1130
1295
|
deadline: z.string().optional().describe(DATE_FORMAT),
|
|
@@ -1177,7 +1342,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1177
1342
|
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
1178
1343
|
}, writeOptions(args)));
|
|
1179
1344
|
}));
|
|
1180
|
-
|
|
1345
|
+
registerTool("update", {
|
|
1181
1346
|
description: "Edit an existing to-do, project, area, or tag — kind selects which. " +
|
|
1182
1347
|
"kind todo/project: title, notes (or append_notes/prepend_notes to add a line to the " +
|
|
1183
1348
|
"existing body, exclusive with notes), schedule (when), reminder/clear_reminder, and " +
|
|
@@ -1198,7 +1363,10 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1198
1363
|
.describe("The item to update — a to-do by uuid; a project, area, or tag also accepts a " +
|
|
1199
1364
|
"unique name"),
|
|
1200
1365
|
title: z.string().optional().describe("New title (any kind)"),
|
|
1201
|
-
notes: z
|
|
1366
|
+
notes: z
|
|
1367
|
+
.string()
|
|
1368
|
+
.optional()
|
|
1369
|
+
.describe(`todo/project: replaces the whole notes body — ${NOTES_FORMAT}`),
|
|
1202
1370
|
append_notes: z.string().optional().describe("todo/project: add a line after the notes"),
|
|
1203
1371
|
prepend_notes: z.string().optional().describe("todo/project: add a line before the notes"),
|
|
1204
1372
|
when: whenSchema,
|
|
@@ -1220,6 +1388,17 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1220
1388
|
.array(z.string())
|
|
1221
1389
|
.optional()
|
|
1222
1390
|
.describe(`area: replace the tag set (full) — ${TAG_REF_FORMAT}`),
|
|
1391
|
+
exception: z
|
|
1392
|
+
.boolean()
|
|
1393
|
+
.optional()
|
|
1394
|
+
.describe("todo, repeating only: change just the NEXT occurrence and leave the series alone " +
|
|
1395
|
+
"(the occurrence is created if it has not appeared yet). Refused when the series " +
|
|
1396
|
+
"already lands on the requested day, and when the series names no next date at " +
|
|
1397
|
+
"all (a paused one — resume it first). The result's occurrence field names both the occurrence " +
|
|
1398
|
+
"and the series. Each call takes another occurrence out of the series, so pass " +
|
|
1399
|
+
"op_id when retrying: a resubmission with the same key replays the first result " +
|
|
1400
|
+
"instead of creating a second occurrence. Undo restores the occurrence's own " +
|
|
1401
|
+
"change but cannot remove the occurrence or rewind the series"),
|
|
1223
1402
|
parent: z.string().optional().describe("tag: existing tag to nest under"),
|
|
1224
1403
|
unnest: z.boolean().optional().describe("tag: move the tag to the top level"),
|
|
1225
1404
|
shortcut: z.string().optional().describe("tag: keyboard shortcut character"),
|
|
@@ -1238,49 +1417,23 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1238
1417
|
const opts = writeOptions(args);
|
|
1239
1418
|
const c = getClient();
|
|
1240
1419
|
if (args.kind === "todo" || args.kind === "project") {
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
if (
|
|
1246
|
-
return usage(
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
return usage(sugar.message);
|
|
1255
|
-
const when = sugar.kind === "split" ? sugar.when : args.when;
|
|
1256
|
-
const reminder = sugar.kind === "split" ? sugar.reminder : args.reminder;
|
|
1257
|
-
return mutationResult(await c.write.updateTodo(args.uuid, {
|
|
1258
|
-
...(args.title !== undefined && { title: args.title }),
|
|
1259
|
-
...(args.notes !== undefined && { notes: args.notes }),
|
|
1260
|
-
...(args.append_notes !== undefined && { appendNotes: args.append_notes }),
|
|
1261
|
-
...(args.prepend_notes !== undefined && { prependNotes: args.prepend_notes }),
|
|
1262
|
-
...(when !== undefined && { when: when }),
|
|
1263
|
-
...(reminder !== undefined && { reminder }),
|
|
1264
|
-
...(args.clear_reminder === true && { reminder: null }),
|
|
1265
|
-
...(args.deadline !== undefined && { deadline: args.deadline }),
|
|
1266
|
-
...(args.clear_deadline === true && { deadline: null }),
|
|
1267
|
-
...(args.created_at !== undefined && { createdAt: args.created_at }),
|
|
1268
|
-
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
1269
|
-
}, opts));
|
|
1420
|
+
// The patch — and every exclusive pair in the vocabulary, the `@time`
|
|
1421
|
+
// sugar included — comes from the ONE builder the CLI shares, so the two
|
|
1422
|
+
// surfaces cannot drift on which flags they accept (#491 doctrine).
|
|
1423
|
+
const built = buildUpdatePatch(args, MCP_UPDATE_LABELS);
|
|
1424
|
+
if (built.kind === "error")
|
|
1425
|
+
return usage(built.message);
|
|
1426
|
+
if (args.exception === true) {
|
|
1427
|
+
if (args.kind !== "todo")
|
|
1428
|
+
return usage("exception applies to kind todo");
|
|
1429
|
+
if (args.created_at !== undefined || args.completed_at !== undefined) {
|
|
1430
|
+
return usage("exception cannot be combined with created_at/completed_at");
|
|
1431
|
+
}
|
|
1432
|
+
return mutationResult(await c.write.updateTodoOccurrence(args.uuid, built.patch, opts));
|
|
1270
1433
|
}
|
|
1271
|
-
return mutationResult(
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
...(args.append_notes !== undefined && { appendNotes: args.append_notes }),
|
|
1275
|
-
...(args.prepend_notes !== undefined && { prependNotes: args.prepend_notes }),
|
|
1276
|
-
...(args.when !== undefined && { when: args.when }),
|
|
1277
|
-
...(args.reminder !== undefined && { reminder: args.reminder }),
|
|
1278
|
-
...(args.clear_reminder === true && { reminder: null }),
|
|
1279
|
-
...(args.deadline !== undefined && { deadline: args.deadline }),
|
|
1280
|
-
...(args.clear_deadline === true && { deadline: null }),
|
|
1281
|
-
...(args.created_at !== undefined && { createdAt: args.created_at }),
|
|
1282
|
-
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
1283
|
-
}, opts));
|
|
1434
|
+
return mutationResult(args.kind === "todo"
|
|
1435
|
+
? await c.write.updateTodo(args.uuid, built.patch, opts)
|
|
1436
|
+
: await c.write.updateProject(args.uuid, built.patch, opts));
|
|
1284
1437
|
}
|
|
1285
1438
|
if (args.kind === "area") {
|
|
1286
1439
|
if (args.title === undefined && args.tags === undefined) {
|
|
@@ -1313,9 +1466,15 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1313
1466
|
...(args.clear_shortcut === true && { clearShortcut: true }),
|
|
1314
1467
|
}, opts));
|
|
1315
1468
|
}));
|
|
1316
|
-
|
|
1469
|
+
registerTool("set_status", {
|
|
1317
1470
|
description: "Set a to-do's or project's status (scope selects which): completed, canceled, or open " +
|
|
1318
|
-
"(reopening a completed/canceled item).
|
|
1471
|
+
"(reopening a completed/canceled item). scope todo, a REPEATING to-do: completed/canceled " +
|
|
1472
|
+
"resolves the series' CURRENT occurrence — the unfinished one if there is one, otherwise " +
|
|
1473
|
+
"the next one, created for the purpose — and leaves the series running; the result's " +
|
|
1474
|
+
"occurrence field names both uuids and says which of the two was created. Each call is a " +
|
|
1475
|
+
"new resolution that takes the FOLLOWING occurrence, so pass op_id when retrying: a " +
|
|
1476
|
+
"resubmission with the same key replays the first result instead of creating a second " +
|
|
1477
|
+
"occurrence. Reopening a repeating to-do is not available. " +
|
|
1319
1478
|
"scope project, completing or canceling requires a children policy: 'require-resolved' " +
|
|
1320
1479
|
"errors if open to-dos remain; 'auto-complete'/'auto-cancel' resolves them together with " +
|
|
1321
1480
|
"the project (canceling never alters already-completed children). scope project, status " +
|
|
@@ -1401,7 +1560,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1401
1560
|
? jsonResult(outcome)
|
|
1402
1561
|
: mutationResult(outcome.project);
|
|
1403
1562
|
}));
|
|
1404
|
-
|
|
1563
|
+
registerTool("move_todo", {
|
|
1405
1564
|
description: "Move one or more to-dos as an ordered block (spec §4). MOVE changes WHAT a to-do " +
|
|
1406
1565
|
"belongs to (membership somewhere); to REARRANGE to-dos that already share a container " +
|
|
1407
1566
|
"without changing membership, call this with a position (first/last/before/after) and " +
|
|
@@ -1474,7 +1633,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1474
1633
|
};
|
|
1475
1634
|
return moveResult(await getClient().write.moveTodos(request, writeOptions(args)));
|
|
1476
1635
|
}));
|
|
1477
|
-
|
|
1636
|
+
registerTool("set_tags", {
|
|
1478
1637
|
description: "Replace or extend a to-do's or project's tags. mode 'replace' (default) sets exactly " +
|
|
1479
1638
|
"the given list — an empty list removes all tags; mode 'add' merges with the current " +
|
|
1480
1639
|
"tags. Tags must exist unless create_tags is set (or create them first with add_tag).",
|
|
@@ -1501,7 +1660,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1501
1660
|
? await c.write.setProjectTags(args.uuid, args.tags, opts)
|
|
1502
1661
|
: await c.write.setTags(args.uuid, args.tags, opts));
|
|
1503
1662
|
}));
|
|
1504
|
-
|
|
1663
|
+
registerTool("edit_checklist", {
|
|
1505
1664
|
description: "Edit a to-do's checklist. The single-item actions add / remove / check / uncheck " +
|
|
1506
1665
|
"/ rename / move change one item — targeted by title or 1-based index — and leave " +
|
|
1507
1666
|
"every other item and its checked state untouched (duplicate titles resolve " +
|
|
@@ -1591,11 +1750,16 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1591
1750
|
return mutationResult(await c.write.editChecklist(args.uuid, edit, writeOptions(args)));
|
|
1592
1751
|
}));
|
|
1593
1752
|
// ------------------------------------------------- to-dos AND projects
|
|
1594
|
-
|
|
1753
|
+
registerTool("delete", {
|
|
1595
1754
|
description: "Delete a to-do/project (kind item), an area, or a tag — kind selects which. " +
|
|
1596
1755
|
"kind item: moves a to-do or project to the Trash (recoverable via restore_item until " +
|
|
1597
|
-
"the Trash is emptied; a deleted project takes its to-dos with it
|
|
1598
|
-
"
|
|
1756
|
+
"the Trash is emptied; a deleted project takes its to-dos with it). Deleting a repeating " +
|
|
1757
|
+
"series' TEMPLATE is allowed and ends the series: it stops generating new occurrences and " +
|
|
1758
|
+
"its existing occurrences are left in place (the result names how many, and the current " +
|
|
1759
|
+
"one). That one cannot be undone — neither undo nor restore_item brings a template back, " +
|
|
1760
|
+
"so no undo token is returned; the series returns only via Put Back in the Things app's " +
|
|
1761
|
+
"Trash. Deleting a single occurrence leaves the series running. kind area: PERMANENT — " +
|
|
1762
|
+
"areas do not go to the Trash, so this cannot " +
|
|
1599
1763
|
"be undone and requires dangerously_permanent; deleting an area moves its to-dos and " +
|
|
1600
1764
|
"projects to the Trash, so a non-empty area is refused unless you pass " +
|
|
1601
1765
|
"allow_non_empty_area (empty it first to keep its contents). kind tag: PERMANENT — requires " +
|
|
@@ -1637,7 +1801,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1637
1801
|
return mutationResult(await c.write.deleteTag(args.uuid, opts));
|
|
1638
1802
|
}
|
|
1639
1803
|
}));
|
|
1640
|
-
|
|
1804
|
+
registerTool("restore_item", {
|
|
1641
1805
|
description: "Restore a trashed to-do or project. A to-do returns to the Inbox without its " +
|
|
1642
1806
|
"previous schedule or project/area. A project is restored in place: its schedule, " +
|
|
1643
1807
|
"area, and children come back exactly as they were.",
|
|
@@ -1655,11 +1819,11 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1655
1819
|
// write tools: add_todo / add_project (Logbook import), update (rewrite an
|
|
1656
1820
|
// already-resolved item's timestamps), and set_status (backdate on
|
|
1657
1821
|
// complete/cancel) — matching the CLI's --created-at/--completed-at flags.
|
|
1658
|
-
|
|
1822
|
+
registerTool("heading", {
|
|
1659
1823
|
description: "Manage a project's headings — action selects which; project is always required, and a " +
|
|
1660
1824
|
"heading is selected by its exact title or its uuid (never an ordinal). add_heading: a " +
|
|
1661
1825
|
"new heading in the project (project + title; returns its uuid; uses the Things proxy " +
|
|
1662
|
-
"shortcuts, set up once with `things setup
|
|
1826
|
+
"shortcuts, set up once with `things setup`); a placement flag positions it, " +
|
|
1663
1827
|
"else it appends. rename_heading: rename in place (project + heading + title; works on " +
|
|
1664
1828
|
"archived headings). archive_heading: retire a heading so it leaves the active project " +
|
|
1665
1829
|
"view (reversible with unarchive_heading); with open children pass children — complete " +
|
|
@@ -1820,7 +1984,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1820
1984
|
}
|
|
1821
1985
|
}
|
|
1822
1986
|
}));
|
|
1823
|
-
|
|
1987
|
+
registerTool("convert_to_project", {
|
|
1824
1988
|
description: "Promote a to-do into a project. This REPLACES the to-do with a new project (its notes " +
|
|
1825
1989
|
"are kept); the to-do's identity is gone and it cannot be undone. Requires " +
|
|
1826
1990
|
"dangerously_drive_gui. The new project's uuid is on the result. (To promote a HEADING, " +
|
|
@@ -1834,7 +1998,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1834
1998
|
},
|
|
1835
1999
|
annotations: DESTRUCTIVE,
|
|
1836
2000
|
}, async (args) => guard(async () => mutationResult(await getClient().write.run("todo.convert-to-project", { uuid: args.uuid }, writeOptions(args)))));
|
|
1837
|
-
|
|
2001
|
+
registerTool("clear_reminder", {
|
|
1838
2002
|
description: "Clear a to-do's time-of-day reminder while keeping its scheduled date. Uses the " +
|
|
1839
2003
|
"Things proxy shortcuts when installed (in place, and the only path for a repeating " +
|
|
1840
2004
|
"to-do); otherwise a non-repeating dated to-do falls back to a URL re-schedule that " +
|
|
@@ -1889,7 +2053,8 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1889
2053
|
.number()
|
|
1890
2054
|
.int()
|
|
1891
2055
|
.optional()
|
|
1892
|
-
.describe("With deadline: start each occurrence N days before its deadline"
|
|
2056
|
+
.describe("With deadline: start each occurrence N days before its deadline (on add, an " +
|
|
2057
|
+
"alternative spelling of project_deadline — give one or the other, both must agree)"),
|
|
1893
2058
|
};
|
|
1894
2059
|
// oxlint-disable-next-line consistent-function-scoping -- kept beside repeatRuleShape it mirrors
|
|
1895
2060
|
const repeatExtras = (a, frequency) => {
|
|
@@ -1920,7 +2085,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1920
2085
|
fields.startDaysEarlier = a.start_days_earlier;
|
|
1921
2086
|
return fields;
|
|
1922
2087
|
};
|
|
1923
|
-
|
|
2088
|
+
registerTool("repeat", {
|
|
1924
2089
|
description: "Manage recurrence on a to-do or project (scope) by driving the local Things app's " +
|
|
1925
2090
|
"interface — every action needs dangerously_drive_gui. action start: turn a plain item " +
|
|
1926
2091
|
"into a repeating one (promote-via-clone: a disposable copy is promoted and the ORIGINAL " +
|
|
@@ -1942,12 +2107,16 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1942
2107
|
.optional()
|
|
1943
2108
|
.describe("start/reschedule/pause/resume: the item (a project also accepts a unique name)"),
|
|
1944
2109
|
title: z.string().optional().describe("add: the new item's title"),
|
|
1945
|
-
notes: z.string().optional().describe(
|
|
2110
|
+
notes: z.string().optional().describe(`add: notes — ${NOTES_FORMAT}`),
|
|
1946
2111
|
area: z.string().optional().describe(`add (project): destination area (${REF_FORMAT})`),
|
|
1947
2112
|
project_deadline: z
|
|
1948
2113
|
.string()
|
|
1949
2114
|
.optional()
|
|
1950
|
-
.describe(`add:
|
|
2115
|
+
.describe(`add: due date (${DATE_FORMAT}). It deadlines EVERY occurrence — each starts ` +
|
|
2116
|
+
`(deadline − when) days before its own deadline, so it needs a concrete when on or ` +
|
|
2117
|
+
`before it. For a to-do it is an alternative spelling of start_days_earlier (give ` +
|
|
2118
|
+
`one or the other, both must agree). Not available with after_completion on a ` +
|
|
2119
|
+
`project, whose occurrences are created without a deadline.`),
|
|
1951
2120
|
todos: z.array(z.string()).optional().describe("add (project): initial child to-do titles"),
|
|
1952
2121
|
frequency: z
|
|
1953
2122
|
.enum(["daily", "weekly", "monthly", "yearly"])
|
|
@@ -1986,15 +2155,19 @@ export function createThingsMcpServer(options = {}) {
|
|
|
1986
2155
|
if (frequency === undefined || interval === undefined) {
|
|
1987
2156
|
return usage('action "add" requires frequency and interval');
|
|
1988
2157
|
}
|
|
1989
|
-
// add-repeating carries only the calendar-anchor rule fields; the
|
|
1990
|
-
//
|
|
1991
|
-
//
|
|
1992
|
-
|
|
2158
|
+
// add-repeating carries only the calendar-anchor rule fields here; the rule
|
|
2159
|
+
// reminder is set with a follow-up reschedule. A concrete `project_deadline`
|
|
2160
|
+
// maps to the RULE on BOTH scopes (DBLSPAWN1 — each occurrence deadlined,
|
|
2161
|
+
// the created item deadline-free). For a to-do, `start_days_earlier` is the
|
|
2162
|
+
// alternative spelling of that deadline geometry (the two must AGREE when
|
|
2163
|
+
// both are given — enforced in the library).
|
|
2164
|
+
const { reminder: _reminder, deadline: _deadline, startDaysEarlier, ...ruleExtras } = repeatExtras(args, frequency);
|
|
1993
2165
|
if (args.scope === "todo") {
|
|
1994
2166
|
return mutationResult(await c.write.addRepeatingTodo({
|
|
1995
2167
|
title: args.title,
|
|
1996
2168
|
...(args.notes !== undefined && { notes: args.notes }),
|
|
1997
2169
|
...(args.project_deadline !== undefined && { deadline: args.project_deadline }),
|
|
2170
|
+
...(startDaysEarlier !== undefined && { startDaysEarlier }),
|
|
1998
2171
|
frequency,
|
|
1999
2172
|
interval,
|
|
2000
2173
|
...ruleExtras,
|
|
@@ -2041,7 +2214,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2041
2214
|
// scope project, action start → the dedicated make-repeating method
|
|
2042
2215
|
return mutationResult(await c.write.makeRepeatingProject(args.uuid, { frequency, interval, ...extras }, opts));
|
|
2043
2216
|
}));
|
|
2044
|
-
|
|
2217
|
+
registerTool("duplicate_item", {
|
|
2045
2218
|
description: "Duplicate a to-do or project and return the copy's uuid; a duplicated project " +
|
|
2046
2219
|
"includes its children. Not available for repeating items.",
|
|
2047
2220
|
inputSchema: { uuid: z.string(), ...dryRunShape, ...preserveModifiedShape, ...opIdShape },
|
|
@@ -2052,7 +2225,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2052
2225
|
? await c.write.duplicateTodo(args.uuid, writeOptions(args))
|
|
2053
2226
|
: await c.write.duplicateProject(args.uuid, writeOptions(args)));
|
|
2054
2227
|
}));
|
|
2055
|
-
|
|
2228
|
+
registerTool("clone_item", {
|
|
2056
2229
|
description: "Clone a to-do or project — a faithful content copy through official write surfaces and " +
|
|
2057
2230
|
"return the clone's uuid. Copies title, notes, tags, when, reminder, deadline, checklist " +
|
|
2058
2231
|
"items and their checked state, container, and completed/canceled state with the exact " +
|
|
@@ -2089,12 +2262,12 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2089
2262
|
: await c.write.cloneProject(args.uuid, clone, writeOptions(args)));
|
|
2090
2263
|
}));
|
|
2091
2264
|
// -------------------------------------------------------------- projects
|
|
2092
|
-
|
|
2265
|
+
registerTool("add_project", {
|
|
2093
2266
|
description: "Create a project and return its uuid. Optionally place it in an area, schedule " +
|
|
2094
2267
|
"it, set a deadline, and seed it with initial to-dos.",
|
|
2095
2268
|
inputSchema: {
|
|
2096
2269
|
title: z.string(),
|
|
2097
|
-
notes: z.string().optional(),
|
|
2270
|
+
notes: z.string().optional().describe(`Notes body — ${NOTES_FORMAT}`),
|
|
2098
2271
|
area: z.string().optional().describe(`Destination area (${REF_FORMAT})`),
|
|
2099
2272
|
when: whenSchema,
|
|
2100
2273
|
deadline: z.string().optional().describe(DATE_FORMAT),
|
|
@@ -2129,7 +2302,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2129
2302
|
...(args.completed_at !== undefined && { completedAt: args.completed_at }),
|
|
2130
2303
|
}, writeOptions(args)));
|
|
2131
2304
|
}));
|
|
2132
|
-
|
|
2305
|
+
registerTool("move_project", {
|
|
2133
2306
|
description: "Move one or more projects as an ordered block (spec §4/§5). Pass at most one " +
|
|
2134
2307
|
"destination: to_area, or no_area (leave the area — a project's complete detach). To " +
|
|
2135
2308
|
"REORDER projects among their siblings without changing area, pass a position " +
|
|
@@ -2164,7 +2337,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2164
2337
|
return moveResult(await getClient().write.moveProjects(request, writeOptions(args)));
|
|
2165
2338
|
}));
|
|
2166
2339
|
// ----------------------------------------------------------------- areas
|
|
2167
|
-
|
|
2340
|
+
registerTool("add_area", {
|
|
2168
2341
|
description: "Create an area, optionally tagged. Tags must exist unless create_tags is set.",
|
|
2169
2342
|
inputSchema: {
|
|
2170
2343
|
title: z.string(),
|
|
@@ -2177,7 +2350,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2177
2350
|
annotations: NON_DESTRUCTIVE,
|
|
2178
2351
|
}, async (args) => guard(async () => mutationResult(await getClient().write.addArea({ title: args.title, ...(args.tags !== undefined && { tags: args.tags }) }, writeOptions(args)))));
|
|
2179
2352
|
// ------------------------------------------------------------------ tags
|
|
2180
|
-
|
|
2353
|
+
registerTool("add_tag", {
|
|
2181
2354
|
description: "Create a tag, optionally nested under an existing parent tag.",
|
|
2182
2355
|
inputSchema: {
|
|
2183
2356
|
title: z.string(),
|
|
@@ -2188,7 +2361,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2188
2361
|
},
|
|
2189
2362
|
annotations: NON_DESTRUCTIVE,
|
|
2190
2363
|
}, async (args) => guard(async () => mutationResult(await getClient().write.addTag({ title: args.title, ...(args.parent !== undefined && { parent: args.parent }) }, writeOptions(args)))));
|
|
2191
|
-
|
|
2364
|
+
registerTool("log_now", {
|
|
2192
2365
|
description: "Move completed items to the Logbook now. The result discloses how many items were moved " +
|
|
2193
2366
|
"(observed.logged); when none are waiting it logs nothing — a clean no-op, not an error. " +
|
|
2194
2367
|
"This cannot be undone.",
|
|
@@ -2196,10 +2369,13 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2196
2369
|
annotations: DESTRUCTIVE,
|
|
2197
2370
|
}, async (args) => guard(async () => mutationResult(await getClient().write.logNow(writeOptions(args)))));
|
|
2198
2371
|
// -------------------------------------------------- generic + discovery
|
|
2199
|
-
|
|
2372
|
+
registerTool("run_operation", {
|
|
2200
2373
|
description: "Run any cataloged operation by kind — the generic entry for operations without a " +
|
|
2201
2374
|
"dedicated tool (e.g. trash.empty). Call capabilities first for the catalog of " +
|
|
2202
|
-
"operation kinds and their parameter shapes
|
|
2375
|
+
"operation kinds and their parameter shapes (each entry carries a params list naming " +
|
|
2376
|
+
"every field, whether it is optional, and the shape it accepts). A malformed params " +
|
|
2377
|
+
"bag — an unknown field, a wrong type, a bare string where a container reference " +
|
|
2378
|
+
"object belongs — is refused before anything runs.",
|
|
2203
2379
|
inputSchema: {
|
|
2204
2380
|
op: z.enum(OPERATION_KINDS),
|
|
2205
2381
|
params: z
|
|
@@ -2225,11 +2401,13 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2225
2401
|
},
|
|
2226
2402
|
annotations: DESTRUCTIVE,
|
|
2227
2403
|
}, async (args) => guard(async () => mutationResult(await getClient().write.run(args.op, args.params, writeOptions(args)))));
|
|
2228
|
-
|
|
2404
|
+
registerTool("batch", {
|
|
2229
2405
|
description: "Run several operations in order, each independently — there are no transactions, " +
|
|
2230
2406
|
"and a failure does not roll back earlier operations. A statically-invalid operation " +
|
|
2231
|
-
"(
|
|
2232
|
-
"refuses the WHOLE batch before anything runs, naming
|
|
2407
|
+
"(unknown op, an unknown or wrongly-typed param, a $ref to an undeclared/forward " +
|
|
2408
|
+
"temp_id, a duplicate temp_id) refuses the WHOLE batch before anything runs, naming " +
|
|
2409
|
+
"every bad operation and the field it names. A container param takes an object " +
|
|
2410
|
+
'({"project": {"uuid": "…"}}), never a bare string. Otherwise ' +
|
|
2233
2411
|
"per-operation results return in order. By DEFAULT a runtime failure STOPS the batch " +
|
|
2234
2412
|
"(later operations reported not-run, with resume guidance in the summary); " +
|
|
2235
2413
|
"continue_on_error runs past failures. " +
|
|
@@ -2240,8 +2418,11 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2240
2418
|
"reference a tag by title) and unique per batch. IDEMPOTENCY: op_id makes resubmission " +
|
|
2241
2419
|
"safe — an operation matching an earlier success is reported already-applied, not " +
|
|
2242
2420
|
"re-created (put an op_id on EVERY operation so a stopped batch can be resubmitted " +
|
|
2243
|
-
"verbatim to resume).
|
|
2244
|
-
"
|
|
2421
|
+
"verbatim to resume). TIMELINE: preserve_modified applies to EVERY operation (an " +
|
|
2422
|
+
"operation may override it with its own options.preserve_modified), so a bulk re-tag " +
|
|
2423
|
+
"stays off the modification-date timeline under one undo token. The result adds " +
|
|
2424
|
+
"temp_id_mapping (handle → uuid) and undo_token, which reverses the whole batch as one " +
|
|
2425
|
+
"unit via the undo tool.",
|
|
2245
2426
|
inputSchema: {
|
|
2246
2427
|
ops: z
|
|
2247
2428
|
.array(z.object({
|
|
@@ -2263,6 +2444,11 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2263
2444
|
acknowledge_tag_subtree: z.boolean().optional(),
|
|
2264
2445
|
allow_non_empty_area: z.boolean().optional(),
|
|
2265
2446
|
dangerously_drive_gui: z.boolean().optional(),
|
|
2447
|
+
preserve_modified: z
|
|
2448
|
+
.boolean()
|
|
2449
|
+
.optional()
|
|
2450
|
+
.describe("Keep THIS operation off the modification-date timeline; overrides the " +
|
|
2451
|
+
"run-level preserve_modified (false opts one operation back on)"),
|
|
2266
2452
|
})
|
|
2267
2453
|
.optional(),
|
|
2268
2454
|
}))
|
|
@@ -2289,6 +2475,12 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2289
2475
|
...(o?.acknowledge_tag_subtree === true && { acknowledgeTagSubtree: true }),
|
|
2290
2476
|
...(o?.allow_non_empty_area === true && { allowNonEmptyArea: true }),
|
|
2291
2477
|
...(o?.dangerously_drive_gui === true && { dangerouslyDriveGui: true }),
|
|
2478
|
+
// Per-op override of the run-level preserve_modified; passed through
|
|
2479
|
+
// when EXPLICIT (including false, which opts one op back onto the
|
|
2480
|
+
// timeline) so the batch engine's `??` precedence sees it.
|
|
2481
|
+
...(o?.preserve_modified !== undefined && {
|
|
2482
|
+
preserveModified: o.preserve_modified,
|
|
2483
|
+
}),
|
|
2292
2484
|
...(ceiling !== undefined && { maxDisruption: ceiling }),
|
|
2293
2485
|
};
|
|
2294
2486
|
return {
|
|
@@ -2302,6 +2494,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2302
2494
|
const batchResult = await getClient().write.batch(ops, {
|
|
2303
2495
|
...(args.dry_run === true && { dryRun: true }),
|
|
2304
2496
|
...(args.continue_on_error === true && { continueOnError: true }),
|
|
2497
|
+
...(args.preserve_modified === true && { preserveModified: true }),
|
|
2305
2498
|
actor: mcpActor(),
|
|
2306
2499
|
});
|
|
2307
2500
|
// First block: the per-op results, each FLATTENED to the wire shape (a
|
|
@@ -2332,7 +2525,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2332
2525
|
],
|
|
2333
2526
|
};
|
|
2334
2527
|
}));
|
|
2335
|
-
|
|
2528
|
+
registerTool("reorder", {
|
|
2336
2529
|
description: "The ONE reorder tool — rearrange a single-KIND set IN PLACE: to-dos, projects, headings, " +
|
|
2337
2530
|
"OR sidebar areas. This REARRANGES, never changes what an item belongs to (to change " +
|
|
2338
2531
|
"membership use move_todo / move_project). All refs must be one kind; only to-dos and " +
|
|
@@ -2382,7 +2575,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2382
2575
|
};
|
|
2383
2576
|
return moveResult(await getClient().write.reorderAny(request, writeOptions(args)));
|
|
2384
2577
|
}));
|
|
2385
|
-
|
|
2578
|
+
registerTool("undo", {
|
|
2386
2579
|
description: "Undo the last N changes, newest first (changes made directly in the Things app " +
|
|
2387
2580
|
"cannot be undone here). By default this undoes only changes made through THIS " +
|
|
2388
2581
|
"connection — this client's own writes; it will not touch the user's own edits, or " +
|
|
@@ -2445,7 +2638,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2445
2638
|
});
|
|
2446
2639
|
return jsonResult(items);
|
|
2447
2640
|
}));
|
|
2448
|
-
|
|
2641
|
+
registerTool("capabilities", {
|
|
2449
2642
|
description: "Support reference for every operation kind usable with run_operation and batch: " +
|
|
2450
2643
|
"whether it is available, its caveats, and the confirmation parameters it needs.",
|
|
2451
2644
|
inputSchema: {
|
|
@@ -2456,7 +2649,7 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2456
2649
|
},
|
|
2457
2650
|
annotations: READ_ONLY,
|
|
2458
2651
|
}, async (args) => guard(() => jsonResult(capabilitiesTable(args.op))));
|
|
2459
|
-
|
|
2652
|
+
registerTool("doctor", {
|
|
2460
2653
|
description: "Check the environment: whether the Things app and its database are reachable, " +
|
|
2461
2654
|
"whether changes can be made, any one-time setup still needed (macOS permissions, " +
|
|
2462
2655
|
"the app's 'Enable Things URLs' setting), whether the environment changed since " +
|
|
@@ -2480,6 +2673,16 @@ export function createThingsMcpServer(options = {}) {
|
|
|
2480
2673
|
? jsonResult(report)
|
|
2481
2674
|
: errorResult(error ?? { code: "unexpected", message: "no report" });
|
|
2482
2675
|
}));
|
|
2676
|
+
registerTool("op_result", {
|
|
2677
|
+
description: "Look up what happened to a write you dispatched with op_id, from the local change history " +
|
|
2678
|
+
"alone (opens no database, changes nothing). Use it to recover the outcome when the write " +
|
|
2679
|
+
"call was interrupted before it returned: report FOUND (the final result + target + " +
|
|
2680
|
+
"observation), INTENT-ONLY (the op started but no outcome was written — still running or " +
|
|
2681
|
+
"the process died mid-flight, outcome UNCERTAIN — re-read the target, do not blind-retry), " +
|
|
2682
|
+
"or UNKNOWN (no such op_id in history).",
|
|
2683
|
+
inputSchema: { op_id: z.string().describe("the op_id the original write carried") },
|
|
2684
|
+
annotations: READ_ONLY,
|
|
2685
|
+
}, async (args) => guard(() => readResult(opResult(assertOpId(args.op_id)))));
|
|
2483
2686
|
return server;
|
|
2484
2687
|
}
|
|
2485
2688
|
//# sourceMappingURL=server.js.map
|