Litelead-mcp

getOrders Tool

Overview

The getOrders tool retrieves order records from the Firebase/Firestore database with support for pagination, sorting, and automatic population of nested data (customer, contact, and staff references).

Location

tools/getOrders.js

Description

Retrieves orders for the authenticated account with cursor-based pagination support. This tool automatically fetches and embeds related customer, contact, technician, and responsible staff data. Orders are retrieved from the Firestore path: /Accounts/{accountId}/Orders.

Input Schema

{
  limit?: number;                    // Number of records to retrieve (1-100, default: 50)
  cursor?: string;                   // Document ID for cursor-based pagination
  orderBy?: string;                  // Field to order by (default: "orderId")
  orderDirection?: "asc" | "desc";   // Sort direction (default: "asc")
  cursorDirection?: "next" | "previous";  // Direction for pagination (default: "next")
  includeTotal?: boolean;            // Include total count of all orders (default: false)
}

Parameters

Parameter Type Required Default Description
limit number No 50 Maximum number of orders to return (1-100)
cursor string No - Document ID to start pagination from
orderBy string No “orderId” Field name to sort by
orderDirection enum No “asc” Sort direction: “asc” or “desc”
cursorDirection enum No “next” Pagination direction: “next” or “previous”
includeTotal boolean No false Whether to include total count (expensive)

Output Schema

{
  orders: Order[];
  pagination: PaginationMetadata;
}

Order Object

{
  id: string;                        // Firestore document ID
  path: string;                      // Full Firestore path
  bcc: string[];                     // BCC email addresses
  closed: boolean;                   // Whether order is closed
  pinned?: boolean;                  // Whether order is pinned
  type?: string;                     // Order type
  deadline?: string | Date;          // Order deadline (converted to locale string)
  location?: string;                 // Order location
  createdAt: string | Date;          // Creation timestamp (converted to locale string)
  createdBy: string;                 // Creator user ID
  lastCommentAt: string | Date | null; // Last comment timestamp
  orderId: number;                   // Unique order ID
  orderNumber: string;               // Order number
  ownedBy: string;                   // Owner user ID
  stage: number | null;              // Current stage number
  stages: Stage[];                   // Available stages
  ticketNumber: string;              // Ticket number
  title: string;                     // Order title
  users: User[];                     // Associated users
  customer: Customer | null;         // Embedded customer data
  contact: Contact | null;           // Embedded contact data
  technician_staff: Staff | null;    // Embedded technician data
  responsible_staff: Staff | null;   // Embedded responsible staff data
}

Nested Objects

Customer (Embedded)

{
  id: string;
  path: string;
  address: string;
  city: string;
  country: string;
  createdAt: string | Date;
  createdBy: string;
  customerId: number;
  metadata: Record<string, any>;
  name: string;
  ownedBy: string;
  zip: string;
}

Contact (Embedded)

{
  id: string;
  path: string;
  contactId: number;
  createdAt: string | Date;
  createdBy: string;
  customer: string;
  email: string;
  firstName: string;
  greeting: string;
  imported: boolean;
  lastName: string;
  mailchimp: { id: string | null };
  metadata: Record<string, any>;
  mobilePhone: string;
  opt_out_email?: boolean;
  opt_out_letter?: boolean;
  ownedBy: string;
  tags: any[];
  telePhone: string;
}

Staff (Embedded)

{
  id: string;
  path: string;
  name?: string;
  email?: string;
}

Stage

{
  color: string;
  label: Record<string, string>;  // Localized labels
  stage: number;
}

User

{
  email?: string;
  name?: string;
}

Pagination Metadata

{
  limit: number;                 // Applied limit
  hasMore: boolean;              // Whether more results exist
  nextCursor: string | null;     // Cursor for next page
  previousCursor: string | null; // Cursor for previous page
  total?: number | null;         // Total count (if includeTotal was true)
}

Usage Examples

Basic Usage

// Get first 10 orders with all nested data
const result = await getOrders(context, {
  limit: 10
});

console.log(result.orders);        // Array of 10 orders with nested data
console.log(result.orders[0].customer);  // Embedded customer object
console.log(result.orders[0].contact);   // Embedded contact object

Paginated Retrieval

// First page
const page1 = await getOrders(context, {
  limit: 20,
  orderBy: "orderId",
  orderDirection: "desc"  // Get newest orders first
});

// Next page
const page2 = await getOrders(context, {
  limit: 20,
  cursor: page1.pagination.nextCursor,
  cursorDirection: "next"
});

Filter by Stage

// Note: Filtering by stage requires custom implementation
// Current tool returns all orders; filtering can be done client-side
const result = await getOrders(context, { limit: 100 });
const openOrders = result.orders.filter(order => !order.closed);
const stage1Orders = openOrders.filter(order => order.stage === 1);

Get Open Orders

// Get orders and filter for open ones
const result = await getOrders(context, {
  limit: 50,
  orderBy: "lastCommentAt",
  orderDirection: "desc"
});

const openOrders = result.orders.filter(order => !order.closed);

Implementation Details

Data Population

The tool performs automatic data population for referenced documents:

  1. Customer: Fetched from the customer reference path
  2. Contact: Fetched from the contact reference path
  3. Technician Staff: Fetched from /Accounts/{accountId}/Staff/{technician_staff}
  4. Responsible Staff: Fetched from /Accounts/{accountId}/Staff/{responsible_staff}
const customer = await getDocument(db, orderData.customer);
const contact = await getDocument(db, orderData.contact);
const technician = await getDocumentById(db, `Accounts/${accountId}/Staff`, orderData.technician_staff);
const responsible = await getDocumentById(db, `Accounts/${accountId}/Staff`, orderData.responsible_staff);

Two-Phase Transform

The tool uses a two-phase approach to handle async transforms:

  1. Phase 1: Fetch paginated order documents
  2. Phase 2: Transform each order by fetching nested documents in parallel
// Phase 1: Get paginated results
const result = await executePaginatedQuery(ordersRef, options, (doc) => ({ doc }));

// Phase 2: Transform with nested data
const orders = await Promise.all(
  result.items.map((item) => transformOrder(item.doc))
);

Date Transformations

Null Handling

If any referenced document (customer, contact, staff) is not found or fails to load, it returns null instead of throwing an error. This ensures orders can still be retrieved even if references are broken.

Error Handling

Common Errors

Error Cause Solution
“Error fetching orders: [message]” Firestore query failed Check Firebase connection and permissions
“Error fetching document at path [path]: [error]” Referenced document not found Document may have been deleted; returns null
Validation error Invalid input parameters Ensure parameters match the input schema

Error Response

Main query errors are logged and re-thrown:

catch (error) {
  console.error("Error fetching orders:", error);
  throw new Error(error.message);
}

Nested document errors are logged but return null:

catch (error) {
  console.error(`Error fetching document at path ${path}:`, error);
  return null;
}

Performance Considerations

  1. Nested Queries: Each order triggers up to 4 additional document fetches
    • Consider caching frequently accessed customers/contacts
    • Use appropriate limit sizes to balance data completeness vs. performance
  2. Parallel Fetching: Nested documents are fetched in parallel using Promise.all
  3. Limit Size: Recommended limit is 10-50 for optimal performance due to nested fetches
  4. Total Count: Setting includeTotal: true adds additional overhead
  5. Firestore Reads: Each order consumes 1-5 Firestore reads (order + nested docs)

Firestore Collection Structure

/Accounts/{accountId}/Orders/{orderId}
  - bcc: string[]
  - closed: boolean
  - pinned: boolean
  - type: string
  - deadline: timestamp
  - location: string
  - createdAt: timestamp
  - createdBy: string
  - lastCommentAt: timestamp
  - orderId: number
  - orderNumber: string
  - ownedBy: string
  - stage: number
  - stages: array of stage objects
  - ticketNumber: string
  - title: string
  - users: array of user objects
  - customer: string (reference path)
  - contact: string (reference path)
  - technician_staff: string (document ID)
  - responsible_staff: string (document ID)

See Also