Skip to content

Latest commit

 

History

History
771 lines (639 loc) · 16.2 KB

File metadata and controls

771 lines (639 loc) · 16.2 KB

Array-Based Unified API

← Back to Index


Introduced in v7.2.0 - Unified array-based tools that replace separate single-item and batch operations with a single interface.

Table of Contents

  1. Overview
  2. Benefits
  3. Core Tools
  4. Response Format
  5. Error Handling
  6. Migration Guide

Overview

Version 7.2.0 introduces array-based tools that accept 1-100 items per call and return consistent response formats. This replaces the previous pattern of having separate tools for single and batch operations.

Design Philosophy

Before (v7.1.0 and earlier):

// Single item
await create_work_item({ title: 'Task', categoryId: 'programming' });

// Batch (different tool)
await batch_create_work_items({
  items: [
    { title: 'Task 1', categoryId: 'programming' },
    { title: 'Task 2', categoryId: 'design' }
  ]
});

After (v7.2.0+):

// Single OR batch - same tool
await create_work_items({
  items: [
    { title: 'Task', categoryId: 'programming' }  // Single
  ]
});

await create_work_items({
  items: [
    { title: 'Task 1', categoryId: 'programming' },  // Batch
    { title: 'Task 2', categoryId: 'design' }
  ]
});

Benefits

1. Unified Interface

One tool handles both single and batch operations:

  • Simpler API - Fewer tools to learn
  • Consistent usage - Same parameters for 1 or 100 items
  • Better scaling - Start with 1, scale to 100 seamlessly

2. Consistent Response Format

All array-based tools return:

{
  items: Array<T | null>;       // Results (null for failures)
  total: number;                // Total items processed
  successful: number;           // Success count
  failed: number;               // Failure count
  errors: Array<ErrorDetail>;   // Per-item errors
  warnings: Array<string>;      // Non-fatal warnings
}

3. Auto-Create Tags

Update operations auto-create tags by name:

await update_work_items({
  items: [{
    workItemId: 123,
    tagIds: ['existing-tag', 'new-tag-name']  // Auto-creates 'new-tag-name'
  }]
});

// Response includes warning:
// warnings: ["Auto-created tag: 'new-tag-name'"]

4. Partial Success Handling

Individual failures don't block other items:

const result = await create_work_items({
  items: [
    { title: 'Valid Task', categoryId: 'programming' },  // ✅ Success
    { title: 'Invalid', categoryId: 'nonexistent' },     // ❌ Fails
    { title: 'Another Valid', categoryId: 'design' }     // ✅ Success
  ]
});

// result.successful: 2
// result.failed: 1
// result.items: [{ workItemId: 1 }, null, { workItemId: 3 }]

5. Context Reduction

Combined with Slim Mode, reduces tool count by 73%:

  • Full Mode: 89 tools
  • Slim Mode + Array API: 26 tools (~71% reduction)
  • With verbosity=slim: Additional ~60% response size reduction

Core Tools

create_work_items

Create 1-100 work items in a single call.

Parameters:

  • projectId (number, optional): Project ID
  • items (array, required): Work items to create (1-100)
    • title (string, required): Task title
    • categoryId (number|string, required): Category ID or name
    • description (string, optional): Details (markdown)
    • importanceLevelId (number|string, optional): Priority
    • tagIds (array, optional): Tag IDs or names
    • boardId (number, optional): Sprint assignment
    • estimatedCost (number, optional): Hours/points
    • assignedUserIds (array, optional): User IDs

Response:

{
  "items": [
    { "workItemId": 1, "title": "Task 1", ... },
    { "workItemId": 2, "title": "Task 2", ... }
  ],
  "total": 2,
  "successful": 2,
  "failed": 0,
  "errors": [],
  "warnings": []
}

Examples:

// Single task
await create_work_items({
  items: [{
    title: 'Implement OAuth',
    categoryId: 'programming',
    importanceLevelId: 'high',
    estimatedCost: 8
  }]
});

// Multiple tasks with name resolution
await create_work_items({
  projectId: 230954,
  items: [
    {
      title: 'Backend API',
      categoryId: 'programming',
      tagIds: ['vulkan', 'performance']
    },
    {
      title: 'UI Bug',
      categoryId: 'bug',
      importanceLevelId: 'urgent'
    },
    {
      title: 'Design Review',
      categoryId: 'design',
      estimatedCost: 4,
      boardId: 650299
    }
  ]
});

update_work_items

Update 1-100 work items. Supports auto-creating tags by name.

Parameters:

  • projectId (number, optional): Project ID
  • items (array, required): Updates (1-100)
    • workItemId (number, required): Item to update
    • title (string, optional): New title
    • description (string, optional): New description
    • stageId (number, optional): Move to stage
    • boardId (number, optional): Move to board
    • isCompleted (boolean, optional): Mark completed
    • tagIds (array, optional): Replace tags (auto-creates unknowns)
    • assignedUserIds (array, optional): Replace assignments
    • parentStoryId (number, optional): Set parent

Response:

{
  "items": [
    { "workItemId": 123, "title": "Updated Task", ... }
  ],
  "total": 1,
  "successful": 1,
  "failed": 0,
  "errors": [],
  "warnings": ["Auto-created tag: 'new-feature'"]
}

Examples:

// Single update
await update_work_items({
  items: [{
    workItemId: 123,
    stageId: 2,
    isCompleted: true
  }]
});

// Batch updates with tag auto-create
await update_work_items({
  items: [
    {
      workItemId: 123,
      tagIds: ['vulkan', 'new-feature'],  // Auto-creates 'new-feature'
      stageId: 3
    },
    {
      workItemId: 124,
      isCompleted: true
    },
    {
      workItemId: 125,
      boardId: 650300,
      assignedUserIds: [1, 2]
    }
  ]
});

delete_work_items

Delete 1-100 work items (requires confirmation).

Parameters:

  • projectId (number, optional): Project ID
  • workItemIds (array, required): IDs to delete (1-100)

Response (Staging):

{
  "status": "confirmation_required",
  "confirmationToken": "del_1734567890000_abc123",
  "expiresAt": "2025-12-18T15:00:00.000Z",
  "preview": {
    "totalItems": 3,
    "exceedsCache": false,
    "itemsAtRisk": 0,
    "breakdown": {
      "completed": 1,
      "inProgress": 1,
      "inClosedSprints": 0,
      "withLoggedHours": 2
    },
    "samples": [
      { "workItemId": 1, "title": "Task 1", "status": "completed" }
    ]
  }
}

Workflow:

// 1. Stage deletion
const staged = await delete_work_items({
  workItemIds: [123, 124, 125]
});

// 2. Review preview
console.log(`Deleting ${staged.preview.totalItems} items`);
console.log(`Breakdown:`, staged.preview.breakdown);

// 3. Confirm
const result = await confirm_deletion({
  confirmationToken: staged.confirmationToken,
  acknowledged: 'DELETE'
});

// Or cancel
await cancel_deletion({
  confirmationToken: staged.confirmationToken
});

See Deletion Safety for comprehensive guide.


add_comments

Add 1-100 comments to work items.

Parameters:

  • projectId (number, optional): Project ID
  • comments (array, required): Comments (1-100)
    • workItemId (number, required): Target work item
    • text (string, required): Comment text (markdown)

Response:

{
  "items": [
    { "commentId": 1, "text": "Comment", "workItemId": 42 }
  ],
  "total": 1,
  "successful": 1,
  "failed": 0,
  "errors": [],
  "warnings": []
}

Example:

// Add comments to multiple tasks
await add_comments({
  comments: [
    {
      workItemId: 123,
      text: '## Progress\n\nCompleted phase 1, moving to testing'
    },
    {
      workItemId: 124,
      text: 'Blocked by dependency on #123'
    },
    {
      workItemId: 125,
      text: 'Ready for code review'
    }
  ]
});

create_boards

Create 1-100 boards/sprints.

Parameters:

  • projectId (number, optional): Project ID
  • boards (array, required): Boards to create (1-100)
    • name (string, required): Board name
    • description (string, optional): Description
    • startDate (string, optional): Start date (ISO)
    • endDate (string, optional): End date (ISO)

Response:

{
  "items": [
    { "boardId": 1, "name": "Sprint 1", "startDate": "2025-01-01" }
  ],
  "total": 1,
  "successful": 1,
  "failed": 0,
  "errors": [],
  "warnings": []
}

Example:

// Create quarterly sprints
await create_boards({
  boards: [
    {
      name: 'Sprint 1: Foundation',
      startDate: '2025-01-01',
      endDate: '2025-01-14'
    },
    {
      name: 'Sprint 2: Core Features',
      startDate: '2025-01-15',
      endDate: '2025-01-28'
    },
    {
      name: 'Sprint 3: Polish',
      startDate: '2025-01-29',
      endDate: '2025-02-11'
    }
  ]
});

log_work_sessions

Log work time for 1-100 work items.

Parameters:

  • projectId (number, optional): Project ID
  • sessions (array, required): Work sessions (1-100)
    • workItemId (number, required): Work item ID
    • hours (number, required): Hours worked (> 0)
    • description (string, optional): Session description
    • date (string, optional): Date (ISO, default: today)

Response:

{
  "items": [
    { "workItemId": 42, "hours": 2.5 }
  ],
  "total": 1,
  "successful": 1,
  "failed": 0,
  "totalHours": 2.5,
  "errors": [],
  "warnings": []
}

Example:

// Log time across multiple tasks
await log_work_sessions({
  sessions: [
    {
      workItemId: 123,
      hours: 2,
      description: 'Backend API implementation'
    },
    {
      workItemId: 124,
      hours: 1.5,
      description: 'Code review and testing'
    },
    {
      workItemId: 125,
      hours: 0.5,
      description: 'Bug fix in auth flow'
    }
  ]
});
// Response: { totalHours: 4 }

Note: This tool does NOT integrate with automatic time tracking. Use complete_task for auto-tracked time logging.


Response Format

All array-based tools return a consistent structure:

interface ArrayResponse<T> {
  items: Array<T | null>;     // Results (null for failures)
  total: number;              // Total items processed
  successful: number;         // Success count
  failed: number;             // Failure count
  errors: ErrorDetail[];      // Per-item errors
  warnings: string[];         // Non-fatal warnings
}

interface ErrorDetail {
  index: number;              // Position in input array (0-based)
  error: string;              // Error message
  item: any;                  // Original input item
}

Understanding items Array

The items array preserves input order:

  • Successful items: Full object at original position
  • Failed items: null at original position

Example:

const result = await create_work_items({
  items: [
    { title: 'Task 1', categoryId: 'programming' },  // Index 0 - success
    { title: 'Task 2', categoryId: 'invalid' },      // Index 1 - fails
    { title: 'Task 3', categoryId: 'design' }        // Index 2 - success
  ]
});

// result.items: [
//   { workItemId: 1, title: 'Task 1', ... },  // Index 0
//   null,                                      // Index 1 (failed)
//   { workItemId: 3, title: 'Task 3', ... }   // Index 2
// ]
//
// result.successful: 2
// result.failed: 1
//
// result.errors: [{
//   index: 1,
//   error: "Invalid category: 'invalid'",
//   item: { title: 'Task 2', categoryId: 'invalid' }
// }]

Error Handling

Partial Success

Individual failures don't block other items:

const result = await create_work_items({
  items: [
    { title: 'Valid 1', categoryId: 'programming' },    // ✅
    { title: 'Invalid', categoryId: 'nonexistent' },    // ❌
    { title: 'Valid 2', categoryId: 'design' },         // ✅
    { title: 'Missing category' },                      // ❌ (missing required field)
    { title: 'Valid 3', categoryId: 'bug' }             // ✅
  ]
});

// result.successful: 3
// result.failed: 2
// result.errors: [
//   { index: 1, error: "Invalid category...", item: {...} },
//   { index: 3, error: "Missing required field...", item: {...} }
// ]

Handling Errors

const result = await update_work_items({ items: [...] });

if (result.failed > 0) {
  console.error(`${result.failed} items failed:`);
  result.errors.forEach(err => {
    console.error(`  Item ${err.index}: ${err.error}`);
    console.error(`  Input:`, err.item);
  });
}

if (result.successful > 0) {
  console.log(`✅ Successfully processed ${result.successful} items`);
  const successfulItems = result.items.filter(item => item !== null);
  // Process successful items...
}

Warnings

Non-fatal warnings (e.g., auto-created tags):

const result = await update_work_items({
  items: [{
    workItemId: 123,
    tagIds: ['existing-tag', 'new-tag']
  }]
});

if (result.warnings.length > 0) {
  console.warn('Warnings:', result.warnings);
  // ["Auto-created tag: 'new-tag'"]
}

Migration Guide

From Single-Item Tools

Before (v7.1.0):

await create_work_item({
  title: 'My Task',
  categoryId: 'programming'
});

After (v7.2.0):

await create_work_items({
  items: [{
    title: 'My Task',
    categoryId: 'programming'
  }]
});

Tip: Wrap single item in array.


From Batch Tools

Before (v7.1.0):

await batch_create_work_items({
  items: [
    { title: 'Task 1', categoryId: 'programming' },
    { title: 'Task 2', categoryId: 'design' }
  ]
});

After (v7.2.0):

await create_work_items({  // Renamed (no 'batch_' prefix)
  items: [
    { title: 'Task 1', categoryId: 'programming' },
    { title: 'Task 2', categoryId: 'design' }
  ]
});

Tip: Remove batch_ prefix, parameters unchanged.


From Setter Tools

Before (v7.1.0):

// Multiple API calls
await set_work_item_tags({
  workItemId: 123,
  tagIds: [1, 5]
});

await assign_work_item({
  workItemId: 123,
  userIds: [10, 20]
});

await set_work_item_parent({
  workItemId: 123,
  parentStoryId: 100
});

After (v7.2.0):

// Single API call
await update_work_items({
  items: [{
    workItemId: 123,
    tagIds: [1, 5],
    assignedUserIds: [10, 20],
    parentStoryId: 100
  }]
});

Tip: Use update_work_items for all property changes.


Best Practices

1. Batch Related Operations

❌ Bad:

for (const id of workItemIds) {
  await update_work_items({
    items: [{ workItemId: id, stageId: 3 }]
  });
}
// N API calls!

✅ Good:

await update_work_items({
  items: workItemIds.map(id => ({
    workItemId: id,
    stageId: 3
  }))
});
// 1 API call

2. Handle Partial Failures

const result = await update_work_items({ items: updates });

// Separate successful from failed
const successful = result.items
  .map((item, i) => ({ item, originalInput: updates[i] }))
  .filter(({ item }) => item !== null);

const failed = result.errors.map(err => ({
  input: err.item,
  error: err.error
}));

// Retry failed items if appropriate
if (failed.length > 0 && shouldRetry(failed)) {
  const retryResult = await update_work_items({
    items: failed.map(f => f.input)
  });
}

3. Use Auto-Create Tags

// Let the API create tags automatically
await update_work_items({
  items: [{
    workItemId: 123,
    tagIds: ['backend', 'urgent', 'new-feature']  // Auto-creates unknowns
  }]
});

// Check warnings for created tags
if (result.warnings.length > 0) {
  console.log('Auto-created:', result.warnings);
}

4. Limit Batch Size

Max 100 items per call. For larger sets, chunk:

const chunkSize = 100;
const chunks = [];

for (let i = 0; i < allItems.length; i += chunkSize) {
  chunks.push(allItems.slice(i, i + chunkSize));
}

for (const chunk of chunks) {
  const result = await create_work_items({ items: chunk });
  console.log(`Processed ${result.successful}/${result.total}`);
}

See Also: