@kynver-app/mcp-estimator 0.2.8 → 0.2.11
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/dist/lib/mcp-sse-server.d.ts +15 -0
- package/dist/lib/mcp-sse-server.js +103 -2
- package/dist/server.js +16 -0
- package/dist/tools/catalog-tools.js +1 -1
- package/dist/tools/email-pdf-tools.d.ts +45 -0
- package/dist/tools/email-pdf-tools.js +21 -0
- package/dist/tools/intake-tools.d.ts +463 -0
- package/dist/tools/intake-tools.js +82 -0
- package/dist/tools/price-catalog-tools.d.ts +96 -0
- package/dist/tools/price-catalog-tools.js +72 -0
- package/dist/tools/remodel-deliverable-tools.d.ts +1868 -0
- package/dist/tools/remodel-deliverable-tools.js +138 -0
- package/dist/tools/remodel-estimate-tools.d.ts +525 -0
- package/dist/tools/remodel-estimate-tools.js +71 -0
- package/dist/tools/takeoff-tools.d.ts +274 -0
- package/dist/tools/takeoff-tools.js +38 -4
- package/dist/tools/vision-tools.js +2 -2
- package/package.json +6 -1
- package/src/index.ts +0 -18
- package/src/lib/kynver-client.ts +0 -136
- package/src/lib/mcp-sse-server.ts +0 -249
- package/src/lib/package-version.ts +0 -55
- package/src/server.ts +0 -141
- package/src/sse.ts +0 -10
- package/src/tools/catalog-tools.ts +0 -217
- package/src/tools/client-tools.ts +0 -114
- package/src/tools/company-tools.ts +0 -108
- package/src/tools/email-pdf-tools.ts +0 -27
- package/src/tools/estimate-tools.ts +0 -281
- package/src/tools/intake-tools.ts +0 -95
- package/src/tools/photo-tools.ts +0 -60
- package/src/tools/price-catalog-tools.ts +0 -80
- package/src/tools/remodel-deliverable-tools.ts +0 -163
- package/src/tools/remodel-estimate-tools.ts +0 -80
- package/src/tools/stats-pdf-tools.ts +0 -80
- package/src/tools/takeoff-tools.ts +0 -319
- package/src/tools/template-tools.ts +0 -80
- package/src/tools/version-tools.ts +0 -39
- package/src/tools/vision-tools.ts +0 -48
- package/tsconfig.json +0 -15
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import * as client from "../lib/kynver-client.js";
|
|
3
|
-
|
|
4
|
-
// --- estimator_import_catalog ---
|
|
5
|
-
|
|
6
|
-
const importCatalogSchema = z.object({
|
|
7
|
-
message: z.string(),
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
export const importCatalog = {
|
|
11
|
-
name: "estimator_import_catalog",
|
|
12
|
-
description:
|
|
13
|
-
"Start a catalog import from a file. This is a placeholder — MCP cannot do file uploads. Describe the import request in the message field and the system will guide the user through the upload flow.",
|
|
14
|
-
inputSchemaZod: importCatalogSchema,
|
|
15
|
-
execute: async (args: z.infer<typeof importCatalogSchema>): Promise<string> => {
|
|
16
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
17
|
-
const data = await client.post("/v1/agents/kynver-estimator/chat", {
|
|
18
|
-
message: args.message,
|
|
19
|
-
});
|
|
20
|
-
return JSON.stringify(data);
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// --- estimator_review_import ---
|
|
25
|
-
|
|
26
|
-
const reviewImportSchema = z.object({
|
|
27
|
-
importId: z.string(),
|
|
28
|
-
approved: z.boolean().optional(),
|
|
29
|
-
changes: z
|
|
30
|
-
.array(
|
|
31
|
-
z.object({
|
|
32
|
-
index: z.number(),
|
|
33
|
-
action: z.enum(["keep", "skip", "edit"]),
|
|
34
|
-
edits: z.record(z.string(), z.unknown()).optional(),
|
|
35
|
-
})
|
|
36
|
-
)
|
|
37
|
-
.optional(),
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
export const reviewImport = {
|
|
41
|
-
name: "estimator_review_import",
|
|
42
|
-
description: "Review a pending catalog import batch before committing. Shows parsed rows with suggested service codes, categories, and pricing. You can approve all, skip individual rows, or edit values before committing. Requires an importId from a previous import.",
|
|
43
|
-
inputSchemaZod: reviewImportSchema,
|
|
44
|
-
execute: async (args: z.infer<typeof reviewImportSchema>): Promise<string> => {
|
|
45
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
46
|
-
const { importId, ...body } = args;
|
|
47
|
-
const data = await client.patch(`/estimator/import/${importId}`, body);
|
|
48
|
-
return JSON.stringify(data);
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
// --- estimator_commit_import ---
|
|
53
|
-
|
|
54
|
-
const commitImportSchema = z.object({
|
|
55
|
-
importId: z.string(),
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
export const commitImport = {
|
|
59
|
-
name: "estimator_commit_import",
|
|
60
|
-
description: "Finalize a reviewed catalog import, writing all approved rows into the user's service catalog. Requires an importId that has been reviewed. This action cannot be undone — rows are added permanently.",
|
|
61
|
-
inputSchemaZod: commitImportSchema,
|
|
62
|
-
execute: async (args: z.infer<typeof commitImportSchema>): Promise<string> => {
|
|
63
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
64
|
-
const data = await client.post(`/estimator/import/${args.importId}/commit`, {});
|
|
65
|
-
return JSON.stringify(data);
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// --- estimator_setup_price ---
|
|
70
|
-
|
|
71
|
-
const setupPriceSchema = z.object({
|
|
72
|
-
name: z.string(),
|
|
73
|
-
serviceCode: z.string(),
|
|
74
|
-
category: z.string(),
|
|
75
|
-
materialType: z.string().nullable().optional(),
|
|
76
|
-
unit: z.string(),
|
|
77
|
-
materialCost: z.number().min(0),
|
|
78
|
-
laborCost: z.number().min(0),
|
|
79
|
-
minCharge: z.number().min(0).optional(),
|
|
80
|
-
notes: z.string().optional(),
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
export const setupPrice = {
|
|
84
|
-
name: "estimator_setup_price",
|
|
85
|
-
description: "Add a new service to the user's price book (service catalog). Every service needs: a unique serviceCode (e.g., ROOF_INSPECT, SHINGLE_REPAIR), a display name, category (tear_off, decking, underlayment, field_install, flashing, ridge_cap, ventilation, gutters, cleanup, permit, misc, etc.), unit (sq_ft, linear_ft, each, sheet, roll, bundle, hour, lump), and material/labor costs. Items in the catalog are used when creating estimates.",
|
|
86
|
-
inputSchemaZod: setupPriceSchema,
|
|
87
|
-
execute: async (args: z.infer<typeof setupPriceSchema>): Promise<string> => {
|
|
88
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
89
|
-
const data = await client.post("/estimator/catalog", args);
|
|
90
|
-
return JSON.stringify(data);
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// --- estimator_update_price ---
|
|
95
|
-
|
|
96
|
-
const updatePriceSchema = z.object({
|
|
97
|
-
itemId: z.string(),
|
|
98
|
-
name: z.string().optional(),
|
|
99
|
-
serviceCode: z.string().optional(),
|
|
100
|
-
category: z.string().optional(),
|
|
101
|
-
materialType: z.string().nullable().optional(),
|
|
102
|
-
unit: z.string().optional(),
|
|
103
|
-
materialCost: z.number().min(0).optional(),
|
|
104
|
-
laborCost: z.number().min(0).optional(),
|
|
105
|
-
minCharge: z.number().min(0).optional(),
|
|
106
|
-
notes: z.string().optional(),
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
export const updatePrice = {
|
|
110
|
-
name: "estimator_update_price",
|
|
111
|
-
description: "Update an existing item in the service catalog. Use estimator_get_catalog first to find the itemId. You can change the name, service code, category, unit, material cost, labor cost, minimum charge, or notes.",
|
|
112
|
-
inputSchemaZod: updatePriceSchema,
|
|
113
|
-
execute: async (args: z.infer<typeof updatePriceSchema>): Promise<string> => {
|
|
114
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
115
|
-
const { itemId, ...body } = args;
|
|
116
|
-
const data = await client.patch(`/estimator/catalog/${itemId}`, body);
|
|
117
|
-
return JSON.stringify(data);
|
|
118
|
-
},
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
// --- estimator_get_catalog ---
|
|
122
|
-
|
|
123
|
-
const getCatalogSchema = z.object({
|
|
124
|
-
category: z.string().optional(),
|
|
125
|
-
materialType: z.string().optional(),
|
|
126
|
-
limit: z.number().optional(),
|
|
127
|
-
cursor: z.string().optional(),
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
export const getCatalog = {
|
|
131
|
-
name: "estimator_get_catalog",
|
|
132
|
-
description:
|
|
133
|
-
"List all items in the user's service catalog (price book). Returns service codes, names, categories, units, and material/labor costs. IMPORTANT: Always call this before creating estimates to get valid service codes. Optional filters: category (tear_off, decking, underlayment, field_install, ridge_hip, flashing, misc, …) and materialType (asphalt_architectural, asphalt_3tab, metal_standing_seam, tpo, …) must match those exact enum strings — when unsure, omit both to fetch the full catalog.",
|
|
134
|
-
inputSchemaZod: getCatalogSchema,
|
|
135
|
-
execute: async (args: z.infer<typeof getCatalogSchema>): Promise<string> => {
|
|
136
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
137
|
-
|
|
138
|
-
const params = new URLSearchParams();
|
|
139
|
-
if (args.category) params.set("category", args.category);
|
|
140
|
-
if (args.materialType) params.set("materialType", args.materialType);
|
|
141
|
-
if (args.limit != null) params.set("limit", String(args.limit));
|
|
142
|
-
if (args.cursor) params.set("cursor", args.cursor);
|
|
143
|
-
|
|
144
|
-
const q = params.toString();
|
|
145
|
-
const path = q ? `/estimator/catalog?${q}` : "/estimator/catalog";
|
|
146
|
-
|
|
147
|
-
const data = await client.get<unknown>(path);
|
|
148
|
-
return JSON.stringify(data);
|
|
149
|
-
},
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// --- estimator_get_config ---
|
|
153
|
-
|
|
154
|
-
const getConfigSchema = z.object({});
|
|
155
|
-
|
|
156
|
-
export const getConfig = {
|
|
157
|
-
name: "estimator_get_config",
|
|
158
|
-
description: "Get the user's estimator settings — company name, default markup/waste percentage, tax rate, currency, contact info, license number, and default notes/terms for estimates.",
|
|
159
|
-
inputSchemaZod: getConfigSchema,
|
|
160
|
-
execute: async (_args: z.infer<typeof getConfigSchema>): Promise<string> => {
|
|
161
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
162
|
-
const data = await client.get<unknown>("/estimator/config");
|
|
163
|
-
return JSON.stringify(data);
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
// --- estimator_update_config ---
|
|
168
|
-
|
|
169
|
-
const updateConfigSchema = z.object({
|
|
170
|
-
defaultMarkup: z.number().optional(),
|
|
171
|
-
taxRate: z.number().optional(),
|
|
172
|
-
companyName: z.string().optional(),
|
|
173
|
-
companyPhone: z.string().optional(),
|
|
174
|
-
companyEmail: z.string().optional(),
|
|
175
|
-
companyAddress: z.string().optional(),
|
|
176
|
-
companyLogo: z.string().optional(),
|
|
177
|
-
licenseNumber: z.string().optional(),
|
|
178
|
-
defaultNotes: z.string().optional(),
|
|
179
|
-
defaultTerms: z.string().optional(),
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
export const updateConfig = {
|
|
183
|
-
name: "estimator_update_config",
|
|
184
|
-
description: "Update the user's estimator settings. You can set: companyName, defaultMarkup (waste %), taxRate, companyPhone, companyEmail, companyAddress, companyLogo (URL), licenseNumber, defaultNotes (appended to every estimate), and defaultTerms (terms & conditions).",
|
|
185
|
-
inputSchemaZod: updateConfigSchema,
|
|
186
|
-
execute: async (args: z.infer<typeof updateConfigSchema>): Promise<string> => {
|
|
187
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
188
|
-
const data = await client.patch("/estimator/config", args);
|
|
189
|
-
return JSON.stringify(data);
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// --- estimator_import_template ---
|
|
194
|
-
|
|
195
|
-
const importTemplateSchema = z.object({
|
|
196
|
-
templateId: z.string().optional(),
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
export const importTemplate = {
|
|
200
|
-
name: "estimator_import_template",
|
|
201
|
-
description:
|
|
202
|
-
"Import a pre-built pricing catalog template into the user's service catalog. Call without arguments to see available templates (returns id, name, description, item count). Call with a templateId to import all items. These are MATERIAL COSTS ONLY — users should add their own labor rates after import. Items that already exist in the user's catalog are skipped (never overwritten).",
|
|
203
|
-
inputSchemaZod: importTemplateSchema,
|
|
204
|
-
execute: async (args: z.infer<typeof importTemplateSchema>): Promise<string> => {
|
|
205
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
206
|
-
|
|
207
|
-
if (!args.templateId) {
|
|
208
|
-
const data = await client.get("/estimator/catalog/templates");
|
|
209
|
-
return JSON.stringify(data);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const data = await client.post("/estimator/catalog/templates", {
|
|
213
|
-
templateId: args.templateId,
|
|
214
|
-
});
|
|
215
|
-
return JSON.stringify(data);
|
|
216
|
-
},
|
|
217
|
-
};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import * as client from "../lib/kynver-client.js";
|
|
3
|
-
|
|
4
|
-
// --- estimator_create_client ---
|
|
5
|
-
|
|
6
|
-
const createClientSchema = z.object({
|
|
7
|
-
name: z.string(),
|
|
8
|
-
companyId: z.string().optional(),
|
|
9
|
-
title: z.string().optional(),
|
|
10
|
-
phone: z.string().optional(),
|
|
11
|
-
email: z.string().optional(),
|
|
12
|
-
address: z.string().optional(),
|
|
13
|
-
notes: z.string().optional(),
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
export const createClient = {
|
|
17
|
-
name: "estimator_create_client",
|
|
18
|
-
description: "Create a new client (individual person) in the client book. Clients can optionally belong to a company (pass companyId). Include contact details: name (required), title, phone, email, address, and notes. The client can then be linked to estimates via their clientId.",
|
|
19
|
-
inputSchemaZod: createClientSchema,
|
|
20
|
-
execute: async (args: z.infer<typeof createClientSchema>): Promise<string> => {
|
|
21
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
22
|
-
const data = await client.post("/estimator/clients", args);
|
|
23
|
-
return JSON.stringify(data);
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// --- estimator_list_clients ---
|
|
28
|
-
|
|
29
|
-
const listClientsSchema = z.object({
|
|
30
|
-
search: z.string().optional(),
|
|
31
|
-
companyId: z.string().optional(),
|
|
32
|
-
limit: z.number().optional(),
|
|
33
|
-
cursor: z.string().optional(),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
export const listClients = {
|
|
37
|
-
name: "estimator_list_clients",
|
|
38
|
-
description: "Search and list clients in the client book. Use the search param to find clients by name, phone, or email. Filter by companyId to see all contacts at a specific company. Returns client details with linked company info. Always search here before creating a new client to avoid duplicates.",
|
|
39
|
-
inputSchemaZod: listClientsSchema,
|
|
40
|
-
execute: async (args: z.infer<typeof listClientsSchema>): Promise<string> => {
|
|
41
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
42
|
-
|
|
43
|
-
const params = new URLSearchParams();
|
|
44
|
-
if (args.search) params.set("search", args.search);
|
|
45
|
-
if (args.companyId) params.set("companyId", args.companyId);
|
|
46
|
-
if (args.limit != null) params.set("limit", String(args.limit));
|
|
47
|
-
if (args.cursor) params.set("cursor", args.cursor);
|
|
48
|
-
|
|
49
|
-
const q = params.toString();
|
|
50
|
-
const path = q ? `/estimator/clients?${q}` : "/estimator/clients";
|
|
51
|
-
|
|
52
|
-
const data = await client.get<unknown>(path);
|
|
53
|
-
return JSON.stringify(data);
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
// --- estimator_get_client ---
|
|
58
|
-
|
|
59
|
-
const getClientSchema = z.object({
|
|
60
|
-
clientId: z.string(),
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
export const getClient = {
|
|
64
|
-
name: "estimator_get_client",
|
|
65
|
-
description: "Get a specific client's full details including their company info and a summary of linked estimates (recent 10). Use this to see a client's estimate history before creating new ones.",
|
|
66
|
-
inputSchemaZod: getClientSchema,
|
|
67
|
-
execute: async (args: z.infer<typeof getClientSchema>): Promise<string> => {
|
|
68
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
69
|
-
const data = await client.get<unknown>(`/estimator/clients/${args.clientId}`);
|
|
70
|
-
return JSON.stringify(data);
|
|
71
|
-
},
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
// --- estimator_update_client ---
|
|
75
|
-
|
|
76
|
-
const updateClientSchema = z.object({
|
|
77
|
-
clientId: z.string(),
|
|
78
|
-
name: z.string().optional(),
|
|
79
|
-
companyId: z.string().optional(),
|
|
80
|
-
title: z.string().optional(),
|
|
81
|
-
phone: z.string().optional(),
|
|
82
|
-
email: z.string().optional(),
|
|
83
|
-
address: z.string().optional(),
|
|
84
|
-
notes: z.string().optional(),
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
export const updateClient = {
|
|
88
|
-
name: "estimator_update_client",
|
|
89
|
-
description: "Update a client's contact details. Pass the clientId and any fields to change: name, title, phone, email, address, notes, or companyId (to move them to a different company).",
|
|
90
|
-
inputSchemaZod: updateClientSchema,
|
|
91
|
-
execute: async (args: z.infer<typeof updateClientSchema>): Promise<string> => {
|
|
92
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
93
|
-
const { clientId, ...body } = args;
|
|
94
|
-
const data = await client.patch(`/estimator/clients/${clientId}`, body);
|
|
95
|
-
return JSON.stringify(data);
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// --- estimator_delete_client ---
|
|
100
|
-
|
|
101
|
-
const deleteClientSchema = z.object({
|
|
102
|
-
clientId: z.string(),
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
export const deleteClient = {
|
|
106
|
-
name: "estimator_delete_client",
|
|
107
|
-
description: "Soft-delete a client from the client book. The client's data is preserved but hidden from searches. Existing estimates linked to this client are not affected.",
|
|
108
|
-
inputSchemaZod: deleteClientSchema,
|
|
109
|
-
execute: async (args: z.infer<typeof deleteClientSchema>): Promise<string> => {
|
|
110
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
111
|
-
const data = await client.del(`/estimator/clients/${args.clientId}`);
|
|
112
|
-
return JSON.stringify(data);
|
|
113
|
-
},
|
|
114
|
-
};
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import * as client from "../lib/kynver-client.js";
|
|
3
|
-
|
|
4
|
-
// --- estimator_create_company ---
|
|
5
|
-
|
|
6
|
-
const createCompanySchema = z.object({
|
|
7
|
-
name: z.string(),
|
|
8
|
-
phone: z.string().optional(),
|
|
9
|
-
email: z.string().optional(),
|
|
10
|
-
address: z.string().optional(),
|
|
11
|
-
notes: z.string().optional(),
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
export const createCompany = {
|
|
15
|
-
name: "estimator_create_company",
|
|
16
|
-
description: "Create a new company in the client book. Companies group clients (contacts) together. Include: name (required), phone, email, address, and notes. After creating, you can add clients to the company using estimator_create_client with the companyId.",
|
|
17
|
-
inputSchemaZod: createCompanySchema,
|
|
18
|
-
execute: async (args: z.infer<typeof createCompanySchema>): Promise<string> => {
|
|
19
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
20
|
-
const data = await client.post("/estimator/companies", args);
|
|
21
|
-
return JSON.stringify(data);
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// --- estimator_list_companies ---
|
|
26
|
-
|
|
27
|
-
const listCompaniesSchema = z.object({
|
|
28
|
-
search: z.string().optional(),
|
|
29
|
-
limit: z.number().optional(),
|
|
30
|
-
cursor: z.string().optional(),
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
export const listCompanies = {
|
|
34
|
-
name: "estimator_list_companies",
|
|
35
|
-
description: "Search and list companies in the client book. Use the search param to find companies by name. Returns company details with a count of contacts and estimates. Always search here before creating a new company to avoid duplicates.",
|
|
36
|
-
inputSchemaZod: listCompaniesSchema,
|
|
37
|
-
execute: async (args: z.infer<typeof listCompaniesSchema>): Promise<string> => {
|
|
38
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
39
|
-
|
|
40
|
-
const params = new URLSearchParams();
|
|
41
|
-
if (args.search) params.set("search", args.search);
|
|
42
|
-
if (args.limit != null) params.set("limit", String(args.limit));
|
|
43
|
-
if (args.cursor) params.set("cursor", args.cursor);
|
|
44
|
-
|
|
45
|
-
const q = params.toString();
|
|
46
|
-
const path = q ? `/estimator/companies?${q}` : "/estimator/companies";
|
|
47
|
-
|
|
48
|
-
const data = await client.get<unknown>(path);
|
|
49
|
-
return JSON.stringify(data);
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
// --- estimator_get_company ---
|
|
54
|
-
|
|
55
|
-
const getCompanySchema = z.object({
|
|
56
|
-
companyId: z.string(),
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
export const getCompany = {
|
|
60
|
-
name: "estimator_get_company",
|
|
61
|
-
description: "Get a specific company's full details including all its client contacts and a summary of linked estimates.",
|
|
62
|
-
inputSchemaZod: getCompanySchema,
|
|
63
|
-
execute: async (args: z.infer<typeof getCompanySchema>): Promise<string> => {
|
|
64
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
65
|
-
const data = await client.get<unknown>(`/estimator/companies/${args.companyId}`);
|
|
66
|
-
return JSON.stringify(data);
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// --- estimator_update_company ---
|
|
71
|
-
|
|
72
|
-
const updateCompanySchema = z.object({
|
|
73
|
-
companyId: z.string(),
|
|
74
|
-
name: z.string().optional(),
|
|
75
|
-
phone: z.string().optional(),
|
|
76
|
-
email: z.string().optional(),
|
|
77
|
-
address: z.string().optional(),
|
|
78
|
-
notes: z.string().optional(),
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
export const updateCompany = {
|
|
82
|
-
name: "estimator_update_company",
|
|
83
|
-
description: "Update a company's details. Pass the companyId and any fields to change: name, phone, email, address, or notes.",
|
|
84
|
-
inputSchemaZod: updateCompanySchema,
|
|
85
|
-
execute: async (args: z.infer<typeof updateCompanySchema>): Promise<string> => {
|
|
86
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
87
|
-
const { companyId, ...body } = args;
|
|
88
|
-
const data = await client.patch(`/estimator/companies/${companyId}`, body);
|
|
89
|
-
return JSON.stringify(data);
|
|
90
|
-
},
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// --- estimator_delete_company ---
|
|
94
|
-
|
|
95
|
-
const deleteCompanySchema = z.object({
|
|
96
|
-
companyId: z.string(),
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
export const deleteCompany = {
|
|
100
|
-
name: "estimator_delete_company",
|
|
101
|
-
description: "Soft-delete a company from the client book. The company's data is preserved but hidden. Clients belonging to this company are NOT deleted — they become unlinked.",
|
|
102
|
-
inputSchemaZod: deleteCompanySchema,
|
|
103
|
-
execute: async (args: z.infer<typeof deleteCompanySchema>): Promise<string> => {
|
|
104
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
105
|
-
const data = await client.del(`/estimator/companies/${args.companyId}`);
|
|
106
|
-
return JSON.stringify(data);
|
|
107
|
-
},
|
|
108
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import * as client from "../lib/kynver-client.js";
|
|
3
|
-
|
|
4
|
-
const emailPdfSchema = z.object({
|
|
5
|
-
estimateId: z.string(),
|
|
6
|
-
recipient: z.string().email().optional(),
|
|
7
|
-
subject: z.string().min(1).max(200).optional(),
|
|
8
|
-
message: z.string().min(1).max(10_000).optional(),
|
|
9
|
-
confirmExternalRecipient: z.boolean().optional().default(false),
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export const emailPdf = {
|
|
13
|
-
name: "estimator_email_pdf",
|
|
14
|
-
description:
|
|
15
|
-
"Email an estimate PDF to a recipient. Fetches and attaches the PDF server-side (no secrets exposed). Defaults to the account owner's email when recipient is omitted. Estimate client emails and linked client records are trusted without extra confirmation; any other address requires confirmExternalRecipient=true after explicit user confirmation. Respects the estimate approval gate before customer send. Returns send status and audit metadata.",
|
|
16
|
-
inputSchemaZod: emailPdfSchema,
|
|
17
|
-
execute: async (args: z.infer<typeof emailPdfSchema>): Promise<string> => {
|
|
18
|
-
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
19
|
-
|
|
20
|
-
const { estimateId, ...body } = args;
|
|
21
|
-
const data = await client.post<unknown>(
|
|
22
|
-
`/estimator/estimates/${estimateId}/email-pdf`,
|
|
23
|
-
body,
|
|
24
|
-
);
|
|
25
|
-
return JSON.stringify(data);
|
|
26
|
-
},
|
|
27
|
-
};
|