AroFlo: Get CustomHolders
aroflo_get_customholdersRetrieve custom holder data from AroFlo with flexible querying options including WHERE clauses, ordering, joins, and response modes to manage project information.
Instructions
Query the AroFlo CustomHolders zone (GET). Use pipe-delimited WHERE clauses like "and|field|=|value", ORDER clauses like "field|asc", and JOIN areas like "lineitems". where/order/join accept either a single string or an array. mode: data|verbose|debug|raw (default: data). Set compact=true and optionally select=["field","nested.field"] to reduce payload size. See resource "aroflo://docs/api/" (example: "aroflo://docs/api/quotes") for valid fields/values.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| where | No | ||
| order | No | ||
| join | No | ||
| page | No | ||
| pageSize | No | ||
| autoPaginate | No | ||
| maxPages | No | ||
| maxResults | No | ||
| maxItemsTotal | No | ||
| validateWhere | No | ||
| mode | No | ||
| verbose | No | ||
| debug | No | ||
| raw | No | ||
| compact | No | ||
| select | No | ||
| maxItems | No | ||
| extra | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/mcp/tools/get-zones.ts:56-271 (handler)The tool 'aroflo_get_customholders' is dynamically registered within the registerZoneGetTools function in src/mcp/tools/get-zones.ts. It iterates over the AROFLO_ZONES array (which includes 'CustomHolders') and uses 'zoneToToolSuffix' to generate the tool name. The handler logic for all zone-based GET tools is also contained in this function, utilizing the AroFloClient to fetch data.
server.registerTool( toolName, { title: `AroFlo: Get ${zone}`, description: `Query the AroFlo ${zone} zone (GET). ` + `Use pipe-delimited WHERE clauses like "and|field|=|value", ORDER clauses like "field|asc", and JOIN areas like "lineitems". ` + `where/order/join accept either a single string or an array. ` + `mode: data|verbose|debug|raw (default: data). ` + `Set compact=true and optionally select=[\"field\",\"nested.field\"] to reduce payload size. ` + `See resource "aroflo://docs/api/<slug>" (example: "aroflo://docs/api/quotes") for valid fields/values.`, inputSchema, // MCP SDK expects output schemas to be object schemas (or raw object shapes). // `z.any()` causes output validation to crash under the current SDK. outputSchema: z.object({}).passthrough(), annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true } }, async (args) => { const mode = resolveOutputMode(args); const envelopeRequested = typeof args.mode === 'string' || Boolean(args.raw) || Boolean(args.verbose); try { const where = normalizeWhereParam(args.where); const order = normalizeOrderParam(args.order); const join = normalizeJoinParam(args.join); if (args.validateWhere !== false) { await validateWhereOrThrow({ zone, where }); } const autoPaginate = Boolean(args.autoPaginate); const startPage = args.page ?? 1; const pageSize = args.pageSize ?? (autoPaginate ? 200 : undefined); const maxResultsRaw = typeof args.maxItemsTotal === 'number' ? args.maxItemsTotal : args.maxResults; const maxResults = typeof args.maxResults === 'number' && typeof args.maxItemsTotal === 'number' ? Math.min(args.maxResults, args.maxItemsTotal) : maxResultsRaw; const maxPages = args.maxPages ?? (autoPaginate ? 25 : undefined); const debugInfo: Record<string, unknown> | undefined = args.debug ? { zone, normalized: { where, order, join, page: startPage, pageSize, extra: args.extra } } : undefined; let response = await client.get(zone, { where, order, join, page: startPage, pageSize, extra: args.extra }); let pagesFetched = 1; let truncated = false; let truncatedReason: string | undefined; let nextPage: number | undefined; if (autoPaginate) { let currentPage = startPage; let lastPageCount = extractZoneItems(zone, response.data).items.length; while (true) { const total = extractZoneItems(zone, response.data).items.length; if (typeof maxResults === 'number' && total >= maxResults) { truncated = true; truncatedReason = 'maxResults'; nextPage = currentPage + 1; break; } if (typeof maxPages === 'number' && pagesFetched >= maxPages) { truncated = true; truncatedReason = 'maxPages'; nextPage = currentPage + 1; break; } if (typeof pageSize === 'number' && lastPageCount < pageSize) { break; } currentPage += 1; const next = await client.get(zone, { where, order, join, page: currentPage, pageSize, extra: args.extra }); // If the next page contributes nothing, stop. const nextCount = extractZoneItems(zone, next.data).items.length; if (nextCount === 0) { break; } response = { ...response, data: mergeZoneResponseData(response.data, next.data).merged }; pagesFetched += 1; lastPageCount = nextCount; // Stop if the last page was short. if (typeof pageSize === 'number' && nextCount < pageSize) { break; } } } if (typeof maxResults === 'number') { const total = extractZoneItems(zone, response.data).items.length; if (total > maxResults) { const { truncated: newData } = truncateZoneArrays(response.data, maxResults); response = { ...response, data: newData }; truncated = true; truncatedReason = truncatedReason ?? 'maxResults'; } } let compactApplied = false; let effectiveResponse = response; const defaultSelect = zone === 'Tasks' && args.compact && (!args.select || args.select.length === 0) ? [ 'taskid', 'jobnumber', 'status', 'taskname', 'daterequested', 'createdutc', 'clientid', 'org.orgid', 'org.orgname', 'projectid', 'stageid', 'project.projectid', 'project.projectname', 'tasktotals.totalhrs' ] : undefined; const select = args.select ?? defaultSelect; if (args.compact || (select && select.length > 0) || args.maxItems) { compactApplied = true; const compactedData = compactZoneResponseData(response.data, { select, maxItems: args.maxItems }); effectiveResponse = { ...response, data: compactedData }; } // Backward compatible default: return the full AroFlo client response, optionally // annotated with pagination/debug metadata. The new minimal envelope is opt-in via // args.mode / args.verbose / args.raw. if (!envelopeRequested) { let finalData: unknown = effectiveResponse.data; if (autoPaginate || truncated) { finalData = withZoneResponseMeta(finalData, { pagesFetched, truncated, truncatedReason, nextPage }); } if (debugInfo) { finalData = withDebug(finalData, debugInfo); } return successToolResult({ ...effectiveResponse, data: finalData }); } const out = buildZoneDataEnvelope({ zone, response: effectiveResponse, page: startPage, pageSize, mode, mcp: autoPaginate || truncated ? { autoPaginate, pagesFetched, truncated, truncatedReason, nextPage } : undefined, debug: debugInfo, compactApplied, select, maxItems: args.maxItems }); return successToolResult(out); } catch (error) { return errorToolResult(error, { mode, debug: { zone } }); } } );