@lovelybunch/mcp 1.0.75-alpha.8 → 1.0.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Tests for resources tool definition
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import { resourcesTool } from './resources-tool.js';
7
+
8
+ describe('resourcesTool', () => {
9
+ it('should have correct name', () => {
10
+ expect(resourcesTool.name).toBe('resources');
11
+ });
12
+
13
+ it('should have a description', () => {
14
+ expect(resourcesTool.description).toBeTruthy();
15
+ expect(resourcesTool.description).toContain('resource');
16
+ });
17
+
18
+ it('should have correct parameter structure', () => {
19
+ expect(resourcesTool.parameters.type).toBe('object');
20
+ expect(resourcesTool.parameters.required).toContain('operation');
21
+ expect(resourcesTool.parameters.additionalProperties).toBe(false);
22
+ });
23
+
24
+ describe('operations', () => {
25
+ it('should include all six operations', () => {
26
+ const ops = resourcesTool.parameters.properties.operation.enum;
27
+ expect(ops).toContain('list');
28
+ expect(ops).toContain('get');
29
+ expect(ops).toContain('generate_image');
30
+ expect(ops).toContain('generate_audio');
31
+ expect(ops).toContain('generate_video');
32
+ expect(ops).toContain('add_from_url');
33
+ expect(ops).toHaveLength(6);
34
+ });
35
+ });
36
+
37
+ describe('READ parameters', () => {
38
+ it('should have query parameter for list filtering', () => {
39
+ expect(resourcesTool.parameters.properties.query).toBeDefined();
40
+ expect(resourcesTool.parameters.properties.query.type).toBe('string');
41
+ });
42
+
43
+ it('should have type_filter parameter', () => {
44
+ expect(resourcesTool.parameters.properties.type_filter).toBeDefined();
45
+ expect(resourcesTool.parameters.properties.type_filter.type).toBe('string');
46
+ });
47
+
48
+ it('should have resource_id parameter for get', () => {
49
+ expect(resourcesTool.parameters.properties.resource_id).toBeDefined();
50
+ expect(resourcesTool.parameters.properties.resource_id.type).toBe('string');
51
+ });
52
+ });
53
+
54
+ describe('generate_image parameters', () => {
55
+ it('should have prompt parameter', () => {
56
+ expect(resourcesTool.parameters.properties.prompt).toBeDefined();
57
+ expect(resourcesTool.parameters.properties.prompt.type).toBe('string');
58
+ });
59
+
60
+ it('should have model parameter with valid enum values', () => {
61
+ const modelParam = resourcesTool.parameters.properties.model;
62
+ expect(modelParam).toBeDefined();
63
+ expect(modelParam.type).toBe('string');
64
+ expect(modelParam.enum).toContain('nano-banana-pro');
65
+ expect(modelParam.enum).toContain('flux-2-dev');
66
+ expect(modelParam.enum).toContain('flux-2-pro');
67
+ expect(modelParam.enum).toContain('gpt-image-1.5');
68
+ });
69
+
70
+ it('should have aspect_ratio parameter', () => {
71
+ expect(resourcesTool.parameters.properties.aspect_ratio).toBeDefined();
72
+ expect(resourcesTool.parameters.properties.aspect_ratio.type).toBe('string');
73
+ });
74
+ });
75
+
76
+ describe('generate_audio parameters', () => {
77
+ it('should have text parameter', () => {
78
+ expect(resourcesTool.parameters.properties.text).toBeDefined();
79
+ expect(resourcesTool.parameters.properties.text.type).toBe('string');
80
+ });
81
+
82
+ it('should have voice parameter', () => {
83
+ expect(resourcesTool.parameters.properties.voice).toBeDefined();
84
+ expect(resourcesTool.parameters.properties.voice.type).toBe('string');
85
+ });
86
+ });
87
+
88
+ describe('generate_video parameters', () => {
89
+ it('should have duration parameter', () => {
90
+ expect(resourcesTool.parameters.properties.duration).toBeDefined();
91
+ expect(resourcesTool.parameters.properties.duration.type).toBe('number');
92
+ });
93
+
94
+ it('should have resolution parameter', () => {
95
+ expect(resourcesTool.parameters.properties.resolution).toBeDefined();
96
+ expect(resourcesTool.parameters.properties.resolution.type).toBe('string');
97
+ });
98
+ });
99
+
100
+ describe('add_from_url parameters', () => {
101
+ it('should have url parameter', () => {
102
+ expect(resourcesTool.parameters.properties.url).toBeDefined();
103
+ expect(resourcesTool.parameters.properties.url.type).toBe('string');
104
+ });
105
+ });
106
+
107
+ describe('shared metadata parameters', () => {
108
+ it('should have tags parameter', () => {
109
+ expect(resourcesTool.parameters.properties.tags).toBeDefined();
110
+ expect(resourcesTool.parameters.properties.tags.type).toBe('string');
111
+ });
112
+
113
+ it('should have description parameter', () => {
114
+ expect(resourcesTool.parameters.properties.description).toBeDefined();
115
+ expect(resourcesTool.parameters.properties.description.type).toBe('string');
116
+ });
117
+ });
118
+
119
+ describe('schema validation compatibility', () => {
120
+ it('should only require operation (all other params are optional)', () => {
121
+ expect(resourcesTool.parameters.required).toEqual(['operation']);
122
+ });
123
+
124
+ it('should disallow additional properties', () => {
125
+ expect(resourcesTool.parameters.additionalProperties).toBe(false);
126
+ });
127
+
128
+ it('should have descriptions for all parameters', () => {
129
+ const props = resourcesTool.parameters.properties;
130
+ for (const [key, value] of Object.entries(props)) {
131
+ if (key !== 'operation') {
132
+ expect((value as any).description, `${key} should have a description`).toBeTruthy();
133
+ }
134
+ }
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,77 @@
1
+ import type { MCPTool } from './tasks-tool.js'
2
+
3
+ export const resourcesTool: MCPTool = {
4
+ name: 'resources',
5
+ description: 'Manage media resources: generate images, audio, and video via AI, save files from URLs, and browse existing resources. Generation operations auto-save to the resource library with thumbnails.',
6
+ parameters: {
7
+ type: 'object',
8
+ properties: {
9
+ operation: {
10
+ type: 'string',
11
+ enum: ['list', 'get', 'generate_image', 'generate_audio', 'generate_video', 'add_from_url'],
12
+ description: 'The resource operation to execute'
13
+ },
14
+ // READ params
15
+ query: {
16
+ type: 'string',
17
+ description: 'Search filter for list operation — matches name, description, or tags'
18
+ },
19
+ type_filter: {
20
+ type: 'string',
21
+ description: 'Filter by media type prefix for list operation (e.g. "image", "audio", "video")'
22
+ },
23
+ resource_id: {
24
+ type: 'string',
25
+ description: 'Resource ID for get operation (e.g. "res-1234567890-abc123def")'
26
+ },
27
+ // generate_image params
28
+ prompt: {
29
+ type: 'string',
30
+ description: 'Text prompt describing the image or video to generate'
31
+ },
32
+ model: {
33
+ type: 'string',
34
+ enum: ['nano-banana-pro', 'flux-2-dev', 'flux-2-pro', 'gpt-image-1.5'],
35
+ description: 'AI model for image generation. Default: nano-banana-pro. Use gpt-image-1.5 for transparent backgrounds.'
36
+ },
37
+ aspect_ratio: {
38
+ type: 'string',
39
+ description: 'Aspect ratio for image/video (e.g. "1:1", "16:9", "9:16", "4:3", "3:2"). Default: 16:9 for images, 16:9 for video.'
40
+ },
41
+ // generate_audio params
42
+ text: {
43
+ type: 'string',
44
+ description: 'Text to convert to speech for audio generation'
45
+ },
46
+ voice: {
47
+ type: 'string',
48
+ description: 'Voice ID for audio generation. Default: English_Trustworth_Man'
49
+ },
50
+ // generate_video params (prompt is reused)
51
+ duration: {
52
+ type: 'number',
53
+ description: 'Video duration in seconds, 5-16. Default: 8'
54
+ },
55
+ resolution: {
56
+ type: 'string',
57
+ description: 'Video resolution (480p, 720p, 1080p). Default: 720p'
58
+ },
59
+ // add_from_url params
60
+ url: {
61
+ type: 'string',
62
+ description: 'URL to download and save as a resource'
63
+ },
64
+ // Shared metadata
65
+ tags: {
66
+ type: 'string',
67
+ description: 'Comma-separated tags for the resource'
68
+ },
69
+ description: {
70
+ type: 'string',
71
+ description: 'Description of the resource'
72
+ }
73
+ },
74
+ required: ['operation'],
75
+ additionalProperties: false
76
+ }
77
+ }
@@ -3,7 +3,7 @@
3
3
  * Allows the AI assistant to read and update the role definition document (.nut/context/role.md)
4
4
  */
5
5
 
6
- import type { MCPTool } from './proposals-tool.js'
6
+ import type { MCPTool } from './tasks-tool.js'
7
7
 
8
8
  /**
9
9
  * Role context tool for AI assistant.
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Tests for tasks tool utilities
3
+ */
4
+
5
+ import { describe, it, expect } from 'vitest';
6
+ import {
7
+ validateTaskData,
8
+ createToolCall,
9
+ listTasksTool,
10
+ tasksTool,
11
+ tasksReadOnlyTool,
12
+ tasksFullTool,
13
+ } from './tasks-tool.js';
14
+
15
+ describe('validateTaskData', () => {
16
+ it('should extract intent and content', () => {
17
+ const result = validateTaskData({
18
+ intent: 'Add dark mode',
19
+ content: 'Implement dark mode toggle',
20
+ });
21
+ expect(result.intent).toBe('Add dark mode');
22
+ expect(result.content).toBe('Implement dark mode toggle');
23
+ });
24
+
25
+ it('should extract all supported fields', () => {
26
+ const result = validateTaskData({
27
+ intent: 'Test',
28
+ content: 'Body',
29
+ author: 'engineer',
30
+ planSteps: ['step1'],
31
+ status: 'draft',
32
+ metadata: { foo: 'bar' },
33
+ });
34
+ expect(result.intent).toBe('Test');
35
+ expect(result.content).toBe('Body');
36
+ expect(result.author).toBe('engineer');
37
+ expect(result.planSteps).toEqual(['step1']);
38
+ expect(result.status).toBe('draft');
39
+ expect(result.metadata).toEqual({ foo: 'bar' });
40
+ });
41
+
42
+ it('should return empty object for empty input', () => {
43
+ const result = validateTaskData({});
44
+ expect(result).toEqual({});
45
+ });
46
+
47
+ it('should ignore unknown fields', () => {
48
+ const result = validateTaskData({
49
+ intent: 'Test',
50
+ unknownField: 'ignored',
51
+ });
52
+ expect(result.intent).toBe('Test');
53
+ expect((result as any).unknownField).toBeUndefined();
54
+ });
55
+ });
56
+
57
+ describe('createToolCall', () => {
58
+ it('should create tool call with operation and args', () => {
59
+ const call = createToolCall('list', { filters: { status: 'draft' } });
60
+ expect(call.name).toBe('tasks');
61
+ expect(call.arguments).toEqual({
62
+ operation: 'list',
63
+ filters: { status: 'draft' },
64
+ });
65
+ });
66
+
67
+ it('should create tool call for get operation', () => {
68
+ const call = createToolCall('get', { id: 'cp-123' });
69
+ expect(call.name).toBe('tasks');
70
+ expect(call.arguments).toEqual({
71
+ operation: 'get',
72
+ id: 'cp-123',
73
+ });
74
+ });
75
+
76
+ it('should merge operation with spread args', () => {
77
+ const call = createToolCall('create', { task: { intent: 'Test' } });
78
+ expect(call.arguments.operation).toBe('create');
79
+ expect(call.arguments.task).toEqual({ intent: 'Test' });
80
+ });
81
+ });
82
+
83
+ describe('tasks tools', () => {
84
+ it('should have listTasksTool with correct structure', () => {
85
+ expect(listTasksTool.name).toBe('list_tasks');
86
+ expect(listTasksTool.parameters.type).toBe('object');
87
+ expect(listTasksTool.parameters.properties.filters).toBeDefined();
88
+ });
89
+
90
+ it('should have tasksTool with list, get, create, update, delete operations', () => {
91
+ expect(tasksTool.name).toBe('tasks');
92
+ const ops = tasksTool.parameters.properties.operation.enum;
93
+ expect(ops).toContain('list');
94
+ expect(ops).toContain('get');
95
+ expect(ops).toContain('create');
96
+ expect(ops).toContain('update');
97
+ expect(ops).toContain('delete');
98
+ });
99
+
100
+ it('should have tasksReadOnlyTool with only list and get', () => {
101
+ const ops = tasksReadOnlyTool.parameters.properties.operation.enum;
102
+ expect(ops).toEqual(['list', 'get']);
103
+ });
104
+
105
+ it('should have tasksFullTool with task and updates', () => {
106
+ expect(tasksFullTool.parameters.properties.task).toBeDefined();
107
+ expect(tasksFullTool.parameters.properties.updates).toBeDefined();
108
+ });
109
+
110
+ it('should mention summaries in tool descriptions to clarify list returns lightweight data', () => {
111
+ expect(tasksFullTool.description).toContain('summaries');
112
+ expect(tasksTool.description).toContain('summaries');
113
+ expect(tasksReadOnlyTool.description).toContain('summaries');
114
+ });
115
+
116
+ it('should mention using get for full details in tool descriptions', () => {
117
+ expect(tasksFullTool.description).toContain('get');
118
+ expect(tasksTool.description).toContain('get');
119
+ expect(tasksReadOnlyTool.description).toContain('get');
120
+ });
121
+
122
+ it('should mention default limit in filters description', () => {
123
+ expect(tasksFullTool.parameters.properties.filters.description).toContain('limit=20');
124
+ expect(tasksTool.parameters.properties.filters.description).toContain('limit=20');
125
+ expect(tasksReadOnlyTool.parameters.properties.filters.description).toContain('limit=20');
126
+ });
127
+
128
+ it('should expose limit in the filters properties via JSON schema', () => {
129
+ // Verify that the auto-generated filter properties include a limit field
130
+ const fullFilters = tasksFullTool.parameters.properties.filters.properties;
131
+ expect(fullFilters).toHaveProperty('limit');
132
+
133
+ const readOnlyFilters = tasksReadOnlyTool.parameters.properties.filters.properties;
134
+ expect(readOnlyFilters).toHaveProperty('limit');
135
+
136
+ const standardFilters = tasksTool.parameters.properties.filters.properties;
137
+ expect(standardFilters).toHaveProperty('limit');
138
+ });
139
+ });
@@ -0,0 +1,170 @@
1
+ import type { Task } from '@lovelybunch/types'
2
+ import { taskJsonSchemas } from '@lovelybunch/core'
3
+
4
+ export interface MCPTool {
5
+ name: string
6
+ description: string
7
+ parameters: {
8
+ type: 'object'
9
+ properties: Record<string, any>
10
+ required?: string[]
11
+ additionalProperties?: boolean
12
+ }
13
+ }
14
+
15
+ export interface MCPToolCall {
16
+ name: string
17
+ arguments: Record<string, any>
18
+ }
19
+
20
+ // Use pre-computed JSON schemas from core
21
+ const filtersProperties = taskJsonSchemas.filters
22
+ const createProperties = taskJsonSchemas.create
23
+ const updateProperties = taskJsonSchemas.update
24
+ const createRequired = taskJsonSchemas.createRequired
25
+
26
+ export const listTasksTool: MCPTool = {
27
+ name: "list_tasks",
28
+ description: "List all tasks with metadata only (title, ID, status, priority, tags)",
29
+ parameters: {
30
+ type: "object",
31
+ properties: {
32
+ filters: {
33
+ type: "object",
34
+ description: "Optional filters for the task list",
35
+ properties: filtersProperties
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ export const tasksTool: MCPTool = {
42
+ name: "tasks",
43
+ description: "Manage tasks - create, read, update, delete tasks. IMPORTANT: When searching for tasks by topic or keyword, always use filters.search to filter results server-side. The 'list' operation returns lightweight summaries — use 'get' with a task ID for full details.",
44
+ parameters: {
45
+ type: "object",
46
+ properties: {
47
+ operation: {
48
+ type: "string",
49
+ enum: ["list", "get", "create", "update", "delete"],
50
+ description: "The operation to perform on tasks"
51
+ },
52
+ id: {
53
+ type: "string",
54
+ description: "Task ID (required for get, update, delete operations)"
55
+ },
56
+ // Filters for list operation - auto-generated from Zod schema
57
+ filters: {
58
+ type: "object",
59
+ description: "Filters for list operation. Defaults to limit=20. Use search to find tasks by keyword — results are filtered server-side for accuracy.",
60
+ properties: filtersProperties
61
+ },
62
+ // Task data for create operation - auto-generated from Zod schema
63
+ task: {
64
+ type: "object",
65
+ description: "Task data for create/update operations. For create: intent and content are required. For update: at least one field is required.",
66
+ properties: createProperties,
67
+ required: [...createRequired]
68
+ }
69
+ },
70
+ required: ["operation"]
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Read-only version of the tasks tool for AI assistant.
76
+ * Only exposes list and get operations - no create/update/delete.
77
+ * Creating tasks should be done by coding agents (Claude Code, Cursor, etc.) or via the UI.
78
+ */
79
+ export const tasksReadOnlyTool: MCPTool = {
80
+ name: "tasks",
81
+ description: "READ-ONLY: Search and read tasks. You can ONLY use 'list' and 'get' operations. You CANNOT create, update, or delete tasks — that should be done by coding agents (Claude Code, Cursor, etc.) which have broader codebase context, or via the Tasks UI. The 'list' operation returns lightweight summaries — use 'get' with a task ID for full details.",
82
+ parameters: {
83
+ type: "object",
84
+ properties: {
85
+ operation: {
86
+ type: "string",
87
+ enum: ["list", "get"],
88
+ description: "ONLY 'list' or 'get' are allowed. 'list' to search/browse tasks (returns summaries), 'get' to retrieve a specific task by ID (returns full details)"
89
+ },
90
+ id: {
91
+ type: "string",
92
+ description: "Task ID (required for get operation)"
93
+ },
94
+ // Filters auto-generated from Zod schema
95
+ filters: {
96
+ type: "object",
97
+ description: "Filters for list operation. Defaults to limit=20. Use search to find tasks by keyword.",
98
+ properties: filtersProperties
99
+ }
100
+ },
101
+ required: ["operation"]
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Full CRUD tasks tool that includes create and update operations.
107
+ * Uses Zod schemas for validation.
108
+ */
109
+ export const tasksFullTool: MCPTool = {
110
+ name: "tasks",
111
+ description: "Full CRUD for tasks - create, read, update, delete. Use this when you need to create or modify tasks via MCP. The 'list' operation returns lightweight summaries (id, title, status, priority, tags, author, dates) — use 'get' with a specific task ID to retrieve full task details including content and plan steps.",
112
+ parameters: {
113
+ type: "object",
114
+ properties: {
115
+ operation: {
116
+ type: "string",
117
+ enum: ["list", "get", "create", "update", "delete"],
118
+ description: "The operation to perform"
119
+ },
120
+ id: {
121
+ type: "string",
122
+ description: "Task ID (required for get, update, delete operations)"
123
+ },
124
+ filters: {
125
+ type: "object",
126
+ description: "Filters for list operation. Defaults to limit=20. Use status, priority, tags, search, or author to narrow results.",
127
+ properties: filtersProperties
128
+ },
129
+ // For create - all fields from CreateTaskInputSchema
130
+ task: {
131
+ type: "object",
132
+ description: "Task data for create operation",
133
+ properties: createProperties,
134
+ required: [...createRequired]
135
+ },
136
+ // For update - partial fields from UpdateTaskInputSchema
137
+ updates: {
138
+ type: "object",
139
+ description: "Fields to update (for update operation). At least one field required.",
140
+ properties: updateProperties
141
+ }
142
+ },
143
+ required: ["operation"]
144
+ }
145
+ }
146
+
147
+ export function validateTaskData(data: any): Partial<Task> {
148
+ const task: Partial<Task> = {}
149
+
150
+ if (data.title) task.title = data.title
151
+ if (data.intent) task.intent = data.intent
152
+ if (data.content) task.content = data.content
153
+ if (data.author) task.author = data.author
154
+ if (data.planSteps) task.planSteps = data.planSteps
155
+ if (data.status) task.status = data.status
156
+ if (data.metadata) task.metadata = data.metadata
157
+ if (data.productSpecRef) task.productSpecRef = data.productSpecRef
158
+
159
+ return task
160
+ }
161
+
162
+ export function createToolCall(operation: string, args: any): MCPToolCall {
163
+ return {
164
+ name: "tasks",
165
+ arguments: {
166
+ operation,
167
+ ...args
168
+ }
169
+ }
170
+ }
@@ -1,109 +0,0 @@
1
- /**
2
- * Tests for proposals tool utilities
3
- */
4
-
5
- import { describe, it, expect } from 'vitest';
6
- import {
7
- validateProposalData,
8
- createToolCall,
9
- listProposalsTool,
10
- proposalsTool,
11
- proposalsReadOnlyTool,
12
- proposalsFullTool,
13
- } from './proposals-tool.js';
14
-
15
- describe('validateProposalData', () => {
16
- it('should extract intent and content', () => {
17
- const result = validateProposalData({
18
- intent: 'Add dark mode',
19
- content: 'Implement dark mode toggle',
20
- });
21
- expect(result.intent).toBe('Add dark mode');
22
- expect(result.content).toBe('Implement dark mode toggle');
23
- });
24
-
25
- it('should extract all supported fields', () => {
26
- const result = validateProposalData({
27
- intent: 'Test',
28
- content: 'Body',
29
- author: 'engineer',
30
- planSteps: ['step1'],
31
- status: 'draft',
32
- metadata: { foo: 'bar' },
33
- });
34
- expect(result.intent).toBe('Test');
35
- expect(result.content).toBe('Body');
36
- expect(result.author).toBe('engineer');
37
- expect(result.planSteps).toEqual(['step1']);
38
- expect(result.status).toBe('draft');
39
- expect(result.metadata).toEqual({ foo: 'bar' });
40
- });
41
-
42
- it('should return empty object for empty input', () => {
43
- const result = validateProposalData({});
44
- expect(result).toEqual({});
45
- });
46
-
47
- it('should ignore unknown fields', () => {
48
- const result = validateProposalData({
49
- intent: 'Test',
50
- unknownField: 'ignored',
51
- });
52
- expect(result.intent).toBe('Test');
53
- expect((result as any).unknownField).toBeUndefined();
54
- });
55
- });
56
-
57
- describe('createToolCall', () => {
58
- it('should create tool call with operation and args', () => {
59
- const call = createToolCall('list', { filters: { status: 'draft' } });
60
- expect(call.name).toBe('change_proposals');
61
- expect(call.arguments).toEqual({
62
- operation: 'list',
63
- filters: { status: 'draft' },
64
- });
65
- });
66
-
67
- it('should create tool call for get operation', () => {
68
- const call = createToolCall('get', { id: 'cp-123' });
69
- expect(call.name).toBe('change_proposals');
70
- expect(call.arguments).toEqual({
71
- operation: 'get',
72
- id: 'cp-123',
73
- });
74
- });
75
-
76
- it('should merge operation with spread args', () => {
77
- const call = createToolCall('create', { proposal: { intent: 'Test' } });
78
- expect(call.arguments.operation).toBe('create');
79
- expect(call.arguments.proposal).toEqual({ intent: 'Test' });
80
- });
81
- });
82
-
83
- describe('proposals tools', () => {
84
- it('should have listProposalsTool with correct structure', () => {
85
- expect(listProposalsTool.name).toBe('list_proposals');
86
- expect(listProposalsTool.parameters.type).toBe('object');
87
- expect(listProposalsTool.parameters.properties.filters).toBeDefined();
88
- });
89
-
90
- it('should have proposalsTool with list, get, create, update, delete operations', () => {
91
- expect(proposalsTool.name).toBe('change_proposals');
92
- const ops = proposalsTool.parameters.properties.operation.enum;
93
- expect(ops).toContain('list');
94
- expect(ops).toContain('get');
95
- expect(ops).toContain('create');
96
- expect(ops).toContain('update');
97
- expect(ops).toContain('delete');
98
- });
99
-
100
- it('should have proposalsReadOnlyTool with only list and get', () => {
101
- const ops = proposalsReadOnlyTool.parameters.properties.operation.enum;
102
- expect(ops).toEqual(['list', 'get']);
103
- });
104
-
105
- it('should have proposalsFullTool with create and updates', () => {
106
- expect(proposalsFullTool.parameters.properties.proposal).toBeDefined();
107
- expect(proposalsFullTool.parameters.properties.updates).toBeDefined();
108
- });
109
- });