@kivimedia/kmhub 2.9.1 → 2.11.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/README.md +170 -170
- package/bin/kmhub.mjs +896 -896
- package/coach-book-output-guard.mjs +760 -760
- package/index.mjs +57 -57
- package/package.json +56 -56
- package/prompts/briefing.md +29 -29
- package/prompts/luxury.md +70 -70
- package/prompts/play.md +49 -49
- package/prompts/run.md +37 -36
- package/prompts/setup.md +33 -33
- package/prompts/vs-booked.md +46 -46
- package/prompts/what-can-you-do.md +40 -40
- package/prompts.mjs +110 -110
- package/read-only-tools.json +143 -142
- package/remote.mjs +929 -929
- package/tools/balloon-costing.mjs +80 -80
- package/tools/booking-equipment.mjs +110 -110
- package/tools/bridges.mjs +54 -54
- package/tools/briefing.mjs +91 -91
- package/tools/calendar.mjs +170 -170
- package/tools/capabilities.mjs +155 -155
- package/tools/catalog.mjs +288 -288
- package/tools/clubs.mjs +176 -176
- package/tools/coach.mjs +771 -771
- package/tools/compare.mjs +76 -76
- package/tools/core.mjs +244 -244
- package/tools/crm.mjs +209 -209
- package/tools/dubsado.mjs +137 -137
- package/tools/exports.mjs +128 -128
- package/tools/fact-review.mjs +125 -125
- package/tools/flows.mjs +261 -261
- package/tools/forms.mjs +158 -158
- package/tools/gols.mjs +144 -134
- package/tools/hr.mjs +162 -162
- package/tools/knowledge.mjs +129 -125
- package/tools/marketing.mjs +396 -396
- package/tools/meta.mjs +245 -245
- package/tools/military.mjs +244 -244
- package/tools/money.mjs +235 -197
- package/tools/outreach.mjs +238 -238
- package/tools/pending.mjs +122 -122
- package/tools/photos.mjs +140 -140
- package/tools/plays.mjs +244 -244
- package/tools/profile.mjs +118 -118
- package/tools/radar.mjs +173 -173
- package/tools/recurring-invoices.mjs +149 -149
- package/tools/reengage.mjs +434 -434
- package/tools/schedules.mjs +55 -55
- package/tools/setup.mjs +168 -168
- package/tools/sops-bridges.mjs +86 -86
- package/tools/sops.mjs +314 -314
- package/tools/sourcing.mjs +268 -268
- package/tools/strategy.mjs +146 -146
- package/tools/studio.mjs +132 -132
- package/tools/venueradar.mjs +151 -151
- package/tools/voice.mjs +137 -134
- package/tools.mjs +407 -407
package/README.md
CHANGED
|
@@ -1,170 +1,170 @@
|
|
|
1
|
-
# kmhub-mcp
|
|
2
|
-
|
|
3
|
-
A standalone [MCP](https://modelcontextprotocol.io) server for KM Hub. It exposes your
|
|
4
|
-
workspace to any MCP client (Claude Desktop, Claude Code, IDEs, agents) using a KM Hub
|
|
5
|
-
API key, by wrapping the public REST API (`kmhub-api`).
|
|
6
|
-
|
|
7
|
-
It ships in two transports that share the exact same tool set (`tools.mjs` plus the
|
|
8
|
-
family modules in `tools/`):
|
|
9
|
-
|
|
10
|
-
- **Local stdio** (`index.mjs`) - the client spawns the process; the org key comes from
|
|
11
|
-
the `KMHUB_API_KEY` env var. One process = one workspace. Best for a single power user
|
|
12
|
-
on their own machine.
|
|
13
|
-
- **Remote Streamable HTTP** (`remote.mjs`) - one long-running server (hosted on the VPS)
|
|
14
|
-
serves many orgs with zero local install. Each request authenticates with its own KM Hub
|
|
15
|
-
key sent as a `Authorization: Bearer kmh_live_...` header, and is scoped to that org only.
|
|
16
|
-
Deploy runbook: [DEPLOY-VPS.md](./DEPLOY-VPS.md).
|
|
17
|
-
|
|
18
|
-
## Tools
|
|
19
|
-
|
|
20
|
-
Tools are grouped into **families**, one file per family in [`tools/`](./tools/README.md).
|
|
21
|
-
`tools.mjs` discovers them at load time, so a new family is a new file and nothing else.
|
|
22
|
-
|
|
23
|
-
### `core` - orientation and the CRM essentials
|
|
24
|
-
- `km_me` - the connected workspace (id, name, plan) + the key's scopes
|
|
25
|
-
- `km_waiting` - what needs you (approvals, enquiries, tasks, invoices, contracts)
|
|
26
|
-
- `km_list_clients` - recent clients / leads (optional `limit`)
|
|
27
|
-
- `km_create_lead` - create a lead (`first_name`, `last_name`, `company`, `email`, `phone`, `notes`)
|
|
28
|
-
- `km_list_deals` - pipeline deals (optional `limit`, `stage`, `status`)
|
|
29
|
-
- `km_list_events` - calendar events (optional `limit`, `from`/`to` start-date range)
|
|
30
|
-
- `km_list_bookings` - recent bookings (optional `limit`, `status`, `from`/`to` event-date range)
|
|
31
|
-
- `km_create_inquiry` - log an enquiry + its client (`client_name` required; `event_type`, `event_date`, `budget_cents`, `notes`, ...). Opens a pipeline deal automatically. Audited + undoable in the AI activity log.
|
|
32
|
-
- `km_create_booking` - create a real booking + its client (`client_name` and `event_date` required; optional `status`: `pending` default or `confirmed`). Opens a pipeline deal at booked/won. Audited + undoable.
|
|
33
|
-
- `km_create_outreach_draft` - queue an outreach message as a DRAFT (`body` required; optional `subject`, `channel` email/linkedin/ig/sms_whatsapp, `deal_id`, `contact_id`). It only QUEUES: a human approves the draft in the Approval Queue before anything can send. Audited.
|
|
34
|
-
|
|
35
|
-
### `meta` - keeping the connection current
|
|
36
|
-
- `km_check_updates` - asks KM Hub what it publishes today (`GET /version` returns
|
|
37
|
-
`api_version`, `rules_version`, `tools_version`, `min_client`, `changelog_url`) and answers
|
|
38
|
-
in plain language. Two judgements: is this connector still above `min_client` (the build
|
|
39
|
-
KM Hub refuses to serve below), and is the rules file on disk still the published
|
|
40
|
-
`rules_version`? Pass the local one in as `rules_version`. The rules pack carries a content
|
|
41
|
-
hash after a `+`, and the two endpoints do not always both publish it, so the semver base
|
|
42
|
-
and the hash are judged separately rather than looping on a suffix that proves nothing.
|
|
43
|
-
- `km_fetch_rules` - downloads the current KM Hub operating rules (`GET /rules`) and returns
|
|
44
|
-
`{ version, markdown, sha256, bytes, instructions }`. The model is instructed to write that
|
|
45
|
-
markdown verbatim over the user's global rules file (for Claude Code, their `CLAUDE.md`),
|
|
46
|
-
replacing any older KM Hub block, and then to tell the user it did so.
|
|
47
|
-
|
|
48
|
-
Both meta tools degrade gracefully: if your KM Hub has not shipped `/version` or `/rules` yet
|
|
49
|
-
they say "your KM Hub does not support update checks yet" and report success, rather than
|
|
50
|
-
failing. The checksum is computed locally from the bytes that actually arrived; if KM Hub also
|
|
51
|
-
sends one and the two differ, the response carries a warning instead of a silent bad write.
|
|
52
|
-
|
|
53
|
-
## Profiles
|
|
54
|
-
|
|
55
|
-
Every tool schema is loaded into every turn, so a big tool set is a real context tax. A
|
|
56
|
-
profile loads only the families you want:
|
|
57
|
-
|
|
58
|
-
| Profile | Loads |
|
|
59
|
-
|---|---|
|
|
60
|
-
| `core` | orientation + CRM essentials, plus `meta` |
|
|
61
|
-
| `outreach` | `core` + the outreach families |
|
|
62
|
-
| `money` | `core` + the billing families |
|
|
63
|
-
| `content` | `core` + the content families |
|
|
64
|
-
| `coach` | Fully Booked Coach operations, client delivery, acquisition, and content requests |
|
|
65
|
-
| `full` | everything (**default**) |
|
|
66
|
-
|
|
67
|
-
`full` is the default, so nothing changes for an existing connection. An unknown profile name
|
|
68
|
-
falls back to `full` rather than erroring: a typo in a config file should never cost someone
|
|
69
|
-
their tools.
|
|
70
|
-
|
|
71
|
-
- **Remote**: per request, `POST /mcp?profile=money` or an `X-KMHub-Profile: money` header.
|
|
72
|
-
The query string wins if both are present. The profile is read from the request and passed
|
|
73
|
-
down by argument, so the server stays stateless and two tenants can hold two different
|
|
74
|
-
profiles at the same moment.
|
|
75
|
-
- **Local stdio**: the `KMHUB_PROFILE` env var.
|
|
76
|
-
|
|
77
|
-
`GET /health` reports the families that loaded and the tool count for each profile.
|
|
78
|
-
|
|
79
|
-
## Setup - local stdio (`index.mjs`)
|
|
80
|
-
1. In KM Hub: **Settings > Developer & API** -> create an API key (`kmh_live_...`).
|
|
81
|
-
Read tools need the `read` scope, create tools need `write` (a read-only key
|
|
82
|
-
gets 403 on writes).
|
|
83
|
-
2. Install deps in this folder:
|
|
84
|
-
```
|
|
85
|
-
npm install
|
|
86
|
-
```
|
|
87
|
-
3. Add to your MCP client.
|
|
88
|
-
|
|
89
|
-
Claude Code (one command):
|
|
90
|
-
```
|
|
91
|
-
claude mcp add kmhub -e KMHUB_API_KEY=kmh_live_xxxxxxxx -- node /absolute/path/to/kmhub/mcp-server/index.mjs
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Claude Desktop `claude_desktop_config.json`:
|
|
95
|
-
```json
|
|
96
|
-
{
|
|
97
|
-
"mcpServers": {
|
|
98
|
-
"kmhub": {
|
|
99
|
-
"command": "node",
|
|
100
|
-
"args": ["/absolute/path/to/kmhub/mcp-server/index.mjs"],
|
|
101
|
-
"env": { "KMHUB_API_KEY": "kmh_live_xxxxxxxx", "KMHUB_PROFILE": "full" }
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
(Override the API base with `KMHUB_API_BASE` if self-hosting. `KMHUB_PROFILE` is optional
|
|
107
|
-
and defaults to `full`.)
|
|
108
|
-
|
|
109
|
-
## Setup - remote Streamable HTTP (`remote.mjs`)
|
|
110
|
-
|
|
111
|
-
No local install for the user - they point their client at the hosted VPS endpoint and
|
|
112
|
-
authenticate with their own KM Hub key as a header. One server process serves every org;
|
|
113
|
-
each request is scoped to the token it carries. Operators: see
|
|
114
|
-
[DEPLOY-VPS.md](./DEPLOY-VPS.md) (pm2 `kmhub-mcp`, port `8830`,
|
|
115
|
-
`https://mcp.kivimedia.co/mcp`).
|
|
116
|
-
|
|
117
|
-
Claude Code (one command):
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
claude mcp add --transport http kmhub https://mcp.kivimedia.co/mcp \
|
|
121
|
-
--header "Authorization: Bearer kmh_live_xxxxxxxx"
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Add `?profile=core` to the URL to load a smaller tool set:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
claude mcp add --transport http kmhub "https://mcp.kivimedia.co/mcp?profile=core" \
|
|
128
|
-
--header "Authorization: Bearer kmh_live_xxxxxxxx"
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Claude Desktop / config-file clients:
|
|
132
|
-
|
|
133
|
-
```json
|
|
134
|
-
{
|
|
135
|
-
"mcpServers": {
|
|
136
|
-
"kmhub": {
|
|
137
|
-
"type": "streamable-http",
|
|
138
|
-
"url": "https://mcp.kivimedia.co/mcp",
|
|
139
|
-
"headers": {
|
|
140
|
-
"Authorization": "Bearer kmh_live_xxxxxxxx",
|
|
141
|
-
"X-KMHub-Profile": "full"
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Health check (no auth): `GET https://mcp.kivimedia.co/health`.
|
|
149
|
-
|
|
150
|
-
## Adding tools
|
|
151
|
-
|
|
152
|
-
See [tools/README.md](./tools/README.md) for the family contract. Short version: drop a new
|
|
153
|
-
`tools/<family>.mjs` exporting `FAMILY`, `TOOLS`, `register(server, call, helpers)` and an
|
|
154
|
-
optional `PROFILES`, and it loads itself. You never edit `tools.mjs`, so parallel work on
|
|
155
|
-
different families cannot collide. A family that fails to load, or claims a tool name another
|
|
156
|
-
family already took, is skipped with a warning on stderr; the rest of the server carries on.
|
|
157
|
-
|
|
158
|
-
## Notes
|
|
159
|
-
This is the same org-scoped, key-authed substrate the Telegram bot and Zapier use - one
|
|
160
|
-
auth surface for all machine access. Everything is scoped to the key's workspace; a key
|
|
161
|
-
can never reach another tenant's data. The API rate-limits each key to 60 reads and 20
|
|
162
|
-
writes per minute (429 + `retry-after` beyond that). Writes land in the AI activity log;
|
|
163
|
-
outreach drafts additionally wait for human approval in the Approval Queue before any send.
|
|
164
|
-
|
|
165
|
-
**Lapsed subscription (402).** If the API answers `402 subscription_inactive`, every tool in
|
|
166
|
-
every family returns a plain-English message instead of a raw JSON blob: the subscription is
|
|
167
|
-
not active, nothing in the workspace changed, and here is where to restart it. That is handled
|
|
168
|
-
once in the shared `out()` helper in `tools.mjs`, so it covers families that do not exist yet.
|
|
169
|
-
Other statuses keep the raw JSON body, which is what the model needs to reason about validation
|
|
170
|
-
errors.
|
|
1
|
+
# kmhub-mcp
|
|
2
|
+
|
|
3
|
+
A standalone [MCP](https://modelcontextprotocol.io) server for KM Hub. It exposes your
|
|
4
|
+
workspace to any MCP client (Claude Desktop, Claude Code, IDEs, agents) using a KM Hub
|
|
5
|
+
API key, by wrapping the public REST API (`kmhub-api`).
|
|
6
|
+
|
|
7
|
+
It ships in two transports that share the exact same tool set (`tools.mjs` plus the
|
|
8
|
+
family modules in `tools/`):
|
|
9
|
+
|
|
10
|
+
- **Local stdio** (`index.mjs`) - the client spawns the process; the org key comes from
|
|
11
|
+
the `KMHUB_API_KEY` env var. One process = one workspace. Best for a single power user
|
|
12
|
+
on their own machine.
|
|
13
|
+
- **Remote Streamable HTTP** (`remote.mjs`) - one long-running server (hosted on the VPS)
|
|
14
|
+
serves many orgs with zero local install. Each request authenticates with its own KM Hub
|
|
15
|
+
key sent as a `Authorization: Bearer kmh_live_...` header, and is scoped to that org only.
|
|
16
|
+
Deploy runbook: [DEPLOY-VPS.md](./DEPLOY-VPS.md).
|
|
17
|
+
|
|
18
|
+
## Tools
|
|
19
|
+
|
|
20
|
+
Tools are grouped into **families**, one file per family in [`tools/`](./tools/README.md).
|
|
21
|
+
`tools.mjs` discovers them at load time, so a new family is a new file and nothing else.
|
|
22
|
+
|
|
23
|
+
### `core` - orientation and the CRM essentials
|
|
24
|
+
- `km_me` - the connected workspace (id, name, plan) + the key's scopes
|
|
25
|
+
- `km_waiting` - what needs you (approvals, enquiries, tasks, invoices, contracts)
|
|
26
|
+
- `km_list_clients` - recent clients / leads (optional `limit`)
|
|
27
|
+
- `km_create_lead` - create a lead (`first_name`, `last_name`, `company`, `email`, `phone`, `notes`)
|
|
28
|
+
- `km_list_deals` - pipeline deals (optional `limit`, `stage`, `status`)
|
|
29
|
+
- `km_list_events` - calendar events (optional `limit`, `from`/`to` start-date range)
|
|
30
|
+
- `km_list_bookings` - recent bookings (optional `limit`, `status`, `from`/`to` event-date range)
|
|
31
|
+
- `km_create_inquiry` - log an enquiry + its client (`client_name` required; `event_type`, `event_date`, `budget_cents`, `notes`, ...). Opens a pipeline deal automatically. Audited + undoable in the AI activity log.
|
|
32
|
+
- `km_create_booking` - create a real booking + its client (`client_name` and `event_date` required; optional `status`: `pending` default or `confirmed`). Opens a pipeline deal at booked/won. Audited + undoable.
|
|
33
|
+
- `km_create_outreach_draft` - queue an outreach message as a DRAFT (`body` required; optional `subject`, `channel` email/linkedin/ig/sms_whatsapp, `deal_id`, `contact_id`). It only QUEUES: a human approves the draft in the Approval Queue before anything can send. Audited.
|
|
34
|
+
|
|
35
|
+
### `meta` - keeping the connection current
|
|
36
|
+
- `km_check_updates` - asks KM Hub what it publishes today (`GET /version` returns
|
|
37
|
+
`api_version`, `rules_version`, `tools_version`, `min_client`, `changelog_url`) and answers
|
|
38
|
+
in plain language. Two judgements: is this connector still above `min_client` (the build
|
|
39
|
+
KM Hub refuses to serve below), and is the rules file on disk still the published
|
|
40
|
+
`rules_version`? Pass the local one in as `rules_version`. The rules pack carries a content
|
|
41
|
+
hash after a `+`, and the two endpoints do not always both publish it, so the semver base
|
|
42
|
+
and the hash are judged separately rather than looping on a suffix that proves nothing.
|
|
43
|
+
- `km_fetch_rules` - downloads the current KM Hub operating rules (`GET /rules`) and returns
|
|
44
|
+
`{ version, markdown, sha256, bytes, instructions }`. The model is instructed to write that
|
|
45
|
+
markdown verbatim over the user's global rules file (for Claude Code, their `CLAUDE.md`),
|
|
46
|
+
replacing any older KM Hub block, and then to tell the user it did so.
|
|
47
|
+
|
|
48
|
+
Both meta tools degrade gracefully: if your KM Hub has not shipped `/version` or `/rules` yet
|
|
49
|
+
they say "your KM Hub does not support update checks yet" and report success, rather than
|
|
50
|
+
failing. The checksum is computed locally from the bytes that actually arrived; if KM Hub also
|
|
51
|
+
sends one and the two differ, the response carries a warning instead of a silent bad write.
|
|
52
|
+
|
|
53
|
+
## Profiles
|
|
54
|
+
|
|
55
|
+
Every tool schema is loaded into every turn, so a big tool set is a real context tax. A
|
|
56
|
+
profile loads only the families you want:
|
|
57
|
+
|
|
58
|
+
| Profile | Loads |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `core` | orientation + CRM essentials, plus `meta` |
|
|
61
|
+
| `outreach` | `core` + the outreach families |
|
|
62
|
+
| `money` | `core` + the billing families |
|
|
63
|
+
| `content` | `core` + the content families |
|
|
64
|
+
| `coach` | Fully Booked Coach operations, client delivery, acquisition, and content requests |
|
|
65
|
+
| `full` | everything (**default**) |
|
|
66
|
+
|
|
67
|
+
`full` is the default, so nothing changes for an existing connection. An unknown profile name
|
|
68
|
+
falls back to `full` rather than erroring: a typo in a config file should never cost someone
|
|
69
|
+
their tools.
|
|
70
|
+
|
|
71
|
+
- **Remote**: per request, `POST /mcp?profile=money` or an `X-KMHub-Profile: money` header.
|
|
72
|
+
The query string wins if both are present. The profile is read from the request and passed
|
|
73
|
+
down by argument, so the server stays stateless and two tenants can hold two different
|
|
74
|
+
profiles at the same moment.
|
|
75
|
+
- **Local stdio**: the `KMHUB_PROFILE` env var.
|
|
76
|
+
|
|
77
|
+
`GET /health` reports the families that loaded and the tool count for each profile.
|
|
78
|
+
|
|
79
|
+
## Setup - local stdio (`index.mjs`)
|
|
80
|
+
1. In KM Hub: **Settings > Developer & API** -> create an API key (`kmh_live_...`).
|
|
81
|
+
Read tools need the `read` scope, create tools need `write` (a read-only key
|
|
82
|
+
gets 403 on writes).
|
|
83
|
+
2. Install deps in this folder:
|
|
84
|
+
```
|
|
85
|
+
npm install
|
|
86
|
+
```
|
|
87
|
+
3. Add to your MCP client.
|
|
88
|
+
|
|
89
|
+
Claude Code (one command):
|
|
90
|
+
```
|
|
91
|
+
claude mcp add kmhub -e KMHUB_API_KEY=kmh_live_xxxxxxxx -- node /absolute/path/to/kmhub/mcp-server/index.mjs
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Claude Desktop `claude_desktop_config.json`:
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"mcpServers": {
|
|
98
|
+
"kmhub": {
|
|
99
|
+
"command": "node",
|
|
100
|
+
"args": ["/absolute/path/to/kmhub/mcp-server/index.mjs"],
|
|
101
|
+
"env": { "KMHUB_API_KEY": "kmh_live_xxxxxxxx", "KMHUB_PROFILE": "full" }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
(Override the API base with `KMHUB_API_BASE` if self-hosting. `KMHUB_PROFILE` is optional
|
|
107
|
+
and defaults to `full`.)
|
|
108
|
+
|
|
109
|
+
## Setup - remote Streamable HTTP (`remote.mjs`)
|
|
110
|
+
|
|
111
|
+
No local install for the user - they point their client at the hosted VPS endpoint and
|
|
112
|
+
authenticate with their own KM Hub key as a header. One server process serves every org;
|
|
113
|
+
each request is scoped to the token it carries. Operators: see
|
|
114
|
+
[DEPLOY-VPS.md](./DEPLOY-VPS.md) (pm2 `kmhub-mcp`, port `8830`,
|
|
115
|
+
`https://mcp.kivimedia.co/mcp`).
|
|
116
|
+
|
|
117
|
+
Claude Code (one command):
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
claude mcp add --transport http kmhub https://mcp.kivimedia.co/mcp \
|
|
121
|
+
--header "Authorization: Bearer kmh_live_xxxxxxxx"
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Add `?profile=core` to the URL to load a smaller tool set:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
claude mcp add --transport http kmhub "https://mcp.kivimedia.co/mcp?profile=core" \
|
|
128
|
+
--header "Authorization: Bearer kmh_live_xxxxxxxx"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Claude Desktop / config-file clients:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mcpServers": {
|
|
136
|
+
"kmhub": {
|
|
137
|
+
"type": "streamable-http",
|
|
138
|
+
"url": "https://mcp.kivimedia.co/mcp",
|
|
139
|
+
"headers": {
|
|
140
|
+
"Authorization": "Bearer kmh_live_xxxxxxxx",
|
|
141
|
+
"X-KMHub-Profile": "full"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Health check (no auth): `GET https://mcp.kivimedia.co/health`.
|
|
149
|
+
|
|
150
|
+
## Adding tools
|
|
151
|
+
|
|
152
|
+
See [tools/README.md](./tools/README.md) for the family contract. Short version: drop a new
|
|
153
|
+
`tools/<family>.mjs` exporting `FAMILY`, `TOOLS`, `register(server, call, helpers)` and an
|
|
154
|
+
optional `PROFILES`, and it loads itself. You never edit `tools.mjs`, so parallel work on
|
|
155
|
+
different families cannot collide. A family that fails to load, or claims a tool name another
|
|
156
|
+
family already took, is skipped with a warning on stderr; the rest of the server carries on.
|
|
157
|
+
|
|
158
|
+
## Notes
|
|
159
|
+
This is the same org-scoped, key-authed substrate the Telegram bot and Zapier use - one
|
|
160
|
+
auth surface for all machine access. Everything is scoped to the key's workspace; a key
|
|
161
|
+
can never reach another tenant's data. The API rate-limits each key to 60 reads and 20
|
|
162
|
+
writes per minute (429 + `retry-after` beyond that). Writes land in the AI activity log;
|
|
163
|
+
outreach drafts additionally wait for human approval in the Approval Queue before any send.
|
|
164
|
+
|
|
165
|
+
**Lapsed subscription (402).** If the API answers `402 subscription_inactive`, every tool in
|
|
166
|
+
every family returns a plain-English message instead of a raw JSON blob: the subscription is
|
|
167
|
+
not active, nothing in the workspace changed, and here is where to restart it. That is handled
|
|
168
|
+
once in the shared `out()` helper in `tools.mjs`, so it covers families that do not exist yet.
|
|
169
|
+
Other statuses keep the raw JSON body, which is what the model needs to reason about validation
|
|
170
|
+
errors.
|