@nightsquawktech/appfolio-mcp-server 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMMERCIAL.md +35 -0
- package/LICENSE +661 -0
- package/README.md +127 -0
- package/dist/catalog/index.json +1511 -0
- package/dist/catalog/report-spec.js +27 -0
- package/dist/catalog/reports/account_totals.json +112 -0
- package/dist/catalog/reports/additional_fees.json +73 -0
- package/dist/catalog/reports/aged_payables_summary.json +124 -0
- package/dist/catalog/reports/aged_receivables_detail.json +132 -0
- package/dist/catalog/reports/amenities_by_property.json +73 -0
- package/dist/catalog/reports/annual_budget_comparative.json +138 -0
- package/dist/catalog/reports/annual_budget_forecast.json +125 -0
- package/dist/catalog/reports/appfolio_stack_usage.json +112 -0
- package/dist/catalog/reports/association_work_order.json +232 -0
- package/dist/catalog/reports/balance_sheet.json +124 -0
- package/dist/catalog/reports/balance_sheet_comparative.json +126 -0
- package/dist/catalog/reports/balance_sheet_comparison.json +113 -0
- package/dist/catalog/reports/bank_account_association.json +94 -0
- package/dist/catalog/reports/bill_detail.json +183 -0
- package/dist/catalog/reports/board_of_directors.json +87 -0
- package/dist/catalog/reports/budget_comparative.json +162 -0
- package/dist/catalog/reports/budget_comparison.json +141 -0
- package/dist/catalog/reports/cancelled_processes.json +122 -0
- package/dist/catalog/reports/cash_flow.json +158 -0
- package/dist/catalog/reports/cash_flow_comparison.json +158 -0
- package/dist/catalog/reports/cash_flow_detail.json +129 -0
- package/dist/catalog/reports/charge_detail.json +198 -0
- package/dist/catalog/reports/chart_of_accounts.json +22 -0
- package/dist/catalog/reports/check_register.json +181 -0
- package/dist/catalog/reports/check_register_detail.json +181 -0
- package/dist/catalog/reports/completed_processes.json +117 -0
- package/dist/catalog/reports/delinquency.json +164 -0
- package/dist/catalog/reports/delinquency_as_of.json +155 -0
- package/dist/catalog/reports/deposit_register.json +51 -0
- package/dist/catalog/reports/dues_roll.json +89 -0
- package/dist/catalog/reports/dues_roll_itemized.json +113 -0
- package/dist/catalog/reports/eligible_debt_summary.json +73 -0
- package/dist/catalog/reports/email_delivery_errors.json +104 -0
- package/dist/catalog/reports/expense_distribution.json +109 -0
- package/dist/catalog/reports/expense_register.json +149 -0
- package/dist/catalog/reports/fixed_assets.json +111 -0
- package/dist/catalog/reports/flows_assigned_tasks.json +103 -0
- package/dist/catalog/reports/fund_balance_sheet.json +113 -0
- package/dist/catalog/reports/fund_balance_sheet_active_funds.json +113 -0
- package/dist/catalog/reports/fund_income_statement.json +132 -0
- package/dist/catalog/reports/general_ledger.json +166 -0
- package/dist/catalog/reports/gross_potential_rent_enhanced.json +102 -0
- package/dist/catalog/reports/guest_card_inquiries.json +177 -0
- package/dist/catalog/reports/guest_cards.json +161 -0
- package/dist/catalog/reports/historical_advertised_rent.json +92 -0
- package/dist/catalog/reports/homeowner_delinquency.json +143 -0
- package/dist/catalog/reports/homeowner_delinquency_as_of.json +161 -0
- package/dist/catalog/reports/homeowner_directory.json +126 -0
- package/dist/catalog/reports/homeowner_ledger.json +59 -0
- package/dist/catalog/reports/homeowner_prepayment_balance.json +127 -0
- package/dist/catalog/reports/homeowner_resale.json +107 -0
- package/dist/catalog/reports/homeowner_vehicle_info.json +104 -0
- package/dist/catalog/reports/import_variances.json +41 -0
- package/dist/catalog/reports/in_progress_workflows.json +181 -0
- package/dist/catalog/reports/inactive_guest_cards.json +134 -0
- package/dist/catalog/reports/income_register.json +131 -0
- package/dist/catalog/reports/income_statement.json +130 -0
- package/dist/catalog/reports/income_statement_comparative.json +140 -0
- package/dist/catalog/reports/income_statement_comparison.json +135 -0
- package/dist/catalog/reports/income_statement_date_range.json +134 -0
- package/dist/catalog/reports/inspection_detail.json +107 -0
- package/dist/catalog/reports/insurance_enforcement.json +22 -0
- package/dist/catalog/reports/insurance_usage.json +92 -0
- package/dist/catalog/reports/inventory_status.json +73 -0
- package/dist/catalog/reports/inventory_usage.json +131 -0
- package/dist/catalog/reports/keys_detail.json +131 -0
- package/dist/catalog/reports/late_fee_policy_comparison.json +135 -0
- package/dist/catalog/reports/lease_expiration_detail.json +147 -0
- package/dist/catalog/reports/lease_expiration_summary.json +95 -0
- package/dist/catalog/reports/lease_history.json +147 -0
- package/dist/catalog/reports/leasing_agent_performance.json +54 -0
- package/dist/catalog/reports/leasing_funnel_performance.json +113 -0
- package/dist/catalog/reports/leasing_summary.json +92 -0
- package/dist/catalog/reports/loans.json +95 -0
- package/dist/catalog/reports/occupancy_custom_fields.json +73 -0
- package/dist/catalog/reports/occupancy_summary.json +95 -0
- package/dist/catalog/reports/owner1099.json +135 -0
- package/dist/catalog/reports/owner1099_detail.json +156 -0
- package/dist/catalog/reports/owner_custom_fields.json +73 -0
- package/dist/catalog/reports/owner_directory.json +101 -0
- package/dist/catalog/reports/owner_leasing.json +115 -0
- package/dist/catalog/reports/owner_withholdings.json +170 -0
- package/dist/catalog/reports/payment_plans.json +128 -0
- package/dist/catalog/reports/premium_leads_billing_detail.json +70 -0
- package/dist/catalog/reports/project_budget_detail.json +56 -0
- package/dist/catalog/reports/project_directory.json +144 -0
- package/dist/catalog/reports/property_budget.json +83 -0
- package/dist/catalog/reports/property_custom_fields.json +73 -0
- package/dist/catalog/reports/property_directory.json +73 -0
- package/dist/catalog/reports/property_group_directory.json +85 -0
- package/dist/catalog/reports/property_performance.json +115 -0
- package/dist/catalog/reports/property_staff_assignments.json +73 -0
- package/dist/catalog/reports/prospect_source_tracking.json +92 -0
- package/dist/catalog/reports/purchase_order.json +163 -0
- package/dist/catalog/reports/receivables_activity.json +138 -0
- package/dist/catalog/reports/renewal_summary.json +134 -0
- package/dist/catalog/reports/rent_roll.json +114 -0
- package/dist/catalog/reports/rent_roll_commercial.json +106 -0
- package/dist/catalog/reports/rent_roll_itemized.json +125 -0
- package/dist/catalog/reports/rentable_items.json +97 -0
- package/dist/catalog/reports/rental_applications.json +118 -0
- package/dist/catalog/reports/renter_directory.json +60 -0
- package/dist/catalog/reports/resident_financial_activity.json +107 -0
- package/dist/catalog/reports/screening_assessments.json +104 -0
- package/dist/catalog/reports/screening_usage.json +92 -0
- package/dist/catalog/reports/security_deposit_funds_detail.json +141 -0
- package/dist/catalog/reports/showings.json +123 -0
- package/dist/catalog/reports/student_housing_rent_roll_by_bed.json +125 -0
- package/dist/catalog/reports/surveys_summary.json +118 -0
- package/dist/catalog/reports/tenant_debt_collections_status.json +90 -0
- package/dist/catalog/reports/tenant_directory.json +120 -0
- package/dist/catalog/reports/tenant_ledger.json +72 -0
- package/dist/catalog/reports/tenant_tickler.json +102 -0
- package/dist/catalog/reports/tenant_transactions_summary.json +97 -0
- package/dist/catalog/reports/tenant_unpaid_charges_summary.json +147 -0
- package/dist/catalog/reports/tenant_vehicle_info.json +107 -0
- package/dist/catalog/reports/trial_balance.json +104 -0
- package/dist/catalog/reports/trial_balance_by_property.json +104 -0
- package/dist/catalog/reports/trust_account_balance.json +116 -0
- package/dist/catalog/reports/trust_account_balance_detail.json +112 -0
- package/dist/catalog/reports/twelve_month_cash_flow.json +157 -0
- package/dist/catalog/reports/twelve_month_income_statement.json +147 -0
- package/dist/catalog/reports/unit_custom_fields.json +73 -0
- package/dist/catalog/reports/unit_directory.json +80 -0
- package/dist/catalog/reports/unit_inspection.json +95 -0
- package/dist/catalog/reports/unit_turn_detail.json +104 -0
- package/dist/catalog/reports/unit_vacancy.json +143 -0
- package/dist/catalog/reports/unpaid_balances_by_month.json +181 -0
- package/dist/catalog/reports/upcoming_activities.json +142 -0
- package/dist/catalog/reports/vendor1099.json +84 -0
- package/dist/catalog/reports/vendor_custom_fields.json +22 -0
- package/dist/catalog/reports/vendor_directory.json +134 -0
- package/dist/catalog/reports/vendor_ledger.json +122 -0
- package/dist/catalog/reports/vendor_ledger_enhanced.json +122 -0
- package/dist/catalog/reports/violation_detail.json +122 -0
- package/dist/catalog/reports/work_order.json +254 -0
- package/dist/catalog/reports/work_order_labor_summary.json +148 -0
- package/dist/catalog-db/endpoint-spec.js +22 -0
- package/dist/catalog-db/endpoints/delete_inspections_inspectionid.json +21 -0
- package/dist/catalog-db/endpoints/delete_inventories_id.json +21 -0
- package/dist/catalog-db/endpoints/delete_properties_propertyid_marketing_photos_marketingphotoid.json +27 -0
- package/dist/catalog-db/endpoints/delete_properties_propertyid_photos_photoid.json +27 -0
- package/dist/catalog-db/endpoints/delete_units_unitid_photos_photoid.json +27 -0
- package/dist/catalog-db/endpoints/delete_work_orders_workorderid_attachments_attachmentid.json +27 -0
- package/dist/catalog-db/endpoints/get_bank_accounts.json +58 -0
- package/dist/catalog-db/endpoints/get_bills.json +185 -0
- package/dist/catalog-db/endpoints/get_ca_associations.json +106 -0
- package/dist/catalog-db/endpoints/get_ca_bank_balances.json +101 -0
- package/dist/catalog-db/endpoints/get_ca_board_members.json +146 -0
- package/dist/catalog-db/endpoints/get_ca_homeowner_ledgers.json +70 -0
- package/dist/catalog-db/endpoints/get_ca_homeowners.json +151 -0
- package/dist/catalog-db/endpoints/get_ca_renters.json +132 -0
- package/dist/catalog-db/endpoints/get_ca_rules.json +76 -0
- package/dist/catalog-db/endpoints/get_ca_units.json +130 -0
- package/dist/catalog-db/endpoints/get_ca_violations.json +119 -0
- package/dist/catalog-db/endpoints/get_charges.json +76 -0
- package/dist/catalog-db/endpoints/get_corporate_entities.json +88 -0
- package/dist/catalog-db/endpoints/get_custom_fields.json +120 -0
- package/dist/catalog-db/endpoints/get_delinquent_charges.json +88 -0
- package/dist/catalog-db/endpoints/get_gl_accounts.json +115 -0
- package/dist/catalog-db/endpoints/get_gl_details.json +154 -0
- package/dist/catalog-db/endpoints/get_inventories.json +131 -0
- package/dist/catalog-db/endpoints/get_inventory_locations.json +70 -0
- package/dist/catalog-db/endpoints/get_jobs.json +79 -0
- package/dist/catalog-db/endpoints/get_journal_entries.json +137 -0
- package/dist/catalog-db/endpoints/get_late_fee_policies.json +148 -0
- package/dist/catalog-db/endpoints/get_leads.json +248 -0
- package/dist/catalog-db/endpoints/get_leads_notes.json +83 -0
- package/dist/catalog-db/endpoints/get_leases.json +70 -0
- package/dist/catalog-db/endpoints/get_leases_renewal_pricings.json +52 -0
- package/dist/catalog-db/endpoints/get_listings.json +259 -0
- package/dist/catalog-db/endpoints/get_occupancies_documents.json +70 -0
- package/dist/catalog-db/endpoints/get_owner_groups.json +230 -0
- package/dist/catalog-db/endpoints/get_owners.json +258 -0
- package/dist/catalog-db/endpoints/get_owners_attachments.json +82 -0
- package/dist/catalog-db/endpoints/get_owners_notes.json +71 -0
- package/dist/catalog-db/endpoints/get_owners_ownerid.json +247 -0
- package/dist/catalog-db/endpoints/get_payables.json +138 -0
- package/dist/catalog-db/endpoints/get_portfolios.json +70 -0
- package/dist/catalog-db/endpoints/get_properties.json +267 -0
- package/dist/catalog-db/endpoints/get_properties_attachments.json +76 -0
- package/dist/catalog-db/endpoints/get_properties_marketing_photos.json +76 -0
- package/dist/catalog-db/endpoints/get_properties_notes.json +71 -0
- package/dist/catalog-db/endpoints/get_properties_photos.json +76 -0
- package/dist/catalog-db/endpoints/get_property_groups.json +84 -0
- package/dist/catalog-db/endpoints/get_recurring_charges.json +106 -0
- package/dist/catalog-db/endpoints/get_rental_applications.json +186 -0
- package/dist/catalog-db/endpoints/get_rental_applications_notes.json +71 -0
- package/dist/catalog-db/endpoints/get_showings.json +99 -0
- package/dist/catalog-db/endpoints/get_tenant_ledgers.json +82 -0
- package/dist/catalog-db/endpoints/get_tenants.json +273 -0
- package/dist/catalog-db/endpoints/get_tenants_notes.json +71 -0
- package/dist/catalog-db/endpoints/get_unit_types.json +168 -0
- package/dist/catalog-db/endpoints/get_units.json +359 -0
- package/dist/catalog-db/endpoints/get_units_attachments.json +76 -0
- package/dist/catalog-db/endpoints/get_units_notes.json +71 -0
- package/dist/catalog-db/endpoints/get_units_photos.json +70 -0
- package/dist/catalog-db/endpoints/get_users.json +88 -0
- package/dist/catalog-db/endpoints/get_vendors.json +317 -0
- package/dist/catalog-db/endpoints/get_vendors_notes.json +71 -0
- package/dist/catalog-db/endpoints/get_work_orders.json +322 -0
- package/dist/catalog-db/endpoints/get_work_orders_attachments.json +83 -0
- package/dist/catalog-db/endpoints/patch_bills_billid.json +156 -0
- package/dist/catalog-db/endpoints/patch_ca_violations_violationid.json +71 -0
- package/dist/catalog-db/endpoints/patch_inspections_inspectionid.json +77 -0
- package/dist/catalog-db/endpoints/patch_inventories_id.json +113 -0
- package/dist/catalog-db/endpoints/patch_inventory_locations_id.json +52 -0
- package/dist/catalog-db/endpoints/patch_journal_entries_journalentryid.json +90 -0
- package/dist/catalog-db/endpoints/patch_leads_guestcardid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/patch_leads_leadid.json +162 -0
- package/dist/catalog-db/endpoints/patch_leases_leaseid.json +52 -0
- package/dist/catalog-db/endpoints/patch_owner_groups_id.json +72 -0
- package/dist/catalog-db/endpoints/patch_owners_ownerid.json +344 -0
- package/dist/catalog-db/endpoints/patch_owners_ownerid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/patch_portfolios_portfolioid.json +46 -0
- package/dist/catalog-db/endpoints/patch_properties_propertyid.json +333 -0
- package/dist/catalog-db/endpoints/patch_properties_propertyid_marketing_photos_marketingphotoid.json +46 -0
- package/dist/catalog-db/endpoints/patch_properties_propertyid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/patch_properties_propertyid_photos_photoid.json +46 -0
- package/dist/catalog-db/endpoints/patch_property_groups_propertygroupid.json +40 -0
- package/dist/catalog-db/endpoints/patch_recurring_charges_recurringchargeid.json +89 -0
- package/dist/catalog-db/endpoints/patch_rental_applications_rentalapplicationid.json +47 -0
- package/dist/catalog-db/endpoints/patch_rental_applications_rentalapplicationid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/patch_showings_showingid.json +69 -0
- package/dist/catalog-db/endpoints/patch_tenants_bulk.json +131 -0
- package/dist/catalog-db/endpoints/patch_tenants_tenantid.json +48 -0
- package/dist/catalog-db/endpoints/patch_tenants_tenantid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/patch_unit_types_unittypeid.json +126 -0
- package/dist/catalog-db/endpoints/patch_units_unitid.json +296 -0
- package/dist/catalog-db/endpoints/patch_units_unitid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/patch_units_unitid_photos_photoid.json +46 -0
- package/dist/catalog-db/endpoints/patch_vendors_vendorid.json +263 -0
- package/dist/catalog-db/endpoints/patch_vendors_vendorid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/patch_work_orders_workorderid.json +273 -0
- package/dist/catalog-db/endpoints/patch_work_orders_workorderid_notes_id.json +46 -0
- package/dist/catalog-db/endpoints/post_bank_accounts_bulk.json +167 -0
- package/dist/catalog-db/endpoints/post_bills.json +160 -0
- package/dist/catalog-db/endpoints/post_bills_billid_attachments.json +40 -0
- package/dist/catalog-db/endpoints/post_bills_bulk.json +185 -0
- package/dist/catalog-db/endpoints/post_bills_bulk_async.json +192 -0
- package/dist/catalog-db/endpoints/post_ca_violations.json +63 -0
- package/dist/catalog-db/endpoints/post_ca_violations_violationid_attachments.json +41 -0
- package/dist/catalog-db/endpoints/post_charges.json +56 -0
- package/dist/catalog-db/endpoints/post_charges_bulk.json +70 -0
- package/dist/catalog-db/endpoints/post_charges_bulk_async.json +70 -0
- package/dist/catalog-db/endpoints/post_charges_chargeid_attachments.json +40 -0
- package/dist/catalog-db/endpoints/post_corporate_gl_accounts_bulk.json +71 -0
- package/dist/catalog-db/endpoints/post_gl_accounts_bulk.json +77 -0
- package/dist/catalog-db/endpoints/post_inspections.json +70 -0
- package/dist/catalog-db/endpoints/post_inspections_bulk.json +76 -0
- package/dist/catalog-db/endpoints/post_inspections_inspectionid_attachments.json +41 -0
- package/dist/catalog-db/endpoints/post_inventories.json +105 -0
- package/dist/catalog-db/endpoints/post_inventories_bulk.json +119 -0
- package/dist/catalog-db/endpoints/post_inventory_locations.json +44 -0
- package/dist/catalog-db/endpoints/post_inventory_locations_bulk.json +58 -0
- package/dist/catalog-db/endpoints/post_journal_entries.json +101 -0
- package/dist/catalog-db/endpoints/post_journal_entries_bulk.json +115 -0
- package/dist/catalog-db/endpoints/post_journal_entries_journalentryid_attachments.json +41 -0
- package/dist/catalog-db/endpoints/post_late_fee_policies_bulk.json +115 -0
- package/dist/catalog-db/endpoints/post_leads.json +204 -0
- package/dist/catalog-db/endpoints/post_leads_guestcardid_notes.json +40 -0
- package/dist/catalog-db/endpoints/post_occupancies_occupancyid_attachments.json +41 -0
- package/dist/catalog-db/endpoints/post_owner_groups.json +96 -0
- package/dist/catalog-db/endpoints/post_owners.json +330 -0
- package/dist/catalog-db/endpoints/post_owners_bulk.json +203 -0
- package/dist/catalog-db/endpoints/post_owners_ownerid_attachments.json +40 -0
- package/dist/catalog-db/endpoints/post_owners_ownerid_notes.json +40 -0
- package/dist/catalog-db/endpoints/post_properties_bulk.json +245 -0
- package/dist/catalog-db/endpoints/post_properties_propertyid_attachments.json +40 -0
- package/dist/catalog-db/endpoints/post_properties_propertyid_marketing_photos.json +52 -0
- package/dist/catalog-db/endpoints/post_properties_propertyid_notes.json +40 -0
- package/dist/catalog-db/endpoints/post_properties_propertyid_photos.json +40 -0
- package/dist/catalog-db/endpoints/post_recurring_charges.json +81 -0
- package/dist/catalog-db/endpoints/post_recurring_charges_bulk.json +95 -0
- package/dist/catalog-db/endpoints/post_rental_applications_rentalapplicationid_attachments.json +41 -0
- package/dist/catalog-db/endpoints/post_rental_applications_rentalapplicationid_notes.json +40 -0
- package/dist/catalog-db/endpoints/post_showings.json +62 -0
- package/dist/catalog-db/endpoints/post_tenants_bulk.json +161 -0
- package/dist/catalog-db/endpoints/post_tenants_tenantid_notes.json +40 -0
- package/dist/catalog-db/endpoints/post_unit_types_bulk.json +138 -0
- package/dist/catalog-db/endpoints/post_units_bulk.json +236 -0
- package/dist/catalog-db/endpoints/post_units_unitid_attachments.json +40 -0
- package/dist/catalog-db/endpoints/post_units_unitid_notes.json +40 -0
- package/dist/catalog-db/endpoints/post_units_unitid_photos.json +40 -0
- package/dist/catalog-db/endpoints/post_vendors.json +279 -0
- package/dist/catalog-db/endpoints/post_vendors_bulk.json +279 -0
- package/dist/catalog-db/endpoints/post_vendors_vendorid_notes.json +40 -0
- package/dist/catalog-db/endpoints/post_work_orders.json +251 -0
- package/dist/catalog-db/endpoints/post_work_orders_workorderid_attachments.json +41 -0
- package/dist/catalog-db/endpoints/post_work_orders_workorderid_notes.json +40 -0
- package/dist/catalog-db/endpoints/put_leases_renewal_pricings_bulk.json +58 -0
- package/dist/catalog-db/endpoints/put_units_pricing_matrices_bulk.json +58 -0
- package/dist/catalog-db/endpoints/put_units_pricing_matrices_bulk_async.json +46 -0
- package/dist/catalog-db/endpoints/put_units_unitid_pricing_matrices.json +66 -0
- package/dist/catalog-db/index.json +1894 -0
- package/dist/clients/appfolio-client.js +131 -0
- package/dist/clients/appfolio-db-client.js +186 -0
- package/dist/helpers/register-db-tools.js +27 -0
- package/dist/helpers/register-report-tools.js +27 -0
- package/dist/index.js +10 -0
- package/dist/server/appfolio-mcp-server.js +18 -0
- package/dist/tools/call-endpoint.js +109 -0
- package/dist/tools/describe-endpoint.js +63 -0
- package/dist/tools/describe-report.js +55 -0
- package/dist/tools/list-endpoints.js +90 -0
- package/dist/tools/list-reports.js +58 -0
- package/dist/tools/run-report.js +102 -0
- package/package.json +42 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { loadDbIndex } from "../catalog-db/endpoint-spec.js";
|
|
3
|
+
const schema = z.object({
|
|
4
|
+
category: z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.describe("Optional category filter (e.g. 'Accounting', 'Leasing', 'People', 'Properties', " +
|
|
8
|
+
"'Maintenance', 'Community Associations', 'Corporate Accounting', 'Custom Fields', 'System'). " +
|
|
9
|
+
"Call without args first to see all categories."),
|
|
10
|
+
resource: z
|
|
11
|
+
.string()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("Optional resource filter (e.g. 'bills', 'tenants', 'work_orders')."),
|
|
14
|
+
method: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Optional HTTP method filter: GET, POST, PATCH, PUT, or DELETE."),
|
|
18
|
+
reads_only: z
|
|
19
|
+
.boolean()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("If true, return only read (GET) endpoints."),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Lists available AppFolio Database API (v0) endpoints from the catalog.
|
|
25
|
+
* Lightweight discovery tool: returns operationId, method, path, category, and a
|
|
26
|
+
* one-line description per endpoint so the model can pick one, then call
|
|
27
|
+
* describe_endpoint for its full parameter spec.
|
|
28
|
+
*/
|
|
29
|
+
export const listEndpointsTool = {
|
|
30
|
+
name: "list_endpoints",
|
|
31
|
+
description: "List the available AppFolio Database API endpoints (operationId, method, path, " +
|
|
32
|
+
"category, description). Optionally filter by category, resource, method, or reads-only. " +
|
|
33
|
+
"Use this to discover which endpoint to use, then call describe_endpoint for its parameters " +
|
|
34
|
+
"and call_endpoint to execute it. This is the AppFolio v0 Database API (records/CRUD), " +
|
|
35
|
+
"distinct from the Reporting API (list_reports/run_report).",
|
|
36
|
+
schema,
|
|
37
|
+
handler: async ({ params }) => {
|
|
38
|
+
const index = loadDbIndex();
|
|
39
|
+
const category = params?.category;
|
|
40
|
+
const resource = params?.resource;
|
|
41
|
+
const method = params?.method?.toUpperCase();
|
|
42
|
+
const readsOnly = params?.reads_only;
|
|
43
|
+
let endpoints = index.endpoints;
|
|
44
|
+
if (category) {
|
|
45
|
+
const wanted = category.toLowerCase();
|
|
46
|
+
endpoints = endpoints.filter((e) => e.category.toLowerCase() === wanted);
|
|
47
|
+
}
|
|
48
|
+
if (resource) {
|
|
49
|
+
const wanted = resource.toLowerCase();
|
|
50
|
+
endpoints = endpoints.filter((e) => e.resource.toLowerCase() === wanted);
|
|
51
|
+
}
|
|
52
|
+
if (method)
|
|
53
|
+
endpoints = endpoints.filter((e) => e.method === method);
|
|
54
|
+
if (readsOnly)
|
|
55
|
+
endpoints = endpoints.filter((e) => !e.writeOperation);
|
|
56
|
+
const categories = [...new Set(index.endpoints.map((e) => e.category))].sort();
|
|
57
|
+
if ((category || resource || method) && endpoints.length === 0) {
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: "text",
|
|
62
|
+
text: `No endpoints matched your filters. ` +
|
|
63
|
+
`Available categories: ${categories.join(", ")}.`,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const payload = {
|
|
69
|
+
endpointCount: endpoints.length,
|
|
70
|
+
totalEndpoints: index.endpointCount,
|
|
71
|
+
baseUrl: index.baseUrl,
|
|
72
|
+
categories,
|
|
73
|
+
paginationParams: index.paginationParams,
|
|
74
|
+
endpoints: endpoints.map((e) => ({
|
|
75
|
+
operation_id: e.operationId,
|
|
76
|
+
method: e.method,
|
|
77
|
+
path: e.path,
|
|
78
|
+
name: e.name,
|
|
79
|
+
category: e.category,
|
|
80
|
+
resource: e.resource,
|
|
81
|
+
description: e.description,
|
|
82
|
+
writeOperation: e.writeOperation,
|
|
83
|
+
...(e.deprecated ? { deprecated: true } : {}),
|
|
84
|
+
})),
|
|
85
|
+
};
|
|
86
|
+
return {
|
|
87
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { loadIndex } from "../catalog/report-spec.js";
|
|
3
|
+
const schema = z.object({
|
|
4
|
+
category: z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.describe("Optional category filter (e.g. 'Financial', 'Leasing', 'Tenant'). " +
|
|
8
|
+
"Omit to list every report. Call without args first to see all categories."),
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Lists available AppFolio reports from the catalog. Lightweight discovery tool:
|
|
12
|
+
* returns slug, name, category, and required filters per report so the model can
|
|
13
|
+
* pick one, then call describe_report for its full parameter spec.
|
|
14
|
+
*/
|
|
15
|
+
export const listReportsTool = {
|
|
16
|
+
name: "list_reports",
|
|
17
|
+
description: "List the available AppFolio reports (slug, name, category, required filters). " +
|
|
18
|
+
"Optionally filter by category. Use this to discover which report to run, then " +
|
|
19
|
+
"call describe_report for its parameters and run_report to execute it.",
|
|
20
|
+
schema,
|
|
21
|
+
handler: async ({ params }) => {
|
|
22
|
+
const index = loadIndex();
|
|
23
|
+
const category = params?.category;
|
|
24
|
+
let reports = index.reports;
|
|
25
|
+
if (category) {
|
|
26
|
+
const wanted = category.toLowerCase();
|
|
27
|
+
reports = reports.filter((r) => r.category.toLowerCase() === wanted);
|
|
28
|
+
}
|
|
29
|
+
const categories = [...new Set(index.reports.map((r) => r.category))].sort();
|
|
30
|
+
const payload = {
|
|
31
|
+
reportCount: reports.length,
|
|
32
|
+
totalReports: index.reportCount,
|
|
33
|
+
categories,
|
|
34
|
+
universalParameters: index.universalParameters,
|
|
35
|
+
reports: reports.map((r) => ({
|
|
36
|
+
report_name: r.slug,
|
|
37
|
+
name: r.name,
|
|
38
|
+
category: r.category,
|
|
39
|
+
description: r.description,
|
|
40
|
+
requiredFilters: r.requiredFilters,
|
|
41
|
+
})),
|
|
42
|
+
};
|
|
43
|
+
if (category && reports.length === 0) {
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: `No reports found in category "${category}". ` +
|
|
49
|
+
`Available categories: ${categories.join(", ")}.`,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { loadReportSpec, loadIndex, universalParameters } from "../catalog/report-spec.js";
|
|
3
|
+
import { appfolioClient } from "../clients/appfolio-client.js";
|
|
4
|
+
const schema = z.object({
|
|
5
|
+
report_name: z
|
|
6
|
+
.string()
|
|
7
|
+
.describe("The report slug to run, e.g. 'balance_sheet'. See list_reports / describe_report."),
|
|
8
|
+
filters: z
|
|
9
|
+
.record(z.any())
|
|
10
|
+
.optional()
|
|
11
|
+
.describe("Report-specific filters as a JSON object keyed by the parameter names from " +
|
|
12
|
+
"describe_report (e.g. { posted_on_to: '2024-12-31', properties: { properties_ids: ['123'] } }). " +
|
|
13
|
+
"Required filters must be included or the request is rejected."),
|
|
14
|
+
columns: z
|
|
15
|
+
.array(z.string())
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("Optional subset of response columns to return. All columns are returned by default."),
|
|
18
|
+
paginate_results: z
|
|
19
|
+
.boolean()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Default true (5,000 rows/page, auto-followed). Set false to return all rows as one array."),
|
|
22
|
+
page: z.number().optional().describe("Pagination page (advanced; usually handled automatically)."),
|
|
23
|
+
limit: z.number().optional().describe("Rows per page (advanced)."),
|
|
24
|
+
});
|
|
25
|
+
function errorText(text) {
|
|
26
|
+
return { content: [{ type: "text", text }] };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Executes an AppFolio report after validating the request against the catalog
|
|
30
|
+
* spec: the report must exist, all required filters must be present, and every
|
|
31
|
+
* supplied parameter must be a known filter for that report (or a universal
|
|
32
|
+
* pagination param). Enum values are advisory (see describe_report) and not hard
|
|
33
|
+
* enforced, to avoid rejecting valid inputs over doc-formatting quirks.
|
|
34
|
+
*/
|
|
35
|
+
export const runReportTool = {
|
|
36
|
+
name: "run_report",
|
|
37
|
+
description: "Run an AppFolio report and return its rows. Validates the report name, required " +
|
|
38
|
+
"filters, and parameter names against the report catalog before calling AppFolio. " +
|
|
39
|
+
"Use describe_report first to learn the exact filter keys for the report.",
|
|
40
|
+
schema,
|
|
41
|
+
handler: async ({ params }) => {
|
|
42
|
+
const reportName = params?.report_name;
|
|
43
|
+
const spec = loadReportSpec(reportName);
|
|
44
|
+
if (!spec) {
|
|
45
|
+
const known = loadIndex().reports.map((r) => r.slug);
|
|
46
|
+
const suggestions = known
|
|
47
|
+
.filter((s) => reportName && (s.includes(reportName) || reportName.includes(s)))
|
|
48
|
+
.slice(0, 5);
|
|
49
|
+
return errorText(`Unknown report "${reportName}". ` +
|
|
50
|
+
(suggestions.length ? `Did you mean: ${suggestions.join(", ")}? ` : "") +
|
|
51
|
+
`Call list_reports to see all ${known.length} reports.`);
|
|
52
|
+
}
|
|
53
|
+
// Assemble the request body from filters + pagination/column args.
|
|
54
|
+
const body = { ...(params?.filters ?? {}) };
|
|
55
|
+
if (params?.columns !== undefined)
|
|
56
|
+
body.columns = params.columns;
|
|
57
|
+
if (params?.paginate_results !== undefined)
|
|
58
|
+
body.paginate_results = params.paginate_results;
|
|
59
|
+
if (params?.page !== undefined)
|
|
60
|
+
body.page = params.page;
|
|
61
|
+
if (params?.limit !== undefined)
|
|
62
|
+
body.limit = params.limit;
|
|
63
|
+
// Reject unknown parameters early with a helpful list.
|
|
64
|
+
const allowed = new Set([
|
|
65
|
+
...spec.parameters.map((p) => p.name),
|
|
66
|
+
...universalParameters(),
|
|
67
|
+
]);
|
|
68
|
+
const unknown = Object.keys(body).filter((k) => !allowed.has(k));
|
|
69
|
+
if (unknown.length > 0) {
|
|
70
|
+
return errorText(`Unknown parameter(s) for "${spec.slug}": ${unknown.join(", ")}. ` +
|
|
71
|
+
`Valid filters: ${spec.parameters.map((p) => p.name).join(", ")}. ` +
|
|
72
|
+
`Plus universal: ${universalParameters().join(", ")}. ` +
|
|
73
|
+
`Call describe_report("${spec.slug}") for details.`);
|
|
74
|
+
}
|
|
75
|
+
// Enforce required filters.
|
|
76
|
+
const missing = spec.requiredFilters.filter((rf) => {
|
|
77
|
+
const v = body[rf];
|
|
78
|
+
return v === undefined || v === null || (typeof v === "string" && v.trim() === "");
|
|
79
|
+
});
|
|
80
|
+
if (missing.length > 0) {
|
|
81
|
+
return errorText(`Missing required filter(s) for "${spec.slug}": ${missing.join(", ")}. ` +
|
|
82
|
+
`Call describe_report("${spec.slug}") to see their expected format.`);
|
|
83
|
+
}
|
|
84
|
+
// The client appends ".json" to the slug.
|
|
85
|
+
const response = await appfolioClient.runReport(spec.slug, body);
|
|
86
|
+
if (response.isError) {
|
|
87
|
+
return errorText(`Error running ${spec.slug}: ${response.error}`);
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: JSON.stringify({
|
|
94
|
+
report_name: spec.slug,
|
|
95
|
+
rowCount: Array.isArray(response.result) ? response.result.length : 0,
|
|
96
|
+
results: response.result,
|
|
97
|
+
}),
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nightsquawktech/appfolio-mcp-server",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MCP server for AppFolio Reporting API v2",
|
|
5
|
+
"license": "AGPL-3.0-only",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": "./dist/index.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"COMMERCIAL.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc && shx mkdir -p dist/catalog && shx cp -r src/catalog/reports dist/catalog && shx cp src/catalog/index.json dist/catalog && shx mkdir -p dist/catalog-db && shx cp -r src/catalog-db/endpoints dist/catalog-db && shx cp src/catalog-db/index.json dist/catalog-db && shx chmod +x dist/*.js",
|
|
17
|
+
"prepare": "npm run build",
|
|
18
|
+
"watch": "tsc --watch"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.6.0",
|
|
22
|
+
"@nightsquawktech/mcp-core": "^0.1.0",
|
|
23
|
+
"zod": "^3.24.2"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.13.10",
|
|
27
|
+
"shx": "^0.3.4",
|
|
28
|
+
"typescript": "^5.8.2"
|
|
29
|
+
},
|
|
30
|
+
"author": "Kevin Reyes (NightSquawk Tech) <hello@nightsquawk.tech>",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/NightSquawk/appfolio-mcp-server.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/NightSquawk/appfolio-mcp-server#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/NightSquawk/appfolio-mcp-server/issues"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public"
|
|
41
|
+
}
|
|
42
|
+
}
|