agencyhandy-mcp 1.4.0
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/.env.example +13 -0
- package/PRODUCTION.md +31 -0
- package/README.md +239 -0
- package/package.json +40 -0
- package/src/lib.js +430 -0
- package/src/platformTools.js +479 -0
- package/src/prompts.js +170 -0
- package/src/resolve.js +216 -0
- package/src/resources.js +833 -0
- package/src/server.js +180 -0
- package/src/tools.js +1689 -0
- package/src/writeTools.js +1628 -0
package/.env.example
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Production MCP — copy to .env and fill from Agency Handy UI
|
|
2
|
+
# Settings → API tokens (member API key / x-api-key JWT)
|
|
3
|
+
|
|
4
|
+
AGENCY_BACKEND_URL=https://api.agencyhandy.com
|
|
5
|
+
AGENCY_BACKEND_API_KEY=
|
|
6
|
+
|
|
7
|
+
# Optional overrides
|
|
8
|
+
# AGENCY_COMPANY_ID= # normally decoded from API key JWT
|
|
9
|
+
# AGENCY_WORKSPACE_ID=
|
|
10
|
+
# AGENCY_BACKEND_TIMEOUT_MS=45000
|
|
11
|
+
# AGENCY_MCP_MAX_RPM=120 # in-process tool call rate limit
|
|
12
|
+
# AGENCY_MCP_STRICT_API_KEY=1 # require JWT exp (default on when NODE_ENV=production)
|
|
13
|
+
# NODE_ENV=production
|
package/PRODUCTION.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Production checklist — Agency Handy MCP
|
|
2
|
+
|
|
3
|
+
npm package name: **`agencyhandy-mcp`**
|
|
4
|
+
Repo: https://github.com/AgencyHandy/agency-backend-mcp
|
|
5
|
+
|
|
6
|
+
## End-user flow
|
|
7
|
+
|
|
8
|
+
1. Agency Handy → **Workspace Config → API Key** → Generate
|
|
9
|
+
2. Copy MCP config (key filled in once)
|
|
10
|
+
3. Paste into Cursor `.mcp.json` or Claude Desktop config
|
|
11
|
+
4. Restart host (Node.js 20+)
|
|
12
|
+
|
|
13
|
+
## Ship checklist
|
|
14
|
+
|
|
15
|
+
1. Push this repo to GitHub
|
|
16
|
+
2. `npm publish --access public` (so `npx -y agencyhandy-mcp@1` works)
|
|
17
|
+
3. Merge/deploy ah-client PR that shows the MCP copy UI on the API Key tab
|
|
18
|
+
|
|
19
|
+
## Runtime guarantees
|
|
20
|
+
|
|
21
|
+
- Fail fast on missing / placeholder / expired API keys
|
|
22
|
+
- Stderr JSON audit logs (stdout = MCP protocol only)
|
|
23
|
+
- Secret redaction, rate limit (120 RPM), one retry on 502/503
|
|
24
|
+
- `companyid` from JWT; `ah_health` probes backend
|
|
25
|
+
- Writes allowlisted: CRM, work assign/status, invoices (+send), proposals, orders (create/update/assign)
|
|
26
|
+
- Owner digest: `ah_dashboard_digest`
|
|
27
|
+
- Resolve-only ids: `ah_member_resolve`, `ah_status_resolve` (no invented ObjectIds)
|
|
28
|
+
|
|
29
|
+
## Version
|
|
30
|
+
|
|
31
|
+
`1.4.0` — DB-backed member/status resolve; assign/status writes require real ids.
|
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Agency Handy MCP
|
|
2
|
+
|
|
3
|
+
Official [Model Context Protocol](https://modelcontextprotocol.io) server for **Agency Handy**.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
| | |
|
|
8
|
+
|---|---|
|
|
9
|
+
| **npm** | `agencyhandy-mcp` (publish after this repo is set up) |
|
|
10
|
+
| **Node** | ≥ 20 |
|
|
11
|
+
| **Auth** | Workspace API key (`x-api-key`) from Agency Handy |
|
|
12
|
+
| **API** | Your Agency Handy backend (`AGENCY_BACKEND_URL`) |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Quick start (Agency Handy users)
|
|
17
|
+
|
|
18
|
+
1. In Agency Handy go to **Workspace Config → API Key**
|
|
19
|
+
2. **Generate** a key (or use one you already saved)
|
|
20
|
+
3. **Copy MCP config** from that page (or use the JSON below)
|
|
21
|
+
4. Paste into:
|
|
22
|
+
- **Cursor:** project or user `.mcp.json`
|
|
23
|
+
- **Claude Desktop:** `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
24
|
+
5. Restart the host. Requires **Node.js 20+**.
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"agency-handy": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["-y", "agencyhandy-mcp@1"],
|
|
32
|
+
"env": {
|
|
33
|
+
"NODE_ENV": "production",
|
|
34
|
+
"AGENCY_BACKEND_URL": "https://api.agencyhandy.com",
|
|
35
|
+
"AGENCY_BACKEND_API_KEY": "YOUR_API_KEY"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
> Prefer the in-app config: it fills the correct API base URL for your environment (production, staging, etc.).
|
|
43
|
+
|
|
44
|
+
Until the package is on npm, developers can run from this repo:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"mcpServers": {
|
|
49
|
+
"agency-handy": {
|
|
50
|
+
"command": "node",
|
|
51
|
+
"args": ["/absolute/path/to/agency-backend-mcp/src/server.js"],
|
|
52
|
+
"env": {
|
|
53
|
+
"AGENCY_BACKEND_URL": "https://api.agencyhandy.com",
|
|
54
|
+
"AGENCY_BACKEND_API_KEY": "YOUR_API_KEY"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## What you can do
|
|
64
|
+
|
|
65
|
+
### Product naming → API
|
|
66
|
+
|
|
67
|
+
| In Agency Handy | Backend |
|
|
68
|
+
|-----------------|---------|
|
|
69
|
+
| Proposal | Quotations |
|
|
70
|
+
| Project | Orders (`pid`) |
|
|
71
|
+
| Ticket | Tasks with `type=issue` |
|
|
72
|
+
| Task | Tasks with `type=task` |
|
|
73
|
+
|
|
74
|
+
### Write tools
|
|
75
|
+
|
|
76
|
+
| Tool | Purpose |
|
|
77
|
+
|------|---------|
|
|
78
|
+
| `ah_lead_create` | Create lead(s) — `POST /api/v1/members/bulk-lead` |
|
|
79
|
+
| `ah_client_create` | Create client(s) — `POST /api/v1/members/bulk-client` |
|
|
80
|
+
| `ah_client_company_create` | Create client company — `POST /api/v1/client-companies` |
|
|
81
|
+
| `ah_member_update` | Update lead/client — `PUT /api/v1/accounts/update` |
|
|
82
|
+
| `ah_lead_convert` | Convert lead → client — `PUT /api/v1/accounts/update` |
|
|
83
|
+
| `ah_client_invite` | Send portal invite — `POST /api/v1/members/send-client-invite` |
|
|
84
|
+
| `ah_ticket_create` / `ah_ticket_update` | Ticket (`type=issue`) — `POST/PUT /api/v1/tasks` |
|
|
85
|
+
| `ah_task_create` / `ah_task_update` | Task (`type=task`) — `POST/PUT /api/v1/tasks` |
|
|
86
|
+
| `ah_comment_create` | Comment on ticket/task — `POST /api/v1/comments` |
|
|
87
|
+
| `ah_invoice_create` / `ah_invoice_update` | Invoice — `POST/PUT /api/v1/invoices` |
|
|
88
|
+
| `ah_proposal_create` / `ah_proposal_update` | Proposal — `POST/PUT /api/v1/quotations` |
|
|
89
|
+
| `ah_proposal_send` | Send proposal — `PUT /api/v1/quotations/send/:id` |
|
|
90
|
+
| `ah_order_update` | Update order — `PUT /api/v1/orders?pid=` |
|
|
91
|
+
| `ah_order_create` | Create order from service package — `POST /api/v1/orders/create` |
|
|
92
|
+
| `ah_order_assign` | Set order PMs — `PUT /orders?pid=` |
|
|
93
|
+
| `ah_invoice_send` | Send/resend invoice email — open status or `/invoices/resend` |
|
|
94
|
+
| `ah_invoice_set_status` | Mark paid/void/open/… — `PUT /invoices/:id` |
|
|
95
|
+
| `ah_work_assign` | Add/remove ticket or task assignees |
|
|
96
|
+
| `ah_work_set_status` | Move ticket/task board status |
|
|
97
|
+
|
|
98
|
+
**Owner digest (read)**
|
|
99
|
+
|
|
100
|
+
| Tool | Purpose |
|
|
101
|
+
|------|---------|
|
|
102
|
+
| `ah_dashboard_digest` | Morning briefing: open invoices, overdue work, new leads + dashboard |
|
|
103
|
+
| `ah_member_resolve` | Name/email → DB memberId (`unique` / `ambiguous` / `none`) — never invent ids |
|
|
104
|
+
| `ah_status_resolve` | Status label → DB statusId — required before `ah_work_set_status` |
|
|
105
|
+
|
|
106
|
+
**Resolve rule:** If `ambiguous` or `none`, ask the user. Only assign/update with an id returned from these tools (or a user-confirmed match).
|
|
107
|
+
|
|
108
|
+
**Lead example**
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"email": "lead@acme.com",
|
|
113
|
+
"firstName": "Ada",
|
|
114
|
+
"lastName": "Lovelace",
|
|
115
|
+
"status": "New",
|
|
116
|
+
"sendInvitationEmail": false
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Client example**
|
|
121
|
+
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"email": "client@acme.com",
|
|
125
|
+
"firstName": "Ada",
|
|
126
|
+
"sendInvitationEmail": false
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Client company example**
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"name": "Acme Inc",
|
|
135
|
+
"ownerId": "<existing-member-id>",
|
|
136
|
+
"city": "Dhaka"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Ticket example**
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"name": "Portal login broken",
|
|
145
|
+
"priority": "high",
|
|
146
|
+
"labels": [],
|
|
147
|
+
"client": "<client-member-id>",
|
|
148
|
+
"visibility": "client"
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Invoice example**
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"currency": "usd",
|
|
157
|
+
"client": "<client-member-id>",
|
|
158
|
+
"amount": 500,
|
|
159
|
+
"additionalFields": [{ "name": "Design", "quantity": 1, "value": 500 }]
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Order create is intentionally omitted (multipart / service-package heavy). Use `ah_order_update` for existing orders.
|
|
164
|
+
|
|
165
|
+
### Read tools (highlights)
|
|
166
|
+
|
|
167
|
+
- **Health:** `ah_ping`, `ah_health`
|
|
168
|
+
- **Context packs:** `ah_proposal_context`, `ah_project_context`, `ah_ticket_context`, `ah_task_context`, `ah_invoice_context`, `ah_chat_context`, `ah_webhook_context`
|
|
169
|
+
- **Lists / gets:** proposals, projects, tickets, tasks, invoices, members, client companies, services, forms, dashboard
|
|
170
|
+
- **Platform:** chat, email-to-ticket settings, webhooks, order & platform subscriptions
|
|
171
|
+
|
|
172
|
+
Prefer `*_context` tools and the `ah://full-context` resource before inventing ObjectIds.
|
|
173
|
+
|
|
174
|
+
### Resources & prompts
|
|
175
|
+
|
|
176
|
+
Resources: `ah://full-context`, `ah://glossary`, `ah://models`, `ah://fields`, `ah://workflows`, `ah://workflows-platform`, `ah://usage`, …
|
|
177
|
+
|
|
178
|
+
Prompts: `agency_handy_orientation`, `investigate_ticket`, `investigate_project`, `investigate_proposal`, `investigate_task`
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Environment
|
|
183
|
+
|
|
184
|
+
| Variable | Required | Description |
|
|
185
|
+
|----------|----------|-------------|
|
|
186
|
+
| `AGENCY_BACKEND_URL` | yes | API origin, e.g. `https://api.agencyhandy.com` (no `/api/v1` suffix) |
|
|
187
|
+
| `AGENCY_BACKEND_API_KEY` | yes | Member API JWT from Workspace Config → API Key |
|
|
188
|
+
| `AGENCY_COMPANY_ID` | no | Override; normally taken from the JWT `company` claim |
|
|
189
|
+
| `AGENCY_WORKSPACE_ID` | no | Optional workspace hint for tools that accept it |
|
|
190
|
+
| `AGENCY_BACKEND_TIMEOUT_MS` | no | Default `45000` |
|
|
191
|
+
| `AGENCY_MCP_MAX_RPM` | no | Tool rate limit per process (default `120`) |
|
|
192
|
+
| `AGENCY_MCP_STRICT_API_KEY` | no | `1` to require JWT `exp`; on by default when `NODE_ENV=production` |
|
|
193
|
+
| `NODE_ENV` | no | Set `production` for hosts |
|
|
194
|
+
|
|
195
|
+
Copy `.env.example` → `.env` for local development.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Develop
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git clone https://github.com/AgencyHandy/agency-backend-mcp.git
|
|
203
|
+
cd agency-backend-mcp
|
|
204
|
+
npm ci
|
|
205
|
+
cp .env.example .env # fill URL + API key
|
|
206
|
+
npm start # stdio MCP server
|
|
207
|
+
npm test # offline smoke / production gate
|
|
208
|
+
npm run test:live # strict live calls (backend must be reachable)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### CI
|
|
212
|
+
|
|
213
|
+
GitHub Actions runs offline smoke on Node 20 (`.github/workflows/ci.yml`).
|
|
214
|
+
|
|
215
|
+
### Publish to npm
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
npm ci
|
|
219
|
+
npm test
|
|
220
|
+
npm publish --access public
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
See [PRODUCTION.md](./PRODUCTION.md) for the operator checklist.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Security
|
|
228
|
+
|
|
229
|
+
- The API key is a **full workspace credential** — treat it like a password
|
|
230
|
+
- Never commit `.env` or paste keys into public chats
|
|
231
|
+
- Rotate keys in Agency Handy if leaked
|
|
232
|
+
- MCP is **stdio / local-trust**; do not expose it on a public network without extra auth
|
|
233
|
+
- Allowed writes are limited to lead / client / client-company create
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
UNLICENSED — Agency Handy proprietary.
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agencyhandy-mcp",
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "Official Agency Handy MCP server — DB-backed resolve + owner ops (no invented ids)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/server.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agencyhandy-mcp": "src/server.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=20"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"README.md",
|
|
16
|
+
"PRODUCTION.md",
|
|
17
|
+
".env.example"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"start": "node src/server.js",
|
|
21
|
+
"dev": "node --watch src/server.js",
|
|
22
|
+
"test": "node scripts/smoke.mjs",
|
|
23
|
+
"test:live": "SMOKE_LIVE=1 node scripts/smoke.mjs",
|
|
24
|
+
"prepublishOnly": "npm test"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"agencyhandy",
|
|
28
|
+
"mcp",
|
|
29
|
+
"claude",
|
|
30
|
+
"cursor",
|
|
31
|
+
"model-context-protocol"
|
|
32
|
+
],
|
|
33
|
+
"license": "UNLICENSED",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.12.3",
|
|
36
|
+
"axios": "^1.9.0",
|
|
37
|
+
"dotenv": "^16.5.0",
|
|
38
|
+
"zod": "^3.24.4"
|
|
39
|
+
}
|
|
40
|
+
}
|