diff --git a/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.graphql.ts b/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.graphql.ts index aab78137..aa329ff6 100644 --- a/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.graphql.ts +++ b/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.graphql.ts @@ -11,6 +11,8 @@ export const readDocs = gql` $includeBlocks: Boolean = false $blocksLimit: Int $blocksPage: Int + $item_id: ID + $column_id: String ) { docs( ids: $ids @@ -19,6 +21,8 @@ export const readDocs = gql` order_by: $order_by page: $page workspace_ids: $workspace_ids + item_id: $item_id + column_id: $column_id ) { id object_id diff --git a/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.test.ts b/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.test.ts index 8462091a..410df14c 100644 --- a/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.test.ts +++ b/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.test.ts @@ -89,10 +89,16 @@ describe('ReadDocsTool', () => { expect(result.pagination.current_page).toBe(1); }); - it('should return error when type or ids are missing in content mode', async () => { + it('should return error when type is missing in content mode', async () => { const result = await callToolByNameRawAsync(TOOL_NAME, { mode: 'content' }); - expect(result.content[0].text).toContain('type and ids are required'); + expect(result.content[0].text).toContain('type is required'); + }); + + it('should return error when ids are missing for non-item_column type', async () => { + const result = await callToolByNameRawAsync(TOOL_NAME, { mode: 'content', type: 'ids' }); + + expect(result.content[0].text).toContain('ids is required'); }); it('should fall back to object_ids when ids returns no results', async () => { @@ -125,6 +131,78 @@ describe('ReadDocsTool', () => { }); }); + // ─── item_column type ──────────────────────────────────────────────────────── + + describe('item_column type', () => { + const ITEM_ID = '8386012534'; + const COLUMN_ID = 'monday_doc_mkmt9fgg'; + + it('passes item_id and column_id to the API, not ids/object_ids', async () => { + mocks.mockRequest.mockResolvedValueOnce(mockDocsResponse).mockResolvedValueOnce(mockMarkdownResponse); + + await callToolByNameAsync(TOOL_NAME, { + mode: 'content', + type: 'item_column', + item_id: ITEM_ID, + column_id: COLUMN_ID, + }); + + const firstCall = mocks.getMockRequest().mock.calls[0]; + expect(firstCall[1]).toMatchObject({ + item_id: ITEM_ID, + column_id: COLUMN_ID, + ids: undefined, + object_ids: undefined, + }); + }); + + it('returns the doc content when found', async () => { + mocks.mockRequest.mockResolvedValueOnce(mockDocsResponse).mockResolvedValueOnce(mockMarkdownResponse); + + const result = await callToolByNameAsync(TOOL_NAME, { + type: 'item_column', + item_id: ITEM_ID, + column_id: COLUMN_ID, + }); + + expect(result.data).toHaveLength(1); + expect(result.data[0].id).toBe(DOC_ID); + }); + + it('returns error when item_id is missing', async () => { + const result = await callToolByNameRawAsync(TOOL_NAME, { + type: 'item_column', + column_id: COLUMN_ID, + }); + + expect(result.content[0].text).toContain('item_id and column_id are required'); + expect(mocks.getMockRequest()).not.toHaveBeenCalled(); + }); + + it('returns error when column_id is missing', async () => { + const result = await callToolByNameRawAsync(TOOL_NAME, { + type: 'item_column', + item_id: ITEM_ID, + }); + + expect(result.content[0].text).toContain('item_id and column_id are required'); + expect(mocks.getMockRequest()).not.toHaveBeenCalled(); + }); + + it('does NOT fall back to object_ids when item_column returns no docs', async () => { + mocks.mockRequest.mockResolvedValueOnce(mockEmptyDocsResponse); + + const result = await callToolByNameRawAsync(TOOL_NAME, { + type: 'item_column', + item_id: ITEM_ID, + column_id: COLUMN_ID, + }); + + expect(mocks.getMockRequest()).toHaveBeenCalledTimes(1); + expect(result.content[0].text).toContain('No documents found'); + }); + }); + // ─── version_history mode ─────────────────────────────────────────────────── describe('version_history mode', () => { diff --git a/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.ts b/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.ts index 7f25f223..6b14f98a 100644 --- a/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.ts +++ b/packages/agent-toolkit/src/core/tools/platform-api-tools/read-docs-tool/read-docs-tool.ts @@ -58,7 +58,7 @@ type GetDocCommentsQuery = { const CONTENT_MODE = 'content' as const; const VERSION_HISTORY_MODE = 'version_history' as const; -const QueryByIdEnum = z.enum(['ids', 'object_ids', 'workspace_ids']); +const QueryByIdEnum = z.enum(['ids', 'object_ids', 'workspace_ids', 'item_column']); const MAX_DIFF_POINTS = 10; @@ -121,6 +121,19 @@ export const readDocsToolSchema = { 'Maximum number of comments (updates) to fetch per item when include_comments is true. Defaults to 50. Only used in content mode.', ), + item_id: z + .string() + .optional() + .describe( + 'The ID of the item that contains the doc column. Required when type is "item_column".', + ), + column_id: z + .string() + .optional() + .describe( + 'The column ID of the doc column on the item. Required when type is "item_column".', + ), + // --- version_history mode fields --- version_history_limit: z .number() @@ -163,9 +176,10 @@ export class ReadDocsTool extends BaseMondayApiTool { return `Get information about monday.com documents. Supports two modes: MODE: "content" (default) — Fetch documents with their full markdown content. -- Requires: type ("ids" | "object_ids" | "workspace_ids") and ids array +- Requires: type ("ids" | "object_ids" | "workspace_ids" | "item_column") +- type "item_column": pass item_id and column_id to read a doc attached to a board item via a doc column (use when docs() by id returns empty). +- type "ids" / "object_ids" / "workspace_ids": pass ids array. If type "ids" returns no results, automatically retries with object_ids. - Supports pagination via page/limit. Check has_more_pages in response. -- If type "ids" returns no results, automatically retries with object_ids. - Set include_blocks: true to include block IDs, types, and positions in the response — required before calling update_doc. - Blocks default to 25 per page. Use blocks_limit and blocks_page to paginate through long documents. - Set include_comments: true to fetch all comments and replies on the document. Use comments_limit to control how many comments per item (default 50). @@ -194,8 +208,15 @@ MODE: "version_history" — Fetch the edit history of a single document. private async executeContent(input: ToolInputType): Promise> { try { - if (!input.type || !input.ids || input.ids.length === 0) { - return { content: 'Error: type and ids are required when mode is "content".' }; + if (!input.type) { + return { content: 'Error: type is required when mode is "content".' }; + } + if (input.type === 'item_column') { + if (!input.item_id || !input.column_id) { + return { content: 'Error: item_id and column_id are required when type is "item_column".' }; + } + } else if (!input.ids || input.ids.length === 0) { + return { content: 'Error: ids is required when mode is "content" for type "ids", "object_ids", or "workspace_ids".' }; } this.sessionContext.metadata = { @@ -219,9 +240,18 @@ MODE: "version_history" — Fetch the edit history of a single document. case 'workspace_ids': workspace_ids = input.ids; break; + case 'item_column': + // item_id and column_id are wired into variables below; no ids needed + break; } - type ReadDocsVariables = ReadDocsQueryVariables & { includeBlocks: boolean; blocksLimit?: number; blocksPage?: number }; + type ReadDocsVariables = ReadDocsQueryVariables & { + includeBlocks: boolean; + blocksLimit?: number; + blocksPage?: number; + item_id?: string; + column_id?: string; + }; const includeBlocks = input.include_blocks ?? false; const blocksPagination = includeBlocks ? { blocksLimit: input.blocks_limit, blocksPage: input.blocks_page } : {}; @@ -234,11 +264,15 @@ MODE: "version_history" — Fetch the edit history of a single document. workspace_ids, includeBlocks, ...blocksPagination, + ...(input.type === 'item_column' && { + item_id: input.item_id, + column_id: input.column_id, + }), }; let res = await this.mondayApi.request(readDocs, variables); - if ((!res.docs || res.docs.length === 0) && ids) { + if ((!res.docs || res.docs.length === 0) && ids && input.type !== 'item_column') { const fallbackVariables: ReadDocsVariables = { ids: undefined, object_ids: ids, diff --git a/packages/agent-toolkit/src/monday-graphql/schema.graphql b/packages/agent-toolkit/src/monday-graphql/schema.graphql index 574d3ca9..34618bc5 100644 --- a/packages/agent-toolkit/src/monday-graphql/schema.graphql +++ b/packages/agent-toolkit/src/monday-graphql/schema.graphql @@ -329,7 +329,9 @@ limit: Int = 25, "A list of associated board or object’s unique identifier." object_ids: [ID!], "Property to order by (created_at / used_at)." order_by: DocsOrderBy, "Page number to get, starting at 1." page: Int = 1, "A list of workspace ids the documents are contained in." -workspace_ids: [ID]): [Document] +workspace_ids: [ID], "The ID of the item that contains the doc column. Must be used together with column_id." +item_id: ID, "The column ID of the doc column on the item. Must be used together with item_id." +column_id: String): [Document] "Get a collection of folders. Note: This query won't return folders from closed workspaces to which you are not subscribed" folders( "A list of folders unique identifiers." ids: [ID!], "Number of items to get, the default is 25."