@geanatz/cortex-mcp 5.0.1
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/LICENSE +21 -0
- package/README.md +281 -0
- package/dist/errors/errors.d.ts +109 -0
- package/dist/errors/errors.js +199 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.js +4 -0
- package/dist/features/task-management/models/artifact.d.ts +169 -0
- package/dist/features/task-management/models/artifact.js +155 -0
- package/dist/features/task-management/models/config.d.ts +54 -0
- package/dist/features/task-management/models/config.js +54 -0
- package/dist/features/task-management/models/index.d.ts +6 -0
- package/dist/features/task-management/models/index.js +6 -0
- package/dist/features/task-management/models/task.d.ts +173 -0
- package/dist/features/task-management/models/task.js +84 -0
- package/dist/features/task-management/storage/file-storage.d.ts +130 -0
- package/dist/features/task-management/storage/file-storage.js +575 -0
- package/dist/features/task-management/storage/index.d.ts +5 -0
- package/dist/features/task-management/storage/index.js +5 -0
- package/dist/features/task-management/storage/storage.d.ts +159 -0
- package/dist/features/task-management/storage/storage.js +37 -0
- package/dist/features/task-management/tools/artifacts/index.d.ts +6 -0
- package/dist/features/task-management/tools/artifacts/index.js +174 -0
- package/dist/features/task-management/tools/base/handlers.d.ts +7 -0
- package/dist/features/task-management/tools/base/handlers.js +15 -0
- package/dist/features/task-management/tools/base/index.d.ts +3 -0
- package/dist/features/task-management/tools/base/index.js +3 -0
- package/dist/features/task-management/tools/base/schemas.d.ts +3 -0
- package/dist/features/task-management/tools/base/schemas.js +6 -0
- package/dist/features/task-management/tools/base/types.d.ts +13 -0
- package/dist/features/task-management/tools/base/types.js +1 -0
- package/dist/features/task-management/tools/tasks/index.d.ts +10 -0
- package/dist/features/task-management/tools/tasks/index.js +500 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +57 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +61 -0
- package/dist/types/common.d.ts +10 -0
- package/dist/types/common.js +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/cache.d.ts +104 -0
- package/dist/utils/cache.js +196 -0
- package/dist/utils/file-utils.d.ts +101 -0
- package/dist/utils/file-utils.js +270 -0
- package/dist/utils/index.d.ts +12 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/logger.d.ts +77 -0
- package/dist/utils/logger.js +173 -0
- package/dist/utils/response-builder.d.ts +4 -0
- package/dist/utils/response-builder.js +19 -0
- package/dist/utils/storage-config.d.ts +29 -0
- package/dist/utils/storage-config.js +51 -0
- package/dist/utils/string-utils.d.ts +2 -0
- package/dist/utils/string-utils.js +16 -0
- package/dist/utils/validation.d.ts +9 -0
- package/dist/utils/validation.js +9 -0
- package/dist/utils/version.d.ts +9 -0
- package/dist/utils/version.js +41 -0
- package/package.json +60 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Task, TaskHierarchy, TaskFilters, CreateTaskInput, UpdateTaskInput, Subtask, AddSubtaskInput, UpdateSubtaskInput } from '../models/task.js';
|
|
2
|
+
import { CURRENT_STORAGE_VERSION } from '../models/config.js';
|
|
3
|
+
import { Artifact, CreateArtifactInput, UpdateArtifactInput, ArtifactPhase, TaskArtifacts } from '../models/artifact.js';
|
|
4
|
+
export { CURRENT_STORAGE_VERSION };
|
|
5
|
+
/**
|
|
6
|
+
* Storage operation result
|
|
7
|
+
*/
|
|
8
|
+
export interface StorageResult<T> {
|
|
9
|
+
readonly success: boolean;
|
|
10
|
+
readonly data?: T;
|
|
11
|
+
readonly error?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Storage statistics
|
|
15
|
+
*/
|
|
16
|
+
export interface StorageStats {
|
|
17
|
+
readonly taskCount: number;
|
|
18
|
+
readonly artifactCount: number;
|
|
19
|
+
readonly cacheHitRate?: number;
|
|
20
|
+
readonly lastAccessed?: Date;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Storage interface for the task management system
|
|
24
|
+
*
|
|
25
|
+
* Simplified storage model:
|
|
26
|
+
* - Each parent task has its own folder: .cortex/tasks/{number}-{slug}/
|
|
27
|
+
* - .task.json contains parent task + subtasks array
|
|
28
|
+
* - Subtasks are NOT separate folders - they're stored inline
|
|
29
|
+
* - No dependsOn - simplified model
|
|
30
|
+
* - Single level nesting only
|
|
31
|
+
*
|
|
32
|
+
* Artifacts stored in .cortex/tasks/{number}-{slug}/{phase}.md
|
|
33
|
+
* Each phase (explore, search, plan, build, test) has its own artifact file
|
|
34
|
+
* Artifacts belong to the entire task hierarchy (parent + subtasks)
|
|
35
|
+
*/
|
|
36
|
+
export interface Storage {
|
|
37
|
+
/**
|
|
38
|
+
* Initialize the storage system
|
|
39
|
+
* Must be called before any other operations
|
|
40
|
+
*/
|
|
41
|
+
initialize(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Check if storage is initialized
|
|
44
|
+
*/
|
|
45
|
+
isInitialized(): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Get all parent tasks
|
|
48
|
+
*/
|
|
49
|
+
getTasks(): Promise<readonly Task[]>;
|
|
50
|
+
/**
|
|
51
|
+
* Get tasks with filters
|
|
52
|
+
*/
|
|
53
|
+
getTasksFiltered(filters: TaskFilters): Promise<readonly Task[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Get a single task by ID
|
|
56
|
+
*/
|
|
57
|
+
getTask(id: string): Promise<Task | null>;
|
|
58
|
+
/**
|
|
59
|
+
* Create a new parent task
|
|
60
|
+
*/
|
|
61
|
+
createTask(input: CreateTaskInput): Promise<Task>;
|
|
62
|
+
/**
|
|
63
|
+
* Update an existing task (parent fields or subtask operations)
|
|
64
|
+
*/
|
|
65
|
+
updateTask(id: string, updates: UpdateTaskInput): Promise<Task | null>;
|
|
66
|
+
/**
|
|
67
|
+
* Delete a task and all its subtasks
|
|
68
|
+
*/
|
|
69
|
+
deleteTask(id: string): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* Check if a task exists
|
|
72
|
+
*/
|
|
73
|
+
taskExists(id: string): Promise<boolean>;
|
|
74
|
+
/**
|
|
75
|
+
* Add a subtask to a parent task
|
|
76
|
+
*/
|
|
77
|
+
addSubtask(taskId: string, input: AddSubtaskInput): Promise<Subtask | null>;
|
|
78
|
+
/**
|
|
79
|
+
* Update a subtask
|
|
80
|
+
*/
|
|
81
|
+
updateSubtask(taskId: string, input: UpdateSubtaskInput): Promise<Subtask | null>;
|
|
82
|
+
/**
|
|
83
|
+
* Remove a subtask by ID
|
|
84
|
+
*/
|
|
85
|
+
removeSubtask(taskId: string, subtaskId: string): Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* Get task hierarchy (parent with subtasks)
|
|
88
|
+
*/
|
|
89
|
+
getTaskHierarchy(): Promise<readonly TaskHierarchy[]>;
|
|
90
|
+
/**
|
|
91
|
+
* Get a single artifact for a task
|
|
92
|
+
*/
|
|
93
|
+
getArtifact(taskId: string, phase: ArtifactPhase): Promise<Artifact | null>;
|
|
94
|
+
/**
|
|
95
|
+
* Get all artifacts for a task
|
|
96
|
+
*/
|
|
97
|
+
getAllArtifacts(taskId: string): Promise<TaskArtifacts>;
|
|
98
|
+
/**
|
|
99
|
+
* Create a new artifact (overwrites existing)
|
|
100
|
+
*/
|
|
101
|
+
createArtifact(taskId: string, phase: ArtifactPhase, input: CreateArtifactInput): Promise<Artifact>;
|
|
102
|
+
/**
|
|
103
|
+
* Update an existing artifact
|
|
104
|
+
*/
|
|
105
|
+
updateArtifact(taskId: string, phase: ArtifactPhase, input: UpdateArtifactInput): Promise<Artifact | null>;
|
|
106
|
+
/**
|
|
107
|
+
* Delete an artifact
|
|
108
|
+
*/
|
|
109
|
+
deleteArtifact(taskId: string, phase: ArtifactPhase): Promise<boolean>;
|
|
110
|
+
/**
|
|
111
|
+
* Check if an artifact exists
|
|
112
|
+
*/
|
|
113
|
+
artifactExists(taskId: string, phase: ArtifactPhase): Promise<boolean>;
|
|
114
|
+
/**
|
|
115
|
+
* Get storage version
|
|
116
|
+
*/
|
|
117
|
+
getVersion(): string;
|
|
118
|
+
/**
|
|
119
|
+
* Get storage statistics
|
|
120
|
+
*/
|
|
121
|
+
getStats(): Promise<StorageStats>;
|
|
122
|
+
/**
|
|
123
|
+
* Clear all caches
|
|
124
|
+
*/
|
|
125
|
+
clearCache(): void;
|
|
126
|
+
/**
|
|
127
|
+
* Get the working directory
|
|
128
|
+
*/
|
|
129
|
+
getWorkingDirectory(): string;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Abstract base storage class with common functionality
|
|
133
|
+
*/
|
|
134
|
+
export declare abstract class BaseStorage implements Storage {
|
|
135
|
+
protected initialized: boolean;
|
|
136
|
+
abstract initialize(): Promise<void>;
|
|
137
|
+
abstract getTasks(): Promise<readonly Task[]>;
|
|
138
|
+
abstract getTask(id: string): Promise<Task | null>;
|
|
139
|
+
abstract createTask(input: CreateTaskInput): Promise<Task>;
|
|
140
|
+
abstract updateTask(id: string, updates: UpdateTaskInput): Promise<Task | null>;
|
|
141
|
+
abstract deleteTask(id: string): Promise<boolean>;
|
|
142
|
+
abstract addSubtask(taskId: string, input: AddSubtaskInput): Promise<Subtask | null>;
|
|
143
|
+
abstract updateSubtask(taskId: string, input: UpdateSubtaskInput): Promise<Subtask | null>;
|
|
144
|
+
abstract removeSubtask(taskId: string, subtaskId: string): Promise<boolean>;
|
|
145
|
+
abstract getTaskHierarchy(): Promise<readonly TaskHierarchy[]>;
|
|
146
|
+
abstract getArtifact(taskId: string, phase: ArtifactPhase): Promise<Artifact | null>;
|
|
147
|
+
abstract getAllArtifacts(taskId: string): Promise<TaskArtifacts>;
|
|
148
|
+
abstract createArtifact(taskId: string, phase: ArtifactPhase, input: CreateArtifactInput): Promise<Artifact>;
|
|
149
|
+
abstract updateArtifact(taskId: string, phase: ArtifactPhase, input: UpdateArtifactInput): Promise<Artifact | null>;
|
|
150
|
+
abstract deleteArtifact(taskId: string, phase: ArtifactPhase): Promise<boolean>;
|
|
151
|
+
abstract getStats(): Promise<StorageStats>;
|
|
152
|
+
abstract clearCache(): void;
|
|
153
|
+
abstract getWorkingDirectory(): string;
|
|
154
|
+
isInitialized(): boolean;
|
|
155
|
+
getVersion(): string;
|
|
156
|
+
getTasksFiltered(filters: TaskFilters): Promise<readonly Task[]>;
|
|
157
|
+
taskExists(id: string): Promise<boolean>;
|
|
158
|
+
artifactExists(taskId: string, phase: ArtifactPhase): Promise<boolean>;
|
|
159
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CURRENT_STORAGE_VERSION } from '../models/config.js';
|
|
2
|
+
// Re-export config types for convenience
|
|
3
|
+
export { CURRENT_STORAGE_VERSION };
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base storage class with common functionality
|
|
6
|
+
*/
|
|
7
|
+
export class BaseStorage {
|
|
8
|
+
initialized = false;
|
|
9
|
+
isInitialized() {
|
|
10
|
+
return this.initialized;
|
|
11
|
+
}
|
|
12
|
+
getVersion() {
|
|
13
|
+
return CURRENT_STORAGE_VERSION;
|
|
14
|
+
}
|
|
15
|
+
async getTasksFiltered(filters) {
|
|
16
|
+
let tasks = await this.getTasks();
|
|
17
|
+
if (filters.status) {
|
|
18
|
+
const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
|
|
19
|
+
tasks = tasks.filter(t => statuses.includes(t.status));
|
|
20
|
+
}
|
|
21
|
+
if (filters.tags && filters.tags.length > 0) {
|
|
22
|
+
tasks = tasks.filter(t => t.tags && filters.tags.some(tag => t.tags.includes(tag)));
|
|
23
|
+
}
|
|
24
|
+
if (filters.includeDone === false) {
|
|
25
|
+
tasks = tasks.filter(t => t.status !== 'done');
|
|
26
|
+
}
|
|
27
|
+
return tasks;
|
|
28
|
+
}
|
|
29
|
+
async taskExists(id) {
|
|
30
|
+
const task = await this.getTask(id);
|
|
31
|
+
return task !== null;
|
|
32
|
+
}
|
|
33
|
+
async artifactExists(taskId, phase) {
|
|
34
|
+
const artifact = await this.getArtifact(taskId, phase);
|
|
35
|
+
return artifact !== null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Storage } from '../../storage/storage.js';
|
|
2
|
+
import { StorageConfig } from '../../../../utils/storage-config.js';
|
|
3
|
+
import { ToolDefinition } from '../../tools/base/types.js';
|
|
4
|
+
type StorageFactory = (workingDirectory: string, config: StorageConfig) => Promise<Storage>;
|
|
5
|
+
export declare function createArtifactTools(config: StorageConfig, createStorage: StorageFactory): ToolDefinition[];
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getWorkingDirectoryDescription } from '../../../../utils/storage-config.js';
|
|
3
|
+
import { createErrorResponse } from '../../../../utils/response-builder.js';
|
|
4
|
+
import { createLogger } from '../../../../utils/logger.js';
|
|
5
|
+
import { ARTIFACT_PHASES, OPERATION_DESCRIPTIONS, PHASE_DESCRIPTIONS } from '../../models/artifact.js';
|
|
6
|
+
import { withErrorHandling } from '../../tools/base/handlers.js';
|
|
7
|
+
import { workingDirectorySchema, taskIdSchema } from '../../tools/base/schemas.js';
|
|
8
|
+
const logger = createLogger('artifact-tools');
|
|
9
|
+
function createCreateHandler(phase, config, createStorage) {
|
|
10
|
+
return async (params) => {
|
|
11
|
+
try {
|
|
12
|
+
const { workingDirectory, taskId, content, status, retries, error } = params;
|
|
13
|
+
if (!taskId || taskId.trim().length === 0) {
|
|
14
|
+
return {
|
|
15
|
+
content: [{ type: 'text', text: 'Error: Task ID is required.' }],
|
|
16
|
+
isError: true
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (!content || content.trim().length === 0) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text', text: 'Error: Content is required.' }],
|
|
22
|
+
isError: true
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const storage = await createStorage(workingDirectory, config);
|
|
26
|
+
const artifact = await storage.createArtifact(taskId.trim(), phase, {
|
|
27
|
+
content: content.trim(),
|
|
28
|
+
status,
|
|
29
|
+
retries,
|
|
30
|
+
error
|
|
31
|
+
});
|
|
32
|
+
const phaseUpper = phase.charAt(0).toUpperCase() + phase.slice(1);
|
|
33
|
+
const truncated = artifact.content.length > 500
|
|
34
|
+
? artifact.content.substring(0, 500) + '...\n\n*(truncated - use get_task to see full content)*'
|
|
35
|
+
: artifact.content;
|
|
36
|
+
return {
|
|
37
|
+
content: [{
|
|
38
|
+
type: 'text',
|
|
39
|
+
text: `**${phaseUpper} artifact created for task "${taskId}"**\n\n**Phase:** ${artifact.metadata.phase}\n**Status:** ${artifact.metadata.status}\n**Created:** ${new Date(artifact.metadata.createdAt).toLocaleString()}\n\n---\n\n${truncated}`
|
|
40
|
+
}]
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
return createErrorResponse(err);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function createUpdateHandler(phase, config, createStorage) {
|
|
49
|
+
return async (params) => {
|
|
50
|
+
try {
|
|
51
|
+
const { workingDirectory, taskId, content, status, retries, error } = params;
|
|
52
|
+
if (!taskId || taskId.trim().length === 0) {
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: 'text', text: 'Error: Task ID is required.' }],
|
|
55
|
+
isError: true
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const storage = await createStorage(workingDirectory, config);
|
|
59
|
+
const artifact = await storage.updateArtifact(taskId.trim(), phase, {
|
|
60
|
+
content: content?.trim(),
|
|
61
|
+
status,
|
|
62
|
+
retries,
|
|
63
|
+
error
|
|
64
|
+
});
|
|
65
|
+
if (!artifact) {
|
|
66
|
+
const phaseUpper = phase.charAt(0).toUpperCase() + phase.slice(1);
|
|
67
|
+
return {
|
|
68
|
+
content: [{
|
|
69
|
+
type: 'text',
|
|
70
|
+
text: `Error: ${phaseUpper} artifact not found for task "${taskId}". Use create_${phase} to create it first.`
|
|
71
|
+
}],
|
|
72
|
+
isError: true
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const phaseUpper = phase.charAt(0).toUpperCase() + phase.slice(1);
|
|
76
|
+
const extras = (artifact.metadata.retries !== undefined ? `\n**Retries:** ${artifact.metadata.retries}` : '') + (artifact.metadata.error ? `\n**Error:** ${artifact.metadata.error}` : '');
|
|
77
|
+
return {
|
|
78
|
+
content: [{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: `**${phaseUpper} artifact updated for task "${taskId}"**\n\n**Phase:** ${artifact.metadata.phase}\n**Status:** ${artifact.metadata.status}\n**Updated:** ${new Date(artifact.metadata.updatedAt).toLocaleString()}${extras}`
|
|
81
|
+
}]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return createErrorResponse(err);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function createDeleteHandler(phase, config, createStorage) {
|
|
90
|
+
return async (params) => {
|
|
91
|
+
try {
|
|
92
|
+
const { workingDirectory, taskId, confirm } = params;
|
|
93
|
+
if (!taskId || taskId.trim().length === 0) {
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: 'text', text: 'Error: Task ID is required.' }],
|
|
96
|
+
isError: true
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (!confirm) {
|
|
100
|
+
return {
|
|
101
|
+
content: [{
|
|
102
|
+
type: 'text',
|
|
103
|
+
text: `Error: Deletion not confirmed. Set confirm=true to delete the ${phase} artifact.`
|
|
104
|
+
}],
|
|
105
|
+
isError: true
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const storage = await createStorage(workingDirectory, config);
|
|
109
|
+
const deleted = await storage.deleteArtifact(taskId.trim(), phase);
|
|
110
|
+
if (!deleted) {
|
|
111
|
+
const phaseUpper = phase.charAt(0).toUpperCase() + phase.slice(1);
|
|
112
|
+
return {
|
|
113
|
+
content: [{
|
|
114
|
+
type: 'text',
|
|
115
|
+
text: `${phaseUpper} artifact not found for task "${taskId}". Nothing to delete.`
|
|
116
|
+
}]
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const phaseUpper = phase.charAt(0).toUpperCase() + phase.slice(1);
|
|
120
|
+
return {
|
|
121
|
+
content: [{
|
|
122
|
+
type: 'text',
|
|
123
|
+
text: `**${phaseUpper} artifact deleted from task "${taskId}"**\n\nThe ${phase} phase has been reset and can be re-run.`
|
|
124
|
+
}]
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
return createErrorResponse(err);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
export function createArtifactTools(config, createStorage) {
|
|
133
|
+
const tools = [];
|
|
134
|
+
const wdSchema = workingDirectorySchema.describe(getWorkingDirectoryDescription(config));
|
|
135
|
+
for (const phase of ARTIFACT_PHASES) {
|
|
136
|
+
tools.push({
|
|
137
|
+
name: `create_${phase}`,
|
|
138
|
+
description: OPERATION_DESCRIPTIONS.create[phase],
|
|
139
|
+
parameters: {
|
|
140
|
+
workingDirectory: wdSchema,
|
|
141
|
+
taskId: taskIdSchema.describe('The ID of the task to create the artifact for'),
|
|
142
|
+
content: z.string().describe(`Markdown content for the ${phase} artifact. ${PHASE_DESCRIPTIONS[phase]}`),
|
|
143
|
+
status: z.enum(['pending', 'in-progress', 'completed', 'failed', 'skipped']).optional().describe('Status of this phase (defaults to "completed")'),
|
|
144
|
+
retries: z.number().min(0).optional().describe('Number of retry attempts for this phase'),
|
|
145
|
+
error: z.string().optional().describe('Error message if status is "failed"')
|
|
146
|
+
},
|
|
147
|
+
handler: withErrorHandling(createCreateHandler(phase, config, createStorage))
|
|
148
|
+
});
|
|
149
|
+
tools.push({
|
|
150
|
+
name: `update_${phase}`,
|
|
151
|
+
description: OPERATION_DESCRIPTIONS.update[phase],
|
|
152
|
+
parameters: {
|
|
153
|
+
workingDirectory: wdSchema,
|
|
154
|
+
taskId: taskIdSchema.describe('The ID of the task to update the artifact for'),
|
|
155
|
+
content: z.string().optional().describe(`Updated markdown content for the ${phase} artifact`),
|
|
156
|
+
status: z.enum(['pending', 'in-progress', 'completed', 'failed', 'skipped']).optional().describe('Updated status of this phase'),
|
|
157
|
+
retries: z.number().min(0).optional().describe('Updated number of retry attempts'),
|
|
158
|
+
error: z.string().optional().describe('Updated error message')
|
|
159
|
+
},
|
|
160
|
+
handler: withErrorHandling(createUpdateHandler(phase, config, createStorage))
|
|
161
|
+
});
|
|
162
|
+
tools.push({
|
|
163
|
+
name: `delete_${phase}`,
|
|
164
|
+
description: OPERATION_DESCRIPTIONS.delete[phase],
|
|
165
|
+
parameters: {
|
|
166
|
+
workingDirectory: wdSchema,
|
|
167
|
+
taskId: taskIdSchema.describe('The ID of the task to delete the artifact from'),
|
|
168
|
+
confirm: z.boolean().describe('Must be set to true to confirm deletion (safety measure)')
|
|
169
|
+
},
|
|
170
|
+
handler: withErrorHandling(createDeleteHandler(phase, config, createStorage))
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return tools;
|
|
174
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ToolHandler } from './types.js';
|
|
2
|
+
import { McpToolResponse } from '../../../../types/common.js';
|
|
3
|
+
/**
|
|
4
|
+
* Wrap a tool handler with top-level error handling.
|
|
5
|
+
* Any thrown error is converted to an MCP error response.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withErrorHandling<TInput, TOutput extends McpToolResponse>(handler: ToolHandler<TInput, TOutput>): ToolHandler<TInput, TOutput>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createErrorResponse } from '../../../../utils/response-builder.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wrap a tool handler with top-level error handling.
|
|
4
|
+
* Any thrown error is converted to an MCP error response.
|
|
5
|
+
*/
|
|
6
|
+
export function withErrorHandling(handler) {
|
|
7
|
+
return async (input) => {
|
|
8
|
+
try {
|
|
9
|
+
return await handler(input);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
return createErrorResponse(error);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ValidationLimits } from '../../../../utils/validation.js';
|
|
3
|
+
export const workingDirectorySchema = z.string().min(1, 'Working directory is required');
|
|
4
|
+
export const taskIdSchema = z.string()
|
|
5
|
+
.min(1, 'Task ID is required')
|
|
6
|
+
.max(ValidationLimits.TASK_ID_MAX_LENGTH);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { McpToolResponse } from '../../../../types/common.js';
|
|
2
|
+
/**
|
|
3
|
+
* Definition for a tool that can be registered with the MCP server.
|
|
4
|
+
* The handler uses a permissive input type because the MCP SDK
|
|
5
|
+
* validates params against the zod schemas at the protocol layer.
|
|
6
|
+
*/
|
|
7
|
+
export interface ToolDefinition {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly description: string;
|
|
10
|
+
readonly parameters: Record<string, unknown>;
|
|
11
|
+
readonly handler: (params: any) => Promise<McpToolResponse>;
|
|
12
|
+
}
|
|
13
|
+
export type ToolHandler<TInput = Record<string, unknown>, TOutput = McpToolResponse> = (input: TInput) => Promise<TOutput>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Storage } from '../../storage/storage.js';
|
|
2
|
+
import { ToolDefinition } from '../base/types.js';
|
|
3
|
+
import { StorageConfig } from '../../../../utils/storage-config.js';
|
|
4
|
+
type StorageFactory = (workingDirectory: string, config: StorageConfig) => Promise<Storage>;
|
|
5
|
+
/**
|
|
6
|
+
* Create all task management tools using the same factory pattern as artifact tools.
|
|
7
|
+
* Each tool creates its own storage instance per-call using the provided factory.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createTaskTools(config: StorageConfig, createStorage: StorageFactory): ToolDefinition[];
|
|
10
|
+
export {};
|