search_projects
Find Autotask projects using search terms and filters like company, status, or manager. Returns optimized data for efficient project management.
Instructions
Search for projects in Autotask with optional filters. Returns optimized project data to prevent large responses.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| searchTerm | No | Search term for project name | |
| companyID | No | Filter by company ID | |
| status | No | Filter by project status | |
| projectManagerResourceID | No | Filter by project manager resource ID | |
| pageSize | No | Number of results to return (default: 25, max: 100) |
Implementation Reference
- src/handlers/tool.handler.ts:374-404 (registration)MCP tool registration and input schema definition for 'search_projects' in the listTools() method.{ name: 'search_projects', description: 'Search for projects in Autotask with optional filters. Returns optimized project data to prevent large responses.', inputSchema: { type: 'object', properties: { searchTerm: { type: 'string', description: 'Search term for project name' }, companyID: { type: 'number', description: 'Filter by company ID' }, status: { type: 'number', description: 'Filter by project status' }, projectManagerResourceID: { type: 'number', description: 'Filter by project manager resource ID' }, pageSize: { type: 'number', description: 'Number of results to return (default: 25, max: 100)', minimum: 1, maximum: 100 } }, required: [] }
- src/handlers/tool.handler.ts:1128-1131 (handler)Handler logic in callTool() method that dispatches 'search_projects' tool calls to the AutotaskService.searchProjects method.case 'search_projects': result = await this.autotaskService.searchProjects(args); message = `Found ${result.length} projects`; break;
- Core implementation of searchProjects method in AutotaskService, including direct API workaround for projects.query endpoint and data optimization.async searchProjects(options: AutotaskQueryOptions = {}): Promise<AutotaskProject[]> { const client = await this.ensureClient(); try { this.logger.debug('Searching projects with options:', options); // WORKAROUND: The autotask-node library's projects.list() method is broken // It uses GET with query params instead of POST with body like the working companies endpoint // We'll bypass it and make the correct API call directly // Essential fields for optimized response size const essentialFields = [ 'id', 'projectName', 'projectNumber', 'description', 'status', 'projectType', 'department', 'companyID', 'projectManagerResourceID', 'startDateTime', 'endDateTime', 'actualHours', 'estimatedHours', 'laborEstimatedRevenue', 'createDate', 'completedDate', 'contractID', 'originalEstimatedRevenue' ]; // Prepare search body in the same format as working companies endpoint const searchBody: any = {}; // Ensure there's a filter - Autotask API requires a filter if (!options.filter || (Array.isArray(options.filter) && options.filter.length === 0) || (!Array.isArray(options.filter) && Object.keys(options.filter).length === 0)) { searchBody.filter = [ { "op": "gte", "field": "id", "value": 0 } ]; } else { // If filter is provided as an object, convert to array format expected by API if (!Array.isArray(options.filter)) { const filterArray = []; for (const [field, value] of Object.entries(options.filter)) { filterArray.push({ "op": "eq", "field": field, "value": value }); } searchBody.filter = filterArray; } else { searchBody.filter = options.filter; } } // Add other search parameters if (options.sort) searchBody.sort = options.sort; if (options.page) searchBody.page = options.page; if (options.pageSize) searchBody.pageSize = options.pageSize; // Add field limiting for optimization if (essentialFields.length > 0) { searchBody.includeFields = essentialFields; } // Set default pagination and field limits const pageSize = options.pageSize || 25; const finalPageSize = pageSize > 100 ? 100 : pageSize; searchBody.pageSize = finalPageSize; this.logger.debug('Making direct API call to Projects/query with body:', searchBody); // Make the correct API call directly using the axios instance from the client const response = await (client as any).axios.post('/Projects/query', searchBody); // Extract projects from response (should be in response.data.items format) let projects: AutotaskProject[] = []; if (response.data && response.data.items) { projects = response.data.items; } else if (Array.isArray(response.data)) { projects = response.data; } else { this.logger.warn('Unexpected response format from Projects/query:', response.data); projects = []; } // Transform projects to optimize data size const optimizedProjects = projects.map(project => this.optimizeProjectData(project)); this.logger.info(`Retrieved ${optimizedProjects.length} projects (optimized for size)`); return optimizedProjects; } catch (error: any) { // Check if it's the same 405 error pattern if (error.response && error.response.status === 405) { this.logger.warn('Projects endpoint may not support listing via API (405 Method Not Allowed). This is common with some Autotask configurations.'); return []; } this.logger.error('Failed to search projects:', error); throw error; } }
- src/mcp/server.ts:113-123 (handler)MCP server registration of the generic tool call handler that routes to EnhancedAutotaskToolHandler.callTool().this.server.setRequestHandler(CallToolRequestSchema, async (request) => { try { this.logger.debug(`Handling tool call: ${request.params.name}`); const result = await this.toolHandler.callTool( request.params.name, request.params.arguments || {} ); return { content: result.content, isError: result.isError };
- src/mcp/server.ts:98-102 (schema)MCP server registration for listing tools, which calls toolHandler.listTools() exposing the 'search_projects' tool.this.server.setRequestHandler(ListToolsRequestSchema, async () => { try { this.logger.debug('Handling list tools request'); const tools = await this.toolHandler.listTools(); return { tools };