@curenorway/kode-mcp 1.2.0 → 1.3.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.
Files changed (2) hide show
  1. package/dist/index.js +519 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -105,6 +105,16 @@ var KodeApiClient = class {
105
105
  async getDeploymentStatus(siteId) {
106
106
  return this.request(`/api/cdn/sites/${siteId}/deployments/status`);
107
107
  }
108
+ // v2.3: Production enabled toggle
109
+ async setProductionEnabled(siteId, enabled, productionDomain) {
110
+ return this.request(`/api/cdn/sites/${siteId}/production`, {
111
+ method: "POST",
112
+ body: JSON.stringify({
113
+ enabled,
114
+ productionDomain
115
+ })
116
+ });
117
+ }
108
118
  // HTML operations
109
119
  async fetchHtml(siteId, url) {
110
120
  return this.request("/api/cdn/fetch-html", {
@@ -121,6 +131,16 @@ var KodeApiClient = class {
121
131
  return false;
122
132
  }
123
133
  }
134
+ // Metadata operations
135
+ async analyzeScript(scriptId, options = {}) {
136
+ return this.request(`/api/cdn/scripts/${scriptId}/analyze`, {
137
+ method: "POST",
138
+ body: JSON.stringify(options)
139
+ });
140
+ }
141
+ async getScriptMetadata(scriptId) {
142
+ return this.request(`/api/cdn/scripts/${scriptId}/metadata`);
143
+ }
124
144
  };
125
145
 
126
146
  // src/config.ts
@@ -274,7 +294,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
274
294
  },
275
295
  {
276
296
  name: "kode_create_script",
277
- description: "Create a new script on the Cure Kode CDN. The script will be available at the CDN URL after deployment. Global scripts auto-load by default; page-specific scripts do not.",
297
+ description: "Create a new script entry (metadata only). After creating, write the script file locally to .cure-kode-scripts/{name}.js and use kode_push to upload content. This keeps script content out of MCP context.",
278
298
  inputSchema: {
279
299
  type: "object",
280
300
  properties: {
@@ -287,10 +307,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
287
307
  enum: ["javascript", "css"],
288
308
  description: "Script type"
289
309
  },
290
- content: {
291
- type: "string",
292
- description: "Script content (JavaScript or CSS code)"
293
- },
294
310
  scope: {
295
311
  type: "string",
296
312
  enum: ["global", "page-specific"],
@@ -298,15 +314,37 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
298
314
  },
299
315
  autoLoad: {
300
316
  type: "boolean",
301
- description: "Whether to auto-load the script. Default: true for global scripts, false for page-specific. Set to false for scripts you want to load manually via CK.loadScript()."
317
+ description: "Whether to auto-load the script. Default: true for global scripts, false for page-specific."
318
+ },
319
+ purpose: {
320
+ type: "string",
321
+ description: "Brief description of what the script does (for metadata)"
322
+ }
323
+ },
324
+ required: ["name", "type"]
325
+ }
326
+ },
327
+ {
328
+ name: "kode_push",
329
+ description: "Push local script files to Cure Kode. Reads files from .cure-kode-scripts/ folder and uploads to API. This is the proper way to sync code - never send script content through other MCP tools.",
330
+ inputSchema: {
331
+ type: "object",
332
+ properties: {
333
+ scriptSlug: {
334
+ type: "string",
335
+ description: 'Specific script slug to push (e.g., "map"). If omitted, pushes all changed scripts.'
336
+ },
337
+ force: {
338
+ type: "boolean",
339
+ description: "Force push even if content appears unchanged. Default: false"
302
340
  }
303
341
  },
304
- required: ["name", "type", "content"]
342
+ required: []
305
343
  }
306
344
  },
307
345
  {
308
346
  name: "kode_update_script",
309
- description: "Update an existing script's content or settings. This creates a new version of the script when content changes.",
347
+ description: "Update script settings (NOT content). For content changes, edit the local file in .cure-kode-scripts/ and use kode_push. This tool is for metadata, scope, and autoLoad changes only.",
310
348
  inputSchema: {
311
349
  type: "object",
312
350
  properties: {
@@ -314,10 +352,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
314
352
  type: "string",
315
353
  description: "Script slug or ID to update"
316
354
  },
317
- content: {
318
- type: "string",
319
- description: "New script content"
320
- },
321
355
  autoLoad: {
322
356
  type: "boolean",
323
357
  description: "Whether to auto-load the script. Set to true to load automatically, false for manual loading via CK.loadScript()."
@@ -327,9 +361,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
327
361
  enum: ["global", "page-specific"],
328
362
  description: "Change script scope. Note: changing to page-specific requires assigning to pages."
329
363
  },
330
- changeSummary: {
364
+ purpose: {
331
365
  type: "string",
332
- description: "Brief description of the changes (for version history)"
366
+ description: "Update the script purpose description (shown in Chrome Extension)"
367
+ },
368
+ regenerateSummary: {
369
+ type: "boolean",
370
+ description: "Re-analyze the script content and regenerate the AI summary. Useful after significant changes."
333
371
  }
334
372
  },
335
373
  required: ["slug"]
@@ -379,7 +417,30 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
379
417
  },
380
418
  {
381
419
  name: "kode_status",
382
- description: "Get the current deployment status for both staging and production environments.",
420
+ description: "Get the current deployment status including production enabled state, staging and production environments.",
421
+ inputSchema: {
422
+ type: "object",
423
+ properties: {},
424
+ required: []
425
+ }
426
+ },
427
+ {
428
+ name: "kode_production_enable",
429
+ description: "Enable production environment for this site. Required before promoting to production. Sites start in staging-only mode by default.",
430
+ inputSchema: {
431
+ type: "object",
432
+ properties: {
433
+ productionDomain: {
434
+ type: "string",
435
+ description: 'Production domain (e.g., "example.com"). Optional - can be set later.'
436
+ }
437
+ },
438
+ required: []
439
+ }
440
+ },
441
+ {
442
+ name: "kode_production_disable",
443
+ description: "Disable production environment for this site. Production requests will return an empty script. Useful during development when only staging should be active.",
383
444
  inputSchema: {
384
445
  type: "object",
385
446
  properties: {},
@@ -458,6 +519,38 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
458
519
  required: []
459
520
  }
460
521
  },
522
+ {
523
+ name: "kode_analyze_script",
524
+ description: "Analyze a script's content and auto-generate metadata. Detects DOM selectors, event triggers, dependencies (GSAP, Swiper, etc.), and behavior patterns. Optionally saves the analysis to the script.",
525
+ inputSchema: {
526
+ type: "object",
527
+ properties: {
528
+ slug: {
529
+ type: "string",
530
+ description: "Script slug or ID to analyze"
531
+ },
532
+ saveToScript: {
533
+ type: "boolean",
534
+ description: "Save the generated metadata to the script. Default: false (preview only)"
535
+ }
536
+ },
537
+ required: ["slug"]
538
+ }
539
+ },
540
+ {
541
+ name: "kode_get_script_metadata",
542
+ description: "Get the metadata for a script, including detected DOM targets, triggers, dependencies, and AI summary. Useful for Chrome Extension visibility.",
543
+ inputSchema: {
544
+ type: "object",
545
+ properties: {
546
+ slug: {
547
+ type: "string",
548
+ description: "Script slug or ID to get metadata for"
549
+ }
550
+ },
551
+ required: ["slug"]
552
+ }
553
+ },
461
554
  {
462
555
  name: "kode_read_context",
463
556
  description: "Read the project context file (.cure-kode/context.md). Contains current scripts, notes, and session history. ALWAYS call this before starting work on a Kode project.",
@@ -667,35 +760,155 @@ ${script.content}`
667
760
  };
668
761
  }
669
762
  case "kode_create_script": {
670
- const { name: scriptName, type, content, scope, autoLoad } = args;
763
+ const { name: scriptName, type, scope, autoLoad, purpose } = args;
671
764
  const scriptScope = scope || "global";
672
765
  const script = await client.createScript(siteId, {
673
766
  name: scriptName,
674
767
  slug: scriptName,
675
768
  type,
676
- content,
769
+ content: "",
770
+ // Empty - content comes from local files via kode_push
677
771
  scope: scriptScope,
678
- autoLoad
679
- // Let API handle default based on scope
772
+ autoLoad,
773
+ metadata: purpose ? { purpose } : void 0
680
774
  });
681
- const autoLoadStatus = script.auto_load ? "will auto-load" : "manual load only (use CK.loadScript())";
775
+ const scriptsDir = getScriptsDir();
776
+ const ext = type === "javascript" ? "js" : "css";
777
+ const localPath = scriptsDir ? `${scriptsDir}/${scriptName}.${ext}` : `.cure-kode-scripts/${scriptName}.${ext}`;
778
+ let responseText = `Created script "${script.name}" (${script.type})`;
779
+ responseText += `
780
+ Slug: ${script.slug}`;
781
+ responseText += `
782
+ Scope: ${script.scope}`;
783
+ responseText += `
784
+ Auto-load: ${script.auto_load ? "yes" : "no"}`;
785
+ if (purpose) responseText += `
786
+ Purpose: ${purpose}`;
787
+ responseText += `
788
+
789
+ Next steps:`;
790
+ responseText += `
791
+ 1. Create file: ${localPath}`;
792
+ responseText += `
793
+ 2. Write your ${type} code`;
794
+ responseText += `
795
+ 3. Run kode_push to upload content`;
796
+ responseText += `
797
+ 4. Run kode_deploy to make it live`;
682
798
  return {
683
799
  content: [
684
800
  {
685
801
  type: "text",
686
- text: `Created script "${script.name}" (${script.type})
687
- Slug: ${script.slug}
688
- Scope: ${script.scope}
689
- Auto-load: ${autoLoadStatus}
690
- Version: ${script.current_version}
691
-
692
- Note: Run kode_deploy to make it live.`
802
+ text: responseText
693
803
  }
694
804
  ]
695
805
  };
696
806
  }
807
+ case "kode_push": {
808
+ const { scriptSlug, force } = args;
809
+ const scriptsDir = getScriptsDir();
810
+ if (!scriptsDir || !fs2.existsSync(scriptsDir)) {
811
+ return {
812
+ content: [{
813
+ type: "text",
814
+ text: 'No .cure-kode-scripts/ folder found. Run "kode init" first or create the folder manually.'
815
+ }],
816
+ isError: true
817
+ };
818
+ }
819
+ const remoteScripts = await client.listScripts(siteId);
820
+ const localFiles = fs2.readdirSync(scriptsDir).filter((f) => f.endsWith(".js") || f.endsWith(".css"));
821
+ if (scriptSlug) {
822
+ const ext = remoteScripts.find((s) => s.slug === scriptSlug)?.type === "css" ? "css" : "js";
823
+ const fileName = `${scriptSlug}.${ext}`;
824
+ const filePath = path2.join(scriptsDir, fileName);
825
+ if (!fs2.existsSync(filePath)) {
826
+ return {
827
+ content: [{
828
+ type: "text",
829
+ text: `File not found: ${filePath}
830
+
831
+ Available files: ${localFiles.join(", ") || "(none)"}`
832
+ }],
833
+ isError: true
834
+ };
835
+ }
836
+ const content = fs2.readFileSync(filePath, "utf-8");
837
+ const remote = remoteScripts.find((s) => s.slug === scriptSlug);
838
+ if (!remote) {
839
+ return {
840
+ content: [{
841
+ type: "text",
842
+ text: `Script "${scriptSlug}" not found on server. Create it first with kode_create_script.`
843
+ }],
844
+ isError: true
845
+ };
846
+ }
847
+ if (!force && remote.content === content) {
848
+ return {
849
+ content: [{
850
+ type: "text",
851
+ text: `Script "${scriptSlug}" is already up to date (${content.length} chars). Use force: true to push anyway.`
852
+ }]
853
+ };
854
+ }
855
+ const updated = await client.updateScript(remote.id, {
856
+ content,
857
+ changeSummary: "Pushed via MCP"
858
+ });
859
+ return {
860
+ content: [{
861
+ type: "text",
862
+ text: `Pushed "${scriptSlug}": ${content.length} chars \u2192 v${updated.current_version}
863
+
864
+ Run kode_deploy to make changes live.`
865
+ }]
866
+ };
867
+ }
868
+ const results = [];
869
+ let pushedCount = 0;
870
+ let skippedCount = 0;
871
+ for (const file of localFiles) {
872
+ const slug = file.replace(/\.(js|css)$/, "");
873
+ const filePath = path2.join(scriptsDir, file);
874
+ const content = fs2.readFileSync(filePath, "utf-8");
875
+ const remote = remoteScripts.find((s) => s.slug === slug);
876
+ if (!remote) {
877
+ results.push(`\u26A0\uFE0F ${slug}: not on server (create with kode_create_script first)`);
878
+ continue;
879
+ }
880
+ if (!force && remote.content === content) {
881
+ skippedCount++;
882
+ continue;
883
+ }
884
+ try {
885
+ const updated = await client.updateScript(remote.id, {
886
+ content,
887
+ changeSummary: "Pushed via MCP"
888
+ });
889
+ results.push(`\u2713 ${slug}: ${content.length} chars \u2192 v${updated.current_version}`);
890
+ pushedCount++;
891
+ } catch (err) {
892
+ results.push(`\u2717 ${slug}: ${err instanceof Error ? err.message : "failed"}`);
893
+ }
894
+ }
895
+ let responseText = `Push complete: ${pushedCount} updated, ${skippedCount} unchanged`;
896
+ if (results.length > 0) {
897
+ responseText += `
898
+
899
+ ${results.join("\n")}`;
900
+ }
901
+ if (pushedCount > 0) {
902
+ responseText += `
903
+
904
+ Run kode_deploy to make changes live.`;
905
+ }
906
+ return {
907
+ content: [{ type: "text", text: responseText }]
908
+ };
909
+ }
697
910
  case "kode_update_script": {
698
- const { slug, content, autoLoad, scope, changeSummary } = args;
911
+ const { slug, autoLoad, scope, purpose, regenerateSummary } = args;
699
912
  const scripts = await client.listScripts(siteId);
700
913
  const script = scripts.find((s) => s.slug === slug || s.id === slug);
701
914
  if (!script) {
@@ -704,24 +917,32 @@ Note: Run kode_deploy to make it live.`
704
917
  isError: true
705
918
  };
706
919
  }
707
- const updateData = {};
708
- if (content !== void 0) updateData.content = content;
920
+ const updateData = {
921
+ changeSummary: "Updated settings via MCP"
922
+ };
709
923
  if (autoLoad !== void 0) updateData.autoLoad = autoLoad;
710
924
  if (scope !== void 0) updateData.scope = scope;
711
- updateData.changeSummary = changeSummary || "Updated via MCP";
925
+ if (purpose !== void 0) updateData.metadata = { purpose };
926
+ if (regenerateSummary !== void 0) updateData.regenerateSummary = regenerateSummary;
712
927
  const updated = await client.updateScript(script.id, updateData);
713
928
  const changes = [];
714
- if (content !== void 0) changes.push(`content \u2192 v${updated.current_version}`);
715
- if (autoLoad !== void 0) changes.push(`auto_load \u2192 ${autoLoad}`);
929
+ if (autoLoad !== void 0) changes.push(`autoLoad \u2192 ${autoLoad}`);
716
930
  if (scope !== void 0) changes.push(`scope \u2192 ${scope}`);
931
+ if (purpose !== void 0) changes.push(`purpose updated`);
932
+ if (regenerateSummary) changes.push("AI summary regenerated");
933
+ let responseText = `Updated script "${updated.name}"`;
934
+ if (changes.length > 0) {
935
+ responseText += `
936
+ Changes: ${changes.join(", ")}`;
937
+ }
938
+ responseText += `
939
+
940
+ To update content: edit local file and run kode_push`;
717
941
  return {
718
942
  content: [
719
943
  {
720
944
  type: "text",
721
- text: `Updated script "${updated.name}"
722
- Changes: ${changes.join(", ")}
723
-
724
- Note: Run kode_deploy to make changes live.`
945
+ text: responseText
725
946
  }
726
947
  ]
727
948
  };
@@ -752,19 +973,54 @@ Note: Run kode_deploy to make changes live.`
752
973
  environment: environment || "staging",
753
974
  notes: notes || "Deployed via MCP"
754
975
  });
976
+ let responseText = `Deployment ${deployment.version} to ${deployment.environment}: ${deployment.status}`;
977
+ responseText += `
978
+ Started: ${deployment.started_at}`;
979
+ if (deployment.completed_at) {
980
+ responseText += `
981
+ Completed: ${deployment.completed_at}`;
982
+ }
983
+ const scriptSizes = deployment.stats?.scriptSizes;
984
+ if (scriptSizes && scriptSizes.length > 0) {
985
+ responseText += `
986
+
987
+ Scripts deployed (${scriptSizes.length}):`;
988
+ for (const s of scriptSizes) {
989
+ const flags = [
990
+ s.scope === "global" ? "G" : "P",
991
+ s.autoLoad ? "\u26A1" : "\u25CB"
992
+ ].join("");
993
+ responseText += `
994
+ ${s.slug} [${flags}]: ${s.contentSize} chars`;
995
+ if (s.contentSize === 0) {
996
+ responseText += " \u26A0\uFE0F EMPTY";
997
+ } else if (s.contentSize < 50 && s.scope === "global") {
998
+ responseText += " \u26A0\uFE0F very small";
999
+ }
1000
+ }
1001
+ }
755
1002
  return {
756
1003
  content: [
757
1004
  {
758
1005
  type: "text",
759
- text: `Deployment ${deployment.version} to ${deployment.environment}: ${deployment.status}
760
- Started: ${deployment.started_at}${deployment.completed_at ? `
761
- Completed: ${deployment.completed_at}` : ""}`
1006
+ text: responseText
762
1007
  }
763
1008
  ]
764
1009
  };
765
1010
  }
766
1011
  case "kode_promote": {
767
1012
  const status = await client.getDeploymentStatus(siteId);
1013
+ if (!status.productionEnabled) {
1014
+ return {
1015
+ content: [
1016
+ {
1017
+ type: "text",
1018
+ text: "Cannot promote: Production is not enabled for this site.\n\nRun kode_production_enable first to activate the production environment."
1019
+ }
1020
+ ],
1021
+ isError: true
1022
+ };
1023
+ }
768
1024
  if (!status.canPromote) {
769
1025
  return {
770
1026
  content: [
@@ -787,12 +1043,46 @@ Status: ${deployment.status}`
787
1043
  ]
788
1044
  };
789
1045
  }
1046
+ case "kode_production_enable": {
1047
+ const { productionDomain } = args;
1048
+ const result = await client.setProductionEnabled(siteId, true, productionDomain);
1049
+ let text = "Production environment enabled!\n\n";
1050
+ if (result.productionDomain) {
1051
+ text += `Domain: ${result.productionDomain}
1052
+ `;
1053
+ }
1054
+ text += "\nNext steps:\n";
1055
+ text += "1. Deploy to staging: kode_deploy\n";
1056
+ text += "2. Promote to production: kode_promote";
1057
+ return {
1058
+ content: [{ type: "text", text }]
1059
+ };
1060
+ }
1061
+ case "kode_production_disable": {
1062
+ await client.setProductionEnabled(siteId, false);
1063
+ return {
1064
+ content: [
1065
+ {
1066
+ type: "text",
1067
+ text: "Production environment disabled.\n\nOnly staging is now active. Production domain requests will receive an empty script with a warning."
1068
+ }
1069
+ ]
1070
+ };
1071
+ }
790
1072
  case "kode_status": {
791
1073
  const status = await client.getDeploymentStatus(siteId);
792
1074
  const config = getConfig();
793
1075
  let text = `Site: ${config?.siteName || "Unknown"}
794
1076
 
795
1077
  `;
1078
+ const productionEnabled = status.productionEnabled ?? false;
1079
+ text += `PRODUCTION STATUS: ${productionEnabled ? "\u2713 Enabled" : "\u25CB Disabled (staging only)"}
1080
+ `;
1081
+ if (productionEnabled && status.productionDomain) {
1082
+ text += ` Domain: ${status.productionDomain}
1083
+ `;
1084
+ }
1085
+ text += "\n";
796
1086
  text += `STAGING:
797
1087
  `;
798
1088
  if (status.staging.lastSuccessful) {
@@ -807,17 +1097,25 @@ Status: ${deployment.status}`
807
1097
  text += `
808
1098
  PRODUCTION:
809
1099
  `;
810
- if (status.production.lastSuccessful) {
1100
+ if (!productionEnabled) {
1101
+ text += ` (Disabled - use kode_production_enable to activate)
1102
+ `;
1103
+ } else if (status.production.lastSuccessful) {
811
1104
  text += ` Version: ${status.production.lastSuccessful.version}
812
1105
  `;
813
1106
  text += ` Deployed: ${status.production.lastSuccessful.completed_at}
814
1107
  `;
815
1108
  } else {
816
- text += ` Not deployed
1109
+ text += ` Enabled, not yet deployed
817
1110
  `;
818
1111
  }
819
- text += `
1112
+ if (productionEnabled) {
1113
+ text += `
820
1114
  Can promote staging to production: ${status.canPromote ? "Yes" : "No"}`;
1115
+ } else {
1116
+ text += `
1117
+ Enable production first (kode_production_enable) before promoting.`;
1118
+ }
821
1119
  return {
822
1120
  content: [{ type: "text", text }]
823
1121
  };
@@ -1002,6 +1300,184 @@ Embed code:
1002
1300
  content: [{ type: "text", text }]
1003
1301
  };
1004
1302
  }
1303
+ case "kode_analyze_script": {
1304
+ const { slug, saveToScript } = args;
1305
+ const scripts = await client.listScripts(siteId);
1306
+ const script = scripts.find((s) => s.slug === slug || s.id === slug);
1307
+ if (!script) {
1308
+ return {
1309
+ content: [{ type: "text", text: `Script "${slug}" not found` }],
1310
+ isError: true
1311
+ };
1312
+ }
1313
+ try {
1314
+ const result = await client.analyzeScript(script.id, { saveToScript });
1315
+ let text = `Script Analysis: ${script.name}
1316
+
1317
+ `;
1318
+ text += `Purpose: ${result.metadata.purpose || "(not detected)"}
1319
+
1320
+ `;
1321
+ if (result.metadata.triggers.length > 0) {
1322
+ text += `Triggers:
1323
+ `;
1324
+ for (const t of result.metadata.triggers) {
1325
+ text += ` - ${t.event}${t.selector ? ` on ${t.selector}` : ""}
1326
+ `;
1327
+ }
1328
+ text += "\n";
1329
+ }
1330
+ if (result.metadata.domTargets.length > 0) {
1331
+ text += `DOM Targets:
1332
+ `;
1333
+ for (const d of result.metadata.domTargets) {
1334
+ text += ` - ${d.selector} (${d.action})
1335
+ `;
1336
+ }
1337
+ text += "\n";
1338
+ }
1339
+ if (result.metadata.dependencies.length > 0) {
1340
+ text += `Dependencies:
1341
+ `;
1342
+ for (const dep of result.metadata.dependencies) {
1343
+ text += ` - ${dep.name}${dep.required ? " (required)" : ""}
1344
+ `;
1345
+ }
1346
+ text += "\n";
1347
+ }
1348
+ text += `Flags:
1349
+ `;
1350
+ text += ` - Modifies DOM: ${result.metadata.modifiesDom ? "Yes" : "No"}
1351
+ `;
1352
+ text += ` - Event listeners: ${result.metadata.addsEventListeners ? "Yes" : "No"}
1353
+ `;
1354
+ if (result.metadata.usesExternalApis) text += ` - Uses external APIs: Yes
1355
+ `;
1356
+ if (result.metadata.usesLocalStorage) text += ` - Uses localStorage: Yes
1357
+ `;
1358
+ if (result.metadata.usesCookies) text += ` - Uses cookies: Yes
1359
+ `;
1360
+ if (result.warnings.length > 0) {
1361
+ text += `
1362
+ Warnings:
1363
+ `;
1364
+ for (const w of result.warnings) {
1365
+ text += ` \u26A0\uFE0F ${w}
1366
+ `;
1367
+ }
1368
+ }
1369
+ if (result.suggestions.length > 0) {
1370
+ text += `
1371
+ Suggestions:
1372
+ `;
1373
+ for (const s of result.suggestions) {
1374
+ text += ` \u{1F4A1} ${s}
1375
+ `;
1376
+ }
1377
+ }
1378
+ if (saveToScript) {
1379
+ text += `
1380
+ \u2713 Metadata saved to script. Run kode_deploy to update CDN.`;
1381
+ } else {
1382
+ text += `
1383
+ Use saveToScript: true to save this metadata to the script.`;
1384
+ }
1385
+ return {
1386
+ content: [{ type: "text", text }]
1387
+ };
1388
+ } catch (error) {
1389
+ const message = error instanceof Error ? error.message : "Unknown error";
1390
+ return {
1391
+ content: [{ type: "text", text: `Failed to analyze script: ${message}` }],
1392
+ isError: true
1393
+ };
1394
+ }
1395
+ }
1396
+ case "kode_get_script_metadata": {
1397
+ const { slug } = args;
1398
+ const scripts = await client.listScripts(siteId);
1399
+ const script = scripts.find((s) => s.slug === slug || s.id === slug);
1400
+ if (!script) {
1401
+ return {
1402
+ content: [{ type: "text", text: `Script "${slug}" not found` }],
1403
+ isError: true
1404
+ };
1405
+ }
1406
+ try {
1407
+ const result = await client.getScriptMetadata(script.id);
1408
+ if (!result.metadata) {
1409
+ return {
1410
+ content: [{
1411
+ type: "text",
1412
+ text: `Script "${script.name}" has no metadata.
1413
+
1414
+ Use kode_analyze_script to generate metadata, or provide metadata when creating/updating the script.`
1415
+ }]
1416
+ };
1417
+ }
1418
+ let text = `Metadata for: ${script.name}
1419
+
1420
+ `;
1421
+ if (result.metadata.purpose) {
1422
+ text += `Purpose: ${result.metadata.purpose}
1423
+
1424
+ `;
1425
+ }
1426
+ if (result.aiSummary) {
1427
+ text += `AI Summary: ${result.aiSummary}
1428
+
1429
+ `;
1430
+ }
1431
+ if (result.metadata.triggers.length > 0) {
1432
+ text += `Triggers:
1433
+ `;
1434
+ for (const t of result.metadata.triggers) {
1435
+ text += ` - ${t.event}${t.selector ? ` on ${t.selector}` : ""}
1436
+ `;
1437
+ }
1438
+ text += "\n";
1439
+ }
1440
+ if (result.metadata.domTargets.length > 0) {
1441
+ text += `DOM Targets:
1442
+ `;
1443
+ for (const d of result.metadata.domTargets) {
1444
+ text += ` - ${d.selector} (${d.action})
1445
+ `;
1446
+ }
1447
+ text += "\n";
1448
+ }
1449
+ if (result.metadata.dependencies.length > 0) {
1450
+ text += `Dependencies:
1451
+ `;
1452
+ for (const dep of result.metadata.dependencies) {
1453
+ text += ` - ${dep.name}${dep.required ? " (required)" : ""}
1454
+ `;
1455
+ }
1456
+ text += "\n";
1457
+ }
1458
+ text += `Flags:
1459
+ `;
1460
+ text += ` - AI Generated: ${result.aiGenerated ? "Yes" : "No"}
1461
+ `;
1462
+ text += ` - Modifies DOM: ${result.metadata.modifiesDom ? "Yes" : "No"}
1463
+ `;
1464
+ text += ` - Event listeners: ${result.metadata.addsEventListeners ? "Yes" : "No"}
1465
+ `;
1466
+ if (result.lastAnalyzed) {
1467
+ text += `
1468
+ Last analyzed: ${result.lastAnalyzed}`;
1469
+ }
1470
+ return {
1471
+ content: [{ type: "text", text }]
1472
+ };
1473
+ } catch (error) {
1474
+ const message = error instanceof Error ? error.message : "Unknown error";
1475
+ return {
1476
+ content: [{ type: "text", text: `Failed to get metadata: ${message}` }],
1477
+ isError: true
1478
+ };
1479
+ }
1480
+ }
1005
1481
  case "kode_read_context": {
1006
1482
  const contextPath = getContextPath();
1007
1483
  if (!contextPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curenorway/kode-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "MCP server for Cure Kode - enables AI agents to manage Webflow scripts",
5
5
  "type": "module",
6
6
  "bin": {