Skip to main content
Glama
asachs01

Autotask MCP Server

autotask_router

Describe your intent to get the appropriate Autotask tool suggestion with pre-filled parameters. Use when uncertain which tool to call.

Instructions

Intelligent tool router - describe what you want to do and get the right tool suggestion with pre-filled parameters. Use this when unsure which tool to call.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
intentYesNatural language description of what you want to do (e.g., "find tickets for Acme Corp", "log 2 hours on ticket 12345", "create a quote for client")

Implementation Reference

  • Schema definition for autotask_router tool - accepts a natural language 'intent' string and returns a suggested tool with pre-filled parameters
      name: 'autotask_router',
      description: 'Intelligent tool router - describe what you want to do and get the right tool suggestion with pre-filled parameters. Use this when unsure which tool to call.',
      inputSchema: {
        type: 'object',
        properties: {
          intent: {
            type: 'string',
            description: 'Natural language description of what you want to do (e.g., "find tickets for Acme Corp", "log 2 hours on ticket 12345", "create a quote for client")'
          }
        },
        required: ['intent']
      }
    },
  • Registration of autotask_router as a meta-tool exposed in lazy loading mode alongside list_categories, list_category_tools, and execute_tool
    if (this.lazyLoading) {
      // In lazy loading mode, only expose the 3 meta-tools
      const metaTools = TOOL_DEFINITIONS.filter(t =>
        t.name === 'autotask_list_categories' ||
        t.name === 'autotask_list_category_tools' ||
        t.name === 'autotask_execute_tool' ||
        t.name === 'autotask_router'
      );
      this.logger.debug(`Lazy loading mode: exposing ${metaTools.length} meta-tools (${TOOL_DEFINITIONS.length} total available)`);
  • Handler dispatch for autotask_router - extracts the intent and calls routeIntent() to produce a tool suggestion
    ['autotask_router', async (a) => {
      const rawIntent = a.intent || '';
      const suggestion = this.routeIntent(rawIntent);
      return { result: suggestion, message: `Suggested tool: ${suggestion.suggestedTool}` };
    }],
  • The routeIntent() helper function that implements the natural language intent routing logic with a large decision tree matching keywords to suggest the best tool and pre-fill parameters
    private routeIntent(rawIntent: string): {
      suggestedTool: string;
      suggestedParams: Record<string, any>;
      description: string;
      requiredParams: string[];
    } {
      // Extract quoted strings from original (preserves case) before lowercasing
      const quotedStrings = rawIntent.match(/["']([^"']+)["']/g)?.map(s => s.slice(1, -1)) || [];
      const intent = rawIntent.toLowerCase();
    
      // Extract potential IDs from the intent
      const numbers = intent.match(/\b\d+\b/g)?.map(Number) || [];
    
      // Extract hours pattern (e.g., "2 hours", "1.5 hrs")
      const hoursMatch = intent.match(/(\d+(?:\.\d+)?)\s*(?:hours?|hrs?)/i);
      const hours = hoursMatch ? parseFloat(hoursMatch[1]) : undefined;
    
      // Decision tree based on keyword matching
      // Time tracking (check BEFORE tickets — "log hours on ticket" should route here, not to tickets)
      if (/\b(?:hours?|hrs?)\b/.test(intent) && /\b(?:log|enter|add|record|track|create)\b/.test(intent)) {
        const params: Record<string, any> = {};
        if (hours) params.hoursWorked = hours;
        // Look for ticket ID pattern
        const ticketIdMatch = intent.match(/ticket\s*#?\s*(\d+)/i) || intent.match(/on\s+(\d+)/);
        if (ticketIdMatch) params.ticketID = parseInt(ticketIdMatch[1]);
        else if (numbers[0] && !hours) params.ticketID = numbers[0];
        else if (numbers.length > 1) params.ticketID = numbers.find(n => n > 100) || numbers[1]; // larger numbers are likely ticket IDs
        return {
          suggestedTool: 'autotask_create_time_entry',
          suggestedParams: params,
          description: 'Log a time entry',
          requiredParams: [...(!params.ticketID ? ['ticketID'] : []), ...(!params.hoursWorked ? ['hoursWorked'] : [])],
        };
      }
    
      // Ticket operations
      if (/\b(?:tickets?|issues?|requests?)\b/.test(intent)) {
        if (/\b(?:create|open|new|submit)\b/.test(intent)) {
          const params: Record<string, any> = {};
          if (numbers[0]) params.companyId = numbers[0];
          if (quotedStrings[0]) params.title = quotedStrings[0];
          return {
            suggestedTool: 'autotask_create_ticket',
            suggestedParams: params,
            description: 'Create a new service ticket',
            requiredParams: [...(!params.companyId ? ['companyId'] : []), ...(!params.title ? ['title'] : [])],
          };
        }
        if (/\b(?:update|change|modify|edit|assign|reassign|close)\b/.test(intent)) {
          const params: Record<string, any> = {};
          if (numbers[0]) params.ticketId = numbers[0];
          return {
            suggestedTool: 'autotask_update_ticket',
            suggestedParams: params,
            description: 'Update an existing ticket',
            requiredParams: !params.ticketId ? ['ticketId'] : [],
          };
        }
        if (/\b(?:details?|info|view|show|get)\b/.test(intent) && numbers[0]) {
          return {
            suggestedTool: 'autotask_get_ticket_details',
            suggestedParams: { ticketID: numbers[0], fullDetails: true },
            description: 'Get full ticket details by ID',
            requiredParams: [],
          };
        }
        if (/\b(?:notes?|comments?)\b/.test(intent)) {
          if (/\b(?:add|create|post)\b/.test(intent)) {
            const params: Record<string, any> = {};
            if (numbers[0]) params.ticketId = numbers[0];
            return {
              suggestedTool: 'autotask_create_ticket_note',
              suggestedParams: params,
              description: 'Add a note to a ticket',
              requiredParams: [...(!params.ticketId ? ['ticketId'] : []), 'title', 'description'],
            };
          }
          const params: Record<string, any> = {};
          if (numbers[0]) params.ticketId = numbers[0];
          return {
            suggestedTool: 'autotask_search_ticket_notes',
            suggestedParams: params,
            description: 'List notes on a ticket',
            requiredParams: !params.ticketId ? ['ticketId'] : [],
          };
        }
        // Default: search tickets
        const params: Record<string, any> = {};
        if (quotedStrings[0]) params.searchTerm = quotedStrings[0];
        else if (/for\s+(\w[\w\s]*?)(?:\.|$|,)/i.test(intent)) {
          const match = intent.match(/for\s+(\w[\w\s]*?)(?:\.|$|,)/i);
          if (match) params.searchTerm = match[1].trim();
        }
        if (numbers[0]) params.companyID = numbers[0];
        return {
          suggestedTool: 'autotask_search_tickets',
          suggestedParams: params,
          description: 'Search for tickets',
          requiredParams: [],
        };
      }
    
      // Quote operations (check before company — "quote for client" should match quote, not company)
      if (/\b(?:quotes?|proposals?|estimates?)\b/.test(intent)) {
        if (/\b(?:item|line|add.*to)\b/.test(intent)) {
          const params: Record<string, any> = {};
          if (numbers[0]) params.quoteId = numbers[0];
          return {
            suggestedTool: 'autotask_create_quote_item',
            suggestedParams: params,
            description: 'Add a line item to a quote',
            requiredParams: [...(!params.quoteId ? ['quoteId'] : []), 'name', 'quantity', 'unitPrice'],
          };
        }
        if (/\b(?:create|new|build)\b/.test(intent)) {
          const params: Record<string, any> = {};
          if (quotedStrings[0]) params.name = quotedStrings[0];
          return {
            suggestedTool: 'autotask_create_quote',
            suggestedParams: params,
            description: 'Create a new quote',
            requiredParams: [...(!params.name ? ['name'] : []), 'companyId'],
          };
        }
        const params: Record<string, any> = {};
        if (numbers[0]) params.quoteId = numbers[0];
        return {
          suggestedTool: numbers[0] ? 'autotask_get_quote' : 'autotask_search_quotes',
          suggestedParams: params,
          description: numbers[0] ? 'Get quote details' : 'Search for quotes',
          requiredParams: [],
        };
      }
    
      // Company operations
      if (/\b(?:company|companies|organization|client|account)\b/.test(intent)) {
        if (/\b(?:create|new|add)\b/.test(intent)) {
          const params: Record<string, any> = {};
          if (quotedStrings[0]) params.companyName = quotedStrings[0];
          return {
            suggestedTool: 'autotask_create_company',
            suggestedParams: params,
            description: 'Create a new company',
            requiredParams: !params.companyName ? ['companyName'] : [],
          };
        }
        if (/\b(?:update|edit|modify)\b/.test(intent)) {
          const params: Record<string, any> = {};
          if (numbers[0]) params.id = numbers[0];
          return {
            suggestedTool: 'autotask_update_company',
            suggestedParams: params,
            description: 'Update company details',
            requiredParams: !params.id ? ['id'] : [],
          };
        }
        const params: Record<string, any> = {};
        if (quotedStrings[0]) params.searchTerm = quotedStrings[0];
        return {
          suggestedTool: 'autotask_search_companies',
          suggestedParams: params,
          description: 'Search for companies',
          requiredParams: [],
        };
      }
    
      // Contact operations
      if (/\b(?:contacts?|person|people)\b/.test(intent)) {
        if (/\b(?:create|new|add)\b/.test(intent)) {
          return {
            suggestedTool: 'autotask_create_contact',
            suggestedParams: {},
            description: 'Create a new contact',
            requiredParams: ['firstName', 'lastName', 'companyID'],
          };
        }
        const params: Record<string, any> = {};
        if (quotedStrings[0]) params.searchTerm = quotedStrings[0];
        return {
          suggestedTool: 'autotask_search_contacts',
          suggestedParams: params,
          description: 'Search for contacts',
          requiredParams: [],
        };
      }
    
      // Project operations
      if (/\b(?:projects?)\b/.test(intent)) {
        if (/\b(?:create|new)\b/.test(intent)) {
          return {
            suggestedTool: 'autotask_create_project',
            suggestedParams: {},
            description: 'Create a new project',
            requiredParams: ['projectName', 'companyID'],
          };
        }
        const params: Record<string, any> = {};
        if (quotedStrings[0]) params.searchTerm = quotedStrings[0];
        return {
          suggestedTool: 'autotask_search_projects',
          suggestedParams: params,
          description: 'Search for projects',
          requiredParams: [],
        };
      }
    
      // Resource operations
      if (/\b(?:resource|technician|tech|engineer|staff)\b/.test(intent)) {
        const params: Record<string, any> = {};
        if (quotedStrings[0]) params.searchTerm = quotedStrings[0];
        return {
          suggestedTool: 'autotask_search_resources',
          suggestedParams: params,
          description: 'Search for resources/technicians',
          requiredParams: [],
        };
      }
    
      // Expense operations
      if (/\b(?:expense|receipt)\b/.test(intent)) {
        if (/\b(?:create|new|submit)\b/.test(intent)) {
          return {
            suggestedTool: 'autotask_create_expense_report',
            suggestedParams: {},
            description: 'Create an expense report',
            requiredParams: ['name', 'submitterId', 'weekEndingDate'],
          };
        }
        return {
          suggestedTool: 'autotask_search_expense_reports',
          suggestedParams: {},
          description: 'Search expense reports',
          requiredParams: [],
        };
      }
    
      // Configuration items / assets
      if (/\b(?:config|asset|device|hardware|ci)\b/.test(intent)) {
        const params: Record<string, any> = {};
        if (quotedStrings[0]) params.searchTerm = quotedStrings[0];
        return {
          suggestedTool: 'autotask_search_configuration_items',
          suggestedParams: params,
          description: 'Search configuration items/assets',
          requiredParams: [],
        };
      }
    
      // Product/service catalog
      if (/\b(?:product|service|bundle|catalog)\b/.test(intent)) {
        if (/\b(?:bundle)\b/.test(intent)) {
          return {
            suggestedTool: 'autotask_search_service_bundles',
            suggestedParams: {},
            description: 'Search service bundles',
            requiredParams: [],
          };
        }
        if (/\b(?:service)\b/.test(intent)) {
          return {
            suggestedTool: 'autotask_search_services',
            suggestedParams: {},
            description: 'Search services',
            requiredParams: [],
          };
        }
        return {
          suggestedTool: 'autotask_search_products',
          suggestedParams: {},
          description: 'Search products',
          requiredParams: [],
        };
      }
    
      // Charge operations
      if (/\b(?:charges?|material|cost)\b/.test(intent) && /\b(?:ticket|bill)\b/.test(intent)) {
        if (/\b(?:create|add|new)\b/.test(intent)) {
          const params: Record<string, any> = {};
          const ticketMatch = intent.match(/ticket\s*#?\s*(\d+)/i);
          if (ticketMatch) params.ticketID = parseInt(ticketMatch[1]);
          else if (numbers[0]) params.ticketID = numbers[0];
          return {
            suggestedTool: 'autotask_create_ticket_charge',
            suggestedParams: params,
            description: 'Create a ticket charge',
            requiredParams: [...(!params.ticketID ? ['ticketID'] : []), 'name', 'chargeType'],
          };
        }
        if (/\b(?:delete|remove)\b/.test(intent) && numbers[0]) {
          const deleteParams: Record<string, any> = { chargeId: numbers[0] };
          const ticketDeleteMatch = intent.match(/ticket\s*#?\s*(\d+)/i);
          if (ticketDeleteMatch) deleteParams.ticketId = parseInt(ticketDeleteMatch[1]);
          else if (numbers[1]) deleteParams.ticketId = numbers[1];
          return {
            suggestedTool: 'autotask_delete_ticket_charge',
            suggestedParams: deleteParams,
            description: 'Delete a ticket charge',
            requiredParams: [...(!deleteParams.ticketId ? ['ticketId'] : [])],
          };
        }
        const params: Record<string, any> = {};
        const ticketMatch = intent.match(/ticket\s*#?\s*(\d+)/i);
        if (ticketMatch) params.ticketId = parseInt(ticketMatch[1]);
        else if (numbers[0]) params.ticketId = numbers[0];
        return {
          suggestedTool: 'autotask_search_ticket_charges',
          suggestedParams: params,
          description: 'Search ticket charges',
          requiredParams: [],
        };
      }
    
      // Contract operations
      if (/\b(?:contract|agreement)\b/.test(intent)) {
        return {
          suggestedTool: 'autotask_search_contracts',
          suggestedParams: {},
          description: 'Search contracts',
          requiredParams: [],
        };
      }
    
      // Invoice operations
      if (/\b(?:invoice|bill|billing)\b/.test(intent)) {
        return {
          suggestedTool: 'autotask_search_invoices',
          suggestedParams: {},
          description: 'Search invoices',
          requiredParams: [],
        };
      }
    
      // Field info / picklist
      if (/\b(?:field|picklist|dropdown|options)\b/.test(intent)) {
        const entityMatch = intent.match(/(?:for|on|of)\s+(\w+)/i);
        return {
          suggestedTool: 'autotask_get_field_info',
          suggestedParams: entityMatch ? { entityType: entityMatch[1] } : {},
          description: 'Get field definitions and picklist values',
          requiredParams: entityMatch ? [] : ['entityType'],
        };
      }
    
      // Queue / status / priority lookups
      if (/\b(?:queue|status|statuses|priorit)\b/.test(intent)) {
        if (/\bqueue\b/.test(intent)) return { suggestedTool: 'autotask_list_queues', suggestedParams: {}, description: 'List ticket queues', requiredParams: [] };
        if (/\bstatus\b/.test(intent)) return { suggestedTool: 'autotask_list_ticket_statuses', suggestedParams: {}, description: 'List ticket statuses', requiredParams: [] };
        return { suggestedTool: 'autotask_list_ticket_priorities', suggestedParams: {}, description: 'List ticket priorities', requiredParams: [] };
      }
    
      // Connection test
      if (/\b(?:test|connect|connection|ping|health)\b/.test(intent)) {
        return {
          suggestedTool: 'autotask_test_connection',
          suggestedParams: {},
          description: 'Test API connection',
          requiredParams: [],
        };
      }
    
      // Fallback: suggest list_categories
      return {
        suggestedTool: 'autotask_list_categories',
        suggestedParams: {},
        description: 'Could not determine intent. Use autotask_list_categories to discover available tool categories.',
        requiredParams: [],
      };
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must fully disclose behavior. It states the tool will 'get the right tool suggestion with pre-filled parameters' but does not clarify whether the tool executes the suggested tool or just returns the suggestion. This ambiguity reduces transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences long, front-loading the purpose and immediately providing usage guidance. Every sentence is informative and there is no redundancy or wasted text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the large number of sibling tools and the absence of an output schema, the description should explain what the tool returns (e.g., a tool name and parameters) and any caveats. It fails to provide this, leaving the agent uncertain about the output format or next steps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% coverage for the single 'intent' parameter with a clear description. The tool description does not add significant new meaning beyond what the schema already provides, so a baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the tool as an 'intelligent tool router' that suggests the right Autotask tool with pre-filled parameters. This distinguishes it from its many sibling tools which are specific CRUD or search operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly instructs to use this tool when unsure which tool to call. While it doesn't mention when not to use it or alternatives, the guidance is clear and contextually appropriate for a meta-tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/asachs01/autotask-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server