@hyperfixi/mcp-server 2.7.2 → 2.9.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 CHANGED
@@ -5,8 +5,109 @@ import {
5
5
  } from "./chunk-P76KH42G.js";
6
6
 
7
7
  // src/index.ts
8
+ import { createRequire as createRequire2 } from "module";
8
9
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
9
10
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+
12
+ // src/freshness.ts
13
+ import { createRequire } from "module";
14
+ import { statSync, existsSync, readdirSync } from "fs";
15
+ import { dirname, join } from "path";
16
+ import { fileURLToPath } from "url";
17
+ var require_ = createRequire(import.meta.url);
18
+ var WORKSPACE_SCOPES = ["@lokascript/", "@hyperfixi/"];
19
+ var PROBE_TTL_MS = 1e3;
20
+ var watched = [];
21
+ var lastProbeAt = 0;
22
+ var lastResult = [];
23
+ function watchedPackages() {
24
+ const pkg = require_("../package.json");
25
+ const names = /* @__PURE__ */ new Set([
26
+ ...Object.keys(pkg.dependencies ?? {}),
27
+ ...Object.keys(pkg.peerDependencies ?? {})
28
+ ]);
29
+ return [...names].filter((n) => WORKSPACE_SCOPES.some((s) => n.startsWith(s))).sort();
30
+ }
31
+ function resolveMarker(name) {
32
+ try {
33
+ const url = import.meta.resolve(name);
34
+ const path = fileURLToPath(url);
35
+ return existsSync(path) ? path : null;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+ function captureFreshnessBaseline() {
41
+ watched = watchedPackages().flatMap((pkg) => {
42
+ const marker = resolveMarker(pkg);
43
+ if (!marker) return [];
44
+ return [{ pkg, marker, loadedMtimeMs: statSync(marker).mtimeMs }];
45
+ });
46
+ lastProbeAt = 0;
47
+ lastResult = [];
48
+ }
49
+ function staleSinceStartup(now = Date.now()) {
50
+ if (now - lastProbeAt < PROBE_TTL_MS) return lastResult;
51
+ lastProbeAt = now;
52
+ lastResult = watched.filter((w) => {
53
+ try {
54
+ return statSync(w.marker).mtimeMs !== w.loadedMtimeMs;
55
+ } catch {
56
+ return true;
57
+ }
58
+ }).map((w) => w.pkg);
59
+ return lastResult;
60
+ }
61
+ function hasNewerTs(dir, builtAt) {
62
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
63
+ const p = join(dir, entry.name);
64
+ if (entry.isDirectory()) {
65
+ if (hasNewerTs(p, builtAt)) return true;
66
+ } else if (entry.name.endsWith(".ts") && statSync(p).mtimeMs > builtAt) {
67
+ return true;
68
+ }
69
+ }
70
+ return false;
71
+ }
72
+ function staleAtStartup() {
73
+ return watched.filter((w) => {
74
+ const srcDir = join(dirname(dirname(w.marker)), "src");
75
+ if (!existsSync(srcDir)) return false;
76
+ try {
77
+ return hasNewerTs(srcDir, statSync(w.marker).mtimeMs);
78
+ } catch {
79
+ return false;
80
+ }
81
+ }).map((w) => ({ pkg: w.pkg, dir: dirname(dirname(w.marker)) }));
82
+ }
83
+ function staleToolError(stale) {
84
+ return {
85
+ content: [
86
+ {
87
+ type: "text",
88
+ text: `\u2717 This MCP server is serving STALE code and refused the call.
89
+
90
+ Rebuilt since the server started: ${stale.join(", ")}
91
+
92
+ The server resolves these packages' dist/ at startup and Node caches the module
93
+ graph, so it cannot pick up a rebuild without restarting. Any answer it gave now
94
+ would reflect the code as it was BEFORE the rebuild \u2014 silently.
95
+
96
+ Fix: restart the MCP server (in Claude Code: /mcp, then reconnect "lokascript").
97
+
98
+ If you are mid-arc and only need to probe the parser, prefer a direct tsx probe
99
+ (which imports fresh each run) over this server \u2014 see the probe recipe in
100
+ docs-internal/HANDOFF_foreign-validity-burndown.md.`
101
+ }
102
+ ],
103
+ isError: true
104
+ };
105
+ }
106
+ function freshnessSummary() {
107
+ return `watching ${watched.length} workspace dist entries`;
108
+ }
109
+
110
+ // src/index.ts
10
111
  import {
11
112
  CallToolRequestSchema,
12
113
  GetPromptRequestSchema,
@@ -5877,7 +5978,7 @@ async function handleBDDMultiStep(name, args) {
5877
5978
  let rendered = null;
5878
5979
  if (renderer) {
5879
5980
  const renderedSteps = result.steps.map((step) => renderer(step, to));
5880
- rendered = renderedSteps.join("\n");
5981
+ rendered = renderedSteps.every((step) => step != null) ? renderedSteps.join("\n") : null;
5881
5982
  }
5882
5983
  return jsonResponse({
5883
5984
  input: { scenario, language: from },
@@ -5976,23 +6077,32 @@ async function handleBehaviorSpecMultiLine(name, args) {
5976
6077
  if (!from || !to) return errorResponse("Missing required parameter: from, to");
5977
6078
  const result = parseSpec(scenario, from);
5978
6079
  const renderedTests = [];
6080
+ let complete = true;
6081
+ const renderLine = (node, indent) => {
6082
+ const line = render(node, to);
6083
+ if (line == null) {
6084
+ complete = false;
6085
+ return;
6086
+ }
6087
+ lines.push(`${indent}${line}`);
6088
+ };
6089
+ let lines = [];
5979
6090
  for (const test of result.tests) {
5980
- const lines = [];
5981
- lines.push(`test "${test.name}"`);
6091
+ lines = [`test "${test.name}"`];
5982
6092
  for (const given of test.givens) {
5983
- lines.push(` ${render(given, to)}`);
6093
+ renderLine(given, " ");
5984
6094
  }
5985
6095
  for (const interaction of test.interactions) {
5986
- lines.push(` ${render(interaction.when, to)}`);
6096
+ renderLine(interaction.when, " ");
5987
6097
  for (const exp of interaction.expectations) {
5988
- lines.push(` ${render(exp.node, to)}`);
6098
+ renderLine(exp.node, " ");
5989
6099
  }
5990
6100
  }
5991
6101
  renderedTests.push(lines.join("\n"));
5992
6102
  }
5993
6103
  return jsonResponse({
5994
6104
  input: { scenario, language: from },
5995
- translated: renderedTests.join("\n\n"),
6105
+ translated: complete ? renderedTests.join("\n\n") : null,
5996
6106
  playwright: compile(scenario, from),
5997
6107
  errors: result.errors
5998
6108
  });
@@ -7977,7 +8087,7 @@ function createInlineExtractor() {
7977
8087
  const fs = await import("fs");
7978
8088
  const path = await import("path");
7979
8089
  const crypto = await import("crypto");
7980
- const { glob } = await import("./esm-YPGJSQ36.js");
8090
+ const { glob } = await import("./esm-7SV6EOOG.js");
7981
8091
  const globs = [
7982
8092
  "**/*.html",
7983
8093
  "**/*.htm",
@@ -10134,6 +10244,7 @@ function getLSEProtocolSpec() {
10134
10244
  | Disabled flag | \`~name\` | \`~nullable\` |
10135
10245
  | String literal | \`"..."\` or \`'...'\` | \`patient:"hello world"\` |
10136
10246
  | Selector | \`#id\` \`.class\` \`[attr]\` \`@aria\` \`*wild\` | \`destination:#button\` |
10247
+ | Selector literal | \`<...selector.../>\` | \`patient:<ul > li/>\`, \`patient:<.a, .b/>\` |
10137
10248
  | Boolean | \`true\` / \`false\` | \`goal:true\` |
10138
10249
  | Number | digits with optional decimal | \`quantity:5\` |
10139
10250
  | Duration | number + suffix | \`delay:500ms\`, \`timeout:2s\` |
@@ -10141,15 +10252,22 @@ function getLSEProtocolSpec() {
10141
10252
  | Nested body | bracket command in value | \`body:[toggle patient:.active]\` |
10142
10253
  | Compound | chain operator between commands | \`[add ...] then [fetch ...]\` |
10143
10254
 
10255
+ A selector containing a space, a combinator (\`>\` \`+\` \`~\`), or a comma must use a
10256
+ selector literal \u2014 the tokenizer would otherwise split it. Inside a structural role
10257
+ (\`body\`, \`then\`, \`else\`, \`condition\`, \`loop-body\`, \`variable\`, \`catch\`, \`finally\`)
10258
+ a \`[...]\` value is always a nested command, so an attribute selector there must also
10259
+ use the literal form: \`condition:<[data-active]/>\`.
10260
+
10144
10261
  ## Value Classification Priority (first match wins)
10145
10262
 
10146
- 1. **Selector** \u2014 starts with \`#\`, \`.\`, \`[\`, \`@\`, \`*\`
10147
- 2. **String** \u2014 starts with \`"\` or \`'\`
10148
- 3. **Boolean** \u2014 exact: \`true\` or \`false\`
10149
- 4. **Reference** \u2014 \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\`
10150
- 5. **Duration** \u2014 number + \`ms\`/\`s\`/\`m\`/\`h\`
10151
- 6. **Number** \u2014 integer or decimal
10152
- 7. **Plain** \u2014 fallback (any non-whitespace)
10263
+ 1. **Selector literal** \u2014 starts with \`<\` and ends with \`/>\`; delimiters stripped
10264
+ 2. **Selector** \u2014 starts with \`#\`, \`.\`, \`[\`, \`@\`, \`*\`
10265
+ 3. **String** \u2014 starts with \`"\` or \`'\`
10266
+ 4. **Boolean** \u2014 exact: \`true\` or \`false\`
10267
+ 5. **Reference** \u2014 \`me\`, \`you\`, \`it\`, \`result\`, \`event\`, \`target\`, \`body\`
10268
+ 6. **Duration** \u2014 number + \`ms\`/\`s\`/\`m\`/\`h\`
10269
+ 7. **Number** \u2014 integer or decimal
10270
+ 8. **Plain** \u2014 fallback (any non-whitespace)
10153
10271
 
10154
10272
  ## Node Kinds
10155
10273
 
@@ -10388,10 +10506,11 @@ function readResource(uri) {
10388
10506
 
10389
10507
  // src/index.ts
10390
10508
  var registry = createDomainRegistry();
10509
+ var { version: pkgVersion } = createRequire2(import.meta.url)("../package.json");
10391
10510
  var server = new Server(
10392
10511
  {
10393
10512
  name: "@hyperfixi/mcp-server",
10394
- version: "1.0.0"
10513
+ version: pkgVersion
10395
10514
  },
10396
10515
  {
10397
10516
  capabilities: {
@@ -10428,6 +10547,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
10428
10547
  });
10429
10548
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
10430
10549
  const { name, arguments: args } = request.params;
10550
+ const stale = staleSinceStartup();
10551
+ if (stale.length > 0) {
10552
+ return staleToolError(stale);
10553
+ }
10431
10554
  if (name.startsWith("analyze_") || name === "explain_code" || name === "recognize_intent") {
10432
10555
  return handleAnalysisTool(name, args);
10433
10556
  }
@@ -10484,7 +10607,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
10484
10607
  if (name === "lse_validate_and_feedback" || name === "lse_pattern_stats") {
10485
10608
  return handleFeedbackTool(name, args);
10486
10609
  }
10487
- if (name === "lse_from_hyperscript" || name === "lse_to_hyperscript") {
10610
+ if (name === "lse_from_hyperscript" || name === "lse_to_hyperscript" || name === "execute_lse" || name === "validate_lse" || name === "translate_lse") {
10488
10611
  return handleLsePipelineTool(name, args);
10489
10612
  }
10490
10613
  if (name.startsWith("grail_")) {
@@ -10529,9 +10652,19 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
10529
10652
  return readResource(uri);
10530
10653
  });
10531
10654
  async function main() {
10655
+ captureFreshnessBaseline();
10656
+ const bornStale = staleAtStartup();
10657
+ if (bornStale.length > 0) {
10658
+ console.error(
10659
+ `\u26A0 MCP server started against a STALE dist/ in: ${bornStale.map((s) => s.pkg).join(", ")} \u2014
10660
+ src/ is newer than the built output, so answers will reflect the last build, not the
10661
+ checkout. Rebuild, then restart this server:
10662
+ ` + bornStale.map((s) => ` npm run build --prefix ${s.dir}`).join("\n")
10663
+ );
10664
+ }
10532
10665
  const transport = new StdioServerTransport();
10533
10666
  await server.connect(transport);
10534
- console.error("HyperFixi MCP server started");
10667
+ console.error(`HyperFixi MCP server started (${freshnessSummary()})`);
10535
10668
  }
10536
10669
  main().catch((error) => {
10537
10670
  console.error("Failed to start MCP server:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperfixi/mcp-server",
3
- "version": "2.7.2",
3
+ "version": "2.9.0",
4
4
  "description": "Model Context Protocol server for HyperFixi hyperscript development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -37,31 +37,31 @@
37
37
  "author": "LokaScript Contributors",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "@hyperfixi/patterns-reference": "^2.7.2",
41
- "@lokascript/compilation-service": "^2.7.2",
42
- "@lokascript/domain-config": "^2.7.2",
43
- "@lokascript/framework": "^2.7.2",
44
- "@lokascript/planner": "^2.7.2",
45
- "@lokascript/semantic": "^2.7.2",
40
+ "@hyperfixi/patterns-reference": "^2.9.0",
41
+ "@lokascript/compilation-service": "^2.9.0",
42
+ "@lokascript/domain-config": "^2.9.0",
43
+ "@lokascript/framework": "^2.9.0",
44
+ "@lokascript/planner": "^2.9.0",
45
+ "@lokascript/semantic": "^2.9.0",
46
46
  "@modelcontextprotocol/sdk": "^1.25.0",
47
47
  "yaml": "^2.8.3"
48
48
  },
49
49
  "peerDependencies": {
50
- "@hyperfixi/core": "^2.7.2",
51
- "@hyperfixi/patterns-reference": "^2.7.2",
52
- "@hyperfixi/server-bridge": "^2.7.2",
53
- "@lokascript/compilation-service": "^2.7.2",
54
- "@lokascript/domain-bdd": "^2.7.2",
55
- "@lokascript/domain-behaviorspec": "^2.7.2",
56
- "@lokascript/domain-flow": "^2.7.2",
57
- "@lokascript/domain-jsx": "^2.7.2",
58
- "@lokascript/domain-learn": "^2.7.2",
59
- "@lokascript/domain-llm": "^2.7.2",
60
- "@lokascript/domain-sql": "^2.7.2",
61
- "@lokascript/domain-todo": "^2.7.2",
62
- "@lokascript/domain-voice": "^2.7.2",
63
- "@lokascript/framework": "^2.7.2",
64
- "@lokascript/semantic": "^2.7.2"
50
+ "@hyperfixi/core": "^2.9.0",
51
+ "@hyperfixi/patterns-reference": "^2.9.0",
52
+ "@hyperfixi/server-bridge": "^2.9.0",
53
+ "@lokascript/compilation-service": "^2.9.0",
54
+ "@lokascript/domain-bdd": "^2.9.0",
55
+ "@lokascript/domain-behaviorspec": "^2.9.0",
56
+ "@lokascript/domain-flow": "^2.9.0",
57
+ "@lokascript/domain-jsx": "^2.9.0",
58
+ "@lokascript/domain-learn": "^2.9.0",
59
+ "@lokascript/domain-llm": "^2.9.0",
60
+ "@lokascript/domain-sql": "^2.9.0",
61
+ "@lokascript/domain-todo": "^2.9.0",
62
+ "@lokascript/domain-voice": "^2.9.0",
63
+ "@lokascript/framework": "^2.9.0",
64
+ "@lokascript/semantic": "^2.9.0"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "@lokascript/compilation-service": {
@@ -111,29 +111,30 @@
111
111
  }
112
112
  },
113
113
  "devDependencies": {
114
- "@hyperfixi/core": "^2.7.2",
115
- "@hyperfixi/patterns-reference": "^2.7.2",
116
- "@hyperfixi/server-bridge": "^2.7.2",
117
- "@lokascript/compilation-service": "^2.7.2",
118
- "@lokascript/domain-bdd": "^2.7.2",
119
- "@lokascript/domain-behaviorspec": "^2.7.2",
120
- "@lokascript/domain-flow": "^2.7.2",
121
- "@lokascript/domain-jsx": "^2.7.2",
122
- "@lokascript/domain-learn": "^2.7.2",
123
- "@lokascript/domain-llm": "^2.7.2",
124
- "@lokascript/domain-sql": "^2.7.2",
125
- "@lokascript/domain-todo": "^2.7.2",
126
- "@lokascript/domain-voice": "^2.7.2",
127
- "@lokascript/framework": "^2.7.2",
128
- "@lokascript/semantic": "^2.7.2",
114
+ "@hyperfixi/core": "^2.9.0",
115
+ "@hyperfixi/patterns-reference": "^2.9.0",
116
+ "@hyperfixi/server-bridge": "^2.9.0",
117
+ "@lokascript/compilation-service": "^2.9.0",
118
+ "@lokascript/domain-bdd": "^2.9.0",
119
+ "@lokascript/domain-behaviorspec": "^2.9.0",
120
+ "@lokascript/domain-flow": "^2.9.0",
121
+ "@lokascript/domain-jsx": "^2.9.0",
122
+ "@lokascript/domain-learn": "^2.9.0",
123
+ "@lokascript/domain-llm": "^2.9.0",
124
+ "@lokascript/domain-sql": "^2.9.0",
125
+ "@lokascript/domain-todo": "^2.9.0",
126
+ "@lokascript/domain-voice": "^2.9.0",
127
+ "@lokascript/framework": "^2.9.0",
128
+ "@lokascript/semantic": "^2.9.0",
129
129
  "@types/node": "^20.0.0",
130
130
  "tsup": "^8.0.0",
131
- "tsx": "^4.0.0",
131
+ "tsx": "^4.23.1",
132
132
  "typescript": "^5.0.0",
133
133
  "vitest": "^4.1.5"
134
134
  },
135
135
  "files": [
136
- "dist"
136
+ "dist",
137
+ "CHANGELOG.md"
137
138
  ],
138
139
  "publishConfig": {
139
140
  "access": "public"