import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { JsmClient } from '../api/jsmClient.js';
import { validateParentObjectType, formatError, logDebug, paginateAqlSearch, formatPaginationInfo } from '../utils/index.js';
export const searchChildObjectsTool: Tool = {
name: 'search_child_objects',
description: 'Search for child objects of a specific parent object type, with optional filters and robust automatic pagination.',
inputSchema: {
type: 'object',
properties: {
parentObjectType: {
type: 'string',
description: 'Name of the parent object type to search children for'
},
filters: {
type: 'object',
description: 'Optional filters to apply to the search',
properties: {
dateFrom: {
type: 'string',
description: 'Start date for filtering (format: YYYY-MM-DD HH:mm)',
pattern: '^\\d{4}-\\d{2}-\\d{2}( \\d{2}:\\d{2})?$'
},
dateTo: {
type: 'string',
description: 'End date for filtering (format: YYYY-MM-DD HH:mm)',
pattern: '^\\d{4}-\\d{2}-\\d{2}( \\d{2}:\\d{2})?$'
},
keyPrefix: {
type: 'string',
description: 'Key prefix to filter objects (e.g., "IASM" for keys starting with IASM)'
}
},
additionalProperties: false
},
autoPages: {
type: 'boolean',
description: 'Enable automatic pagination to fetch all results (default: true for child objects)',
default: true
},
maxPages: {
type: 'number',
description: 'Maximum number of pages to fetch when autoPages=true (default: 10, safety limit)',
default: 10
},
pageSize: {
type: 'number',
description: 'Number of results per page when autoPages=true (default: 1000)',
default: 1000
}
},
required: ['parentObjectType']
}
};
export async function handleSearchChildObjects(
client: JsmClient,
args: any
): Promise<{ content: Array<{ type: string; text: string }> }> {
try {
const {
parentObjectType,
filters,
autoPages = true, // Default to true for child objects
maxPages = 10,
pageSize = 1000
} = args;
validateParentObjectType(parentObjectType);
logDebug('Searching child objects', {
parentObjectType,
filters,
autoPages,
maxPages,
pageSize
});
// Build AQL query for child objects
let aqlQuery = `objectType IN objectTypeAndChildren("${parentObjectType}")`;
if (filters?.keyPrefix) {
aqlQuery += ` AND Key startswith ${filters.keyPrefix}`;
}
if (filters?.dateFrom && filters?.dateTo) {
aqlQuery += ` AND "Created" >= "${filters.dateFrom}" AND "Created" <= "${filters.dateTo}"`;
}
let filterInfo = '';
if (filters) {
const filterParts = [];
if (filters.keyPrefix) filterParts.push(`Key prefix: ${filters.keyPrefix}`);
if (filters.dateFrom && filters.dateTo) {
filterParts.push(`Date range: ${filters.dateFrom} to ${filters.dateTo}`);
}
if (filterParts.length > 0) {
filterInfo = ` (Filters: ${filterParts.join(', ')})`;
}
}
let summary: string;
let allResults: any[];
if (autoPages) {
// Use robust automatic pagination
const paginatedResult = await paginateAqlSearch(
client,
{ qlQuery: aqlQuery },
{ maxPages, pageSize }
);
summary = `Child objects of "${parentObjectType}"${filterInfo}: ${formatPaginationInfo(paginatedResult)}`;
allResults = paginatedResult.values;
logDebug('Auto-paginated child objects search completed', {
parentObjectType,
pagesFetched: paginatedResult.pagesFetched,
totalRetrieved: paginatedResult.totalRetrieved,
limitReached: paginatedResult.limitReached
});
} else {
// Traditional single-page request
const result = await client.searchAssetsAql({
qlQuery: aqlQuery,
startAt: 0,
maxResults: pageSize
});
logDebug('Single-page child objects search completed', {
total: result.total,
returned: result.values?.length,
parentObjectType
});
summary = `Found ${result.total} child objects of "${parentObjectType}"${filterInfo} (showing ${result.values?.length || 0} results)`;
allResults = result.values || [];
}
let formattedResults = '';
if (allResults.length > 0) {
formattedResults = allResults.map((asset, index) => {
const attributes = asset.attributes
.map((attr: any) =>
attr.objectAttributeValues
.map((val: any) => `${val.displayValue}`)
.join(', ')
)
.filter((val: string) => val.length > 0)
.join(' | ');
const hierarchy = asset.objectType.parentObjectTypeId ?
` (Child of type ID: ${asset.objectType.parentObjectTypeId})` : '';
return `${index + 1}. ${asset.label} (${asset.objectKey})
Type: ${asset.objectType.name}${hierarchy}
Created: ${asset.created}
Updated: ${asset.updated}
Attributes: ${attributes || 'None'}`;
}).join('\n\n');
}
return {
content: [
{
type: 'text',
text: `${summary}\n\n${formattedResults}`
}
]
};
} catch (error) {
logDebug('Search child objects error', error);
return {
content: [
{
type: 'text',
text: `Error searching child objects: ${formatError(error)}`
}
]
};
}
}