-
Notifications
You must be signed in to change notification settings - Fork 51
Added mcp support tool #145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
b1fe050
b2f65a6
51c2704
e47f626
1644c22
cd4712b
7b7421e
e79a8b3
436bd55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| import { z } from 'zod'; | ||
| import { createMockApiClient } from './test-utils/mock-api-client'; | ||
| import { SendMcpSupportTool, sendMcpSupportToolSchema } from './send-mcp-support-tool'; | ||
| import { trackEvent } from '../../../utils/tracking.utils'; | ||
| import { extractTokenInfo } from '../../../utils/token.utils'; | ||
|
|
||
| jest.mock('../../../utils/tracking.utils'); | ||
| jest.mock('../../../utils/token.utils'); | ||
|
|
||
| const mockedTrackEvent = trackEvent as jest.MockedFunction<typeof trackEvent>; | ||
| const mockedExtractTokenInfo = extractTokenInfo as jest.MockedFunction<typeof extractTokenInfo>; | ||
|
|
||
| describe('Send MCP Support Tool', () => { | ||
| let mocks: ReturnType<typeof createMockApiClient>; | ||
|
|
||
| beforeEach(() => { | ||
| mocks = createMockApiClient(); | ||
| jest.clearAllMocks(); | ||
| mockedExtractTokenInfo.mockReturnValue({ | ||
| tid: 123, | ||
| aai: 456, | ||
| uid: 789, | ||
| actid: 101112, | ||
| rgn: 'us-east', | ||
| per: 'admin', | ||
| }); | ||
| }); | ||
|
|
||
| it('Sends feedback with complete tracking data', async () => { | ||
| const tool = new SendMcpSupportTool(mocks.mockApiClient, 'fake_token'); | ||
|
|
||
| const result = await tool.execute({ | ||
| type: 'bug_report', | ||
| message: 'This is a test bug report with sufficient details.', | ||
| }); | ||
|
|
||
| expect(result.content).toContain('Thank you for your bug report!'); | ||
| expect(result.content).toContain('sent to the monday MCP team'); | ||
|
|
||
| const feedbackCall = mockedTrackEvent.mock.calls.find( | ||
| call => call[0].name === 'monday_mcp_support_feedback' | ||
| ); | ||
| expect(feedbackCall).toBeDefined(); | ||
| expect(feedbackCall![0].data).toMatchObject({ | ||
| feedbackType: 'bug_report', | ||
| message: 'This is a test bug report with sufficient details.', | ||
| tid: 123, | ||
| aai: 456, | ||
| uid: 789, | ||
| }); | ||
| }); | ||
|
|
||
| it('Handles optional title field', async () => { | ||
| const tool = new SendMcpSupportTool(mocks.mockApiClient, 'fake_token'); | ||
|
|
||
| const withTitle = await tool.execute({ | ||
| type: 'feedback', | ||
| message: 'Message with title', | ||
| title: 'Test Title', | ||
| }); | ||
| expect(withTitle.content).toContain('Thank you'); | ||
|
|
||
| let feedbackCall = mockedTrackEvent.mock.calls.find( | ||
| call => call[0].name === 'monday_mcp_support_feedback' | ||
| ); | ||
| expect(feedbackCall![0].data.title).toBe('Test Title'); | ||
|
|
||
| jest.clearAllMocks(); | ||
|
|
||
| const withoutTitle = await tool.execute({ | ||
| type: 'feedback', | ||
| message: 'Message without title', | ||
| }); | ||
| expect(withoutTitle.content).toContain('Thank you'); | ||
|
|
||
| feedbackCall = mockedTrackEvent.mock.calls.find( | ||
| call => call[0].name === 'monday_mcp_support_feedback' | ||
| ); | ||
| expect(feedbackCall![0].data).not.toHaveProperty('title'); | ||
| }); | ||
|
|
||
| it('Captures token and context information', async () => { | ||
| const tool = new SendMcpSupportTool(mocks.mockApiClient, 'fake_token', { | ||
| boardId: 12345, | ||
| agentType: 'test-agent', | ||
| agentClientName: 'TestClient', | ||
| }); | ||
|
|
||
| await tool.execute({ | ||
| type: 'feedback', | ||
| message: 'Test message with context', | ||
| }); | ||
|
|
||
| const feedbackCall = mockedTrackEvent.mock.calls.find( | ||
| call => call[0].name === 'monday_mcp_support_feedback' | ||
| ); | ||
| expect(feedbackCall![0].data).toMatchObject({ | ||
| boardId: 12345, | ||
| agentType: 'test-agent', | ||
| agentClientName: 'TestClient', | ||
| tid: 123, | ||
| uid: 789, | ||
| }); | ||
| }); | ||
|
|
||
| it('Works without token or context', async () => { | ||
| mockedExtractTokenInfo.mockReturnValue({}); | ||
| const tool = new SendMcpSupportTool(mocks.mockApiClient); | ||
|
|
||
| await tool.execute({ | ||
| type: 'feedback', | ||
| message: 'Test message without extras', | ||
| }); | ||
|
|
||
| const feedbackCall = mockedTrackEvent.mock.calls.find( | ||
| call => call[0].name === 'monday_mcp_support_feedback' | ||
| ); | ||
| expect(feedbackCall![0].data).toMatchObject({ | ||
| feedbackType: 'feedback', | ||
| message: 'Test message without extras', | ||
| }); | ||
| expect(feedbackCall![0].data).not.toHaveProperty('boardId'); | ||
| }); | ||
|
|
||
| it('Validates message length and type requirements', () => { | ||
| const schema = z.object(sendMcpSupportToolSchema); | ||
|
|
||
| // Too short | ||
| expect(schema.safeParse({ type: 'feedback', message: 'Short' }).success).toBe(false); | ||
|
|
||
| // Too long | ||
| expect(schema.safeParse({ type: 'feedback', message: 'a'.repeat(2001) }).success).toBe(false); | ||
|
|
||
| // Invalid type | ||
| expect(schema.safeParse({ type: 'invalid', message: 'Valid message' }).success).toBe(false); | ||
|
|
||
| // Valid | ||
| expect(schema.safeParse({ type: 'feedback', message: '1234567890' }).success).toBe(true); | ||
| }); | ||
|
|
||
| it('Validates title length requirement', () => { | ||
| const schema = z.object(sendMcpSupportToolSchema); | ||
|
|
||
| // Too long title | ||
| const longTitle = 'a'.repeat(101); | ||
| expect(schema.safeParse({ | ||
| type: 'bug_report', | ||
| message: 'Valid message', | ||
| title: longTitle | ||
| }).success).toBe(false); | ||
|
|
||
| // Valid title | ||
| expect(schema.safeParse({ | ||
| type: 'bug_report', | ||
| message: 'Valid message', | ||
| title: 'Valid Title' | ||
| }).success).toBe(true); | ||
| }); | ||
| }); | ||
|
|
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,80 @@ | ||||
| import { z } from 'zod'; | ||||
| import { ToolInputType, ToolOutputType, ToolType } from '../../tool'; | ||||
| import { BaseMondayApiTool, createMondayApiAnnotations } from './base-monday-api-tool'; | ||||
| import { trackEvent } from '../../../utils/tracking.utils'; | ||||
| import { extractTokenInfo } from '../../../utils/token.utils'; | ||||
|
|
||||
| export const sendMcpSupportToolSchema = { | ||||
| type: z | ||||
| .enum(['feature_request', 'bug_report', 'feedback', 'other']) | ||||
| .describe('The type of message being sent to the MCP support team'), | ||||
| message: z | ||||
| .string() | ||||
| .min(10, 'Message must be at least 10 characters') | ||||
| .max(2000, 'Message must be at most 2000 characters') | ||||
| .describe('The detailed message, bug report, feature request, or feedback (max 2000 chars)'), | ||||
| title: z | ||||
| .string() | ||||
| .max(100) | ||||
| .optional() | ||||
| .describe('Optional brief subject line summarizing the feedback'), | ||||
| }; | ||||
|
|
||||
| export class SendMcpSupportTool extends BaseMondayApiTool<typeof sendMcpSupportToolSchema> { | ||||
| name = 'send_mcp_support'; | ||||
| type = ToolType.READ; | ||||
| annotations = createMondayApiAnnotations({ | ||||
| title: 'Send Feedback to MCP Team', | ||||
| readOnlyHint: true, | ||||
| destructiveHint: false, | ||||
| idempotentHint: false, | ||||
| }); | ||||
|
|
||||
| getDescription(): string { | ||||
| return `Send the monday MCP team a feature_request, bug_report, feedback or any other message. We will look at every message to fix issues and design what to build next. | ||||
|
|
||||
| Use this tool proactively when: | ||||
| - Something goes wrong that shouldn't have | ||||
damian-rakus marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| - The user expresses frustration with the MCP | ||||
| - The user requests a feature that doesn't exist | ||||
| - The user provides general feedback about their experience | ||||
|
|
||||
| The MCP team reviews all submissions to improve the toolkit.`; | ||||
|
||||
| The MCP team reviews all submissions to improve the toolkit.`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notice I did not add a toolType and toolName as I don't think it is needed (we do have those in our track tool event)
damian-rakus marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think we should speak in first person like this - will be confusing as to who this message is coming from
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I fully understand your comment.
I changed the text to:
Thank you for your ${feedbackTypeLabel}! Your message has been sent to the monday MCP team. The team reviews all feedback to improve the toolkit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh got it.
Changed it to:
Message has been sent to the monday MCP team. The team reviews all feedback to improve the toolkit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We talked about it online:
Changed the message to:
Message has been sent successfully. Thank you for your feedback!
Uh oh!
There was an error while loading. Please reload this page.