UNPKG

aios-core

Version:

Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework

535 lines (481 loc) 17.8 kB
tool: schema_version: 2.0 id: clickup type: mcp name: ClickUp version: 1.0.0 description: ClickUp project management with pre-execution validation and API complexity handling knowledge_strategy: executable executable_knowledge: validators: # Combined validator for create_task (all checks in one function) - id: validate-create-task validates: create_task language: javascript checks: - required_fields: [name, list_id] function: | (function() { const errors = []; // 1. Required fields if (!args.args.name) { errors.push("name is required"); } if (!args.args.list_id && !args.args.listId) { errors.push("list_id is required"); } // 2. Assignee format (must be array for create) if (args.args.assignees && typeof args.args.assignees === 'object' && !Array.isArray(args.args.assignees)) { errors.push("assignees must be array for create_task, got object"); } // 3. Custom fields structure const customFields = args.args.custom_fields; if (customFields) { if (!Array.isArray(customFields)) { errors.push("custom_fields must be an array"); } else { customFields.forEach((field, index) => { if (!field.id) { errors.push(`custom_fields[${index}] missing required 'id' field`); } if (!field.hasOwnProperty('value')) { errors.push(`custom_fields[${index}] missing required 'value' field`); } }); } } // 4. List ID format (numeric string) const listId = args.args.list_id || args.args.listId; if (listId && !/^\d+$/.test(String(listId))) { errors.push("list_id must be a numeric string"); } // 5. Priority range (1-4) const priority = args.args.priority; if (priority !== undefined && priority !== null) { const p = Number(priority); if (isNaN(p) || p < 1 || p > 4) { errors.push("priority must be between 1 (urgent) and 4 (low)"); } } // 6. Time estimate (positive number) const timeEstimate = args.args.time_estimate; if (timeEstimate !== undefined) { const estimate = Number(timeEstimate); if (isNaN(estimate) || estimate < 0) { errors.push("time_estimate must be a positive number (milliseconds)"); } } return { valid: errors.length === 0, errors: errors }; })(); # Combined validator for update_task - id: validate-update-task validates: update_task language: javascript function: | (function() { const errors = []; const assignees = args.args.assignees; // Assignees format (must be object with add/rem for update) if (assignees) { if (Array.isArray(assignees)) { errors.push("assignees must be object {add: [], rem: []} for update_task, got array"); } else if (typeof assignees === 'object') { if (assignees.add && !Array.isArray(assignees.add)) { errors.push("assignees.add must be an array"); } if (assignees.rem && !Array.isArray(assignees.rem)) { errors.push("assignees.rem must be an array"); } } } return { valid: errors.length === 0, errors: errors }; })(); # Validator for parse_webhook - id: validate-webhook-payload validates: parse_webhook language: javascript function: | (function() { const errors = []; const payload = args.args; if (!payload) { errors.push("payload is required"); } else { // Check for required webhook identification fields if (!payload.event && !payload.webhook_id && !payload.history_items) { errors.push("Invalid webhook payload: missing event, webhook_id, or history_items"); } } return { valid: errors.length === 0, errors: errors }; })(); helpers: - id: extract-custom-field language: javascript runtime: isolated_vm description: "Extract specific custom field value from task response" function: | (function() { const { response, fieldName } = args; if (!response || !response.custom_fields) { return null; } const field = response.custom_fields.find(f => f.name === fieldName); return field || null; })(); - id: format-assignee-for-create language: javascript runtime: isolated_vm description: "Convert assignee IDs to create_task format (array)" function: | (function() { const { assignees } = args; if (!assignees) return []; if (Array.isArray(assignees)) return assignees; if (typeof assignees === 'number') return [assignees]; return []; })(); - id: format-assignee-for-update language: javascript runtime: isolated_vm description: "Convert assignee IDs to update_task format (object)" function: | (function() { const { add, remove } = args; const result = {}; if (add) result.add = Array.isArray(add) ? add : [add]; if (remove) result.rem = Array.isArray(remove) ? remove : [remove]; return result; })(); - id: parse-webhook-type language: javascript runtime: isolated_vm description: "Detect webhook payload type from structure" function: | (function() { const { payload } = args; if (!payload) return 'unknown'; if (payload.event) return 'standard'; if (payload.webhook_id) return 'webhook_variant'; if (payload.history_items) return 'history_items'; return 'unknown'; })(); - id: extract-webhook-payload language: javascript runtime: isolated_vm description: "Extract actual payload based on webhook type" function: | (function() { const { webhook } = args; if (!webhook) return null; // Standard format if (webhook.event && webhook.payload) { return webhook.payload; } // Webhook variant format if (webhook.webhook_id && webhook.data && webhook.data.webhook) { return webhook.data.webhook.payload; } // History items format if (webhook.history_items) { return webhook; } return null; })(); - id: calculate-time-tracking-total language: javascript runtime: isolated_vm description: "Sum time tracking entries for a task" function: | (function() { const { timeEntries } = args; if (!Array.isArray(timeEntries)) return 0; return timeEntries.reduce((total, entry) => { return total + (entry.duration || 0); }, 0); })(); - id: format-custom-field-update language: javascript runtime: isolated_vm description: "Format custom field for API update" function: | (function() { const { fieldId, value } = args; return { id: fieldId, value: value }; })(); api_complexity: payload_schemas: - type: standard detection: "event field exists in root" payload_path: "payload" example: | { "event": "taskCreated", "payload": { "task": {...} } } - type: webhook_variant detection: "webhook_id field exists" payload_path: "data.webhook.payload" example: | { "webhook_id": "abc123", "data": { "webhook": { "payload": { "task": {...} } } } } - type: history_items detection: "history_items array exists" payload_path: "history_items" example: | { "history_items": [ { "field": "status", "before": "todo", "after": "in progress" } ] } field_mappings: assignees: create_format: structure: "[user_id1, user_id2]" type: "array of integers" example: "[123, 456]" update_format: structure: "{add: [user_id], rem: [user_id]}" type: "object with add/rem arrays" example: "{add: [789], rem: [456]}" note: "Different formats for create vs update - common error source" custom_fields: structure: "[{id: field_id, value: field_value}]" type: "array of objects" validation: "Each object must have 'id' and 'value' keys" example: "[{id: 'field-uuid', value: 'text value'}]" list_id: format: "numeric string" validation: "Must match /^\\d+$/" note: "Often called 'listId' in some API responses but 'list_id' in requests" api_quirks: - quirk: assignee_format_mismatch description: "Create API expects [123], Update expects {add: [123], rem: []}" impact: "Runtime errors if wrong format used" mitigation: "Use command-specific validators (validate-assignee-format, validate-task-update-assignees)" - quirk: rate_limit_headers_missing description: "Rate limit info only in X-RateLimit-* headers, not in error response body" impact: "Difficult to implement retry logic without header access" mitigation: "Always capture response headers for rate limit tracking" - quirk: custom_field_id_instability description: "Custom field IDs can change when field is renamed or recreated" impact: "Stored field IDs may become invalid" mitigation: "Use field names to lookup IDs via get_workspace_hierarchy before updates" - quirk: inconsistent_list_id_naming description: "API uses 'list_id' in requests but 'listId' in responses" impact: "Field mapping confusion in response processing" mitigation: "Normalize to 'list_id' in all internal processing" anti_patterns: - pattern: wrong_assignee_format description: "Using update format in create API" category: api_usage severity: high wrong: | create_task({ name: "Task", list_id: "123", assignees: {add: [456]} // ❌ Wrong - object format }) correct: | create_task({ name: "Task", list_id: "123", assignees: [456] // ✅ Correct - array format }) rationale: "Create API expects array, not object. Use format-assignee-for-create helper." - pattern: missing_custom_field_validation description: "Setting custom fields without validating structure" category: data_validation severity: medium wrong: | create_task({ name: "Task", list_id: "123", custom_fields: "field-value" // ❌ Wrong - string instead of array }) correct: | create_task({ name: "Task", list_id: "123", custom_fields: [ // ✅ Correct - array of objects {id: "field-uuid", value: "field-value"} ] }) rationale: "Custom fields must be array of {id, value} objects. Use validate-custom-field-structure." - pattern: undocumented_rate_limit_handling description: "Not checking rate limit headers before subsequent requests" category: api_reliability severity: medium wrong: | // Make 100 requests in rapid succession for (let i = 0; i < 100; i++) { await create_task({...}); // ❌ No rate limit checking } correct: | for (let i = 0; i < 100; i++) { const response = await create_task({...}); const remaining = response.headers['X-RateLimit-Remaining']; if (remaining < 10) { // Wait for reset await sleep(60000); // ✅ Proper rate limit handling } } rationale: "ClickUp API has strict rate limits. Always monitor X-RateLimit-* headers." examples: # WORKFLOW EXAMPLE: Complete Story Creation Flow story_creation_workflow: - step: 1 action: get_workspace_hierarchy description: "First, get workspace structure to find list IDs" command: get_workspace_hierarchy input: {} output_extract: | // From the response, find the Backlog list: { "spaces": [{ "name": "AIOS Project", "lists": [ { "name": "Backlog", "id": "901317181013" // ← This is the list_id you need } ] }] } notes: "CRITICAL: You must use the numeric list_id from this response in create_task" - step: 2 action: create_task description: "Create story task with discovered list_id" command: create_task input: list_id: "901317181013" # ← From step 1 (numeric string) name: "Story 5.2: Implement Feature X" markdown_description: "Complete story content here..." parent: "86acfeqeq" # Epic task ID (if creating as subtask) tags: ["story", "epic-5", "story-5.2"] custom_fields: - id: "epic_number" value: 5 - id: "story_number" value: "5.2" output: id: "86acfetr9" name: "Story 5.2: Implement Feature X" url: "https://app.clickup.com/t/86acfetr9" notes: "list_id MUST be numeric string from get_workspace_hierarchy. Using 'listName' or non-numeric values will fail validation." create_task: - scenario: success description: "Standard task creation with assignees" input: name: "Implement user authentication" list_id: "123456789" assignees: [456, 789] priority: 2 output: id: "task_abc123" status: "created" name: "Implement user authentication" - scenario: failure_invalid_param description: "Wrong assignee format (object instead of array)" input: name: "Fix login bug" list_id: "123456789" assignees: {add: [456]} error: code: VALIDATION_ERROR message: "assignees must be array for create_task, got object" validator: validate-assignee-format - scenario: failure_api_error description: "Invalid list_id" input: name: "Task" list_id: "invalid" error: code: API_ERROR message: "List not found" http_status: 404 - scenario: edge_case description: "Task with custom fields" input: name: "Research task" list_id: "123456789" custom_fields: - id: "field-uuid-1" value: "High" - id: "field-uuid-2" value: 1000 output: id: "task_xyz789" status: "created" custom_fields: - name: "Complexity" value: "High" - name: "Estimate" value: 1000 update_task: - scenario: success description: "Update task assignees" input: task_id: "task_abc123" assignees: add: [999] rem: [456] output: id: "task_abc123" status: "updated" - scenario: failure_invalid_param description: "Wrong assignee format (array instead of object)" input: task_id: "task_abc123" assignees: [999] error: code: VALIDATION_ERROR message: "assignees must be object {add: [], rem: []} for update_task, got array" validator: validate-task-update-assignees - scenario: failure_api_error description: "Task not found" input: task_id: "nonexistent" name: "Updated name" error: code: API_ERROR message: "Task not found" http_status: 404 - scenario: edge_case description: "Update with partial assignee change" input: task_id: "task_abc123" assignees: add: [111, 222] output: id: "task_abc123" assignees: [456, 789, 111, 222] mcp_specific: server_command: "npx -y @modelcontextprotocol/server-clickup" transport: stdio environment_variables: - name: CLICKUP_API_KEY required: true description: "ClickUp API key for authentication" health_check: method: tool_call command: get_workspace_hierarchy expected_response: "Object with 'spaces' array" timeout_ms: 5000