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/dist/index.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const DEFAULT_MULTIFY_MCP_URL = "https://api.multifyco.com/mcp";
|
|
4
|
+
export const mcpScopeValues = [
|
|
5
|
+
"contacts.read",
|
|
6
|
+
"contacts.create",
|
|
7
|
+
"contacts.update",
|
|
8
|
+
"contacts.bulk_create",
|
|
9
|
+
"contacts.bulk_update",
|
|
10
|
+
"lists.read",
|
|
11
|
+
"lists.create",
|
|
12
|
+
"lists.update",
|
|
13
|
+
"lists.bulk_create",
|
|
14
|
+
"lists.bulk_update",
|
|
15
|
+
"lists.membership.write",
|
|
16
|
+
];
|
|
17
|
+
export const mcpScopeSchema = z.enum(mcpScopeValues);
|
|
18
|
+
export const claudeCodeScopeValues = ["local", "user", "project"];
|
|
19
|
+
export const claudeCodeScopeSchema = z.enum(claudeCodeScopeValues);
|
|
20
|
+
export const remoteMcpConnectionSchema = z.object({
|
|
21
|
+
name: z.string().min(1),
|
|
22
|
+
transport: z.literal("http"),
|
|
23
|
+
url: z.string().url(),
|
|
24
|
+
headers: z.record(z.string(), z.string()),
|
|
25
|
+
});
|
|
26
|
+
export const apiKeyIntrospectionSchema = z.object({
|
|
27
|
+
key_id: z.string().min(1),
|
|
28
|
+
workspace_id: z.string().min(1),
|
|
29
|
+
actor_user_id: z.string().min(1),
|
|
30
|
+
scopes: z.array(mcpScopeSchema).default([]),
|
|
31
|
+
rate_limit_per_minute: z.number().int().min(1).max(10_000).default(120),
|
|
32
|
+
active: z.boolean().default(true),
|
|
33
|
+
});
|
|
34
|
+
export const contactCreateInputSchema = z.object({
|
|
35
|
+
list_id: z.string().min(1),
|
|
36
|
+
full_name: z.string().min(1).max(255),
|
|
37
|
+
email: z.string().min(1).max(320),
|
|
38
|
+
phone: z.string().optional().nullable(),
|
|
39
|
+
whatsapp_number: z.string().optional().nullable(),
|
|
40
|
+
job_title: z.string().optional().nullable(),
|
|
41
|
+
company_name: z.string().optional().nullable(),
|
|
42
|
+
company_domain: z.string().optional().nullable(),
|
|
43
|
+
linkedin_url: z.string().optional().nullable(),
|
|
44
|
+
country: z.string().optional().nullable(),
|
|
45
|
+
source: z.string().optional().nullable(),
|
|
46
|
+
tags: z.array(z.string()).default([]),
|
|
47
|
+
});
|
|
48
|
+
export const contactUpdateInputSchema = z.object({
|
|
49
|
+
list_id: z.string().min(1).optional().nullable(),
|
|
50
|
+
full_name: z.string().min(1).max(255).optional().nullable(),
|
|
51
|
+
email: z.string().min(1).max(320).optional().nullable(),
|
|
52
|
+
phone: z.string().optional().nullable(),
|
|
53
|
+
whatsapp_number: z.string().optional().nullable(),
|
|
54
|
+
job_title: z.string().optional().nullable(),
|
|
55
|
+
company_name: z.string().optional().nullable(),
|
|
56
|
+
company_domain: z.string().optional().nullable(),
|
|
57
|
+
linkedin_url: z.string().optional().nullable(),
|
|
58
|
+
country: z.string().optional().nullable(),
|
|
59
|
+
source: z.string().optional().nullable(),
|
|
60
|
+
tags: z.array(z.string()).optional().nullable(),
|
|
61
|
+
});
|
|
62
|
+
export const contactSchema = z.object({
|
|
63
|
+
id: z.string().min(1),
|
|
64
|
+
workspace_id: z.string().min(1),
|
|
65
|
+
list_id: z.string().min(1),
|
|
66
|
+
full_name: z.string().optional().nullable(),
|
|
67
|
+
email: z.string().optional().nullable(),
|
|
68
|
+
phone: z.string().optional().nullable(),
|
|
69
|
+
whatsapp_number: z.string().optional().nullable(),
|
|
70
|
+
job_title: z.string().optional().nullable(),
|
|
71
|
+
company_name: z.string().optional().nullable(),
|
|
72
|
+
company_domain: z.string().optional().nullable(),
|
|
73
|
+
linkedin_url: z.string().optional().nullable(),
|
|
74
|
+
country: z.string().optional().nullable(),
|
|
75
|
+
source: z.string().optional().nullable(),
|
|
76
|
+
tags: z.array(z.string()).default([]),
|
|
77
|
+
});
|
|
78
|
+
export const contactBulkCreateInputSchema = z.object({
|
|
79
|
+
idempotency_key: z.string().min(1).max(255),
|
|
80
|
+
items: z.array(contactCreateInputSchema).min(1).max(500),
|
|
81
|
+
});
|
|
82
|
+
export const contactBulkUpdateItemSchema = z.object({
|
|
83
|
+
contact_id: z.string().min(1),
|
|
84
|
+
patch: contactUpdateInputSchema,
|
|
85
|
+
});
|
|
86
|
+
export const contactBulkUpdateInputSchema = z.object({
|
|
87
|
+
idempotency_key: z.string().min(1).max(255),
|
|
88
|
+
items: z.array(contactBulkUpdateItemSchema).min(1).max(500),
|
|
89
|
+
});
|
|
90
|
+
export const listCreateInputSchema = z.object({
|
|
91
|
+
name: z.string().min(1).max(255),
|
|
92
|
+
description: z.string().optional().nullable(),
|
|
93
|
+
});
|
|
94
|
+
export const listUpdateInputSchema = z.object({
|
|
95
|
+
name: z.string().min(1).max(255).optional().nullable(),
|
|
96
|
+
description: z.string().optional().nullable(),
|
|
97
|
+
});
|
|
98
|
+
export const workspaceListSchema = z.object({
|
|
99
|
+
id: z.string().min(1),
|
|
100
|
+
workspace_id: z.string().min(1),
|
|
101
|
+
name: z.string().min(1),
|
|
102
|
+
description: z.string().optional().nullable(),
|
|
103
|
+
});
|
|
104
|
+
export const listBulkCreateInputSchema = z.object({
|
|
105
|
+
idempotency_key: z.string().min(1).max(255),
|
|
106
|
+
items: z.array(listCreateInputSchema).min(1).max(500),
|
|
107
|
+
});
|
|
108
|
+
export const listBulkUpdateItemSchema = z.object({
|
|
109
|
+
list_id: z.string().min(1),
|
|
110
|
+
patch: listUpdateInputSchema,
|
|
111
|
+
});
|
|
112
|
+
export const listBulkUpdateInputSchema = z.object({
|
|
113
|
+
idempotency_key: z.string().min(1).max(255),
|
|
114
|
+
items: z.array(listBulkUpdateItemSchema).min(1).max(500),
|
|
115
|
+
});
|
|
116
|
+
export const listAddContactsInputSchema = z.object({
|
|
117
|
+
list_id: z.string().min(1),
|
|
118
|
+
contact_ids: z.array(z.string().min(1)).min(1).max(500),
|
|
119
|
+
idempotency_key: z.string().min(1).max(255).optional().nullable(),
|
|
120
|
+
});
|
|
121
|
+
export const listRemoveContactsInputSchema = z.object({
|
|
122
|
+
list_id: z.string().min(1),
|
|
123
|
+
contact_ids: z.array(z.string().min(1)).min(1).max(500),
|
|
124
|
+
idempotency_key: z.string().min(1).max(255).optional().nullable(),
|
|
125
|
+
});
|
|
126
|
+
export const listReplaceContactsInputSchema = z.object({
|
|
127
|
+
list_id: z.string().min(1),
|
|
128
|
+
contact_ids: z.array(z.string().min(1)).max(500).default([]),
|
|
129
|
+
idempotency_key: z.string().min(1).max(255).optional().nullable(),
|
|
130
|
+
});
|
|
131
|
+
export const listBulkMembershipItemSchema = z.object({
|
|
132
|
+
list_id: z.string().min(1),
|
|
133
|
+
contact_ids: z.array(z.string().min(1)).min(1).max(500),
|
|
134
|
+
});
|
|
135
|
+
export const listBulkAddContactsInputSchema = z.object({
|
|
136
|
+
idempotency_key: z.string().min(1).max(255),
|
|
137
|
+
items: z.array(listBulkMembershipItemSchema).min(1).max(500),
|
|
138
|
+
});
|
|
139
|
+
export const resourcePayloadSchema = z.union([contactSchema, workspaceListSchema]);
|
|
140
|
+
export const resourceOperationResultSchema = z.object({
|
|
141
|
+
operation_id: z.string().min(1),
|
|
142
|
+
workspace_id: z.string().min(1),
|
|
143
|
+
resource: resourcePayloadSchema,
|
|
144
|
+
});
|
|
145
|
+
export const collectionOperationResultSchema = z.object({
|
|
146
|
+
operation_id: z.string().min(1),
|
|
147
|
+
workspace_id: z.string().min(1),
|
|
148
|
+
items: z.array(resourcePayloadSchema).default([]),
|
|
149
|
+
total: z.number().int().min(0),
|
|
150
|
+
});
|
|
151
|
+
export const bulkItemErrorSchema = z.object({
|
|
152
|
+
code: z.string().min(1),
|
|
153
|
+
message: z.string().min(1),
|
|
154
|
+
});
|
|
155
|
+
export const bulkItemResultSchema = z.object({
|
|
156
|
+
index: z.number().int().min(0),
|
|
157
|
+
status: z.enum(["success", "error"]),
|
|
158
|
+
item_id: z.string().optional().nullable(),
|
|
159
|
+
resource: resourcePayloadSchema.optional().nullable(),
|
|
160
|
+
error: bulkItemErrorSchema.optional().nullable(),
|
|
161
|
+
});
|
|
162
|
+
export const bulkOperationResultSchema = z.object({
|
|
163
|
+
operation_id: z.string().min(1),
|
|
164
|
+
workspace_id: z.string().min(1),
|
|
165
|
+
total_count: z.number().int().min(0),
|
|
166
|
+
succeeded_count: z.number().int().min(0),
|
|
167
|
+
failed_count: z.number().int().min(0),
|
|
168
|
+
results: z.array(bulkItemResultSchema).default([]),
|
|
169
|
+
});
|
|
170
|
+
export const membershipOperationResultSchema = z.object({
|
|
171
|
+
operation_id: z.string().min(1),
|
|
172
|
+
workspace_id: z.string().min(1),
|
|
173
|
+
affected_count: z.number().int().min(0),
|
|
174
|
+
list_ids: z.array(z.string().min(1)).default([]),
|
|
175
|
+
});
|
|
176
|
+
export function getMultifyMcpUrl(override) {
|
|
177
|
+
const normalized = override?.trim();
|
|
178
|
+
return normalized && normalized.length > 0 ? normalized : DEFAULT_MULTIFY_MCP_URL;
|
|
179
|
+
}
|
|
180
|
+
export function buildAuthorizationHeaders(apiKey) {
|
|
181
|
+
const normalized = apiKey.trim();
|
|
182
|
+
if (!normalized) {
|
|
183
|
+
throw new Error("apiKey must be a non-empty string");
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
Authorization: `Bearer ${normalized}`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
export function ensureIdempotencyKey(value) {
|
|
190
|
+
const normalized = value?.trim();
|
|
191
|
+
return normalized && normalized.length > 0 ? normalized : randomUUID();
|
|
192
|
+
}
|
|
193
|
+
export function buildRemoteMcpConnection(apiKey, options) {
|
|
194
|
+
const name = options?.serverName?.trim() || "multify-mcp";
|
|
195
|
+
const url = getMultifyMcpUrl(options?.url);
|
|
196
|
+
const headers = buildAuthorizationHeaders(apiKey);
|
|
197
|
+
return remoteMcpConnectionSchema.parse({
|
|
198
|
+
name,
|
|
199
|
+
transport: "http",
|
|
200
|
+
url,
|
|
201
|
+
headers,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
export function buildClaudeCodeAddArgs(apiKey, options) {
|
|
205
|
+
const scope = options?.scope ? claudeCodeScopeSchema.parse(options.scope) : null;
|
|
206
|
+
const connection = buildRemoteMcpConnection(apiKey, options);
|
|
207
|
+
const args = ["mcp", "add", "--transport", "http"];
|
|
208
|
+
if (scope) {
|
|
209
|
+
args.push("--scope", scope);
|
|
210
|
+
}
|
|
211
|
+
args.push(connection.name, connection.url, "--header", `Authorization: ${connection.headers.Authorization}`);
|
|
212
|
+
return args;
|
|
213
|
+
}
|
|
214
|
+
function quoteShellArg(value) {
|
|
215
|
+
if (/^[A-Za-z0-9_./:@=-]+$/.test(value)) {
|
|
216
|
+
return value;
|
|
217
|
+
}
|
|
218
|
+
return `'${value.replace(/'/g, `'\"'\"'`)}'`;
|
|
219
|
+
}
|
|
220
|
+
export function buildClaudeCodeAddCommand(apiKey, options) {
|
|
221
|
+
return ["claude", ...buildClaudeCodeAddArgs(apiKey, options)].map(quoteShellArg).join(" ");
|
|
222
|
+
}
|
|
223
|
+
export function normalizeScopes(scopes) {
|
|
224
|
+
const unique = [...new Set(Array.from(scopes, (scope) => scope.trim()).filter(Boolean))];
|
|
225
|
+
return unique.map((scope) => mcpScopeSchema.parse(scope));
|
|
226
|
+
}
|
|
227
|
+
export function normalizeContactBulkCreateInput(input) {
|
|
228
|
+
return contactBulkCreateInputSchema.parse({
|
|
229
|
+
...input,
|
|
230
|
+
idempotency_key: ensureIdempotencyKey(input.idempotency_key),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
export function normalizeListBulkAddContactsInput(input) {
|
|
234
|
+
return listBulkAddContactsInputSchema.parse({
|
|
235
|
+
...input,
|
|
236
|
+
idempotency_key: ensureIdempotencyKey(input.idempotency_key),
|
|
237
|
+
});
|
|
238
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "multify-mcp",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "JavaScript helpers and schemas for the hosted Multify MCP.",
|
|
5
|
+
"packageManager": "pnpm@11.0.8",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"bin": {
|
|
11
|
+
"multify-mcp": "./dist/cli.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "rm -rf dist && tsc -p tsconfig.json",
|
|
25
|
+
"check": "tsc --noEmit -p tsconfig.json",
|
|
26
|
+
"test": "pnpm run check",
|
|
27
|
+
"release:verify": "npm run build && npm run check && UV_CACHE_DIR=/tmp/uv-cache uv run --group dev ruff check . && UV_CACHE_DIR=/tmp/uv-cache uv run --group dev pytest tests/py -q && node ./scripts/release/verify-npm-packages.mjs && bash ./scripts/release/verify-python-packages.sh",
|
|
28
|
+
"release:pack:npm": "node ./scripts/release/verify-npm-packages.mjs",
|
|
29
|
+
"release:pack:py": "bash ./scripts/release/verify-python-packages.sh",
|
|
30
|
+
"release:publish:npm": "bash ./scripts/release/publish-npm.sh",
|
|
31
|
+
"release:publish:py": "bash ./scripts/release/publish-pypi.sh"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"zod": "^3.25.76"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^24.3.0",
|
|
41
|
+
"typescript": "^5.9.2"
|
|
42
|
+
}
|
|
43
|
+
}
|