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
2 changes: 1 addition & 1 deletion packages/agent-toolkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The toolkit includes several pre-built tools for common monday.com operations, o

### Workspace Operations

- `ListWorkspaceTool` - List workspaces available to the user, prioritizing workspaces where the user is a member
- `ListWorkspaceTool` - List workspaces available to the user

### Dynamic API Tools

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ describe('ListWorkspaceTool', () => {
workspaces: null,
};

// Both member and all workspaces return null
mocks.setResponses([response, response]);
mocks.setResponses([response]);

const args: inputType = {};

Expand All @@ -53,25 +52,19 @@ describe('ListWorkspaceTool', () => {
const parsed = parseToolResult(result);
expect(parsed.message).toBe('No workspaces found.');
expect(parsed.data).toEqual([]);
// Two calls: first member (empty), then all (also empty)
expect(mocks.getMockRequest()).toHaveBeenCalledTimes(2);
expect(mocks.getMockRequest()).toHaveBeenCalledTimes(1);

const firstCall = mocks.getMockRequest().mock.calls[0];
expect(firstCall[0]).toContain('query listWorkspaces');
expect(firstCall[1]).toMatchObject({ membershipKind: 'member' });

const secondCall = mocks.getMockRequest().mock.calls[1];
expect(secondCall[0]).toContain('query listWorkspaces');
expect(secondCall[1]).toMatchObject({ membershipKind: 'all' });
expect(firstCall[1]).toMatchObject({ membershipKind: 'all' });
});

it('should return "No workspaces found." when GraphQL query returns empty array', async () => {
const response = {
workspaces: [],
};

// Both member and all workspaces return empty array
mocks.setResponses([response, response]);
mocks.setResponses([response]);

const args: inputType = {};

Expand All @@ -80,21 +73,16 @@ describe('ListWorkspaceTool', () => {
const parsed = parseToolResult(result);
expect(parsed.message).toBe('No workspaces found.');
expect(parsed.data).toEqual([]);
// Two calls: first member (empty), then all (also empty)
expect(mocks.getMockRequest()).toHaveBeenCalledTimes(2);
expect(mocks.getMockRequest()).toHaveBeenCalledTimes(1);

const firstCall = mocks.getMockRequest().mock.calls[0];
expect(firstCall[0]).toContain('query listWorkspaces');
expect(firstCall[1]).toMatchObject({ membershipKind: 'member' });

const secondCall = mocks.getMockRequest().mock.calls[1];
expect(secondCall[0]).toContain('query listWorkspaces');
expect(secondCall[1]).toMatchObject({ membershipKind: 'all' });
expect(firstCall[1]).toMatchObject({ membershipKind: 'all' });
});
});

describe('Successful Flow Without SearchTerm', () => {
it('should list workspaces without search term (basic case) using member-only query', async () => {
it('should list all accessible workspaces without search term', async () => {
const response = {
workspaces: [
{ id: '123', name: 'Marketing Team', description: 'Marketing workspace' },
Expand All @@ -116,7 +104,7 @@ describe('ListWorkspaceTool', () => {
expect(mockCall[1]).toEqual({
limit: 100,
page: 1,
membershipKind: 'member',
membershipKind: 'all',
});

const parsed = parseToolResult(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,28 +72,30 @@ export class ListWorkspaceTool extends BaseMondayApiTool<typeof listWorkspaceToo
membershipKind,
});

// First, try to get workspaces where the user is a member (more relevant results)
const memberRes = await this.mondayApi.request<ListWorkspacesQuery>(
const initialMembershipKind = searchTermNormalized ? WorkspaceMembershipKind.Member : WorkspaceMembershipKind.All;

const initialRes = await this.mondayApi.request<ListWorkspacesQuery>(
listWorkspaces,
createVariables(WorkspaceMembershipKind.Member),
createVariables(initialMembershipKind),
);
const memberWorkspaces = filterNullWorkspaces(memberRes);
let workspaces = filterNullWorkspaces(initialRes);

const shouldFetchAllWorkspaces =
!arrayHasElements(memberWorkspaces) || (searchTermNormalized && !hasMatchingWorkspace(searchTermNormalized, memberWorkspaces));
searchTermNormalized &&
(!arrayHasElements(workspaces) || !hasMatchingWorkspace(searchTermNormalized, workspaces));

// Fetch all workspaces only if needed, otherwise use member workspaces
let workspaces = memberWorkspaces;
// Unfiltered listing uses all workspaces. For search, start with member
// workspaces and fall back to all workspaces when member workspaces do not match.

if (shouldFetchAllWorkspaces) {
const allWorkspacesRes = await this.mondayApi.request<ListWorkspacesQuery>(
listWorkspaces,
createVariables(WorkspaceMembershipKind.All),
);
workspaces = filterNullWorkspaces(allWorkspacesRes);
const allWorkspaces = filterNullWorkspaces(allWorkspacesRes);
workspaces = arrayHasElements(allWorkspaces) ? allWorkspaces : workspaces;
}


if (!arrayHasElements(workspaces)) {
return {
content: { message: 'No workspaces found.', data: [] },
Expand Down