jobnimbus-mcp-client
Version:
JobNimbus MCP Client - Connect Claude Desktop to remote JobNimbus MCP server
272 lines • 11.9 kB
JavaScript
/**
* Get Activities Tool
* Enhanced with schedule filtering, activity type filtering, and sorting capabilities
*/
import { BaseTool } from '../baseTool.js';
import { compactActivity, compactArray } from '../../utils/compactData.js';
export class GetActivitiesTool extends BaseTool {
get definition() {
return {
name: 'get_activities',
description: 'Retrieve activities from JobNimbus with pagination, date filtering, scheduling filters, activity type filtering, and sorting',
inputSchema: {
type: 'object',
properties: {
from: {
type: 'number',
description: 'Starting index for pagination (default: 0)',
},
size: {
type: 'number',
description: 'Number of records to retrieve (default: 50, max: 100)',
},
date_from: {
type: 'string',
description: 'Start date filter for date_created (YYYY-MM-DD format)',
},
date_to: {
type: 'string',
description: 'End date filter for date_created (YYYY-MM-DD format)',
},
scheduled_from: {
type: 'string',
description: 'Filter activities scheduled on or after this date (date_start >= fecha, YYYY-MM-DD format)',
},
scheduled_to: {
type: 'string',
description: 'Filter activities scheduled on or before this date (date_start <= fecha, YYYY-MM-DD format)',
},
has_schedule: {
type: 'boolean',
description: 'Filter only activities with scheduled dates (date_start > 0)',
},
activity_type: {
type: 'string',
description: 'Filter by activity type (e.g., "Meeting", "Call", "Task")',
},
sort_by: {
type: 'string',
description: 'Field to sort by',
enum: ['date_start', 'date_end', 'date_created', 'date_updated'],
},
order: {
type: 'string',
description: 'Sort order (asc or desc)',
enum: ['asc', 'desc'],
},
include_full_details: {
type: 'boolean',
description: 'Return full activity details. Default: false (compact mode - RECOMMENDED to prevent token limit issues). WARNING: Setting to true with large result sets may cause Claude Desktop to crash. Only use for small queries (< 20 results).',
},
},
},
};
}
/**
* Convert YYYY-MM-DD string to Unix timestamp
*/
dateStringToUnix(dateStr, isStartOfDay = true) {
const date = new Date(dateStr + 'T00:00:00Z');
if (isStartOfDay) {
return Math.floor(date.getTime() / 1000);
}
else {
// End of day (23:59:59)
return Math.floor(date.getTime() / 1000) + 86399;
}
}
/**
* Filter activities by date_created
*/
filterByDateCreated(activities, dateFrom, dateTo) {
let filtered = activities;
if (dateFrom) {
const fromTs = this.dateStringToUnix(dateFrom, true);
filtered = filtered.filter(a => (a.date_created || 0) >= fromTs);
}
if (dateTo) {
const toTs = this.dateStringToUnix(dateTo, false);
filtered = filtered.filter(a => (a.date_created || 0) <= toTs);
}
return filtered;
}
/**
* Filter activities by scheduling parameters (date_start/date_end)
*/
filterBySchedule(activities, scheduledFrom, scheduledTo, hasSchedule) {
let filtered = activities;
// Filter by has_schedule first
if (hasSchedule !== undefined) {
if (hasSchedule) {
filtered = filtered.filter(a => (a.date_start || 0) > 0);
}
else {
filtered = filtered.filter(a => (a.date_start || 0) === 0);
}
}
// Filter by scheduled_from
if (scheduledFrom) {
const scheduledFromTs = this.dateStringToUnix(scheduledFrom, true);
filtered = filtered.filter(a => (a.date_start || 0) >= scheduledFromTs);
}
// Filter by scheduled_to
if (scheduledTo) {
const scheduledToTs = this.dateStringToUnix(scheduledTo, false);
filtered = filtered.filter(a => {
const dateStart = a.date_start || 0;
return dateStart > 0 && dateStart <= scheduledToTs;
});
}
return filtered;
}
/**
* Filter activities by activity type
*/
filterByActivityType(activities, activityType) {
if (!activityType) {
return activities;
}
const lowerType = activityType.toLowerCase();
return activities.filter(a => {
const type = (a.type || '').toLowerCase();
return type.includes(lowerType);
});
}
/**
* Sort activities by specified field
*/
sortActivities(activities, sortBy, order = 'desc') {
if (!sortBy || activities.length === 0) {
return activities;
}
const validFields = ['date_start', 'date_end', 'date_created', 'date_updated'];
if (!validFields.includes(sortBy)) {
return activities;
}
const reverse = order === 'desc';
return [...activities].sort((a, b) => {
const aVal = a[sortBy] || 0;
const bVal = b[sortBy] || 0;
return reverse ? bVal - aVal : aVal - bVal;
});
}
async execute(input, context) {
const fromIndex = input.from || 0;
const requestedSize = Math.min(input.size || 50, 100);
const order = input.order || 'desc';
// Determine if we need to fetch all activities for filtering/sorting
const needsFullFetch = input.date_from ||
input.date_to ||
input.scheduled_from ||
input.scheduled_to ||
input.has_schedule !== undefined ||
input.activity_type ||
input.sort_by;
if (needsFullFetch) {
// Fetch all activities with pagination
const batchSize = 100;
const maxIterations = 50;
let allActivities = [];
let offset = 0;
let iteration = 0;
while (iteration < maxIterations) {
const params = { size: batchSize, from: offset };
const response = await this.client.get(context.apiKey, 'activities', params);
const batch = response.data?.activity || [];
if (batch.length === 0) {
break;
}
allActivities = allActivities.concat(batch);
offset += batchSize;
iteration++;
if (batch.length < batchSize) {
break;
}
}
// Apply date_created filtering
let filteredActivities = this.filterByDateCreated(allActivities, input.date_from, input.date_to);
// Apply schedule filtering
if (input.scheduled_from || input.scheduled_to || input.has_schedule !== undefined) {
filteredActivities = this.filterBySchedule(filteredActivities, input.scheduled_from, input.scheduled_to, input.has_schedule);
}
// Apply activity type filtering
if (input.activity_type) {
filteredActivities = this.filterByActivityType(filteredActivities, input.activity_type);
}
// Apply sorting
if (input.sort_by) {
filteredActivities = this.sortActivities(filteredActivities, input.sort_by, order);
}
// Paginate
const paginatedActivities = filteredActivities.slice(fromIndex, fromIndex + requestedSize);
// Apply compaction if not requesting full details OR if result set is too large
// Safety override: Force compact mode if more than 20 results to prevent token limit issues
const forceCompact = paginatedActivities.length > 20;
const useCompactMode = !input.include_full_details || forceCompact;
const resultActivities = useCompactMode
? compactArray(paginatedActivities, compactActivity)
: paginatedActivities;
return {
_code_version: 'v2.0-compact-mode-2025-10-10',
count: paginatedActivities.length,
total_filtered: filteredActivities.length,
total_fetched: allActivities.length,
iterations: iteration,
from: fromIndex,
size: requestedSize,
has_more: fromIndex + paginatedActivities.length < filteredActivities.length,
total_pages: Math.ceil(filteredActivities.length / requestedSize),
current_page: Math.floor(fromIndex / requestedSize) + 1,
date_filter_applied: !!(input.date_from || input.date_to),
date_from: input.date_from,
date_to: input.date_to,
schedule_filter_applied: !!(input.scheduled_from ||
input.scheduled_to ||
input.has_schedule !== undefined),
scheduled_from: input.scheduled_from,
scheduled_to: input.scheduled_to,
has_schedule: input.has_schedule,
activity_type_filter_applied: !!input.activity_type,
activity_type: input.activity_type,
sort_applied: !!input.sort_by,
sort_by: input.sort_by,
order: order,
compact_mode: useCompactMode,
compact_mode_forced: forceCompact,
activity: resultActivities,
};
}
else {
// Simple pagination without filtering
const params = {
from: fromIndex,
size: requestedSize,
};
const result = await this.client.get(context.apiKey, 'activities', params);
const activities = result.data?.activity || [];
// Apply compaction if not requesting full details OR if result set is too large
// Safety override: Force compact mode if more than 20 results to prevent token limit issues
const forceCompact = activities.length > 20;
const useCompactMode = !input.include_full_details || forceCompact;
const resultActivities = useCompactMode
? compactArray(activities, compactActivity)
: activities;
return {
_code_version: 'v2.0-compact-mode-2025-10-10',
count: activities.length,
total_filtered: activities.length,
from: fromIndex,
size: requestedSize,
has_more: false,
date_filter_applied: false,
schedule_filter_applied: false,
activity_type_filter_applied: false,
sort_applied: false,
compact_mode: useCompactMode,
compact_mode_forced: forceCompact,
activity: resultActivities,
};
}
}
}
//# sourceMappingURL=getActivities.js.map