@kynver-app/mcp-estimator 0.2.4 → 0.2.6
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/package.json
CHANGED
package/src/server.ts
CHANGED
|
@@ -11,6 +11,7 @@ import * as templateTools from "./tools/template-tools.js";
|
|
|
11
11
|
import * as versionTools from "./tools/version-tools.js";
|
|
12
12
|
import * as photoTools from "./tools/photo-tools.js";
|
|
13
13
|
import * as statsPdfTools from "./tools/stats-pdf-tools.js";
|
|
14
|
+
import * as emailPdfTools from "./tools/email-pdf-tools.js";
|
|
14
15
|
import * as visionTools from "./tools/vision-tools.js";
|
|
15
16
|
import * as companyTools from "./tools/company-tools.js";
|
|
16
17
|
import * as clientTools from "./tools/client-tools.js";
|
|
@@ -92,6 +93,7 @@ export function createEstimatorServer(): McpServer {
|
|
|
92
93
|
register(statsPdfTools.getStats.name, { description: statsPdfTools.getStats.description, inputSchema: statsPdfTools.getStats.inputSchemaZod }, (args) => statsPdfTools.getStats.execute(args as any));
|
|
93
94
|
register(statsPdfTools.exportPdf.name, { description: statsPdfTools.exportPdf.description, inputSchema: statsPdfTools.exportPdf.inputSchemaZod }, (args) => statsPdfTools.exportPdf.execute(args as any));
|
|
94
95
|
register(statsPdfTools.updateOutputConfig.name, { description: statsPdfTools.updateOutputConfig.description, inputSchema: statsPdfTools.updateOutputConfig.inputSchemaZod }, (args) => statsPdfTools.updateOutputConfig.execute(args as any));
|
|
96
|
+
register(emailPdfTools.emailPdf.name, { description: emailPdfTools.emailPdf.description, inputSchema: emailPdfTools.emailPdf.inputSchemaZod }, (args) => emailPdfTools.emailPdf.execute(args as any));
|
|
95
97
|
|
|
96
98
|
// --- Vision tools (2) ---
|
|
97
99
|
register(visionTools.analyzePhotos.name, { description: visionTools.analyzePhotos.description, inputSchema: visionTools.analyzePhotos.inputSchemaZod }, (args) => visionTools.analyzePhotos.execute(args as any));
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
};
|
|
@@ -27,7 +27,9 @@ const buildRemodelSchema = z.object({
|
|
|
27
27
|
)
|
|
28
28
|
.optional(),
|
|
29
29
|
includeStandardAlternates: z.boolean().optional(),
|
|
30
|
+
expandScopeComponents: z.boolean().optional(),
|
|
30
31
|
exclusions: z.array(z.string()).optional(),
|
|
32
|
+
assumptions: z.array(z.string()).optional(),
|
|
31
33
|
notes: z.string().optional(),
|
|
32
34
|
}),
|
|
33
35
|
takeoffRows: z
|
|
@@ -68,7 +70,7 @@ const buildRemodelSchema = z.object({
|
|
|
68
70
|
export const buildRemodelEstimate = {
|
|
69
71
|
name: "estimator_build_remodel_estimate",
|
|
70
72
|
description:
|
|
71
|
-
"Build a reviewable residential remodel estimate from normalized intake scopes, optional takeoff quantities, catalog prices, and overrides. Returns line items with provenance, alternates (both baths, upstairs expansion, roof, wall moving), exclusions, contingency/markup/profit breakdown, and reviewMarkdown. Does not persist — use estimator_generate_estimate_from_takeoff or estimator_create_repair after review.
|
|
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.",
|
|
72
74
|
inputSchemaZod: buildRemodelSchema,
|
|
73
75
|
execute: async (args: z.infer<typeof buildRemodelSchema>): Promise<string> => {
|
|
74
76
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|