agencyhandy-mcp
v1.7.2
Published
Official Agency Handy MCP server — owner insights, context packs, API builder context
Maintainers
Readme
Agency Handy MCP
Official Model Context Protocol server for Agency Handy.
Connect Claude Desktop, Cursor, or any MCP host to your Agency Handy workspace: inspect proposals, projects, tickets, invoices, and create leads & clients — using your existing workspace API key.
| | |
|---|---|
| npm | agencyhandy-mcp (publish after this repo is set up) |
| Node | ≥ 20 |
| Auth | Workspace API key (x-api-key) from Agency Handy |
| API | Your Agency Handy backend (AGENCY_BACKEND_URL) |
Quick start (Agency Handy users)
- In Agency Handy go to Workspace Config → API Key
- Generate a key (or use one you already saved)
- Copy MCP config from that page (or use the JSON below)
- Paste into:
- Cursor: project or user
.mcp.json - Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)
- Cursor: project or user
- Restart the host. Requires Node.js 20+.
{
"mcpServers": {
"agency-handy": {
"command": "npx",
"args": ["-y", "agencyhandy-mcp@1"],
"env": {
"NODE_ENV": "production",
"AGENCY_BACKEND_URL": "https://api.agencyhandy.com",
"AGENCY_BACKEND_API_KEY": "YOUR_API_KEY"
}
}
}
}Prefer the in-app config: it fills the correct API base URL for your environment (production, staging, etc.).
Until the package is on npm, developers can run from this repo:
{
"mcpServers": {
"agency-handy": {
"command": "node",
"args": ["/absolute/path/to/agency-backend-mcp/src/server.js"],
"env": {
"AGENCY_BACKEND_URL": "https://api.agencyhandy.com",
"AGENCY_BACKEND_API_KEY": "YOUR_API_KEY"
}
}
}
}What you can do
Product naming → API
| In Agency Handy | Backend |
|-----------------|---------|
| Proposal | Quotations |
| Project | Orders (pid) |
| Ticket | Tasks with type=issue |
| Task | Tasks with type=task |
Write tools
| Tool | Purpose |
|------|---------|
| ah_lead_create | Create lead(s) — POST /api/v1/members/bulk-lead |
| ah_client_create | Create client(s) — POST /api/v1/members/bulk-client |
| ah_client_company_create | Create client company — POST /api/v1/client-companies |
| ah_member_update | Update lead/client — PUT /api/v1/accounts/update |
| ah_lead_convert | Convert lead → client — PUT /api/v1/accounts/update |
| ah_client_invite | Send portal invite — POST /api/v1/members/send-client-invite |
| ah_custom_field_create | Create lead/client/task field definition — POST /api/v1/custom-fields |
| ah_custom_field_set_values | Fill values on a member — POST /api/v1/custom-fields/respond |
| ah_custom_field_delete | Delete definitions — DELETE /api/v1/custom-fields |
| ah_ticket_create / ah_ticket_update | Ticket (type=issue) — POST/PUT /api/v1/tasks |
| ah_task_create / ah_task_update | Task (type=task) — POST/PUT /api/v1/tasks |
| ah_comment_create | Comment on ticket/task — POST /api/v1/comments |
| ah_invoice_create / ah_invoice_update | Invoice — POST/PUT /api/v1/invoices |
| ah_proposal_create / ah_proposal_update | Proposal — POST/PUT /api/v1/quotations |
| ah_proposal_send | Send proposal — PUT /api/v1/quotations/send/:id |
| ah_order_update | Update order — PUT /api/v1/orders?pid= |
| ah_order_create | Create order from service package — POST /api/v1/orders/create |
| ah_order_assign | Set order PMs — PUT /orders?pid= |
| ah_invoice_send | Send/resend invoice email — open status or /invoices/resend |
| ah_invoice_set_status | Mark paid/void/open/… — PUT /invoices/:id |
| ah_work_assign | Add/remove ticket or task assignees |
| ah_work_set_status | Move ticket/task board status |
Owner digest (read)
| Tool | Purpose |
|------|---------|
| ah_dashboard_digest | Morning briefing: open invoices, overdue work, new leads + dashboard |
| ah_cash_risk | Open invoice aging buckets + top clients by amount due |
| ah_proposal_pipeline | Waiting / stale / expiring proposals |
| ah_lead_segments | Lead counts by status, source, optional custom field |
| ah_client_churn_risk | Heuristic churn score (overdue + quiet orders) |
| ah_member_resolve | Name/email → DB memberId (unique / ambiguous / none) — never invent ids |
| ah_status_resolve | Status label → DB statusId — required before ah_work_set_status |
| ah_custom_field_list | Workspace custom field definitions (lead/client/task) |
| ah_custom_field_seed_types | Platform seed types (usually auto-resolved by create) |
| ah_member_custom_fields | Member profile + custom field values |
| ah_task_context / ah_ticket_context | Work item + who to assign (ranked candidates, order team/PM, workload) |
| ah_order_context / ah_project_context | Order pack: tasks, tickets, files, related invoices/subscriptions |
| ah_proposal_context | Proposal summary + comments + public + linked service |
| ah_invoice_context | Invoice summary + public + linked order + related invoices |
Resolve rule: If ambiguous or none, ask the user. Only assign/update with an id returned from these tools (or a user-confirmed match).
Assigning work
ah_task_context/ah_ticket_context(passpidwhen known)- Read
assignment.assignmentCandidates+assignment.advice ah_work_assignwith a realmemberIdfrom candidates (orah_member_resolveif the user named someone)
Owner insights
| Ask | Tool |
|---|---|
| Top AR clients + past 30/60/90 — who to chase today | ah_cash_risk → then ah_invoice_context / send |
| Stale or expiring proposals — bump vs write off | ah_proposal_pipeline → ah_proposal_context |
| Lead mix by source / Industry — volume vs converting | ah_lead_segments (customFieldName) |
| Quiet + overdue clients — watchlist & one next action | ah_client_churn_risk → order/invoice context |
| Monday pulse: cash + cold proposals + slipping work | ah_dashboard_digest + the tools above |
Custom fields
Lead and client custom fields are separate catalogs (same display name may exist once per owner).
ah_custom_field_list(type=leadorclient) → use returned definition_id(never invent ids)- Fill with
ah_custom_field_set_values, or passcustomFieldsonah_lead_create/ah_client_create - Updates:
ah_member_custom_fieldsfirst; prefer an existing filled-value row id when present - Admins can create definitions with
ah_custom_field_create(name + type; options required for selects) - Duplicates: creating a field whose name already exists for that owner (case/spacing ignored) returns
notCreatedCustomFieldswith reasonField already exists— reuse the existing id instead
Lead example
{
"email": "[email protected]",
"firstName": "Ada",
"lastName": "Lovelace",
"status": "New",
"sendInvitationEmail": false
}Client example
{
"email": "[email protected]",
"firstName": "Ada",
"sendInvitationEmail": false
}Client company example
{
"name": "Acme Inc",
"ownerId": "<existing-member-id>",
"city": "Dhaka"
}Ticket example
{
"name": "Portal login broken",
"priority": "high",
"labels": [],
"client": "<client-member-id>",
"visibility": "client"
}Invoice example
{
"currency": "usd",
"client": "<client-member-id>",
"amount": 500,
"additionalFields": [{ "name": "Design", "quantity": 1, "value": 500 }]
}Order create is intentionally omitted (multipart / service-package heavy). Use ah_order_update for existing orders.
Read tools (highlights)
- Health:
ah_ping,ah_health - Context packs:
ah_proposal_context,ah_project_context,ah_ticket_context,ah_task_context,ah_invoice_context,ah_chat_context,ah_webhook_context - Lists / gets: proposals, projects, tickets, tasks, invoices, members, client companies, services, forms, dashboard
- Platform: chat, email-to-ticket settings, webhooks, order & platform subscriptions
Prefer *_context tools and the ah://full-context resource before inventing ObjectIds. For custom API tooling, also read ah://api-context (leads/orders/tasks/comments/labels/clients/invoices/vouchers/forms gotchas).
Resources & prompts
Resources: ah://full-context, ah://glossary, ah://models, ah://fields, ah://workflows, ah://workflows-platform, ah://api-context, ah://usage, …
Prompts: agency_handy_orientation, investigate_ticket, investigate_project, investigate_proposal, investigate_task
Environment
| Variable | Required | Description |
|----------|----------|-------------|
| AGENCY_BACKEND_URL | yes | API origin, e.g. https://api.agencyhandy.com (no /api/v1 suffix) |
| AGENCY_BACKEND_API_KEY | yes | Member API JWT from Workspace Config → API Key |
| AGENCY_COMPANY_ID | no | Override; normally taken from the JWT company claim |
| AGENCY_WORKSPACE_ID | no | Optional workspace hint for tools that accept it |
| AGENCY_BACKEND_TIMEOUT_MS | no | Default 45000 |
| AGENCY_MCP_MAX_RPM | no | Tool rate limit per process (default 120) |
| AGENCY_MCP_STRICT_API_KEY | no | 1 to require JWT exp; on by default when NODE_ENV=production |
| NODE_ENV | no | Set production for hosts |
Copy .env.example → .env for local development.
Develop
git clone https://github.com/AgencyHandy/agency-backend-mcp.git
cd agency-backend-mcp
npm ci
cp .env.example .env # fill URL + API key
npm start # stdio MCP server
npm test # offline smoke / production gate
npm run test:live # strict live calls (backend must be reachable)CI
GitHub Actions runs offline smoke on Node 20 (.github/workflows/ci.yml).
Publish to npm
npm ci
npm test
npm publish --access publicSee PRODUCTION.md for the operator checklist.
Security
- The API key is a full workspace credential — treat it like a password
- Never commit
.envor paste keys into public chats - Rotate keys in Agency Handy if leaked
- MCP is stdio / local-trust; do not expose it on a public network without extra auth
- Allowed writes are limited to lead / client / client-company create
License
UNLICENSED — Agency Handy proprietary.
