@kivimedia/kmhub 2.0.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 ADDED
@@ -0,0 +1,169 @@
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
+ | `full` | everything (**default**) |
65
+
66
+ `full` is the default, so nothing changes for an existing connection. An unknown profile name
67
+ falls back to `full` rather than erroring: a typo in a config file should never cost someone
68
+ their tools.
69
+
70
+ - **Remote**: per request, `POST /mcp?profile=money` or an `X-KMHub-Profile: money` header.
71
+ The query string wins if both are present. The profile is read from the request and passed
72
+ down by argument, so the server stays stateless and two tenants can hold two different
73
+ profiles at the same moment.
74
+ - **Local stdio**: the `KMHUB_PROFILE` env var.
75
+
76
+ `GET /health` reports the families that loaded and the tool count for each profile.
77
+
78
+ ## Setup - local stdio (`index.mjs`)
79
+ 1. In KM Hub: **Settings > Developer & API** -> create an API key (`kmh_live_...`).
80
+ Read tools need the `read` scope, create tools need `write` (a read-only key
81
+ gets 403 on writes).
82
+ 2. Install deps in this folder:
83
+ ```
84
+ npm install
85
+ ```
86
+ 3. Add to your MCP client.
87
+
88
+ Claude Code (one command):
89
+ ```
90
+ claude mcp add kmhub -e KMHUB_API_KEY=kmh_live_xxxxxxxx -- node /absolute/path/to/kmhub/mcp-server/index.mjs
91
+ ```
92
+
93
+ Claude Desktop `claude_desktop_config.json`:
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "kmhub": {
98
+ "command": "node",
99
+ "args": ["/absolute/path/to/kmhub/mcp-server/index.mjs"],
100
+ "env": { "KMHUB_API_KEY": "kmh_live_xxxxxxxx", "KMHUB_PROFILE": "full" }
101
+ }
102
+ }
103
+ }
104
+ ```
105
+ (Override the API base with `KMHUB_API_BASE` if self-hosting. `KMHUB_PROFILE` is optional
106
+ and defaults to `full`.)
107
+
108
+ ## Setup - remote Streamable HTTP (`remote.mjs`)
109
+
110
+ No local install for the user - they point their client at the hosted VPS endpoint and
111
+ authenticate with their own KM Hub key as a header. One server process serves every org;
112
+ each request is scoped to the token it carries. Operators: see
113
+ [DEPLOY-VPS.md](./DEPLOY-VPS.md) (pm2 `kmhub-mcp`, port `8830`,
114
+ `https://kmhub-mcp.104-200-30-37.sslip.io/mcp`).
115
+
116
+ Claude Code (one command):
117
+
118
+ ```bash
119
+ claude mcp add --transport http kmhub https://kmhub-mcp.104-200-30-37.sslip.io/mcp \
120
+ --header "Authorization: Bearer kmh_live_xxxxxxxx"
121
+ ```
122
+
123
+ Add `?profile=core` to the URL to load a smaller tool set:
124
+
125
+ ```bash
126
+ claude mcp add --transport http kmhub "https://kmhub-mcp.104-200-30-37.sslip.io/mcp?profile=core" \
127
+ --header "Authorization: Bearer kmh_live_xxxxxxxx"
128
+ ```
129
+
130
+ Claude Desktop / config-file clients:
131
+
132
+ ```json
133
+ {
134
+ "mcpServers": {
135
+ "kmhub": {
136
+ "type": "streamable-http",
137
+ "url": "https://kmhub-mcp.104-200-30-37.sslip.io/mcp",
138
+ "headers": {
139
+ "Authorization": "Bearer kmh_live_xxxxxxxx",
140
+ "X-KMHub-Profile": "full"
141
+ }
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ Health check (no auth): `GET https://kmhub-mcp.104-200-30-37.sslip.io/health`.
148
+
149
+ ## Adding tools
150
+
151
+ See [tools/README.md](./tools/README.md) for the family contract. Short version: drop a new
152
+ `tools/<family>.mjs` exporting `FAMILY`, `TOOLS`, `register(server, call, helpers)` and an
153
+ optional `PROFILES`, and it loads itself. You never edit `tools.mjs`, so parallel work on
154
+ different families cannot collide. A family that fails to load, or claims a tool name another
155
+ family already took, is skipped with a warning on stderr; the rest of the server carries on.
156
+
157
+ ## Notes
158
+ This is the same org-scoped, key-authed substrate the Telegram bot and Zapier use - one
159
+ auth surface for all machine access. Everything is scoped to the key's workspace; a key
160
+ can never reach another tenant's data. The API rate-limits each key to 60 reads and 20
161
+ writes per minute (429 + `retry-after` beyond that). Writes land in the AI activity log;
162
+ outreach drafts additionally wait for human approval in the Approval Queue before any send.
163
+
164
+ **Lapsed subscription (402).** If the API answers `402 subscription_inactive`, every tool in
165
+ every family returns a plain-English message instead of a raw JSON blob: the subscription is
166
+ not active, nothing in the workspace changed, and here is where to restart it. That is handled
167
+ once in the shared `out()` helper in `tools.mjs`, so it covers families that do not exist yet.
168
+ Other statuses keep the raw JSON body, which is what the model needs to reason about validation
169
+ errors.