@unified-product-graph/mcp-server 0.8.4 → 0.8.5

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/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to `@unified-product-graph/mcp-server` are documented in this file. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
4
4
 
5
+ ## [0.8.5] - 2026-06-02
6
+
7
+ Field-report fast-follow (tester report on 0.8.4).
8
+
9
+ ### Fixed
10
+ - `skill_audit` no longer false-reports skills as out of sync on npm/npx installs. It resolved the canonical source from `process.cwd()/packages/upg-mcp-server/skills` — a monorepo-only path absent in a user's project — so every deployed skill came back unverifiable. It now resolves the skills bundled in the installed package (relative to the module), and treats a symlink to a byte-identical bundle, or a matching copy, as healthy: content match, not deployment method, is the signal.
11
+
12
+ ### Changed
13
+ - The `prioritise` `type_mismatch` hint now points to the framework_exercise escape hatch (`apply_framework` / `upg apply`, then prioritise with `exercise_id`), so scoring a non-target entity type is discoverable.
14
+
5
15
  ## [0.8.4] - 2026-06-02
6
16
 
7
17
  Framework exercises, with the 0.8.3 launch fix folded in.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { UPGFileStore } from "@unified-product-graph/sdk";
10
10
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
11
11
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
12
  import fs2 from "fs";
13
- import { fileURLToPath } from "url";
13
+ import { fileURLToPath as fileURLToPath2 } from "url";
14
14
  import * as path5 from "path";
15
15
  import {
16
16
  CallToolRequestSchema,
@@ -27778,7 +27778,8 @@ var pushToCloud = async (args, ctx) => {
27778
27778
 
27779
27779
  // src/tools/skills.ts
27780
27780
  import { existsSync as existsSync2, lstatSync, readlinkSync, readFileSync as readFileSync2, readdirSync as readdirSync2, realpathSync } from "fs";
27781
- import { join as join5, resolve as resolve2 } from "path";
27781
+ import { join as join5, resolve as resolve2, dirname as dirname3 } from "path";
27782
+ import { fileURLToPath } from "url";
27782
27783
  function repoRoot() {
27783
27784
  return process.cwd();
27784
27785
  }
@@ -27789,8 +27790,40 @@ function canonicalisePath(p) {
27789
27790
  return p;
27790
27791
  }
27791
27792
  }
27793
+ function isSkillsDir(candidate) {
27794
+ try {
27795
+ if (!existsSync2(candidate)) return false;
27796
+ return readdirSync2(candidate, { withFileTypes: true }).some(
27797
+ (e) => (e.isDirectory() || e.isSymbolicLink()) && existsSync2(join5(candidate, e.name, "SKILL.md"))
27798
+ );
27799
+ } catch {
27800
+ return false;
27801
+ }
27802
+ }
27803
+ function resolveBundledSkillsDir() {
27804
+ let md;
27805
+ try {
27806
+ md = dirname3(fileURLToPath(import.meta.url));
27807
+ } catch {
27808
+ md = process.cwd();
27809
+ }
27810
+ for (const c of [resolve2(md, "..", "skills"), resolve2(md, "..", "..", "skills"), resolve2(md, "skills")]) {
27811
+ if (isSkillsDir(c)) return c;
27812
+ }
27813
+ let dir = md;
27814
+ for (let i = 0; i < 12; i++) {
27815
+ const mono = join5(dir, "packages", "upg-mcp-server", "skills");
27816
+ if (isSkillsDir(mono)) return mono;
27817
+ const parent = dirname3(dir);
27818
+ if (parent === dir) break;
27819
+ dir = parent;
27820
+ }
27821
+ return null;
27822
+ }
27792
27823
  function sourceSkillsDir() {
27793
- return resolve2(repoRoot(), "packages/upg-mcp-server/skills");
27824
+ const cwdPath = resolve2(repoRoot(), "packages/upg-mcp-server/skills");
27825
+ if (existsSync2(cwdPath)) return cwdPath;
27826
+ return resolveBundledSkillsDir() ?? cwdPath;
27794
27827
  }
27795
27828
  function deployedSkillsDir() {
27796
27829
  return resolve2(repoRoot(), ".claude/skills");
@@ -27833,6 +27866,21 @@ function auditOne(name) {
27833
27866
  const deployedExists = existsSync2(deployedPath);
27834
27867
  if (!sourceExists) issues.push("Canonical source SKILL.md is missing");
27835
27868
  if (!deployedExists) issues.push("Deployed SKILL.md is missing; run ./scripts/link-skills.sh");
27869
+ let inSync = false;
27870
+ let deployedFrontmatter = null;
27871
+ let deployedFirstHeading = null;
27872
+ if (deployedExists) {
27873
+ const deployedBody = readFileSync2(deployedPath, "utf8");
27874
+ deployedFrontmatter = parseFrontmatter(deployedBody);
27875
+ deployedFirstHeading = firstHeading(deployedBody);
27876
+ if (sourceExists) {
27877
+ const sourceBody = readFileSync2(sourcePath, "utf8");
27878
+ inSync = deployedBody === sourceBody;
27879
+ if (!inSync) {
27880
+ issues.push("Deployed SKILL.md differs from canonical source; symlink is stale or broken");
27881
+ }
27882
+ }
27883
+ }
27836
27884
  let isSymlink = false;
27837
27885
  let symlinkTarget = null;
27838
27886
  if (existsSync2(deployedDir)) {
@@ -27840,32 +27888,17 @@ function auditOne(name) {
27840
27888
  isSymlink = stat.isSymbolicLink();
27841
27889
  if (isSymlink) {
27842
27890
  symlinkTarget = readlinkSync(deployedDir);
27843
- const targetReal = canonicalisePath(symlinkTarget);
27844
- const expectedReal = canonicalisePath(sourceDir);
27845
- if (targetReal !== expectedReal) {
27846
- issues.push(`Symlink points to ${symlinkTarget}, expected ${sourceDir}`);
27891
+ if (!inSync && sourceExists) {
27892
+ const targetReal = canonicalisePath(symlinkTarget);
27893
+ const expectedReal = canonicalisePath(sourceDir);
27894
+ if (targetReal !== expectedReal) {
27895
+ issues.push(`Symlink points to ${symlinkTarget}, expected ${sourceDir}`);
27896
+ }
27847
27897
  }
27848
- } else if (deployedExists) {
27898
+ } else if (deployedExists && !inSync) {
27849
27899
  issues.push("Deployed entry is a real directory, not a symlink; stale copy will not pick up source updates; run ./scripts/link-skills.sh");
27850
27900
  }
27851
27901
  }
27852
- let inSync = false;
27853
- let deployedFrontmatter = null;
27854
- let deployedFirstHeading = null;
27855
- if (deployedExists && sourceExists) {
27856
- const deployedBody = readFileSync2(deployedPath, "utf8");
27857
- const sourceBody = readFileSync2(sourcePath, "utf8");
27858
- inSync = deployedBody === sourceBody;
27859
- deployedFrontmatter = parseFrontmatter(deployedBody);
27860
- deployedFirstHeading = firstHeading(deployedBody);
27861
- if (!inSync) {
27862
- issues.push("Deployed SKILL.md differs from canonical source; symlink is stale or broken");
27863
- }
27864
- } else if (deployedExists) {
27865
- const deployedBody = readFileSync2(deployedPath, "utf8");
27866
- deployedFrontmatter = parseFrontmatter(deployedBody);
27867
- deployedFirstHeading = firstHeading(deployedBody);
27868
- }
27869
27902
  return {
27870
27903
  name,
27871
27904
  deployed_path: deployedPath,
@@ -29417,7 +29450,7 @@ var SERVER_INSTRUCTIONS = [
29417
29450
  ].join("\n");
29418
29451
  function resolvePackageVersion() {
29419
29452
  try {
29420
- const here = path5.dirname(fileURLToPath(import.meta.url));
29453
+ const here = path5.dirname(fileURLToPath2(import.meta.url));
29421
29454
  const pkgPath = path5.resolve(here, "..", "package.json");
29422
29455
  const raw = fs2.readFileSync(pkgPath, "utf-8");
29423
29456
  const pkg = JSON.parse(raw);
@@ -29475,7 +29508,7 @@ function createServer(store) {
29475
29508
 
29476
29509
  // src/index.ts
29477
29510
  import { nanoid } from "nanoid";
29478
- import { fileURLToPath as fileURLToPath2 } from "url";
29511
+ import { fileURLToPath as fileURLToPath3 } from "url";
29479
29512
  import { realpathSync as realpathSync2 } from "fs";
29480
29513
  async function discoverUPGFile(explicitFile) {
29481
29514
  if (explicitFile) return path6.resolve(explicitFile);
@@ -29649,7 +29682,7 @@ ${lines.join("\n")}
29649
29682
  function isEntrypoint() {
29650
29683
  if (!process.argv[1]) return false;
29651
29684
  try {
29652
- return realpathSync2(process.argv[1]) === realpathSync2(fileURLToPath2(import.meta.url));
29685
+ return realpathSync2(process.argv[1]) === realpathSync2(fileURLToPath3(import.meta.url));
29653
29686
  } catch {
29654
29687
  return false;
29655
29688
  }