@nightsquawktech/invoiceninja-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 +128 -0
- package/dist/catalog/endpoint-spec.js +36 -0
- package/dist/catalog/endpoints/action_invoice.json +41 -0
- package/dist/catalog/endpoints/action_payment.json +41 -0
- package/dist/catalog/endpoints/action_purchase_order.json +41 -0
- package/dist/catalog/endpoints/action_quote.json +41 -0
- package/dist/catalog/endpoints/action_recurring_invoice.json +41 -0
- package/dist/catalog/endpoints/action_recurring_quote.json +41 -0
- package/dist/catalog/endpoints/bulk_bank_integrations.json +26 -0
- package/dist/catalog/endpoints/bulk_bank_transaction_rules.json +26 -0
- package/dist/catalog/endpoints/bulk_bank_transactions.json +26 -0
- package/dist/catalog/endpoints/bulk_clients.json +46 -0
- package/dist/catalog/endpoints/bulk_company_gateways.json +27 -0
- package/dist/catalog/endpoints/bulk_credits.json +26 -0
- package/dist/catalog/endpoints/bulk_designs.json +27 -0
- package/dist/catalog/endpoints/bulk_expense_categorys.json +27 -0
- package/dist/catalog/endpoints/bulk_expenses.json +27 -0
- package/dist/catalog/endpoints/bulk_group_settings.json +26 -0
- package/dist/catalog/endpoints/bulk_invoices.json +44 -0
- package/dist/catalog/endpoints/bulk_locations.json +40 -0
- package/dist/catalog/endpoints/bulk_payment_terms.json +27 -0
- package/dist/catalog/endpoints/bulk_payments.json +27 -0
- package/dist/catalog/endpoints/bulk_products.json +46 -0
- package/dist/catalog/endpoints/bulk_projects.json +27 -0
- package/dist/catalog/endpoints/bulk_purchase_orderss.json +26 -0
- package/dist/catalog/endpoints/bulk_quotes.json +39 -0
- package/dist/catalog/endpoints/bulk_recurring_expenses.json +27 -0
- package/dist/catalog/endpoints/bulk_recurring_invoices.json +39 -0
- package/dist/catalog/endpoints/bulk_recurring_quotes.json +27 -0
- package/dist/catalog/endpoints/bulk_subscriptions.json +27 -0
- package/dist/catalog/endpoints/bulk_tags.json +51 -0
- package/dist/catalog/endpoints/bulk_task_scheduler_actions.json +27 -0
- package/dist/catalog/endpoints/bulk_task_statuss.json +27 -0
- package/dist/catalog/endpoints/bulk_tasks.json +27 -0
- package/dist/catalog/endpoints/bulk_tax_rates.json +27 -0
- package/dist/catalog/endpoints/bulk_tokens.json +27 -0
- package/dist/catalog/endpoints/bulk_users.json +27 -0
- package/dist/catalog/endpoints/bulk_vendors.json +27 -0
- package/dist/catalog/endpoints/bulk_webhooks.json +27 -0
- package/dist/catalog/endpoints/client_statement.json +77 -0
- package/dist/catalog/endpoints/confirm_apple_purchase.json +23 -0
- package/dist/catalog/endpoints/create_task_scheduler.json +81 -0
- package/dist/catalog/endpoints/delete_bank_integration.json +32 -0
- package/dist/catalog/endpoints/delete_bank_transaction.json +32 -0
- package/dist/catalog/endpoints/delete_bank_transaction_rule.json +32 -0
- package/dist/catalog/endpoints/delete_client.json +38 -0
- package/dist/catalog/endpoints/delete_client_gateway_token.json +32 -0
- package/dist/catalog/endpoints/delete_company.json +32 -0
- package/dist/catalog/endpoints/delete_company_gateway.json +32 -0
- package/dist/catalog/endpoints/delete_credit.json +32 -0
- package/dist/catalog/endpoints/delete_design.json +32 -0
- package/dist/catalog/endpoints/delete_expense.json +32 -0
- package/dist/catalog/endpoints/delete_expense_category.json +24 -0
- package/dist/catalog/endpoints/delete_group_setting.json +32 -0
- package/dist/catalog/endpoints/delete_invoice.json +32 -0
- package/dist/catalog/endpoints/delete_location.json +24 -0
- package/dist/catalog/endpoints/delete_payment.json +32 -0
- package/dist/catalog/endpoints/delete_payment_term.json +32 -0
- package/dist/catalog/endpoints/delete_product.json +32 -0
- package/dist/catalog/endpoints/delete_project.json +32 -0
- package/dist/catalog/endpoints/delete_purchase_order.json +32 -0
- package/dist/catalog/endpoints/delete_quote.json +32 -0
- package/dist/catalog/endpoints/delete_recurring_expense.json +32 -0
- package/dist/catalog/endpoints/delete_recurring_invoice.json +32 -0
- package/dist/catalog/endpoints/delete_recurring_quote.json +32 -0
- package/dist/catalog/endpoints/delete_settings_passkey.json +22 -0
- package/dist/catalog/endpoints/delete_subscription.json +32 -0
- package/dist/catalog/endpoints/delete_tag.json +24 -0
- package/dist/catalog/endpoints/delete_task.json +32 -0
- package/dist/catalog/endpoints/delete_task_status.json +32 -0
- package/dist/catalog/endpoints/delete_tax_rate.json +24 -0
- package/dist/catalog/endpoints/delete_token.json +32 -0
- package/dist/catalog/endpoints/delete_user.json +40 -0
- package/dist/catalog/endpoints/delete_vendor.json +32 -0
- package/dist/catalog/endpoints/delete_webhook.json +32 -0
- package/dist/catalog/endpoints/destroy_task_scheduler.json +24 -0
- package/dist/catalog/endpoints/detach_user.json +32 -0
- package/dist/catalog/endpoints/download_credit.json +32 -0
- package/dist/catalog/endpoints/download_invoice_by_invitation.json +32 -0
- package/dist/catalog/endpoints/download_purchase_order.json +32 -0
- package/dist/catalog/endpoints/download_quote.json +32 -0
- package/dist/catalog/endpoints/download_recurring_invoice.json +32 -0
- package/dist/catalog/endpoints/edit_bank_integration.json +33 -0
- package/dist/catalog/endpoints/edit_bank_transaction.json +33 -0
- package/dist/catalog/endpoints/edit_bank_transaction_rule.json +33 -0
- package/dist/catalog/endpoints/edit_client.json +39 -0
- package/dist/catalog/endpoints/edit_client_gateway_token.json +33 -0
- package/dist/catalog/endpoints/edit_company.json +33 -0
- package/dist/catalog/endpoints/edit_company_gateway.json +33 -0
- package/dist/catalog/endpoints/edit_credit.json +33 -0
- package/dist/catalog/endpoints/edit_design.json +33 -0
- package/dist/catalog/endpoints/edit_expense.json +33 -0
- package/dist/catalog/endpoints/edit_expense_category.json +25 -0
- package/dist/catalog/endpoints/edit_group_setting.json +33 -0
- package/dist/catalog/endpoints/edit_invoice.json +33 -0
- package/dist/catalog/endpoints/edit_payment.json +33 -0
- package/dist/catalog/endpoints/edit_payment_terms.json +33 -0
- package/dist/catalog/endpoints/edit_product.json +33 -0
- package/dist/catalog/endpoints/edit_project.json +33 -0
- package/dist/catalog/endpoints/edit_purchase_order.json +33 -0
- package/dist/catalog/endpoints/edit_quote.json +33 -0
- package/dist/catalog/endpoints/edit_recurring_expense.json +33 -0
- package/dist/catalog/endpoints/edit_recurring_invoice.json +33 -0
- package/dist/catalog/endpoints/edit_recurring_quote.json +33 -0
- package/dist/catalog/endpoints/edit_subscription.json +33 -0
- package/dist/catalog/endpoints/edit_tag.json +24 -0
- package/dist/catalog/endpoints/edit_task.json +33 -0
- package/dist/catalog/endpoints/edit_task_statuss.json +33 -0
- package/dist/catalog/endpoints/edit_tax_rate.json +25 -0
- package/dist/catalog/endpoints/edit_token.json +33 -0
- package/dist/catalog/endpoints/edit_user.json +33 -0
- package/dist/catalog/endpoints/edit_vendor.json +33 -0
- package/dist/catalog/endpoints/edit_webhook.json +33 -0
- package/dist/catalog/endpoints/get_account_transactions.json +24 -0
- package/dist/catalog/endpoints/get_activities.json +44 -0
- package/dist/catalog/endpoints/get_activity_historical_entity_pdf.json +31 -0
- package/dist/catalog/endpoints/get_activity_report.json +49 -0
- package/dist/catalog/endpoints/get_ardetail_report.json +49 -0
- package/dist/catalog/endpoints/get_arsummary_report.json +49 -0
- package/dist/catalog/endpoints/get_bank_integrations.json +61 -0
- package/dist/catalog/endpoints/get_bank_integrations_create.json +24 -0
- package/dist/catalog/endpoints/get_bank_transaction_rules.json +62 -0
- package/dist/catalog/endpoints/get_bank_transaction_rules_create.json +24 -0
- package/dist/catalog/endpoints/get_bank_transactions.json +63 -0
- package/dist/catalog/endpoints/get_bank_transactions_create.json +24 -0
- package/dist/catalog/endpoints/get_chart_calculated_fields.json +18 -0
- package/dist/catalog/endpoints/get_chart_summary_v2.json +18 -0
- package/dist/catalog/endpoints/get_chart_totals.json +38 -0
- package/dist/catalog/endpoints/get_chart_totals_v2.json +18 -0
- package/dist/catalog/endpoints/get_claim_license.json +32 -0
- package/dist/catalog/endpoints/get_client_balance_report.json +49 -0
- package/dist/catalog/endpoints/get_client_contact_report.json +49 -0
- package/dist/catalog/endpoints/get_client_gateway_tokens.json +45 -0
- package/dist/catalog/endpoints/get_client_gateway_tokens_create.json +24 -0
- package/dist/catalog/endpoints/get_client_report.json +64 -0
- package/dist/catalog/endpoints/get_client_sales_report.json +49 -0
- package/dist/catalog/endpoints/get_clients.json +152 -0
- package/dist/catalog/endpoints/get_clients_create.json +30 -0
- package/dist/catalog/endpoints/get_companies.json +38 -0
- package/dist/catalog/endpoints/get_companies_create.json +24 -0
- package/dist/catalog/endpoints/get_company_gateways.json +47 -0
- package/dist/catalog/endpoints/get_company_gateways_create.json +24 -0
- package/dist/catalog/endpoints/get_company_ledger.json +24 -0
- package/dist/catalog/endpoints/get_contact_report.json +64 -0
- package/dist/catalog/endpoints/get_credit_report.json +49 -0
- package/dist/catalog/endpoints/get_credits.json +55 -0
- package/dist/catalog/endpoints/get_credits_create.json +24 -0
- package/dist/catalog/endpoints/get_designs.json +54 -0
- package/dist/catalog/endpoints/get_designs_create.json +24 -0
- package/dist/catalog/endpoints/get_document_report.json +64 -0
- package/dist/catalog/endpoints/get_documents.json +54 -0
- package/dist/catalog/endpoints/get_expense_category_create.json +16 -0
- package/dist/catalog/endpoints/get_expense_categorys.json +47 -0
- package/dist/catalog/endpoints/get_expense_report.json +49 -0
- package/dist/catalog/endpoints/get_expenses.json +55 -0
- package/dist/catalog/endpoints/get_expenses_create.json +24 -0
- package/dist/catalog/endpoints/get_export.json +15 -0
- package/dist/catalog/endpoints/get_export_preview.json +25 -0
- package/dist/catalog/endpoints/get_group_settings.json +47 -0
- package/dist/catalog/endpoints/get_group_settings_create.json +24 -0
- package/dist/catalog/endpoints/get_health_check.json +15 -0
- package/dist/catalog/endpoints/get_import_json.json +15 -0
- package/dist/catalog/endpoints/get_invoice_create.json +16 -0
- package/dist/catalog/endpoints/get_invoice_delivery_note.json +32 -0
- package/dist/catalog/endpoints/get_invoice_item_report.json +64 -0
- package/dist/catalog/endpoints/get_invoice_report.json +64 -0
- package/dist/catalog/endpoints/get_invoices.json +174 -0
- package/dist/catalog/endpoints/get_last_error.json +15 -0
- package/dist/catalog/endpoints/get_locations.json +54 -0
- package/dist/catalog/endpoints/get_locations_create.json +24 -0
- package/dist/catalog/endpoints/get_logout.json +30 -0
- package/dist/catalog/endpoints/get_payment_report.json +64 -0
- package/dist/catalog/endpoints/get_payment_terms.json +54 -0
- package/dist/catalog/endpoints/get_payment_terms_create.json +24 -0
- package/dist/catalog/endpoints/get_payments.json +118 -0
- package/dist/catalog/endpoints/get_payments_create.json +24 -0
- package/dist/catalog/endpoints/get_ping.json +15 -0
- package/dist/catalog/endpoints/get_preview.json +15 -0
- package/dist/catalog/endpoints/get_preview_purchase_order.json +15 -0
- package/dist/catalog/endpoints/get_product_report.json +64 -0
- package/dist/catalog/endpoints/get_product_sales_report.json +64 -0
- package/dist/catalog/endpoints/get_products.json +118 -0
- package/dist/catalog/endpoints/get_products_create.json +24 -0
- package/dist/catalog/endpoints/get_profit_loss_report.json +64 -0
- package/dist/catalog/endpoints/get_project_report.json +49 -0
- package/dist/catalog/endpoints/get_projects.json +55 -0
- package/dist/catalog/endpoints/get_projects_create.json +24 -0
- package/dist/catalog/endpoints/get_purchase_order_create.json +24 -0
- package/dist/catalog/endpoints/get_purchase_orders.json +55 -0
- package/dist/catalog/endpoints/get_quote_item_report.json +64 -0
- package/dist/catalog/endpoints/get_quote_report.json +64 -0
- package/dist/catalog/endpoints/get_quotes.json +125 -0
- package/dist/catalog/endpoints/get_quotes_create.json +24 -0
- package/dist/catalog/endpoints/get_recurring_expenses.json +54 -0
- package/dist/catalog/endpoints/get_recurring_expenses_create.json +24 -0
- package/dist/catalog/endpoints/get_recurring_invoice_report.json +64 -0
- package/dist/catalog/endpoints/get_recurring_invoices.json +139 -0
- package/dist/catalog/endpoints/get_recurring_invoices_create.json +24 -0
- package/dist/catalog/endpoints/get_recurring_quotes.json +47 -0
- package/dist/catalog/endpoints/get_recurring_quotes_create.json +24 -0
- package/dist/catalog/endpoints/get_refresh_accounts.json +24 -0
- package/dist/catalog/endpoints/get_remove_account.json +24 -0
- package/dist/catalog/endpoints/get_report_preview.json +25 -0
- package/dist/catalog/endpoints/get_scheduler.json +15 -0
- package/dist/catalog/endpoints/get_settings_passkeys.json +15 -0
- package/dist/catalog/endpoints/get_show_template.json +47 -0
- package/dist/catalog/endpoints/get_statics.json +23 -0
- package/dist/catalog/endpoints/get_subscriptions.json +47 -0
- package/dist/catalog/endpoints/get_subscriptions_create.json +24 -0
- package/dist/catalog/endpoints/get_system_logs.json +47 -0
- package/dist/catalog/endpoints/get_tags.json +63 -0
- package/dist/catalog/endpoints/get_tags_create.json +16 -0
- package/dist/catalog/endpoints/get_task_report.json +64 -0
- package/dist/catalog/endpoints/get_task_scheduler.json +38 -0
- package/dist/catalog/endpoints/get_task_schedulers.json +15 -0
- package/dist/catalog/endpoints/get_task_statuses.json +54 -0
- package/dist/catalog/endpoints/get_task_statuss_create.json +24 -0
- package/dist/catalog/endpoints/get_tasks.json +55 -0
- package/dist/catalog/endpoints/get_tasks_create.json +24 -0
- package/dist/catalog/endpoints/get_tax_period_report.json +49 -0
- package/dist/catalog/endpoints/get_tax_rate_create.json +16 -0
- package/dist/catalog/endpoints/get_tax_rates.json +47 -0
- package/dist/catalog/endpoints/get_tax_summary_report.json +49 -0
- package/dist/catalog/endpoints/get_template_preview.json +25 -0
- package/dist/catalog/endpoints/get_tokens.json +54 -0
- package/dist/catalog/endpoints/get_tokens_create.json +24 -0
- package/dist/catalog/endpoints/get_user_sales_report.json +49 -0
- package/dist/catalog/endpoints/get_users.json +48 -0
- package/dist/catalog/endpoints/get_users_create.json +24 -0
- package/dist/catalog/endpoints/get_vendors.json +55 -0
- package/dist/catalog/endpoints/get_vendors_create.json +24 -0
- package/dist/catalog/endpoints/get_webcron.json +15 -0
- package/dist/catalog/endpoints/get_webhooks.json +54 -0
- package/dist/catalog/endpoints/get_webhooks_create.json +24 -0
- package/dist/catalog/endpoints/invite_user.json +32 -0
- package/dist/catalog/endpoints/invite_user_reconfirm.json +32 -0
- package/dist/catalog/endpoints/match_bank_transactions.json +26 -0
- package/dist/catalog/endpoints/merge_client.json +54 -0
- package/dist/catalog/endpoints/post_activities_entity.json +18 -0
- package/dist/catalog/endpoints/post_activities_notes.json +18 -0
- package/dist/catalog/endpoints/post_connected_account.json +38 -0
- package/dist/catalog/endpoints/post_import.json +51 -0
- package/dist/catalog/endpoints/post_login.json +79 -0
- package/dist/catalog/endpoints/post_one_time_token.json +15 -0
- package/dist/catalog/endpoints/post_passkey_login_options.json +24 -0
- package/dist/catalog/endpoints/post_purge_company.json +24 -0
- package/dist/catalog/endpoints/post_purge_company_save_settings.json +24 -0
- package/dist/catalog/endpoints/post_refresh.json +46 -0
- package/dist/catalog/endpoints/post_search.json +18 -0
- package/dist/catalog/endpoints/post_settings_passkeys.json +34 -0
- package/dist/catalog/endpoints/post_settings_passkeys_options.json +15 -0
- package/dist/catalog/endpoints/post_start_migration.json +31 -0
- package/dist/catalog/endpoints/postmark_webhook.json +24 -0
- package/dist/catalog/endpoints/preimport.json +69 -0
- package/dist/catalog/endpoints/process_apple_webhook.json +23 -0
- package/dist/catalog/endpoints/purge_client.json +32 -0
- package/dist/catalog/endpoints/reactivate_email.json +38 -0
- package/dist/catalog/endpoints/self_update.json +31 -0
- package/dist/catalog/endpoints/self_update_check_version.json +15 -0
- package/dist/catalog/endpoints/send_email_template.json +73 -0
- package/dist/catalog/endpoints/set_default_company.json +33 -0
- package/dist/catalog/endpoints/show_bank_integration.json +33 -0
- package/dist/catalog/endpoints/show_bank_transaction.json +33 -0
- package/dist/catalog/endpoints/show_bank_transaction_rule.json +33 -0
- package/dist/catalog/endpoints/show_client.json +39 -0
- package/dist/catalog/endpoints/show_client_gateway_token.json +33 -0
- package/dist/catalog/endpoints/show_company.json +33 -0
- package/dist/catalog/endpoints/show_company_gateway.json +33 -0
- package/dist/catalog/endpoints/show_credit.json +33 -0
- package/dist/catalog/endpoints/show_current_company.json +24 -0
- package/dist/catalog/endpoints/show_design.json +33 -0
- package/dist/catalog/endpoints/show_expense.json +33 -0
- package/dist/catalog/endpoints/show_expense_category.json +25 -0
- package/dist/catalog/endpoints/show_group_setting.json +33 -0
- package/dist/catalog/endpoints/show_invoice.json +33 -0
- package/dist/catalog/endpoints/show_location.json +33 -0
- package/dist/catalog/endpoints/show_payment.json +33 -0
- package/dist/catalog/endpoints/show_payment_term.json +33 -0
- package/dist/catalog/endpoints/show_product.json +33 -0
- package/dist/catalog/endpoints/show_project.json +33 -0
- package/dist/catalog/endpoints/show_purchase_order.json +33 -0
- package/dist/catalog/endpoints/show_quote.json +33 -0
- package/dist/catalog/endpoints/show_recurring_expense.json +33 -0
- package/dist/catalog/endpoints/show_recurring_invoice.json +33 -0
- package/dist/catalog/endpoints/show_recurring_quote.json +33 -0
- package/dist/catalog/endpoints/show_subscription.json +33 -0
- package/dist/catalog/endpoints/show_system_logs.json +33 -0
- package/dist/catalog/endpoints/show_tag.json +24 -0
- package/dist/catalog/endpoints/show_task.json +33 -0
- package/dist/catalog/endpoints/show_task_scheduler.json +24 -0
- package/dist/catalog/endpoints/show_task_status.json +33 -0
- package/dist/catalog/endpoints/show_tax_rate.json +25 -0
- package/dist/catalog/endpoints/show_token.json +33 -0
- package/dist/catalog/endpoints/show_user.json +33 -0
- package/dist/catalog/endpoints/show_vendor.json +33 -0
- package/dist/catalog/endpoints/show_webhook.json +33 -0
- package/dist/catalog/endpoints/sort_tasks.json +46 -0
- package/dist/catalog/endpoints/store_bank_integration.json +24 -0
- package/dist/catalog/endpoints/store_bank_transaction.json +24 -0
- package/dist/catalog/endpoints/store_bank_transaction_rule.json +24 -0
- package/dist/catalog/endpoints/store_client.json +237 -0
- package/dist/catalog/endpoints/store_client_gateway_token.json +24 -0
- package/dist/catalog/endpoints/store_company.json +24 -0
- package/dist/catalog/endpoints/store_company_gateway.json +24 -0
- package/dist/catalog/endpoints/store_credit.json +251 -0
- package/dist/catalog/endpoints/store_design.json +24 -0
- package/dist/catalog/endpoints/store_expense.json +24 -0
- package/dist/catalog/endpoints/store_expense_category.json +24 -0
- package/dist/catalog/endpoints/store_group_setting.json +24 -0
- package/dist/catalog/endpoints/store_invoice.json +257 -0
- package/dist/catalog/endpoints/store_location.json +119 -0
- package/dist/catalog/endpoints/store_payment.json +140 -0
- package/dist/catalog/endpoints/store_payment_term.json +59 -0
- package/dist/catalog/endpoints/store_product.json +179 -0
- package/dist/catalog/endpoints/store_project.json +113 -0
- package/dist/catalog/endpoints/store_purchase_order.json +89 -0
- package/dist/catalog/endpoints/store_quote.json +89 -0
- package/dist/catalog/endpoints/store_recurring_expense.json +24 -0
- package/dist/catalog/endpoints/store_recurring_invoice.json +101 -0
- package/dist/catalog/endpoints/store_recurring_quote.json +24 -0
- package/dist/catalog/endpoints/store_refund.json +235 -0
- package/dist/catalog/endpoints/store_subscription.json +24 -0
- package/dist/catalog/endpoints/store_tag.json +43 -0
- package/dist/catalog/endpoints/store_task.json +113 -0
- package/dist/catalog/endpoints/store_task_status.json +77 -0
- package/dist/catalog/endpoints/store_token.json +24 -0
- package/dist/catalog/endpoints/store_user.json +24 -0
- package/dist/catalog/endpoints/store_vendor.json +131 -0
- package/dist/catalog/endpoints/store_webhook.json +24 -0
- package/dist/catalog/endpoints/support_message.json +25 -0
- package/dist/catalog/endpoints/update_bank_integration.json +33 -0
- package/dist/catalog/endpoints/update_bank_transaction.json +33 -0
- package/dist/catalog/endpoints/update_bank_transaction_rule.json +33 -0
- package/dist/catalog/endpoints/update_client.json +246 -0
- package/dist/catalog/endpoints/update_client_gateway_token.json +33 -0
- package/dist/catalog/endpoints/update_client_tax_data.json +38 -0
- package/dist/catalog/endpoints/update_company.json +33 -0
- package/dist/catalog/endpoints/update_company_gateway.json +33 -0
- package/dist/catalog/endpoints/update_company_user.json +16 -0
- package/dist/catalog/endpoints/update_credit.json +33 -0
- package/dist/catalog/endpoints/update_design.json +33 -0
- package/dist/catalog/endpoints/update_expense.json +33 -0
- package/dist/catalog/endpoints/update_expense_category.json +25 -0
- package/dist/catalog/endpoints/update_group_setting.json +33 -0
- package/dist/catalog/endpoints/update_invoice.json +33 -0
- package/dist/catalog/endpoints/update_location.json +128 -0
- package/dist/catalog/endpoints/update_payment.json +33 -0
- package/dist/catalog/endpoints/update_payment_term.json +33 -0
- package/dist/catalog/endpoints/update_product.json +188 -0
- package/dist/catalog/endpoints/update_project.json +122 -0
- package/dist/catalog/endpoints/update_purchase_order.json +98 -0
- package/dist/catalog/endpoints/update_quote.json +98 -0
- package/dist/catalog/endpoints/update_recurring_expense.json +33 -0
- package/dist/catalog/endpoints/update_recurring_invoice.json +33 -0
- package/dist/catalog/endpoints/update_recurring_quote.json +33 -0
- package/dist/catalog/endpoints/update_subscription.json +33 -0
- package/dist/catalog/endpoints/update_tag.json +38 -0
- package/dist/catalog/endpoints/update_task.json +122 -0
- package/dist/catalog/endpoints/update_task_scheduler.json +90 -0
- package/dist/catalog/endpoints/update_task_status.json +33 -0
- package/dist/catalog/endpoints/update_tax_rate.json +25 -0
- package/dist/catalog/endpoints/update_token.json +33 -0
- package/dist/catalog/endpoints/update_user.json +33 -0
- package/dist/catalog/endpoints/update_vendor.json +140 -0
- package/dist/catalog/endpoints/update_webhook.json +33 -0
- package/dist/catalog/endpoints/upload_client.json +54 -0
- package/dist/catalog/endpoints/upload_companies.json +47 -0
- package/dist/catalog/endpoints/upload_credits.json +47 -0
- package/dist/catalog/endpoints/upload_expense.json +47 -0
- package/dist/catalog/endpoints/upload_group_setting.json +47 -0
- package/dist/catalog/endpoints/upload_invoice_document.json +47 -0
- package/dist/catalog/endpoints/upload_payment.json +47 -0
- package/dist/catalog/endpoints/upload_product.json +46 -0
- package/dist/catalog/endpoints/upload_project.json +47 -0
- package/dist/catalog/endpoints/upload_purchase_order.json +47 -0
- package/dist/catalog/endpoints/upload_quote.json +47 -0
- package/dist/catalog/endpoints/upload_recurring_expense.json +47 -0
- package/dist/catalog/endpoints/upload_recurring_invoice.json +47 -0
- package/dist/catalog/endpoints/upload_task.json +47 -0
- package/dist/catalog/endpoints/upload_vendor.json +47 -0
- package/dist/catalog/endpoints/yodlee_refresh_webhook.json +16 -0
- package/dist/catalog/index.json +4428 -0
- package/dist/clients/invoiceninja-client.js +205 -0
- package/dist/helpers/register-endpoint-tools.js +27 -0
- package/dist/index.js +8 -0
- package/dist/server/invoiceninja-mcp-server.js +18 -0
- package/dist/tools/call-endpoint.js +153 -0
- package/dist/tools/describe-endpoint.js +60 -0
- package/dist/tools/list-endpoints.js +77 -0
- package/dist/types/tool-response.js +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { basename, join } from "node:path";
|
|
5
|
+
import { formatError } from "@nightsquawktech/mcp-core/catalog";
|
|
6
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
7
|
+
/**
|
|
8
|
+
* Thin Invoice Ninja v5 REST client. One generic `call()` drives every endpoint
|
|
9
|
+
* in the catalog (the catalog spec supplies method + path + which params exist),
|
|
10
|
+
* so the MCP layer never hard-codes routes.
|
|
11
|
+
*
|
|
12
|
+
* v5 specifics vs. the AppFolio client this is modeled on:
|
|
13
|
+
* - Auth is the `X-Api-Token` header, not HTTP Basic.
|
|
14
|
+
* - Full CRUD (GET/POST/PUT/DELETE), not just report POSTs.
|
|
15
|
+
* - Record IDs are hashed strings; list pagination lives in `meta.pagination`.
|
|
16
|
+
*/
|
|
17
|
+
export class InvoiceNinjaClient {
|
|
18
|
+
baseUrl = null;
|
|
19
|
+
apiToken = null;
|
|
20
|
+
timeoutMs = DEFAULT_TIMEOUT_MS;
|
|
21
|
+
initialized = false;
|
|
22
|
+
/** Lazily read env on first call so import-time issues don't crash startup. */
|
|
23
|
+
ensureInitialized() {
|
|
24
|
+
if (this.initialized)
|
|
25
|
+
return;
|
|
26
|
+
const baseUrl = process.env.INVOICENINJA_BASE_URL;
|
|
27
|
+
const apiToken = process.env.INVOICENINJA_API_TOKEN;
|
|
28
|
+
const timeoutRaw = process.env.INVOICENINJA_TIMEOUT_MS;
|
|
29
|
+
const missing = [];
|
|
30
|
+
if (!baseUrl)
|
|
31
|
+
missing.push("INVOICENINJA_BASE_URL");
|
|
32
|
+
if (!apiToken)
|
|
33
|
+
missing.push("INVOICENINJA_API_TOKEN");
|
|
34
|
+
if (missing.length > 0) {
|
|
35
|
+
throw new Error(`Missing required environment variables: ${missing.join(", ")}`);
|
|
36
|
+
}
|
|
37
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
38
|
+
this.apiToken = apiToken;
|
|
39
|
+
if (timeoutRaw) {
|
|
40
|
+
const t = Number.parseInt(timeoutRaw, 10);
|
|
41
|
+
if (Number.isFinite(t) && t >= 1_000)
|
|
42
|
+
this.timeoutMs = t;
|
|
43
|
+
}
|
|
44
|
+
this.initialized = true;
|
|
45
|
+
}
|
|
46
|
+
/** Substitute {placeholders} in the path template from pathParams. */
|
|
47
|
+
buildPath(spec, pathParams) {
|
|
48
|
+
return spec.path.replace(/\{([^}]+)\}/g, (_m, key) => {
|
|
49
|
+
const v = pathParams[key];
|
|
50
|
+
if (v === undefined || v === null || v === "") {
|
|
51
|
+
throw new Error(`Missing path parameter "${key}" for ${spec.slug}`);
|
|
52
|
+
}
|
|
53
|
+
return encodeURIComponent(String(v));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
buildQuery(query) {
|
|
57
|
+
const usp = new URLSearchParams();
|
|
58
|
+
for (const [k, v] of Object.entries(query)) {
|
|
59
|
+
if (v === undefined || v === null || v === "")
|
|
60
|
+
continue;
|
|
61
|
+
if (Array.isArray(v)) {
|
|
62
|
+
for (const item of v)
|
|
63
|
+
usp.append(`${k}[]`, String(item));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
usp.append(k, String(v));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const s = usp.toString();
|
|
70
|
+
return s ? `?${s}` : "";
|
|
71
|
+
}
|
|
72
|
+
/** Write a pre-mutation backup of the request (+ optional prior record). */
|
|
73
|
+
writeBackup(spec, opts, prior) {
|
|
74
|
+
const dir = mkdtempSync(join(tmpdir(), "invoiceninja-mcp-"));
|
|
75
|
+
const file = join(dir, `${spec.slug}.json`);
|
|
76
|
+
writeFileSync(file, JSON.stringify({
|
|
77
|
+
slug: spec.slug,
|
|
78
|
+
method: spec.method,
|
|
79
|
+
path: spec.path,
|
|
80
|
+
pathParams: opts.pathParams ?? {},
|
|
81
|
+
query: opts.query ?? {},
|
|
82
|
+
body: opts.body ?? {},
|
|
83
|
+
authorizationNote: opts.authorizationNote ?? null,
|
|
84
|
+
priorRecord: prior ?? null,
|
|
85
|
+
}, null, 2));
|
|
86
|
+
return file;
|
|
87
|
+
}
|
|
88
|
+
async fetchJson(url, init) {
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
91
|
+
try {
|
|
92
|
+
const res = await fetch(url, { ...init, signal: controller.signal });
|
|
93
|
+
const text = await res.text();
|
|
94
|
+
let data = null;
|
|
95
|
+
if (text) {
|
|
96
|
+
try {
|
|
97
|
+
data = JSON.parse(text);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
data = text;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return { ok: res.ok, status: res.status, data, text };
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
clearTimeout(timer);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Standard headers. For multipart uploads, omit Content-Type so fetch sets the
|
|
111
|
+
* multipart/form-data boundary (with its random delimiter) itself.
|
|
112
|
+
*/
|
|
113
|
+
headers(multipart = false) {
|
|
114
|
+
const h = {
|
|
115
|
+
"X-Api-Token": this.apiToken,
|
|
116
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
117
|
+
Accept: "application/json",
|
|
118
|
+
};
|
|
119
|
+
if (!multipart)
|
|
120
|
+
h["Content-Type"] = "application/json";
|
|
121
|
+
return h;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Execute one catalog endpoint. The caller has already validated params against
|
|
125
|
+
* the spec; this performs path substitution, the HTTP call, and (for mutating
|
|
126
|
+
* endpoints) a pre-call backup.
|
|
127
|
+
*/
|
|
128
|
+
async call(spec, opts = {}) {
|
|
129
|
+
try {
|
|
130
|
+
this.ensureInitialized();
|
|
131
|
+
const path = this.buildPath(spec, opts.pathParams ?? {});
|
|
132
|
+
const query = this.buildQuery(opts.query ?? {});
|
|
133
|
+
const url = `${this.baseUrl}${path}${query}`;
|
|
134
|
+
let backupPath;
|
|
135
|
+
if (spec.mutating) {
|
|
136
|
+
// Best-effort: capture the current record before PUT/DELETE so the change is reversible.
|
|
137
|
+
// Derive the show URL from the endpoint's own path (replace {id}) — NOT from category,
|
|
138
|
+
// which can differ from the real resource segment on singular/plural mismatches.
|
|
139
|
+
let prior = null;
|
|
140
|
+
if ((spec.method === "PUT" || spec.method === "DELETE") && /\{id\}/.test(spec.path) && opts.pathParams?.id) {
|
|
141
|
+
try {
|
|
142
|
+
const showPath = spec.path.replace("{id}", encodeURIComponent(String(opts.pathParams.id)));
|
|
143
|
+
const priorRes = await this.fetchJson(`${this.baseUrl}${showPath}`, {
|
|
144
|
+
method: "GET",
|
|
145
|
+
headers: this.headers(),
|
|
146
|
+
});
|
|
147
|
+
if (priorRes.ok)
|
|
148
|
+
prior = priorRes.data;
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
/* prior-record capture is best-effort */
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
backupPath = this.writeBackup(spec, opts, prior);
|
|
155
|
+
}
|
|
156
|
+
const isMultipart = spec.requestBody?.contentType === "multipart/form-data";
|
|
157
|
+
const init = { method: spec.method, headers: this.headers(isMultipart) };
|
|
158
|
+
if (isMultipart) {
|
|
159
|
+
// Invoice Ninja registers the upload routes as PUT but expects clients to POST with a
|
|
160
|
+
// `_method=PUT` form field (Laravel method spoofing) — a JSON body 404s with
|
|
161
|
+
// "Method not supported for this route". Build multipart/form-data instead.
|
|
162
|
+
const form = new FormData();
|
|
163
|
+
const body = (opts.body ?? {});
|
|
164
|
+
const wantsSpoof = (spec.requestBody?.fields ?? []).some((f) => f.name === "_method");
|
|
165
|
+
if (body._method !== undefined && body._method !== null) {
|
|
166
|
+
form.append("_method", String(body._method));
|
|
167
|
+
}
|
|
168
|
+
else if (wantsSpoof) {
|
|
169
|
+
form.append("_method", "PUT");
|
|
170
|
+
}
|
|
171
|
+
// Pass through any extra scalar body fields (e.g. an import's `import_type`).
|
|
172
|
+
for (const [k, v] of Object.entries(body)) {
|
|
173
|
+
if (k === "_method" || v === undefined || v === null || typeof v === "object")
|
|
174
|
+
continue;
|
|
175
|
+
form.append(k, String(v));
|
|
176
|
+
}
|
|
177
|
+
const fileField = opts.fileField ?? "documents[]";
|
|
178
|
+
for (const p of opts.files ?? []) {
|
|
179
|
+
const buf = await readFile(p);
|
|
180
|
+
// The filename (3rd arg) is mandatory — without it the part has no filename and
|
|
181
|
+
// Laravel won't register it as an uploaded file even though routing succeeds.
|
|
182
|
+
form.append(fileField, new Blob([buf]), basename(p));
|
|
183
|
+
}
|
|
184
|
+
init.body = form;
|
|
185
|
+
}
|
|
186
|
+
else if (spec.method !== "GET" && spec.method !== "DELETE" && opts.body) {
|
|
187
|
+
init.body = JSON.stringify(opts.body);
|
|
188
|
+
}
|
|
189
|
+
const res = await this.fetchJson(url, init);
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
return {
|
|
192
|
+
result: null,
|
|
193
|
+
isError: true,
|
|
194
|
+
error: `HTTP ${res.status}: ${typeof res.data === "string" ? res.data : JSON.stringify(res.data)}`,
|
|
195
|
+
backupPath,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return { result: res.data, isError: false, error: null, backupPath };
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
return { result: null, isError: true, error: formatError(error) };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export const invoiceNinjaClient = new InvoiceNinjaClient();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RegisterTool } from "@nightsquawktech/mcp-core/catalog";
|
|
2
|
+
import { listEndpointsTool } from "../tools/list-endpoints.js";
|
|
3
|
+
import { describeEndpointTool } from "../tools/describe-endpoint.js";
|
|
4
|
+
import { callEndpointTool } from "../tools/call-endpoint.js";
|
|
5
|
+
import { loadIndex } from "../catalog/endpoint-spec.js";
|
|
6
|
+
/**
|
|
7
|
+
* Registers the consolidated Invoice Ninja tools with the MCP server.
|
|
8
|
+
*
|
|
9
|
+
* Instead of one tool per endpoint (~379 tools = heavy context cost and poor tool
|
|
10
|
+
* selection), we expose three tools backed by the endpoint catalog:
|
|
11
|
+
* - list_endpoints discover endpoints by category / search / read-vs-mutating
|
|
12
|
+
* - describe_endpoint full parameter spec for one endpoint
|
|
13
|
+
* - call_endpoint validate against the catalog spec, then execute (guarded writes)
|
|
14
|
+
*
|
|
15
|
+
* Boot-time sanity check: the catalog index must load and be non-empty, so a
|
|
16
|
+
* missing/uncopied catalog fails fast at startup rather than on first tool call.
|
|
17
|
+
*/
|
|
18
|
+
export function registerAllEndpointTools(server) {
|
|
19
|
+
const index = loadIndex();
|
|
20
|
+
if (!index.endpoints || index.endpoints.length === 0) {
|
|
21
|
+
throw new Error("Endpoint catalog is empty or missing — expected src/catalog/index.json with endpoints. " +
|
|
22
|
+
"Did the build copy the catalog into dist/?");
|
|
23
|
+
}
|
|
24
|
+
RegisterTool(server, listEndpointsTool);
|
|
25
|
+
RegisterTool(server, describeEndpointTool);
|
|
26
|
+
RegisterTool(server, callEndpointTool);
|
|
27
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { InvoiceNinjaMCPServer } from "./server/invoiceninja-mcp-server.js";
|
|
4
|
+
import { registerAllEndpointTools } from "./helpers/register-endpoint-tools.js";
|
|
5
|
+
const server = InvoiceNinjaMCPServer.GetServer();
|
|
6
|
+
registerAllEndpointTools(server);
|
|
7
|
+
const transport = new StdioServerTransport();
|
|
8
|
+
await server.connect(transport);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export class InvoiceNinjaMCPServer {
|
|
3
|
+
static instance = null;
|
|
4
|
+
constructor() { }
|
|
5
|
+
static GetServer() {
|
|
6
|
+
if (InvoiceNinjaMCPServer.instance === null) {
|
|
7
|
+
InvoiceNinjaMCPServer.instance = new McpServer({
|
|
8
|
+
name: "Invoice Ninja MCP Server",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
}, {
|
|
11
|
+
capabilities: {
|
|
12
|
+
tools: {},
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return InvoiceNinjaMCPServer.instance;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { loadEndpointSpec, loadIndex, suggestSlugs } from "../catalog/endpoint-spec.js";
|
|
4
|
+
import { invoiceNinjaClient } from "../clients/invoiceninja-client.js";
|
|
5
|
+
const schema = z.object({
|
|
6
|
+
endpoint_name: z
|
|
7
|
+
.string()
|
|
8
|
+
.describe("The endpoint slug to call, e.g. 'get_invoices' or 'show_invoice'. See list_endpoints / describe_endpoint."),
|
|
9
|
+
path_params: z
|
|
10
|
+
.record(z.union([z.string(), z.number()]))
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Values for {placeholders} in the path, e.g. { id: 'D2J234DFA' }. Required ones must be present."),
|
|
13
|
+
query: z
|
|
14
|
+
.record(z.any())
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Query-string parameters keyed by name from describe_endpoint, e.g. { include: 'client', per_page: 50, " +
|
|
17
|
+
"status: 'active' }. Arrays are serialized as key[]=value."),
|
|
18
|
+
body: z
|
|
19
|
+
.record(z.any())
|
|
20
|
+
.optional()
|
|
21
|
+
.describe("Request-body object for POST/PUT (keys from describe_endpoint's requestBody.fields)."),
|
|
22
|
+
files: z
|
|
23
|
+
.array(z.string())
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Local file path(s) to upload for multipart endpoints (the upload_* endpoints, e.g. " +
|
|
26
|
+
"upload_expense / upload_invoice_document to attach a receipt). Each is sent as documents[]."),
|
|
27
|
+
file_field: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Override the multipart file field name (default 'documents[]')."),
|
|
31
|
+
authorization_confirmed: z
|
|
32
|
+
.boolean()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("REQUIRED true for mutating endpoints (POST/PUT/DELETE). Confirms the user explicitly approved this change."),
|
|
35
|
+
authorization_note: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("REQUIRED for mutating endpoints: a short note describing the user's explicit approval. Stored in the backup."),
|
|
39
|
+
});
|
|
40
|
+
function errorText(text) {
|
|
41
|
+
return { content: [{ type: "text", text }] };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Executes one Invoice Ninja endpoint after validating against the catalog spec:
|
|
45
|
+
* the endpoint must exist, required path parameters must be present, and any
|
|
46
|
+
* mutating endpoint must carry authorization_confirmed=true + authorization_note
|
|
47
|
+
* (a JSON backup is written before the call). Read-only GET endpoints run freely.
|
|
48
|
+
*/
|
|
49
|
+
export const callEndpointTool = {
|
|
50
|
+
name: "call_endpoint",
|
|
51
|
+
description: "Call an Invoice Ninja API endpoint and return its response. Validates the endpoint name and " +
|
|
52
|
+
"required path parameters against the catalog before calling. Mutating endpoints (POST/PUT/DELETE) " +
|
|
53
|
+
"require authorization_confirmed=true and an authorization_note and are backed up before execution. " +
|
|
54
|
+
"Use describe_endpoint first to learn the exact parameters.",
|
|
55
|
+
schema,
|
|
56
|
+
handler: async ({ params }) => {
|
|
57
|
+
const name = params?.endpoint_name;
|
|
58
|
+
const spec = loadEndpointSpec(name);
|
|
59
|
+
if (!spec) {
|
|
60
|
+
const suggestions = suggestSlugs(name);
|
|
61
|
+
const total = loadIndex().endpointCount;
|
|
62
|
+
return errorText(`Unknown endpoint "${name}". ` +
|
|
63
|
+
(suggestions.length ? `Did you mean: ${suggestions.join(", ")}? ` : "") +
|
|
64
|
+
`Call list_endpoints to see all ${total} endpoints.`);
|
|
65
|
+
}
|
|
66
|
+
// Required path parameters must be present.
|
|
67
|
+
const pathParams = params?.path_params ?? {};
|
|
68
|
+
const missingPath = spec.pathParameters
|
|
69
|
+
.filter((p) => p.required)
|
|
70
|
+
.map((p) => p.name)
|
|
71
|
+
.filter((n) => {
|
|
72
|
+
const v = pathParams[n];
|
|
73
|
+
return v === undefined || v === null || (typeof v === "string" && v.trim() === "");
|
|
74
|
+
});
|
|
75
|
+
// Also catch placeholders in the path that have no provided value.
|
|
76
|
+
const placeholders = [...spec.path.matchAll(/\{([^}]+)\}/g)].map((m) => m[1]);
|
|
77
|
+
for (const ph of placeholders) {
|
|
78
|
+
if (!(ph in pathParams) && !missingPath.includes(ph))
|
|
79
|
+
missingPath.push(ph);
|
|
80
|
+
}
|
|
81
|
+
if (missingPath.length > 0) {
|
|
82
|
+
return errorText(`Missing required path parameter(s) for "${spec.slug}": ${[...new Set(missingPath)].join(", ")}. ` +
|
|
83
|
+
`Call describe_endpoint("${spec.slug}") for details.`);
|
|
84
|
+
}
|
|
85
|
+
// Enforce required query parameters (rare, but honored).
|
|
86
|
+
const query = params?.query ?? {};
|
|
87
|
+
const missingQuery = spec.queryParameters
|
|
88
|
+
.filter((p) => p.required)
|
|
89
|
+
.map((p) => p.name)
|
|
90
|
+
.filter((n) => query[n] === undefined || query[n] === null || query[n] === "");
|
|
91
|
+
if (missingQuery.length > 0) {
|
|
92
|
+
return errorText(`Missing required query parameter(s) for "${spec.slug}": ${missingQuery.join(", ")}. ` +
|
|
93
|
+
`Call describe_endpoint("${spec.slug}") for details.`);
|
|
94
|
+
}
|
|
95
|
+
// Write guard for state-changing endpoints. Defense-in-depth: any {action} route
|
|
96
|
+
// (email/mark_paid/archive/delete/clone/…) is treated as mutating even if the flag
|
|
97
|
+
// were ever miscomputed, so destructive action GETs can never slip past unguarded.
|
|
98
|
+
const isMutating = spec.mutating || /\{action\}/.test(spec.path);
|
|
99
|
+
if (isMutating) {
|
|
100
|
+
const confirmed = params?.authorization_confirmed === true;
|
|
101
|
+
const note = (params?.authorization_note ?? "").toString().trim();
|
|
102
|
+
if (!confirmed || !note) {
|
|
103
|
+
return errorText(`"${spec.slug}" is a ${spec.method} (mutating) endpoint and changes data. ` +
|
|
104
|
+
`To proceed, re-call with authorization_confirmed=true and a non-empty authorization_note ` +
|
|
105
|
+
`describing the user's explicit approval. A JSON backup is written before the call.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Multipart upload endpoints (upload_*) need local file(s); a JSON body never reaches them.
|
|
109
|
+
const isMultipart = spec.requestBody?.contentType === "multipart/form-data";
|
|
110
|
+
const files = Array.isArray(params?.files) ? params.files : [];
|
|
111
|
+
if (files.length > 0 && !isMultipart) {
|
|
112
|
+
return errorText(`"${spec.slug}" does not accept file uploads (its body is ${spec.requestBody?.contentType ?? "JSON"}). ` +
|
|
113
|
+
`Remove "files" or choose a multipart upload endpoint.`);
|
|
114
|
+
}
|
|
115
|
+
if (isMultipart && files.length === 0) {
|
|
116
|
+
return errorText(`"${spec.slug}" is a multipart upload endpoint — pass file path(s) via "files", ` +
|
|
117
|
+
`e.g. files: ["C:/path/to/receipt.pdf"].`);
|
|
118
|
+
}
|
|
119
|
+
const missingFiles = files.filter((f) => !existsSync(f));
|
|
120
|
+
if (missingFiles.length > 0) {
|
|
121
|
+
return errorText(`File(s) not found: ${missingFiles.join(", ")}. Provide absolute local path(s).`);
|
|
122
|
+
}
|
|
123
|
+
const response = await invoiceNinjaClient.call(spec, {
|
|
124
|
+
pathParams,
|
|
125
|
+
query,
|
|
126
|
+
body: params?.body,
|
|
127
|
+
files: files.length ? files : undefined,
|
|
128
|
+
fileField: params?.file_field,
|
|
129
|
+
authorizationNote: params?.authorization_note,
|
|
130
|
+
});
|
|
131
|
+
if (response.isError) {
|
|
132
|
+
return errorText(`Error calling ${spec.slug} (${spec.method} ${spec.path}): ${response.error}` +
|
|
133
|
+
(response.backupPath ? ` [backup: ${response.backupPath}]` : ""));
|
|
134
|
+
}
|
|
135
|
+
const data = response.result;
|
|
136
|
+
const rowCount = Array.isArray(data?.data) ? data.data.length : undefined;
|
|
137
|
+
return {
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: "text",
|
|
141
|
+
text: JSON.stringify({
|
|
142
|
+
endpoint_name: spec.slug,
|
|
143
|
+
method: spec.method,
|
|
144
|
+
path: spec.path,
|
|
145
|
+
...(rowCount !== undefined ? { rowCount } : {}),
|
|
146
|
+
...(response.backupPath ? { backupPath: response.backupPath } : {}),
|
|
147
|
+
response: response.result,
|
|
148
|
+
}),
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { loadEndpointSpec, loadIndex, suggestSlugs } from "../catalog/endpoint-spec.js";
|
|
3
|
+
const schema = z.object({
|
|
4
|
+
endpoint_name: z
|
|
5
|
+
.string()
|
|
6
|
+
.describe("The endpoint slug, e.g. 'get_invoices', 'show_invoice', 'store_invoice'. See list_endpoints."),
|
|
7
|
+
});
|
|
8
|
+
/**
|
|
9
|
+
* Returns the full spec for one endpoint: method, path, every path/query parameter
|
|
10
|
+
* and request-body field with types, required flags, enums and descriptions, plus
|
|
11
|
+
* the mutating flag. The model calls this just before call_endpoint so it sends the
|
|
12
|
+
* exact parameter keys this endpoint expects.
|
|
13
|
+
*/
|
|
14
|
+
export const describeEndpointTool = {
|
|
15
|
+
name: "describe_endpoint",
|
|
16
|
+
description: "Get the full specification for a single Invoice Ninja endpoint: HTTP method, path, " +
|
|
17
|
+
"path/query parameters, request-body fields (types, required, enums, descriptions), and whether " +
|
|
18
|
+
"it mutates data. Call this before call_endpoint to know exactly what to pass.",
|
|
19
|
+
schema,
|
|
20
|
+
handler: async ({ params }) => {
|
|
21
|
+
const name = params?.endpoint_name;
|
|
22
|
+
const spec = loadEndpointSpec(name);
|
|
23
|
+
if (!spec) {
|
|
24
|
+
const suggestions = suggestSlugs(name);
|
|
25
|
+
const total = loadIndex().endpointCount;
|
|
26
|
+
return {
|
|
27
|
+
content: [
|
|
28
|
+
{
|
|
29
|
+
type: "text",
|
|
30
|
+
text: `Unknown endpoint "${name}". ` +
|
|
31
|
+
(suggestions.length ? `Did you mean: ${suggestions.join(", ")}? ` : "") +
|
|
32
|
+
`Call list_endpoints to see all ${total} endpoints.`,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const payload = {
|
|
38
|
+
endpoint_name: spec.slug,
|
|
39
|
+
name: spec.name,
|
|
40
|
+
method: spec.method,
|
|
41
|
+
path: spec.path,
|
|
42
|
+
category: spec.category,
|
|
43
|
+
mutating: spec.mutating,
|
|
44
|
+
description: spec.description,
|
|
45
|
+
whenToUse: spec.whenToUse,
|
|
46
|
+
pathParameters: spec.pathParameters,
|
|
47
|
+
queryParameters: spec.queryParameters,
|
|
48
|
+
headerParameters: spec.headerParameters,
|
|
49
|
+
requestBody: spec.requestBody,
|
|
50
|
+
responseSchema: spec.responseSchema,
|
|
51
|
+
...(spec.mutating
|
|
52
|
+
? {
|
|
53
|
+
mutationNotice: "This endpoint changes data. call_endpoint requires authorization_confirmed=true and an " +
|
|
54
|
+
"authorization_note, and writes a JSON backup before executing.",
|
|
55
|
+
}
|
|
56
|
+
: {}),
|
|
57
|
+
};
|
|
58
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { loadIndex } from "../catalog/endpoint-spec.js";
|
|
3
|
+
const schema = z.object({
|
|
4
|
+
category: z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.describe("Optional category filter (the API resource tag, e.g. 'invoices', 'clients', 'payments', " +
|
|
8
|
+
"'reports'). Omit to list every endpoint. Call without args first to see all categories."),
|
|
9
|
+
mutating: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Optional: true = only state-changing endpoints (POST/PUT/DELETE); false = only read-only (GET)."),
|
|
13
|
+
search: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("Optional case-insensitive substring matched against slug, name, path, and summary."),
|
|
17
|
+
});
|
|
18
|
+
/**
|
|
19
|
+
* Lightweight discovery tool. Returns slug, method, path, category, mutating flag
|
|
20
|
+
* and a one-line summary per endpoint so the model can pick one, then call
|
|
21
|
+
* describe_endpoint for its full parameter spec.
|
|
22
|
+
*/
|
|
23
|
+
export const listEndpointsTool = {
|
|
24
|
+
name: "list_endpoints",
|
|
25
|
+
description: "List Invoice Ninja API endpoints (slug, method, path, category, mutating, summary). " +
|
|
26
|
+
"Filter by category, read-vs-mutating, or a search term. Use this to discover which endpoint " +
|
|
27
|
+
"to call, then describe_endpoint for its parameters and call_endpoint to execute it.",
|
|
28
|
+
schema,
|
|
29
|
+
handler: async ({ params }) => {
|
|
30
|
+
const index = loadIndex();
|
|
31
|
+
let endpoints = index.endpoints;
|
|
32
|
+
const category = params?.category;
|
|
33
|
+
const mutating = params?.mutating;
|
|
34
|
+
const search = params?.search;
|
|
35
|
+
if (category) {
|
|
36
|
+
const wanted = category.toLowerCase();
|
|
37
|
+
endpoints = endpoints.filter((e) => e.category.toLowerCase() === wanted);
|
|
38
|
+
}
|
|
39
|
+
if (mutating !== undefined) {
|
|
40
|
+
endpoints = endpoints.filter((e) => e.mutating === mutating);
|
|
41
|
+
}
|
|
42
|
+
if (search) {
|
|
43
|
+
const q = search.toLowerCase();
|
|
44
|
+
endpoints = endpoints.filter((e) => e.slug.includes(q) ||
|
|
45
|
+
e.name.toLowerCase().includes(q) ||
|
|
46
|
+
e.path.toLowerCase().includes(q) ||
|
|
47
|
+
e.summary.toLowerCase().includes(q));
|
|
48
|
+
}
|
|
49
|
+
const payload = {
|
|
50
|
+
matched: endpoints.length,
|
|
51
|
+
totalEndpoints: index.endpointCount,
|
|
52
|
+
readCount: index.readCount,
|
|
53
|
+
mutatingCount: index.mutatingCount,
|
|
54
|
+
categories: index.categories,
|
|
55
|
+
endpoints: endpoints.map((e) => ({
|
|
56
|
+
endpoint_name: e.slug,
|
|
57
|
+
method: e.method,
|
|
58
|
+
path: e.path,
|
|
59
|
+
category: e.category,
|
|
60
|
+
mutating: e.mutating,
|
|
61
|
+
summary: e.summary,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
if ((category || search || mutating !== undefined) && endpoints.length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: `No endpoints matched. ` +
|
|
70
|
+
`Available categories: ${index.categories.map((c) => c.name).join(", ")}.`,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { content: [{ type: "text", text: JSON.stringify(payload) }] };
|
|
76
|
+
},
|
|
77
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nightsquawktech/invoiceninja-mcp-server",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MCP server for Invoice Ninja v5 (self-hosted): generic reads + report catalog, guarded writes.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": "./dist/index.js",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE",
|
|
12
|
+
"COMMERCIAL.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc && shx mkdir -p dist/catalog && shx cp -r src/catalog/endpoints dist/catalog && shx cp src/catalog/index.json dist/catalog",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"dev": "tsx src/index.ts",
|
|
18
|
+
"generate": "node scripts/generate-catalog.mjs",
|
|
19
|
+
"catalog": "node scripts/assemble-catalog.mjs",
|
|
20
|
+
"smoke": "node scripts/smoke-test.mjs",
|
|
21
|
+
"clean": "rimraf dist"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=20"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
28
|
+
"@nightsquawktech/mcp-core": "^0.1.0",
|
|
29
|
+
"zod": "^3.25.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.0.0",
|
|
33
|
+
"rimraf": "^6.0.1",
|
|
34
|
+
"shx": "^0.3.4",
|
|
35
|
+
"tsx": "^4.20.0",
|
|
36
|
+
"typescript": "^5.9.0",
|
|
37
|
+
"yaml": "^2.6.1"
|
|
38
|
+
},
|
|
39
|
+
"license": "AGPL-3.0-only",
|
|
40
|
+
"author": "Kevin Reyes (NightSquawk Tech) <hello@nightsquawk.tech>",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/NightSquawk/invoiceninja-mcp-server.git"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/NightSquawk/invoiceninja-mcp-server#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/NightSquawk/invoiceninja-mcp-server/issues"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
}
|
|
52
|
+
}
|