@roam-research/roam-tools-core 0.6.6 → 0.6.8
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/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/operations/blocks.d.ts +1 -1
- package/dist/operations/blocks.d.ts.map +1 -1
- package/dist/operations/blocks.js +19 -5
- package/dist/operations/datalog.d.ts.map +1 -1
- package/dist/operations/datalog.js +2 -0
- package/dist/operations/pages.d.ts.map +1 -1
- package/dist/operations/pages.js +4 -1
- package/dist/relative-date.d.ts +27 -0
- package/dist/relative-date.d.ts.map +1 -0
- package/dist/relative-date.js +83 -0
- package/dist/tools.d.ts +15 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +107 -33
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +10 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { CallToolResult, TextContent, ImageContent, GraphType, AccessLevel, GraphConfig, RoamMcpConfig, ToolGraph, ResolvedGraph, RoamActionClient, ErrorCode, RoamApiError, RoamResponse, Block, Page, BlockLocation, WindowType, SidebarWindow, SidebarWindowInfo, FocusedBlock, SelectedBlock, MainWindowViewType, MainWindowView, SearchResultPath, SearchResult, SearchResponse, RecentlyOpenedBlock, RecentlyOpenedItem, DailyNotePagesViewItem, RecentlyEditedPage, SearchSuggestionsResponse, SearchTemplatesResponse, GetPageResponse, GetBlockResponse, Template, QueryResult, QueryResponse, TokenInfoResponse, TokenInfoResult, ToolAnnotations, } from "./types.js";
|
|
2
2
|
export { GraphConfigSchema, RoamMcpConfigSchema, ErrorCodes, RoamError, CONFIG_VERSION, EXPECTED_API_VERSION, textResult, imageResult, errorResult, getErrorMessage, } from "./types.js";
|
|
3
|
-
export { tools, findTool, routeToolCall, contentTools, dataTools, desktopUiTools, defineTool, defineStandaloneTool, } from "./tools.js";
|
|
3
|
+
export { tools, findTool, routeToolCall, stripUndeclaredStructuredContent, contentTools, dataTools, desktopUiTools, defineTool, defineStandaloneTool, } from "./tools.js";
|
|
4
4
|
export type { ToolDefinition, ClientToolDefinition, StandaloneToolDefinition, RouteToolCallOptions, } from "./tools.js";
|
|
5
|
+
export { localTodayString } from "./relative-date.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,YAAY,EACV,cAAc,EACd,WAAW,EACX,YAAY,EACZ,SAAS,EACT,WAAW,EACX,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,KAAK,EACL,IAAI,EACJ,aAAa,EACb,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,EACV,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,EACL,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,EACV,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,YAAY,EACV,cAAc,EACd,WAAW,EACX,YAAY,EACZ,SAAS,EACT,WAAW,EACX,WAAW,EACX,aAAa,EACb,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,KAAK,EACL,IAAI,EACJ,aAAa,EACb,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,UAAU,EACV,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,WAAW,EACX,WAAW,EACX,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,KAAK,EACL,QAAQ,EACR,aAAa,EACb,gCAAgC,EAChC,YAAY,EACZ,SAAS,EACT,cAAc,EACd,UAAU,EACV,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAKpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -7,4 +7,8 @@
|
|
|
7
7
|
// routeToolCall's options.
|
|
8
8
|
export { GraphConfigSchema, RoamMcpConfigSchema, ErrorCodes, RoamError, CONFIG_VERSION, EXPECTED_API_VERSION, textResult, imageResult, errorResult, getErrorMessage, } from "./types.js";
|
|
9
9
|
// Tool definitions, helpers, and routing
|
|
10
|
-
export { tools, findTool, routeToolCall, contentTools, dataTools, desktopUiTools, defineTool, defineStandaloneTool, } from "./tools.js";
|
|
10
|
+
export { tools, findTool, routeToolCall, stripUndeclaredStructuredContent, contentTools, dataTools, desktopUiTools, defineTool, defineStandaloneTool, } from "./tools.js";
|
|
11
|
+
// localTodayString is the only relative-date helper with an external consumer
|
|
12
|
+
// (the local transport's getCurrentDate()). The schema/resolver helpers stay
|
|
13
|
+
// core-internal — operations import them directly from ./relative-date.js.
|
|
14
|
+
export { localTodayString } from "./relative-date.js";
|
|
@@ -3,7 +3,7 @@ import type { CallToolResult, RoamActionClient } from "../types.js";
|
|
|
3
3
|
export declare const CreateBlockSchema: z.ZodObject<{
|
|
4
4
|
parentUid: z.ZodOptional<z.ZodString>;
|
|
5
5
|
pageTitle: z.ZodOptional<z.ZodString>;
|
|
6
|
-
dailyNotePage: z.ZodOptional<z.ZodString
|
|
6
|
+
dailyNotePage: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
7
7
|
nestUnder: z.ZodOptional<z.ZodString>;
|
|
8
8
|
markdown: z.ZodString;
|
|
9
9
|
order: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodEnum<["first", "last"]>]>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/operations/blocks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAoB,gBAAgB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/operations/blocks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { textResult, RoamError, ErrorCodes } from "../types.js";
|
|
3
|
+
import { isRelativeDateWord, MM_DD_YYYY, resolveDailyNotePage } from "../relative-date.js";
|
|
3
4
|
// Schemas
|
|
4
5
|
export const CreateBlockSchema = z.object({
|
|
5
6
|
parentUid: z
|
|
@@ -12,9 +13,11 @@ export const CreateBlockSchema = z.object({
|
|
|
12
13
|
.describe("Page title to create block under (creates the page if it doesn't exist). Exactly one of parentUid, pageTitle, or dailyNotePage is required."),
|
|
13
14
|
dailyNotePage: z
|
|
14
15
|
.string()
|
|
15
|
-
.
|
|
16
|
+
.refine((v) => MM_DD_YYYY.test(v) || isRelativeDateWord(v), {
|
|
17
|
+
message: "Must be MM-DD-YYYY format (e.g. '03-17-2026') or a relative day: 'today', 'yesterday', or 'tomorrow'",
|
|
18
|
+
})
|
|
16
19
|
.optional()
|
|
17
|
-
.describe("
|
|
20
|
+
.describe("Target a daily note page, creating it if needed. Either a date in MM-DD-YYYY format (e.g. '03-17-2026') or a relative day: 'today', 'yesterday', or 'tomorrow' (case-insensitive; resolved to the user's local calendar date). Exactly one of parentUid, pageTitle, or dailyNotePage is required."),
|
|
18
21
|
nestUnder: z
|
|
19
22
|
.string()
|
|
20
23
|
.optional()
|
|
@@ -88,14 +91,22 @@ export async function createBlock(client, params) {
|
|
|
88
91
|
if (targets.length > 1) {
|
|
89
92
|
throw new RoamError("Provide only one of 'parentUid', 'pageTitle', or 'dailyNotePage'", ErrorCodes.VALIDATION_ERROR);
|
|
90
93
|
}
|
|
94
|
+
// Resolve a relative dailyNotePage ("today"/"yesterday"/"tomorrow") to a
|
|
95
|
+
// concrete MM-DD-YYYY before it goes on the wire, against the transport's
|
|
96
|
+
// notion of "today" (remote: picker timezone; local: machine clock). A
|
|
97
|
+
// literal MM-DD-YYYY passes through unchanged, so the backend/renderer see no
|
|
98
|
+
// new vocabulary.
|
|
99
|
+
const resolvedDailyNote = params.dailyNotePage !== undefined
|
|
100
|
+
? resolveDailyNotePage(params.dailyNotePage, client.getCurrentDate?.())
|
|
101
|
+
: undefined;
|
|
91
102
|
const location = {
|
|
92
103
|
order: params.order ?? "last",
|
|
93
104
|
};
|
|
94
105
|
if (params.parentUid !== undefined) {
|
|
95
106
|
location["parent-uid"] = params.parentUid;
|
|
96
107
|
}
|
|
97
|
-
else if (
|
|
98
|
-
location["page-title"] = { "daily-note-page":
|
|
108
|
+
else if (resolvedDailyNote !== undefined) {
|
|
109
|
+
location["page-title"] = { "daily-note-page": resolvedDailyNote };
|
|
99
110
|
}
|
|
100
111
|
else {
|
|
101
112
|
location["page-title"] = params.pageTitle;
|
|
@@ -113,7 +124,10 @@ export async function getBlock(client, params) {
|
|
|
113
124
|
if (params.maxDepth !== undefined)
|
|
114
125
|
apiParams.maxDepth = params.maxDepth;
|
|
115
126
|
const response = await client.call("data.ai.getBlock", [apiParams]);
|
|
116
|
-
|
|
127
|
+
// Not-found: a found block always has a `uid`, so treat a nullish/uid-less result
|
|
128
|
+
// (incl. `{}`) as a miss and return an explicit { found: false } signal — clearer
|
|
129
|
+
// than an empty object that reads as a successful empty block.
|
|
130
|
+
return textResult(response.result?.uid ? response.result : { found: false });
|
|
117
131
|
}
|
|
118
132
|
export async function updateBlock(client, params) {
|
|
119
133
|
const block = { uid: params.uid };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datalog.d.ts","sourceRoot":"","sources":["../../src/operations/datalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpE,eAAO,MAAM,kBAAkB;;;;;;;;;EAQ7B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"datalog.d.ts","sourceRoot":"","sources":["../../src/operations/datalog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpE,eAAO,MAAM,kBAAkB;;;;;;;;;EAQ7B,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,cAAc,CAAC,CAMzB"}
|
|
@@ -12,5 +12,7 @@ export const DatalogQuerySchema = z.object({
|
|
|
12
12
|
export async function datalogQuery(client, params) {
|
|
13
13
|
const args = params.inputs ? [params.query, ...params.inputs] : [params.query];
|
|
14
14
|
const response = await client.call("q", args);
|
|
15
|
+
// Content-only read (no outputSchema): the raw scalar/tuple/collection/relation
|
|
16
|
+
// goes on the text channel as-is.
|
|
15
17
|
return textResult(response.result ?? []);
|
|
16
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/operations/pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIrF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAa3B,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;;;EAOxB,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;EAE3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAa3B,CAAC;AAEH,eAAO,MAAM,mBAAmB,gDAAe,CAAC;AAGhD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAChE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAChE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,CAAC,CASzB;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,cAAc,CAAC,
|
|
1
|
+
{"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../src/operations/pages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAmB,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIrF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAa3B,CAAC;AAEH,eAAO,MAAM,aAAa;;;;;;;;;;;;EAOxB,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;EAE3B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAa3B,CAAC;AAEH,eAAO,MAAM,mBAAmB,gDAAe,CAAC;AAGhD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAChE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAChE,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,CAAC,CASzB;AAED,wBAAsB,OAAO,CAC3B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,cAAc,CAAC,CAWzB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,CAAC,CAGzB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,CAAC,CAUzB;AAED,wBAAsB,aAAa,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,cAAc,CAAC,CA0BrF"}
|
package/dist/operations/pages.js
CHANGED
|
@@ -55,7 +55,10 @@ export async function getPage(client, params) {
|
|
|
55
55
|
if (params.maxDepth !== undefined)
|
|
56
56
|
apiParams.maxDepth = params.maxDepth;
|
|
57
57
|
const response = await client.call("data.ai.getPage", [apiParams]);
|
|
58
|
-
|
|
58
|
+
// Not-found: a found page always has a `uid`, so treat a nullish/uid-less result
|
|
59
|
+
// (incl. `{}`) as a miss and return an explicit { found: false } signal — clearer
|
|
60
|
+
// than an empty object that reads as a successful empty page.
|
|
61
|
+
return textResult(response.result?.uid ? response.result : { found: false });
|
|
59
62
|
}
|
|
60
63
|
export async function deletePage(client, params) {
|
|
61
64
|
await client.call("data.page.delete", [{ page: { uid: params.uid } }]);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** The MM-DD-YYYY daily-note-page wire format. */
|
|
2
|
+
export declare const MM_DD_YYYY: RegExp;
|
|
3
|
+
/** Is `value` (trimmed, case-insensitive) one of today/yesterday/tomorrow? */
|
|
4
|
+
export declare function isRelativeDateWord(value: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* The host machine's local calendar date as yyyy-MM-dd. This is the one place
|
|
7
|
+
* that reads the host clock: the local transport calls it for its
|
|
8
|
+
* getCurrentDate(), so "use machine-local time" is an explicit transport
|
|
9
|
+
* decision, never something core guesses on its own. `now` is injectable for
|
|
10
|
+
* deterministic tests.
|
|
11
|
+
*/
|
|
12
|
+
export declare function localTodayString(now?: Date): string;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a dailyNotePage value to a concrete MM-DD-YYYY string.
|
|
15
|
+
* - A literal MM-DD-YYYY is returned unchanged (the base date is ignored).
|
|
16
|
+
* - A relative word (today/yesterday/tomorrow, case-insensitive) is resolved
|
|
17
|
+
* against `baseDateStr` — a yyyy-MM-dd calendar date parsed by digits into a
|
|
18
|
+
* UTC anchor, then offset by ±1 day.
|
|
19
|
+
*
|
|
20
|
+
* If a relative word is given but `baseDateStr` is absent (or malformed), THROW.
|
|
21
|
+
* Core must never silently fall back to its own clock, because it also runs on
|
|
22
|
+
* the hosted transport (Cloud Run, UTC). Both first-party transports supply
|
|
23
|
+
* getCurrentDate(), so a missing base means a misbehaving/third-party client —
|
|
24
|
+
* a loud error beats a silently-wrong date.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveDailyNotePage(value: string, baseDateStr: string | undefined): string;
|
|
27
|
+
//# sourceMappingURL=relative-date.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relative-date.d.ts","sourceRoot":"","sources":["../src/relative-date.ts"],"names":[],"mappings":"AAwBA,kDAAkD;AAClD,eAAO,MAAM,UAAU,QAAwB,CAAC;AAEhD,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEzD;AAWD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,IAAiB,GAAG,MAAM,CAE/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAoC3F"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Relative daily-note date resolution for the create_block dailyNotePage param.
|
|
2
|
+
//
|
|
3
|
+
// Resolves the words "today" / "yesterday" / "tomorrow" to the MM-DD-YYYY
|
|
4
|
+
// string Roam uses for daily-note pages, BEFORE the value crosses the wire — so
|
|
5
|
+
// the backend and renderer only ever receive a concrete MM-DD-YYYY (no new
|
|
6
|
+
// vocabulary, no version coupling).
|
|
7
|
+
//
|
|
8
|
+
// All arithmetic is done in UTC (Date.UTC / setUTCDate): every UTC day is
|
|
9
|
+
// exactly 86_400_000 ms, so the ±1-day math has no DST edge cases and the host
|
|
10
|
+
// machine's timezone can never perturb the result. Core has no date library;
|
|
11
|
+
// this is intentionally tiny.
|
|
12
|
+
import { RoamError, ErrorCodes } from "./types.js";
|
|
13
|
+
// Null-prototype so `in` / bracket-access only see our own keys — a plain
|
|
14
|
+
// object would let already-lowercase Object.prototype members ("constructor",
|
|
15
|
+
// "__proto__") slip through `isRelativeDateWord` and index a function as the
|
|
16
|
+
// offset, producing a "NaN-NaN-NaN" date.
|
|
17
|
+
const RELATIVE_OFFSETS = Object.assign(Object.create(null), {
|
|
18
|
+
today: 0,
|
|
19
|
+
yesterday: -1,
|
|
20
|
+
tomorrow: 1,
|
|
21
|
+
});
|
|
22
|
+
/** The MM-DD-YYYY daily-note-page wire format. */
|
|
23
|
+
export const MM_DD_YYYY = /^\d{2}-\d{2}-\d{4}$/;
|
|
24
|
+
/** Is `value` (trimmed, case-insensitive) one of today/yesterday/tomorrow? */
|
|
25
|
+
export function isRelativeDateWord(value) {
|
|
26
|
+
return value.trim().toLowerCase() in RELATIVE_OFFSETS;
|
|
27
|
+
}
|
|
28
|
+
function pad2(n) {
|
|
29
|
+
return String(n).padStart(2, "0");
|
|
30
|
+
}
|
|
31
|
+
/** Format a UTC-anchored Date as MM-DD-YYYY. */
|
|
32
|
+
function toMmDdYyyy(d) {
|
|
33
|
+
return `${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}-${d.getUTCFullYear()}`;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The host machine's local calendar date as yyyy-MM-dd. This is the one place
|
|
37
|
+
* that reads the host clock: the local transport calls it for its
|
|
38
|
+
* getCurrentDate(), so "use machine-local time" is an explicit transport
|
|
39
|
+
* decision, never something core guesses on its own. `now` is injectable for
|
|
40
|
+
* deterministic tests.
|
|
41
|
+
*/
|
|
42
|
+
export function localTodayString(now = new Date()) {
|
|
43
|
+
return `${now.getFullYear()}-${pad2(now.getMonth() + 1)}-${pad2(now.getDate())}`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a dailyNotePage value to a concrete MM-DD-YYYY string.
|
|
47
|
+
* - A literal MM-DD-YYYY is returned unchanged (the base date is ignored).
|
|
48
|
+
* - A relative word (today/yesterday/tomorrow, case-insensitive) is resolved
|
|
49
|
+
* against `baseDateStr` — a yyyy-MM-dd calendar date parsed by digits into a
|
|
50
|
+
* UTC anchor, then offset by ±1 day.
|
|
51
|
+
*
|
|
52
|
+
* If a relative word is given but `baseDateStr` is absent (or malformed), THROW.
|
|
53
|
+
* Core must never silently fall back to its own clock, because it also runs on
|
|
54
|
+
* the hosted transport (Cloud Run, UTC). Both first-party transports supply
|
|
55
|
+
* getCurrentDate(), so a missing base means a misbehaving/third-party client —
|
|
56
|
+
* a loud error beats a silently-wrong date.
|
|
57
|
+
*/
|
|
58
|
+
export function resolveDailyNotePage(value, baseDateStr) {
|
|
59
|
+
const offset = RELATIVE_OFFSETS[value.trim().toLowerCase()];
|
|
60
|
+
if (offset === undefined) {
|
|
61
|
+
return value; // literal MM-DD-YYYY passthrough — never alter it
|
|
62
|
+
}
|
|
63
|
+
if (baseDateStr === undefined) {
|
|
64
|
+
throw new RoamError(`Could not resolve relative date "${value}": this transport provides no current date.`, ErrorCodes.VALIDATION_ERROR);
|
|
65
|
+
}
|
|
66
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(baseDateStr.trim());
|
|
67
|
+
if (!m) {
|
|
68
|
+
throw new RoamError(`Could not resolve relative date "${value}": invalid current date "${baseDateStr}".`, ErrorCodes.VALIDATION_ERROR);
|
|
69
|
+
}
|
|
70
|
+
const year = Number(m[1]);
|
|
71
|
+
const month = Number(m[2]);
|
|
72
|
+
const day = Number(m[3]);
|
|
73
|
+
const anchor = new Date(Date.UTC(year, month - 1, day));
|
|
74
|
+
// The regex is shape-only — reject an out-of-range base (e.g. "2026-13-40")
|
|
75
|
+
// rather than letting Date.UTC roll it over into a silently-wrong date.
|
|
76
|
+
if (anchor.getUTCFullYear() !== year ||
|
|
77
|
+
anchor.getUTCMonth() !== month - 1 ||
|
|
78
|
+
anchor.getUTCDate() !== day) {
|
|
79
|
+
throw new RoamError(`Could not resolve relative date "${value}": invalid current date "${baseDateStr}".`, ErrorCodes.VALIDATION_ERROR);
|
|
80
|
+
}
|
|
81
|
+
anchor.setUTCDate(anchor.getUTCDate() + offset);
|
|
82
|
+
return toMmDdYyyy(anchor);
|
|
83
|
+
}
|
package/dist/tools.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface ClientToolDefinition {
|
|
|
8
8
|
type: "client";
|
|
9
9
|
title?: string;
|
|
10
10
|
annotations?: ToolAnnotations;
|
|
11
|
+
outputSchema?: z.AnyZodObject;
|
|
11
12
|
}
|
|
12
13
|
export interface StandaloneToolDefinition {
|
|
13
14
|
name: string;
|
|
@@ -17,11 +18,13 @@ export interface StandaloneToolDefinition {
|
|
|
17
18
|
type: "standalone";
|
|
18
19
|
title?: string;
|
|
19
20
|
annotations?: ToolAnnotations;
|
|
21
|
+
outputSchema?: z.AnyZodObject;
|
|
20
22
|
}
|
|
21
23
|
export type ToolDefinition = ClientToolDefinition | StandaloneToolDefinition;
|
|
22
24
|
type ToolMetaArg = {
|
|
23
25
|
title?: string;
|
|
24
26
|
annotations?: ToolAnnotations;
|
|
27
|
+
outputSchema?: z.AnyZodObject;
|
|
25
28
|
};
|
|
26
29
|
export declare function defineTool<T extends z.ZodRawShape>(name: string, description: string, schema: z.ZodObject<T>, action: (client: RoamActionClient, args: z.infer<z.ZodObject<T>>) => Promise<CallToolResult>, meta?: ToolMetaArg): ClientToolDefinition;
|
|
27
30
|
export declare function defineStandaloneTool<T extends z.ZodRawShape>(name: string, description: string, schema: z.ZodObject<T>, action: (args: z.infer<z.ZodObject<T>>) => Promise<CallToolResult>, meta?: ToolMetaArg): StandaloneToolDefinition;
|
|
@@ -30,6 +33,16 @@ export declare const desktopUiTools: ClientToolDefinition[];
|
|
|
30
33
|
export declare const contentTools: ClientToolDefinition[];
|
|
31
34
|
export declare const tools: ToolDefinition[];
|
|
32
35
|
export declare function findTool(name: string): ToolDefinition | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Drop `structuredContent` when the tool declares no `outputSchema`. structuredContent
|
|
38
|
+
* is only meaningful (and SDK-validated) when a schema is declared; for content-only
|
|
39
|
+
* tools (all reads + file/nav) it would just duplicate the text channel (e.g. a large
|
|
40
|
+
* file_get payload). Shared by both transports so the "schema-less ⇒ content-only"
|
|
41
|
+
* invariant can't drift between them.
|
|
42
|
+
*/
|
|
43
|
+
export declare function stripUndeclaredStructuredContent(result: CallToolResult, tool: {
|
|
44
|
+
outputSchema?: unknown;
|
|
45
|
+
}): CallToolResult;
|
|
33
46
|
export interface RouteToolCallOptions {
|
|
34
47
|
/**
|
|
35
48
|
* Resolve a graph identifier (nickname/name) to a ToolGraph. Required.
|
|
@@ -46,8 +59,8 @@ export interface RouteToolCallOptions {
|
|
|
46
59
|
/**
|
|
47
60
|
* "local-sync" runs the desktop token-info side-flow on get_graph_guidelines:
|
|
48
61
|
* parallel getTokenInfo, access-level validation, status writes, and result
|
|
49
|
-
* enrichment. "skip" (default) disables that side-flow entirely.
|
|
50
|
-
*
|
|
62
|
+
* enrichment. "skip" (default) disables that side-flow entirely. The graph
|
|
63
|
+
* field (withGraphField) is unaffected by this mode and runs in both.
|
|
51
64
|
*/
|
|
52
65
|
tokenInfoMode?: "local-sync" | "skip";
|
|
53
66
|
/**
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,cAAc,EAEd,WAAW,EACX,gBAAgB,EAChB,SAAS,EAET,eAAe,EAChB,MAAM,YAAY,CAAC;AA2EpB,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACnC,MAAM,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7E,IAAI,EAAE,QAAQ,CAAC;IAGf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAGD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IACnC,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;IACnD,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAAG,wBAAwB,CAAC;AAM7E,KAAK,WAAW,GAAG;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.js
CHANGED
|
@@ -28,6 +28,7 @@ export function defineTool(name, description, schema, action, meta) {
|
|
|
28
28
|
type: "client",
|
|
29
29
|
title: meta?.title,
|
|
30
30
|
annotations: meta?.annotations,
|
|
31
|
+
outputSchema: meta?.outputSchema,
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
// Helper to create standalone tool (no graph parameter, handles its own resolution)
|
|
@@ -40,6 +41,7 @@ export function defineStandaloneTool(name, description, schema, action, meta) {
|
|
|
40
41
|
type: "standalone",
|
|
41
42
|
title: meta?.title,
|
|
42
43
|
annotations: meta?.annotations,
|
|
44
|
+
outputSchema: meta?.outputSchema,
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
// Note appended to all client tool descriptions
|
|
@@ -51,10 +53,14 @@ const GUIDELINES_NOTE = "\n\n(If you haven't fetched this graph's guidelines yet
|
|
|
51
53
|
// mirror the backend's read/append/edit/delete classification. Hints only — NOT
|
|
52
54
|
// a security boundary; the backend still enforces authorization.
|
|
53
55
|
//
|
|
54
|
-
// readOnlyHint describes effects on the user's GRAPH CONTENT.
|
|
55
|
-
//
|
|
56
|
-
// ~/.roam-tools.json
|
|
57
|
-
//
|
|
56
|
+
// readOnlyHint describes effects on the user's GRAPH CONTENT. We keep it true for a
|
|
57
|
+
// few tools that do idempotent orientation scaffolding rather than a real write:
|
|
58
|
+
// get_graph_guidelines syncs local token status (~/.roam-tools.json, local-sync) and,
|
|
59
|
+
// on the hosted backend under an append/full grant, may auto-create today's daily note
|
|
60
|
+
// page + provision the "<name> (AI)" display page. These never fire for a read-only
|
|
61
|
+
// grant and are idempotent, so flipping to readOnlyHint:false would only make clients
|
|
62
|
+
// gate the very orientation tool they're told to call first. Contrast setup_new_graph,
|
|
63
|
+
// whose config write IS its purpose, so it is not read-only.
|
|
58
64
|
//
|
|
59
65
|
// openWorldHint is false for every tool except file_upload (its url path fetches
|
|
60
66
|
// an arbitrary external host server-side).
|
|
@@ -96,23 +102,53 @@ const NAV = {
|
|
|
96
102
|
};
|
|
97
103
|
// file_upload's url path does a server-side fetch of an arbitrary host.
|
|
98
104
|
const UPLOAD = { ...APPEND, openWorldHint: true };
|
|
105
|
+
// ----------------------------------------------------------------------------
|
|
106
|
+
// Output schemas (MCP tools/list structured-result hints) — WRITE TOOLS ONLY.
|
|
107
|
+
// The 8 write tools declare a schema (and emit structuredContent); the 9 read
|
|
108
|
+
// tools are content-only. Why write-only:
|
|
109
|
+
// - structuredContent duplicates the whole result into the text channel
|
|
110
|
+
// (textResult already JSON-stringifies it), so a schema on big reads
|
|
111
|
+
// (get_page/search) just doubles the payload for no gain.
|
|
112
|
+
// - read output shapes still evolve; write shapes ({success}/{uids}/{uid}) are
|
|
113
|
+
// small and finalized.
|
|
114
|
+
// CACHE HAZARD (drives the additive-only rule): ChatGPT caches the tools/list
|
|
115
|
+
// descriptor ~1 day and validates live responses against that STALE cached
|
|
116
|
+
// outputSchema. With .passthrough() + all-optional, additive changes survive a
|
|
117
|
+
// stale cache; a NON-additive change to a declared field (retype/rename/remove)
|
|
118
|
+
// can break the tool for ~a day. To change a declared field: new tool name, or
|
|
119
|
+
// expand-contract (add new field → wait out the cache → drop the old). See the
|
|
120
|
+
// chatgpt-mcp-annotations-and-tool-cache finding. NOTE: .optional() accepts
|
|
121
|
+
// absent/undefined but REJECTS null — a declared field whose producer can emit
|
|
122
|
+
// null needs .nullable(). `graph` is injected by withGraphField (canonical name).
|
|
123
|
+
// ----------------------------------------------------------------------------
|
|
124
|
+
const SuccessOutput = z
|
|
125
|
+
.object({ success: z.boolean().optional(), graph: z.string().optional() })
|
|
126
|
+
.passthrough();
|
|
127
|
+
const UidOutput = z
|
|
128
|
+
.object({ uid: z.string().optional(), graph: z.string().optional() })
|
|
129
|
+
.passthrough();
|
|
130
|
+
const UidsOutput = z
|
|
131
|
+
.object({ uids: z.array(z.string()).optional(), graph: z.string().optional() })
|
|
132
|
+
.passthrough();
|
|
99
133
|
// Data Tools (require graph/client; reusable across local + hosted MCP transports)
|
|
100
134
|
export const dataTools = [
|
|
101
135
|
defineTool("get_graph_guidelines", "Returns this graph's agent-facing setup: naming conventions, structural preferences, orientation actions, and any constraints the user has explicitly recorded for AI agents. Call once per graph per session before reading or writing content — skipping it means operating on assumptions the user has already overridden, so your work will likely need to be redone. The `nextSteps` field in the response lists orientation actions to take before proceeding.", GetGuidelinesSchema, getGuidelines, { title: "Get graph guidelines", annotations: READ }),
|
|
102
|
-
defineTool("create_page", "Create a new page in Roam, optionally with markdown content." + GUIDELINES_NOTE, CreatePageSchema, createPage, { title: "Create page", annotations: APPEND }),
|
|
136
|
+
defineTool("create_page", "Create a new page in Roam, optionally with markdown content." + GUIDELINES_NOTE, CreatePageSchema, createPage, { title: "Create page", annotations: APPEND, outputSchema: UidOutput }),
|
|
103
137
|
defineTool("create_block", "Create blocks from markdown content. Target by parentUid, pageTitle, or dailyNotePage (page created if needed). Use nestUnder to insert under a specific child block. Supports nested bulleted lists via markdown indentation." +
|
|
104
|
-
GUIDELINES_NOTE, CreateBlockSchema, createBlock, { title: "Create blocks", annotations: APPEND }),
|
|
138
|
+
GUIDELINES_NOTE, CreateBlockSchema, createBlock, { title: "Create blocks", annotations: APPEND, outputSchema: UidsOutput }),
|
|
105
139
|
defineTool("update_block", "Update a single existing block's text and display properties (heading, collapse, alignment, children view type). Affects only the block with `uid`: `string` sets that block's literal text — it is NOT expanded into child blocks (unlike create_block/create_page), and the block's existing children are left untouched. To add or restructure children, use create_block / move_block / delete_block." +
|
|
106
|
-
GUIDELINES_NOTE, UpdateBlockSchema, updateBlock, { title: "Update block", annotations: EDIT }),
|
|
107
|
-
defineTool("delete_block",
|
|
108
|
-
|
|
140
|
+
GUIDELINES_NOTE, UpdateBlockSchema, updateBlock, { title: "Update block", annotations: EDIT, outputSchema: SuccessOutput }),
|
|
141
|
+
defineTool("delete_block", 'Delete a block and all its descendants — irreversible. If the block is referenced elsewhere, deletion REPLACES those ((uid)) refs with its text (graph surgery, not string removal — on approval you can instead delete the referencing blocks). Inspect with get_block first: its markdown flags referenced blocks with `refs="N"` (and `hiddenChildren="N"` for subtrees beyond maxDepth), and comments count as refs. If anything shows `refs`, or the subtree is large (~20+ blocks / 500+ words), check get_backlinks and confirm with the user before deleting. For cleanup, only delete blocks created this task or named by the user.' +
|
|
142
|
+
GUIDELINES_NOTE, DeleteBlockSchema, deleteBlock, { title: "Delete block", annotations: DELETE, outputSchema: SuccessOutput }),
|
|
143
|
+
defineTool("move_block", "Move a block to a new location." + GUIDELINES_NOTE, MoveBlockSchema, moveBlock, { title: "Move block", annotations: EDIT, outputSchema: SuccessOutput }),
|
|
109
144
|
defineTool("add_comment", "Add a comment to a block (comment thread, NOT a child block). Prefer `comment` for simple text; use `commentMarkdown` for structured content. Same-day calls on the same block append to your existing comment." +
|
|
110
|
-
GUIDELINES_NOTE, AddCommentSchema, addComment, { title: "Add comment", annotations: APPEND }),
|
|
145
|
+
GUIDELINES_NOTE, AddCommentSchema, addComment, { title: "Add comment", annotations: APPEND, outputSchema: UidsOutput }),
|
|
111
146
|
defineTool("get_comments", "Get comments on a block with author, timestamps, and edit info. If singleEditableUid is set, the comment can be edited with update_block. Only works for blocks, not pages." +
|
|
112
147
|
GUIDELINES_NOTE, GetCommentsSchema, getComments, { title: "Get comments", annotations: READ }),
|
|
113
|
-
defineTool("delete_page", "Delete a page and all its
|
|
148
|
+
defineTool("delete_page", "Delete a page and all its blocks — irreversible. If the page is referenced elsewhere, deleting it also edits every block that links to it — those `[[page]]` references are removed (graph surgery, not just removing the page). Inspect with get_page first: its header shows the page's `refs:` count (how many blocks link to it). If `refs:` is non-zero, or the page has substantial content, confirm with the user before deleting. For cleanup, only delete pages created this task or named by the user." +
|
|
149
|
+
GUIDELINES_NOTE, DeletePageSchema, deletePage, { title: "Delete page", annotations: DELETE, outputSchema: SuccessOutput }),
|
|
114
150
|
defineTool("update_page", "Update a page's title or children view type. Set mergePages to true if renaming to a title that already exists." +
|
|
115
|
-
GUIDELINES_NOTE, UpdatePageSchema, updatePage, { title: "Update page", annotations: EDIT }),
|
|
151
|
+
GUIDELINES_NOTE, UpdatePageSchema, updatePage, { title: "Update page", annotations: EDIT, outputSchema: SuccessOutput }),
|
|
116
152
|
defineTool("search", "Search for pages and blocks by text. Returns paginated results with markdown content and optional breadcrumb paths. Call with an empty query to get recently edited and viewed content — useful for understanding what the user is currently working on." +
|
|
117
153
|
GUIDELINES_NOTE, SearchSchema, search, { title: "Search", annotations: READ }),
|
|
118
154
|
defineTool("search_templates", "Search Roam templates by name. When the user mentions 'my X template' or 'the X template', use this tool to find it. Templates are user-created reusable content blocks tagged with [[roam/templates]]. Returns template name, uid, and content as markdown." +
|
|
@@ -150,25 +186,54 @@ export function findTool(name) {
|
|
|
150
186
|
return tools.find((t) => t.name === name);
|
|
151
187
|
}
|
|
152
188
|
/**
|
|
153
|
-
*
|
|
189
|
+
* Drop `structuredContent` when the tool declares no `outputSchema`. structuredContent
|
|
190
|
+
* is only meaningful (and SDK-validated) when a schema is declared; for content-only
|
|
191
|
+
* tools (all reads + file/nav) it would just duplicate the text channel (e.g. a large
|
|
192
|
+
* file_get payload). Shared by both transports so the "schema-less ⇒ content-only"
|
|
193
|
+
* invariant can't drift between them.
|
|
154
194
|
*/
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
const content = result.content;
|
|
158
|
-
if (!content || content.length === 0)
|
|
195
|
+
export function stripUndeclaredStructuredContent(result, tool) {
|
|
196
|
+
if (tool.outputSchema || result.structuredContent === undefined)
|
|
159
197
|
return result;
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
198
|
+
const stripped = { ...result };
|
|
199
|
+
delete stripped.structuredContent;
|
|
200
|
+
return stripped;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Carry the resolved graph identity as a structured `graph` field rather than a
|
|
204
|
+
* "Roam graph: <name>" text prefix (which read as block content and made a read's
|
|
205
|
+
* JSON text non-parseable). Injects the canonical graph name into
|
|
206
|
+
* structuredContent (write tools) and into content[0].text when it parses to a
|
|
207
|
+
* plain JSON object (the only channel for content-only reads). Bare arrays/scalars
|
|
208
|
+
* (datalog raw text), images, non-JSON prose, and isError results are left
|
|
209
|
+
* untouched. Mirrors enrichResultWithTokenInfo's parse-and-rewrite.
|
|
210
|
+
*/
|
|
211
|
+
function withGraphField(result, graphName) {
|
|
212
|
+
let out = result;
|
|
213
|
+
const sc = result.structuredContent;
|
|
214
|
+
if (sc && typeof sc === "object" && !Array.isArray(sc)) {
|
|
215
|
+
// canonical resolved graph wins over any `graph` key the backend included
|
|
216
|
+
out = { ...out, structuredContent: { ...sc, graph: graphName } };
|
|
166
217
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
218
|
+
const first = out.content?.[0];
|
|
219
|
+
if (first && first.type === "text") {
|
|
220
|
+
try {
|
|
221
|
+
const parsed = JSON.parse(first.text);
|
|
222
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
223
|
+
out = {
|
|
224
|
+
...out,
|
|
225
|
+
content: [
|
|
226
|
+
{ ...first, text: JSON.stringify({ ...parsed, graph: graphName }, null, 2) },
|
|
227
|
+
...out.content.slice(1),
|
|
228
|
+
],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// not a JSON object (datalog raw array, prose) — leave the text untouched
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
172
237
|
}
|
|
173
238
|
/**
|
|
174
239
|
* Enrich a JSON text result with token info (accessLevel + scopes).
|
|
@@ -181,10 +246,19 @@ function enrichResultWithTokenInfo(result, info) {
|
|
|
181
246
|
const parsed = JSON.parse(first.text);
|
|
182
247
|
parsed.accessLevel = info.grantedAccessLevel;
|
|
183
248
|
parsed.scopes = info.grantedScopes;
|
|
184
|
-
|
|
249
|
+
const enriched = {
|
|
185
250
|
...result,
|
|
186
251
|
content: [{ ...first, text: JSON.stringify(parsed, null, 2) }, ...result.content.slice(1)],
|
|
187
252
|
};
|
|
253
|
+
// Keep structuredContent in sync with the enriched text (local-sync path).
|
|
254
|
+
if (result.structuredContent && typeof result.structuredContent === "object") {
|
|
255
|
+
enriched.structuredContent = {
|
|
256
|
+
...result.structuredContent,
|
|
257
|
+
accessLevel: info.grantedAccessLevel,
|
|
258
|
+
scopes: info.grantedScopes,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return enriched;
|
|
188
262
|
}
|
|
189
263
|
catch {
|
|
190
264
|
return result;
|
|
@@ -311,7 +385,7 @@ export async function routeToolCall(toolName, args, options) {
|
|
|
311
385
|
}
|
|
312
386
|
if (!result.isError) {
|
|
313
387
|
const enriched = enrichResultWithTokenInfo(result, info);
|
|
314
|
-
return
|
|
388
|
+
return withGraphField(enriched, resolvedGraph.name);
|
|
315
389
|
}
|
|
316
390
|
return result;
|
|
317
391
|
}
|
|
@@ -325,15 +399,15 @@ export async function routeToolCall(toolName, args, options) {
|
|
|
325
399
|
}
|
|
326
400
|
}
|
|
327
401
|
if (!result.isError) {
|
|
328
|
-
return
|
|
402
|
+
return withGraphField(result, resolvedGraph.name);
|
|
329
403
|
}
|
|
330
404
|
return result;
|
|
331
405
|
}
|
|
332
406
|
// Normal flow for all other tools (and get_graph_guidelines when token-info
|
|
333
|
-
// sync is skipped or unavailable).
|
|
407
|
+
// sync is skipped or unavailable). The graph field runs in both modes.
|
|
334
408
|
const result = await tool.action(client, restArgs);
|
|
335
409
|
if (!result.isError) {
|
|
336
|
-
return
|
|
410
|
+
return withGraphField(result, graph.name);
|
|
337
411
|
}
|
|
338
412
|
return result;
|
|
339
413
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -252,7 +252,7 @@ export interface GetPageResponse {
|
|
|
252
252
|
export interface GetBlockResponse {
|
|
253
253
|
uid: string;
|
|
254
254
|
markdown: string;
|
|
255
|
-
path: string;
|
|
255
|
+
path: string[];
|
|
256
256
|
queriedAt: string;
|
|
257
257
|
}
|
|
258
258
|
export interface QueryResult {
|
|
@@ -289,5 +289,6 @@ export type TokenInfoResult = {
|
|
|
289
289
|
export interface RoamActionClient {
|
|
290
290
|
call<T = unknown>(action: string, args?: unknown[]): Promise<RoamResponse<T>>;
|
|
291
291
|
getTokenInfo?(): Promise<TokenInfoResult>;
|
|
292
|
+
getCurrentDate?(): string | undefined;
|
|
292
293
|
}
|
|
293
294
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,YAAY,EACV,cAAc,EACd,WAAW,EACX,YAAY,EACZ,eAAe,GAChB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -2,7 +2,16 @@ import { z } from "zod";
|
|
|
2
2
|
// Helper to create a text result
|
|
3
3
|
export function textResult(value) {
|
|
4
4
|
const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
5
|
-
|
|
5
|
+
const result = { content: [{ type: "text", text }] };
|
|
6
|
+
// Additive structured output: when value is a plain object, also expose it as
|
|
7
|
+
// structuredContent so tools that declare an outputSchema satisfy the SDK's
|
|
8
|
+
// validation (it requires structuredContent on success). The text block above
|
|
9
|
+
// is unchanged. Arrays/strings/null get text only — tools returning those declare
|
|
10
|
+
// no outputSchema, so the registration strip-gate keeps them content-only.
|
|
11
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
12
|
+
result.structuredContent = value;
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
6
15
|
}
|
|
7
16
|
// Helper to create an image result
|
|
8
17
|
export function imageResult(data, mimeType) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roam-research/roam-tools-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.8",
|
|
4
4
|
"description": "Official transport-agnostic core for Roam Research MCP and CLI tools",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"test": "vitest run"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@modelcontextprotocol/sdk": "
|
|
35
|
+
"@modelcontextprotocol/sdk": ">=1.26.0 <2.0.0",
|
|
36
36
|
"zod": "^3.25.76"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|