@kynver-app/mcp-estimator 0.2.16 → 0.2.18
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 +1 -14
- package/dist/sse.js +1 -9
- package/package.json +8 -5
- package/dist/index.d.ts +0 -7
- package/dist/lib/kynver-client.d.ts +0 -20
- package/dist/lib/kynver-client.js +0 -115
- package/dist/lib/mcp-sse-server.d.ts +0 -44
- package/dist/lib/mcp-sse-server.js +0 -441
- package/dist/lib/package-version.d.ts +0 -7
- package/dist/lib/package-version.js +0 -44
- package/dist/server.d.ts +0 -6
- package/dist/server.js +0 -119
- package/dist/sse.d.ts +0 -1
- package/dist/tools/catalog-tools.d.ts +0 -381
- package/dist/tools/catalog-tools.js +0 -192
- package/dist/tools/client-tools.d.ts +0 -193
- package/dist/tools/client-tools.js +0 -105
- package/dist/tools/company-tools.d.ts +0 -163
- package/dist/tools/company-tools.js +0 -98
- package/dist/tools/email-pdf-tools.d.ts +0 -51
- package/dist/tools/email-pdf-tools.js +0 -22
- package/dist/tools/estimate-tools.d.ts +0 -531
- package/dist/tools/estimate-tools.js +0 -259
- package/dist/tools/intake-tools.d.ts +0 -463
- package/dist/tools/intake-tools.js +0 -82
- package/dist/tools/photo-tools.d.ts +0 -77
- package/dist/tools/photo-tools.js +0 -51
- package/dist/tools/price-catalog-tools.d.ts +0 -96
- package/dist/tools/price-catalog-tools.js +0 -72
- package/dist/tools/remodel-deliverable-tools.d.ts +0 -1868
- package/dist/tools/remodel-deliverable-tools.js +0 -138
- package/dist/tools/remodel-estimate-tools.d.ts +0 -525
- package/dist/tools/remodel-estimate-tools.js +0 -71
- package/dist/tools/signing-tools.d.ts +0 -89
- package/dist/tools/signing-tools.js +0 -51
- package/dist/tools/stats-pdf-tools.d.ts +0 -113
- package/dist/tools/stats-pdf-tools.js +0 -73
- package/dist/tools/takeoff-tools.d.ts +0 -730
- package/dist/tools/takeoff-tools.js +0 -297
- package/dist/tools/template-tools.d.ts +0 -96
- package/dist/tools/template-tools.js +0 -70
- package/dist/tools/version-tools.d.ts +0 -46
- package/dist/tools/version-tools.js +0 -33
- package/dist/tools/vision-tools.d.ts +0 -52
- package/dist/tools/vision-tools.js +0 -42
|
@@ -1,441 +0,0 @@
|
|
|
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
|
-
import { createServer } from "node:http";
|
|
4
|
-
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
5
|
-
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
6
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
|
-
export const MAX_SSE_BODY_BYTES = 1 * 1024 * 1024;
|
|
8
|
-
const FORBIDDEN_QUERY_KEYS = ["apiKey", "secret", "userId"];
|
|
9
|
-
const TRUTHY_ENV = new Set(["1", "true", "yes", "on"]);
|
|
10
|
-
function isServiceSecretImpersonationRejectedInProduction() {
|
|
11
|
-
const override = process.env.KYNVER_ALLOW_SERVICE_SECRET_IMPERSONATION?.trim().toLowerCase();
|
|
12
|
-
if (override !== undefined && TRUTHY_ENV.has(override))
|
|
13
|
-
return false;
|
|
14
|
-
return process.env.NODE_ENV === "production" || process.env.VERCEL_ENV === "production";
|
|
15
|
-
}
|
|
16
|
-
export function headerValue(req, name) {
|
|
17
|
-
const v = req.headers[name];
|
|
18
|
-
return typeof v === "string" ? v : Array.isArray(v) ? (v[0] ?? null) : null;
|
|
19
|
-
}
|
|
20
|
-
export function timingSafeEqualString(a, b) {
|
|
21
|
-
const left = Buffer.from(a);
|
|
22
|
-
const right = Buffer.from(b);
|
|
23
|
-
if (left.length !== right.length)
|
|
24
|
-
return false;
|
|
25
|
-
return timingSafeEqual(left, right);
|
|
26
|
-
}
|
|
27
|
-
export function hasForbiddenQueryAuth(url) {
|
|
28
|
-
return FORBIDDEN_QUERY_KEYS.some((key) => url.searchParams.has(key));
|
|
29
|
-
}
|
|
30
|
-
export function resolveListenHost() {
|
|
31
|
-
const configured = process.env.MCP_SSE_HOST?.trim();
|
|
32
|
-
return configured || "127.0.0.1";
|
|
33
|
-
}
|
|
34
|
-
/** Positive integer from env, else fallback. */
|
|
35
|
-
export function intFromEnv(name, fallback) {
|
|
36
|
-
const raw = process.env[name];
|
|
37
|
-
if (!raw)
|
|
38
|
-
return fallback;
|
|
39
|
-
const n = parseInt(raw, 10);
|
|
40
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Resolve + validate the Kynver API origin the server sends bearer keys to.
|
|
44
|
-
* A poisoned/misconfigured value would exfiltrate API keys, so fail closed on
|
|
45
|
-
* anything that isn't https (http is allowed only for localhost dev).
|
|
46
|
-
*/
|
|
47
|
-
export function resolveApiBaseUrl() {
|
|
48
|
-
const raw = process.env.KYNVER_API_URL?.trim();
|
|
49
|
-
if (!raw)
|
|
50
|
-
return "https://kynver.com";
|
|
51
|
-
let parsed;
|
|
52
|
-
try {
|
|
53
|
-
parsed = new URL(raw);
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
throw new Error(`Invalid KYNVER_API_URL: ${raw}`);
|
|
57
|
-
}
|
|
58
|
-
const isLocalhost = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
|
|
59
|
-
if (parsed.protocol !== "https:" && !(parsed.protocol === "http:" && isLocalhost)) {
|
|
60
|
-
throw new Error(`KYNVER_API_URL must be https (got "${parsed.protocol}//${parsed.hostname}"); API keys are sent to this origin.`);
|
|
61
|
-
}
|
|
62
|
-
return raw.replace(/\/$/, "");
|
|
63
|
-
}
|
|
64
|
-
/** First client IP from x-forwarded-for, else the socket address. */
|
|
65
|
-
export function clientIp(req) {
|
|
66
|
-
const fwd = headerValue(req, "x-forwarded-for");
|
|
67
|
-
if (fwd)
|
|
68
|
-
return fwd.split(",")[0].trim();
|
|
69
|
-
return req.socket?.remoteAddress ?? "unknown";
|
|
70
|
-
}
|
|
71
|
-
/** Minimal in-process fixed-window limiter (the SSE server is single-process). */
|
|
72
|
-
export function createFixedWindowRateLimiter(limit, windowMs) {
|
|
73
|
-
const hits = new Map();
|
|
74
|
-
return {
|
|
75
|
-
check(key) {
|
|
76
|
-
const now = Date.now();
|
|
77
|
-
const entry = hits.get(key);
|
|
78
|
-
if (!entry || now >= entry.resetAt) {
|
|
79
|
-
hits.set(key, { count: 1, resetAt: now + windowMs });
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
if (entry.count >= limit)
|
|
83
|
-
return false;
|
|
84
|
-
entry.count += 1;
|
|
85
|
-
return true;
|
|
86
|
-
},
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
export function parseJsonBody(req, maxBytes = MAX_SSE_BODY_BYTES) {
|
|
90
|
-
return new Promise((resolve, reject) => {
|
|
91
|
-
const chunks = [];
|
|
92
|
-
let totalSize = 0;
|
|
93
|
-
req.on("data", (chunk) => {
|
|
94
|
-
totalSize += chunk.length;
|
|
95
|
-
if (totalSize > maxBytes) {
|
|
96
|
-
req.destroy();
|
|
97
|
-
reject(new Error("Request body too large"));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
chunks.push(chunk);
|
|
101
|
-
});
|
|
102
|
-
req.on("end", () => {
|
|
103
|
-
try {
|
|
104
|
-
resolve(JSON.parse(Buffer.concat(chunks).toString()));
|
|
105
|
-
}
|
|
106
|
-
catch (error) {
|
|
107
|
-
reject(error);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
req.on("error", reject);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
export function validateMcporterServerName(name) {
|
|
114
|
-
const trimmed = name.trim();
|
|
115
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(trimmed)) {
|
|
116
|
-
throw new Error("Invalid mcporter server name.");
|
|
117
|
-
}
|
|
118
|
-
return trimmed;
|
|
119
|
-
}
|
|
120
|
-
function bearerToken(req) {
|
|
121
|
-
const auth = headerValue(req, "authorization");
|
|
122
|
-
if (!auth)
|
|
123
|
-
return "";
|
|
124
|
-
const match = /^Bearer\s+(.+)$/i.exec(auth);
|
|
125
|
-
return match?.[1]?.trim() ?? "";
|
|
126
|
-
}
|
|
127
|
-
function resolveInternalAuth(req, serviceSecret) {
|
|
128
|
-
const internalUserId = headerValue(req, "x-kynver-user-id");
|
|
129
|
-
const internalSecret = headerValue(req, "x-kynver-service-secret");
|
|
130
|
-
if (!internalUserId || !serviceSecret || !internalSecret)
|
|
131
|
-
return null;
|
|
132
|
-
if (isServiceSecretImpersonationRejectedInProduction())
|
|
133
|
-
return null;
|
|
134
|
-
if (!timingSafeEqualString(internalSecret, serviceSecret))
|
|
135
|
-
return null;
|
|
136
|
-
return { userId: internalUserId, auth: { kind: "internal", userId: internalUserId } };
|
|
137
|
-
}
|
|
138
|
-
function verifyPostAuth(req, url, sessionAuth, serviceSecret) {
|
|
139
|
-
if (sessionAuth.kind === "apiKey") {
|
|
140
|
-
const token = bearerToken(req);
|
|
141
|
-
return token.length > 0 && timingSafeEqualString(token, sessionAuth.apiKey);
|
|
142
|
-
}
|
|
143
|
-
const internal = resolveInternalAuth(req, serviceSecret);
|
|
144
|
-
return (internal !== null &&
|
|
145
|
-
internal.userId === sessionAuth.userId &&
|
|
146
|
-
internal.auth.kind === "internal");
|
|
147
|
-
}
|
|
148
|
-
async function resolveUserFromApiKey(baseUrl, apiKey) {
|
|
149
|
-
try {
|
|
150
|
-
const res = await fetch(`${baseUrl.replace(/\/$/, "")}/api/users/me`, {
|
|
151
|
-
headers: { Authorization: `Bearer ${apiKey}` },
|
|
152
|
-
});
|
|
153
|
-
if (!res.ok)
|
|
154
|
-
return null;
|
|
155
|
-
const data = (await res.json());
|
|
156
|
-
return data.id ?? null;
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function parseUrl(req, port) {
|
|
163
|
-
return new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
164
|
-
}
|
|
165
|
-
function json(res, status, body) {
|
|
166
|
-
res.writeHead(status, { "Content-Type": "application/json" });
|
|
167
|
-
res.end(JSON.stringify(body));
|
|
168
|
-
}
|
|
169
|
-
export function startKynverMcpSseServer(options) {
|
|
170
|
-
const port = parseInt(process.env.PORT ?? String(options.defaultPort), 10);
|
|
171
|
-
const baseUrl = resolveApiBaseUrl();
|
|
172
|
-
const serviceSecret = process.env.KYNVER_SERVICE_SECRET ?? "";
|
|
173
|
-
const host = resolveListenHost();
|
|
174
|
-
const sessions = {};
|
|
175
|
-
const streamableSessions = {};
|
|
176
|
-
// Resource / abuse limits (env-overridable; in-process per server).
|
|
177
|
-
const maxSessions = intFromEnv("MCP_SSE_MAX_SESSIONS", 200);
|
|
178
|
-
const maxSessionsPerUser = intFromEnv("MCP_SSE_MAX_SESSIONS_PER_USER", 20);
|
|
179
|
-
const idleTimeoutMs = intFromEnv("MCP_SSE_IDLE_TIMEOUT_MS", 5 * 60 * 1000);
|
|
180
|
-
const rateLimitPerMin = intFromEnv("MCP_SSE_RATE_LIMIT_PER_MIN", 120);
|
|
181
|
-
const rateLimiter = createFixedWindowRateLimiter(rateLimitPerMin, 60 * 1000);
|
|
182
|
-
// Close idle sessions (both transports) so a flood of half-open connections
|
|
183
|
-
// can't pin memory.
|
|
184
|
-
const idleSweep = setInterval(() => {
|
|
185
|
-
const now = Date.now();
|
|
186
|
-
for (const [sid, entry] of Object.entries(sessions)) {
|
|
187
|
-
if (now - entry.lastActivity > idleTimeoutMs) {
|
|
188
|
-
delete sessions[sid];
|
|
189
|
-
void entry.transport.close().catch(() => { });
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
for (const [sid, entry] of Object.entries(streamableSessions)) {
|
|
193
|
-
if (now - entry.lastActivity > idleTimeoutMs) {
|
|
194
|
-
delete streamableSessions[sid];
|
|
195
|
-
void entry.transport.close().catch(() => { });
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}, 60 * 1000);
|
|
199
|
-
idleSweep.unref?.();
|
|
200
|
-
const activeSessionCount = () => Object.keys(sessions).length + Object.keys(streamableSessions).length;
|
|
201
|
-
const activeSessionCountForUser = (userId) => Object.values(sessions).filter((s) => s.userId === userId).length +
|
|
202
|
-
Object.values(streamableSessions).filter((s) => s.userId === userId).length;
|
|
203
|
-
const httpServer = createServer(async (req, res) => {
|
|
204
|
-
const url = parseUrl(req, port);
|
|
205
|
-
const path = url.pathname;
|
|
206
|
-
if (req.method === "GET" && path === "/sse") {
|
|
207
|
-
// Pre-auth rate limit by client IP (we don't know the user yet).
|
|
208
|
-
if (!rateLimiter.check(`sse:${clientIp(req)}`)) {
|
|
209
|
-
json(res, 429, { error: "Too many requests. Slow down and retry." });
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
if (hasForbiddenQueryAuth(url)) {
|
|
213
|
-
json(res, 400, {
|
|
214
|
-
error: "Internal auth via query params is not allowed. Use X-Kynver-User-Id and X-Kynver-Service-Secret headers.",
|
|
215
|
-
});
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
let userId = null;
|
|
219
|
-
let auth = null;
|
|
220
|
-
const internal = serviceSecret ? resolveInternalAuth(req, serviceSecret) : null;
|
|
221
|
-
if (internal) {
|
|
222
|
-
userId = internal.userId;
|
|
223
|
-
auth = internal.auth;
|
|
224
|
-
}
|
|
225
|
-
else {
|
|
226
|
-
const apiKey = bearerToken(req);
|
|
227
|
-
if (apiKey) {
|
|
228
|
-
userId = await resolveUserFromApiKey(baseUrl, apiKey);
|
|
229
|
-
if (userId)
|
|
230
|
-
auth = { kind: "apiKey", apiKey };
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (!userId || !auth) {
|
|
234
|
-
json(res, 401, {
|
|
235
|
-
error: "Unauthorized. Pass API key via Authorization: Bearer <key> or internal X-Kynver-User-Id + X-Kynver-Service-Secret headers.",
|
|
236
|
-
});
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
// Connection caps — bound total + per-user concurrent sessions (across
|
|
240
|
-
// both transports) so an authenticated caller can't exhaust
|
|
241
|
-
// memory/sockets by opening many.
|
|
242
|
-
if (activeSessionCount() >= maxSessions) {
|
|
243
|
-
json(res, 503, { error: "Server at capacity. Try again shortly." });
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
if (activeSessionCountForUser(userId) >= maxSessionsPerUser) {
|
|
247
|
-
json(res, 429, { error: "Too many concurrent connections for this account." });
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
console.log(`[MCP SSE] New connection (${options.label}, user: ${userId.slice(0, 8)}...)`);
|
|
251
|
-
const transport = new SSEServerTransport("/messages", res);
|
|
252
|
-
sessions[transport.sessionId] = { transport, userId, auth, lastActivity: Date.now() };
|
|
253
|
-
res.on("close", () => {
|
|
254
|
-
delete sessions[transport.sessionId];
|
|
255
|
-
});
|
|
256
|
-
const server = await options.createMcpServer();
|
|
257
|
-
await server.connect(transport);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
if (req.method === "POST" && path === "/messages") {
|
|
261
|
-
const sessionId = url.searchParams.get("sessionId");
|
|
262
|
-
const entry = sessionId ? sessions[sessionId] : undefined;
|
|
263
|
-
if (!entry) {
|
|
264
|
-
json(res, 400, { error: "No active SSE session for this sessionId" });
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
if (!verifyPostAuth(req, url, entry.auth, serviceSecret)) {
|
|
268
|
-
json(res, 401, { error: "Unauthorized. Re-send the same credentials used to open the SSE session." });
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
// Per-user rate limit on tool/message traffic.
|
|
272
|
-
if (!rateLimiter.check(`msg:${entry.userId}`)) {
|
|
273
|
-
json(res, 429, { error: "Too many requests. Slow down and retry." });
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
entry.lastActivity = Date.now();
|
|
277
|
-
try {
|
|
278
|
-
const body = await parseJsonBody(req);
|
|
279
|
-
await options.requestContext.run({ userId: entry.userId }, async () => {
|
|
280
|
-
await entry.transport.handlePostMessage(req, res, body);
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
catch (error) {
|
|
284
|
-
json(res, 400, {
|
|
285
|
-
error: error instanceof Error ? error.message : "Invalid request body",
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
// Streamable HTTP transport (MCP spec 2025-03-26+) — the transport modern
|
|
291
|
-
// connector clients (Grok, ChatGPT, Gemini, Claude) expect for remote
|
|
292
|
-
// servers. One path, three verbs: POST carries JSON-RPC (initialize opens a
|
|
293
|
-
// session; the response's mcp-session-id header identifies it), GET opens
|
|
294
|
-
// the standalone server→client SSE stream, DELETE terminates the session.
|
|
295
|
-
// Auth model mirrors the legacy /sse pair: credentials are verified on the
|
|
296
|
-
// initialize POST, bound to the session, and every subsequent request must
|
|
297
|
-
// re-present the same credentials.
|
|
298
|
-
if (path === "/mcp") {
|
|
299
|
-
if (req.method !== "POST" && req.method !== "GET" && req.method !== "DELETE") {
|
|
300
|
-
res.writeHead(405, { "Content-Type": "application/json", Allow: "GET, POST, DELETE" });
|
|
301
|
-
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
if (hasForbiddenQueryAuth(url)) {
|
|
305
|
-
json(res, 400, {
|
|
306
|
-
error: "Internal auth via query params is not allowed. Use X-Kynver-User-Id and X-Kynver-Service-Secret headers.",
|
|
307
|
-
});
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
const sessionId = headerValue(req, "mcp-session-id");
|
|
311
|
-
if (sessionId) {
|
|
312
|
-
const entry = streamableSessions[sessionId];
|
|
313
|
-
if (!entry) {
|
|
314
|
-
// 404 (not 400) per spec so a well-behaved client re-initializes.
|
|
315
|
-
json(res, 404, { error: "Unknown or expired MCP session. Re-initialize." });
|
|
316
|
-
return;
|
|
317
|
-
}
|
|
318
|
-
if (!verifyPostAuth(req, url, entry.auth, serviceSecret)) {
|
|
319
|
-
json(res, 401, {
|
|
320
|
-
error: "Unauthorized. Re-send the same credentials used to initialize the MCP session.",
|
|
321
|
-
});
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
if (!rateLimiter.check(`msg:${entry.userId}`)) {
|
|
325
|
-
json(res, 429, { error: "Too many requests. Slow down and retry." });
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
entry.lastActivity = Date.now();
|
|
329
|
-
try {
|
|
330
|
-
const body = req.method === "POST" ? await parseJsonBody(req) : undefined;
|
|
331
|
-
await options.requestContext.run({ userId: entry.userId }, async () => {
|
|
332
|
-
await entry.transport.handleRequest(req, res, body);
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
catch (error) {
|
|
336
|
-
if (!res.headersSent) {
|
|
337
|
-
json(res, 400, {
|
|
338
|
-
error: error instanceof Error ? error.message : "Invalid request body",
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
// No session header — only an initialize POST may open a session. (The
|
|
345
|
-
// transport itself rejects a sessionless non-initialize POST with 400.)
|
|
346
|
-
if (req.method !== "POST") {
|
|
347
|
-
json(res, 400, {
|
|
348
|
-
error: "Missing mcp-session-id header. Open a session with an initialize POST first.",
|
|
349
|
-
});
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
// Pre-auth rate limit by client IP (we don't know the user yet).
|
|
353
|
-
if (!rateLimiter.check(`mcp:${clientIp(req)}`)) {
|
|
354
|
-
json(res, 429, { error: "Too many requests. Slow down and retry." });
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
let userId = null;
|
|
358
|
-
let auth = null;
|
|
359
|
-
const internal = serviceSecret ? resolveInternalAuth(req, serviceSecret) : null;
|
|
360
|
-
if (internal) {
|
|
361
|
-
userId = internal.userId;
|
|
362
|
-
auth = internal.auth;
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
const apiKey = bearerToken(req);
|
|
366
|
-
if (apiKey) {
|
|
367
|
-
userId = await resolveUserFromApiKey(baseUrl, apiKey);
|
|
368
|
-
if (userId)
|
|
369
|
-
auth = { kind: "apiKey", apiKey };
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
if (!userId || !auth) {
|
|
373
|
-
json(res, 401, {
|
|
374
|
-
error: "Unauthorized. Pass API key via Authorization: Bearer <key> or internal X-Kynver-User-Id + X-Kynver-Service-Secret headers.",
|
|
375
|
-
});
|
|
376
|
-
return;
|
|
377
|
-
}
|
|
378
|
-
if (activeSessionCount() >= maxSessions) {
|
|
379
|
-
json(res, 503, { error: "Server at capacity. Try again shortly." });
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
if (activeSessionCountForUser(userId) >= maxSessionsPerUser) {
|
|
383
|
-
json(res, 429, { error: "Too many concurrent connections for this account." });
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
let body;
|
|
387
|
-
try {
|
|
388
|
-
body = await parseJsonBody(req);
|
|
389
|
-
}
|
|
390
|
-
catch (error) {
|
|
391
|
-
json(res, 400, {
|
|
392
|
-
error: error instanceof Error ? error.message : "Invalid request body",
|
|
393
|
-
});
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
const boundUserId = userId;
|
|
397
|
-
const boundAuth = auth;
|
|
398
|
-
const transport = new StreamableHTTPServerTransport({
|
|
399
|
-
sessionIdGenerator: () => randomUUID(),
|
|
400
|
-
onsessioninitialized: (sid) => {
|
|
401
|
-
streamableSessions[sid] = {
|
|
402
|
-
transport,
|
|
403
|
-
userId: boundUserId,
|
|
404
|
-
auth: boundAuth,
|
|
405
|
-
lastActivity: Date.now(),
|
|
406
|
-
};
|
|
407
|
-
console.log(`[MCP HTTP] New session (${options.label}, user: ${boundUserId.slice(0, 8)}...)`);
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
transport.onclose = () => {
|
|
411
|
-
if (transport.sessionId)
|
|
412
|
-
delete streamableSessions[transport.sessionId];
|
|
413
|
-
};
|
|
414
|
-
const server = await options.createMcpServer();
|
|
415
|
-
await server.connect(transport);
|
|
416
|
-
try {
|
|
417
|
-
await options.requestContext.run({ userId: boundUserId }, async () => {
|
|
418
|
-
await transport.handleRequest(req, res, body);
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
catch (error) {
|
|
422
|
-
if (!res.headersSent) {
|
|
423
|
-
json(res, 400, {
|
|
424
|
-
error: error instanceof Error ? error.message : "Invalid request",
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
if (req.method === "GET" && path === "/health") {
|
|
431
|
-
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
432
|
-
res.end("ok");
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
json(res, 404, { error: "Not found" });
|
|
436
|
-
});
|
|
437
|
-
httpServer.listen(port, host, () => {
|
|
438
|
-
console.log(`[MCP SSE] ${options.label} listening on ${host}:${port}`);
|
|
439
|
-
});
|
|
440
|
-
return httpServer;
|
|
441
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
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;
|
|
@@ -1,44 +0,0 @@
|
|
|
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.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates and configures the estimator MCP server with all tools registered.
|
|
3
|
-
* Separated from transport so it can be used with stdio (CLI) or SSE (hosted).
|
|
4
|
-
*/
|
|
5
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
-
export declare function createEstimatorServer(): McpServer;
|
package/dist/server.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Creates and configures the estimator MCP server with all tools registered.
|
|
3
|
-
* Separated from transport so it can be used with stdio (CLI) or SSE (hosted).
|
|
4
|
-
*/
|
|
5
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
-
import * as catalogTools from "./tools/catalog-tools.js";
|
|
7
|
-
import * as priceCatalogTools from "./tools/price-catalog-tools.js";
|
|
8
|
-
import * as estimateTools from "./tools/estimate-tools.js";
|
|
9
|
-
import * as templateTools from "./tools/template-tools.js";
|
|
10
|
-
import * as versionTools from "./tools/version-tools.js";
|
|
11
|
-
import * as photoTools from "./tools/photo-tools.js";
|
|
12
|
-
import * as statsPdfTools from "./tools/stats-pdf-tools.js";
|
|
13
|
-
import * as emailPdfTools from "./tools/email-pdf-tools.js";
|
|
14
|
-
import * as visionTools from "./tools/vision-tools.js";
|
|
15
|
-
import * as companyTools from "./tools/company-tools.js";
|
|
16
|
-
import * as clientTools from "./tools/client-tools.js";
|
|
17
|
-
import * as takeoffTools from "./tools/takeoff-tools.js";
|
|
18
|
-
import * as intakeTools from "./tools/intake-tools.js";
|
|
19
|
-
import * as remodelEstimateTools from "./tools/remodel-estimate-tools.js";
|
|
20
|
-
import * as remodelDeliverableTools from "./tools/remodel-deliverable-tools.js";
|
|
21
|
-
import * as signingTools from "./tools/signing-tools.js";
|
|
22
|
-
import { readOwnPackageVersion } from "./lib/package-version.js";
|
|
23
|
-
const PACKAGE_VERSION = readOwnPackageVersion(import.meta.url);
|
|
24
|
-
function toolResult(text) {
|
|
25
|
-
return { content: [{ type: "text", text }] };
|
|
26
|
-
}
|
|
27
|
-
export function createEstimatorServer() {
|
|
28
|
-
const server = new McpServer({ name: "kynver-mcp-estimator", version: PACKAGE_VERSION }, { capabilities: { tools: {} } });
|
|
29
|
-
function register(name, config, run) {
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
-
server.registerTool(name, config, async (args) => toolResult(await run(args)));
|
|
32
|
-
}
|
|
33
|
-
// --- Catalog tools (9) ---
|
|
34
|
-
register(catalogTools.importCatalog.name, { description: catalogTools.importCatalog.description, inputSchema: catalogTools.importCatalog.inputSchemaZod }, (args) => catalogTools.importCatalog.execute(args));
|
|
35
|
-
register(catalogTools.reviewImport.name, { description: catalogTools.reviewImport.description, inputSchema: catalogTools.reviewImport.inputSchemaZod }, (args) => catalogTools.reviewImport.execute(args));
|
|
36
|
-
register(catalogTools.commitImport.name, { description: catalogTools.commitImport.description, inputSchema: catalogTools.commitImport.inputSchemaZod }, (args) => catalogTools.commitImport.execute(args));
|
|
37
|
-
register(catalogTools.setupPrice.name, { description: catalogTools.setupPrice.description, inputSchema: catalogTools.setupPrice.inputSchemaZod }, (args) => catalogTools.setupPrice.execute(args));
|
|
38
|
-
register(catalogTools.updatePrice.name, { description: catalogTools.updatePrice.description, inputSchema: catalogTools.updatePrice.inputSchemaZod }, (args) => catalogTools.updatePrice.execute(args));
|
|
39
|
-
register(catalogTools.getCatalog.name, { description: catalogTools.getCatalog.description, inputSchema: catalogTools.getCatalog.inputSchemaZod }, (args) => catalogTools.getCatalog.execute(args));
|
|
40
|
-
register(catalogTools.getConfig.name, { description: catalogTools.getConfig.description, inputSchema: catalogTools.getConfig.inputSchemaZod }, (args) => catalogTools.getConfig.execute(args));
|
|
41
|
-
register(catalogTools.updateConfig.name, { description: catalogTools.updateConfig.description, inputSchema: catalogTools.updateConfig.inputSchemaZod }, (args) => catalogTools.updateConfig.execute(args));
|
|
42
|
-
register(catalogTools.importTemplate.name, { description: catalogTools.importTemplate.description, inputSchema: catalogTools.importTemplate.inputSchemaZod }, (args) => catalogTools.importTemplate.execute(args));
|
|
43
|
-
// --- Price catalog tools (4) ---
|
|
44
|
-
register(priceCatalogTools.listPriceCatalogs.name, { description: priceCatalogTools.listPriceCatalogs.description, inputSchema: priceCatalogTools.listPriceCatalogs.inputSchemaZod }, (args) => priceCatalogTools.listPriceCatalogs.execute(args));
|
|
45
|
-
register(priceCatalogTools.getPriceCatalog.name, { description: priceCatalogTools.getPriceCatalog.description, inputSchema: priceCatalogTools.getPriceCatalog.inputSchemaZod }, (args) => priceCatalogTools.getPriceCatalog.execute(args));
|
|
46
|
-
register(priceCatalogTools.resolvePrice.name, { description: priceCatalogTools.resolvePrice.description, inputSchema: priceCatalogTools.resolvePrice.inputSchemaZod }, (args) => priceCatalogTools.resolvePrice.execute(args));
|
|
47
|
-
register(priceCatalogTools.seedPriceCatalog.name, { description: priceCatalogTools.seedPriceCatalog.description, inputSchema: priceCatalogTools.seedPriceCatalog.inputSchemaZod }, (args) => priceCatalogTools.seedPriceCatalog.execute(args));
|
|
48
|
-
// --- Estimate tools (11) ---
|
|
49
|
-
register(estimateTools.createReplacement.name, { description: estimateTools.createReplacement.description, inputSchema: estimateTools.createReplacement.inputSchemaZod }, (args) => estimateTools.createReplacement.execute(args));
|
|
50
|
-
register(estimateTools.createRepair.name, { description: estimateTools.createRepair.description, inputSchema: estimateTools.createRepair.inputSchemaZod }, (args) => estimateTools.createRepair.execute(args));
|
|
51
|
-
register(estimateTools.getEstimate.name, { description: estimateTools.getEstimate.description, inputSchema: estimateTools.getEstimate.inputSchemaZod }, (args) => estimateTools.getEstimate.execute(args));
|
|
52
|
-
register(estimateTools.listEstimates.name, { description: estimateTools.listEstimates.description, inputSchema: estimateTools.listEstimates.inputSchemaZod }, (args) => estimateTools.listEstimates.execute(args));
|
|
53
|
-
register(estimateTools.addLineItem.name, { description: estimateTools.addLineItem.description, inputSchema: estimateTools.addLineItem.inputSchemaZod }, (args) => estimateTools.addLineItem.execute(args));
|
|
54
|
-
register(estimateTools.removeLineItem.name, { description: estimateTools.removeLineItem.description, inputSchema: estimateTools.removeLineItem.inputSchemaZod }, (args) => estimateTools.removeLineItem.execute(args));
|
|
55
|
-
register(estimateTools.updateLineItem.name, { description: estimateTools.updateLineItem.description, inputSchema: estimateTools.updateLineItem.inputSchemaZod }, (args) => estimateTools.updateLineItem.execute(args));
|
|
56
|
-
register(estimateTools.adjustToTarget.name, { description: estimateTools.adjustToTarget.description, inputSchema: estimateTools.adjustToTarget.inputSchemaZod }, (args) => estimateTools.adjustToTarget.execute(args));
|
|
57
|
-
register(estimateTools.updateEstimate.name, { description: estimateTools.updateEstimate.description, inputSchema: estimateTools.updateEstimate.inputSchemaZod }, (args) => estimateTools.updateEstimate.execute(args));
|
|
58
|
-
register(estimateTools.deleteEstimate.name, { description: estimateTools.deleteEstimate.description, inputSchema: estimateTools.deleteEstimate.inputSchemaZod }, (args) => estimateTools.deleteEstimate.execute(args));
|
|
59
|
-
register(estimateTools.duplicateEstimate.name, { description: estimateTools.duplicateEstimate.description, inputSchema: estimateTools.duplicateEstimate.inputSchemaZod }, (args) => estimateTools.duplicateEstimate.execute(args));
|
|
60
|
-
register(remodelEstimateTools.buildRemodelEstimate.name, { description: remodelEstimateTools.buildRemodelEstimate.description, inputSchema: remodelEstimateTools.buildRemodelEstimate.inputSchemaZod }, (args) => remodelEstimateTools.buildRemodelEstimate.execute(args));
|
|
61
|
-
register(remodelDeliverableTools.buildRemodelDeliverable.name, { description: remodelDeliverableTools.buildRemodelDeliverable.description, inputSchema: remodelDeliverableTools.buildRemodelDeliverable.inputSchemaZod }, (args) => remodelDeliverableTools.buildRemodelDeliverable.execute(args));
|
|
62
|
-
register(remodelDeliverableTools.draftRemodelDeliverable.name, { description: remodelDeliverableTools.draftRemodelDeliverable.description, inputSchema: remodelDeliverableTools.draftRemodelDeliverable.inputSchemaZod }, (args) => remodelDeliverableTools.draftRemodelDeliverable.execute(args));
|
|
63
|
-
// --- Template tools (4) ---
|
|
64
|
-
register(templateTools.saveTemplate.name, { description: templateTools.saveTemplate.description, inputSchema: templateTools.saveTemplate.inputSchemaZod }, (args) => templateTools.saveTemplate.execute(args));
|
|
65
|
-
register(templateTools.listTemplates.name, { description: templateTools.listTemplates.description, inputSchema: templateTools.listTemplates.inputSchemaZod }, (args) => templateTools.listTemplates.execute(args));
|
|
66
|
-
register(templateTools.deleteTemplate.name, { description: templateTools.deleteTemplate.description, inputSchema: templateTools.deleteTemplate.inputSchemaZod }, (args) => templateTools.deleteTemplate.execute(args));
|
|
67
|
-
register(templateTools.createFromTemplate.name, { description: templateTools.createFromTemplate.description, inputSchema: templateTools.createFromTemplate.inputSchemaZod }, (args) => templateTools.createFromTemplate.execute(args));
|
|
68
|
-
// --- Version tools (2) ---
|
|
69
|
-
register(versionTools.listVersions.name, { description: versionTools.listVersions.description, inputSchema: versionTools.listVersions.inputSchemaZod }, (args) => versionTools.listVersions.execute(args));
|
|
70
|
-
register(versionTools.getVersion.name, { description: versionTools.getVersion.description, inputSchema: versionTools.getVersion.inputSchemaZod }, (args) => versionTools.getVersion.execute(args));
|
|
71
|
-
// --- Photo tools (3) ---
|
|
72
|
-
register(photoTools.uploadPhoto.name, { description: photoTools.uploadPhoto.description, inputSchema: photoTools.uploadPhoto.inputSchemaZod }, (args) => photoTools.uploadPhoto.execute(args));
|
|
73
|
-
register(photoTools.listPhotos.name, { description: photoTools.listPhotos.description, inputSchema: photoTools.listPhotos.inputSchemaZod }, (args) => photoTools.listPhotos.execute(args));
|
|
74
|
-
register(photoTools.deletePhoto.name, { description: photoTools.deletePhoto.description, inputSchema: photoTools.deletePhoto.inputSchemaZod }, (args) => photoTools.deletePhoto.execute(args));
|
|
75
|
-
// --- Stats / PDF tools (3) ---
|
|
76
|
-
register(statsPdfTools.getStats.name, { description: statsPdfTools.getStats.description, inputSchema: statsPdfTools.getStats.inputSchemaZod }, (args) => statsPdfTools.getStats.execute(args));
|
|
77
|
-
register(statsPdfTools.exportPdf.name, { description: statsPdfTools.exportPdf.description, inputSchema: statsPdfTools.exportPdf.inputSchemaZod }, (args) => statsPdfTools.exportPdf.execute(args));
|
|
78
|
-
register(statsPdfTools.updateOutputConfig.name, { description: statsPdfTools.updateOutputConfig.description, inputSchema: statsPdfTools.updateOutputConfig.inputSchemaZod }, (args) => statsPdfTools.updateOutputConfig.execute(args));
|
|
79
|
-
register(emailPdfTools.emailPdf.name, { description: emailPdfTools.emailPdf.description, inputSchema: emailPdfTools.emailPdf.inputSchemaZod }, (args) => emailPdfTools.emailPdf.execute(args));
|
|
80
|
-
// --- Vision tools (2) ---
|
|
81
|
-
register(visionTools.analyzePhotos.name, { description: visionTools.analyzePhotos.description, inputSchema: visionTools.analyzePhotos.inputSchemaZod }, (args) => visionTools.analyzePhotos.execute(args));
|
|
82
|
-
register(visionTools.analyzeDocument.name, { description: visionTools.analyzeDocument.description, inputSchema: visionTools.analyzeDocument.inputSchemaZod }, (args) => visionTools.analyzeDocument.execute(args));
|
|
83
|
-
// --- Company tools (5) ---
|
|
84
|
-
register(companyTools.createCompany.name, { description: companyTools.createCompany.description, inputSchema: companyTools.createCompany.inputSchemaZod }, (args) => companyTools.createCompany.execute(args));
|
|
85
|
-
register(companyTools.listCompanies.name, { description: companyTools.listCompanies.description, inputSchema: companyTools.listCompanies.inputSchemaZod }, (args) => companyTools.listCompanies.execute(args));
|
|
86
|
-
register(companyTools.getCompany.name, { description: companyTools.getCompany.description, inputSchema: companyTools.getCompany.inputSchemaZod }, (args) => companyTools.getCompany.execute(args));
|
|
87
|
-
register(companyTools.updateCompany.name, { description: companyTools.updateCompany.description, inputSchema: companyTools.updateCompany.inputSchemaZod }, (args) => companyTools.updateCompany.execute(args));
|
|
88
|
-
register(companyTools.deleteCompany.name, { description: companyTools.deleteCompany.description, inputSchema: companyTools.deleteCompany.inputSchemaZod }, (args) => companyTools.deleteCompany.execute(args));
|
|
89
|
-
// --- Client tools (5) ---
|
|
90
|
-
register(clientTools.createClient.name, { description: clientTools.createClient.description, inputSchema: clientTools.createClient.inputSchemaZod }, (args) => clientTools.createClient.execute(args));
|
|
91
|
-
register(clientTools.listClients.name, { description: clientTools.listClients.description, inputSchema: clientTools.listClients.inputSchemaZod }, (args) => clientTools.listClients.execute(args));
|
|
92
|
-
register(clientTools.getClient.name, { description: clientTools.getClient.description, inputSchema: clientTools.getClient.inputSchemaZod }, (args) => clientTools.getClient.execute(args));
|
|
93
|
-
register(clientTools.updateClient.name, { description: clientTools.updateClient.description, inputSchema: clientTools.updateClient.inputSchemaZod }, (args) => clientTools.updateClient.execute(args));
|
|
94
|
-
register(clientTools.deleteClient.name, { description: clientTools.deleteClient.description, inputSchema: clientTools.deleteClient.inputSchemaZod }, (args) => clientTools.deleteClient.execute(args));
|
|
95
|
-
// --- Takeoff / approval tools (16) ---
|
|
96
|
-
register(takeoffTools.createTakeoffProject.name, { description: takeoffTools.createTakeoffProject.description, inputSchema: takeoffTools.createTakeoffProject.inputSchemaZod }, (args) => takeoffTools.createTakeoffProject.execute(args));
|
|
97
|
-
register(takeoffTools.getTakeoffProject.name, { description: takeoffTools.getTakeoffProject.description, inputSchema: takeoffTools.getTakeoffProject.inputSchemaZod }, (args) => takeoffTools.getTakeoffProject.execute(args));
|
|
98
|
-
register(takeoffTools.listTakeoffProjects.name, { description: takeoffTools.listTakeoffProjects.description, inputSchema: takeoffTools.listTakeoffProjects.inputSchemaZod }, (args) => takeoffTools.listTakeoffProjects.execute(args));
|
|
99
|
-
register(takeoffTools.uploadTakeoffInput.name, { description: takeoffTools.uploadTakeoffInput.description, inputSchema: takeoffTools.uploadTakeoffInput.inputSchemaZod }, (args) => takeoffTools.uploadTakeoffInput.execute(args));
|
|
100
|
-
register(takeoffTools.runTakeoffExtract.name, { description: takeoffTools.runTakeoffExtract.description, inputSchema: takeoffTools.runTakeoffExtract.inputSchemaZod }, (args) => takeoffTools.runTakeoffExtract.execute(args));
|
|
101
|
-
register(takeoffTools.listTakeoffRows.name, { description: takeoffTools.listTakeoffRows.description, inputSchema: takeoffTools.listTakeoffRows.inputSchemaZod }, (args) => takeoffTools.listTakeoffRows.execute(args));
|
|
102
|
-
register(takeoffTools.reviewTakeoffRow.name, { description: takeoffTools.reviewTakeoffRow.description, inputSchema: takeoffTools.reviewTakeoffRow.inputSchemaZod }, (args) => takeoffTools.reviewTakeoffRow.execute(args));
|
|
103
|
-
register(takeoffTools.commitTakeoff.name, { description: takeoffTools.commitTakeoff.description, inputSchema: takeoffTools.commitTakeoff.inputSchemaZod }, (args) => takeoffTools.commitTakeoff.execute(args));
|
|
104
|
-
register(takeoffTools.listSystemPacks.name, { description: takeoffTools.listSystemPacks.description, inputSchema: takeoffTools.listSystemPacks.inputSchemaZod }, (args) => takeoffTools.listSystemPacks.execute(args));
|
|
105
|
-
register(takeoffTools.applySystemPack.name, { description: takeoffTools.applySystemPack.description, inputSchema: takeoffTools.applySystemPack.inputSchemaZod }, (args) => takeoffTools.applySystemPack.execute(args));
|
|
106
|
-
register(takeoffTools.listServiceProfiles.name, { description: takeoffTools.listServiceProfiles.description, inputSchema: takeoffTools.listServiceProfiles.inputSchemaZod }, (args) => takeoffTools.listServiceProfiles.execute(args));
|
|
107
|
-
register(takeoffTools.upsertServiceProfile.name, { description: takeoffTools.upsertServiceProfile.description, inputSchema: takeoffTools.upsertServiceProfile.inputSchemaZod }, (args) => takeoffTools.upsertServiceProfile.execute(args));
|
|
108
|
-
register(takeoffTools.generateEstimateFromTakeoff.name, { description: takeoffTools.generateEstimateFromTakeoff.description, inputSchema: takeoffTools.generateEstimateFromTakeoff.inputSchemaZod }, (args) => takeoffTools.generateEstimateFromTakeoff.execute(args));
|
|
109
|
-
register(takeoffTools.requestEstimateApproval.name, { description: takeoffTools.requestEstimateApproval.description, inputSchema: takeoffTools.requestEstimateApproval.inputSchemaZod }, (args) => takeoffTools.requestEstimateApproval.execute(args));
|
|
110
|
-
register(takeoffTools.approveEstimate.name, { description: takeoffTools.approveEstimate.description, inputSchema: takeoffTools.approveEstimate.inputSchemaZod }, (args) => takeoffTools.approveEstimate.execute(args));
|
|
111
|
-
register(takeoffTools.getEstimateApproval.name, { description: takeoffTools.getEstimateApproval.description, inputSchema: takeoffTools.getEstimateApproval.inputSchemaZod }, (args) => takeoffTools.getEstimateApproval.execute(args));
|
|
112
|
-
// --- Intake tools (1) ---
|
|
113
|
-
register(intakeTools.normalizeRemodelIntake.name, { description: intakeTools.normalizeRemodelIntake.description, inputSchema: intakeTools.normalizeRemodelIntake.inputSchemaZod }, (args) => intakeTools.normalizeRemodelIntake.execute(args));
|
|
114
|
-
// --- Signing tools (3) ---
|
|
115
|
-
register(signingTools.createSigningRequest.name, { description: signingTools.createSigningRequest.description, inputSchema: signingTools.createSigningRequest.inputSchemaZod }, (args) => signingTools.createSigningRequest.execute(args));
|
|
116
|
-
register(signingTools.getSigningRequests.name, { description: signingTools.getSigningRequests.description, inputSchema: signingTools.getSigningRequests.inputSchemaZod }, (args) => signingTools.getSigningRequests.execute(args));
|
|
117
|
-
register(signingTools.cancelSigningRequest.name, { description: signingTools.cancelSigningRequest.description, inputSchema: signingTools.cancelSigningRequest.inputSchemaZod }, (args) => signingTools.cancelSigningRequest.execute(args));
|
|
118
|
-
return server;
|
|
119
|
-
}
|
package/dist/sse.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|