From ddebf9d2a171edd67d62e4ad930ced8a053e8372 Mon Sep 17 00:00:00 2001 From: Charles Date: Thu, 16 Jul 2026 22:46:53 -0400 Subject: [PATCH] fix: allow structured leaf nodes without children --- CHANGELOG.md | 2 ++ src/tools/index.ts | 9 +++++---- test/unit/tools.test.ts | 25 +++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b8ac6b..596f1b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,8 @@ Versioning](https://semver.org/spec/v2.0.0.html). ### Fixed - Reject empty `parentRemId` values for `remnote_search` instead of treating them as unscoped search. +- Mark structured content node `children` arrays optional in `remnote_search` and `remnote_read_note` output schemas, + matching bridge responses that omit `children` for leaf nodes. ## [0.16.0] - 2026-06-05 diff --git a/src/tools/index.ts b/src/tools/index.ts index 4b8b85a..6f3d116 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -332,11 +332,12 @@ export const SEARCH_TOOL = { }, children: { type: 'array', - description: 'Nested child nodes (same shape recursively)', + description: + 'Nested child nodes (same shape recursively; omitted for leaf nodes)', items: { type: 'object' }, }, }, - required: ['remId', 'title', 'headline', 'remType', 'children'], + required: ['remId', 'title', 'headline', 'remType'], }, }, contentProperties: { @@ -590,11 +591,11 @@ export const READ_NOTE_TOOL = { }, children: { type: 'array', - description: 'Nested child nodes (same shape recursively)', + description: 'Nested child nodes (same shape recursively; omitted for leaf nodes)', items: { type: 'object' }, }, }, - required: ['remId', 'title', 'headline', 'remType', 'children'], + required: ['remId', 'title', 'headline', 'remType'], }, }, contentProperties: { diff --git a/test/unit/tools.test.ts b/test/unit/tools.test.ts index e036d9b..11c7278 100644 --- a/test/unit/tools.test.ts +++ b/test/unit/tools.test.ts @@ -166,6 +166,31 @@ describe('Tool Definitions', () => { expect(searchResultProps.contentStructured).toBeDefined(); }); + it('should advertise structured child nodes with optional children arrays', () => { + const searchResultProps = (( + SEARCH_TOOL.outputSchema.properties.results as { + items?: { properties?: Record }; + } + ).items?.properties ?? {}) as Record; + const searchChildSchema = ( + searchResultProps.contentStructured as { + items?: { properties?: Record; required?: string[] }; + } + ).items; + const readProps = (READ_NOTE_TOOL.outputSchema.properties ?? {}) as Record; + const readChildSchema = ( + readProps.contentStructured as { + items?: { properties?: Record; required?: string[] }; + } + ).items; + + for (const schema of [searchChildSchema, readChildSchema]) { + expect(schema?.properties).toHaveProperty('children'); + expect(schema?.required).toEqual(['remId', 'title', 'headline', 'remType']); + expect(schema?.required).not.toContain('children'); + } + }); + it('should advertise cursor paging for SEARCH_TOOL and SEARCH_BY_TAG_TOOL', () => { const outputProps = SEARCH_TOOL.outputSchema.properties as Record; const searchByTagOutputProps = SEARCH_BY_TAG_TOOL.outputSchema.properties as Record<