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 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
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
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();