@ogygie/mcp 0.10.0 → 0.10.2
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/mcp.js +47 -22
- package/dist/server.js +47 -22
- package/package.json +2 -2
package/dist/mcp.js
CHANGED
|
@@ -823,7 +823,8 @@ function declaredEntries(manifest) {
|
|
|
823
823
|
name: feature.name,
|
|
824
824
|
status: feature.status,
|
|
825
825
|
folder: owningApp === void 0 ? null : `${owningApp.path}/${feature.folder}`,
|
|
826
|
-
specId: feature.name
|
|
826
|
+
specId: feature.name,
|
|
827
|
+
appType: owningApp?.type ?? null
|
|
827
828
|
});
|
|
828
829
|
}
|
|
829
830
|
for (const productPackage of manifest.packages ?? []) {
|
|
@@ -832,7 +833,8 @@ function declaredEntries(manifest) {
|
|
|
832
833
|
name: productPackage.name,
|
|
833
834
|
status: productPackage.status,
|
|
834
835
|
folder: productPackage.path,
|
|
835
|
-
specId: productPackage.name
|
|
836
|
+
specId: productPackage.name,
|
|
837
|
+
appType: null
|
|
836
838
|
});
|
|
837
839
|
}
|
|
838
840
|
for (const frameworkPackage of manifest.framework?.packages ?? []) {
|
|
@@ -842,7 +844,8 @@ function declaredEntries(manifest) {
|
|
|
842
844
|
status: frameworkPackage.status,
|
|
843
845
|
folder: frameworkPackage.path,
|
|
844
846
|
// A published npm name is not a spec id: the link is DECLARED.
|
|
845
|
-
specId: frameworkPackage.spec === null ? null : specIdOf(frameworkPackage.spec)
|
|
847
|
+
specId: frameworkPackage.spec === null ? null : specIdOf(frameworkPackage.spec),
|
|
848
|
+
appType: null
|
|
846
849
|
});
|
|
847
850
|
}
|
|
848
851
|
return entries;
|
|
@@ -1055,6 +1058,7 @@ async function buildReport(deps, entries, target) {
|
|
|
1055
1058
|
dependents.set(entry.name, accumulator);
|
|
1056
1059
|
}
|
|
1057
1060
|
if (isTest(file.path)) continue;
|
|
1061
|
+
if (entry.appType === "front" || entry.appType === "gateway") continue;
|
|
1058
1062
|
for (const table of ownedTables) {
|
|
1059
1063
|
if (!content.includes(`"${table}"`)) continue;
|
|
1060
1064
|
tableReadersOutsideOwner.push({ table, feature: entry.name, file: filePath });
|
|
@@ -1092,7 +1096,7 @@ async function buildReport(deps, entries, target) {
|
|
|
1092
1096
|
scope: {
|
|
1093
1097
|
scannedFolders: scannedFolders.sort(),
|
|
1094
1098
|
coveredKinds: [...new Set(entries.map((entry) => entry.kind))],
|
|
1095
|
-
outOfReach: [...outOfReach, ...outOfReachOf(target)]
|
|
1099
|
+
outOfReach: [...outOfReach, ...outOfReachOf(target, entries)]
|
|
1096
1100
|
}
|
|
1097
1101
|
};
|
|
1098
1102
|
return ImpactReportSchema.parse(report);
|
|
@@ -1105,11 +1109,19 @@ function tablesOf(manifest, featureName) {
|
|
|
1105
1109
|
const feature = manifest.features.find((candidate) => candidate.name === featureName);
|
|
1106
1110
|
return feature === void 0 ? [] : dbTableNamesOf(feature);
|
|
1107
1111
|
}
|
|
1108
|
-
function outOfReachOf(target) {
|
|
1112
|
+
function outOfReachOf(target, entries) {
|
|
1109
1113
|
const limits = [
|
|
1110
1114
|
"code that no manifest entry declares \u2014 the map is the only chart",
|
|
1111
1115
|
"anything outside this repo: no network, no npm registry"
|
|
1112
1116
|
];
|
|
1117
|
+
const spared = entries.filter(
|
|
1118
|
+
(entry) => entry.appType === "front" || entry.appType === "gateway"
|
|
1119
|
+
);
|
|
1120
|
+
if (spared.length > 0 && target.kind === "feature") {
|
|
1121
|
+
limits.push(
|
|
1122
|
+
`table ownership was NOT checked in ${spared.length} front/gateway entr${spared.length === 1 ? "y" : "ies"} \u2014 that code holds no database client, so a table name there is a query key or a route`
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1113
1125
|
if (target.kind === "framework-package" || target.kind === "package") {
|
|
1114
1126
|
limits.unshift(
|
|
1115
1127
|
`consumers of "${target.name}" OUTSIDE this repo \u2014 it is a package, and a published package is imported by projects this analysis cannot see`
|
|
@@ -1488,11 +1500,18 @@ function refuse(response, status, error) {
|
|
|
1488
1500
|
response.writeHead(status, { "content-type": "application/json" });
|
|
1489
1501
|
response.end(JSON.stringify(error));
|
|
1490
1502
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1503
|
+
var SECRET_PATH_PREFIX = "/mcp-";
|
|
1504
|
+
function isAuthorized(authorization, expected, requestPath) {
|
|
1505
|
+
if (authorization !== void 0) {
|
|
1506
|
+
const [scheme, value] = authorization.split(" ");
|
|
1507
|
+
if (scheme?.toLowerCase() === "bearer" && value !== void 0) {
|
|
1508
|
+
if (tokenMatches(expected, value)) return true;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
if (requestPath === void 0) return false;
|
|
1512
|
+
const [pathOnly] = requestPath.split("?");
|
|
1513
|
+
if (pathOnly === void 0 || !pathOnly.startsWith(SECRET_PATH_PREFIX)) return false;
|
|
1514
|
+
return tokenMatches(expected, pathOnly.slice(SECRET_PATH_PREFIX.length));
|
|
1496
1515
|
}
|
|
1497
1516
|
async function startHttpTransport(createServerInstance, options) {
|
|
1498
1517
|
const token = assertUsableToken(options.token);
|
|
@@ -1507,18 +1526,24 @@ async function startHttpTransport(createServerInstance, options) {
|
|
|
1507
1526
|
await transport.handleRequest(request, response, body);
|
|
1508
1527
|
};
|
|
1509
1528
|
const httpServer = createServer((request, response) => {
|
|
1510
|
-
if (!isAuthorized(request.headers.authorization, token)) {
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1529
|
+
if (!isAuthorized(request.headers.authorization, token, request.url)) {
|
|
1530
|
+
const announcedCredential = request.headers.authorization !== void 0;
|
|
1531
|
+
if (announcedCredential) {
|
|
1532
|
+
refuse(
|
|
1533
|
+
response,
|
|
1534
|
+
401,
|
|
1535
|
+
ogygieError({
|
|
1536
|
+
code: "UNAUTHORIZED",
|
|
1537
|
+
message: "This Ogygie MCP server refused the credential it was given.",
|
|
1538
|
+
file: null,
|
|
1539
|
+
cause: "the Authorization header does not carry the expected bearer token",
|
|
1540
|
+
suggestedFix: "Send `Authorization: Bearer <OGYGIE_MCP_TOKEN>`, or address the secret path `/mcp-<OGYGIE_MCP_TOKEN>`."
|
|
1541
|
+
})
|
|
1542
|
+
);
|
|
1543
|
+
return;
|
|
1544
|
+
}
|
|
1545
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
1546
|
+
response.end(JSON.stringify({ error: "not found" }));
|
|
1522
1547
|
return;
|
|
1523
1548
|
}
|
|
1524
1549
|
const chunks = [];
|
package/dist/server.js
CHANGED
|
@@ -818,7 +818,8 @@ function declaredEntries(manifest) {
|
|
|
818
818
|
name: feature.name,
|
|
819
819
|
status: feature.status,
|
|
820
820
|
folder: owningApp === void 0 ? null : `${owningApp.path}/${feature.folder}`,
|
|
821
|
-
specId: feature.name
|
|
821
|
+
specId: feature.name,
|
|
822
|
+
appType: owningApp?.type ?? null
|
|
822
823
|
});
|
|
823
824
|
}
|
|
824
825
|
for (const productPackage of manifest.packages ?? []) {
|
|
@@ -827,7 +828,8 @@ function declaredEntries(manifest) {
|
|
|
827
828
|
name: productPackage.name,
|
|
828
829
|
status: productPackage.status,
|
|
829
830
|
folder: productPackage.path,
|
|
830
|
-
specId: productPackage.name
|
|
831
|
+
specId: productPackage.name,
|
|
832
|
+
appType: null
|
|
831
833
|
});
|
|
832
834
|
}
|
|
833
835
|
for (const frameworkPackage of manifest.framework?.packages ?? []) {
|
|
@@ -837,7 +839,8 @@ function declaredEntries(manifest) {
|
|
|
837
839
|
status: frameworkPackage.status,
|
|
838
840
|
folder: frameworkPackage.path,
|
|
839
841
|
// A published npm name is not a spec id: the link is DECLARED.
|
|
840
|
-
specId: frameworkPackage.spec === null ? null : specIdOf(frameworkPackage.spec)
|
|
842
|
+
specId: frameworkPackage.spec === null ? null : specIdOf(frameworkPackage.spec),
|
|
843
|
+
appType: null
|
|
841
844
|
});
|
|
842
845
|
}
|
|
843
846
|
return entries;
|
|
@@ -1050,6 +1053,7 @@ async function buildReport(deps, entries, target) {
|
|
|
1050
1053
|
dependents.set(entry.name, accumulator);
|
|
1051
1054
|
}
|
|
1052
1055
|
if (isTest(file.path)) continue;
|
|
1056
|
+
if (entry.appType === "front" || entry.appType === "gateway") continue;
|
|
1053
1057
|
for (const table of ownedTables) {
|
|
1054
1058
|
if (!content.includes(`"${table}"`)) continue;
|
|
1055
1059
|
tableReadersOutsideOwner.push({ table, feature: entry.name, file: filePath });
|
|
@@ -1087,7 +1091,7 @@ async function buildReport(deps, entries, target) {
|
|
|
1087
1091
|
scope: {
|
|
1088
1092
|
scannedFolders: scannedFolders.sort(),
|
|
1089
1093
|
coveredKinds: [...new Set(entries.map((entry) => entry.kind))],
|
|
1090
|
-
outOfReach: [...outOfReach, ...outOfReachOf(target)]
|
|
1094
|
+
outOfReach: [...outOfReach, ...outOfReachOf(target, entries)]
|
|
1091
1095
|
}
|
|
1092
1096
|
};
|
|
1093
1097
|
return ImpactReportSchema.parse(report);
|
|
@@ -1100,11 +1104,19 @@ function tablesOf(manifest, featureName) {
|
|
|
1100
1104
|
const feature = manifest.features.find((candidate) => candidate.name === featureName);
|
|
1101
1105
|
return feature === void 0 ? [] : dbTableNamesOf(feature);
|
|
1102
1106
|
}
|
|
1103
|
-
function outOfReachOf(target) {
|
|
1107
|
+
function outOfReachOf(target, entries) {
|
|
1104
1108
|
const limits = [
|
|
1105
1109
|
"code that no manifest entry declares \u2014 the map is the only chart",
|
|
1106
1110
|
"anything outside this repo: no network, no npm registry"
|
|
1107
1111
|
];
|
|
1112
|
+
const spared = entries.filter(
|
|
1113
|
+
(entry) => entry.appType === "front" || entry.appType === "gateway"
|
|
1114
|
+
);
|
|
1115
|
+
if (spared.length > 0 && target.kind === "feature") {
|
|
1116
|
+
limits.push(
|
|
1117
|
+
`table ownership was NOT checked in ${spared.length} front/gateway entr${spared.length === 1 ? "y" : "ies"} \u2014 that code holds no database client, so a table name there is a query key or a route`
|
|
1118
|
+
);
|
|
1119
|
+
}
|
|
1108
1120
|
if (target.kind === "framework-package" || target.kind === "package") {
|
|
1109
1121
|
limits.unshift(
|
|
1110
1122
|
`consumers of "${target.name}" OUTSIDE this repo \u2014 it is a package, and a published package is imported by projects this analysis cannot see`
|
|
@@ -1483,11 +1495,18 @@ function refuse(response, status, error) {
|
|
|
1483
1495
|
response.writeHead(status, { "content-type": "application/json" });
|
|
1484
1496
|
response.end(JSON.stringify(error));
|
|
1485
1497
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1498
|
+
var SECRET_PATH_PREFIX = "/mcp-";
|
|
1499
|
+
function isAuthorized(authorization, expected, requestPath) {
|
|
1500
|
+
if (authorization !== void 0) {
|
|
1501
|
+
const [scheme, value] = authorization.split(" ");
|
|
1502
|
+
if (scheme?.toLowerCase() === "bearer" && value !== void 0) {
|
|
1503
|
+
if (tokenMatches(expected, value)) return true;
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
if (requestPath === void 0) return false;
|
|
1507
|
+
const [pathOnly] = requestPath.split("?");
|
|
1508
|
+
if (pathOnly === void 0 || !pathOnly.startsWith(SECRET_PATH_PREFIX)) return false;
|
|
1509
|
+
return tokenMatches(expected, pathOnly.slice(SECRET_PATH_PREFIX.length));
|
|
1491
1510
|
}
|
|
1492
1511
|
async function startHttpTransport(createServerInstance, options) {
|
|
1493
1512
|
const token = assertUsableToken(options.token);
|
|
@@ -1502,18 +1521,24 @@ async function startHttpTransport(createServerInstance, options) {
|
|
|
1502
1521
|
await transport.handleRequest(request, response, body);
|
|
1503
1522
|
};
|
|
1504
1523
|
const httpServer = createServer((request, response) => {
|
|
1505
|
-
if (!isAuthorized(request.headers.authorization, token)) {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1524
|
+
if (!isAuthorized(request.headers.authorization, token, request.url)) {
|
|
1525
|
+
const announcedCredential = request.headers.authorization !== void 0;
|
|
1526
|
+
if (announcedCredential) {
|
|
1527
|
+
refuse(
|
|
1528
|
+
response,
|
|
1529
|
+
401,
|
|
1530
|
+
ogygieError({
|
|
1531
|
+
code: "UNAUTHORIZED",
|
|
1532
|
+
message: "This Ogygie MCP server refused the credential it was given.",
|
|
1533
|
+
file: null,
|
|
1534
|
+
cause: "the Authorization header does not carry the expected bearer token",
|
|
1535
|
+
suggestedFix: "Send `Authorization: Bearer <OGYGIE_MCP_TOKEN>`, or address the secret path `/mcp-<OGYGIE_MCP_TOKEN>`."
|
|
1536
|
+
})
|
|
1537
|
+
);
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
response.writeHead(404, { "content-type": "application/json" });
|
|
1541
|
+
response.end(JSON.stringify({ error: "not found" }));
|
|
1517
1542
|
return;
|
|
1518
1543
|
}
|
|
1519
1544
|
const chunks = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ogygie/mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "Ogygie MCP server: read-only access to the governance files and features of any Ogygie repo, the manifest as index. Ships built; runnable standalone or through `ogygie mcp`.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"tsx": "^4.20.0",
|
|
29
29
|
"typescript": "^5.8.0",
|
|
30
30
|
"vitest": "^3.2.0",
|
|
31
|
-
"@ogygie/core": "0.10.
|
|
31
|
+
"@ogygie/core": "0.10.2"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"typecheck": "tsc -p tsconfig.json",
|