Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export const readDocs = gql`
$includeBlocks: Boolean = false
$blocksLimit: Int
$blocksPage: Int
$item_id: ID
$column_id: String
) {
docs(
ids: $ids
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down Expand Up @@ -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', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -163,9 +176,10 @@ export class ReadDocsTool extends BaseMondayApiTool<typeof readDocsToolSchema> {
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).
Expand Down Expand Up @@ -194,8 +208,15 @@ MODE: "version_history" — Fetch the edit history of a single document.

private async executeContent(input: ToolInputType<typeof readDocsToolSchema>): Promise<ToolOutputType<never>> {
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 = {
Expand All @@ -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 } : {};
Expand All @@ -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<ReadDocsQuery>(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,
Expand Down
4 changes: 3 additions & 1 deletion packages/agent-toolkit/src/monday-graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
Loading