halopsa_query
Execute SQL queries to retrieve data from HaloPSA tables including tickets, users, sites, actions, and request types through the reporting API.
Instructions
Execute a SQL query against HaloPSA reporting API. Use this to retrieve data from any HaloPSA table including tickets (FAULTS), users (USERS), sites (SITE), actions (ACTIONS), request types (REQUESTTYPE), and more. Returns the full report response with data rows, column metadata, and available filters.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sql | Yes | SQL query to execute against HaloPSA database. Supports standard SQL syntax including SELECT, JOIN, WHERE, ORDER BY, GROUP BY, etc. Example: SELECT * FROM FAULTS WHERE Status = 1 | |
| loadReportOnly | No | Whether to load report data only (default: true) |
Implementation Reference
- src/halopsa-client.ts:76-112 (handler)Core implementation of halopsa_query tool: executes SQL query via HaloPSA /api/Report endpoint. Handles authentication, constructs request payload with _loadreportonly=true and sql, sends POST request, and returns full report response.async executeQuery(sql: string): Promise<any> { // Ensure we have a valid token await this.authenticate(); const reportUrl = `${this.config.url}/api/Report`; const queryUrl = `${reportUrl}?tenant=${this.config.tenant}`; const query: ReportQuery = { _loadreportonly: true, sql: sql }; try { const response = await fetch(queryUrl, { method: 'POST', headers: { 'accept': '*/*', 'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8', 'authorization': `Bearer ${this.accessToken}`, 'Content-Type': 'application/json' }, body: JSON.stringify([query]) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Query execution failed: ${response.status} - ${errorText}`); } const result = await response.json(); // HaloPSA returns a single object for reporting API, not an array return result; } catch (error) { throw new Error(`Failed to execute query: ${error}`); } }
- src/index.ts:387-401 (handler)MCP tool handler dispatcher for halopsa_query: validates sql argument, delegates to HaloPSAClient.executeQuery(), formats result as MCP text content response.case 'halopsa_query': { const { sql } = args as any; if (!sql) { throw new Error('SQL query is required'); } result = await haloPSAClient.executeQuery(sql); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }
- src/index.ts:74-92 (schema)Tool registration object defining name 'halopsa_query', description, and inputSchema requiring 'sql' string parameter with optional loadReportOnly boolean.{ name: 'halopsa_query', description: 'Execute a SQL query against HaloPSA reporting API. Use this to retrieve data from any HaloPSA table including tickets (FAULTS), users (USERS), sites (SITE), actions (ACTIONS), request types (REQUESTTYPE), and more. Returns the full report response with data rows, column metadata, and available filters.', inputSchema: { type: 'object', properties: { sql: { type: 'string', description: 'SQL query to execute against HaloPSA database. Supports standard SQL syntax including SELECT, JOIN, WHERE, ORDER BY, GROUP BY, etc. Example: SELECT * FROM FAULTS WHERE Status = 1' }, loadReportOnly: { type: 'boolean', description: 'Whether to load report data only (default: true)', default: true } }, required: ['sql'] } },
- src/index.ts:279-281 (registration)MCP server registration for ListToolsRequestSchema which returns the tools array containing halopsa_query definition.server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools }; });
- src/halopsa-client.ts:31-71 (helper)Authentication helper used by executeQuery: obtains and refreshes Bearer token via /auth/token endpoint using client credentials.private async authenticate(): Promise<void> { // Check if we have a valid token if (this.accessToken && this.tokenExpiry && this.tokenExpiry > new Date()) { return; } const tokenUrl = `${this.config.url}/auth/token`; const params = new URLSearchParams({ grant_type: 'client_credentials', client_id: this.config.clientId, client_secret: this.config.clientSecret, scope: 'all' }); try { const response = await fetch(tokenUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' }, body: params.toString() }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Authentication failed: ${response.status} - ${errorText}`); } const tokenData: TokenResponse = await response.json(); this.accessToken = tokenData.access_token; // Set token expiry (subtract 60 seconds for safety) const expiryMs = (tokenData.expires_in - 60) * 1000; this.tokenExpiry = new Date(Date.now() + expiryMs); } catch (error) { throw new Error(`Failed to authenticate with HaloPSA: ${error}`); } }