agencyhandy-mcp 1.7.1 → 1.7.2
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/PRODUCTION.md +1 -0
- package/README.md +2 -2
- package/package.json +4 -3
- package/src/lib.js +1 -1
- package/src/prompts.js +3 -2
- package/src/resources.js +140 -5
- package/src/tools.js +1 -0
package/PRODUCTION.md
CHANGED
|
@@ -28,5 +28,6 @@ Repo: https://github.com/AgencyHandy/agency-backend-mcp
|
|
|
28
28
|
|
|
29
29
|
## Version
|
|
30
30
|
|
|
31
|
+
`1.7.2` — API builder context resource (`ah://api-context`): leads/orders/tasks/comments/labels/clients/invoices/vouchers/forms gotchas + unit/mutation tests.
|
|
31
32
|
`1.7.1` — Security harden (SSRF path/url guards, axios/deps audit clean) + mutation/npx entry tests; owner insights tolerate null/sparse API rows.
|
|
32
33
|
`1.7.0` — Owner insights: cash risk, proposal pipeline aging, lead segments, client churn heuristics; plus advanced context packs.
|
package/README.md
CHANGED
|
@@ -209,11 +209,11 @@ Order create is intentionally omitted (multipart / service-package heavy). Use `
|
|
|
209
209
|
- **Lists / gets:** proposals, projects, tickets, tasks, invoices, members, client companies, services, forms, dashboard
|
|
210
210
|
- **Platform:** chat, email-to-ticket settings, webhooks, order & platform subscriptions
|
|
211
211
|
|
|
212
|
-
Prefer `*_context` tools and the `ah://full-context` resource before inventing ObjectIds.
|
|
212
|
+
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).
|
|
213
213
|
|
|
214
214
|
### Resources & prompts
|
|
215
215
|
|
|
216
|
-
Resources: `ah://full-context`, `ah://glossary`, `ah://models`, `ah://fields`, `ah://workflows`, `ah://workflows-platform`, `ah://usage`, …
|
|
216
|
+
Resources: `ah://full-context`, `ah://glossary`, `ah://models`, `ah://fields`, `ah://workflows`, `ah://workflows-platform`, `ah://api-context`, `ah://usage`, …
|
|
217
217
|
|
|
218
218
|
Prompts: `agency_handy_orientation`, `investigate_ticket`, `investigate_project`, `investigate_proposal`, `investigate_task`
|
|
219
219
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agencyhandy-mcp",
|
|
3
|
-
"version": "1.7.
|
|
4
|
-
"description": "Official Agency Handy MCP server — owner insights
|
|
3
|
+
"version": "1.7.2",
|
|
4
|
+
"description": "Official Agency Handy MCP server — owner insights, context packs, API builder context",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/server.js",
|
|
7
7
|
"bin": {
|
|
@@ -19,8 +19,9 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"start": "node src/server.js",
|
|
21
21
|
"dev": "node --watch src/server.js",
|
|
22
|
-
"test": "node scripts/smoke.mjs && node scripts/unit-custom-fields.mjs && node scripts/unit-work-context.mjs && node scripts/unit-owner-insights.mjs && node scripts/unit-security-mutate.mjs",
|
|
22
|
+
"test": "node scripts/smoke.mjs && node scripts/unit-api-context.mjs && node scripts/unit-custom-fields.mjs && node scripts/unit-work-context.mjs && node scripts/unit-owner-insights.mjs && node scripts/unit-security-mutate.mjs",
|
|
23
23
|
"test:security": "node scripts/unit-security-mutate.mjs",
|
|
24
|
+
"test:api-context": "node scripts/unit-api-context.mjs",
|
|
24
25
|
"test:live": "SMOKE_LIVE=1 node scripts/smoke.mjs",
|
|
25
26
|
"test:live:custom-fields": "node scripts/live-custom-fields.mjs",
|
|
26
27
|
"prepublishOnly": "npm test && npm audit --audit-level=high"
|
package/src/lib.js
CHANGED
package/src/prompts.js
CHANGED
|
@@ -76,12 +76,13 @@ export function getPrompt(name, args = {}) {
|
|
|
76
76
|
type: "text",
|
|
77
77
|
text: `Read MCP resource ah://full-context (preferred single read).
|
|
78
78
|
If the host cannot load the bundle, read in order:
|
|
79
|
-
ah://glossary → ah://models → ah://fields → ah://workflows → ah://workflows-platform → ah://usage → ah://tool-guide → ah://response-shape
|
|
79
|
+
ah://glossary → ah://models → ah://fields → ah://workflows → ah://workflows-platform → ah://api-context → ah://usage → ah://tool-guide → ah://response-shape
|
|
80
80
|
|
|
81
81
|
Then confirm:
|
|
82
82
|
- proposal=quotation, project=order(pid), ticket=task type=issue
|
|
83
|
+
- leads live under /members (bulk-lead, isConvertedClient=false); never PUT /accounts/update without accountId
|
|
83
84
|
- Core delivery workflows (A–O) plus platform/ops (P–CC): auth, email-to-ticket, chat, webhooks, subscriptions
|
|
84
|
-
- Field meanings from ah://fields
|
|
85
|
+
- Field meanings from ah://fields; path/permission gotchas from ah://api-context
|
|
85
86
|
- Prefer *_context tools; never invent ObjectIds
|
|
86
87
|
- People by name: ah_member_resolve first (unique|ambiguous|none). If ambiguous, ask user. Never guess.
|
|
87
88
|
- Status by label: ah_status_resolve first. Same rule.
|
package/src/resources.js
CHANGED
|
@@ -9,7 +9,7 @@ export const RESOURCES = [
|
|
|
9
9
|
name: "Full Agency Handy MCP context (bundle)",
|
|
10
10
|
mimeType: "text/markdown",
|
|
11
11
|
description:
|
|
12
|
-
"Single bundle: glossary, models, fields, product + platform workflows, usage, tools.",
|
|
12
|
+
"Single bundle: glossary, models, fields, product + platform workflows, API builder context, usage, tools.",
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
uri: "ah://glossary",
|
|
@@ -68,6 +68,13 @@ export const RESOURCES = [
|
|
|
68
68
|
mimeType: "text/markdown",
|
|
69
69
|
description: "How to interpret ok/status/data from ah_* tools.",
|
|
70
70
|
},
|
|
71
|
+
{
|
|
72
|
+
uri: "ah://api-context",
|
|
73
|
+
name: "API builder context (gotchas)",
|
|
74
|
+
mimeType: "text/markdown",
|
|
75
|
+
description:
|
|
76
|
+
"Missing/ambiguous API context for MCP builders: leads, orders, tasks, comments, invoices, forms, webhooks, permissions.",
|
|
77
|
+
},
|
|
71
78
|
];
|
|
72
79
|
|
|
73
80
|
function md(uri, text) {
|
|
@@ -88,6 +95,7 @@ const GLOSSARY = `# Agency Handy glossary
|
|
|
88
95
|
| Client company | ClientCompany | \`/api/v1/client-companies\` | B2B org |
|
|
89
96
|
| Form | Form / PublicForm | \`/forms\`, \`/public-forms\` | Intake → lead/order |
|
|
90
97
|
| Comment | Comment / QuotationComment | \`/comments\`, \`/quotation-comments\` | Different namespaces |
|
|
98
|
+
| Lead | Member (client role, not converted) | \`/members\` + \`bulk-lead\` | **No** \`/leads\` mount — see \`ah://api-context\` |
|
|
91
99
|
| Time entry | TimeTracker | \`/time-trackers\` | \`taskId\` / \`projectId\` |
|
|
92
100
|
| Task status | TaskStatus | \`/taskStatus\` | Board lane ObjectIds |
|
|
93
101
|
| Company | Company | \`/company\` | Workspace tenant |
|
|
@@ -96,6 +104,7 @@ const GLOSSARY = `# Agency Handy glossary
|
|
|
96
104
|
| Dashboard | Dashboard | \`/dashboards\` | Aggregates |
|
|
97
105
|
|
|
98
106
|
Auth: \`x-api-key\` (member API token). Optional \`AGENCY_WORKSPACE_ID\`.
|
|
107
|
+
Read **\`ah://api-context\`** for path renames, permission names, and write-side gotchas.
|
|
99
108
|
`;
|
|
100
109
|
|
|
101
110
|
const MODELS = `# Domain models & relationships
|
|
@@ -134,6 +143,7 @@ Company (workspace)
|
|
|
134
143
|
- **Ticket vs Task**: same collection; filter/list with \`type=issue\` vs \`type=task\`.
|
|
135
144
|
- **Project vs Order**: UI "Project" = API \`orders\` = tool \`pid\`.
|
|
136
145
|
- **Proposal vs Quotation**: UI "Proposal" = API \`quotations\`.
|
|
146
|
+
- **Lead vs Client vs Team**: same \`Member\` collection — lead = client role + \`isConvertedClient=false\`; client = converted; team = non-client role.
|
|
137
147
|
- **Human ids**: \`taskId\`, order \`id\`/\`customId\`/\`prefix\` are display ids — MCP tools need Mongo \`_id\`.
|
|
138
148
|
|
|
139
149
|
## ID args for tools
|
|
@@ -679,7 +689,7 @@ If only tools: \`ah_ping\` → use \`forAnyLlm\`; still call \`ah_*_context\`.
|
|
|
679
689
|
|
|
680
690
|
## Startup (every model)
|
|
681
691
|
1. \`ah_ping\`
|
|
682
|
-
2. Read \`ah://full-context\` OR (\`ah://models\` + \`ah://fields\` + \`ah://workflows\` + \`ah://workflows-platform\` + \`ah://usage\`)
|
|
692
|
+
2. Read \`ah://full-context\` OR (\`ah://models\` + \`ah://fields\` + \`ah://workflows\` + \`ah://workflows-platform\` + \`ah://api-context\` + \`ah://usage\`)
|
|
683
693
|
3. One context pack / investigate prompt
|
|
684
694
|
4. Filtered lists before more gets
|
|
685
695
|
|
|
@@ -691,6 +701,7 @@ If only tools: \`ah_ping\` → use \`forAnyLlm\`; still call \`ah_*_context\`.
|
|
|
691
701
|
- Billing → \`ah_invoice_list\` → \`ah_invoice_context\`
|
|
692
702
|
- Board → \`ah_task_statuses\` then filtered \`ah_task_list\` / \`ah_ticket_list\`
|
|
693
703
|
- Email-created ticket / chat / webhooks → \`ah://workflows-platform\` then ticket/project tools
|
|
704
|
+
- Path / permission / lead confusion → \`ah://api-context\`
|
|
694
705
|
|
|
695
706
|
## Token hygiene
|
|
696
707
|
- Prefer \`*_context\` (1 call) over 3–5 gets
|
|
@@ -701,9 +712,10 @@ If only tools: \`ah_ping\` → use \`forAnyLlm\`; still call \`ah_*_context\`.
|
|
|
701
712
|
## Anti-patterns
|
|
702
713
|
- Inventing ObjectIds
|
|
703
714
|
- Ticket id as \`pid\`
|
|
704
|
-
-
|
|
705
|
-
-
|
|
715
|
+
- Calling stale OpenAPI paths (\`/projects\`, \`/company/.../members\`)
|
|
716
|
+
- \`PUT /accounts/update\` without \`accountId\` (updates API-key owner)
|
|
706
717
|
- Mixing \`type=issue\` / \`type=task\`
|
|
718
|
+
- Treating \`/subscriptions\` as client order-subscriptions
|
|
707
719
|
`;
|
|
708
720
|
|
|
709
721
|
const TOOL_GUIDE = `# Tool selection
|
|
@@ -760,7 +772,7 @@ const MODULES = `# Modules
|
|
|
760
772
|
| Platform SaaS sub | \`ah_platform_subscription_list\` | subscriptions/list |
|
|
761
773
|
| Meta | \`ah_ping\` | local |
|
|
762
774
|
|
|
763
|
-
Resources: full-context, glossary, models, fields, workflows, workflows-platform, usage, tool-guide, modules, response-shape.
|
|
775
|
+
Resources: full-context, glossary, models, fields, workflows, workflows-platform, usage, tool-guide, modules, response-shape, api-context.
|
|
764
776
|
Prompts: orientation + investigate_*.
|
|
765
777
|
`;
|
|
766
778
|
|
|
@@ -803,6 +815,126 @@ Zod argument errors: \`{ ok:false, error:"Invalid tool arguments", details:[...]
|
|
|
803
815
|
Config errors: \`Invalid MCP server environment variables\`.
|
|
804
816
|
`;
|
|
805
817
|
|
|
818
|
+
const API_CONTEXT = `# API builder context (missing / easy-to-miss)
|
|
819
|
+
|
|
820
|
+
Use this when mapping product tools to live Agency Handy HTTP APIs, or when OpenAPI and runtime disagree. Prefer \`ah_*\` tools when available — this doc is the ground truth for paths and gotchas.
|
|
821
|
+
|
|
822
|
+
## Cross-cutting
|
|
823
|
+
- Live base: \`/api/v1/*\`. OpenAPI often still shows stale \`/projects/*\` and \`/company/{id}/members/*\` — **do not call those**.
|
|
824
|
+
- Auth: \`x-api-key\` **or** bearer — never both. API key JWT sets company from claims.
|
|
825
|
+
- Many deletes are **PUT + id array**, not HTTP DELETE (\`/tasks\`, \`/invoices\`, \`/quotations\`, \`/vouchers\`, \`/webhooks\`, \`/orders/delete\`, \`/members/bulk-delete\`).
|
|
826
|
+
- Pagination: orders/invoices/tasks often \`skip\`+\`limit\`; quotations use \`page\`+\`limit\`.
|
|
827
|
+
- 403 \`PermissionError\` ("Restricted - No permission…") = missing **permission name** on the API-key member role — not a broken token.
|
|
828
|
+
- Subscription expired: key still authenticates; routes with \`subscriptionExpireCheck\` return 403 \`SubscriptionExpirationError\` after grace.
|
|
829
|
+
|
|
830
|
+
## Leads (no \`/leads\` API)
|
|
831
|
+
- Leads are \`Member\` rows: client role + \`isConvertedClient=false\` (same collection as team/clients).
|
|
832
|
+
- Create: \`POST /members/bulk-lead\` (not \`/members\` or \`/members/bulk\`).
|
|
833
|
+
- List: \`GET /members?isConvertedClient=false\` (+ optional pipeline \`status\`).
|
|
834
|
+
- Update / convert: \`PUT /accounts/update\` with **required \`accountId\`**.
|
|
835
|
+
- **Without \`accountId\`**, update targets the **API-key owner** (can rename the token user).
|
|
836
|
+
- Delete: \`PUT /members/bulk-delete\`.
|
|
837
|
+
- Custom fields: \`/custom-fields?type=lead\` (separate from \`client\` / \`task\`).
|
|
838
|
+
- Pipeline statuses: \`New | Contacted | Qualified | Working | Proposal Sent | Negotiation\`.
|
|
839
|
+
- Permissions: \`addNewMemberToCompany\`, \`getEmployeeDirectory\`. Plan may return \`Lead limit reached\`.
|
|
840
|
+
- MCP: \`ah_lead_create\`, \`ah_lead_convert\`, \`ah_member_update\` (always pass account/member id).
|
|
841
|
+
|
|
842
|
+
## Orders / Projects
|
|
843
|
+
- UI Project = \`/orders\` only (not \`/projects\`).
|
|
844
|
+
- Get: \`GET /orders/:pid\`. Update: \`PUT /orders?pid=\` (query \`pid\` required).
|
|
845
|
+
- Status enum (title case): \`Pending | Ongoing | Review | Completed | Cancelled | Payment Failed\`.
|
|
846
|
+
- Client accept/reject: \`PUT /orders/:pid/actions\` with \`orderStatus: accept|reject\` — not the same as agency status PUT.
|
|
847
|
+
- Create needs \`service\`, \`packageId\`, \`packageName\`, \`clientTargetType\`, \`paymentType\` (\`oneTime|subscription\`), plus \`clientId\` **or** (\`clientCompanyId\` + \`sentToClientIds\`).
|
|
848
|
+
- Soft delete: \`PUT /orders/delete\`.
|
|
849
|
+
|
|
850
|
+
## Tasks / tickets / statuses
|
|
851
|
+
- Board tickets = \`Task\` with \`type=issue\` via \`/company/all-tasks?type=issue|task\` — **not** the legacy \`/tickets\` collection.
|
|
852
|
+
- List/filter \`status\` = TaskStatus **ObjectId(s)**. Update \`PUT /tasks/:taskId\` \`status\` = **status name string** (controller resolves name → id). Passing ObjectId on update fails.
|
|
853
|
+
- Complete: \`PUT /tasks/mark-as-completed\` (needs \`_id\`, \`pid\`, \`memberId\`, \`assingedPersons\` — typo is real). Not the same as status \`done\`.
|
|
854
|
+
- OpenAPI \`POST /tasks/{id}/subtask\` and \`PATCH /tasks/{id}/reopen\` are **not mounted**. Reopen ≈ \`PUT\` with \`status: open\` on closed issues (limited). Subtasks = model \`type=subTask\` only — no external create route.
|
|
855
|
+
- Create requires \`name\`, \`priority\` (0–4), \`labels[]\` (required, may be \`[]\`), \`type\`; \`client\` required unless visibility \`internal|order\`.
|
|
856
|
+
- Permissions: \`updateTask\`, \`deleteTask\`; tickets delete via \`deleteTicket\` / \`PUT /tasks/delete-ticket\`.
|
|
857
|
+
- \`GET /taskStatus\` returns all types — filter \`type\` (\`task\` vs \`issue\`) client-side.
|
|
858
|
+
|
|
859
|
+
## Assignment
|
|
860
|
+
- Add: \`POST /tasks/member/:taskId\` body \`{ assignmembers: [memberIds] }\`.
|
|
861
|
+
- Remove: \`PUT /tasks/member/:taskId\` body \`{ assignedMemberIds: [...] }\` — **different field name**.
|
|
862
|
+
- IDs are workspace **member** ObjectIds, not emails. Resolve with \`ah_member_resolve\`.
|
|
863
|
+
- Order PMs: \`assignedProjectManagers\` on \`PUT /orders?pid=\`, not task member endpoints.
|
|
864
|
+
|
|
865
|
+
## Comments
|
|
866
|
+
- Task/ticket: \`/comments\` — create needs \`text\`, \`type\` (\`agencyComment|clientComment|…\`), \`taskId\`, and query **\`memberId\` required**.
|
|
867
|
+
- Update: \`POST /comments/:commentId/update\` (not PUT).
|
|
868
|
+
- Proposal comments: separate \`/quotation-comments\` — do not reuse \`/comments\`.
|
|
869
|
+
- \`agencyComment\` vs \`clientComment\` controls portal visibility.
|
|
870
|
+
|
|
871
|
+
## Labels
|
|
872
|
+
- Company catalog create: \`POST /labels/create-for-company\`; suggest: \`GET /labels/suggest-by-company\`.
|
|
873
|
+
- \`POST /labels/\` is **task-scoped** (requires task id) — not global create.
|
|
874
|
+
- Attach/detach: \`/labels/task/:task\`, bulk \`/labels/bulk/:taskId\`.
|
|
875
|
+
|
|
876
|
+
## Clients / members / groups
|
|
877
|
+
- Live path: \`/members\` (not OpenAPI \`/company/{COMPANY_ID}/members\`).
|
|
878
|
+
- List permission: \`getEmployeeDirectory\`. \`type\` is \`individual|company|leadCompanyMember\` — not \`client\`/\`lead\`. Role filter is a role ObjectId. \`tab=client|team\` only on \`/members/basic\`.
|
|
879
|
+
- Invite: \`POST /members/send-client-invite\`. Bulk team \`/members/bulk\`, bulk client \`/members/bulk-client\`, bulk lead \`/members/bulk-lead\`.
|
|
880
|
+
- Soft delete: \`PUT /members/bulk-delete\` (+ optional async poll).
|
|
881
|
+
- Client companies: \`/client-companies\` (B2B org ids) ≠ individual client members — orders/invoices need the right \`clientTargetType\`.
|
|
882
|
+
- Groups: \`/client-groups\` (hard DELETE on group id).
|
|
883
|
+
|
|
884
|
+
## Invoices
|
|
885
|
+
- Mount: \`/invoices\`. Permissions: \`getInvoice\`, \`createInvoice\`, \`updateInvoice\`, \`deleteInvoice\`.
|
|
886
|
+
- Status: \`draft | open | paid | void | uncollectable\` (+ processing in places).
|
|
887
|
+
- Resend: \`PUT /invoices/resend\`. Public: \`/invoices/public/:id\`. Delete: \`PUT /invoices\` + ids.
|
|
888
|
+
- Create: \`currency\` + one of \`client\` | \`externalEmail\` | \`clientCompanyId\`; line items via \`additionalFields\` when required.
|
|
889
|
+
|
|
890
|
+
## Proposals / quotations
|
|
891
|
+
- UI Proposal = \`/quotations\` always.
|
|
892
|
+
- Permissions: \`createQuotation\`, \`updateQuotation\`, \`deleteQuotation\`.
|
|
893
|
+
- Recipient: \`clientId\` **or** \`leadId\` **or** external email object.
|
|
894
|
+
- Send: \`PUT /quotations/send/:id\`. Accept/reject: \`/accept/:id\`, \`/reject/:id\`. Public: \`/quotations/public/:id\`.
|
|
895
|
+
- \`acceptAction\`: \`doNothing | createOrder | createOrderAndInvoice\` — accept can spawn order/invoice.
|
|
896
|
+
- Pagination: \`page\` (min 1) + \`limit\`.
|
|
897
|
+
|
|
898
|
+
## Services & packages
|
|
899
|
+
- No \`/packages\` resource — packages nested on \`/services\`.
|
|
900
|
+
- Order create needs both \`packageId\` and \`packageName\` + parent \`service\` id.
|
|
901
|
+
- Permissions: \`createService\`, \`updateService\`, \`deleteService\`. Catalog: \`/services/catalog\`.
|
|
902
|
+
|
|
903
|
+
## Vouchers
|
|
904
|
+
- Admin CRUD permissions: \`createVoucher\`, \`getVouchers\`, \`getVoucher\`, \`updateVoucher\`, \`deleteVoucher\`.
|
|
905
|
+
- \`POST /vouchers/apply\` is **unauthenticated checkout** (\`code\`, \`email\`, \`serviceId\`, \`packageId\`, \`quantity\`) — not admin CRUD.
|
|
906
|
+
- Delete: \`PUT /vouchers\` + \`voucherIds[]\`.
|
|
907
|
+
|
|
908
|
+
## Webhooks
|
|
909
|
+
- Event ids on create come from \`GET /webhooks/events/list\` (ObjectIds), not free-form strings.
|
|
910
|
+
- Delete: \`PUT /webhooks\` + \`webhookIds[]\`. Logs: \`GET /webhooks/logs\`; attempts under \`/logs/:id/attempts\`.
|
|
911
|
+
- Auth configs (\`/webhook-auths\`) are separate from webhook URL rows.
|
|
912
|
+
|
|
913
|
+
## Forms
|
|
914
|
+
- Three surfaces: agency \`/forms\`, share \`/forms/share/:formId\` + \`/forms/public/:shareToken/respond\`, and \`/public-forms\`.
|
|
915
|
+
- Agency submit: \`POST /forms/respond\` (and \`respond-update\`).
|
|
916
|
+
- \`GET /forms/:formId\` auth may be open — treat payload as potentially public-facing.
|
|
917
|
+
|
|
918
|
+
## System
|
|
919
|
+
- Notifications \`GET /notifications\` = **caller inbox only**, not company-wide.
|
|
920
|
+
- Custom fields types: \`task|client|lead\`; mutate often admin/superAdmin only; values via \`/custom-fields/respond\` with \`resourceId\`.
|
|
921
|
+
- Roles: permission **camelCase names** from \`GET /roles/permissions\` (\`createOrder\`, \`getInvoice\`, \`addNewMemberToCompany\`, …).
|
|
922
|
+
- Email templates \`/email-templates\` ≠ builder \`/email-template-builder\`.
|
|
923
|
+
- \`/order-subscriptions\` = client recurring orders. \`/subscriptions\` = Agency Handy **SaaS** plan for the workspace — never mix them.
|
|
924
|
+
|
|
925
|
+
## Highest-risk misunderstandings
|
|
926
|
+
1. \`/projects\` vs \`/orders\`
|
|
927
|
+
2. Proposal vs \`/quotations\`
|
|
928
|
+
3. Board ticket vs legacy \`/tickets\`
|
|
929
|
+
4. Status **name on write** / **ObjectId on list**
|
|
930
|
+
5. OpenAPI subtask/reopen routes not mounted
|
|
931
|
+
6. \`PUT /accounts/update\` without \`accountId\`
|
|
932
|
+
7. Permission camelCase names → 403 "security"
|
|
933
|
+
8. PUT-as-delete
|
|
934
|
+
9. \`/subscriptions\` vs \`/order-subscriptions\`
|
|
935
|
+
10. Leads sharing \`/members\` without \`isConvertedClient=false\`
|
|
936
|
+
`;
|
|
937
|
+
|
|
806
938
|
const CONTENT = {
|
|
807
939
|
"ah://glossary": GLOSSARY,
|
|
808
940
|
"ah://models": MODELS,
|
|
@@ -813,6 +945,7 @@ const CONTENT = {
|
|
|
813
945
|
"ah://tool-guide": TOOL_GUIDE,
|
|
814
946
|
"ah://modules": MODULES,
|
|
815
947
|
"ah://response-shape": RESPONSE_SHAPE,
|
|
948
|
+
"ah://api-context": API_CONTEXT,
|
|
816
949
|
};
|
|
817
950
|
|
|
818
951
|
export function readResource(uri) {
|
|
@@ -837,6 +970,8 @@ export function readResource(uri) {
|
|
|
837
970
|
MODULES,
|
|
838
971
|
"---",
|
|
839
972
|
RESPONSE_SHAPE,
|
|
973
|
+
"---",
|
|
974
|
+
API_CONTEXT,
|
|
840
975
|
].join("\n");
|
|
841
976
|
return md(uri, text);
|
|
842
977
|
}
|