@kynver-app/mcp-estimator 0.2.0 → 0.2.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/dist/index.js +4 -0
- package/dist/lib/package-version.d.ts +7 -0
- package/dist/lib/package-version.js +44 -0
- package/dist/server.js +22 -2
- package/dist/tools/client-tools.d.ts +4 -4
- package/dist/tools/company-tools.d.ts +4 -4
- package/dist/tools/takeoff-tools.d.ts +456 -0
- package/dist/tools/takeoff-tools.js +263 -0
- package/package.json +1 -1
- package/src/index.ts +18 -13
- package/src/lib/mcp-sse-server.ts +3 -3
- package/src/lib/package-version.ts +55 -0
- package/src/server.ts +4 -1
- package/src/tools/takeoff-tools.ts +105 -76
package/dist/index.js
CHANGED
|
@@ -5,7 +5,11 @@
|
|
|
5
5
|
* Env: KYNVER_API_URL (default https://kynver.com), KYNVER_API_KEY (required for all tools)
|
|
6
6
|
*/
|
|
7
7
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { handleCliVersionFlag } from "./lib/package-version.js";
|
|
8
9
|
import { createEstimatorServer } from "./server.js";
|
|
10
|
+
if (handleCliVersionFlag(process.argv.slice(2), import.meta.url, "kynver-mcp-estimator")) {
|
|
11
|
+
// exits
|
|
12
|
+
}
|
|
9
13
|
const server = createEstimatorServer();
|
|
10
14
|
const transport = new StdioServerTransport();
|
|
11
15
|
await server.connect(transport);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Read a field from the nearest `package.json` above this module. */
|
|
2
|
+
export declare function readOwnPackageJsonField(moduleUrl: string, field: "version" | "name"): string;
|
|
3
|
+
export declare function readOwnPackageVersion(moduleUrl?: string): string;
|
|
4
|
+
export declare function wantsCliVersion(argv: string[]): boolean;
|
|
5
|
+
export declare function printCliVersionAndExit(version: string, binName?: string): never;
|
|
6
|
+
/** When `--version` / `-v` is present, print package version and exit before side effects. */
|
|
7
|
+
export declare function handleCliVersionFlag(argv: string[], moduleUrl?: string, binName?: string): boolean;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @generated — synced from packages/kynver-mcp-sse-lib/src/package-version.ts via scripts/sync-mcp-sse-server.mjs
|
|
2
|
+
// Do not edit by hand; change the canonical file and re-run the sync script.
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
function resolvePackageRoot(moduleUrl) {
|
|
7
|
+
let dir = dirname(fileURLToPath(moduleUrl));
|
|
8
|
+
for (let depth = 0; depth < 6; depth += 1) {
|
|
9
|
+
if (existsSync(join(dir, "package.json")))
|
|
10
|
+
return dir;
|
|
11
|
+
const parent = dirname(dir);
|
|
12
|
+
if (parent === dir)
|
|
13
|
+
break;
|
|
14
|
+
dir = parent;
|
|
15
|
+
}
|
|
16
|
+
throw new Error(`package.json not found above ${dirname(fileURLToPath(moduleUrl))}`);
|
|
17
|
+
}
|
|
18
|
+
/** Read a field from the nearest `package.json` above this module. */
|
|
19
|
+
export function readOwnPackageJsonField(moduleUrl, field) {
|
|
20
|
+
const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
|
|
21
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
22
|
+
const value = pkg[field];
|
|
23
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
24
|
+
throw new Error(`Missing package.json ${field} at ${pkgPath}`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
export function readOwnPackageVersion(moduleUrl = import.meta.url) {
|
|
29
|
+
return readOwnPackageJsonField(moduleUrl, "version");
|
|
30
|
+
}
|
|
31
|
+
export function wantsCliVersion(argv) {
|
|
32
|
+
return argv.some((arg) => arg === "--version" || arg === "-v");
|
|
33
|
+
}
|
|
34
|
+
export function printCliVersionAndExit(version, binName) {
|
|
35
|
+
console.log(binName ? `${binName} ${version}` : version);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
/** When `--version` / `-v` is present, print package version and exit before side effects. */
|
|
39
|
+
export function handleCliVersionFlag(argv, moduleUrl = import.meta.url, binName) {
|
|
40
|
+
if (!wantsCliVersion(argv))
|
|
41
|
+
return false;
|
|
42
|
+
printCliVersionAndExit(readOwnPackageVersion(moduleUrl), binName);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -12,16 +12,19 @@ import * as statsPdfTools from "./tools/stats-pdf-tools.js";
|
|
|
12
12
|
import * as visionTools from "./tools/vision-tools.js";
|
|
13
13
|
import * as companyTools from "./tools/company-tools.js";
|
|
14
14
|
import * as clientTools from "./tools/client-tools.js";
|
|
15
|
+
import * as takeoffTools from "./tools/takeoff-tools.js";
|
|
16
|
+
import { readOwnPackageVersion } from "./lib/package-version.js";
|
|
17
|
+
const PACKAGE_VERSION = readOwnPackageVersion(import.meta.url);
|
|
15
18
|
function toolResult(text) {
|
|
16
19
|
return { content: [{ type: "text", text }] };
|
|
17
20
|
}
|
|
18
21
|
export function createEstimatorServer() {
|
|
19
|
-
const server = new McpServer({ name: "kynver-mcp-estimator", version:
|
|
22
|
+
const server = new McpServer({ name: "kynver-mcp-estimator", version: PACKAGE_VERSION }, { capabilities: { tools: {} } });
|
|
20
23
|
function register(name, config, run) {
|
|
21
24
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
25
|
server.registerTool(name, config, async (args) => toolResult(await run(args)));
|
|
23
26
|
}
|
|
24
|
-
// --- Catalog tools (
|
|
27
|
+
// --- Catalog tools (9) ---
|
|
25
28
|
register(catalogTools.importCatalog.name, { description: catalogTools.importCatalog.description, inputSchema: catalogTools.importCatalog.inputSchemaZod }, (args) => catalogTools.importCatalog.execute(args));
|
|
26
29
|
register(catalogTools.reviewImport.name, { description: catalogTools.reviewImport.description, inputSchema: catalogTools.reviewImport.inputSchemaZod }, (args) => catalogTools.reviewImport.execute(args));
|
|
27
30
|
register(catalogTools.commitImport.name, { description: catalogTools.commitImport.description, inputSchema: catalogTools.commitImport.inputSchemaZod }, (args) => catalogTools.commitImport.execute(args));
|
|
@@ -74,5 +77,22 @@ export function createEstimatorServer() {
|
|
|
74
77
|
register(clientTools.getClient.name, { description: clientTools.getClient.description, inputSchema: clientTools.getClient.inputSchemaZod }, (args) => clientTools.getClient.execute(args));
|
|
75
78
|
register(clientTools.updateClient.name, { description: clientTools.updateClient.description, inputSchema: clientTools.updateClient.inputSchemaZod }, (args) => clientTools.updateClient.execute(args));
|
|
76
79
|
register(clientTools.deleteClient.name, { description: clientTools.deleteClient.description, inputSchema: clientTools.deleteClient.inputSchemaZod }, (args) => clientTools.deleteClient.execute(args));
|
|
80
|
+
// --- Takeoff / approval tools (16) ---
|
|
81
|
+
register(takeoffTools.createTakeoffProject.name, { description: takeoffTools.createTakeoffProject.description, inputSchema: takeoffTools.createTakeoffProject.inputSchemaZod }, (args) => takeoffTools.createTakeoffProject.execute(args));
|
|
82
|
+
register(takeoffTools.getTakeoffProject.name, { description: takeoffTools.getTakeoffProject.description, inputSchema: takeoffTools.getTakeoffProject.inputSchemaZod }, (args) => takeoffTools.getTakeoffProject.execute(args));
|
|
83
|
+
register(takeoffTools.listTakeoffProjects.name, { description: takeoffTools.listTakeoffProjects.description, inputSchema: takeoffTools.listTakeoffProjects.inputSchemaZod }, (args) => takeoffTools.listTakeoffProjects.execute(args));
|
|
84
|
+
register(takeoffTools.uploadTakeoffInput.name, { description: takeoffTools.uploadTakeoffInput.description, inputSchema: takeoffTools.uploadTakeoffInput.inputSchemaZod }, (args) => takeoffTools.uploadTakeoffInput.execute(args));
|
|
85
|
+
register(takeoffTools.runTakeoffExtract.name, { description: takeoffTools.runTakeoffExtract.description, inputSchema: takeoffTools.runTakeoffExtract.inputSchemaZod }, (args) => takeoffTools.runTakeoffExtract.execute(args));
|
|
86
|
+
register(takeoffTools.listTakeoffRows.name, { description: takeoffTools.listTakeoffRows.description, inputSchema: takeoffTools.listTakeoffRows.inputSchemaZod }, (args) => takeoffTools.listTakeoffRows.execute(args));
|
|
87
|
+
register(takeoffTools.reviewTakeoffRow.name, { description: takeoffTools.reviewTakeoffRow.description, inputSchema: takeoffTools.reviewTakeoffRow.inputSchemaZod }, (args) => takeoffTools.reviewTakeoffRow.execute(args));
|
|
88
|
+
register(takeoffTools.commitTakeoff.name, { description: takeoffTools.commitTakeoff.description, inputSchema: takeoffTools.commitTakeoff.inputSchemaZod }, (args) => takeoffTools.commitTakeoff.execute(args));
|
|
89
|
+
register(takeoffTools.listSystemPacks.name, { description: takeoffTools.listSystemPacks.description, inputSchema: takeoffTools.listSystemPacks.inputSchemaZod }, (args) => takeoffTools.listSystemPacks.execute(args));
|
|
90
|
+
register(takeoffTools.applySystemPack.name, { description: takeoffTools.applySystemPack.description, inputSchema: takeoffTools.applySystemPack.inputSchemaZod }, (args) => takeoffTools.applySystemPack.execute(args));
|
|
91
|
+
register(takeoffTools.listServiceProfiles.name, { description: takeoffTools.listServiceProfiles.description, inputSchema: takeoffTools.listServiceProfiles.inputSchemaZod }, (args) => takeoffTools.listServiceProfiles.execute(args));
|
|
92
|
+
register(takeoffTools.upsertServiceProfile.name, { description: takeoffTools.upsertServiceProfile.description, inputSchema: takeoffTools.upsertServiceProfile.inputSchemaZod }, (args) => takeoffTools.upsertServiceProfile.execute(args));
|
|
93
|
+
register(takeoffTools.generateEstimateFromTakeoff.name, { description: takeoffTools.generateEstimateFromTakeoff.description, inputSchema: takeoffTools.generateEstimateFromTakeoff.inputSchemaZod }, (args) => takeoffTools.generateEstimateFromTakeoff.execute(args));
|
|
94
|
+
register(takeoffTools.requestEstimateApproval.name, { description: takeoffTools.requestEstimateApproval.description, inputSchema: takeoffTools.requestEstimateApproval.inputSchemaZod }, (args) => takeoffTools.requestEstimateApproval.execute(args));
|
|
95
|
+
register(takeoffTools.approveEstimate.name, { description: takeoffTools.approveEstimate.description, inputSchema: takeoffTools.approveEstimate.inputSchemaZod }, (args) => takeoffTools.approveEstimate.execute(args));
|
|
96
|
+
register(takeoffTools.getEstimateApproval.name, { description: takeoffTools.getEstimateApproval.description, inputSchema: takeoffTools.getEstimateApproval.inputSchemaZod }, (args) => takeoffTools.getEstimateApproval.execute(args));
|
|
77
97
|
return server;
|
|
78
98
|
}
|
|
@@ -60,15 +60,15 @@ declare const listClientsSchema: z.ZodObject<{
|
|
|
60
60
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
61
61
|
cursor: z.ZodOptional<z.ZodString>;
|
|
62
62
|
}, "strip", z.ZodTypeAny, {
|
|
63
|
+
search?: string | undefined;
|
|
63
64
|
limit?: number | undefined;
|
|
64
65
|
cursor?: string | undefined;
|
|
65
66
|
companyId?: string | undefined;
|
|
66
|
-
search?: string | undefined;
|
|
67
67
|
}, {
|
|
68
|
+
search?: string | undefined;
|
|
68
69
|
limit?: number | undefined;
|
|
69
70
|
cursor?: string | undefined;
|
|
70
71
|
companyId?: string | undefined;
|
|
71
|
-
search?: string | undefined;
|
|
72
72
|
}>;
|
|
73
73
|
export declare const listClients: {
|
|
74
74
|
name: string;
|
|
@@ -79,15 +79,15 @@ export declare const listClients: {
|
|
|
79
79
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
80
80
|
cursor: z.ZodOptional<z.ZodString>;
|
|
81
81
|
}, "strip", z.ZodTypeAny, {
|
|
82
|
+
search?: string | undefined;
|
|
82
83
|
limit?: number | undefined;
|
|
83
84
|
cursor?: string | undefined;
|
|
84
85
|
companyId?: string | undefined;
|
|
85
|
-
search?: string | undefined;
|
|
86
86
|
}, {
|
|
87
|
+
search?: string | undefined;
|
|
87
88
|
limit?: number | undefined;
|
|
88
89
|
cursor?: string | undefined;
|
|
89
90
|
companyId?: string | undefined;
|
|
90
|
-
search?: string | undefined;
|
|
91
91
|
}>;
|
|
92
92
|
execute: (args: z.infer<typeof listClientsSchema>) => Promise<string>;
|
|
93
93
|
};
|
|
@@ -47,13 +47,13 @@ declare const listCompaniesSchema: z.ZodObject<{
|
|
|
47
47
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
48
48
|
cursor: z.ZodOptional<z.ZodString>;
|
|
49
49
|
}, "strip", z.ZodTypeAny, {
|
|
50
|
+
search?: string | undefined;
|
|
50
51
|
limit?: number | undefined;
|
|
51
52
|
cursor?: string | undefined;
|
|
52
|
-
search?: string | undefined;
|
|
53
53
|
}, {
|
|
54
|
+
search?: string | undefined;
|
|
54
55
|
limit?: number | undefined;
|
|
55
56
|
cursor?: string | undefined;
|
|
56
|
-
search?: string | undefined;
|
|
57
57
|
}>;
|
|
58
58
|
export declare const listCompanies: {
|
|
59
59
|
name: string;
|
|
@@ -63,13 +63,13 @@ export declare const listCompanies: {
|
|
|
63
63
|
limit: z.ZodOptional<z.ZodNumber>;
|
|
64
64
|
cursor: z.ZodOptional<z.ZodString>;
|
|
65
65
|
}, "strip", z.ZodTypeAny, {
|
|
66
|
+
search?: string | undefined;
|
|
66
67
|
limit?: number | undefined;
|
|
67
68
|
cursor?: string | undefined;
|
|
68
|
-
search?: string | undefined;
|
|
69
69
|
}, {
|
|
70
|
+
search?: string | undefined;
|
|
70
71
|
limit?: number | undefined;
|
|
71
72
|
cursor?: string | undefined;
|
|
72
|
-
search?: string | undefined;
|
|
73
73
|
}>;
|
|
74
74
|
execute: (args: z.infer<typeof listCompaniesSchema>) => Promise<string>;
|
|
75
75
|
};
|
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const projectIdSchema: z.ZodObject<{
|
|
3
|
+
projectId: z.ZodString;
|
|
4
|
+
}, "strip", z.ZodTypeAny, {
|
|
5
|
+
projectId: string;
|
|
6
|
+
}, {
|
|
7
|
+
projectId: string;
|
|
8
|
+
}>;
|
|
9
|
+
declare const createTakeoffProjectSchema: z.ZodObject<{
|
|
10
|
+
projectName: z.ZodString;
|
|
11
|
+
buildingType: z.ZodEnum<["commercial", "residential"]>;
|
|
12
|
+
siteAddress: z.ZodOptional<z.ZodString>;
|
|
13
|
+
clientId: z.ZodOptional<z.ZodString>;
|
|
14
|
+
companyId: z.ZodOptional<z.ZodString>;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
projectName: string;
|
|
17
|
+
buildingType: "commercial" | "residential";
|
|
18
|
+
clientId?: string | undefined;
|
|
19
|
+
companyId?: string | undefined;
|
|
20
|
+
siteAddress?: string | undefined;
|
|
21
|
+
}, {
|
|
22
|
+
projectName: string;
|
|
23
|
+
buildingType: "commercial" | "residential";
|
|
24
|
+
clientId?: string | undefined;
|
|
25
|
+
companyId?: string | undefined;
|
|
26
|
+
siteAddress?: string | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
declare const listTakeoffProjectsSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
29
|
+
declare const uploadTakeoffInputSchema: z.ZodObject<{
|
|
30
|
+
projectId: z.ZodString;
|
|
31
|
+
kind: z.ZodString;
|
|
32
|
+
fileName: z.ZodString;
|
|
33
|
+
base64: z.ZodString;
|
|
34
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
35
|
+
}, "strip", z.ZodTypeAny, {
|
|
36
|
+
base64: string;
|
|
37
|
+
projectId: string;
|
|
38
|
+
kind: string;
|
|
39
|
+
fileName: string;
|
|
40
|
+
mimeType?: string | undefined;
|
|
41
|
+
}, {
|
|
42
|
+
base64: string;
|
|
43
|
+
projectId: string;
|
|
44
|
+
kind: string;
|
|
45
|
+
fileName: string;
|
|
46
|
+
mimeType?: string | undefined;
|
|
47
|
+
}>;
|
|
48
|
+
declare const listTakeoffRowsSchema: z.ZodObject<{
|
|
49
|
+
projectId: z.ZodString;
|
|
50
|
+
reviewState: z.ZodOptional<z.ZodString>;
|
|
51
|
+
}, "strip", z.ZodTypeAny, {
|
|
52
|
+
projectId: string;
|
|
53
|
+
reviewState?: string | undefined;
|
|
54
|
+
}, {
|
|
55
|
+
projectId: string;
|
|
56
|
+
reviewState?: string | undefined;
|
|
57
|
+
}>;
|
|
58
|
+
declare const reviewTakeoffRowSchema: z.ZodObject<{
|
|
59
|
+
projectId: z.ZodString;
|
|
60
|
+
rowId: z.ZodString;
|
|
61
|
+
action: z.ZodEnum<["approve", "skip", "edit"]>;
|
|
62
|
+
edits: z.ZodOptional<z.ZodObject<{
|
|
63
|
+
description: z.ZodOptional<z.ZodString>;
|
|
64
|
+
quantity: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
66
|
+
serviceCode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
67
|
+
}, "strip", z.ZodTypeAny, {
|
|
68
|
+
serviceCode?: string | null | undefined;
|
|
69
|
+
unit?: string | undefined;
|
|
70
|
+
quantity?: number | undefined;
|
|
71
|
+
description?: string | undefined;
|
|
72
|
+
}, {
|
|
73
|
+
serviceCode?: string | null | undefined;
|
|
74
|
+
unit?: string | undefined;
|
|
75
|
+
quantity?: number | undefined;
|
|
76
|
+
description?: string | undefined;
|
|
77
|
+
}>>;
|
|
78
|
+
}, "strip", z.ZodTypeAny, {
|
|
79
|
+
action: "skip" | "edit" | "approve";
|
|
80
|
+
projectId: string;
|
|
81
|
+
rowId: string;
|
|
82
|
+
edits?: {
|
|
83
|
+
serviceCode?: string | null | undefined;
|
|
84
|
+
unit?: string | undefined;
|
|
85
|
+
quantity?: number | undefined;
|
|
86
|
+
description?: string | undefined;
|
|
87
|
+
} | undefined;
|
|
88
|
+
}, {
|
|
89
|
+
action: "skip" | "edit" | "approve";
|
|
90
|
+
projectId: string;
|
|
91
|
+
rowId: string;
|
|
92
|
+
edits?: {
|
|
93
|
+
serviceCode?: string | null | undefined;
|
|
94
|
+
unit?: string | undefined;
|
|
95
|
+
quantity?: number | undefined;
|
|
96
|
+
description?: string | undefined;
|
|
97
|
+
} | undefined;
|
|
98
|
+
}>;
|
|
99
|
+
declare const commitTakeoffSchema: z.ZodObject<{
|
|
100
|
+
projectId: z.ZodString;
|
|
101
|
+
overrideNote: z.ZodOptional<z.ZodString>;
|
|
102
|
+
}, "strip", z.ZodTypeAny, {
|
|
103
|
+
projectId: string;
|
|
104
|
+
overrideNote?: string | undefined;
|
|
105
|
+
}, {
|
|
106
|
+
projectId: string;
|
|
107
|
+
overrideNote?: string | undefined;
|
|
108
|
+
}>;
|
|
109
|
+
declare const listSystemPacksSchema: z.ZodObject<{
|
|
110
|
+
segment: z.ZodOptional<z.ZodEnum<["commercial", "residential"]>>;
|
|
111
|
+
}, "strip", z.ZodTypeAny, {
|
|
112
|
+
segment?: "commercial" | "residential" | undefined;
|
|
113
|
+
}, {
|
|
114
|
+
segment?: "commercial" | "residential" | undefined;
|
|
115
|
+
}>;
|
|
116
|
+
declare const applySystemPackSchema: z.ZodObject<{
|
|
117
|
+
projectId: z.ZodString;
|
|
118
|
+
systemPackSlug: z.ZodString;
|
|
119
|
+
serviceProfileId: z.ZodOptional<z.ZodString>;
|
|
120
|
+
}, "strip", z.ZodTypeAny, {
|
|
121
|
+
projectId: string;
|
|
122
|
+
systemPackSlug: string;
|
|
123
|
+
serviceProfileId?: string | undefined;
|
|
124
|
+
}, {
|
|
125
|
+
projectId: string;
|
|
126
|
+
systemPackSlug: string;
|
|
127
|
+
serviceProfileId?: string | undefined;
|
|
128
|
+
}>;
|
|
129
|
+
declare const listServiceProfilesSchema: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
130
|
+
declare const upsertServiceProfileSchema: z.ZodObject<{
|
|
131
|
+
slug: z.ZodString;
|
|
132
|
+
name: z.ZodString;
|
|
133
|
+
segment: z.ZodEnum<["commercial_service", "commercial_production", "residential_service", "residential_production"]>;
|
|
134
|
+
laborMultiplier: z.ZodOptional<z.ZodNumber>;
|
|
135
|
+
defaultWastePercent: z.ZodOptional<z.ZodNumber>;
|
|
136
|
+
}, "strip", z.ZodTypeAny, {
|
|
137
|
+
name: string;
|
|
138
|
+
segment: "commercial_service" | "commercial_production" | "residential_service" | "residential_production";
|
|
139
|
+
slug: string;
|
|
140
|
+
laborMultiplier?: number | undefined;
|
|
141
|
+
defaultWastePercent?: number | undefined;
|
|
142
|
+
}, {
|
|
143
|
+
name: string;
|
|
144
|
+
segment: "commercial_service" | "commercial_production" | "residential_service" | "residential_production";
|
|
145
|
+
slug: string;
|
|
146
|
+
laborMultiplier?: number | undefined;
|
|
147
|
+
defaultWastePercent?: number | undefined;
|
|
148
|
+
}>;
|
|
149
|
+
declare const requestEstimateApprovalSchema: z.ZodObject<{
|
|
150
|
+
estimateId: z.ZodString;
|
|
151
|
+
note: z.ZodOptional<z.ZodString>;
|
|
152
|
+
}, "strip", z.ZodTypeAny, {
|
|
153
|
+
estimateId: string;
|
|
154
|
+
note?: string | undefined;
|
|
155
|
+
}, {
|
|
156
|
+
estimateId: string;
|
|
157
|
+
note?: string | undefined;
|
|
158
|
+
}>;
|
|
159
|
+
declare const approveEstimateSchema: z.ZodObject<{
|
|
160
|
+
estimateId: z.ZodString;
|
|
161
|
+
approvalId: z.ZodString;
|
|
162
|
+
action: z.ZodEnum<["approve", "reject"]>;
|
|
163
|
+
note: z.ZodOptional<z.ZodString>;
|
|
164
|
+
}, "strip", z.ZodTypeAny, {
|
|
165
|
+
action: "approve" | "reject";
|
|
166
|
+
estimateId: string;
|
|
167
|
+
approvalId: string;
|
|
168
|
+
note?: string | undefined;
|
|
169
|
+
}, {
|
|
170
|
+
action: "approve" | "reject";
|
|
171
|
+
estimateId: string;
|
|
172
|
+
approvalId: string;
|
|
173
|
+
note?: string | undefined;
|
|
174
|
+
}>;
|
|
175
|
+
declare const getEstimateApprovalSchema: z.ZodObject<{
|
|
176
|
+
estimateId: z.ZodString;
|
|
177
|
+
}, "strip", z.ZodTypeAny, {
|
|
178
|
+
estimateId: string;
|
|
179
|
+
}, {
|
|
180
|
+
estimateId: string;
|
|
181
|
+
}>;
|
|
182
|
+
export declare const createTakeoffProject: {
|
|
183
|
+
name: string;
|
|
184
|
+
description: string;
|
|
185
|
+
inputSchemaZod: z.ZodObject<{
|
|
186
|
+
projectName: z.ZodString;
|
|
187
|
+
buildingType: z.ZodEnum<["commercial", "residential"]>;
|
|
188
|
+
siteAddress: z.ZodOptional<z.ZodString>;
|
|
189
|
+
clientId: z.ZodOptional<z.ZodString>;
|
|
190
|
+
companyId: z.ZodOptional<z.ZodString>;
|
|
191
|
+
}, "strip", z.ZodTypeAny, {
|
|
192
|
+
projectName: string;
|
|
193
|
+
buildingType: "commercial" | "residential";
|
|
194
|
+
clientId?: string | undefined;
|
|
195
|
+
companyId?: string | undefined;
|
|
196
|
+
siteAddress?: string | undefined;
|
|
197
|
+
}, {
|
|
198
|
+
projectName: string;
|
|
199
|
+
buildingType: "commercial" | "residential";
|
|
200
|
+
clientId?: string | undefined;
|
|
201
|
+
companyId?: string | undefined;
|
|
202
|
+
siteAddress?: string | undefined;
|
|
203
|
+
}>;
|
|
204
|
+
execute: (args: z.infer<typeof createTakeoffProjectSchema>) => Promise<string>;
|
|
205
|
+
};
|
|
206
|
+
export declare const getTakeoffProject: {
|
|
207
|
+
name: string;
|
|
208
|
+
description: string;
|
|
209
|
+
inputSchemaZod: z.ZodObject<{
|
|
210
|
+
projectId: z.ZodString;
|
|
211
|
+
}, "strip", z.ZodTypeAny, {
|
|
212
|
+
projectId: string;
|
|
213
|
+
}, {
|
|
214
|
+
projectId: string;
|
|
215
|
+
}>;
|
|
216
|
+
execute: (args: z.infer<typeof projectIdSchema>) => Promise<string>;
|
|
217
|
+
};
|
|
218
|
+
export declare const listTakeoffProjects: {
|
|
219
|
+
name: string;
|
|
220
|
+
description: string;
|
|
221
|
+
inputSchemaZod: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
222
|
+
execute: (_args: z.infer<typeof listTakeoffProjectsSchema>) => Promise<string>;
|
|
223
|
+
};
|
|
224
|
+
export declare const uploadTakeoffInput: {
|
|
225
|
+
name: string;
|
|
226
|
+
description: string;
|
|
227
|
+
inputSchemaZod: z.ZodObject<{
|
|
228
|
+
projectId: z.ZodString;
|
|
229
|
+
kind: z.ZodString;
|
|
230
|
+
fileName: z.ZodString;
|
|
231
|
+
base64: z.ZodString;
|
|
232
|
+
mimeType: z.ZodOptional<z.ZodString>;
|
|
233
|
+
}, "strip", z.ZodTypeAny, {
|
|
234
|
+
base64: string;
|
|
235
|
+
projectId: string;
|
|
236
|
+
kind: string;
|
|
237
|
+
fileName: string;
|
|
238
|
+
mimeType?: string | undefined;
|
|
239
|
+
}, {
|
|
240
|
+
base64: string;
|
|
241
|
+
projectId: string;
|
|
242
|
+
kind: string;
|
|
243
|
+
fileName: string;
|
|
244
|
+
mimeType?: string | undefined;
|
|
245
|
+
}>;
|
|
246
|
+
execute: (args: z.infer<typeof uploadTakeoffInputSchema>) => Promise<string>;
|
|
247
|
+
};
|
|
248
|
+
export declare const runTakeoffExtract: {
|
|
249
|
+
name: string;
|
|
250
|
+
description: string;
|
|
251
|
+
inputSchemaZod: z.ZodObject<{
|
|
252
|
+
projectId: z.ZodString;
|
|
253
|
+
}, "strip", z.ZodTypeAny, {
|
|
254
|
+
projectId: string;
|
|
255
|
+
}, {
|
|
256
|
+
projectId: string;
|
|
257
|
+
}>;
|
|
258
|
+
execute: (args: z.infer<typeof projectIdSchema>) => Promise<string>;
|
|
259
|
+
};
|
|
260
|
+
export declare const listTakeoffRows: {
|
|
261
|
+
name: string;
|
|
262
|
+
description: string;
|
|
263
|
+
inputSchemaZod: z.ZodObject<{
|
|
264
|
+
projectId: z.ZodString;
|
|
265
|
+
reviewState: z.ZodOptional<z.ZodString>;
|
|
266
|
+
}, "strip", z.ZodTypeAny, {
|
|
267
|
+
projectId: string;
|
|
268
|
+
reviewState?: string | undefined;
|
|
269
|
+
}, {
|
|
270
|
+
projectId: string;
|
|
271
|
+
reviewState?: string | undefined;
|
|
272
|
+
}>;
|
|
273
|
+
execute: (args: z.infer<typeof listTakeoffRowsSchema>) => Promise<string>;
|
|
274
|
+
};
|
|
275
|
+
export declare const reviewTakeoffRow: {
|
|
276
|
+
name: string;
|
|
277
|
+
description: string;
|
|
278
|
+
inputSchemaZod: z.ZodObject<{
|
|
279
|
+
projectId: z.ZodString;
|
|
280
|
+
rowId: z.ZodString;
|
|
281
|
+
action: z.ZodEnum<["approve", "skip", "edit"]>;
|
|
282
|
+
edits: z.ZodOptional<z.ZodObject<{
|
|
283
|
+
description: z.ZodOptional<z.ZodString>;
|
|
284
|
+
quantity: z.ZodOptional<z.ZodNumber>;
|
|
285
|
+
unit: z.ZodOptional<z.ZodString>;
|
|
286
|
+
serviceCode: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
287
|
+
}, "strip", z.ZodTypeAny, {
|
|
288
|
+
serviceCode?: string | null | undefined;
|
|
289
|
+
unit?: string | undefined;
|
|
290
|
+
quantity?: number | undefined;
|
|
291
|
+
description?: string | undefined;
|
|
292
|
+
}, {
|
|
293
|
+
serviceCode?: string | null | undefined;
|
|
294
|
+
unit?: string | undefined;
|
|
295
|
+
quantity?: number | undefined;
|
|
296
|
+
description?: string | undefined;
|
|
297
|
+
}>>;
|
|
298
|
+
}, "strip", z.ZodTypeAny, {
|
|
299
|
+
action: "skip" | "edit" | "approve";
|
|
300
|
+
projectId: string;
|
|
301
|
+
rowId: string;
|
|
302
|
+
edits?: {
|
|
303
|
+
serviceCode?: string | null | undefined;
|
|
304
|
+
unit?: string | undefined;
|
|
305
|
+
quantity?: number | undefined;
|
|
306
|
+
description?: string | undefined;
|
|
307
|
+
} | undefined;
|
|
308
|
+
}, {
|
|
309
|
+
action: "skip" | "edit" | "approve";
|
|
310
|
+
projectId: string;
|
|
311
|
+
rowId: string;
|
|
312
|
+
edits?: {
|
|
313
|
+
serviceCode?: string | null | undefined;
|
|
314
|
+
unit?: string | undefined;
|
|
315
|
+
quantity?: number | undefined;
|
|
316
|
+
description?: string | undefined;
|
|
317
|
+
} | undefined;
|
|
318
|
+
}>;
|
|
319
|
+
execute: (args: z.infer<typeof reviewTakeoffRowSchema>) => Promise<string>;
|
|
320
|
+
};
|
|
321
|
+
export declare const commitTakeoff: {
|
|
322
|
+
name: string;
|
|
323
|
+
description: string;
|
|
324
|
+
inputSchemaZod: z.ZodObject<{
|
|
325
|
+
projectId: z.ZodString;
|
|
326
|
+
overrideNote: z.ZodOptional<z.ZodString>;
|
|
327
|
+
}, "strip", z.ZodTypeAny, {
|
|
328
|
+
projectId: string;
|
|
329
|
+
overrideNote?: string | undefined;
|
|
330
|
+
}, {
|
|
331
|
+
projectId: string;
|
|
332
|
+
overrideNote?: string | undefined;
|
|
333
|
+
}>;
|
|
334
|
+
execute: (args: z.infer<typeof commitTakeoffSchema>) => Promise<string>;
|
|
335
|
+
};
|
|
336
|
+
export declare const listSystemPacks: {
|
|
337
|
+
name: string;
|
|
338
|
+
description: string;
|
|
339
|
+
inputSchemaZod: z.ZodObject<{
|
|
340
|
+
segment: z.ZodOptional<z.ZodEnum<["commercial", "residential"]>>;
|
|
341
|
+
}, "strip", z.ZodTypeAny, {
|
|
342
|
+
segment?: "commercial" | "residential" | undefined;
|
|
343
|
+
}, {
|
|
344
|
+
segment?: "commercial" | "residential" | undefined;
|
|
345
|
+
}>;
|
|
346
|
+
execute: (args: z.infer<typeof listSystemPacksSchema>) => Promise<string>;
|
|
347
|
+
};
|
|
348
|
+
export declare const applySystemPack: {
|
|
349
|
+
name: string;
|
|
350
|
+
description: string;
|
|
351
|
+
inputSchemaZod: z.ZodObject<{
|
|
352
|
+
projectId: z.ZodString;
|
|
353
|
+
systemPackSlug: z.ZodString;
|
|
354
|
+
serviceProfileId: z.ZodOptional<z.ZodString>;
|
|
355
|
+
}, "strip", z.ZodTypeAny, {
|
|
356
|
+
projectId: string;
|
|
357
|
+
systemPackSlug: string;
|
|
358
|
+
serviceProfileId?: string | undefined;
|
|
359
|
+
}, {
|
|
360
|
+
projectId: string;
|
|
361
|
+
systemPackSlug: string;
|
|
362
|
+
serviceProfileId?: string | undefined;
|
|
363
|
+
}>;
|
|
364
|
+
execute: (args: z.infer<typeof applySystemPackSchema>) => Promise<string>;
|
|
365
|
+
};
|
|
366
|
+
export declare const listServiceProfiles: {
|
|
367
|
+
name: string;
|
|
368
|
+
description: string;
|
|
369
|
+
inputSchemaZod: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
370
|
+
execute: (_args: z.infer<typeof listServiceProfilesSchema>) => Promise<string>;
|
|
371
|
+
};
|
|
372
|
+
export declare const upsertServiceProfile: {
|
|
373
|
+
name: string;
|
|
374
|
+
description: string;
|
|
375
|
+
inputSchemaZod: z.ZodObject<{
|
|
376
|
+
slug: z.ZodString;
|
|
377
|
+
name: z.ZodString;
|
|
378
|
+
segment: z.ZodEnum<["commercial_service", "commercial_production", "residential_service", "residential_production"]>;
|
|
379
|
+
laborMultiplier: z.ZodOptional<z.ZodNumber>;
|
|
380
|
+
defaultWastePercent: z.ZodOptional<z.ZodNumber>;
|
|
381
|
+
}, "strip", z.ZodTypeAny, {
|
|
382
|
+
name: string;
|
|
383
|
+
segment: "commercial_service" | "commercial_production" | "residential_service" | "residential_production";
|
|
384
|
+
slug: string;
|
|
385
|
+
laborMultiplier?: number | undefined;
|
|
386
|
+
defaultWastePercent?: number | undefined;
|
|
387
|
+
}, {
|
|
388
|
+
name: string;
|
|
389
|
+
segment: "commercial_service" | "commercial_production" | "residential_service" | "residential_production";
|
|
390
|
+
slug: string;
|
|
391
|
+
laborMultiplier?: number | undefined;
|
|
392
|
+
defaultWastePercent?: number | undefined;
|
|
393
|
+
}>;
|
|
394
|
+
execute: (args: z.infer<typeof upsertServiceProfileSchema>) => Promise<string>;
|
|
395
|
+
};
|
|
396
|
+
export declare const generateEstimateFromTakeoff: {
|
|
397
|
+
name: string;
|
|
398
|
+
description: string;
|
|
399
|
+
inputSchemaZod: z.ZodObject<{
|
|
400
|
+
projectId: z.ZodString;
|
|
401
|
+
}, "strip", z.ZodTypeAny, {
|
|
402
|
+
projectId: string;
|
|
403
|
+
}, {
|
|
404
|
+
projectId: string;
|
|
405
|
+
}>;
|
|
406
|
+
execute: (args: z.infer<typeof projectIdSchema>) => Promise<string>;
|
|
407
|
+
};
|
|
408
|
+
export declare const requestEstimateApproval: {
|
|
409
|
+
name: string;
|
|
410
|
+
description: string;
|
|
411
|
+
inputSchemaZod: z.ZodObject<{
|
|
412
|
+
estimateId: z.ZodString;
|
|
413
|
+
note: z.ZodOptional<z.ZodString>;
|
|
414
|
+
}, "strip", z.ZodTypeAny, {
|
|
415
|
+
estimateId: string;
|
|
416
|
+
note?: string | undefined;
|
|
417
|
+
}, {
|
|
418
|
+
estimateId: string;
|
|
419
|
+
note?: string | undefined;
|
|
420
|
+
}>;
|
|
421
|
+
execute: (args: z.infer<typeof requestEstimateApprovalSchema>) => Promise<string>;
|
|
422
|
+
};
|
|
423
|
+
export declare const approveEstimate: {
|
|
424
|
+
name: string;
|
|
425
|
+
description: string;
|
|
426
|
+
inputSchemaZod: z.ZodObject<{
|
|
427
|
+
estimateId: z.ZodString;
|
|
428
|
+
approvalId: z.ZodString;
|
|
429
|
+
action: z.ZodEnum<["approve", "reject"]>;
|
|
430
|
+
note: z.ZodOptional<z.ZodString>;
|
|
431
|
+
}, "strip", z.ZodTypeAny, {
|
|
432
|
+
action: "approve" | "reject";
|
|
433
|
+
estimateId: string;
|
|
434
|
+
approvalId: string;
|
|
435
|
+
note?: string | undefined;
|
|
436
|
+
}, {
|
|
437
|
+
action: "approve" | "reject";
|
|
438
|
+
estimateId: string;
|
|
439
|
+
approvalId: string;
|
|
440
|
+
note?: string | undefined;
|
|
441
|
+
}>;
|
|
442
|
+
execute: (args: z.infer<typeof approveEstimateSchema>) => Promise<string>;
|
|
443
|
+
};
|
|
444
|
+
export declare const getEstimateApproval: {
|
|
445
|
+
name: string;
|
|
446
|
+
description: string;
|
|
447
|
+
inputSchemaZod: z.ZodObject<{
|
|
448
|
+
estimateId: z.ZodString;
|
|
449
|
+
}, "strip", z.ZodTypeAny, {
|
|
450
|
+
estimateId: string;
|
|
451
|
+
}, {
|
|
452
|
+
estimateId: string;
|
|
453
|
+
}>;
|
|
454
|
+
execute: (args: z.infer<typeof getEstimateApprovalSchema>) => Promise<string>;
|
|
455
|
+
};
|
|
456
|
+
export {};
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as client from "../lib/kynver-client.js";
|
|
3
|
+
const projectIdSchema = z.object({ projectId: z.string().uuid() });
|
|
4
|
+
const createTakeoffProjectSchema = z.object({
|
|
5
|
+
projectName: z.string(),
|
|
6
|
+
buildingType: z.enum(["commercial", "residential"]),
|
|
7
|
+
siteAddress: z.string().optional(),
|
|
8
|
+
clientId: z.string().optional(),
|
|
9
|
+
companyId: z.string().optional(),
|
|
10
|
+
});
|
|
11
|
+
const listTakeoffProjectsSchema = z.object({});
|
|
12
|
+
const uploadTakeoffInputSchema = z.object({
|
|
13
|
+
projectId: z.string().uuid(),
|
|
14
|
+
kind: z.string(),
|
|
15
|
+
fileName: z.string(),
|
|
16
|
+
base64: z.string(),
|
|
17
|
+
mimeType: z.string().optional(),
|
|
18
|
+
});
|
|
19
|
+
const listTakeoffRowsSchema = z.object({
|
|
20
|
+
projectId: z.string().uuid(),
|
|
21
|
+
reviewState: z.string().optional(),
|
|
22
|
+
});
|
|
23
|
+
const reviewTakeoffRowSchema = z.object({
|
|
24
|
+
projectId: z.string().uuid(),
|
|
25
|
+
rowId: z.string().uuid(),
|
|
26
|
+
action: z.enum(["approve", "skip", "edit"]),
|
|
27
|
+
edits: z
|
|
28
|
+
.object({
|
|
29
|
+
description: z.string().optional(),
|
|
30
|
+
quantity: z.number().optional(),
|
|
31
|
+
unit: z.string().optional(),
|
|
32
|
+
serviceCode: z.string().nullable().optional(),
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
35
|
+
});
|
|
36
|
+
const commitTakeoffSchema = z.object({
|
|
37
|
+
projectId: z.string().uuid(),
|
|
38
|
+
overrideNote: z.string().optional(),
|
|
39
|
+
});
|
|
40
|
+
const listSystemPacksSchema = z.object({ segment: z.enum(["commercial", "residential"]).optional() });
|
|
41
|
+
const applySystemPackSchema = z.object({
|
|
42
|
+
projectId: z.string().uuid(),
|
|
43
|
+
systemPackSlug: z.string(),
|
|
44
|
+
serviceProfileId: z.string().optional(),
|
|
45
|
+
});
|
|
46
|
+
const listServiceProfilesSchema = z.object({});
|
|
47
|
+
const upsertServiceProfileSchema = z.object({
|
|
48
|
+
slug: z.string(),
|
|
49
|
+
name: z.string(),
|
|
50
|
+
segment: z.enum([
|
|
51
|
+
"commercial_service",
|
|
52
|
+
"commercial_production",
|
|
53
|
+
"residential_service",
|
|
54
|
+
"residential_production",
|
|
55
|
+
]),
|
|
56
|
+
laborMultiplier: z.number().optional(),
|
|
57
|
+
defaultWastePercent: z.number().optional(),
|
|
58
|
+
});
|
|
59
|
+
const requestEstimateApprovalSchema = z.object({
|
|
60
|
+
estimateId: z.string().uuid(),
|
|
61
|
+
note: z.string().optional(),
|
|
62
|
+
});
|
|
63
|
+
const approveEstimateSchema = z.object({
|
|
64
|
+
estimateId: z.string().uuid(),
|
|
65
|
+
approvalId: z.string().uuid(),
|
|
66
|
+
action: z.enum(["approve", "reject"]),
|
|
67
|
+
note: z.string().optional(),
|
|
68
|
+
});
|
|
69
|
+
const getEstimateApprovalSchema = z.object({ estimateId: z.string().uuid() });
|
|
70
|
+
export const createTakeoffProject = {
|
|
71
|
+
name: "estimator_create_takeoff_project",
|
|
72
|
+
description: "Create a takeoff project shell (commercial or residential). Upload inputs, run extract, review rows, commit, apply system pack, then generate estimate.",
|
|
73
|
+
inputSchemaZod: createTakeoffProjectSchema,
|
|
74
|
+
execute: async (args) => {
|
|
75
|
+
if (!client.hasApiKey())
|
|
76
|
+
return client.noApiKeyMessage();
|
|
77
|
+
const data = await client.post("/estimator/takeoffs", args);
|
|
78
|
+
return JSON.stringify(data);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
export const getTakeoffProject = {
|
|
82
|
+
name: "estimator_get_takeoff_project",
|
|
83
|
+
description: "Get takeoff project with inputs, rows, status, pack/profile, linked estimate.",
|
|
84
|
+
inputSchemaZod: projectIdSchema,
|
|
85
|
+
execute: async (args) => {
|
|
86
|
+
if (!client.hasApiKey())
|
|
87
|
+
return client.noApiKeyMessage();
|
|
88
|
+
const data = await client.get(`/estimator/takeoffs/${args.projectId}`);
|
|
89
|
+
return JSON.stringify(data);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
export const listTakeoffProjects = {
|
|
93
|
+
name: "estimator_list_takeoff_projects",
|
|
94
|
+
description: "List takeoff projects for the workspace.",
|
|
95
|
+
inputSchemaZod: listTakeoffProjectsSchema,
|
|
96
|
+
execute: async (_args) => {
|
|
97
|
+
if (!client.hasApiKey())
|
|
98
|
+
return client.noApiKeyMessage();
|
|
99
|
+
const data = await client.get("/estimator/takeoffs");
|
|
100
|
+
return JSON.stringify(data);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
export const uploadTakeoffInput = {
|
|
104
|
+
name: "estimator_upload_takeoff_input",
|
|
105
|
+
description: "Register a takeoff file upload. Pass base64 file content via the API route from your client, or use the web UI multipart upload at POST /api/estimator/takeoffs/{id}/inputs with kind + file.",
|
|
106
|
+
inputSchemaZod: uploadTakeoffInputSchema,
|
|
107
|
+
execute: async (args) => {
|
|
108
|
+
if (!client.hasApiKey())
|
|
109
|
+
return client.noApiKeyMessage();
|
|
110
|
+
return JSON.stringify({
|
|
111
|
+
message: "Use POST /api/estimator/takeoffs/{projectId}/inputs multipart (kind + file) from an authenticated client. MCP base64 upload route can be added when hosted file relay is available.",
|
|
112
|
+
projectId: args.projectId,
|
|
113
|
+
kind: args.kind,
|
|
114
|
+
fileName: args.fileName,
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
export const runTakeoffExtract = {
|
|
119
|
+
name: "estimator_run_takeoff_extract",
|
|
120
|
+
description: "Run takeoff extractors on all uploaded inputs (idempotent).",
|
|
121
|
+
inputSchemaZod: projectIdSchema,
|
|
122
|
+
execute: async (args) => {
|
|
123
|
+
if (!client.hasApiKey())
|
|
124
|
+
return client.noApiKeyMessage();
|
|
125
|
+
const data = await client.post(`/estimator/takeoffs/${args.projectId}/extract`, {});
|
|
126
|
+
return JSON.stringify(data);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
export const listTakeoffRows = {
|
|
130
|
+
name: "estimator_list_takeoff_rows",
|
|
131
|
+
description: "List takeoff rows with evidence and review state.",
|
|
132
|
+
inputSchemaZod: listTakeoffRowsSchema,
|
|
133
|
+
execute: async (args) => {
|
|
134
|
+
if (!client.hasApiKey())
|
|
135
|
+
return client.noApiKeyMessage();
|
|
136
|
+
const qs = args.reviewState ? `?reviewState=${args.reviewState}` : "";
|
|
137
|
+
const data = await client.get(`/estimator/takeoffs/${args.projectId}/rows${qs}`);
|
|
138
|
+
return JSON.stringify(data);
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
export const reviewTakeoffRow = {
|
|
142
|
+
name: "estimator_review_takeoff_row",
|
|
143
|
+
description: "Approve, skip, or edit a single takeoff row.",
|
|
144
|
+
inputSchemaZod: reviewTakeoffRowSchema,
|
|
145
|
+
execute: async (args) => {
|
|
146
|
+
if (!client.hasApiKey())
|
|
147
|
+
return client.noApiKeyMessage();
|
|
148
|
+
const { projectId, rowId, ...body } = args;
|
|
149
|
+
const data = await client.patch(`/estimator/takeoffs/${projectId}/rows/${rowId}`, body);
|
|
150
|
+
return JSON.stringify(data);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
export const commitTakeoff = {
|
|
154
|
+
name: "estimator_commit_takeoff",
|
|
155
|
+
description: "Mark takeoff ready after all rows reviewed and area measurement approved (or override note).",
|
|
156
|
+
inputSchemaZod: commitTakeoffSchema,
|
|
157
|
+
execute: async (args) => {
|
|
158
|
+
if (!client.hasApiKey())
|
|
159
|
+
return client.noApiKeyMessage();
|
|
160
|
+
const data = await client.post(`/estimator/takeoffs/${args.projectId}/commit`, {
|
|
161
|
+
overrideNote: args.overrideNote,
|
|
162
|
+
});
|
|
163
|
+
return JSON.stringify(data);
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
export const listSystemPacks = {
|
|
167
|
+
name: "estimator_list_system_packs",
|
|
168
|
+
description: "List built-in commercial and residential system packs.",
|
|
169
|
+
inputSchemaZod: listSystemPacksSchema,
|
|
170
|
+
execute: async (args) => {
|
|
171
|
+
if (!client.hasApiKey())
|
|
172
|
+
return client.noApiKeyMessage();
|
|
173
|
+
const qs = args.segment ? `?segment=${args.segment}` : "";
|
|
174
|
+
const data = await client.get(`/estimator/system-packs${qs}`);
|
|
175
|
+
return JSON.stringify(data);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
export const applySystemPack = {
|
|
179
|
+
name: "estimator_apply_system_pack",
|
|
180
|
+
description: "Set system pack slug and optional service profile on takeoff project.",
|
|
181
|
+
inputSchemaZod: applySystemPackSchema,
|
|
182
|
+
execute: async (args) => {
|
|
183
|
+
if (!client.hasApiKey())
|
|
184
|
+
return client.noApiKeyMessage();
|
|
185
|
+
const data = await client.patch(`/estimator/takeoffs/${args.projectId}`, {
|
|
186
|
+
systemPackSlug: args.systemPackSlug,
|
|
187
|
+
serviceProfileId: args.serviceProfileId ?? null,
|
|
188
|
+
});
|
|
189
|
+
return JSON.stringify(data);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
export const listServiceProfiles = {
|
|
193
|
+
name: "estimator_list_service_profiles",
|
|
194
|
+
description: "List service department profiles (includes v6 defaults).",
|
|
195
|
+
inputSchemaZod: listServiceProfilesSchema,
|
|
196
|
+
execute: async (_args) => {
|
|
197
|
+
if (!client.hasApiKey())
|
|
198
|
+
return client.noApiKeyMessage();
|
|
199
|
+
const data = await client.get("/estimator/service-profiles");
|
|
200
|
+
return JSON.stringify(data);
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
export const upsertServiceProfile = {
|
|
204
|
+
name: "estimator_upsert_service_profile",
|
|
205
|
+
description: "Create or update a service department profile.",
|
|
206
|
+
inputSchemaZod: upsertServiceProfileSchema,
|
|
207
|
+
execute: async (args) => {
|
|
208
|
+
if (!client.hasApiKey())
|
|
209
|
+
return client.noApiKeyMessage();
|
|
210
|
+
const data = await client.post("/estimator/service-profiles", args);
|
|
211
|
+
return JSON.stringify(data);
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
export const generateEstimateFromTakeoff = {
|
|
215
|
+
name: "estimator_generate_estimate_from_takeoff",
|
|
216
|
+
description: "Generate draft estimate from committed takeoff + system pack. Preflight fails if catalog codes missing.",
|
|
217
|
+
inputSchemaZod: projectIdSchema,
|
|
218
|
+
execute: async (args) => {
|
|
219
|
+
if (!client.hasApiKey())
|
|
220
|
+
return client.noApiKeyMessage();
|
|
221
|
+
const data = await client.post(`/estimator/takeoffs/${args.projectId}/generate-estimate`, {});
|
|
222
|
+
return JSON.stringify(data);
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
export const requestEstimateApproval = {
|
|
226
|
+
name: "estimator_request_estimate_approval",
|
|
227
|
+
description: "Request owner approval before customer send.",
|
|
228
|
+
inputSchemaZod: requestEstimateApprovalSchema,
|
|
229
|
+
execute: async (args) => {
|
|
230
|
+
if (!client.hasApiKey())
|
|
231
|
+
return client.noApiKeyMessage();
|
|
232
|
+
const data = await client.post(`/estimator/estimates/${args.estimateId}/approval`, {
|
|
233
|
+
note: args.note,
|
|
234
|
+
});
|
|
235
|
+
return JSON.stringify(data);
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
export const approveEstimate = {
|
|
239
|
+
name: "estimator_approve_estimate",
|
|
240
|
+
description: "Approve or reject estimate (workspace owner).",
|
|
241
|
+
inputSchemaZod: approveEstimateSchema,
|
|
242
|
+
execute: async (args) => {
|
|
243
|
+
if (!client.hasApiKey())
|
|
244
|
+
return client.noApiKeyMessage();
|
|
245
|
+
const data = await client.post(`/estimator/estimates/${args.estimateId}/approval`, {
|
|
246
|
+
approvalId: args.approvalId,
|
|
247
|
+
action: args.action,
|
|
248
|
+
note: args.note,
|
|
249
|
+
});
|
|
250
|
+
return JSON.stringify(data);
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
export const getEstimateApproval = {
|
|
254
|
+
name: "estimator_get_estimate_approval",
|
|
255
|
+
description: "Get latest approval status for an estimate.",
|
|
256
|
+
inputSchemaZod: getEstimateApprovalSchema,
|
|
257
|
+
execute: async (args) => {
|
|
258
|
+
if (!client.hasApiKey())
|
|
259
|
+
return client.noApiKeyMessage();
|
|
260
|
+
const data = await client.get(`/estimator/estimates/${args.estimateId}/approval`);
|
|
261
|
+
return JSON.stringify(data);
|
|
262
|
+
},
|
|
263
|
+
};
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Kynver Estimator MCP server — stdio entry point.
|
|
4
|
-
* Run: node dist/index.js
|
|
5
|
-
* Env: KYNVER_API_URL (default https://kynver.com), KYNVER_API_KEY (required for all tools)
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Kynver Estimator MCP server — stdio entry point.
|
|
4
|
+
* Run: node dist/index.js
|
|
5
|
+
* Env: KYNVER_API_URL (default https://kynver.com), KYNVER_API_KEY (required for all tools)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { handleCliVersionFlag } from "./lib/package-version.js";
|
|
10
|
+
import { createEstimatorServer } from "./server.js";
|
|
11
|
+
|
|
12
|
+
if (handleCliVersionFlag(process.argv.slice(2), import.meta.url, "kynver-mcp-estimator")) {
|
|
13
|
+
// exits
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const server = createEstimatorServer();
|
|
17
|
+
const transport = new StdioServerTransport();
|
|
18
|
+
await server.connect(transport);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// @generated — synced from packages/kynver-mcp-sse-lib/src/index.ts via scripts/sync-mcp-sse-server.mjs
|
|
2
|
-
// Do not edit by hand; change the canonical file and re-run the sync script.
|
|
3
|
-
|
|
1
|
+
// @generated — synced from packages/kynver-mcp-sse-lib/src/index.ts via scripts/sync-mcp-sse-server.mjs
|
|
2
|
+
// Do not edit by hand; change the canonical file and re-run the sync script.
|
|
3
|
+
|
|
4
4
|
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
|
|
5
5
|
import { timingSafeEqual } from "node:crypto";
|
|
6
6
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @generated — synced from packages/kynver-mcp-sse-lib/src/package-version.ts via scripts/sync-mcp-sse-server.mjs
|
|
2
|
+
// Do not edit by hand; change the canonical file and re-run the sync script.
|
|
3
|
+
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
function resolvePackageRoot(moduleUrl: string): string {
|
|
9
|
+
let dir = dirname(fileURLToPath(moduleUrl));
|
|
10
|
+
for (let depth = 0; depth < 6; depth += 1) {
|
|
11
|
+
if (existsSync(join(dir, "package.json"))) return dir;
|
|
12
|
+
const parent = dirname(dir);
|
|
13
|
+
if (parent === dir) break;
|
|
14
|
+
dir = parent;
|
|
15
|
+
}
|
|
16
|
+
throw new Error(`package.json not found above ${dirname(fileURLToPath(moduleUrl))}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Read a field from the nearest `package.json` above this module. */
|
|
20
|
+
export function readOwnPackageJsonField(
|
|
21
|
+
moduleUrl: string,
|
|
22
|
+
field: "version" | "name",
|
|
23
|
+
): string {
|
|
24
|
+
const pkgPath = join(resolvePackageRoot(moduleUrl), "package.json");
|
|
25
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as Record<string, unknown>;
|
|
26
|
+
const value = pkg[field];
|
|
27
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
28
|
+
throw new Error(`Missing package.json ${field} at ${pkgPath}`);
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function readOwnPackageVersion(moduleUrl: string = import.meta.url): string {
|
|
34
|
+
return readOwnPackageJsonField(moduleUrl, "version");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function wantsCliVersion(argv: string[]): boolean {
|
|
38
|
+
return argv.some((arg) => arg === "--version" || arg === "-v");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function printCliVersionAndExit(version: string, binName?: string): never {
|
|
42
|
+
console.log(binName ? `${binName} ${version}` : version);
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** When `--version` / `-v` is present, print package version and exit before side effects. */
|
|
47
|
+
export function handleCliVersionFlag(
|
|
48
|
+
argv: string[],
|
|
49
|
+
moduleUrl: string = import.meta.url,
|
|
50
|
+
binName?: string,
|
|
51
|
+
): boolean {
|
|
52
|
+
if (!wantsCliVersion(argv)) return false;
|
|
53
|
+
printCliVersionAndExit(readOwnPackageVersion(moduleUrl), binName);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -14,6 +14,9 @@ import * as visionTools from "./tools/vision-tools.js";
|
|
|
14
14
|
import * as companyTools from "./tools/company-tools.js";
|
|
15
15
|
import * as clientTools from "./tools/client-tools.js";
|
|
16
16
|
import * as takeoffTools from "./tools/takeoff-tools.js";
|
|
17
|
+
import { readOwnPackageVersion } from "./lib/package-version.js";
|
|
18
|
+
|
|
19
|
+
const PACKAGE_VERSION = readOwnPackageVersion(import.meta.url);
|
|
17
20
|
|
|
18
21
|
type ToolConfig = { description: string; inputSchema: unknown };
|
|
19
22
|
|
|
@@ -23,7 +26,7 @@ function toolResult(text: string) {
|
|
|
23
26
|
|
|
24
27
|
export function createEstimatorServer(): McpServer {
|
|
25
28
|
const server = new McpServer(
|
|
26
|
-
{ name: "kynver-mcp-estimator", version:
|
|
29
|
+
{ name: "kynver-mcp-estimator", version: PACKAGE_VERSION },
|
|
27
30
|
{ capabilities: { tools: {} } }
|
|
28
31
|
);
|
|
29
32
|
|
|
@@ -3,18 +3,91 @@ import * as client from "../lib/kynver-client.js";
|
|
|
3
3
|
|
|
4
4
|
const projectIdSchema = z.object({ projectId: z.string().uuid() });
|
|
5
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 uploadTakeoffInputSchema = z.object({
|
|
17
|
+
projectId: z.string().uuid(),
|
|
18
|
+
kind: z.string(),
|
|
19
|
+
fileName: z.string(),
|
|
20
|
+
base64: z.string(),
|
|
21
|
+
mimeType: z.string().optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const listTakeoffRowsSchema = z.object({
|
|
25
|
+
projectId: z.string().uuid(),
|
|
26
|
+
reviewState: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const reviewTakeoffRowSchema = z.object({
|
|
30
|
+
projectId: z.string().uuid(),
|
|
31
|
+
rowId: z.string().uuid(),
|
|
32
|
+
action: z.enum(["approve", "skip", "edit"]),
|
|
33
|
+
edits: z
|
|
34
|
+
.object({
|
|
35
|
+
description: z.string().optional(),
|
|
36
|
+
quantity: z.number().optional(),
|
|
37
|
+
unit: z.string().optional(),
|
|
38
|
+
serviceCode: z.string().nullable().optional(),
|
|
39
|
+
})
|
|
40
|
+
.optional(),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const commitTakeoffSchema = z.object({
|
|
44
|
+
projectId: z.string().uuid(),
|
|
45
|
+
overrideNote: z.string().optional(),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const listSystemPacksSchema = z.object({ segment: z.enum(["commercial", "residential"]).optional() });
|
|
49
|
+
|
|
50
|
+
const applySystemPackSchema = z.object({
|
|
51
|
+
projectId: z.string().uuid(),
|
|
52
|
+
systemPackSlug: z.string(),
|
|
53
|
+
serviceProfileId: z.string().optional(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const listServiceProfilesSchema = z.object({});
|
|
57
|
+
|
|
58
|
+
const upsertServiceProfileSchema = z.object({
|
|
59
|
+
slug: z.string(),
|
|
60
|
+
name: z.string(),
|
|
61
|
+
segment: z.enum([
|
|
62
|
+
"commercial_service",
|
|
63
|
+
"commercial_production",
|
|
64
|
+
"residential_service",
|
|
65
|
+
"residential_production",
|
|
66
|
+
]),
|
|
67
|
+
laborMultiplier: z.number().optional(),
|
|
68
|
+
defaultWastePercent: z.number().optional(),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const requestEstimateApprovalSchema = z.object({
|
|
72
|
+
estimateId: z.string().uuid(),
|
|
73
|
+
note: z.string().optional(),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const approveEstimateSchema = z.object({
|
|
77
|
+
estimateId: z.string().uuid(),
|
|
78
|
+
approvalId: z.string().uuid(),
|
|
79
|
+
action: z.enum(["approve", "reject"]),
|
|
80
|
+
note: z.string().optional(),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const getEstimateApprovalSchema = z.object({ estimateId: z.string().uuid() });
|
|
84
|
+
|
|
6
85
|
export const createTakeoffProject = {
|
|
7
86
|
name: "estimator_create_takeoff_project",
|
|
8
87
|
description:
|
|
9
88
|
"Create a takeoff project shell (commercial or residential). Upload inputs, run extract, review rows, commit, apply system pack, then generate estimate.",
|
|
10
|
-
inputSchemaZod:
|
|
11
|
-
|
|
12
|
-
buildingType: z.enum(["commercial", "residential"]),
|
|
13
|
-
siteAddress: z.string().optional(),
|
|
14
|
-
clientId: z.string().optional(),
|
|
15
|
-
companyId: z.string().optional(),
|
|
16
|
-
}),
|
|
17
|
-
execute: async (args: z.infer<typeof createTakeoffProject.inputSchemaZod>): Promise<string> => {
|
|
89
|
+
inputSchemaZod: createTakeoffProjectSchema,
|
|
90
|
+
execute: async (args: z.infer<typeof createTakeoffProjectSchema>): Promise<string> => {
|
|
18
91
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
19
92
|
const data = await client.post("/estimator/takeoffs", args);
|
|
20
93
|
return JSON.stringify(data);
|
|
@@ -35,8 +108,8 @@ export const getTakeoffProject = {
|
|
|
35
108
|
export const listTakeoffProjects = {
|
|
36
109
|
name: "estimator_list_takeoff_projects",
|
|
37
110
|
description: "List takeoff projects for the workspace.",
|
|
38
|
-
inputSchemaZod:
|
|
39
|
-
execute: async (): Promise<string> => {
|
|
111
|
+
inputSchemaZod: listTakeoffProjectsSchema,
|
|
112
|
+
execute: async (_args: z.infer<typeof listTakeoffProjectsSchema>): Promise<string> => {
|
|
40
113
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
41
114
|
const data = await client.get("/estimator/takeoffs");
|
|
42
115
|
return JSON.stringify(data);
|
|
@@ -47,14 +120,8 @@ export const uploadTakeoffInput = {
|
|
|
47
120
|
name: "estimator_upload_takeoff_input",
|
|
48
121
|
description:
|
|
49
122
|
"Register a takeoff file upload. Pass base64 file content via the API route from your client, or use the web UI multipart upload at POST /api/estimator/takeoffs/{id}/inputs with kind + file.",
|
|
50
|
-
inputSchemaZod:
|
|
51
|
-
|
|
52
|
-
kind: z.string(),
|
|
53
|
-
fileName: z.string(),
|
|
54
|
-
base64: z.string(),
|
|
55
|
-
mimeType: z.string().optional(),
|
|
56
|
-
}),
|
|
57
|
-
execute: async (args: z.infer<typeof uploadTakeoffInput.inputSchemaZod>): Promise<string> => {
|
|
123
|
+
inputSchemaZod: uploadTakeoffInputSchema,
|
|
124
|
+
execute: async (args: z.infer<typeof uploadTakeoffInputSchema>): Promise<string> => {
|
|
58
125
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
59
126
|
return JSON.stringify({
|
|
60
127
|
message:
|
|
@@ -80,11 +147,8 @@ export const runTakeoffExtract = {
|
|
|
80
147
|
export const listTakeoffRows = {
|
|
81
148
|
name: "estimator_list_takeoff_rows",
|
|
82
149
|
description: "List takeoff rows with evidence and review state.",
|
|
83
|
-
inputSchemaZod:
|
|
84
|
-
|
|
85
|
-
reviewState: z.string().optional(),
|
|
86
|
-
}),
|
|
87
|
-
execute: async (args: z.infer<typeof listTakeoffRows.inputSchemaZod>): Promise<string> => {
|
|
150
|
+
inputSchemaZod: listTakeoffRowsSchema,
|
|
151
|
+
execute: async (args: z.infer<typeof listTakeoffRowsSchema>): Promise<string> => {
|
|
88
152
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
89
153
|
const qs = args.reviewState ? `?reviewState=${args.reviewState}` : "";
|
|
90
154
|
const data = await client.get(`/estimator/takeoffs/${args.projectId}/rows${qs}`);
|
|
@@ -95,20 +159,8 @@ export const listTakeoffRows = {
|
|
|
95
159
|
export const reviewTakeoffRow = {
|
|
96
160
|
name: "estimator_review_takeoff_row",
|
|
97
161
|
description: "Approve, skip, or edit a single takeoff row.",
|
|
98
|
-
inputSchemaZod:
|
|
99
|
-
|
|
100
|
-
rowId: z.string().uuid(),
|
|
101
|
-
action: z.enum(["approve", "skip", "edit"]),
|
|
102
|
-
edits: z
|
|
103
|
-
.object({
|
|
104
|
-
description: z.string().optional(),
|
|
105
|
-
quantity: z.number().optional(),
|
|
106
|
-
unit: z.string().optional(),
|
|
107
|
-
serviceCode: z.string().nullable().optional(),
|
|
108
|
-
})
|
|
109
|
-
.optional(),
|
|
110
|
-
}),
|
|
111
|
-
execute: async (args: z.infer<typeof reviewTakeoffRow.inputSchemaZod>): Promise<string> => {
|
|
162
|
+
inputSchemaZod: reviewTakeoffRowSchema,
|
|
163
|
+
execute: async (args: z.infer<typeof reviewTakeoffRowSchema>): Promise<string> => {
|
|
112
164
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
113
165
|
const { projectId, rowId, ...body } = args;
|
|
114
166
|
const data = await client.patch(`/estimator/takeoffs/${projectId}/rows/${rowId}`, body);
|
|
@@ -119,11 +171,8 @@ export const reviewTakeoffRow = {
|
|
|
119
171
|
export const commitTakeoff = {
|
|
120
172
|
name: "estimator_commit_takeoff",
|
|
121
173
|
description: "Mark takeoff ready after all rows reviewed and area measurement approved (or override note).",
|
|
122
|
-
inputSchemaZod:
|
|
123
|
-
|
|
124
|
-
overrideNote: z.string().optional(),
|
|
125
|
-
}),
|
|
126
|
-
execute: async (args: z.infer<typeof commitTakeoff.inputSchemaZod>): Promise<string> => {
|
|
174
|
+
inputSchemaZod: commitTakeoffSchema,
|
|
175
|
+
execute: async (args: z.infer<typeof commitTakeoffSchema>): Promise<string> => {
|
|
127
176
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
128
177
|
const data = await client.post(`/estimator/takeoffs/${args.projectId}/commit`, {
|
|
129
178
|
overrideNote: args.overrideNote,
|
|
@@ -135,8 +184,8 @@ export const commitTakeoff = {
|
|
|
135
184
|
export const listSystemPacks = {
|
|
136
185
|
name: "estimator_list_system_packs",
|
|
137
186
|
description: "List built-in commercial and residential system packs.",
|
|
138
|
-
inputSchemaZod:
|
|
139
|
-
execute: async (args: z.infer<typeof
|
|
187
|
+
inputSchemaZod: listSystemPacksSchema,
|
|
188
|
+
execute: async (args: z.infer<typeof listSystemPacksSchema>): Promise<string> => {
|
|
140
189
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
141
190
|
const qs = args.segment ? `?segment=${args.segment}` : "";
|
|
142
191
|
const data = await client.get(`/estimator/system-packs${qs}`);
|
|
@@ -147,12 +196,8 @@ export const listSystemPacks = {
|
|
|
147
196
|
export const applySystemPack = {
|
|
148
197
|
name: "estimator_apply_system_pack",
|
|
149
198
|
description: "Set system pack slug and optional service profile on takeoff project.",
|
|
150
|
-
inputSchemaZod:
|
|
151
|
-
|
|
152
|
-
systemPackSlug: z.string(),
|
|
153
|
-
serviceProfileId: z.string().optional(),
|
|
154
|
-
}),
|
|
155
|
-
execute: async (args: z.infer<typeof applySystemPack.inputSchemaZod>): Promise<string> => {
|
|
199
|
+
inputSchemaZod: applySystemPackSchema,
|
|
200
|
+
execute: async (args: z.infer<typeof applySystemPackSchema>): Promise<string> => {
|
|
156
201
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
157
202
|
const data = await client.patch(`/estimator/takeoffs/${args.projectId}`, {
|
|
158
203
|
systemPackSlug: args.systemPackSlug,
|
|
@@ -165,8 +210,8 @@ export const applySystemPack = {
|
|
|
165
210
|
export const listServiceProfiles = {
|
|
166
211
|
name: "estimator_list_service_profiles",
|
|
167
212
|
description: "List service department profiles (includes v6 defaults).",
|
|
168
|
-
inputSchemaZod:
|
|
169
|
-
execute: async (): Promise<string> => {
|
|
213
|
+
inputSchemaZod: listServiceProfilesSchema,
|
|
214
|
+
execute: async (_args: z.infer<typeof listServiceProfilesSchema>): Promise<string> => {
|
|
170
215
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
171
216
|
const data = await client.get("/estimator/service-profiles");
|
|
172
217
|
return JSON.stringify(data);
|
|
@@ -176,19 +221,8 @@ export const listServiceProfiles = {
|
|
|
176
221
|
export const upsertServiceProfile = {
|
|
177
222
|
name: "estimator_upsert_service_profile",
|
|
178
223
|
description: "Create or update a service department profile.",
|
|
179
|
-
inputSchemaZod:
|
|
180
|
-
|
|
181
|
-
name: z.string(),
|
|
182
|
-
segment: z.enum([
|
|
183
|
-
"commercial_service",
|
|
184
|
-
"commercial_production",
|
|
185
|
-
"residential_service",
|
|
186
|
-
"residential_production",
|
|
187
|
-
]),
|
|
188
|
-
laborMultiplier: z.number().optional(),
|
|
189
|
-
defaultWastePercent: z.number().optional(),
|
|
190
|
-
}),
|
|
191
|
-
execute: async (args: z.infer<typeof upsertServiceProfile.inputSchemaZod>): Promise<string> => {
|
|
224
|
+
inputSchemaZod: upsertServiceProfileSchema,
|
|
225
|
+
execute: async (args: z.infer<typeof upsertServiceProfileSchema>): Promise<string> => {
|
|
192
226
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
193
227
|
const data = await client.post("/estimator/service-profiles", args);
|
|
194
228
|
return JSON.stringify(data);
|
|
@@ -210,8 +244,8 @@ export const generateEstimateFromTakeoff = {
|
|
|
210
244
|
export const requestEstimateApproval = {
|
|
211
245
|
name: "estimator_request_estimate_approval",
|
|
212
246
|
description: "Request owner approval before customer send.",
|
|
213
|
-
inputSchemaZod:
|
|
214
|
-
execute: async (args: z.infer<typeof
|
|
247
|
+
inputSchemaZod: requestEstimateApprovalSchema,
|
|
248
|
+
execute: async (args: z.infer<typeof requestEstimateApprovalSchema>): Promise<string> => {
|
|
215
249
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
216
250
|
const data = await client.post(`/estimator/estimates/${args.estimateId}/approval`, {
|
|
217
251
|
note: args.note,
|
|
@@ -223,13 +257,8 @@ export const requestEstimateApproval = {
|
|
|
223
257
|
export const approveEstimate = {
|
|
224
258
|
name: "estimator_approve_estimate",
|
|
225
259
|
description: "Approve or reject estimate (workspace owner).",
|
|
226
|
-
inputSchemaZod:
|
|
227
|
-
|
|
228
|
-
approvalId: z.string().uuid(),
|
|
229
|
-
action: z.enum(["approve", "reject"]),
|
|
230
|
-
note: z.string().optional(),
|
|
231
|
-
}),
|
|
232
|
-
execute: async (args: z.infer<typeof approveEstimate.inputSchemaZod>): Promise<string> => {
|
|
260
|
+
inputSchemaZod: approveEstimateSchema,
|
|
261
|
+
execute: async (args: z.infer<typeof approveEstimateSchema>): Promise<string> => {
|
|
233
262
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
234
263
|
const data = await client.post(`/estimator/estimates/${args.estimateId}/approval`, {
|
|
235
264
|
approvalId: args.approvalId,
|
|
@@ -243,8 +272,8 @@ export const approveEstimate = {
|
|
|
243
272
|
export const getEstimateApproval = {
|
|
244
273
|
name: "estimator_get_estimate_approval",
|
|
245
274
|
description: "Get latest approval status for an estimate.",
|
|
246
|
-
inputSchemaZod:
|
|
247
|
-
execute: async (args: z.infer<typeof
|
|
275
|
+
inputSchemaZod: getEstimateApprovalSchema,
|
|
276
|
+
execute: async (args: z.infer<typeof getEstimateApprovalSchema>): Promise<string> => {
|
|
248
277
|
if (!client.hasApiKey()) return client.noApiKeyMessage();
|
|
249
278
|
const data = await client.get(`/estimator/estimates/${args.estimateId}/approval`);
|
|
250
279
|
return JSON.stringify(data);
|