@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.
Files changed (40) hide show
  1. package/dist/lib/mcp-sse-server.d.ts +15 -0
  2. package/dist/lib/mcp-sse-server.js +103 -2
  3. package/dist/server.js +16 -0
  4. package/dist/tools/catalog-tools.js +1 -1
  5. package/dist/tools/email-pdf-tools.d.ts +45 -0
  6. package/dist/tools/email-pdf-tools.js +21 -0
  7. package/dist/tools/intake-tools.d.ts +463 -0
  8. package/dist/tools/intake-tools.js +82 -0
  9. package/dist/tools/price-catalog-tools.d.ts +96 -0
  10. package/dist/tools/price-catalog-tools.js +72 -0
  11. package/dist/tools/remodel-deliverable-tools.d.ts +1868 -0
  12. package/dist/tools/remodel-deliverable-tools.js +138 -0
  13. package/dist/tools/remodel-estimate-tools.d.ts +525 -0
  14. package/dist/tools/remodel-estimate-tools.js +71 -0
  15. package/dist/tools/takeoff-tools.d.ts +274 -0
  16. package/dist/tools/takeoff-tools.js +38 -4
  17. package/dist/tools/vision-tools.js +2 -2
  18. package/package.json +6 -1
  19. package/src/index.ts +0 -18
  20. package/src/lib/kynver-client.ts +0 -136
  21. package/src/lib/mcp-sse-server.ts +0 -249
  22. package/src/lib/package-version.ts +0 -55
  23. package/src/server.ts +0 -141
  24. package/src/sse.ts +0 -10
  25. package/src/tools/catalog-tools.ts +0 -217
  26. package/src/tools/client-tools.ts +0 -114
  27. package/src/tools/company-tools.ts +0 -108
  28. package/src/tools/email-pdf-tools.ts +0 -27
  29. package/src/tools/estimate-tools.ts +0 -281
  30. package/src/tools/intake-tools.ts +0 -95
  31. package/src/tools/photo-tools.ts +0 -60
  32. package/src/tools/price-catalog-tools.ts +0 -80
  33. package/src/tools/remodel-deliverable-tools.ts +0 -163
  34. package/src/tools/remodel-estimate-tools.ts +0 -80
  35. package/src/tools/stats-pdf-tools.ts +0 -80
  36. package/src/tools/takeoff-tools.ts +0 -319
  37. package/src/tools/template-tools.ts +0 -80
  38. package/src/tools/version-tools.ts +0 -39
  39. package/src/tools/vision-tools.ts +0 -48
  40. package/tsconfig.json +0 -15
@@ -1,80 +0,0 @@
1
- import { z } from "zod";
2
- import * as client from "../lib/kynver-client.js";
3
-
4
- const scopeSchema = z.object({
5
- key: z.string(),
6
- label: z.string().optional(),
7
- quantity: z.number().optional(),
8
- unit: z.string().optional(),
9
- serviceCode: z.string().optional(),
10
- budgetLump: z.number().optional(),
11
- notes: z.string().optional(),
12
- });
13
-
14
- const buildRemodelSchema = z.object({
15
- intake: z.object({
16
- projectName: z.string().optional(),
17
- scopes: z.array(scopeSchema),
18
- alternates: z
19
- .array(
20
- z.object({
21
- key: z.string(),
22
- label: z.string(),
23
- scopeKeys: z.array(z.string()).optional(),
24
- lumpAdd: z.number().optional(),
25
- notes: z.string().optional(),
26
- })
27
- )
28
- .optional(),
29
- includeStandardAlternates: z.boolean().optional(),
30
- expandScopeComponents: z.boolean().optional(),
31
- exclusions: z.array(z.string()).optional(),
32
- assumptions: z.array(z.string()).optional(),
33
- notes: z.string().optional(),
34
- }),
35
- takeoffRows: z
36
- .array(
37
- z.object({
38
- id: z.string().optional(),
39
- description: z.string(),
40
- quantity: z.number(),
41
- unit: z.string(),
42
- serviceCode: z.string().nullable().optional(),
43
- materialType: z.string().nullable().optional(),
44
- })
45
- )
46
- .optional(),
47
- overrides: z
48
- .array(
49
- z.object({
50
- scopeKey: z.string().optional(),
51
- serviceCode: z.string().optional(),
52
- description: z.string().optional(),
53
- quantity: z.number().optional(),
54
- unitMaterial: z.number().optional(),
55
- unitLabor: z.number().optional(),
56
- lumpTotal: z.number().optional(),
57
- notes: z.string().optional(),
58
- })
59
- )
60
- .optional(),
61
- pricing: z
62
- .object({
63
- contingencyPercent: z.number().optional(),
64
- markupPercent: z.number().optional(),
65
- profitPercent: z.number().optional(),
66
- })
67
- .optional(),
68
- });
69
-
70
- export const buildRemodelEstimate = {
71
- name: "estimator_build_remodel_estimate",
72
- description:
73
- "Build a reviewable residential remodel estimate from normalized intake scopes, optional takeoff quantities, catalog prices, and overrides. Lump room scopes (kitchen, baths) auto-expand into a per-trade breakdown of line items (demolition, cabinetry, countertops, plumbing, electrical, appliances, flooring, tile, finishes) — each with its own scope-of-work description carried in notes — so client-facing output is itemized, not a single vague lump (set intake.expandScopeComponents=false for the legacy single-line behavior). Returns line items with provenance, alternates (both baths, upstairs expansion, roof, wall moving), a default+custom assumptions list, default+custom exclusions, contingency/markup/profit breakdown, and reviewMarkdown. Pass project-specific items via intake.assumptions and intake.exclusions. Does not persist — use estimator_generate_estimate_from_takeoff or estimator_create_repair after review. Room scope keys: kitchen, bath_full, bath_half, bath_primary, bath_secondary. Trade/quantity scope keys: cabinets, drywall, paint, framing, roof, flooring, electrical_kitchen, electrical_bath. Whole-project phase scope keys: general_conditions, permits, demolition, protection, final_clean.",
74
- inputSchemaZod: buildRemodelSchema,
75
- execute: async (args: z.infer<typeof buildRemodelSchema>): Promise<string> => {
76
- if (!client.hasApiKey()) return client.noApiKeyMessage();
77
- const data = await client.post("/estimator/estimates/build-remodel", args);
78
- return JSON.stringify(data);
79
- },
80
- };
@@ -1,80 +0,0 @@
1
- import { z } from "zod";
2
- import * as client from "../lib/kynver-client.js";
3
-
4
- // --- estimator_get_stats ---
5
-
6
- const getStatsSchema = z.object({
7
- from: z.string().optional(),
8
- to: z.string().optional(),
9
- });
10
-
11
- export const getStats = {
12
- name: "estimator_get_stats",
13
- description: "Get aggregate statistics for the user's estimating business. Returns: total estimates count, estimates by status, total revenue (accepted estimates), average estimate value, estimates by type (replacement vs repair). Optionally filter by date range (from/to as ISO strings). Use this for dashboard data and business reporting.",
14
- inputSchemaZod: getStatsSchema,
15
- execute: async (args: z.infer<typeof getStatsSchema>): Promise<string> => {
16
- if (!client.hasApiKey()) return client.noApiKeyMessage();
17
-
18
- const params = new URLSearchParams();
19
- if (args.from) params.set("from", args.from);
20
- if (args.to) params.set("to", args.to);
21
-
22
- const q = params.toString();
23
- const path = q ? `/estimator/stats?${q}` : "/estimator/stats";
24
-
25
- const data = await client.get<unknown>(path);
26
- return JSON.stringify(data);
27
- },
28
- };
29
-
30
- // --- estimator_export_pdf ---
31
-
32
- const exportPdfSchema = z.object({
33
- estimateId: z.string(),
34
- });
35
-
36
- export const exportPdf = {
37
- name: "estimator_export_pdf",
38
- description:
39
- "Generate a PDF document for an estimate. Returns a download URL (not the binary file). The PDF layout is controlled by the estimate's output config — use estimator_update_output_config to customize which columns, sections, and details appear. The PDF includes company branding from estimator config.",
40
- inputSchemaZod: exportPdfSchema,
41
- execute: async (args: z.infer<typeof exportPdfSchema>): Promise<string> => {
42
- if (!client.hasApiKey()) return client.noApiKeyMessage();
43
- // Trigger PDF generation on the server side
44
- try {
45
- await client.get<unknown>(`/estimator/estimates/${args.estimateId}/pdf`);
46
- } catch {
47
- // Even if the binary response fails to parse, the PDF was generated
48
- }
49
- return JSON.stringify({
50
- message: "PDF generated successfully.",
51
- downloadUrl: `/api/estimator/estimates/${args.estimateId}/pdf`,
52
- });
53
- },
54
- };
55
-
56
- // --- estimator_update_output_config ---
57
-
58
- const updateOutputConfigSchema = z.object({
59
- estimateId: z.string(),
60
- showMaterialCost: z.boolean().optional(),
61
- showLaborCost: z.boolean().optional(),
62
- showUnitPrice: z.boolean().optional(),
63
- showQuantity: z.boolean().optional(),
64
- showNotes: z.boolean().optional(),
65
- headerText: z.string().optional(),
66
- footerText: z.string().optional(),
67
- template: z.string().optional(),
68
- });
69
-
70
- export const updateOutputConfig = {
71
- name: "estimator_update_output_config",
72
- description: "Configure what appears in an estimate's PDF output. Toggle visibility of: material costs, labor costs, unit prices, quantities, line item notes. Set custom header/footer text. Choose a template style. This controls the presentation layer — the underlying estimate data is not changed.",
73
- inputSchemaZod: updateOutputConfigSchema,
74
- execute: async (args: z.infer<typeof updateOutputConfigSchema>): Promise<string> => {
75
- if (!client.hasApiKey()) return client.noApiKeyMessage();
76
- const { estimateId, ...body } = args;
77
- const data = await client.patch(`/estimator/estimates/${estimateId}/output-config`, body);
78
- return JSON.stringify(data);
79
- },
80
- };
@@ -1,319 +0,0 @@
1
- import { z } from "zod";
2
- import * as client from "../lib/kynver-client.js";
3
-
4
- const projectIdSchema = z.object({ projectId: z.string().uuid() });
5
-
6
- const createTakeoffProjectSchema = z.object({
7
- projectName: z.string(),
8
- buildingType: z.enum(["commercial", "residential"]),
9
- siteAddress: z.string().optional(),
10
- clientId: z.string().optional(),
11
- companyId: z.string().optional(),
12
- });
13
-
14
- const listTakeoffProjectsSchema = z.object({});
15
-
16
- const remodelHintsSchema = z
17
- .object({
18
- cabinetLinearFt: z.number().nonnegative().optional(),
19
- rooms: z
20
- .array(
21
- z.object({
22
- name: z.string(),
23
- lengthFt: z.number().positive().optional(),
24
- widthFt: z.number().positive().optional(),
25
- })
26
- )
27
- .optional(),
28
- baths: z
29
- .array(
30
- z.object({
31
- type: z.enum(["full", "half", "three_quarter", "unknown"]).optional(),
32
- count: z.number().int().positive().optional(),
33
- label: z.string().optional(),
34
- })
35
- )
36
- .optional(),
37
- flooringSqFt: z.number().nonnegative().optional(),
38
- drywallSqFt: z.number().nonnegative().optional(),
39
- paintSqFt: z.number().nonnegative().optional(),
40
- roofSqFt: z.number().nonnegative().optional(),
41
- flags: z
42
- .object({
43
- wallMovingRisk: z.boolean().optional(),
44
- electricalSurpriseRisk: z.boolean().optional(),
45
- plumbingSurpriseRisk: z.boolean().optional(),
46
- })
47
- .optional(),
48
- notes: z.string().optional(),
49
- })
50
- .optional();
51
-
52
- const uploadTakeoffInputSchema = z.object({
53
- projectId: z.string().uuid(),
54
- kind: z.string(),
55
- fileName: z.string(),
56
- base64: z.string(),
57
- mimeType: z.string().optional(),
58
- remodelHints: remodelHintsSchema,
59
- });
60
-
61
- const listTakeoffRowsSchema = z.object({
62
- projectId: z.string().uuid(),
63
- reviewState: z.string().optional(),
64
- });
65
-
66
- const reviewTakeoffRowSchema = z.object({
67
- projectId: z.string().uuid(),
68
- rowId: z.string().uuid(),
69
- action: z.enum(["approve", "skip", "edit"]),
70
- edits: z
71
- .object({
72
- description: z.string().optional(),
73
- quantity: z.number().optional(),
74
- unit: z.string().optional(),
75
- serviceCode: z.string().nullable().optional(),
76
- })
77
- .optional(),
78
- });
79
-
80
- const commitTakeoffSchema = z.object({
81
- projectId: z.string().uuid(),
82
- overrideNote: z.string().optional(),
83
- });
84
-
85
- const listSystemPacksSchema = z.object({ segment: z.enum(["commercial", "residential"]).optional() });
86
-
87
- const applySystemPackSchema = z.object({
88
- projectId: z.string().uuid(),
89
- systemPackSlug: z.string(),
90
- serviceProfileId: z.string().optional(),
91
- });
92
-
93
- const listServiceProfilesSchema = z.object({});
94
-
95
- const upsertServiceProfileSchema = z.object({
96
- slug: z.string(),
97
- name: z.string(),
98
- segment: z.enum([
99
- "commercial_service",
100
- "commercial_production",
101
- "residential_service",
102
- "residential_production",
103
- ]),
104
- laborMultiplier: z.number().optional(),
105
- defaultWastePercent: z.number().optional(),
106
- });
107
-
108
- const requestEstimateApprovalSchema = z.object({
109
- estimateId: z.string().uuid(),
110
- note: z.string().optional(),
111
- });
112
-
113
- const approveEstimateSchema = z.object({
114
- estimateId: z.string().uuid(),
115
- approvalId: z.string().uuid(),
116
- action: z.enum(["approve", "reject"]),
117
- note: z.string().optional(),
118
- });
119
-
120
- const getEstimateApprovalSchema = z.object({ estimateId: z.string().uuid() });
121
-
122
- export const createTakeoffProject = {
123
- name: "estimator_create_takeoff_project",
124
- description:
125
- "Create a takeoff project shell (commercial or residential). Upload inputs, run extract, review rows, commit, apply system pack, then generate estimate.",
126
- inputSchemaZod: createTakeoffProjectSchema,
127
- execute: async (args: z.infer<typeof createTakeoffProjectSchema>): Promise<string> => {
128
- if (!client.hasApiKey()) return client.noApiKeyMessage();
129
- const data = await client.post("/estimator/takeoffs", args);
130
- return JSON.stringify(data);
131
- },
132
- };
133
-
134
- export const getTakeoffProject = {
135
- name: "estimator_get_takeoff_project",
136
- description: "Get takeoff project with inputs, rows, status, pack/profile, linked estimate.",
137
- inputSchemaZod: projectIdSchema,
138
- execute: async (args: z.infer<typeof projectIdSchema>): Promise<string> => {
139
- if (!client.hasApiKey()) return client.noApiKeyMessage();
140
- const data = await client.get(`/estimator/takeoffs/${args.projectId}`);
141
- return JSON.stringify(data);
142
- },
143
- };
144
-
145
- export const listTakeoffProjects = {
146
- name: "estimator_list_takeoff_projects",
147
- description: "List takeoff projects for the workspace.",
148
- inputSchemaZod: listTakeoffProjectsSchema,
149
- execute: async (_args: z.infer<typeof listTakeoffProjectsSchema>): Promise<string> => {
150
- if (!client.hasApiKey()) return client.noApiKeyMessage();
151
- const data = await client.get("/estimator/takeoffs");
152
- return JSON.stringify(data);
153
- },
154
- };
155
-
156
- export const uploadTakeoffInput = {
157
- name: "estimator_upload_takeoff_input",
158
- description:
159
- "Upload a takeoff input file (base64) for a project. kind: eagleview_pdf|eagleview_xml|eagleview_json|eagleview_csv|spec_pdf|spreadsheet|photo|voice|other. For remodel photo/drawing takeoff, use kind=photo or spec_pdf and pass optional remodelHints (cabinet LF, room dims, bath counts, flooring/drywall/paint/roof sf, risk flags) — quantities are reviewable, not auto-measured from pixels.",
160
- inputSchemaZod: uploadTakeoffInputSchema,
161
- execute: async (args: z.infer<typeof uploadTakeoffInputSchema>): Promise<string> => {
162
- if (!client.hasApiKey()) return client.noApiKeyMessage();
163
- const data = await client.post(`/estimator/takeoffs/${args.projectId}/inputs`, {
164
- kind: args.kind,
165
- fileName: args.fileName,
166
- base64: args.base64,
167
- ...(args.mimeType ? { mimeType: args.mimeType } : {}),
168
- ...(args.remodelHints ? { remodelHints: args.remodelHints } : {}),
169
- });
170
- return JSON.stringify(data);
171
- },
172
- };
173
-
174
- export const runTakeoffExtract = {
175
- name: "estimator_run_takeoff_extract",
176
- description: "Run takeoff extractors on all uploaded inputs (idempotent).",
177
- inputSchemaZod: projectIdSchema,
178
- execute: async (args: z.infer<typeof projectIdSchema>): Promise<string> => {
179
- if (!client.hasApiKey()) return client.noApiKeyMessage();
180
- const data = await client.post(`/estimator/takeoffs/${args.projectId}/extract`, {});
181
- return JSON.stringify(data);
182
- },
183
- };
184
-
185
- export const listTakeoffRows = {
186
- name: "estimator_list_takeoff_rows",
187
- description: "List takeoff rows with evidence and review state.",
188
- inputSchemaZod: listTakeoffRowsSchema,
189
- execute: async (args: z.infer<typeof listTakeoffRowsSchema>): Promise<string> => {
190
- if (!client.hasApiKey()) return client.noApiKeyMessage();
191
- const qs = args.reviewState ? `?reviewState=${args.reviewState}` : "";
192
- const data = await client.get(`/estimator/takeoffs/${args.projectId}/rows${qs}`);
193
- return JSON.stringify(data);
194
- },
195
- };
196
-
197
- export const reviewTakeoffRow = {
198
- name: "estimator_review_takeoff_row",
199
- description: "Approve, skip, or edit a single takeoff row.",
200
- inputSchemaZod: reviewTakeoffRowSchema,
201
- execute: async (args: z.infer<typeof reviewTakeoffRowSchema>): Promise<string> => {
202
- if (!client.hasApiKey()) return client.noApiKeyMessage();
203
- const { projectId, rowId, ...body } = args;
204
- const data = await client.patch(`/estimator/takeoffs/${projectId}/rows/${rowId}`, body);
205
- return JSON.stringify(data);
206
- },
207
- };
208
-
209
- export const commitTakeoff = {
210
- name: "estimator_commit_takeoff",
211
- description: "Mark takeoff ready after all rows reviewed and area measurement approved (or override note).",
212
- inputSchemaZod: commitTakeoffSchema,
213
- execute: async (args: z.infer<typeof commitTakeoffSchema>): Promise<string> => {
214
- if (!client.hasApiKey()) return client.noApiKeyMessage();
215
- const data = await client.post(`/estimator/takeoffs/${args.projectId}/commit`, {
216
- overrideNote: args.overrideNote,
217
- });
218
- return JSON.stringify(data);
219
- },
220
- };
221
-
222
- export const listSystemPacks = {
223
- name: "estimator_list_system_packs",
224
- description: "List built-in commercial and residential system packs.",
225
- inputSchemaZod: listSystemPacksSchema,
226
- execute: async (args: z.infer<typeof listSystemPacksSchema>): Promise<string> => {
227
- if (!client.hasApiKey()) return client.noApiKeyMessage();
228
- const qs = args.segment ? `?segment=${args.segment}` : "";
229
- const data = await client.get(`/estimator/system-packs${qs}`);
230
- return JSON.stringify(data);
231
- },
232
- };
233
-
234
- export const applySystemPack = {
235
- name: "estimator_apply_system_pack",
236
- description: "Set system pack slug and optional service profile on takeoff project.",
237
- inputSchemaZod: applySystemPackSchema,
238
- execute: async (args: z.infer<typeof applySystemPackSchema>): Promise<string> => {
239
- if (!client.hasApiKey()) return client.noApiKeyMessage();
240
- const data = await client.patch(`/estimator/takeoffs/${args.projectId}`, {
241
- systemPackSlug: args.systemPackSlug,
242
- serviceProfileId: args.serviceProfileId ?? null,
243
- });
244
- return JSON.stringify(data);
245
- },
246
- };
247
-
248
- export const listServiceProfiles = {
249
- name: "estimator_list_service_profiles",
250
- description: "List service department profiles (includes v6 defaults).",
251
- inputSchemaZod: listServiceProfilesSchema,
252
- execute: async (_args: z.infer<typeof listServiceProfilesSchema>): Promise<string> => {
253
- if (!client.hasApiKey()) return client.noApiKeyMessage();
254
- const data = await client.get("/estimator/service-profiles");
255
- return JSON.stringify(data);
256
- },
257
- };
258
-
259
- export const upsertServiceProfile = {
260
- name: "estimator_upsert_service_profile",
261
- description: "Create or update a service department profile.",
262
- inputSchemaZod: upsertServiceProfileSchema,
263
- execute: async (args: z.infer<typeof upsertServiceProfileSchema>): Promise<string> => {
264
- if (!client.hasApiKey()) return client.noApiKeyMessage();
265
- const data = await client.post("/estimator/service-profiles", args);
266
- return JSON.stringify(data);
267
- },
268
- };
269
-
270
- export const generateEstimateFromTakeoff = {
271
- name: "estimator_generate_estimate_from_takeoff",
272
- description:
273
- "Generate draft estimate from committed takeoff + system pack. Preflight fails if catalog codes missing.",
274
- inputSchemaZod: projectIdSchema,
275
- execute: async (args: z.infer<typeof projectIdSchema>): Promise<string> => {
276
- if (!client.hasApiKey()) return client.noApiKeyMessage();
277
- const data = await client.post(`/estimator/takeoffs/${args.projectId}/generate-estimate`, {});
278
- return JSON.stringify(data);
279
- },
280
- };
281
-
282
- export const requestEstimateApproval = {
283
- name: "estimator_request_estimate_approval",
284
- description: "Request owner approval before customer send.",
285
- inputSchemaZod: requestEstimateApprovalSchema,
286
- execute: async (args: z.infer<typeof requestEstimateApprovalSchema>): Promise<string> => {
287
- if (!client.hasApiKey()) return client.noApiKeyMessage();
288
- const data = await client.post(`/estimator/estimates/${args.estimateId}/approval`, {
289
- note: args.note,
290
- });
291
- return JSON.stringify(data);
292
- },
293
- };
294
-
295
- export const approveEstimate = {
296
- name: "estimator_approve_estimate",
297
- description: "Approve or reject estimate (workspace owner).",
298
- inputSchemaZod: approveEstimateSchema,
299
- execute: async (args: z.infer<typeof approveEstimateSchema>): Promise<string> => {
300
- if (!client.hasApiKey()) return client.noApiKeyMessage();
301
- const data = await client.post(`/estimator/estimates/${args.estimateId}/approval`, {
302
- approvalId: args.approvalId,
303
- action: args.action,
304
- note: args.note,
305
- });
306
- return JSON.stringify(data);
307
- },
308
- };
309
-
310
- export const getEstimateApproval = {
311
- name: "estimator_get_estimate_approval",
312
- description: "Get latest approval status for an estimate.",
313
- inputSchemaZod: getEstimateApprovalSchema,
314
- execute: async (args: z.infer<typeof getEstimateApprovalSchema>): Promise<string> => {
315
- if (!client.hasApiKey()) return client.noApiKeyMessage();
316
- const data = await client.get(`/estimator/estimates/${args.estimateId}/approval`);
317
- return JSON.stringify(data);
318
- },
319
- };
@@ -1,80 +0,0 @@
1
- import { z } from "zod";
2
- import * as client from "../lib/kynver-client.js";
3
-
4
- // --- estimator_save_template ---
5
-
6
- const saveTemplateSchema = z.object({
7
- estimateId: z.string(),
8
- name: z.string(),
9
- });
10
-
11
- export const saveTemplate = {
12
- name: "estimator_save_template",
13
- description: "Save an existing estimate as a reusable template. Templates capture the estimate's line items, quantities, and structure so you can quickly create similar estimates in the future. Requires the estimateId of the source estimate and a name for the template.",
14
- inputSchemaZod: saveTemplateSchema,
15
- execute: async (args: z.infer<typeof saveTemplateSchema>): Promise<string> => {
16
- if (!client.hasApiKey()) return client.noApiKeyMessage();
17
- const data = await client.post("/estimator/templates", args);
18
- return JSON.stringify(data);
19
- },
20
- };
21
-
22
- // --- estimator_list_templates ---
23
-
24
- const listTemplatesSchema = z.object({
25
- type: z.string().optional(),
26
- });
27
-
28
- export const listTemplates = {
29
- name: "estimator_list_templates",
30
- description: "List all saved estimate templates. Optionally filter by type: 'replacement' or 'repair_service'. Returns template names, types, and IDs. Use template IDs with estimator_create_from_template to create new estimates.",
31
- inputSchemaZod: listTemplatesSchema,
32
- execute: async (args: z.infer<typeof listTemplatesSchema>): Promise<string> => {
33
- if (!client.hasApiKey()) return client.noApiKeyMessage();
34
-
35
- const params = new URLSearchParams();
36
- if (args.type) params.set("type", args.type);
37
-
38
- const q = params.toString();
39
- const path = q ? `/estimator/templates?${q}` : "/estimator/templates";
40
-
41
- const data = await client.get<unknown>(path);
42
- return JSON.stringify(data);
43
- },
44
- };
45
-
46
- // --- estimator_delete_template ---
47
-
48
- const deleteTemplateSchema = z.object({
49
- id: z.string(),
50
- });
51
-
52
- export const deleteTemplate = {
53
- name: "estimator_delete_template",
54
- description: "Delete a saved estimate template permanently. This does not affect any estimates that were previously created from this template.",
55
- inputSchemaZod: deleteTemplateSchema,
56
- execute: async (args: z.infer<typeof deleteTemplateSchema>): Promise<string> => {
57
- if (!client.hasApiKey()) return client.noApiKeyMessage();
58
- const data = await client.del(`/estimator/templates/${args.id}`);
59
- return JSON.stringify(data);
60
- },
61
- };
62
-
63
- // --- estimator_create_from_template ---
64
-
65
- const createFromTemplateSchema = z.object({
66
- templateId: z.string(),
67
- projectName: z.string(),
68
- clientId: z.string().optional(),
69
- });
70
-
71
- export const createFromTemplate = {
72
- name: "estimator_create_from_template",
73
- description: "Create a new estimate from a saved template. The new estimate gets all the template's line items and structure. Requires: templateId (from estimator_list_templates), projectName. Optionally link to a clientId. The new estimate is created as a draft.",
74
- inputSchemaZod: createFromTemplateSchema,
75
- execute: async (args: z.infer<typeof createFromTemplateSchema>): Promise<string> => {
76
- if (!client.hasApiKey()) return client.noApiKeyMessage();
77
- const data = await client.post("/estimator/estimates", args);
78
- return JSON.stringify(data);
79
- },
80
- };
@@ -1,39 +0,0 @@
1
- import { z } from "zod";
2
- import * as client from "../lib/kynver-client.js";
3
-
4
- // --- estimator_list_versions ---
5
-
6
- const listVersionsSchema = z.object({
7
- estimateId: z.string(),
8
- });
9
-
10
- export const listVersions = {
11
- name: "estimator_list_versions",
12
- description: "List all saved version snapshots of an estimate. Versions are created automatically when significant changes are made. Shows version numbers and timestamps. Use this to track the history of changes to an estimate.",
13
- inputSchemaZod: listVersionsSchema,
14
- execute: async (args: z.infer<typeof listVersionsSchema>): Promise<string> => {
15
- if (!client.hasApiKey()) return client.noApiKeyMessage();
16
- const data = await client.get<unknown>(`/estimator/estimates/${args.estimateId}/versions`);
17
- return JSON.stringify(data);
18
- },
19
- };
20
-
21
- // --- estimator_get_version ---
22
-
23
- const getVersionSchema = z.object({
24
- estimateId: z.string(),
25
- versionId: z.string(),
26
- });
27
-
28
- export const getVersion = {
29
- name: "estimator_get_version",
30
- description: "Get a specific historical version of an estimate. Returns the full estimate snapshot as it existed at that point in time — all line items, totals, and metadata. Useful for comparing current vs previous versions or reverting changes.",
31
- inputSchemaZod: getVersionSchema,
32
- execute: async (args: z.infer<typeof getVersionSchema>): Promise<string> => {
33
- if (!client.hasApiKey()) return client.noApiKeyMessage();
34
- const data = await client.get<unknown>(
35
- `/estimator/estimates/${args.estimateId}/versions/${args.versionId}`
36
- );
37
- return JSON.stringify(data);
38
- },
39
- };
@@ -1,48 +0,0 @@
1
- import { z } from "zod";
2
- import * as client from "../lib/kynver-client.js";
3
-
4
- // --- estimator_analyze_photos ---
5
-
6
- const analyzePhotosSchema = z.object({
7
- images: z.array(z.string()),
8
- prompt: z.string().optional(),
9
- });
10
-
11
- export const analyzePhotos = {
12
- name: "estimator_analyze_photos",
13
- description:
14
- "Analyze roof photos using Claude's vision AI. Provide image URLs (can be multiple). The AI identifies: roofing materials, visible damage (missing shingles, cracks, moss, ponding), approximate measurements, and repair recommendations. Use the optional prompt parameter to ask specific questions about the photos. Results can inform estimate creation.",
15
- inputSchemaZod: analyzePhotosSchema,
16
- execute: async (args: z.infer<typeof analyzePhotosSchema>): Promise<string> => {
17
- if (!client.hasApiKey()) return client.noApiKeyMessage();
18
- const data = await client.post("/estimator/analyze", {
19
- type: "photos",
20
- images: args.images,
21
- ...(args.prompt ? { prompt: args.prompt } : {}),
22
- });
23
- return JSON.stringify(data);
24
- },
25
- };
26
-
27
- // --- estimator_analyze_document ---
28
-
29
- const analyzeDocumentSchema = z.object({
30
- text: z.string(),
31
- prompt: z.string().optional(),
32
- });
33
-
34
- export const analyzeDocument = {
35
- name: "estimator_analyze_document",
36
- description:
37
- "Analyze a text document (inspection report, insurance scope of work, contractor notes) and extract structured estimating data — identified services, quantities, materials, and damage descriptions. Pass the document text in the text field. Use the optional prompt parameter to focus the analysis. Results can be used to create estimates with accurate line items.",
38
- inputSchemaZod: analyzeDocumentSchema,
39
- execute: async (args: z.infer<typeof analyzeDocumentSchema>): Promise<string> => {
40
- if (!client.hasApiKey()) return client.noApiKeyMessage();
41
- const data = await client.post("/estimator/analyze", {
42
- type: "document",
43
- text: args.text,
44
- ...(args.prompt ? { prompt: args.prompt } : {}),
45
- });
46
- return JSON.stringify(data);
47
- },
48
- };
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "dist",
7
- "rootDir": "src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "declaration": true,
11
- "skipLibCheck": true
12
- },
13
- "include": ["src/**/*"],
14
- "exclude": ["node_modules", "dist"]
15
- }