multify-mcp 0.1.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/README.md +253 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +95 -0
- package/dist/index.d.ts +1359 -0
- package/dist/index.js +238 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# multify-mcp
|
|
2
|
+
|
|
3
|
+
`multify-mcp` is the hosted, first-party MCP for Multify.
|
|
4
|
+
|
|
5
|
+
It is one MCP server with the full Multify account surface behind it. Contacts, lists, membership actions, and future workspace capabilities all live behind the same server and the same workspace key.
|
|
6
|
+
|
|
7
|
+
## How Multify MCP works
|
|
8
|
+
|
|
9
|
+
There is only one hosted Multify MCP server. It lives behind the Multify API host and uses a workspace-scoped MCP key generated inside the app.
|
|
10
|
+
|
|
11
|
+
This is not Claude-specific. Any client, agent runtime, or connector platform that supports remote MCP over HTTP should use the same hosted endpoint and the same workspace key model.
|
|
12
|
+
|
|
13
|
+
The fixed hosted endpoint is:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
https://api.multifyco.com/mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For normal usage, the user should only need one secret:
|
|
20
|
+
|
|
21
|
+
1. create a workspace MCP key inside Multify
|
|
22
|
+
2. paste that key into the MCP client
|
|
23
|
+
|
|
24
|
+
The base URL is fixed by Multify. It should not be something the user has to discover.
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
### 1. Generate a workspace key
|
|
29
|
+
|
|
30
|
+
Inside Multify:
|
|
31
|
+
|
|
32
|
+
1. open `Settings`
|
|
33
|
+
2. open the `MCP` section
|
|
34
|
+
3. create a workspace-scoped key
|
|
35
|
+
4. copy it once
|
|
36
|
+
|
|
37
|
+
The key belongs to the workspace, not to an individual contact/list tool.
|
|
38
|
+
|
|
39
|
+
### 2. Connection details for any MCP client
|
|
40
|
+
|
|
41
|
+
Use this connection contract:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
Name: multify-mcp
|
|
45
|
+
Transport: http
|
|
46
|
+
URL: https://api.multifyco.com/mcp
|
|
47
|
+
Authorization: Bearer YOUR_MULTIFY_MCP_KEY
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If your client uses a config file instead of a CLI, the shape is usually equivalent to:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"mcpServers": {
|
|
55
|
+
"multify-mcp": {
|
|
56
|
+
"transport": "http",
|
|
57
|
+
"url": "https://api.multifyco.com/mcp",
|
|
58
|
+
"headers": {
|
|
59
|
+
"Authorization": "Bearer YOUR_MULTIFY_MCP_KEY"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Field names vary between clients, but the invariants do not:
|
|
67
|
+
|
|
68
|
+
- use the fixed URL `https://api.multifyco.com/mcp`
|
|
69
|
+
- use remote HTTP MCP transport
|
|
70
|
+
- send `Authorization: Bearer ...`
|
|
71
|
+
|
|
72
|
+
### 3. Claude Code token-only helper
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx multify-mcp claude add --token YOUR_MULTIFY_MCP_KEY
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
That helper wraps the fixed hosted URL for you and runs the Claude Code command. It is a convenience layer, not the primary product model.
|
|
79
|
+
|
|
80
|
+
Raw Claude Code command:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
claude mcp add --transport http multify-mcp https://api.multifyco.com/mcp \
|
|
84
|
+
--header "Authorization: Bearer YOUR_MULTIFY_MCP_KEY"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Claude Code expects auth for remote HTTP MCP servers through `--header`, so the token is carried in the standard `Authorization: Bearer ...` header. The only secret is still the workspace MCP key.
|
|
88
|
+
|
|
89
|
+
### 4. Connect from custom connectors
|
|
90
|
+
|
|
91
|
+
Endpoint:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
https://api.multifyco.com/mcp
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Header:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
Authorization: Bearer YOUR_MULTIFY_MCP_KEY
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 5. Connect from any remote MCP client
|
|
104
|
+
|
|
105
|
+
Any client that supports remote MCP over Streamable HTTP can use the same hosted endpoint and the same Bearer token pattern.
|
|
106
|
+
|
|
107
|
+
There is no extra base URL the user needs to discover or configure manually. The hosted Multify MCP endpoint is fixed on the API host.
|
|
108
|
+
|
|
109
|
+
## What this MCP exposes
|
|
110
|
+
|
|
111
|
+
`multify-mcp` is one server with one workspace boundary. It currently exposes Multify workspace operations such as:
|
|
112
|
+
|
|
113
|
+
- contacts
|
|
114
|
+
- lists
|
|
115
|
+
- list membership actions
|
|
116
|
+
- bulk create/update flows
|
|
117
|
+
|
|
118
|
+
It does not ask the user to wire Gmail, HubSpot, or other third-party accounts through the MCP itself. Those connections stay inside the Multify app.
|
|
119
|
+
|
|
120
|
+
## Security model
|
|
121
|
+
|
|
122
|
+
- keys are workspace-scoped
|
|
123
|
+
- keys are created by workspace owners/admins in the app
|
|
124
|
+
- scopes control what the MCP can do
|
|
125
|
+
- all operations are still enforced by Multify as the source of truth
|
|
126
|
+
- the MCP is an interface over Multify, not a parallel database or permissions system
|
|
127
|
+
|
|
128
|
+
## Python package
|
|
129
|
+
|
|
130
|
+
The user-facing Python package is:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
pip install multify-mcp
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Local runtime command:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
multify-mcp
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This repo intentionally treats the Python runtime as one package: `multify-mcp`.
|
|
143
|
+
|
|
144
|
+
Python helper example:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from multify_mcp import build_remote_mcp_connection
|
|
148
|
+
|
|
149
|
+
connection = build_remote_mcp_connection("YOUR_MULTIFY_MCP_KEY")
|
|
150
|
+
|
|
151
|
+
print(connection.url)
|
|
152
|
+
print(connection.headers["Authorization"])
|
|
153
|
+
print(connection.as_dict())
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
That helper is generic. It is not tied to Claude or any single MCP client.
|
|
157
|
+
|
|
158
|
+
## npm package
|
|
159
|
+
|
|
160
|
+
The public npm package is also `multify-mcp`.
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm install multify-mcp
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
CLI helper:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npx multify-mcp claude add --token YOUR_MULTIFY_MCP_KEY
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import {
|
|
176
|
+
buildAuthorizationHeaders,
|
|
177
|
+
buildRemoteMcpConnection,
|
|
178
|
+
buildClaudeCodeAddArgs,
|
|
179
|
+
buildClaudeCodeAddCommand,
|
|
180
|
+
contactCreateInputSchema,
|
|
181
|
+
DEFAULT_MULTIFY_MCP_URL,
|
|
182
|
+
} from "multify-mcp";
|
|
183
|
+
|
|
184
|
+
const headers = buildAuthorizationHeaders(process.env.MULTIFY_MCP_KEY ?? "");
|
|
185
|
+
const connection = buildRemoteMcpConnection(process.env.MULTIFY_MCP_KEY ?? "");
|
|
186
|
+
const args = buildClaudeCodeAddArgs(process.env.MULTIFY_MCP_KEY ?? "");
|
|
187
|
+
const command = buildClaudeCodeAddCommand(process.env.MULTIFY_MCP_KEY ?? "");
|
|
188
|
+
|
|
189
|
+
const payload = contactCreateInputSchema.parse({
|
|
190
|
+
list_id: "list_123",
|
|
191
|
+
full_name: "Ada Lovelace",
|
|
192
|
+
email: "ada@example.com",
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
console.log(DEFAULT_MULTIFY_MCP_URL);
|
|
196
|
+
console.log(headers.Authorization);
|
|
197
|
+
console.log(connection);
|
|
198
|
+
console.log(args);
|
|
199
|
+
console.log(command);
|
|
200
|
+
console.log(payload);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The npm side is now one package too. There are no separate public npm packages for contracts and SDK anymore. The generic helper is `buildRemoteMcpConnection`; the Claude helper is optional.
|
|
204
|
+
|
|
205
|
+
## Local development
|
|
206
|
+
|
|
207
|
+
Requirements:
|
|
208
|
+
|
|
209
|
+
- Node 22+
|
|
210
|
+
- pnpm 11+
|
|
211
|
+
- Python 3.12+
|
|
212
|
+
- uv
|
|
213
|
+
|
|
214
|
+
Bootstrapping:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pnpm install
|
|
218
|
+
pnpm build
|
|
219
|
+
pnpm check
|
|
220
|
+
UV_CACHE_DIR=/tmp/uv-cache uv run --group dev ruff check .
|
|
221
|
+
UV_CACHE_DIR=/tmp/uv-cache uv run --group dev pytest tests/py -q
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Release flow
|
|
225
|
+
|
|
226
|
+
Full release gate:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pnpm install
|
|
230
|
+
npm run release:verify
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Publish Python:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
export UV_PUBLISH_TOKEN=...
|
|
237
|
+
npm run release:publish:py
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Publish npm helpers:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
export NPM_TOKEN=...
|
|
244
|
+
npm run release:publish:npm
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
More detail lives in `docs/publishing.md`.
|
|
248
|
+
|
|
249
|
+
## Repo structure
|
|
250
|
+
|
|
251
|
+
- `servers/remote-mcp-py`: hosted Python runtime for `multify-mcp`
|
|
252
|
+
- `src`: TypeScript schemas and helpers shipped in the public npm package `multify-mcp`
|
|
253
|
+
- `tests/py`: auth, HTTP, and tool-service coverage
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { buildClaudeCodeAddArgs, buildClaudeCodeAddCommand } from "./index.js";
|
|
5
|
+
function printHelp() {
|
|
6
|
+
process.stdout.write(`multify-mcp
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
multify-mcp claude add --token <workspace-mcp-key> [options]
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
--token <value> Workspace MCP key from Multify
|
|
13
|
+
--scope <value> Claude config scope: local, user, or project
|
|
14
|
+
--server-name <value> Claude MCP server name (default: multify-mcp)
|
|
15
|
+
--url <value> Override hosted MCP URL (default: https://api.multifyco.com/mcp)
|
|
16
|
+
--print Print the underlying claude command instead of executing it
|
|
17
|
+
-h, --help Show help
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
multify-mcp claude add --token sk_live_xxx
|
|
21
|
+
multify-mcp claude add --scope user --token sk_live_xxx
|
|
22
|
+
multify-mcp claude add --token sk_live_xxx --print
|
|
23
|
+
`);
|
|
24
|
+
}
|
|
25
|
+
function fail(message) {
|
|
26
|
+
process.stderr.write(`${message}\n`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
function parseClaudeAddArgs(argv) {
|
|
30
|
+
const parsed = { printOnly: false };
|
|
31
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
32
|
+
const current = argv[index];
|
|
33
|
+
switch (current) {
|
|
34
|
+
case "--token":
|
|
35
|
+
parsed.token = argv[index + 1];
|
|
36
|
+
index += 1;
|
|
37
|
+
break;
|
|
38
|
+
case "--scope":
|
|
39
|
+
parsed.scope = argv[index + 1];
|
|
40
|
+
index += 1;
|
|
41
|
+
break;
|
|
42
|
+
case "--server-name":
|
|
43
|
+
parsed.serverName = argv[index + 1];
|
|
44
|
+
index += 1;
|
|
45
|
+
break;
|
|
46
|
+
case "--url":
|
|
47
|
+
parsed.url = argv[index + 1];
|
|
48
|
+
index += 1;
|
|
49
|
+
break;
|
|
50
|
+
case "--print":
|
|
51
|
+
parsed.printOnly = true;
|
|
52
|
+
break;
|
|
53
|
+
case "-h":
|
|
54
|
+
case "--help":
|
|
55
|
+
printHelp();
|
|
56
|
+
process.exit(0);
|
|
57
|
+
default:
|
|
58
|
+
fail(`Unknown argument: ${current}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!parsed.token?.trim()) {
|
|
62
|
+
fail("Missing required --token value");
|
|
63
|
+
}
|
|
64
|
+
return parsed;
|
|
65
|
+
}
|
|
66
|
+
function run() {
|
|
67
|
+
const args = process.argv.slice(2);
|
|
68
|
+
if (args.length === 0 || args.includes("-h") || args.includes("--help")) {
|
|
69
|
+
printHelp();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (args[0] !== "claude" || args[1] !== "add") {
|
|
73
|
+
fail("Only `multify-mcp claude add` is supported");
|
|
74
|
+
}
|
|
75
|
+
const parsed = parseClaudeAddArgs(args.slice(2));
|
|
76
|
+
const command = buildClaudeCodeAddCommand(parsed.token ?? "", {
|
|
77
|
+
scope: parsed.scope,
|
|
78
|
+
serverName: parsed.serverName,
|
|
79
|
+
url: parsed.url,
|
|
80
|
+
});
|
|
81
|
+
if (parsed.printOnly) {
|
|
82
|
+
process.stdout.write(`${command}\n`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const result = spawnSync("claude", buildClaudeCodeAddArgs(parsed.token ?? "", {
|
|
86
|
+
scope: parsed.scope,
|
|
87
|
+
serverName: parsed.serverName,
|
|
88
|
+
url: parsed.url,
|
|
89
|
+
}), { stdio: "inherit" });
|
|
90
|
+
if (result.error) {
|
|
91
|
+
fail(result.error.message);
|
|
92
|
+
}
|
|
93
|
+
process.exit(result.status ?? 0);
|
|
94
|
+
}
|
|
95
|
+
run();
|