@curenorway/kode-mcp 1.7.0 → 2.0.0
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 +880 -76
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -10,6 +10,387 @@ import {
|
|
|
10
10
|
ReadResourceRequestSchema
|
|
11
11
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
12
12
|
|
|
13
|
+
// src/file-utils.ts
|
|
14
|
+
import * as fs from "fs";
|
|
15
|
+
import * as path from "path";
|
|
16
|
+
var SUPPORTED_EXTENSIONS = [".js", ".css", ".ts", ".tsx", ".jsx"];
|
|
17
|
+
var BUNDLEABLE_EXTENSIONS = [".ts", ".tsx", ".jsx"];
|
|
18
|
+
var SKIP_DIRS = ["packages", "node_modules", ".git", "dist", "build"];
|
|
19
|
+
function isSupportedFile(fileName) {
|
|
20
|
+
return SUPPORTED_EXTENSIONS.some((ext) => fileName.endsWith(ext));
|
|
21
|
+
}
|
|
22
|
+
function needsBundling(fileName) {
|
|
23
|
+
return BUNDLEABLE_EXTENSIONS.some((ext) => fileName.endsWith(ext));
|
|
24
|
+
}
|
|
25
|
+
function slugFromFileName(fileName) {
|
|
26
|
+
return fileName.replace(/\.(js|ts|tsx|jsx|css)$/, "");
|
|
27
|
+
}
|
|
28
|
+
function extFromFileName(fileName) {
|
|
29
|
+
const ext = path.extname(fileName);
|
|
30
|
+
return ext.startsWith(".") ? ext.slice(1) : ext;
|
|
31
|
+
}
|
|
32
|
+
function mimeTypeFromFileName(fileName) {
|
|
33
|
+
if (fileName.endsWith(".css")) return "text/css";
|
|
34
|
+
if (fileName.endsWith(".ts")) return "application/typescript";
|
|
35
|
+
if (fileName.endsWith(".tsx")) return "text/tsx";
|
|
36
|
+
if (fileName.endsWith(".jsx")) return "text/jsx";
|
|
37
|
+
return "application/javascript";
|
|
38
|
+
}
|
|
39
|
+
function buildFileInfo(filePath, scriptsDir, isModule) {
|
|
40
|
+
const fileName = path.basename(filePath);
|
|
41
|
+
return {
|
|
42
|
+
fileName,
|
|
43
|
+
slug: slugFromFileName(fileName),
|
|
44
|
+
ext: extFromFileName(fileName),
|
|
45
|
+
filePath,
|
|
46
|
+
needsBundling: needsBundling(fileName),
|
|
47
|
+
isModule,
|
|
48
|
+
relativePath: path.relative(scriptsDir, filePath)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function getEntryFiles(scriptsDir) {
|
|
52
|
+
if (!fs.existsSync(scriptsDir)) return [];
|
|
53
|
+
return fs.readdirSync(scriptsDir).filter((f) => {
|
|
54
|
+
const fullPath = path.join(scriptsDir, f);
|
|
55
|
+
return fs.statSync(fullPath).isFile() && isSupportedFile(f);
|
|
56
|
+
}).map((f) => buildFileInfo(path.join(scriptsDir, f), scriptsDir, false));
|
|
57
|
+
}
|
|
58
|
+
function getModuleFiles(scriptsDir) {
|
|
59
|
+
if (!fs.existsSync(scriptsDir)) return [];
|
|
60
|
+
const modules = [];
|
|
61
|
+
function scanDir(dir) {
|
|
62
|
+
const entries2 = fs.readdirSync(dir, { withFileTypes: true });
|
|
63
|
+
for (const entry of entries2) {
|
|
64
|
+
const fullPath = path.join(dir, entry.name);
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
if (!SKIP_DIRS.includes(entry.name)) {
|
|
67
|
+
scanDir(fullPath);
|
|
68
|
+
}
|
|
69
|
+
} else if (entry.isFile() && isSupportedFile(entry.name)) {
|
|
70
|
+
modules.push(buildFileInfo(fullPath, scriptsDir, true));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const entries = fs.readdirSync(scriptsDir, { withFileTypes: true });
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
if (entry.isDirectory() && !SKIP_DIRS.includes(entry.name)) {
|
|
77
|
+
scanDir(path.join(scriptsDir, entry.name));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return modules;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/bundler.ts
|
|
84
|
+
import * as esbuild from "esbuild";
|
|
85
|
+
import * as fs2 from "fs";
|
|
86
|
+
import * as path2 from "path";
|
|
87
|
+
async function bundleEntryPoint(options) {
|
|
88
|
+
const {
|
|
89
|
+
entryPoint,
|
|
90
|
+
scriptsDir,
|
|
91
|
+
minify = true,
|
|
92
|
+
packageAliases = {}
|
|
93
|
+
} = options;
|
|
94
|
+
try {
|
|
95
|
+
const result = await esbuild.build({
|
|
96
|
+
entryPoints: [entryPoint],
|
|
97
|
+
bundle: true,
|
|
98
|
+
format: "iife",
|
|
99
|
+
platform: "browser",
|
|
100
|
+
target: ["es2020"],
|
|
101
|
+
minify,
|
|
102
|
+
write: false,
|
|
103
|
+
// React is loaded from CDN at runtime, not bundled
|
|
104
|
+
external: ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
|
|
105
|
+
// Modern JSX transform — no need for React import in every file
|
|
106
|
+
jsx: "automatic",
|
|
107
|
+
jsxImportSource: "react",
|
|
108
|
+
// Resolve imports relative to scripts directory
|
|
109
|
+
absWorkingDir: scriptsDir,
|
|
110
|
+
// File type loaders
|
|
111
|
+
loader: {
|
|
112
|
+
".ts": "ts",
|
|
113
|
+
".tsx": "tsx",
|
|
114
|
+
".jsx": "jsx",
|
|
115
|
+
".js": "js",
|
|
116
|
+
".css": "css"
|
|
117
|
+
},
|
|
118
|
+
// Try these extensions when resolving imports without extension
|
|
119
|
+
resolveExtensions: [".tsx", ".ts", ".jsx", ".js", ".css"],
|
|
120
|
+
// Package aliases for @kode/* imports (populated in Step 5)
|
|
121
|
+
alias: packageAliases,
|
|
122
|
+
// Shim require() for external React in browser IIFE context
|
|
123
|
+
// Maps require("react") → window.React, require("react-dom") → window.ReactDOM, etc.
|
|
124
|
+
banner: {
|
|
125
|
+
js: `var require=function(m){var g={"react":window.React,"react-dom":window.ReactDOM,"react-dom/client":window.ReactDOM,"react/jsx-runtime":window.React,"react/jsx-dev-runtime":window.React};if(g[m])return g[m];throw new Error("Cannot find module '"+m+"'")};`
|
|
126
|
+
},
|
|
127
|
+
// Readable output names for debugging
|
|
128
|
+
logLevel: "silent"
|
|
129
|
+
});
|
|
130
|
+
const code = result.outputFiles?.[0]?.text || "";
|
|
131
|
+
const warnings = result.warnings.map((w) => formatMessage(w));
|
|
132
|
+
const sourceCode = fs2.readFileSync(entryPoint, "utf-8");
|
|
133
|
+
return {
|
|
134
|
+
code,
|
|
135
|
+
warnings,
|
|
136
|
+
errors: [],
|
|
137
|
+
usesReact: detectReactUsage(sourceCode),
|
|
138
|
+
size: code.length
|
|
139
|
+
};
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err && typeof err === "object" && "errors" in err) {
|
|
142
|
+
const buildErr = err;
|
|
143
|
+
return {
|
|
144
|
+
code: "",
|
|
145
|
+
warnings: buildErr.warnings.map((w) => formatMessage(w)),
|
|
146
|
+
errors: buildErr.errors.map((e) => formatMessage(e)),
|
|
147
|
+
usesReact: false,
|
|
148
|
+
size: 0
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
code: "",
|
|
153
|
+
warnings: [],
|
|
154
|
+
errors: [err instanceof Error ? err.message : String(err)],
|
|
155
|
+
usesReact: false,
|
|
156
|
+
size: 0
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function detectReactUsage(sourceCode) {
|
|
161
|
+
return /from\s+['"]react['"]/.test(sourceCode) || /from\s+['"]react-dom/.test(sourceCode) || /import\s+React/.test(sourceCode) || /require\s*\(\s*['"]react/.test(sourceCode) || /React\.createElement/.test(sourceCode);
|
|
162
|
+
}
|
|
163
|
+
function formatMessage(msg) {
|
|
164
|
+
let text = msg.text;
|
|
165
|
+
if (msg.location) {
|
|
166
|
+
const loc = msg.location;
|
|
167
|
+
text = `${loc.file}:${loc.line}:${loc.column}: ${text}`;
|
|
168
|
+
}
|
|
169
|
+
return text;
|
|
170
|
+
}
|
|
171
|
+
function getPackageAliases(scriptsDir) {
|
|
172
|
+
const pkgDir = path2.join(scriptsDir, "packages", "@kode");
|
|
173
|
+
if (!fs2.existsSync(pkgDir)) return {};
|
|
174
|
+
const aliases = {};
|
|
175
|
+
const ENTRY_FILES = ["index.ts", "index.tsx", "index.jsx", "index.js"];
|
|
176
|
+
for (const name of fs2.readdirSync(pkgDir)) {
|
|
177
|
+
const pkgPath = path2.join(pkgDir, name);
|
|
178
|
+
if (!fs2.statSync(pkgPath).isDirectory()) continue;
|
|
179
|
+
for (const entry of ENTRY_FILES) {
|
|
180
|
+
const entryPath = path2.join(pkgPath, entry);
|
|
181
|
+
if (fs2.existsSync(entryPath)) {
|
|
182
|
+
aliases[`@kode/${name}`] = entryPath;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return aliases;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/pkg.ts
|
|
191
|
+
import * as fs3 from "fs";
|
|
192
|
+
import * as path3 from "path";
|
|
193
|
+
var PACKAGES_DIR = "packages/@kode";
|
|
194
|
+
function getPackagesDir(scriptsDir) {
|
|
195
|
+
return path3.join(scriptsDir, PACKAGES_DIR);
|
|
196
|
+
}
|
|
197
|
+
function listInstalledPackages(scriptsDir) {
|
|
198
|
+
const pkgDir = getPackagesDir(scriptsDir);
|
|
199
|
+
if (!fs3.existsSync(pkgDir)) return [];
|
|
200
|
+
return fs3.readdirSync(pkgDir).filter((name) => {
|
|
201
|
+
return fs3.statSync(path3.join(pkgDir, name)).isDirectory();
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function isPackageInstalled(scriptsDir, slug) {
|
|
205
|
+
return fs3.existsSync(path3.join(getPackagesDir(scriptsDir), slug));
|
|
206
|
+
}
|
|
207
|
+
async function pkgAdd(client, scriptsDir, packageSlug) {
|
|
208
|
+
let pkg;
|
|
209
|
+
try {
|
|
210
|
+
pkg = await client.getPackage(packageSlug);
|
|
211
|
+
} catch {
|
|
212
|
+
return { success: false, message: `Package "${packageSlug}" not found in registry.` };
|
|
213
|
+
}
|
|
214
|
+
if (!pkg.files || pkg.files.length === 0) {
|
|
215
|
+
return { success: false, message: `Package "${packageSlug}" has no files.` };
|
|
216
|
+
}
|
|
217
|
+
const pkgDir = path3.join(getPackagesDir(scriptsDir), pkg.slug);
|
|
218
|
+
fs3.mkdirSync(pkgDir, { recursive: true });
|
|
219
|
+
for (const file of pkg.files) {
|
|
220
|
+
const filePath = path3.join(pkgDir, file.path);
|
|
221
|
+
fs3.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
222
|
+
fs3.writeFileSync(filePath, file.content, "utf-8");
|
|
223
|
+
}
|
|
224
|
+
const manifest = {
|
|
225
|
+
name: pkg.name,
|
|
226
|
+
slug: pkg.slug,
|
|
227
|
+
version: pkg.version,
|
|
228
|
+
description: pkg.description,
|
|
229
|
+
entryPoint: pkg.entry_point,
|
|
230
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
231
|
+
};
|
|
232
|
+
fs3.writeFileSync(path3.join(pkgDir, "kode.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
233
|
+
const already = isPackageInstalled(scriptsDir, pkg.slug) ? " (updated)" : "";
|
|
234
|
+
return {
|
|
235
|
+
success: true,
|
|
236
|
+
message: `\u2705 Installed @kode/${pkg.slug} v${pkg.version}${already}
|
|
237
|
+
${pkg.files.length} file(s) \u2192 .cure-kode-scripts/packages/@kode/${pkg.slug}/
|
|
238
|
+
Import with: import { ... } from '@kode/${pkg.slug}'`,
|
|
239
|
+
data: manifest
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
async function pkgRemove(scriptsDir, packageSlug) {
|
|
243
|
+
const pkgDir = path3.join(getPackagesDir(scriptsDir), packageSlug);
|
|
244
|
+
if (!fs3.existsSync(pkgDir)) {
|
|
245
|
+
return { success: false, message: `Package "@kode/${packageSlug}" is not installed.` };
|
|
246
|
+
}
|
|
247
|
+
fs3.rmSync(pkgDir, { recursive: true, force: true });
|
|
248
|
+
return {
|
|
249
|
+
success: true,
|
|
250
|
+
message: `\u2705 Removed @kode/${packageSlug}`
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
async function pkgPublish(client, scriptsDir, options) {
|
|
254
|
+
const sourcePath = path3.join(scriptsDir, options.path);
|
|
255
|
+
if (!fs3.existsSync(sourcePath)) {
|
|
256
|
+
return { success: false, message: `Path not found: ${options.path}` };
|
|
257
|
+
}
|
|
258
|
+
const files = [];
|
|
259
|
+
const stat = fs3.statSync(sourcePath);
|
|
260
|
+
if (stat.isFile()) {
|
|
261
|
+
const ext = path3.extname(sourcePath);
|
|
262
|
+
const content = fs3.readFileSync(sourcePath, "utf-8");
|
|
263
|
+
files.push({ path: `index${ext}`, content });
|
|
264
|
+
} else if (stat.isDirectory()) {
|
|
265
|
+
collectFiles(sourcePath, sourcePath, files);
|
|
266
|
+
}
|
|
267
|
+
if (files.length === 0) {
|
|
268
|
+
return { success: false, message: `No files found at "${options.path}".` };
|
|
269
|
+
}
|
|
270
|
+
let entryPoint = options.entryPoint;
|
|
271
|
+
if (!entryPoint) {
|
|
272
|
+
const entryNames = ["index.ts", "index.tsx", "index.jsx", "index.js"];
|
|
273
|
+
const found = files.find((f) => entryNames.includes(f.path));
|
|
274
|
+
if (found) {
|
|
275
|
+
entryPoint = found.path;
|
|
276
|
+
} else {
|
|
277
|
+
entryPoint = files[0].path;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const displayName = options.name || options.slug.split("-").map(
|
|
281
|
+
(w) => w.charAt(0).toUpperCase() + w.slice(1)
|
|
282
|
+
).join(" ");
|
|
283
|
+
try {
|
|
284
|
+
const result = await client.publishPackage({
|
|
285
|
+
name: displayName,
|
|
286
|
+
slug: options.slug,
|
|
287
|
+
description: options.description,
|
|
288
|
+
entryPoint,
|
|
289
|
+
files,
|
|
290
|
+
tags: options.tags
|
|
291
|
+
});
|
|
292
|
+
const verb = result.created ? "Published" : "Updated";
|
|
293
|
+
return {
|
|
294
|
+
success: true,
|
|
295
|
+
message: `\u2705 ${verb} @kode/${result.slug} v${result.version}
|
|
296
|
+
${files.length} file(s), entry: ${entryPoint}
|
|
297
|
+
Install with: kode_pkg add ${result.slug}`,
|
|
298
|
+
data: { slug: result.slug, version: result.version, files: files.length }
|
|
299
|
+
};
|
|
300
|
+
} catch (err) {
|
|
301
|
+
return {
|
|
302
|
+
success: false,
|
|
303
|
+
message: `Failed to publish: ${err instanceof Error ? err.message : String(err)}`
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function pkgList(client, scriptsDir, options) {
|
|
308
|
+
const queryParts = [];
|
|
309
|
+
if (options?.search) queryParts.push(`search=${encodeURIComponent(options.search)}`);
|
|
310
|
+
if (options?.category) queryParts.push(`category=${encodeURIComponent(options.category)}`);
|
|
311
|
+
const qs = queryParts.length > 0 ? queryParts.join("&") : void 0;
|
|
312
|
+
const packages = await client.listPackages(qs);
|
|
313
|
+
const installed = scriptsDir ? listInstalledPackages(scriptsDir) : [];
|
|
314
|
+
if (packages.length === 0) {
|
|
315
|
+
return { success: true, message: "No packages found in the registry." };
|
|
316
|
+
}
|
|
317
|
+
const lines = packages.map((p) => {
|
|
318
|
+
const isInstalled = installed.includes(p.slug);
|
|
319
|
+
const status = isInstalled ? " \u2705" : "";
|
|
320
|
+
const tags = p.tags?.length ? ` [${p.tags.join(", ")}]` : "";
|
|
321
|
+
return ` ${p.slug} v${p.version}${status} \u2014 ${p.description || "No description"}${tags}`;
|
|
322
|
+
});
|
|
323
|
+
return {
|
|
324
|
+
success: true,
|
|
325
|
+
message: `\u{1F4E6} Packages in registry (${packages.length}):
|
|
326
|
+
${lines.join("\n")}`,
|
|
327
|
+
data: packages
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
async function pkgInfo(client, scriptsDir, packageSlug) {
|
|
331
|
+
let pkg;
|
|
332
|
+
try {
|
|
333
|
+
pkg = await client.getPackage(packageSlug);
|
|
334
|
+
} catch {
|
|
335
|
+
return { success: false, message: `Package "${packageSlug}" not found.` };
|
|
336
|
+
}
|
|
337
|
+
const installed = scriptsDir ? isPackageInstalled(scriptsDir, pkg.slug) : false;
|
|
338
|
+
const fileList = pkg.files?.map((f) => ` ${f.path}`).join("\n") || " (no files)";
|
|
339
|
+
const info = [
|
|
340
|
+
`\u{1F4E6} @kode/${pkg.slug} v${pkg.version}`,
|
|
341
|
+
` Name: ${pkg.name}`,
|
|
342
|
+
pkg.description ? ` Description: ${pkg.description}` : null,
|
|
343
|
+
` Entry: ${pkg.entry_point}`,
|
|
344
|
+
pkg.tags?.length ? ` Tags: ${pkg.tags.join(", ")}` : null,
|
|
345
|
+
` Installs: ${pkg.install_count}`,
|
|
346
|
+
` Status: ${installed ? "\u2705 Installed locally" : "Not installed"}`,
|
|
347
|
+
` Files:
|
|
348
|
+
${fileList}`,
|
|
349
|
+
pkg.example_usage ? `
|
|
350
|
+
Example:
|
|
351
|
+
${pkg.example_usage}` : null
|
|
352
|
+
].filter(Boolean).join("\n");
|
|
353
|
+
return { success: true, message: info, data: pkg };
|
|
354
|
+
}
|
|
355
|
+
async function pkgUpdate(client, scriptsDir, packageSlug) {
|
|
356
|
+
if (!isPackageInstalled(scriptsDir, packageSlug)) {
|
|
357
|
+
return { success: false, message: `Package "@kode/${packageSlug}" is not installed.` };
|
|
358
|
+
}
|
|
359
|
+
const manifestPath = path3.join(getPackagesDir(scriptsDir), packageSlug, "kode.json");
|
|
360
|
+
let currentVersion = 0;
|
|
361
|
+
if (fs3.existsSync(manifestPath)) {
|
|
362
|
+
try {
|
|
363
|
+
const manifest = JSON.parse(fs3.readFileSync(manifestPath, "utf-8"));
|
|
364
|
+
currentVersion = manifest.version || 0;
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
let pkg;
|
|
369
|
+
try {
|
|
370
|
+
pkg = await client.getPackage(packageSlug);
|
|
371
|
+
} catch {
|
|
372
|
+
return { success: false, message: `Package "${packageSlug}" not found in registry.` };
|
|
373
|
+
}
|
|
374
|
+
if (pkg.version <= currentVersion) {
|
|
375
|
+
return { success: true, message: `@kode/${packageSlug} is already up to date (v${currentVersion}).` };
|
|
376
|
+
}
|
|
377
|
+
return pkgAdd(client, scriptsDir, packageSlug);
|
|
378
|
+
}
|
|
379
|
+
function collectFiles(baseDir, currentDir, files) {
|
|
380
|
+
for (const entry of fs3.readdirSync(currentDir, { withFileTypes: true })) {
|
|
381
|
+
const fullPath = path3.join(currentDir, entry.name);
|
|
382
|
+
if (entry.isDirectory()) {
|
|
383
|
+
if (!["node_modules", ".git", "dist"].includes(entry.name)) {
|
|
384
|
+
collectFiles(baseDir, fullPath, files);
|
|
385
|
+
}
|
|
386
|
+
} else if (entry.isFile()) {
|
|
387
|
+
const relativePath = path3.relative(baseDir, fullPath);
|
|
388
|
+
const content = fs3.readFileSync(fullPath, "utf-8");
|
|
389
|
+
files.push({ path: relativePath, content });
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
13
394
|
// src/api.ts
|
|
14
395
|
var KodeApiClient = class {
|
|
15
396
|
apiUrl;
|
|
@@ -18,8 +399,8 @@ var KodeApiClient = class {
|
|
|
18
399
|
this.apiUrl = apiUrl.replace(/\/$/, "");
|
|
19
400
|
this.apiKey = apiKey;
|
|
20
401
|
}
|
|
21
|
-
async request(
|
|
22
|
-
const url = `${this.apiUrl}${
|
|
402
|
+
async request(path6, options = {}) {
|
|
403
|
+
const url = `${this.apiUrl}${path6}`;
|
|
23
404
|
const headers = {
|
|
24
405
|
"Content-Type": "application/json",
|
|
25
406
|
"X-API-Key": this.apiKey,
|
|
@@ -154,6 +535,33 @@ var KodeApiClient = class {
|
|
|
154
535
|
body: JSON.stringify({ siteId, url })
|
|
155
536
|
});
|
|
156
537
|
}
|
|
538
|
+
// Library operations
|
|
539
|
+
async listLibrary(queryString) {
|
|
540
|
+
return this.request(`/api/cdn/library${queryString || ""}`);
|
|
541
|
+
}
|
|
542
|
+
async getLibrarySnippet(slugOrId) {
|
|
543
|
+
return this.request(`/api/cdn/library/${encodeURIComponent(slugOrId)}`);
|
|
544
|
+
}
|
|
545
|
+
async createLibrarySnippet(data) {
|
|
546
|
+
return this.request("/api/cdn/library", {
|
|
547
|
+
method: "POST",
|
|
548
|
+
body: JSON.stringify(data)
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
async updateLibrarySnippet(slugOrId, data) {
|
|
552
|
+
return this.request(`/api/cdn/library/${encodeURIComponent(slugOrId)}`, {
|
|
553
|
+
method: "PATCH",
|
|
554
|
+
body: JSON.stringify(data)
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
async deleteLibrarySnippet(slugOrId) {
|
|
558
|
+
await this.request(`/api/cdn/library/${encodeURIComponent(slugOrId)}`, {
|
|
559
|
+
method: "DELETE"
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
async listLibraryFolders() {
|
|
563
|
+
return this.request("/api/cdn/library/folders");
|
|
564
|
+
}
|
|
157
565
|
// Validate API key
|
|
158
566
|
async validateKey() {
|
|
159
567
|
try {
|
|
@@ -173,32 +581,60 @@ var KodeApiClient = class {
|
|
|
173
581
|
async getScriptMetadata(scriptId) {
|
|
174
582
|
return this.request(`/api/cdn/scripts/${scriptId}/metadata`);
|
|
175
583
|
}
|
|
584
|
+
// ==================== Packages ====================
|
|
585
|
+
async listPackages(queryString) {
|
|
586
|
+
const qs = queryString ? `?${queryString}` : "";
|
|
587
|
+
return this.request(`/api/cdn/packages${qs}`);
|
|
588
|
+
}
|
|
589
|
+
async getPackage(slugOrId) {
|
|
590
|
+
return this.request(`/api/cdn/packages/${slugOrId}`);
|
|
591
|
+
}
|
|
592
|
+
async publishPackage(data) {
|
|
593
|
+
return this.request("/api/cdn/packages", {
|
|
594
|
+
method: "POST",
|
|
595
|
+
body: JSON.stringify(data)
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
async updatePackage(slugOrId, data) {
|
|
599
|
+
return this.request(`/api/cdn/packages/${slugOrId}`, {
|
|
600
|
+
method: "PATCH",
|
|
601
|
+
body: JSON.stringify(data)
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
async deletePackage(slugOrId) {
|
|
605
|
+
return this.request(`/api/cdn/packages/${slugOrId}`, {
|
|
606
|
+
method: "DELETE"
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
async getCmsTypes(siteId) {
|
|
610
|
+
return this.request(`/api/cdn/sites/${siteId}/cms-types`);
|
|
611
|
+
}
|
|
176
612
|
};
|
|
177
613
|
|
|
178
614
|
// src/config.ts
|
|
179
|
-
import * as
|
|
180
|
-
import * as
|
|
615
|
+
import * as fs4 from "fs";
|
|
616
|
+
import * as path4 from "path";
|
|
181
617
|
var DEFAULT_API_URL = "https://app.cure.no";
|
|
182
618
|
var CONFIG_DIR = ".cure-kode";
|
|
183
619
|
var CONFIG_FILE = "config.json";
|
|
184
620
|
function findProjectRoot(startDir = process.cwd()) {
|
|
185
621
|
let currentDir = startDir;
|
|
186
|
-
while (currentDir !==
|
|
187
|
-
const configPath =
|
|
188
|
-
if (
|
|
622
|
+
while (currentDir !== path4.dirname(currentDir)) {
|
|
623
|
+
const configPath = path4.join(currentDir, CONFIG_DIR, CONFIG_FILE);
|
|
624
|
+
if (fs4.existsSync(configPath)) {
|
|
189
625
|
return currentDir;
|
|
190
626
|
}
|
|
191
|
-
currentDir =
|
|
627
|
+
currentDir = path4.dirname(currentDir);
|
|
192
628
|
}
|
|
193
629
|
return void 0;
|
|
194
630
|
}
|
|
195
631
|
function loadProjectConfig() {
|
|
196
632
|
const projectRoot = findProjectRoot();
|
|
197
633
|
if (!projectRoot) return void 0;
|
|
198
|
-
const configPath =
|
|
199
|
-
if (!
|
|
634
|
+
const configPath = path4.join(projectRoot, CONFIG_DIR, CONFIG_FILE);
|
|
635
|
+
if (!fs4.existsSync(configPath)) return void 0;
|
|
200
636
|
try {
|
|
201
|
-
const content =
|
|
637
|
+
const content = fs4.readFileSync(configPath, "utf-8");
|
|
202
638
|
return JSON.parse(content);
|
|
203
639
|
} catch {
|
|
204
640
|
return void 0;
|
|
@@ -235,22 +671,22 @@ function getScriptsDir() {
|
|
|
235
671
|
const projectRoot = findProjectRoot();
|
|
236
672
|
const projectConfig = loadProjectConfig();
|
|
237
673
|
if (!projectRoot || !projectConfig) return void 0;
|
|
238
|
-
return
|
|
674
|
+
return path4.join(projectRoot, projectConfig.scriptsDir);
|
|
239
675
|
}
|
|
240
676
|
function getContextPath() {
|
|
241
677
|
const projectRoot = findProjectRoot();
|
|
242
678
|
if (!projectRoot) return void 0;
|
|
243
|
-
return
|
|
679
|
+
return path4.join(projectRoot, CONFIG_DIR, "context.md");
|
|
244
680
|
}
|
|
245
681
|
function getPagesDir() {
|
|
246
682
|
const projectRoot = findProjectRoot();
|
|
247
683
|
if (!projectRoot) return void 0;
|
|
248
|
-
return
|
|
684
|
+
return path4.join(projectRoot, CONFIG_DIR, "pages");
|
|
249
685
|
}
|
|
250
686
|
|
|
251
687
|
// src/index.ts
|
|
252
|
-
import * as
|
|
253
|
-
import * as
|
|
688
|
+
import * as fs5 from "fs";
|
|
689
|
+
import * as path5 from "path";
|
|
254
690
|
function urlToSlug(url) {
|
|
255
691
|
try {
|
|
256
692
|
const parsed = new URL(url);
|
|
@@ -329,7 +765,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
329
765
|
},
|
|
330
766
|
{
|
|
331
767
|
name: "kode_create_script",
|
|
332
|
-
description: "Create a new script entry (metadata only). After creating, write the script file locally to .cure-kode-scripts/{name}.
|
|
768
|
+
description: "Create a new script entry (metadata only). After creating, write the script file locally to .cure-kode-scripts/{name}.{ext} and use kode_push to upload content. Supports JS, TS, TSX, JSX, and CSS files.",
|
|
333
769
|
inputSchema: {
|
|
334
770
|
type: "object",
|
|
335
771
|
properties: {
|
|
@@ -340,7 +776,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
340
776
|
type: {
|
|
341
777
|
type: "string",
|
|
342
778
|
enum: ["javascript", "css"],
|
|
343
|
-
description:
|
|
779
|
+
description: 'Script type (CDN type). Use "javascript" for JS/TS/TSX/JSX files.'
|
|
780
|
+
},
|
|
781
|
+
sourceType: {
|
|
782
|
+
type: "string",
|
|
783
|
+
enum: ["js", "ts", "tsx", "jsx", "css"],
|
|
784
|
+
description: 'Source file extension. Determines the local file type. Default: "js" for javascript, "css" for css.'
|
|
344
785
|
},
|
|
345
786
|
scope: {
|
|
346
787
|
type: "string",
|
|
@@ -832,6 +1273,169 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
832
1273
|
},
|
|
833
1274
|
required: []
|
|
834
1275
|
}
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
name: "kode_library_list",
|
|
1279
|
+
description: "List all snippets in the global Kode library (Bibliotek). Returns reusable code patterns like GSAP animations, form handlers, scroll effects. Use this before writing common patterns from scratch.",
|
|
1280
|
+
inputSchema: {
|
|
1281
|
+
type: "object",
|
|
1282
|
+
properties: {
|
|
1283
|
+
category: {
|
|
1284
|
+
type: "string",
|
|
1285
|
+
description: "Filter by category: animations, forms, utilities, tracking, analytics, consent, integrations, other"
|
|
1286
|
+
},
|
|
1287
|
+
search: {
|
|
1288
|
+
type: "string",
|
|
1289
|
+
description: "Search by name or tags"
|
|
1290
|
+
},
|
|
1291
|
+
folder: {
|
|
1292
|
+
type: "string",
|
|
1293
|
+
description: "Filter by folder ID"
|
|
1294
|
+
}
|
|
1295
|
+
},
|
|
1296
|
+
required: []
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
name: "kode_library_get",
|
|
1301
|
+
description: "Get a library snippet by slug or ID. Returns full code, description, tags, example usage, and version.",
|
|
1302
|
+
inputSchema: {
|
|
1303
|
+
type: "object",
|
|
1304
|
+
properties: {
|
|
1305
|
+
slug: {
|
|
1306
|
+
type: "string",
|
|
1307
|
+
description: "Snippet slug or ID"
|
|
1308
|
+
}
|
|
1309
|
+
},
|
|
1310
|
+
required: ["slug"]
|
|
1311
|
+
}
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
name: "kode_library_use",
|
|
1315
|
+
description: "Copy a library snippet into the current project as a new script file. The snippet code is written to .cure-kode-scripts/{slug}.{ext}. Use kode_push afterwards to upload to CDN.",
|
|
1316
|
+
inputSchema: {
|
|
1317
|
+
type: "object",
|
|
1318
|
+
properties: {
|
|
1319
|
+
snippetSlug: {
|
|
1320
|
+
type: "string",
|
|
1321
|
+
description: "Library snippet slug to copy"
|
|
1322
|
+
},
|
|
1323
|
+
newName: {
|
|
1324
|
+
type: "string",
|
|
1325
|
+
description: "Optional new filename (without extension). Defaults to the snippet slug."
|
|
1326
|
+
}
|
|
1327
|
+
},
|
|
1328
|
+
required: ["snippetSlug"]
|
|
1329
|
+
}
|
|
1330
|
+
},
|
|
1331
|
+
{
|
|
1332
|
+
name: "kode_library_create",
|
|
1333
|
+
description: "Create a new snippet in the global Kode library. Requires a unique slug.",
|
|
1334
|
+
inputSchema: {
|
|
1335
|
+
type: "object",
|
|
1336
|
+
properties: {
|
|
1337
|
+
name: { type: "string", description: "Display name" },
|
|
1338
|
+
slug: { type: "string", description: "URL-safe slug (must be unique)" },
|
|
1339
|
+
type: { type: "string", enum: ["javascript", "css"], description: "Script type" },
|
|
1340
|
+
code: { type: "string", description: "Script code" },
|
|
1341
|
+
description: { type: "string", description: "Short description" },
|
|
1342
|
+
category: { type: "string", description: "Category: animations, forms, utilities, tracking, analytics, consent, integrations, other" },
|
|
1343
|
+
tags: { type: "array", items: { type: "string" }, description: "Tags for discovery" },
|
|
1344
|
+
folderId: { type: "string", description: "Target folder ID" }
|
|
1345
|
+
},
|
|
1346
|
+
required: ["name", "slug", "type", "code"]
|
|
1347
|
+
}
|
|
1348
|
+
},
|
|
1349
|
+
{
|
|
1350
|
+
name: "kode_library_update",
|
|
1351
|
+
description: "Update an existing library snippet (code, metadata, or both). Version auto-increments when code changes.",
|
|
1352
|
+
inputSchema: {
|
|
1353
|
+
type: "object",
|
|
1354
|
+
properties: {
|
|
1355
|
+
slug: { type: "string", description: "Snippet slug or ID" },
|
|
1356
|
+
code: { type: "string", description: "New code (triggers version bump)" },
|
|
1357
|
+
name: { type: "string", description: "New display name" },
|
|
1358
|
+
description: { type: "string", description: "New description" },
|
|
1359
|
+
category: { type: "string", description: "New category" },
|
|
1360
|
+
tags: { type: "array", items: { type: "string" }, description: "New tags" },
|
|
1361
|
+
folderId: { type: ["string", "null"], description: "Move to folder (null = root)" }
|
|
1362
|
+
},
|
|
1363
|
+
required: ["slug"]
|
|
1364
|
+
}
|
|
1365
|
+
},
|
|
1366
|
+
{
|
|
1367
|
+
name: "kode_library_delete",
|
|
1368
|
+
description: "Soft-delete a library snippet (moves to trash, recoverable for 30 days).",
|
|
1369
|
+
inputSchema: {
|
|
1370
|
+
type: "object",
|
|
1371
|
+
properties: {
|
|
1372
|
+
slug: { type: "string", description: "Snippet slug or ID to delete" }
|
|
1373
|
+
},
|
|
1374
|
+
required: ["slug"]
|
|
1375
|
+
}
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
name: "kode_library_folders",
|
|
1379
|
+
description: "List all folders in the Kode library. Returns folder hierarchy with snippet counts.",
|
|
1380
|
+
inputSchema: {
|
|
1381
|
+
type: "object",
|
|
1382
|
+
properties: {},
|
|
1383
|
+
required: []
|
|
1384
|
+
}
|
|
1385
|
+
},
|
|
1386
|
+
{
|
|
1387
|
+
name: "kode_pkg",
|
|
1388
|
+
description: "Package manager for Cure Kode. Like npm but for @kode/* packages. Actions: add (install a package), remove (uninstall), publish (upload a component to registry), list (browse registry), info (package details), update (update installed package).",
|
|
1389
|
+
inputSchema: {
|
|
1390
|
+
type: "object",
|
|
1391
|
+
properties: {
|
|
1392
|
+
action: {
|
|
1393
|
+
type: "string",
|
|
1394
|
+
enum: ["add", "remove", "publish", "list", "info", "update"],
|
|
1395
|
+
description: "The action to perform."
|
|
1396
|
+
},
|
|
1397
|
+
name: {
|
|
1398
|
+
type: "string",
|
|
1399
|
+
description: "Package slug. Required for add, remove, info, update."
|
|
1400
|
+
},
|
|
1401
|
+
// publish-specific options
|
|
1402
|
+
path: {
|
|
1403
|
+
type: "string",
|
|
1404
|
+
description: 'For publish: path relative to .cure-kode-scripts/ (e.g., "components/Button" or "lib/utils.ts").'
|
|
1405
|
+
},
|
|
1406
|
+
slug: {
|
|
1407
|
+
type: "string",
|
|
1408
|
+
description: "For publish: package slug to publish as."
|
|
1409
|
+
},
|
|
1410
|
+
displayName: {
|
|
1411
|
+
type: "string",
|
|
1412
|
+
description: "For publish: human-readable name."
|
|
1413
|
+
},
|
|
1414
|
+
description: {
|
|
1415
|
+
type: "string",
|
|
1416
|
+
description: "For publish: package description."
|
|
1417
|
+
},
|
|
1418
|
+
entryPoint: {
|
|
1419
|
+
type: "string",
|
|
1420
|
+
description: "For publish: entry point file (auto-detected if not set)."
|
|
1421
|
+
},
|
|
1422
|
+
tags: {
|
|
1423
|
+
type: "array",
|
|
1424
|
+
items: { type: "string" },
|
|
1425
|
+
description: "For publish: tags for discoverability."
|
|
1426
|
+
},
|
|
1427
|
+
// list-specific options
|
|
1428
|
+
search: {
|
|
1429
|
+
type: "string",
|
|
1430
|
+
description: "For list: search query."
|
|
1431
|
+
},
|
|
1432
|
+
category: {
|
|
1433
|
+
type: "string",
|
|
1434
|
+
description: "For list: filter by category."
|
|
1435
|
+
}
|
|
1436
|
+
},
|
|
1437
|
+
required: ["action"]
|
|
1438
|
+
}
|
|
835
1439
|
}
|
|
836
1440
|
]
|
|
837
1441
|
};
|
|
@@ -935,7 +1539,7 @@ ${script.content}`
|
|
|
935
1539
|
};
|
|
936
1540
|
}
|
|
937
1541
|
case "kode_create_script": {
|
|
938
|
-
const { name: scriptName, type, scope, autoLoad, purpose } = args;
|
|
1542
|
+
const { name: scriptName, type, sourceType, scope, autoLoad, purpose } = args;
|
|
939
1543
|
const scriptScope = scope || "global";
|
|
940
1544
|
const script = await client.createScript(siteId, {
|
|
941
1545
|
name: scriptName,
|
|
@@ -948,7 +1552,7 @@ ${script.content}`
|
|
|
948
1552
|
metadata: purpose ? { purpose } : void 0
|
|
949
1553
|
});
|
|
950
1554
|
const scriptsDir = getScriptsDir();
|
|
951
|
-
const ext = type === "javascript" ? "js" : "css";
|
|
1555
|
+
const ext = sourceType || (type === "javascript" ? "js" : "css");
|
|
952
1556
|
const localPath = scriptsDir ? `${scriptsDir}/${scriptName}.${ext}` : `.cure-kode-scripts/${scriptName}.${ext}`;
|
|
953
1557
|
let responseText = `Created script "${script.name}" (${script.type})`;
|
|
954
1558
|
responseText += `
|
|
@@ -957,6 +1561,10 @@ Slug: ${script.slug}`;
|
|
|
957
1561
|
Scope: ${script.scope}`;
|
|
958
1562
|
responseText += `
|
|
959
1563
|
Auto-load: ${script.auto_load ? "yes" : "no"}`;
|
|
1564
|
+
if (sourceType && sourceType !== "js" && sourceType !== "css") {
|
|
1565
|
+
responseText += `
|
|
1566
|
+
Source type: ${sourceType.toUpperCase()}`;
|
|
1567
|
+
}
|
|
960
1568
|
if (purpose) responseText += `
|
|
961
1569
|
Purpose: ${purpose}`;
|
|
962
1570
|
responseText += `
|
|
@@ -965,7 +1573,7 @@ Next steps:`;
|
|
|
965
1573
|
responseText += `
|
|
966
1574
|
1. Create file: ${localPath}`;
|
|
967
1575
|
responseText += `
|
|
968
|
-
2. Write your ${
|
|
1576
|
+
2. Write your ${ext.toUpperCase()} code`;
|
|
969
1577
|
responseText += `
|
|
970
1578
|
3. Run kode_push to upload content`;
|
|
971
1579
|
responseText += `
|
|
@@ -982,7 +1590,7 @@ Next steps:`;
|
|
|
982
1590
|
case "kode_push": {
|
|
983
1591
|
const { scriptSlug, force } = args;
|
|
984
1592
|
const scriptsDir = getScriptsDir();
|
|
985
|
-
if (!scriptsDir || !
|
|
1593
|
+
if (!scriptsDir || !fs5.existsSync(scriptsDir)) {
|
|
986
1594
|
return {
|
|
987
1595
|
content: [{
|
|
988
1596
|
type: "text",
|
|
@@ -992,23 +1600,21 @@ Next steps:`;
|
|
|
992
1600
|
};
|
|
993
1601
|
}
|
|
994
1602
|
const remoteScripts = await client.listScripts(siteId);
|
|
995
|
-
const
|
|
1603
|
+
const entryFiles = getEntryFiles(scriptsDir);
|
|
996
1604
|
if (scriptSlug) {
|
|
997
|
-
const
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
if (!fs2.existsSync(filePath)) {
|
|
1605
|
+
const entry = entryFiles.find((f) => f.slug === scriptSlug);
|
|
1606
|
+
if (!entry) {
|
|
1607
|
+
const available = entryFiles.map((f) => f.fileName).join(", ") || "(none)";
|
|
1001
1608
|
return {
|
|
1002
1609
|
content: [{
|
|
1003
1610
|
type: "text",
|
|
1004
|
-
text: `
|
|
1611
|
+
text: `No entry file found for slug "${scriptSlug}"
|
|
1005
1612
|
|
|
1006
|
-
Available files: ${
|
|
1613
|
+
Available entry files: ${available}`
|
|
1007
1614
|
}],
|
|
1008
1615
|
isError: true
|
|
1009
1616
|
};
|
|
1010
1617
|
}
|
|
1011
|
-
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1012
1618
|
const remote = remoteScripts.find((s) => s.slug === scriptSlug);
|
|
1013
1619
|
if (!remote) {
|
|
1014
1620
|
return {
|
|
@@ -1019,6 +1625,36 @@ Available files: ${localFiles.join(", ") || "(none)"}`
|
|
|
1019
1625
|
isError: true
|
|
1020
1626
|
};
|
|
1021
1627
|
}
|
|
1628
|
+
const sourceContent = fs5.readFileSync(entry.filePath, "utf-8");
|
|
1629
|
+
let content = sourceContent;
|
|
1630
|
+
let bundleNote = "";
|
|
1631
|
+
let usesReact = false;
|
|
1632
|
+
if (entry.needsBundling) {
|
|
1633
|
+
const aliases2 = getPackageAliases(scriptsDir);
|
|
1634
|
+
const result = await bundleEntryPoint({
|
|
1635
|
+
entryPoint: entry.filePath,
|
|
1636
|
+
scriptsDir,
|
|
1637
|
+
packageAliases: aliases2
|
|
1638
|
+
});
|
|
1639
|
+
if (result.errors.length > 0) {
|
|
1640
|
+
return {
|
|
1641
|
+
content: [{
|
|
1642
|
+
type: "text",
|
|
1643
|
+
text: `Build failed for "${scriptSlug}":
|
|
1644
|
+
|
|
1645
|
+
${result.errors.join("\n")}`
|
|
1646
|
+
}],
|
|
1647
|
+
isError: true
|
|
1648
|
+
};
|
|
1649
|
+
}
|
|
1650
|
+
content = result.code;
|
|
1651
|
+
usesReact = result.usesReact;
|
|
1652
|
+
bundleNote = `
|
|
1653
|
+
\u{1F4E6} Bundled from ${entry.ext.toUpperCase()}: ${result.size} bytes`;
|
|
1654
|
+
if (usesReact) bundleNote += " (uses React)";
|
|
1655
|
+
if (result.warnings.length > 0) bundleNote += `
|
|
1656
|
+
\u26A0\uFE0F ${result.warnings.join(", ")}`;
|
|
1657
|
+
}
|
|
1022
1658
|
if (!force && remote.content === content) {
|
|
1023
1659
|
return {
|
|
1024
1660
|
content: [{
|
|
@@ -1027,14 +1663,20 @@ Available files: ${localFiles.join(", ") || "(none)"}`
|
|
|
1027
1663
|
}]
|
|
1028
1664
|
};
|
|
1029
1665
|
}
|
|
1666
|
+
const existingMetadata = remote.metadata || {};
|
|
1030
1667
|
const updated = await client.updateScript(remote.id, {
|
|
1031
1668
|
content,
|
|
1032
|
-
changeSummary:
|
|
1669
|
+
changeSummary: `Pushed via MCP (${entry.ext})`,
|
|
1670
|
+
...entry.needsBundling ? {
|
|
1671
|
+
metadata: { ...existingMetadata, usesReact },
|
|
1672
|
+
sourceContent,
|
|
1673
|
+
sourceType: entry.ext
|
|
1674
|
+
} : {}
|
|
1033
1675
|
});
|
|
1034
1676
|
return {
|
|
1035
1677
|
content: [{
|
|
1036
1678
|
type: "text",
|
|
1037
|
-
text: `Pushed "${scriptSlug}": ${content.length} chars \u2192 v${updated.current_version}
|
|
1679
|
+
text: `Pushed "${scriptSlug}": ${content.length} chars \u2192 v${updated.current_version}${bundleNote}
|
|
1038
1680
|
|
|
1039
1681
|
Run kode_deploy to make changes live.`
|
|
1040
1682
|
}]
|
|
@@ -1043,28 +1685,48 @@ Run kode_deploy to make changes live.`
|
|
|
1043
1685
|
const results = [];
|
|
1044
1686
|
let pushedCount = 0;
|
|
1045
1687
|
let skippedCount = 0;
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
const
|
|
1049
|
-
const content = fs2.readFileSync(filePath, "utf-8");
|
|
1050
|
-
const remote = remoteScripts.find((s) => s.slug === slug);
|
|
1688
|
+
const aliases = getPackageAliases(scriptsDir);
|
|
1689
|
+
for (const entry of entryFiles) {
|
|
1690
|
+
const remote = remoteScripts.find((s) => s.slug === entry.slug);
|
|
1051
1691
|
if (!remote) {
|
|
1052
|
-
results.push(`\u26A0\uFE0F ${slug}: not on server (create with kode_create_script first)`);
|
|
1692
|
+
results.push(`\u26A0\uFE0F ${entry.slug}: not on server (create with kode_create_script first)`);
|
|
1053
1693
|
continue;
|
|
1054
1694
|
}
|
|
1695
|
+
const sourceContent = fs5.readFileSync(entry.filePath, "utf-8");
|
|
1696
|
+
let content = sourceContent;
|
|
1697
|
+
let usesReact = false;
|
|
1698
|
+
if (entry.needsBundling) {
|
|
1699
|
+
const bundleResult = await bundleEntryPoint({
|
|
1700
|
+
entryPoint: entry.filePath,
|
|
1701
|
+
scriptsDir,
|
|
1702
|
+
packageAliases: aliases
|
|
1703
|
+
});
|
|
1704
|
+
if (bundleResult.errors.length > 0) {
|
|
1705
|
+
results.push(`\u2717 ${entry.slug}: build failed: ${bundleResult.errors[0]}`);
|
|
1706
|
+
continue;
|
|
1707
|
+
}
|
|
1708
|
+
content = bundleResult.code;
|
|
1709
|
+
usesReact = bundleResult.usesReact;
|
|
1710
|
+
}
|
|
1055
1711
|
if (!force && remote.content === content) {
|
|
1056
1712
|
skippedCount++;
|
|
1057
1713
|
continue;
|
|
1058
1714
|
}
|
|
1059
1715
|
try {
|
|
1716
|
+
const existingMeta = remote.metadata || {};
|
|
1060
1717
|
const updated = await client.updateScript(remote.id, {
|
|
1061
1718
|
content,
|
|
1062
|
-
changeSummary:
|
|
1719
|
+
changeSummary: `Pushed via MCP (${entry.ext})`,
|
|
1720
|
+
...entry.needsBundling ? {
|
|
1721
|
+
metadata: { ...existingMeta, usesReact },
|
|
1722
|
+
sourceContent,
|
|
1723
|
+
sourceType: entry.ext
|
|
1724
|
+
} : {}
|
|
1063
1725
|
});
|
|
1064
|
-
results.push(`\u2713 ${slug}: ${content.length} chars \u2192 v${updated.current_version}`);
|
|
1726
|
+
results.push(`\u2713 ${entry.slug}: ${content.length} chars \u2192 v${updated.current_version}`);
|
|
1065
1727
|
pushedCount++;
|
|
1066
1728
|
} catch (err) {
|
|
1067
|
-
results.push(`\u2717 ${slug}: ${err instanceof Error ? err.message : "failed"}`);
|
|
1729
|
+
results.push(`\u2717 ${entry.slug}: ${err instanceof Error ? err.message : "failed"}`);
|
|
1068
1730
|
}
|
|
1069
1731
|
}
|
|
1070
1732
|
let responseText = `Push complete: ${pushedCount} updated, ${skippedCount} unchanged`;
|
|
@@ -1905,13 +2567,13 @@ Last analyzed: ${result.lastAnalyzed}`;
|
|
|
1905
2567
|
isError: true
|
|
1906
2568
|
};
|
|
1907
2569
|
}
|
|
1908
|
-
if (!
|
|
2570
|
+
if (!fs5.existsSync(contextPath)) {
|
|
1909
2571
|
return {
|
|
1910
2572
|
content: [{ type: "text", text: 'No context file found. Run "kode context --refresh" to create one.' }],
|
|
1911
2573
|
isError: true
|
|
1912
2574
|
};
|
|
1913
2575
|
}
|
|
1914
|
-
const content =
|
|
2576
|
+
const content = fs5.readFileSync(contextPath, "utf-8");
|
|
1915
2577
|
return {
|
|
1916
2578
|
content: [{ type: "text", text: content }]
|
|
1917
2579
|
};
|
|
@@ -1924,14 +2586,14 @@ Last analyzed: ${result.lastAnalyzed}`;
|
|
|
1924
2586
|
isError: true
|
|
1925
2587
|
};
|
|
1926
2588
|
}
|
|
1927
|
-
if (!
|
|
2589
|
+
if (!fs5.existsSync(contextPath)) {
|
|
1928
2590
|
return {
|
|
1929
2591
|
content: [{ type: "text", text: 'No context file found. Run "kode context --refresh" to create one.' }],
|
|
1930
2592
|
isError: true
|
|
1931
2593
|
};
|
|
1932
2594
|
}
|
|
1933
2595
|
const { addNote, addSession, updateScriptPurpose } = args;
|
|
1934
|
-
let content =
|
|
2596
|
+
let content = fs5.readFileSync(contextPath, "utf-8");
|
|
1935
2597
|
const updates = [];
|
|
1936
2598
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
1937
2599
|
content = content.replace(
|
|
@@ -1992,7 +2654,7 @@ ${sessionMd}`
|
|
|
1992
2654
|
updates.push(`Updated purpose for "${slug}": "${purpose}"`);
|
|
1993
2655
|
}
|
|
1994
2656
|
}
|
|
1995
|
-
|
|
2657
|
+
fs5.writeFileSync(contextPath, content, "utf-8");
|
|
1996
2658
|
return {
|
|
1997
2659
|
content: [
|
|
1998
2660
|
{
|
|
@@ -2100,10 +2762,10 @@ ${result.htmlPreview.slice(0, 5e3)}`;
|
|
|
2100
2762
|
};
|
|
2101
2763
|
}
|
|
2102
2764
|
const slug = urlToSlug(url);
|
|
2103
|
-
const cachePath =
|
|
2104
|
-
if (!force &&
|
|
2765
|
+
const cachePath = path5.join(pagesDir, `${slug}.json`);
|
|
2766
|
+
if (!force && fs5.existsSync(cachePath)) {
|
|
2105
2767
|
try {
|
|
2106
|
-
const cached = JSON.parse(
|
|
2768
|
+
const cached = JSON.parse(fs5.readFileSync(cachePath, "utf-8"));
|
|
2107
2769
|
return {
|
|
2108
2770
|
content: [{
|
|
2109
2771
|
type: "text",
|
|
@@ -2143,12 +2805,12 @@ CMS Collections: ${cached.cmsPatterns.length}`
|
|
|
2143
2805
|
};
|
|
2144
2806
|
}
|
|
2145
2807
|
const structure = await response.json();
|
|
2146
|
-
if (!
|
|
2147
|
-
|
|
2808
|
+
if (!fs5.existsSync(pagesDir)) {
|
|
2809
|
+
fs5.mkdirSync(pagesDir, { recursive: true });
|
|
2148
2810
|
}
|
|
2149
|
-
|
|
2811
|
+
fs5.writeFileSync(cachePath, JSON.stringify(structure, null, 2), "utf-8");
|
|
2150
2812
|
const contextPath = getContextPath();
|
|
2151
|
-
if (contextPath &&
|
|
2813
|
+
if (contextPath && fs5.existsSync(contextPath)) {
|
|
2152
2814
|
}
|
|
2153
2815
|
let text = `Page cached: ${slug}
|
|
2154
2816
|
Path: ${cachePath}
|
|
@@ -2216,8 +2878,8 @@ CMS Collections (${structure.cmsPatterns.length}):
|
|
|
2216
2878
|
};
|
|
2217
2879
|
}
|
|
2218
2880
|
const slug = urlOrSlug.startsWith("http") ? urlToSlug(urlOrSlug) : urlOrSlug;
|
|
2219
|
-
const cachePath =
|
|
2220
|
-
if (!
|
|
2881
|
+
const cachePath = path5.join(pagesDir, `${slug}.json`);
|
|
2882
|
+
if (!fs5.existsSync(cachePath)) {
|
|
2221
2883
|
return {
|
|
2222
2884
|
content: [{
|
|
2223
2885
|
type: "text",
|
|
@@ -2228,7 +2890,7 @@ Use kode_list_pages_context to see cached pages, or kode_refresh_page to cache a
|
|
|
2228
2890
|
isError: true
|
|
2229
2891
|
};
|
|
2230
2892
|
}
|
|
2231
|
-
const context = JSON.parse(
|
|
2893
|
+
const context = JSON.parse(fs5.readFileSync(cachePath, "utf-8"));
|
|
2232
2894
|
const cacheAge = Date.now() - new Date(context.extractedAt).getTime();
|
|
2233
2895
|
const cacheAgeDays = Math.floor(cacheAge / (1e3 * 60 * 60 * 24));
|
|
2234
2896
|
const isStale = cacheAgeDays > 7;
|
|
@@ -2472,7 +3134,7 @@ Use kode_list_pages_context to see cached pages, or kode_refresh_page to cache a
|
|
|
2472
3134
|
isError: true
|
|
2473
3135
|
};
|
|
2474
3136
|
}
|
|
2475
|
-
if (!
|
|
3137
|
+
if (!fs5.existsSync(pagesDir)) {
|
|
2476
3138
|
return {
|
|
2477
3139
|
content: [{
|
|
2478
3140
|
type: "text",
|
|
@@ -2480,7 +3142,7 @@ Use kode_list_pages_context to see cached pages, or kode_refresh_page to cache a
|
|
|
2480
3142
|
}]
|
|
2481
3143
|
};
|
|
2482
3144
|
}
|
|
2483
|
-
const files =
|
|
3145
|
+
const files = fs5.readdirSync(pagesDir).filter((f) => f.endsWith(".json"));
|
|
2484
3146
|
if (files.length === 0) {
|
|
2485
3147
|
return {
|
|
2486
3148
|
content: [{
|
|
@@ -2495,7 +3157,7 @@ Use kode_list_pages_context to see cached pages, or kode_refresh_page to cache a
|
|
|
2495
3157
|
for (const file of files) {
|
|
2496
3158
|
const slug = file.replace(".json", "");
|
|
2497
3159
|
try {
|
|
2498
|
-
const context = JSON.parse(
|
|
3160
|
+
const context = JSON.parse(fs5.readFileSync(path5.join(pagesDir, file), "utf-8"));
|
|
2499
3161
|
const urlPath = new URL(context.url).pathname;
|
|
2500
3162
|
text += `${urlPath} [${slug}]
|
|
2501
3163
|
`;
|
|
@@ -2576,8 +3238,8 @@ Use kode_list_pages_context to see cached pages, or kode_refresh_page to cache a
|
|
|
2576
3238
|
text += "\n";
|
|
2577
3239
|
}
|
|
2578
3240
|
if (updateClaudeMd) {
|
|
2579
|
-
const kodeMdPath =
|
|
2580
|
-
const claudeMdPath =
|
|
3241
|
+
const kodeMdPath = path5.join(projectRoot, ".cure-kode", "KODE.md");
|
|
3242
|
+
const claudeMdPath = path5.join(projectRoot, "CLAUDE.md");
|
|
2581
3243
|
const siteName = config.siteName || "Site";
|
|
2582
3244
|
const siteSlug = config.siteSlug || "site";
|
|
2583
3245
|
let kodeMd = `# Cure Kode: ${siteName}
|
|
@@ -2597,7 +3259,7 @@ Use kode_list_pages_context to see cached pages, or kode_refresh_page to cache a
|
|
|
2597
3259
|
kodeMd += `## CDN URL
|
|
2598
3260
|
|
|
2599
3261
|
\`\`\`html
|
|
2600
|
-
<script
|
|
3262
|
+
<script src="https://app.cure.no/api/cdn/${siteSlug}/init.js"></script>
|
|
2601
3263
|
\`\`\`
|
|
2602
3264
|
|
|
2603
3265
|
---
|
|
@@ -2652,7 +3314,7 @@ Use kode_list_pages_context to see cached pages, or kode_refresh_page to cache a
|
|
|
2652
3314
|
`;
|
|
2653
3315
|
kodeMd += `**Check for updates:** \`kode doctor\`
|
|
2654
3316
|
`;
|
|
2655
|
-
|
|
3317
|
+
fs5.writeFileSync(kodeMdPath, kodeMd);
|
|
2656
3318
|
text += `Updated .cure-kode/KODE.md with current scripts and pages.
|
|
2657
3319
|
`;
|
|
2658
3320
|
const kodeReference = `## Cure Kode
|
|
@@ -2668,16 +3330,16 @@ Full documentation: [.cure-kode/KODE.md](.cure-kode/KODE.md)
|
|
|
2668
3330
|
---
|
|
2669
3331
|
|
|
2670
3332
|
`;
|
|
2671
|
-
if (
|
|
2672
|
-
const claudeContent =
|
|
3333
|
+
if (fs5.existsSync(claudeMdPath)) {
|
|
3334
|
+
const claudeContent = fs5.readFileSync(claudeMdPath, "utf-8");
|
|
2673
3335
|
const hasReference = claudeContent.includes("KODE.md") || claudeContent.includes(".cure-kode/KODE.md");
|
|
2674
3336
|
if (!hasReference) {
|
|
2675
|
-
|
|
3337
|
+
fs5.writeFileSync(claudeMdPath, kodeReference + claudeContent);
|
|
2676
3338
|
text += `Added Kode reference to CLAUDE.md.
|
|
2677
3339
|
`;
|
|
2678
3340
|
}
|
|
2679
3341
|
} else {
|
|
2680
|
-
|
|
3342
|
+
fs5.writeFileSync(claudeMdPath, kodeReference);
|
|
2681
3343
|
text += `Created CLAUDE.md with Kode reference.
|
|
2682
3344
|
`;
|
|
2683
3345
|
}
|
|
@@ -2693,6 +3355,147 @@ Full documentation: [.cure-kode/KODE.md](.cure-kode/KODE.md)
|
|
|
2693
3355
|
};
|
|
2694
3356
|
}
|
|
2695
3357
|
}
|
|
3358
|
+
case "kode_library_list": {
|
|
3359
|
+
const { category, search, folder } = args;
|
|
3360
|
+
const params = new URLSearchParams();
|
|
3361
|
+
if (category) params.set("category", category);
|
|
3362
|
+
if (search) params.set("search", search);
|
|
3363
|
+
if (folder) params.set("folder", folder);
|
|
3364
|
+
const snippets = await client.listLibrary(params.toString() ? `?${params.toString()}` : "");
|
|
3365
|
+
return {
|
|
3366
|
+
content: [{ type: "text", text: JSON.stringify(snippets, null, 2) }]
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
case "kode_library_get": {
|
|
3370
|
+
const { slug } = args;
|
|
3371
|
+
const snippet = await client.getLibrarySnippet(slug);
|
|
3372
|
+
return {
|
|
3373
|
+
content: [{ type: "text", text: JSON.stringify(snippet, null, 2) }]
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
case "kode_library_use": {
|
|
3377
|
+
const { snippetSlug, newName } = args;
|
|
3378
|
+
const snippet = await client.getLibrarySnippet(snippetSlug);
|
|
3379
|
+
const fileName = newName || snippet.slug;
|
|
3380
|
+
const ext = snippet.type === "javascript" ? "js" : "css";
|
|
3381
|
+
const scriptsDir = getScriptsDir();
|
|
3382
|
+
if (!scriptsDir) {
|
|
3383
|
+
return {
|
|
3384
|
+
content: [{ type: "text", text: "Scripts directory not found. Run `kode init` first." }],
|
|
3385
|
+
isError: true
|
|
3386
|
+
};
|
|
3387
|
+
}
|
|
3388
|
+
const filePath = path5.join(scriptsDir, `${fileName}.${ext}`);
|
|
3389
|
+
if (fs5.existsSync(filePath)) {
|
|
3390
|
+
return {
|
|
3391
|
+
content: [{ type: "text", text: `File already exists: ${fileName}.${ext}. Use a different newName or delete the existing file.` }],
|
|
3392
|
+
isError: true
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
if (!fs5.existsSync(scriptsDir)) {
|
|
3396
|
+
fs5.mkdirSync(scriptsDir, { recursive: true });
|
|
3397
|
+
}
|
|
3398
|
+
fs5.writeFileSync(filePath, snippet.code);
|
|
3399
|
+
return {
|
|
3400
|
+
content: [{ type: "text", text: `Copied "${snippet.name}" to ${fileName}.${ext}
|
|
3401
|
+
|
|
3402
|
+
Run \`kode push\` to upload to CDN.` }]
|
|
3403
|
+
};
|
|
3404
|
+
}
|
|
3405
|
+
case "kode_library_create": {
|
|
3406
|
+
const { name: name2, slug, type, code, description, category, tags, folderId } = args;
|
|
3407
|
+
const result = await client.createLibrarySnippet({
|
|
3408
|
+
name: name2,
|
|
3409
|
+
slug,
|
|
3410
|
+
type,
|
|
3411
|
+
code,
|
|
3412
|
+
description,
|
|
3413
|
+
category,
|
|
3414
|
+
tags,
|
|
3415
|
+
folderId
|
|
3416
|
+
});
|
|
3417
|
+
return {
|
|
3418
|
+
content: [{ type: "text", text: `Created library snippet "${name2}" (${result.slug})` }]
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
case "kode_library_update": {
|
|
3422
|
+
const { slug, ...updates } = args;
|
|
3423
|
+
const result = await client.updateLibrarySnippet(slug, updates);
|
|
3424
|
+
return {
|
|
3425
|
+
content: [{ type: "text", text: `Updated library snippet "${result.slug}" (now v${result.version})` }]
|
|
3426
|
+
};
|
|
3427
|
+
}
|
|
3428
|
+
case "kode_library_delete": {
|
|
3429
|
+
const { slug } = args;
|
|
3430
|
+
await client.deleteLibrarySnippet(slug);
|
|
3431
|
+
return {
|
|
3432
|
+
content: [{ type: "text", text: `Soft-deleted library snippet "${slug}". It can be restored from trash within 30 days.` }]
|
|
3433
|
+
};
|
|
3434
|
+
}
|
|
3435
|
+
case "kode_library_folders": {
|
|
3436
|
+
const folders = await client.listLibraryFolders();
|
|
3437
|
+
return {
|
|
3438
|
+
content: [{ type: "text", text: JSON.stringify(folders, null, 2) }]
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
case "kode_pkg": {
|
|
3442
|
+
const action = args?.action;
|
|
3443
|
+
const scriptsDir = getScriptsDir();
|
|
3444
|
+
switch (action) {
|
|
3445
|
+
case "add": {
|
|
3446
|
+
const pkgName = args?.name;
|
|
3447
|
+
if (!pkgName) return { content: [{ type: "text", text: "Error: name is required for add." }], isError: true };
|
|
3448
|
+
if (!scriptsDir) return { content: [{ type: "text", text: "Error: Not in a Kode project. Run kode init first." }], isError: true };
|
|
3449
|
+
const result = await pkgAdd(client, scriptsDir, pkgName);
|
|
3450
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
3451
|
+
}
|
|
3452
|
+
case "remove": {
|
|
3453
|
+
const pkgName = args?.name;
|
|
3454
|
+
if (!pkgName) return { content: [{ type: "text", text: "Error: name is required for remove." }], isError: true };
|
|
3455
|
+
if (!scriptsDir) return { content: [{ type: "text", text: "Error: Not in a Kode project." }], isError: true };
|
|
3456
|
+
const result = await pkgRemove(scriptsDir, pkgName);
|
|
3457
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
3458
|
+
}
|
|
3459
|
+
case "publish": {
|
|
3460
|
+
const pubPath = args?.path;
|
|
3461
|
+
const pubSlug = args?.slug || args?.name;
|
|
3462
|
+
if (!pubPath) return { content: [{ type: "text", text: "Error: path is required for publish." }], isError: true };
|
|
3463
|
+
if (!pubSlug) return { content: [{ type: "text", text: "Error: slug (or name) is required for publish." }], isError: true };
|
|
3464
|
+
if (!scriptsDir) return { content: [{ type: "text", text: "Error: Not in a Kode project." }], isError: true };
|
|
3465
|
+
const result = await pkgPublish(client, scriptsDir, {
|
|
3466
|
+
path: pubPath,
|
|
3467
|
+
slug: pubSlug,
|
|
3468
|
+
name: args?.displayName,
|
|
3469
|
+
description: args?.description,
|
|
3470
|
+
entryPoint: args?.entryPoint,
|
|
3471
|
+
tags: args?.tags
|
|
3472
|
+
});
|
|
3473
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
3474
|
+
}
|
|
3475
|
+
case "list": {
|
|
3476
|
+
const result = await pkgList(client, scriptsDir, {
|
|
3477
|
+
search: args?.search,
|
|
3478
|
+
category: args?.category
|
|
3479
|
+
});
|
|
3480
|
+
return { content: [{ type: "text", text: result.message }] };
|
|
3481
|
+
}
|
|
3482
|
+
case "info": {
|
|
3483
|
+
const pkgName = args?.name;
|
|
3484
|
+
if (!pkgName) return { content: [{ type: "text", text: "Error: name is required for info." }], isError: true };
|
|
3485
|
+
const result = await pkgInfo(client, scriptsDir, pkgName);
|
|
3486
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
3487
|
+
}
|
|
3488
|
+
case "update": {
|
|
3489
|
+
const pkgName = args?.name;
|
|
3490
|
+
if (!pkgName) return { content: [{ type: "text", text: "Error: name is required for update." }], isError: true };
|
|
3491
|
+
if (!scriptsDir) return { content: [{ type: "text", text: "Error: Not in a Kode project." }], isError: true };
|
|
3492
|
+
const result = await pkgUpdate(client, scriptsDir, pkgName);
|
|
3493
|
+
return { content: [{ type: "text", text: result.message }], isError: !result.success };
|
|
3494
|
+
}
|
|
3495
|
+
default:
|
|
3496
|
+
return { content: [{ type: "text", text: `Unknown kode_pkg action: "${action}". Use: add, remove, publish, list, info, update.` }], isError: true };
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
2696
3499
|
default:
|
|
2697
3500
|
return {
|
|
2698
3501
|
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
@@ -2713,14 +3516,15 @@ Full documentation: [.cure-kode/KODE.md](.cure-kode/KODE.md)
|
|
|
2713
3516
|
});
|
|
2714
3517
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
2715
3518
|
const scriptsDir = getScriptsDir();
|
|
2716
|
-
if (!scriptsDir || !
|
|
3519
|
+
if (!scriptsDir || !fs5.existsSync(scriptsDir)) {
|
|
2717
3520
|
return { resources: [] };
|
|
2718
3521
|
}
|
|
2719
|
-
const
|
|
2720
|
-
const
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
3522
|
+
const entries = getEntryFiles(scriptsDir);
|
|
3523
|
+
const modules = getModuleFiles(scriptsDir);
|
|
3524
|
+
const resources = [...entries, ...modules].map((f) => ({
|
|
3525
|
+
uri: `file://${f.filePath}`,
|
|
3526
|
+
name: f.isModule ? f.relativePath : f.fileName,
|
|
3527
|
+
mimeType: mimeTypeFromFileName(f.fileName)
|
|
2724
3528
|
}));
|
|
2725
3529
|
return { resources };
|
|
2726
3530
|
});
|
|
@@ -2730,11 +3534,11 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
|
2730
3534
|
throw new Error("Invalid resource URI");
|
|
2731
3535
|
}
|
|
2732
3536
|
const filePath = uri.replace("file://", "");
|
|
2733
|
-
if (!
|
|
3537
|
+
if (!fs5.existsSync(filePath)) {
|
|
2734
3538
|
throw new Error("File not found");
|
|
2735
3539
|
}
|
|
2736
|
-
const content =
|
|
2737
|
-
const mimeType = filePath
|
|
3540
|
+
const content = fs5.readFileSync(filePath, "utf-8");
|
|
3541
|
+
const mimeType = mimeTypeFromFileName(filePath);
|
|
2738
3542
|
return {
|
|
2739
3543
|
contents: [
|
|
2740
3544
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@curenorway/kode-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "MCP server for Cure Kode CDN - enables AI agents to manage, deploy, and analyze Webflow scripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"prepublishOnly": "pnpm build"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
21
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
22
|
+
"esbuild": "^0.24.2"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^20.10.0",
|