@allpepper/task-orchestrator 0.1.0
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.
- package/README.md +15 -0
- package/package.json +51 -0
- package/src/db/client.ts +34 -0
- package/src/db/index.ts +1 -0
- package/src/db/migrate.ts +51 -0
- package/src/db/migrations/001_initial_schema.sql +160 -0
- package/src/domain/index.ts +1 -0
- package/src/domain/types.ts +225 -0
- package/src/index.ts +7 -0
- package/src/repos/base.ts +151 -0
- package/src/repos/dependencies.ts +356 -0
- package/src/repos/features.ts +507 -0
- package/src/repos/index.ts +4 -0
- package/src/repos/projects.ts +350 -0
- package/src/repos/sections.ts +505 -0
- package/src/repos/tags.example.ts +125 -0
- package/src/repos/tags.ts +175 -0
- package/src/repos/tasks.ts +581 -0
- package/src/repos/templates.ts +649 -0
- package/src/server.ts +121 -0
- package/src/services/index.ts +2 -0
- package/src/services/status-validator.ts +100 -0
- package/src/services/workflow.ts +104 -0
- package/src/tools/apply-template.ts +129 -0
- package/src/tools/get-blocked-tasks.ts +63 -0
- package/src/tools/get-next-status.ts +183 -0
- package/src/tools/get-next-task.ts +75 -0
- package/src/tools/get-tag-usage.ts +54 -0
- package/src/tools/index.ts +30 -0
- package/src/tools/list-tags.ts +56 -0
- package/src/tools/manage-container.ts +333 -0
- package/src/tools/manage-dependency.ts +198 -0
- package/src/tools/manage-sections.ts +388 -0
- package/src/tools/manage-template.ts +313 -0
- package/src/tools/query-container.ts +296 -0
- package/src/tools/query-dependencies.ts +68 -0
- package/src/tools/query-sections.ts +70 -0
- package/src/tools/query-templates.ts +137 -0
- package/src/tools/query-workflow-state.ts +198 -0
- package/src/tools/registry.ts +180 -0
- package/src/tools/rename-tag.ts +64 -0
- package/src/tools/setup-project.ts +189 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createSuccessResponse, createErrorResponse, uuidSchema, optionalUuidSchema } from './registry';
|
|
4
|
+
import {
|
|
5
|
+
createProject,
|
|
6
|
+
getProject,
|
|
7
|
+
updateProject,
|
|
8
|
+
deleteProject,
|
|
9
|
+
searchProjects,
|
|
10
|
+
getProjectOverview,
|
|
11
|
+
} from '../repos/projects';
|
|
12
|
+
import {
|
|
13
|
+
createFeature,
|
|
14
|
+
getFeature,
|
|
15
|
+
updateFeature,
|
|
16
|
+
deleteFeature,
|
|
17
|
+
searchFeatures,
|
|
18
|
+
getFeatureOverview,
|
|
19
|
+
} from '../repos/features';
|
|
20
|
+
import {
|
|
21
|
+
createTask,
|
|
22
|
+
getTask,
|
|
23
|
+
updateTask,
|
|
24
|
+
deleteTask,
|
|
25
|
+
searchTasks,
|
|
26
|
+
setTaskStatus,
|
|
27
|
+
} from '../repos/tasks';
|
|
28
|
+
import { getSections } from '../repos/sections';
|
|
29
|
+
import type { TaskCounts } from '../repos/base';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Unified query_container tool - read operations for containers
|
|
33
|
+
*
|
|
34
|
+
* Operations:
|
|
35
|
+
* - get: Retrieve a single container by ID
|
|
36
|
+
* - search: Search containers with filters
|
|
37
|
+
* - overview: Get global overview (all) or scoped overview (by ID + hierarchy)
|
|
38
|
+
*/
|
|
39
|
+
export function registerQueryContainerTool(server: McpServer): void {
|
|
40
|
+
server.tool(
|
|
41
|
+
'query_container',
|
|
42
|
+
'Unified read operations for containers (project, feature, task). Supports get, search, and overview operations.',
|
|
43
|
+
{
|
|
44
|
+
operation: z.enum(['get', 'search', 'overview']),
|
|
45
|
+
containerType: z.enum(['project', 'feature', 'task']),
|
|
46
|
+
id: optionalUuidSchema,
|
|
47
|
+
query: z.string().optional(),
|
|
48
|
+
status: z.string().optional(),
|
|
49
|
+
priority: z.string().optional(),
|
|
50
|
+
tags: z.string().optional(),
|
|
51
|
+
projectId: optionalUuidSchema,
|
|
52
|
+
featureId: optionalUuidSchema,
|
|
53
|
+
limit: z.number().int().optional().default(20),
|
|
54
|
+
offset: z.number().int().optional().default(0),
|
|
55
|
+
includeSections: z.boolean().optional().default(false),
|
|
56
|
+
},
|
|
57
|
+
async (params) => {
|
|
58
|
+
try {
|
|
59
|
+
const { operation, containerType, id } = params;
|
|
60
|
+
|
|
61
|
+
// ===== GET OPERATION =====
|
|
62
|
+
if (operation === 'get') {
|
|
63
|
+
if (!id) {
|
|
64
|
+
const response = createErrorResponse('id is required for get operation');
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Dispatch based on container type
|
|
71
|
+
let result;
|
|
72
|
+
if (containerType === 'project') {
|
|
73
|
+
result = getProject(id);
|
|
74
|
+
} else if (containerType === 'feature') {
|
|
75
|
+
result = getFeature(id);
|
|
76
|
+
} else {
|
|
77
|
+
result = getTask(id);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!result.success) {
|
|
81
|
+
const response = createErrorResponse(result.error || 'Failed to get container');
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let data: any = { [containerType]: result.data };
|
|
88
|
+
|
|
89
|
+
// Include sections if requested
|
|
90
|
+
if (params.includeSections) {
|
|
91
|
+
const entityTypeMap = {
|
|
92
|
+
project: 'PROJECT',
|
|
93
|
+
feature: 'FEATURE',
|
|
94
|
+
task: 'TASK',
|
|
95
|
+
};
|
|
96
|
+
const sectionsResult = getSections(id, entityTypeMap[containerType]);
|
|
97
|
+
if (sectionsResult.success) {
|
|
98
|
+
data.sections = sectionsResult.data;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Include task counts for features
|
|
103
|
+
if (containerType === 'feature') {
|
|
104
|
+
const overviewResult = getFeatureOverview(id);
|
|
105
|
+
if (overviewResult.success) {
|
|
106
|
+
data.taskCounts = overviewResult.data.taskCounts;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Include task counts for projects
|
|
111
|
+
if (containerType === 'project') {
|
|
112
|
+
const overviewResult = getProjectOverview(id);
|
|
113
|
+
if (overviewResult.success) {
|
|
114
|
+
data.taskCounts = overviewResult.data.taskCounts;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const response = createSuccessResponse(`${containerType} retrieved successfully`, data);
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ===== SEARCH OPERATION =====
|
|
125
|
+
if (operation === 'search') {
|
|
126
|
+
const searchParams = {
|
|
127
|
+
query: params.query,
|
|
128
|
+
status: params.status,
|
|
129
|
+
priority: params.priority,
|
|
130
|
+
tags: params.tags,
|
|
131
|
+
projectId: params.projectId,
|
|
132
|
+
featureId: params.featureId,
|
|
133
|
+
limit: params.limit ?? 20,
|
|
134
|
+
offset: params.offset ?? 0,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
let result;
|
|
138
|
+
if (containerType === 'project') {
|
|
139
|
+
result = searchProjects(searchParams);
|
|
140
|
+
} else if (containerType === 'feature') {
|
|
141
|
+
result = searchFeatures(searchParams);
|
|
142
|
+
} else {
|
|
143
|
+
result = searchTasks(searchParams);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!result.success) {
|
|
147
|
+
const response = createErrorResponse(result.error || 'Search failed');
|
|
148
|
+
return {
|
|
149
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const response = createSuccessResponse(
|
|
154
|
+
`Found ${result.data.length} ${containerType}(s)`,
|
|
155
|
+
{
|
|
156
|
+
items: result.data,
|
|
157
|
+
count: result.data.length,
|
|
158
|
+
limit: params.limit ?? 20,
|
|
159
|
+
offset: params.offset ?? 0,
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
return {
|
|
163
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ===== OVERVIEW OPERATION =====
|
|
168
|
+
if (operation === 'overview') {
|
|
169
|
+
// Global overview - search all with filters
|
|
170
|
+
if (!id) {
|
|
171
|
+
const searchParams = {
|
|
172
|
+
query: params.query,
|
|
173
|
+
status: params.status,
|
|
174
|
+
priority: params.priority,
|
|
175
|
+
tags: params.tags,
|
|
176
|
+
projectId: params.projectId,
|
|
177
|
+
featureId: params.featureId,
|
|
178
|
+
limit: params.limit ?? 20,
|
|
179
|
+
offset: params.offset ?? 0,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
let result;
|
|
183
|
+
if (containerType === 'project') {
|
|
184
|
+
result = searchProjects(searchParams);
|
|
185
|
+
} else if (containerType === 'feature') {
|
|
186
|
+
result = searchFeatures(searchParams);
|
|
187
|
+
} else {
|
|
188
|
+
result = searchTasks(searchParams);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!result.success) {
|
|
192
|
+
const response = createErrorResponse(result.error || 'Overview failed');
|
|
193
|
+
return {
|
|
194
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const response = createSuccessResponse(
|
|
199
|
+
`Global ${containerType} overview`,
|
|
200
|
+
{
|
|
201
|
+
items: result.data,
|
|
202
|
+
count: result.data.length,
|
|
203
|
+
limit: params.limit ?? 20,
|
|
204
|
+
offset: params.offset ?? 0,
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
return {
|
|
208
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Scoped overview - entity + hierarchy
|
|
213
|
+
if (containerType === 'project') {
|
|
214
|
+
const overviewResult = getProjectOverview(id);
|
|
215
|
+
if (!overviewResult.success) {
|
|
216
|
+
const response = createErrorResponse(
|
|
217
|
+
'error' in overviewResult ? overviewResult.error : 'Failed to get project overview'
|
|
218
|
+
);
|
|
219
|
+
return {
|
|
220
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Get features for this project
|
|
225
|
+
const featuresResult = searchFeatures({ projectId: id, limit: 100 });
|
|
226
|
+
const features = featuresResult.success ? featuresResult.data : [];
|
|
227
|
+
|
|
228
|
+
const response = createSuccessResponse('Project overview retrieved', {
|
|
229
|
+
project: overviewResult.data.project,
|
|
230
|
+
taskCounts: overviewResult.data.taskCounts,
|
|
231
|
+
features,
|
|
232
|
+
});
|
|
233
|
+
return {
|
|
234
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (containerType === 'feature') {
|
|
239
|
+
const overviewResult = getFeatureOverview(id);
|
|
240
|
+
if (!overviewResult.success) {
|
|
241
|
+
const response = createErrorResponse(
|
|
242
|
+
'error' in overviewResult ? overviewResult.error : 'Failed to get feature overview'
|
|
243
|
+
);
|
|
244
|
+
return {
|
|
245
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Get tasks for this feature
|
|
250
|
+
const tasksResult = searchTasks({ featureId: id, limit: 100 });
|
|
251
|
+
const tasks = tasksResult.success ? tasksResult.data : [];
|
|
252
|
+
|
|
253
|
+
const response = createSuccessResponse('Feature overview retrieved', {
|
|
254
|
+
feature: overviewResult.data.feature,
|
|
255
|
+
taskCounts: overviewResult.data.taskCounts,
|
|
256
|
+
tasks,
|
|
257
|
+
});
|
|
258
|
+
return {
|
|
259
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (containerType === 'task') {
|
|
264
|
+
const taskResult = getTask(id);
|
|
265
|
+
if (!taskResult.success) {
|
|
266
|
+
const response = createErrorResponse(
|
|
267
|
+
'error' in taskResult ? taskResult.error : 'Failed to get task overview'
|
|
268
|
+
);
|
|
269
|
+
return {
|
|
270
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// For tasks, just return the task (dependencies could be added in future)
|
|
275
|
+
const response = createSuccessResponse('Task overview retrieved', {
|
|
276
|
+
task: taskResult.data,
|
|
277
|
+
});
|
|
278
|
+
return {
|
|
279
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const response = createErrorResponse('Invalid operation');
|
|
285
|
+
return {
|
|
286
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
287
|
+
};
|
|
288
|
+
} catch (error: any) {
|
|
289
|
+
const response = createErrorResponse(error.message || 'Internal error');
|
|
290
|
+
return {
|
|
291
|
+
content: [{ type: 'text' as const, text: JSON.stringify(response, null, 2) }],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createSuccessResponse, createErrorResponse, uuidSchema } from './registry';
|
|
4
|
+
import { getDependencies } from '../repos/dependencies';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register the query_dependencies MCP tool.
|
|
8
|
+
*
|
|
9
|
+
* Queries dependencies for a task with optional direction filtering.
|
|
10
|
+
*
|
|
11
|
+
* @param server - The MCP server instance to register the tool with
|
|
12
|
+
*/
|
|
13
|
+
export function registerQueryDependenciesTool(server: McpServer): void {
|
|
14
|
+
server.tool(
|
|
15
|
+
'query_dependencies',
|
|
16
|
+
'Query dependencies for a task',
|
|
17
|
+
{
|
|
18
|
+
taskId: uuidSchema.describe('Task ID'),
|
|
19
|
+
direction: z
|
|
20
|
+
.enum(['dependencies', 'dependents', 'both'])
|
|
21
|
+
.optional()
|
|
22
|
+
.default('both')
|
|
23
|
+
.describe('Direction filter: dependencies, dependents, or both'),
|
|
24
|
+
},
|
|
25
|
+
async (params) => {
|
|
26
|
+
try {
|
|
27
|
+
const result = getDependencies(params.taskId, params.direction);
|
|
28
|
+
|
|
29
|
+
if (result.success === false) {
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: 'text' as const,
|
|
34
|
+
text: JSON.stringify(
|
|
35
|
+
createErrorResponse(result.error, result.code),
|
|
36
|
+
null,
|
|
37
|
+
2
|
|
38
|
+
),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: 'text' as const,
|
|
48
|
+
text: JSON.stringify(
|
|
49
|
+
createSuccessResponse('Dependencies retrieved', result.data),
|
|
50
|
+
null,
|
|
51
|
+
2
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
} catch (error: any) {
|
|
57
|
+
return {
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: 'text' as const,
|
|
61
|
+
text: JSON.stringify(createErrorResponse(error.message), null, 2),
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createSuccessResponse, createErrorResponse, uuidSchema } from './registry';
|
|
4
|
+
import { getSections } from '../repos/sections';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register the query_sections MCP tool
|
|
8
|
+
*
|
|
9
|
+
* Retrieves sections for a given entity with optional filtering and content inclusion.
|
|
10
|
+
*/
|
|
11
|
+
export function registerQuerySectionsTool(server: McpServer): void {
|
|
12
|
+
server.tool(
|
|
13
|
+
'query_sections',
|
|
14
|
+
'Retrieve sections for an entity (PROJECT, FEATURE, or TASK). Supports filtering by tags, section IDs, and optional content exclusion for token savings.',
|
|
15
|
+
{
|
|
16
|
+
entityType: z.enum(['PROJECT', 'FEATURE', 'TASK']).describe('The type of entity to query sections for'),
|
|
17
|
+
entityId: uuidSchema.describe('The UUID of the entity'),
|
|
18
|
+
includeContent: z.boolean().optional().default(true).describe('Set to false to exclude content field for token savings'),
|
|
19
|
+
tags: z.string().optional().describe('Comma-separated list of tags to filter sections'),
|
|
20
|
+
sectionIds: z.string().optional().describe('Comma-separated list of section IDs to filter')
|
|
21
|
+
},
|
|
22
|
+
async (params) => {
|
|
23
|
+
try {
|
|
24
|
+
// Parse sectionIds if provided
|
|
25
|
+
const sectionIds = params.sectionIds
|
|
26
|
+
? params.sectionIds.split(',').map(id => id.trim().replace(/-/g, ''))
|
|
27
|
+
: undefined;
|
|
28
|
+
|
|
29
|
+
// Call getSections with parsed parameters
|
|
30
|
+
const result = getSections(params.entityId, params.entityType, {
|
|
31
|
+
includeContent: params.includeContent,
|
|
32
|
+
tags: params.tags,
|
|
33
|
+
sectionIds
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Handle result
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
return {
|
|
39
|
+
content: [{
|
|
40
|
+
type: 'text' as const,
|
|
41
|
+
text: JSON.stringify(createErrorResponse(result.error), null, 2)
|
|
42
|
+
}]
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
content: [{
|
|
48
|
+
type: 'text' as const,
|
|
49
|
+
text: JSON.stringify(
|
|
50
|
+
createSuccessResponse('Sections retrieved successfully', result.data),
|
|
51
|
+
null,
|
|
52
|
+
2
|
|
53
|
+
)
|
|
54
|
+
}]
|
|
55
|
+
};
|
|
56
|
+
} catch (error: any) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{
|
|
59
|
+
type: 'text' as const,
|
|
60
|
+
text: JSON.stringify(
|
|
61
|
+
createErrorResponse(error.message || 'Failed to query sections'),
|
|
62
|
+
null,
|
|
63
|
+
2
|
|
64
|
+
)
|
|
65
|
+
}]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createSuccessResponse, createErrorResponse, uuidSchema, optionalUuidSchema } from './registry';
|
|
4
|
+
import { getTemplate, listTemplates } from '../repos/templates';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Registers the `query_templates` MCP tool.
|
|
8
|
+
*
|
|
9
|
+
* Operations:
|
|
10
|
+
* - get: Retrieve a single template by ID
|
|
11
|
+
* - list: List templates with optional filters
|
|
12
|
+
*/
|
|
13
|
+
export function registerQueryTemplatesTool(server: McpServer): void {
|
|
14
|
+
server.tool(
|
|
15
|
+
'query_templates',
|
|
16
|
+
'Query templates with operations: get (retrieve single template) or list (retrieve multiple templates with filters)',
|
|
17
|
+
{
|
|
18
|
+
operation: z.enum(['get', 'list']),
|
|
19
|
+
id: optionalUuidSchema,
|
|
20
|
+
includeSections: z.boolean().optional().default(false),
|
|
21
|
+
targetEntityType: z.string().optional(),
|
|
22
|
+
isBuiltIn: z.boolean().optional(),
|
|
23
|
+
isEnabled: z.boolean().optional(),
|
|
24
|
+
tags: z.string().optional()
|
|
25
|
+
},
|
|
26
|
+
async (params: any) => {
|
|
27
|
+
try {
|
|
28
|
+
const operation = params.operation;
|
|
29
|
+
|
|
30
|
+
if (operation === 'get') {
|
|
31
|
+
// Validate required parameters for get
|
|
32
|
+
if (!params.id) {
|
|
33
|
+
return {
|
|
34
|
+
content: [{
|
|
35
|
+
type: 'text' as const,
|
|
36
|
+
text: JSON.stringify(
|
|
37
|
+
createErrorResponse('Template ID is required for get operation'),
|
|
38
|
+
null,
|
|
39
|
+
2
|
|
40
|
+
)
|
|
41
|
+
}]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const includeSections = params.includeSections ?? false;
|
|
46
|
+
const result = getTemplate(params.id, includeSections);
|
|
47
|
+
|
|
48
|
+
if (!result.success) {
|
|
49
|
+
return {
|
|
50
|
+
content: [{
|
|
51
|
+
type: 'text' as const,
|
|
52
|
+
text: JSON.stringify(
|
|
53
|
+
createErrorResponse(result.error || 'Failed to get template'),
|
|
54
|
+
null,
|
|
55
|
+
2
|
|
56
|
+
)
|
|
57
|
+
}]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
content: [{
|
|
63
|
+
type: 'text' as const,
|
|
64
|
+
text: JSON.stringify(
|
|
65
|
+
createSuccessResponse('Template retrieved successfully', result.data),
|
|
66
|
+
null,
|
|
67
|
+
2
|
|
68
|
+
)
|
|
69
|
+
}]
|
|
70
|
+
};
|
|
71
|
+
} else if (operation === 'list') {
|
|
72
|
+
// Build filter parameters
|
|
73
|
+
const filters = {
|
|
74
|
+
targetEntityType: params.targetEntityType,
|
|
75
|
+
isBuiltIn: params.isBuiltIn,
|
|
76
|
+
isEnabled: params.isEnabled,
|
|
77
|
+
tags: params.tags
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const result = listTemplates(filters);
|
|
81
|
+
|
|
82
|
+
if (!result.success) {
|
|
83
|
+
return {
|
|
84
|
+
content: [{
|
|
85
|
+
type: 'text' as const,
|
|
86
|
+
text: JSON.stringify(
|
|
87
|
+
createErrorResponse(result.error || 'Failed to list templates'),
|
|
88
|
+
null,
|
|
89
|
+
2
|
|
90
|
+
)
|
|
91
|
+
}]
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
content: [{
|
|
97
|
+
type: 'text' as const,
|
|
98
|
+
text: JSON.stringify(
|
|
99
|
+
createSuccessResponse(
|
|
100
|
+
`Found ${result.data.length} template(s)`,
|
|
101
|
+
result.data
|
|
102
|
+
),
|
|
103
|
+
null,
|
|
104
|
+
2
|
|
105
|
+
)
|
|
106
|
+
}]
|
|
107
|
+
};
|
|
108
|
+
} else {
|
|
109
|
+
return {
|
|
110
|
+
content: [{
|
|
111
|
+
type: 'text' as const,
|
|
112
|
+
text: JSON.stringify(
|
|
113
|
+
createErrorResponse(`Invalid operation: ${operation}`),
|
|
114
|
+
null,
|
|
115
|
+
2
|
|
116
|
+
)
|
|
117
|
+
}]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
} catch (error: any) {
|
|
121
|
+
return {
|
|
122
|
+
content: [{
|
|
123
|
+
type: 'text' as const,
|
|
124
|
+
text: JSON.stringify(
|
|
125
|
+
createErrorResponse(
|
|
126
|
+
'Internal error',
|
|
127
|
+
error.message || 'Unknown error occurred'
|
|
128
|
+
),
|
|
129
|
+
null,
|
|
130
|
+
2
|
|
131
|
+
)
|
|
132
|
+
}]
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|