@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 +10 -0
- package/dist/index.js +61 -28
- package/dist/index.js.map +1 -1
- package/dist/tools-manifest.json +2 -2
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
27844
|
-
|
|
27845
|
-
|
|
27846
|
-
|
|
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(
|
|
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
|
|
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(
|
|
29685
|
+
return realpathSync2(process.argv[1]) === realpathSync2(fileURLToPath3(import.meta.url));
|
|
29653
29686
|
} catch {
|
|
29654
29687
|
return false;
|
|
29655
29688
|
}
|