Files
grist-accounting/SKILL.md
Bill aa528d2843 Extract upload-attachment script to separate file
- Move embedded bash script to scripts/upload-attachment.sh
- Update SKILL.md to reference external script with usage examples
- Follows skill best practice: separate reusable tools from documentation
2026-01-03 20:36:23 -05:00

13 KiB

name, description
name description
grist-accounting Use when working with the Grist double-entry accounting system - recording transactions, entering bills, tracking vendors, querying account balances, or generating financial reports

Grist Double-Entry Accounting System

Overview

A complete double-entry accounting system for sole proprietorship service businesses. Every transaction creates balanced journal entries (debits = credits). Account balances roll up through parent-child hierarchy.

MCP Tools Available

Tool Purpose
mcp__grist-accounting__list_documents List accessible Grist documents
mcp__grist-accounting__list_tables List tables in a document
mcp__grist-accounting__describe_table Get column schema for a table
mcp__grist-accounting__get_records Fetch records (with optional filter, sort, limit)
mcp__grist-accounting__add_records Insert new records, returns {"inserted_ids": [...]}
mcp__grist-accounting__update_records Update existing records by ID
mcp__grist-accounting__delete_records Delete records by ID
mcp__grist-accounting__sql_query Run read-only SQL queries

The document name is accounting for all operations.

Date Handling

All date fields use Unix timestamps (seconds since 1970-01-01 UTC).

Date Timestamp
Oct 1, 2025 1759363200
Nov 1, 2025 1762041600
Dec 1, 2025 1764633600
Jan 1, 2026 1767312000

Python: int(datetime(2025, 10, 1).timestamp())

Complete Table Schemas

Accounts

Column Type Notes
Code Text Account number (e.g., "5080")
Name Text Account name
Type Choice "Asset", "Liability", "Equity", "Income", "Expense"
Parent Ref:Accounts Parent account for hierarchy (0 = top-level)
Description Text
IsActive Bool
Balance Formula Calculated from transactions

Vendors

Column Type Notes
Name Text Vendor name
DefaultExpenseAccount Ref:Accounts Auto-fills on bill lines
PaymentTerms Choice "Due on Receipt", "Net 15", "Net 30"
Notes Text
IsActive Bool
Balance Formula Sum of unpaid bills

Items

Column Type Notes
Name Text Item name (e.g., "Software Subscription")
DefaultAccount Ref:Accounts Expense account for this item
DefaultDescription Text Auto-fills on bill lines
IsActive Bool

Bills

Column Type Notes
Vendor Ref:Vendors Required
BillNumber Text Invoice number from vendor
BillDate Date Unix timestamp
DueDate Date Unix timestamp
Status Choice "Open", "Partial", "Paid"
Memo Text
EntryTransaction Ref:Transactions Link to journal entry
Attachment Attachments Invoice/receipt files (use ["L", id] format)
Amount Formula Sum of BillLines.Amount
AmountPaid Formula Sum of BillPayments.Amount
AmountDue Formula Amount - AmountPaid

BillLines

Column Type Notes
Bill Ref:Bills Required
Item Ref:Items Optional - auto-fills Account/Description
Account Ref:Accounts Expense account
Description Text
Amount Numeric Line item amount

Transactions

Column Type Notes
Date Date Unix timestamp
Description Text Transaction description
Reference Text Check number, invoice reference, etc.
Status Choice "Draft", "Posted", "Cleared"
Memo Text
Total Formula Sum of debits
IsBalanced Formula True if debits = credits

TransactionLines

Column Type Notes
Transaction Ref:Transactions Required
Account Ref:Accounts Required
Debit Numeric Debit amount (or 0)
Credit Numeric Credit amount (or 0)
Memo Text

BillPayments

Column Type Notes
Bill Ref:Bills Required
Transaction Ref:Transactions Payment journal entry
Amount Numeric Payment amount
PaymentDate Date Unix timestamp

Key Account IDs

ID Code Name Type
4 2000 Accounts Payable Liability
14 1001 Checking Account Asset
22 2203 Due to Owner Liability
36 5080 Software & Subscriptions Expense

Query all accounts:

SELECT id, Code, Name, Type FROM Accounts WHERE IsActive = true ORDER BY Code

Account Types

Type Normal Balance Increases With Examples
Asset Debit Debit Cash, AR, Prepaid
Liability Credit Credit AP, Credit Cards, Due to Owner
Equity Credit Credit Owner's Investment, Draws, Retained Earnings
Income Credit Credit Service Revenue, Interest Income
Expense Debit Debit Rent, Utilities, Office Supplies

Complete Workflows

Create a Vendor

add_records("Vendors", [{
    "Name": "Acme Corp",
    "DefaultExpenseAccount": 36,  # Software & Subscriptions
    "PaymentTerms": "Due on Receipt",
    "Notes": "Software vendor",
    "IsActive": True
}])
# Returns: {"inserted_ids": [vendor_id]}

Create Items for Common Purchases

add_records("Items", [{
    "Name": "Monthly Software",
    "DefaultAccount": 36,
    "DefaultDescription": "Monthly SaaS subscription",
    "IsActive": True
}])

Complete Bill Entry (4 Steps)

Step 1: Create Bill Header

add_records("Bills", [{
    "Vendor": 1,  # vendor_id
    "BillNumber": "INV-001",
    "BillDate": 1759708800,  # Unix timestamp
    "DueDate": 1759708800,
    "Status": "Open",
    "Memo": "October services"
}])
# Returns: {"inserted_ids": [bill_id]}

Step 2: Create Bill Line(s)

add_records("BillLines", [{
    "Bill": 1,  # bill_id from step 1
    "Item": 1,  # optional - auto-fills Account/Description
    "Account": 36,  # expense account
    "Description": "Monthly subscription",
    "Amount": 100.00
}])

Step 3: Create Journal Entry

# Transaction header
add_records("Transactions", [{
    "Date": 1759708800,
    "Description": "Acme Corp - October services",
    "Reference": "INV-001",
    "Status": "Posted"
}])
# Returns: {"inserted_ids": [txn_id]}

# Transaction lines: Debit expense, Credit AP
add_records("TransactionLines", [
    {"Transaction": 1, "Account": 36, "Debit": 100.00, "Credit": 0, "Memo": "Monthly subscription"},
    {"Transaction": 1, "Account": 4, "Debit": 0, "Credit": 100.00, "Memo": "Monthly subscription"}
])

Step 4: Link Bill to Transaction

update_records("Bills", [{"id": 1, "fields": {"EntryTransaction": 1}}])

Pay Bill from Checking Account

# Step 1: Create payment transaction
add_records("Transactions", [{
    "Date": 1760832000,
    "Description": "Payment - Acme Corp INV-001",
    "Reference": "Check #1001",
    "Status": "Cleared"
}])
# Returns: {"inserted_ids": [txn_id]}

# Step 2: Debit AP, Credit Checking
add_records("TransactionLines", [
    {"Transaction": 2, "Account": 4, "Debit": 100.00, "Credit": 0, "Memo": "Pay INV-001"},
    {"Transaction": 2, "Account": 14, "Debit": 0, "Credit": 100.00, "Memo": "Pay INV-001"}
])

# Step 3: Create BillPayment record
add_records("BillPayments", [{
    "Bill": 1,
    "Transaction": 2,
    "Amount": 100.00,
    "PaymentDate": 1760832000
}])

# Step 4: Update bill status
update_records("Bills", [{"id": 1, "fields": {"Status": "Paid"}}])

Pay Bill via Owner Reimbursement

When the owner pays a business expense personally:

# Step 1: Create payment transaction
add_records("Transactions", [{
    "Date": 1760832000,
    "Description": "Owner payment - Acme Corp INV-001",
    "Reference": "Owner Reimb",
    "Status": "Posted"
}])

# Step 2: Debit AP, Credit Due to Owner (not Checking)
add_records("TransactionLines", [
    {"Transaction": 2, "Account": 4, "Debit": 100.00, "Credit": 0, "Memo": "Pay INV-001"},
    {"Transaction": 2, "Account": 22, "Debit": 0, "Credit": 100.00, "Memo": "Owner paid"}
])

# Step 3: Create BillPayment record
add_records("BillPayments", [{
    "Bill": 1,
    "Transaction": 2,
    "Amount": 100.00,
    "PaymentDate": 1760832000
}])

# Step 4: Update bill status
update_records("Bills", [{"id": 1, "fields": {"Status": "Paid"}}])

Reimburse Owner

When business pays back the owner:

add_records("Transactions", [{
    "Date": 1762041600,
    "Description": "Owner reimbursement",
    "Reference": "Transfer",
    "Status": "Cleared"
}])

add_records("TransactionLines", [
    {"Transaction": 3, "Account": 22, "Debit": 500.00, "Credit": 0, "Memo": "Reimburse owner"},
    {"Transaction": 3, "Account": 14, "Debit": 0, "Credit": 500.00, "Memo": "Reimburse owner"}
])

Batch Operations

When entering multiple bills efficiently:

  1. Create all Bills first → collect inserted IDs
  2. Create all BillLines referencing bill IDs
  3. Create all Transactions → collect inserted IDs
  4. Create all TransactionLines referencing transaction IDs
  5. Update all Bills with EntryTransaction links in one call
  6. (If paying) Create payment transactions, lines, and BillPayments

Example batch update:

update_records("Bills", [
    {"id": 1, "fields": {"EntryTransaction": 1}},
    {"id": 2, "fields": {"EntryTransaction": 2}},
    {"id": 3, "fields": {"EntryTransaction": 3}}
])

Common Queries

Unpaid Bills by Vendor

SELECT v.Name, b.BillNumber, b.BillDate, b.Amount, b.AmountDue
FROM Bills b
JOIN Vendors v ON b.Vendor = v.id
WHERE b.Status IN ('Open', 'Partial')
ORDER BY b.DueDate

Bills Summary by Vendor

SELECT v.Name as Vendor, COUNT(b.id) as Bills, SUM(b.Amount) as Total, SUM(b.AmountDue) as Due
FROM Bills b
JOIN Vendors v ON b.Vendor = v.id
GROUP BY v.Name
ORDER BY Total DESC

Account Balances (Non-Zero)

SELECT Code, Name, Type, Balance
FROM Accounts
WHERE Balance != 0
ORDER BY Code

Owner Reimbursement Balance

SELECT Balance FROM Accounts WHERE Code = '2203'

Expense Summary by Account

SELECT a.Code, a.Name, a.Balance
FROM Accounts a
WHERE a.Type = 'Expense' AND a.Balance != 0
ORDER BY a.Balance DESC

Transaction History for Account

SELECT t.Date, t.Description, t.Reference, tl.Debit, tl.Credit
FROM TransactionLines tl
JOIN Transactions t ON tl.Transaction = t.id
WHERE tl.Account = 36
ORDER BY t.Date DESC

Verify All Transactions Balance

SELECT id, Description, Total, IsBalanced
FROM Transactions
WHERE IsBalanced = false

Validation Checklist

After entering bills, verify:

  • Total bills match expected: SELECT SUM(Amount) FROM Bills
  • All transactions balanced: SELECT * FROM Transactions WHERE IsBalanced = false
  • AP balance correct: SELECT Balance FROM Accounts WHERE Code = '2000'
  • Expense accounts increased appropriately
  • Vendor balances reflect unpaid bills

Common Mistakes

Mistake Fix
Transaction not balanced Ensure SUM(Debit) = SUM(Credit) before saving
Wrong debit/credit direction Assets/Expenses increase with debit; Liabilities/Equity/Income increase with credit
Posting to parent account Post to leaf accounts (1001 Checking, not 1000 Cash)
Forgetting AP entry for bills Bills need both the expense debit AND the AP credit
Missing EntryTransaction link Always update Bill.EntryTransaction after creating journal entry
Bill status not updated Manually set Status to "Paid" after full payment
Using string dates Dates must be Unix timestamps (seconds), not strings

Uploading Attachments

Attachments (invoices, receipts) are uploaded via the HTTP proxy endpoint, not MCP tools. This is efficient for binary files.

Workflow

  1. Request session token with write permission via MCP
  2. Upload file via POST /api/v1/attachments with multipart/form-data
  3. Link attachment to record via update_records

Upload Script

Use scripts/upload-attachment.sh in this skill directory:

# Get session token first (via MCP request_session_token tool)
# Then run:
./scripts/upload-attachment.sh invoice.pdf Bills 13

# Or pass token directly:
./scripts/upload-attachment.sh invoice.pdf Bills 13 sess_abc123...

# Environment variable for custom endpoint:
GRIST_MCP_URL=https://custom.example.com ./scripts/upload-attachment.sh ...

Run ./scripts/upload-attachment.sh without arguments for full usage.

Linking Attachments Manually

Grist attachment columns use format: ["L", attachment_id]

update_records("Bills", [{"id": 13, "fields": {"Attachment": ["L", 1]}}])

Formula Columns (Auto-Calculated)

Table.Column Description
Accounts.Balance OwnBalance + ChildrenBalance
Transactions.IsBalanced True if sum of debits = sum of credits
Transactions.Total Sum of debit amounts
Bills.Amount Sum of BillLines.Amount
Bills.AmountPaid Sum of BillPayments.Amount
Bills.AmountDue Amount - AmountPaid
Vendors.Balance Sum of AmountDue for unpaid bills