@geanatz/cortex-mcp 5.0.1 → 5.0.3

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 CHANGED
@@ -9,6 +9,7 @@ An MCP (Model Context Protocol) server for managing task-based workflows with ar
9
9
  - **File-Based Storage** - All data stored in `.cortex/` directory with no database required
10
10
  - **In-Memory Caching** - High-performance caching layer with TTL for frequently accessed tasks
11
11
  - **Structured Logging** - Comprehensive logging with configurable levels
12
+ - **Production Ready** - Path traversal protection, input validation, size limits, error handling
12
13
 
13
14
  ## Installation
14
15
 
@@ -84,10 +85,10 @@ Or for global mode:
84
85
  ```
85
86
  Tool: create_task
86
87
  Parameters:
87
- - workingDirectory: path where tasks are stored
88
- - details: task description (generates task ID)
88
+ - workingDirectory: path where tasks are stored (absolute path, required)
89
+ - details: task description (generates task ID), max 2000 characters
89
90
  - status (optional): pending | in_progress | done
90
- - tags (optional): categorization tags
91
+ - tags (optional): categorization tags, max 20 tags, max 50 chars each
91
92
  ```
92
93
 
93
94
  ### Managing Subtasks
@@ -101,6 +102,7 @@ Parameters:
101
102
  - addSubtask: { details: "Subtask description", status: "pending" }
102
103
  - updateSubtask: { id: "1", status: "done" }
103
104
  - removeSubtaskId: "1"
105
+ - actualHours (optional): number, max 10000 hours
104
106
  ```
105
107
 
106
108
  ### Managing Artifacts
@@ -115,11 +117,12 @@ Each task can have artifacts for 5 phases:
115
117
  ```
116
118
  Tools: create_{phase}, update_{phase}, delete_{phase}
117
119
  Parameters:
118
- - workingDirectory: project directory
120
+ - workingDirectory: project directory (absolute path)
119
121
  - taskId: which task to attach artifact to
120
- - content: markdown content
122
+ - content: markdown content, max 10MB
121
123
  - status: pending | in-progress | completed | failed | skipped
122
- - retries, error: optional metadata
124
+ - retries (optional): integer, max 100
125
+ - error (optional): error message, max 10,000 characters
123
126
  ```
124
127
 
125
128
  ## Storage Format
@@ -166,12 +169,12 @@ Tasks are stored in `.cortex/tasks/{number}-{slug}/` directories:
166
169
  ```typescript
167
170
  interface Task {
168
171
  id: string; // Task ID (e.g., "001-implement-auth")
169
- details: string; // Full task description
172
+ details: string; // Full task description (max 2000 chars)
170
173
  status: 'pending' | 'in_progress' | 'done';
171
174
  createdAt: string; // ISO 8601 timestamp
172
175
  updatedAt: string; // Last modification timestamp
173
- tags?: string[]; // Categorization tags
174
- actualHours?: number; // Time tracking
176
+ tags?: string[]; // Categorization tags (max 20, max 50 chars each)
177
+ actualHours?: number; // Time tracking (max 10000 hours)
175
178
  subtasks: Subtask[]; // Array of subtasks
176
179
  }
177
180
  ```
@@ -180,7 +183,7 @@ interface Task {
180
183
  ```typescript
181
184
  interface Subtask {
182
185
  id: string; // Simple ID ("1", "2", etc.)
183
- details: string; // Subtask description
186
+ details: string; // Subtask description (max 1000 chars)
184
187
  status: 'pending' | 'in_progress' | 'done';
185
188
  }
186
189
  ```
@@ -193,10 +196,10 @@ interface Artifact {
193
196
  status: 'pending' | 'in-progress' | 'completed' | 'failed' | 'skipped';
194
197
  createdAt: string; // ISO 8601
195
198
  updatedAt: string; // ISO 8601
196
- retries?: number; // Attempt count
197
- error?: string; // Error message if status=failed
199
+ retries?: number; // Attempt count (max 100)
200
+ error?: string; // Error message (max 10000 chars)
198
201
  }
199
- content: string; // Markdown content
202
+ content: string; // Markdown content (max 10MB)
200
203
  }
201
204
  ```
202
205
 
@@ -234,9 +237,41 @@ interface Artifact {
234
237
  ### Command-Line Flags
235
238
  - `--claude` - Use global directory mode (~/.cortex/)
236
239
 
240
+ ### Security Considerations
241
+
242
+ This server implements several security measures:
243
+
244
+ 1. **Path Traversal Protection**: All paths are validated to prevent `../` sequences and directory escape
245
+ 2. **Input Validation**: All inputs are validated using Zod schemas with strict limits
246
+ 3. **Size Limits**:
247
+ - Task details: max 2000 characters
248
+ - Artifact content: max 10MB
249
+ - Tags: max 20 tags, max 50 characters each
250
+ - Error messages: max 10,000 characters
251
+ 4. **Working Directory Validation**: Must be absolute paths without traversal sequences
252
+ 5. **Atomic File Writes**: Uses temp files and atomic rename to prevent data corruption
253
+ 6. **Safe Error Messages**: Internal paths are not exposed in error messages
254
+
255
+ ### Validation Limits
256
+
257
+ | Field | Limit |
258
+ |-------|-------|
259
+ | Task ID | 100 characters |
260
+ | Task details | 2000 characters |
261
+ | Subtask details | 1000 characters |
262
+ | Tags | 20 tags max, 50 chars each |
263
+ | Actual hours | Max 10,000 |
264
+ | Artifact content | 10MB max |
265
+ | Error messages | 10,000 characters max |
266
+ | Retries | Max 100 |
267
+ | Working directory path | 4096 characters max |
268
+
237
269
  ## Development
238
270
 
239
271
  ```bash
272
+ # Install dependencies
273
+ npm install
274
+
240
275
  # Build TypeScript
241
276
  npm run build
242
277
 
@@ -258,15 +293,45 @@ npm start
258
293
  - **No circular dependencies** - Easy to understand data flow
259
294
  - **Abstract storage** - File-based implementation, easily extensible
260
295
  - **MCP-compliant** - Full compliance with Model Context Protocol specification
296
+ - **Security-first** - Path validation, input sanitization, size limits
297
+
298
+ ## Error Handling
299
+
300
+ The server uses a comprehensive error handling strategy:
301
+
302
+ - **AppError hierarchy** - Typed errors with context
303
+ - **Zod validation** - Schema validation at the boundary
304
+ - **Graceful degradation** - Continues operating on non-fatal errors
305
+ - **Structured logging** - All errors logged with context to stderr
306
+ - **Safe shutdown** - SIGINT/SIGTERM handlers for graceful exit
307
+
308
+ ## Troubleshooting
309
+
310
+ ### Connection closed errors
311
+ - Check that the working directory exists and is accessible
312
+ - Verify the working directory is an absolute path
313
+ - Ensure no path traversal sequences (../) in workingDirectory
314
+
315
+ ### Permission denied errors
316
+ - Verify write permissions to the working directory
317
+ - Check if the .cortex directory is owned by the current user
318
+
319
+ ### Storage not initializing
320
+ - Ensure the path is an absolute path (not relative)
321
+ - Check that parent directories exist and are writable
261
322
 
262
323
  ## Version
263
324
 
264
- Current version: **5.0.0**
325
+ Current version: **5.0.2**
265
326
 
266
- - v5.0.0: Simplified model - subtasks stored inline, removed dependencies, removed move_task
267
- - v4.0.0: Complete refactor with artifact support and optimized build
268
- - v3.x: Legacy memory features (deprecated)
269
- - v1.x: Initial implementation
327
+ ### Changelog
328
+
329
+ - **v5.0.2**: Security hardening - path traversal protection, input validation, size limits
330
+ - **v5.0.1**: Added error handlers for connection stability
331
+ - **v5.0.0**: Simplified model - subtasks stored inline, removed dependencies, removed move_task
332
+ - **v4.0.0**: Complete refactor with artifact support and optimized build
333
+ - **v3.x**: Legacy memory features (deprecated)
334
+ - **v1.x**: Initial implementation
270
335
 
271
336
  ## License
272
337
 
@@ -278,4 +343,16 @@ Geanatz
278
343
 
279
344
  ## Contributing
280
345
 
281
- Contributions welcome!
346
+ Contributions welcome! Please ensure:
347
+ - Code follows existing patterns
348
+ - All inputs are validated
349
+ - Security considerations are addressed
350
+ - Tests pass (when test suite is added)
351
+
352
+ ## Security
353
+
354
+ For security issues, please email directly rather than opening a public issue.
355
+
356
+ ### Reporting Vulnerabilities
357
+
358
+ If you discover a security vulnerability, please report it responsibly by contacting the maintainer directly.
@@ -4,7 +4,7 @@ import { createErrorResponse } from '../../../../utils/response-builder.js';
4
4
  import { createLogger } from '../../../../utils/logger.js';
5
5
  import { ARTIFACT_PHASES, OPERATION_DESCRIPTIONS, PHASE_DESCRIPTIONS } from '../../models/artifact.js';
6
6
  import { withErrorHandling } from '../../tools/base/handlers.js';
7
- import { workingDirectorySchema, taskIdSchema } from '../../tools/base/schemas.js';
7
+ import { createWorkingDirectorySchema, taskIdSchema, artifactContentSchema, artifactErrorSchema, retriesSchema } from '../../tools/base/schemas.js';
8
8
  const logger = createLogger('artifact-tools');
9
9
  function createCreateHandler(phase, config, createStorage) {
10
10
  return async (params) => {
@@ -131,7 +131,7 @@ function createDeleteHandler(phase, config, createStorage) {
131
131
  }
132
132
  export function createArtifactTools(config, createStorage) {
133
133
  const tools = [];
134
- const wdSchema = workingDirectorySchema.describe(getWorkingDirectoryDescription(config));
134
+ const wdSchema = createWorkingDirectorySchema(getWorkingDirectoryDescription(config));
135
135
  for (const phase of ARTIFACT_PHASES) {
136
136
  tools.push({
137
137
  name: `create_${phase}`,
@@ -139,10 +139,10 @@ export function createArtifactTools(config, createStorage) {
139
139
  parameters: {
140
140
  workingDirectory: wdSchema,
141
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]}`),
142
+ content: artifactContentSchema.describe(`Markdown content for the ${phase} artifact. ${PHASE_DESCRIPTIONS[phase]}`),
143
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"')
144
+ retries: retriesSchema.optional().describe('Number of retry attempts for this phase'),
145
+ error: artifactErrorSchema.optional().describe('Error message if status is "failed"')
146
146
  },
147
147
  handler: withErrorHandling(createCreateHandler(phase, config, createStorage))
148
148
  });
@@ -152,10 +152,10 @@ export function createArtifactTools(config, createStorage) {
152
152
  parameters: {
153
153
  workingDirectory: wdSchema,
154
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`),
155
+ content: artifactContentSchema.optional().describe(`Updated markdown content for the ${phase} artifact`),
156
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')
157
+ retries: retriesSchema.optional().describe('Updated number of retry attempts'),
158
+ error: artifactErrorSchema.optional().describe('Updated error message')
159
159
  },
160
160
  handler: withErrorHandling(createUpdateHandler(phase, config, createStorage))
161
161
  });
@@ -1,3 +1,35 @@
1
1
  import { z } from 'zod';
2
- export declare const workingDirectorySchema: z.ZodString;
2
+ export declare function createWorkingDirectorySchema(description?: string): z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
3
+ export declare const workingDirectorySchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
4
+ /**
5
+ * Task ID schema
6
+ */
3
7
  export declare const taskIdSchema: z.ZodString;
8
+ /**
9
+ * Task details schema
10
+ */
11
+ export declare const taskDetailsSchema: z.ZodString;
12
+ /**
13
+ * Subtask details schema
14
+ */
15
+ export declare const subtaskDetailsSchema: z.ZodString;
16
+ /**
17
+ * Tags schema
18
+ */
19
+ export declare const tagsSchema: z.ZodArray<z.ZodString, "many">;
20
+ /**
21
+ * Actual hours schema
22
+ */
23
+ export declare const actualHoursSchema: z.ZodNumber;
24
+ /**
25
+ * Artifact content schema
26
+ */
27
+ export declare const artifactContentSchema: z.ZodEffects<z.ZodString, string, string>;
28
+ /**
29
+ * Artifact error schema
30
+ */
31
+ export declare const artifactErrorSchema: z.ZodString;
32
+ /**
33
+ * Retries schema
34
+ */
35
+ export declare const retriesSchema: z.ZodNumber;
@@ -1,6 +1,79 @@
1
1
  import { z } from 'zod';
2
2
  import { ValidationLimits } from '../../../../utils/validation.js';
3
- export const workingDirectorySchema = z.string().min(1, 'Working directory is required');
3
+ import { validateWorkingDirectory, containsPathTraversal } from '../../../../utils/path-security.js';
4
+ // Base schemas without descriptions (for internal use)
5
+ const baseWorkingDirectorySchema = z.string()
6
+ .min(1, 'Working directory is required')
7
+ .max(ValidationLimits.MAX_WORKING_DIRECTORY_LENGTH, 'Working directory path is too long')
8
+ .refine((path) => !containsPathTraversal(path), 'Working directory cannot contain path traversal sequences (..)')
9
+ .refine((path) => {
10
+ try {
11
+ validateWorkingDirectory(path);
12
+ return true;
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }, 'Working directory must be an absolute path without traversal sequences');
18
+ // Export schema builder function
19
+ export function createWorkingDirectorySchema(description) {
20
+ if (description) {
21
+ return baseWorkingDirectorySchema.describe(description);
22
+ }
23
+ return baseWorkingDirectorySchema;
24
+ }
25
+ // For backward compatibility
26
+ export const workingDirectorySchema = baseWorkingDirectorySchema;
27
+ /**
28
+ * Task ID schema
29
+ */
4
30
  export const taskIdSchema = z.string()
5
31
  .min(1, 'Task ID is required')
6
- .max(ValidationLimits.TASK_ID_MAX_LENGTH);
32
+ .max(ValidationLimits.TASK_ID_MAX_LENGTH, `Task ID must be ${ValidationLimits.TASK_ID_MAX_LENGTH} characters or less`);
33
+ /**
34
+ * Task details schema
35
+ */
36
+ export const taskDetailsSchema = z.string()
37
+ .min(ValidationLimits.TASK_DETAILS_MIN_LENGTH, 'Task details cannot be empty')
38
+ .max(ValidationLimits.TASK_DETAILS_MAX_LENGTH, `Task details must be ${ValidationLimits.TASK_DETAILS_MAX_LENGTH} characters or less`);
39
+ /**
40
+ * Subtask details schema
41
+ */
42
+ export const subtaskDetailsSchema = z.string()
43
+ .min(1, 'Subtask details cannot be empty')
44
+ .max(ValidationLimits.SUBTASK_DETAILS_MAX_LENGTH, `Subtask details must be ${ValidationLimits.SUBTASK_DETAILS_MAX_LENGTH} characters or less`);
45
+ /**
46
+ * Tags schema
47
+ */
48
+ export const tagsSchema = z.array(z.string()
49
+ .min(ValidationLimits.TAG_MIN_LENGTH, 'Tag cannot be empty')
50
+ .max(ValidationLimits.TAG_MAX_LENGTH, `Tag must be ${ValidationLimits.TAG_MAX_LENGTH} characters or less`))
51
+ .max(ValidationLimits.MAX_TAGS, `Cannot have more than ${ValidationLimits.MAX_TAGS} tags`);
52
+ /**
53
+ * Actual hours schema
54
+ */
55
+ export const actualHoursSchema = z.number()
56
+ .min(0, 'Actual hours cannot be negative')
57
+ .max(ValidationLimits.MAX_ACTUAL_HOURS, `Actual hours cannot exceed ${ValidationLimits.MAX_ACTUAL_HOURS}`)
58
+ .finite('Actual hours must be a finite number');
59
+ /**
60
+ * Artifact content schema
61
+ */
62
+ export const artifactContentSchema = z.string()
63
+ .min(1, 'Content cannot be empty')
64
+ .refine((content) => {
65
+ const byteLength = Buffer.byteLength(content, 'utf-8');
66
+ return byteLength <= ValidationLimits.ARTIFACT_CONTENT_MAX_LENGTH;
67
+ }, `Content exceeds maximum size of ${ValidationLimits.ARTIFACT_CONTENT_MAX_LENGTH} bytes (10MB)`);
68
+ /**
69
+ * Artifact error schema
70
+ */
71
+ export const artifactErrorSchema = z.string()
72
+ .max(ValidationLimits.ARTIFACT_ERROR_MAX_LENGTH, `Error message cannot exceed ${ValidationLimits.ARTIFACT_ERROR_MAX_LENGTH} characters`);
73
+ /**
74
+ * Retries schema
75
+ */
76
+ export const retriesSchema = z.number()
77
+ .int('Retries must be an integer')
78
+ .min(0, 'Retries cannot be negative')
79
+ .max(ValidationLimits.MAX_RETRIES, `Retries cannot exceed ${ValidationLimits.MAX_RETRIES}`);
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { ARTIFACT_PHASES } from '../../models/artifact.js';
3
3
  import { withErrorHandling } from '../base/handlers.js';
4
- import { workingDirectorySchema } from '../base/schemas.js';
4
+ import { createWorkingDirectorySchema } from '../base/schemas.js';
5
5
  import { getWorkingDirectoryDescription } from '../../../../utils/storage-config.js';
6
6
  import { createLogger } from '../../../../utils/logger.js';
7
7
  const logger = createLogger('task-tools');
@@ -43,7 +43,7 @@ function countDoneSubtasks(task) {
43
43
  * Each tool creates its own storage instance per-call using the provided factory.
44
44
  */
45
45
  export function createTaskTools(config, createStorage) {
46
- const wdSchema = workingDirectorySchema.describe(getWorkingDirectoryDescription(config));
46
+ const wdSchema = createWorkingDirectorySchema(getWorkingDirectoryDescription(config));
47
47
  return [
48
48
  createListTasksTool(wdSchema, config, createStorage),
49
49
  createCreateTaskTool(wdSchema, config, createStorage),
@@ -10,3 +10,4 @@ export * from './response-builder.js';
10
10
  export * from './cache.js';
11
11
  export * from './logger.js';
12
12
  export * from './version.js';
13
+ export * from './path-security.js';
@@ -10,3 +10,4 @@ export * from './response-builder.js';
10
10
  export * from './cache.js';
11
11
  export * from './logger.js';
12
12
  export * from './version.js';
13
+ export * from './path-security.js';
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Secure path utilities to prevent path traversal attacks
3
+ */
4
+ /**
5
+ * Validate that a path doesn't contain traversal sequences
6
+ * Blocks paths containing .. or null bytes
7
+ */
8
+ export declare function containsPathTraversal(filePath: string): boolean;
9
+ /**
10
+ * Securely resolve a path within a base directory
11
+ * Throws if the resolved path escapes the base directory
12
+ *
13
+ * @param baseDir - The allowed base directory
14
+ * @param targetPath - The path to resolve (can be relative)
15
+ * @returns The resolved path (guaranteed to be within baseDir)
16
+ * @throws Error if path escapes baseDir
17
+ */
18
+ export declare function resolveSecurePath(baseDir: string, targetPath: string): string;
19
+ /**
20
+ * Validate that a working directory path is safe
21
+ * - Must be absolute
22
+ * - Must not contain traversal sequences
23
+ * - Must not be empty
24
+ *
25
+ * @param workingDirectory - The path to validate
26
+ * @returns Normalized absolute path
27
+ * @throws Error if path is invalid
28
+ */
29
+ export declare function validateWorkingDirectory(workingDirectory: string): string;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Secure path utilities to prevent path traversal attacks
3
+ */
4
+ import { resolve, normalize, sep } from 'path';
5
+ /**
6
+ * Validate that a path doesn't contain traversal sequences
7
+ * Blocks paths containing .. or null bytes
8
+ */
9
+ export function containsPathTraversal(filePath) {
10
+ // Check for null bytes (null byte injection)
11
+ if (filePath.includes('\0')) {
12
+ return true;
13
+ }
14
+ // Normalize the path and check if it contains parent directory references
15
+ const normalized = normalize(filePath);
16
+ // Check for .. components in the path
17
+ const parts = normalized.split(sep);
18
+ return parts.some(part => part === '..');
19
+ }
20
+ /**
21
+ * Securely resolve a path within a base directory
22
+ * Throws if the resolved path escapes the base directory
23
+ *
24
+ * @param baseDir - The allowed base directory
25
+ * @param targetPath - The path to resolve (can be relative)
26
+ * @returns The resolved path (guaranteed to be within baseDir)
27
+ * @throws Error if path escapes baseDir
28
+ */
29
+ export function resolveSecurePath(baseDir, targetPath) {
30
+ // Reject paths with traversal attempts
31
+ if (containsPathTraversal(targetPath)) {
32
+ throw new Error(`Path traversal detected: ${targetPath}`);
33
+ }
34
+ // Resolve the path
35
+ const resolvedPath = resolve(baseDir, targetPath);
36
+ const resolvedBase = resolve(baseDir);
37
+ // Ensure the resolved path is within the base directory
38
+ // Add trailing separator to base to prevent partial matches
39
+ const baseWithSep = resolvedBase.endsWith(sep) ? resolvedBase : resolvedBase + sep;
40
+ if (!resolvedPath.startsWith(baseWithSep) && resolvedPath !== resolvedBase) {
41
+ throw new Error(`Path escapes base directory: ${targetPath}`);
42
+ }
43
+ return resolvedPath;
44
+ }
45
+ /**
46
+ * Validate that a working directory path is safe
47
+ * - Must be absolute
48
+ * - Must not contain traversal sequences
49
+ * - Must not be empty
50
+ *
51
+ * @param workingDirectory - The path to validate
52
+ * @returns Normalized absolute path
53
+ * @throws Error if path is invalid
54
+ */
55
+ export function validateWorkingDirectory(workingDirectory) {
56
+ if (!workingDirectory || workingDirectory.trim().length === 0) {
57
+ throw new Error('Working directory is required');
58
+ }
59
+ // Check for traversal sequences
60
+ if (containsPathTraversal(workingDirectory)) {
61
+ throw new Error('Working directory cannot contain path traversal sequences (..)');
62
+ }
63
+ const normalized = normalize(workingDirectory);
64
+ // On Windows, check for absolute path (starts with drive letter or \\)
65
+ // On Unix, check for absolute path (starts with /)
66
+ const isWindows = process.platform === 'win32';
67
+ const isAbsolute = isWindows
68
+ ? /^[a-zA-Z]:[\\/]|^\\\\/.test(normalized)
69
+ : normalized.startsWith('/');
70
+ if (!isAbsolute) {
71
+ throw new Error('Working directory must be an absolute path');
72
+ }
73
+ return normalized;
74
+ }
@@ -6,4 +6,23 @@ export declare const ValidationLimits: {
6
6
  readonly MAX_TAGS: 20;
7
7
  readonly MAX_DEPENDENCIES: 50;
8
8
  readonly TASK_ID_MAX_LENGTH: 100;
9
+ readonly ARTIFACT_CONTENT_MAX_LENGTH: number;
10
+ readonly ARTIFACT_ERROR_MAX_LENGTH: 10000;
11
+ readonly SUBTASK_DETAILS_MAX_LENGTH: 1000;
12
+ readonly MAX_ACTUAL_HOURS: 10000;
13
+ readonly MAX_RETRIES: 100;
14
+ readonly MAX_WORKING_DIRECTORY_LENGTH: 4096;
15
+ readonly MAX_CACHE_ENTRY_SIZE: number;
9
16
  };
17
+ /**
18
+ * Validate that a number is within safe integer range
19
+ */
20
+ export declare function isSafeNumber(value: number): boolean;
21
+ /**
22
+ * Validate hours value
23
+ */
24
+ export declare function validateHours(value: number): boolean;
25
+ /**
26
+ * Validate content size
27
+ */
28
+ export declare function validateContentSize(content: string, maxSize: number): boolean;
@@ -1,9 +1,50 @@
1
1
  export const ValidationLimits = {
2
+ // Task fields
2
3
  TASK_DETAILS_MAX_LENGTH: 2000,
3
4
  TASK_DETAILS_MIN_LENGTH: 1,
5
+ // Tags
4
6
  TAG_MAX_LENGTH: 50,
5
7
  TAG_MIN_LENGTH: 1,
6
8
  MAX_TAGS: 20,
9
+ // Dependencies (not used in simplified model but kept for compatibility)
7
10
  MAX_DEPENDENCIES: 50,
11
+ // Task ID
8
12
  TASK_ID_MAX_LENGTH: 100,
13
+ // Artifact content - prevent DoS via huge content
14
+ ARTIFACT_CONTENT_MAX_LENGTH: 10 * 1024 * 1024, // 10MB max
15
+ ARTIFACT_ERROR_MAX_LENGTH: 10000, // 10KB max for error messages
16
+ // Subtask details
17
+ SUBTASK_DETAILS_MAX_LENGTH: 1000,
18
+ // Time tracking
19
+ MAX_ACTUAL_HOURS: 10000, // Max 10,000 hours (reasonable limit)
20
+ // Retry attempts
21
+ MAX_RETRIES: 100, // Reasonable max for retry attempts
22
+ // Working directory
23
+ MAX_WORKING_DIRECTORY_LENGTH: 4096, // Max path length on most systems
24
+ // Cache
25
+ MAX_CACHE_ENTRY_SIZE: 1024 * 1024, // 1MB max per cache entry
9
26
  };
27
+ /**
28
+ * Validate that a number is within safe integer range
29
+ */
30
+ export function isSafeNumber(value) {
31
+ return Number.isFinite(value) &&
32
+ value >= Number.MIN_SAFE_INTEGER &&
33
+ value <= Number.MAX_SAFE_INTEGER;
34
+ }
35
+ /**
36
+ * Validate hours value
37
+ */
38
+ export function validateHours(value) {
39
+ return isSafeNumber(value) &&
40
+ value >= 0 &&
41
+ value <= ValidationLimits.MAX_ACTUAL_HOURS;
42
+ }
43
+ /**
44
+ * Validate content size
45
+ */
46
+ export function validateContentSize(content, maxSize) {
47
+ // Use Buffer to get accurate byte length for unicode
48
+ const byteLength = Buffer.byteLength(content, 'utf-8');
49
+ return byteLength <= maxSize;
50
+ }
@@ -26,10 +26,10 @@ export function getVersion() {
26
26
  continue;
27
27
  }
28
28
  }
29
- return '5.0.0'; // Fallback version — keep in sync with package.json
29
+ return '5.0.2'; // Fallback version — keep in sync with package.json
30
30
  }
31
31
  catch {
32
- return '5.0.0'; // Fallback version — keep in sync with package.json
32
+ return '5.0.2'; // Fallback version — keep in sync with package.json
33
33
  }
34
34
  }
35
35
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geanatz/cortex-mcp",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
4
4
  "description": "An MCP server for task-based orchestration workflows with phase artifacts for agentic development",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -11,7 +11,9 @@
11
11
  "build": "tsc",
12
12
  "dev": "tsc --watch",
13
13
  "start": "node dist/index.js",
14
- "prepublishOnly": "npm run build"
14
+ "prepublishOnly": "npm run build",
15
+ "test": "echo \"No tests specified\" && exit 0",
16
+ "lint": "echo \"No linter configured\" && exit 0"
15
17
  },
16
18
  "keywords": [
17
19
  "mcp",
@@ -22,7 +24,9 @@
22
24
  "file-storage",
23
25
  "productivity",
24
26
  "ai-tools",
25
- "opencode"
27
+ "opencode",
28
+ "claude",
29
+ "cursor"
26
30
  ],
27
31
  "author": "Geanatz",
28
32
  "license": "MIT",
@@ -39,8 +43,12 @@
39
43
  },
40
44
  "repository": {
41
45
  "type": "git",
42
- "url": "https://github.com/geanatz/cortex-mcp.git"
46
+ "url": "git+https://github.com/geanatz/cortex-mcp.git"
43
47
  },
48
+ "bugs": {
49
+ "url": "https://github.com/geanatz/cortex-mcp/issues"
50
+ },
51
+ "homepage": "https://github.com/geanatz/cortex-mcp#readme",
44
52
  "files": [
45
53
  "dist/**/*",
46
54
  "!dist/.tsbuildinfo",