bc-observatory 0.4.0 → 0.5.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/server.js +38 -7
  2. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -393,9 +393,10 @@ function normOf(r) {
393
393
  const symbol = r.kind !== "page";
394
394
  const main = /* @__PURE__ */ new Set([...indexTokens(r.name, symbol), ...indexTokens(r.title, symbol), ...r.caption ? indexTokens(r.caption) : []]);
395
395
  const own = r.type === "topic" ? new Set(tokenize(r.title)) : null;
396
- const tags = (r.tags ?? []).flatMap((t) => indexTokens(t)).filter((t) => !own?.has(t));
396
+ const learn = r.type === "learn";
397
+ const tags = (r.tags ?? []).flatMap((t) => indexTokens(t, learn)).filter((t) => !own?.has(t));
397
398
  const eqs = r.type === "object" || symbol ? [tokenize(r.name), ...r.caption ? [tokenize(r.caption)] : []] : [tokenize(r.title)];
398
- const n = { main: [...main], tags: [...new Set(tags)], text: symbol ? null : indexTokens(r.text ?? ""), eqs };
399
+ const n = { main: [...main], tags: learn ? tags : [...new Set(tags)], text: symbol ? null : indexTokens(r.text ?? ""), eqs };
399
400
  NORM.set(r, n);
400
401
  return n;
401
402
  }
@@ -783,6 +784,26 @@ function hints(index2, q, hits) {
783
784
  return out;
784
785
  }
785
786
 
787
+ // ../search/src/learn.ts
788
+ function learnRecords(doc) {
789
+ return doc.rows.map(([path, title, summary, date, h, system, ids]) => {
790
+ const hub = h >= 0 ? doc.hubs[h] : void 0;
791
+ const sys = system ?? hub?.[2] ?? null;
792
+ return {
793
+ path: /^https?:\/\//.test(path) ? path : `${doc.base}${path}`,
794
+ type: "learn",
795
+ title,
796
+ summary,
797
+ tier: "official",
798
+ ...sys ? { system: sys } : {},
799
+ ...date ? { date } : {},
800
+ ...ids?.length ? { tags: ids } : {},
801
+ ...hub?.[1] ? { path_label: hub[1] } : {},
802
+ ...hub?.[0] ? { hub: hub[0] } : {}
803
+ };
804
+ });
805
+ }
806
+
786
807
  // src/embed.ts
787
808
  var MAX_TOKENS = 512;
788
809
  function parseModel(tokenizerJson, safetensors, config = {}) {
@@ -1243,7 +1264,7 @@ ${pageView(md, clean, {}, budget, site)}`;
1243
1264
  }
1244
1265
 
1245
1266
  // src/server.ts
1246
- var VERSION = "0.4.0";
1267
+ var VERSION = "0.5.0";
1247
1268
  var SITE = (process.env.BC_OBSERVATORY_SITE ?? "https://waldo1001.github.io/waldo.BCObservatory/").replace(/\/?$/, "/");
1248
1269
  var LOCAL = process.env.BC_OBSERVATORY_LOCAL ? resolve(process.env.BC_OBSERVATORY_LOCAL) : null;
1249
1270
  var BUDGET = budgetOf(process.env.BC_OBSERVATORY_MAX_CHARS);
@@ -1287,6 +1308,12 @@ async function loadIndex() {
1287
1308
  for (const s of manifest.shards) all.push(...JSON.parse(await shard(s.file, s.sha256)));
1288
1309
  pages = all;
1289
1310
  const idx = prepare(all.map(pageToRecord));
1311
+ if (manifest.learn) {
1312
+ try {
1313
+ prepare(learnRecords(JSON.parse(await shard(manifest.learn.file, manifest.learn.sha256))).map(pageToRecord), idx);
1314
+ } catch {
1315
+ }
1316
+ }
1290
1317
  let sm = null;
1291
1318
  try {
1292
1319
  sm = JSON.parse(await dataFile("index/symbols-manifest.json"));
@@ -1314,7 +1341,10 @@ var line = (r, tag = "") => `- ${r.title}${r.caption ? ` (captioned "${r.caption
1314
1341
  var text = (t) => ({ content: [{ type: "text", text: t }] });
1315
1342
  var symbolHit = (r, tag = "") => `- ${r.name} (${symbolLine(r).replace(/ · /g, ", ")}) path=${r.id}${tag}${r.text ? `
1316
1343
  ${r.text}` : ""}`;
1317
- var hitLine = (r, tag = "") => r.kind === "page" && r.page ? line(r.page, tag) : symbolHit(r, tag);
1344
+ var learnLine = (r, tag = "") => `- ${r.title} [learn, official${r.date ? `, ${r.date}` : ""}] url=${r.path}${tag}${r.path_label ? `
1345
+ in: ${r.path_label}${r.hub ? ` (hub: ${r.hub})` : ""}` : ""}
1346
+ ${r.summary}`;
1347
+ var hitLine = (r, tag = "") => r.kind === "page" && r.page ? r.type === "learn" ? learnLine(r.page, tag) : line(r.page, tag) : symbolHit(r, tag);
1318
1348
  var MODEL = {
1319
1349
  repo: "minishlab/potion-base-8M",
1320
1350
  revision: "bf8b056651a2c21b8d2565580b8569da283cab23",
@@ -1414,7 +1444,8 @@ async function toolSearch(a) {
1414
1444
  ${hs.map((h) => `Hint: ${h.text}${h.query ? ` (search "${h.query}")` : h.path ? ` (cat("${h.path}"))` : ""}`).join("\n")}` : "";
1415
1445
  if (!shown.length) return `No pages match "${a.query}". Try fewer words, or ls("") to browse sections.${note}${hintText}`;
1416
1446
  const how = semantic.length && keyword.length && mode === "hybrid" ? "keyword + meaning" : mode === "semantic" && semantic.length ? "meaning" : "keyword";
1417
- return `${shown.length} results, ${how}${note}${how === "keyword + meaning" || mode === "hybrid" ? ` (exact band: ${exactBand})` : ""} (read one with cat(path)):
1447
+ const learnNote = shown.some((r) => r.type === "learn") ? "; a Learn article's url opens on learn.microsoft.com, cat(hub) reads its hub" : "";
1448
+ return `${shown.length} results, ${how}${note}${how === "keyword + meaning" || mode === "hybrid" ? ` (exact band: ${exactBand})` : ""} (read one with cat(path)${learnNote}):
1418
1449
  ${shown.map((r) => hitLine(r, tag(r.id))).join("\n")}${hintText}`;
1419
1450
  }
1420
1451
  async function toolLs(a) {
@@ -1535,13 +1566,13 @@ ${a.message}
1535
1566
  var VIEW = { section: z.string().optional(), offset: z.number().int().min(0).max(1e5).optional(), limit: z.number().int().min(1).max(5e3).optional() };
1536
1567
  function createServer() {
1537
1568
  const server = new McpServer({ name: "bc-observatory", version: VERSION }, {
1538
- instructions: "BC Observatory: an agent-first knowledge base of Microsoft Dynamics 365 Business Central (Learn hubs, AL objects from the code for BC28-30, localizations, roadmap features, videos, community posts, code changes: merged pull requests of Microsoft's Business Central repositories, BCApps joined to the AL objects they changed, under changes/, and AL Language extension releases (the marketplace changelog, under releases/)). Every page carries a trust tier (official = Microsoft, community = everyone else) and a review state: say which tier a claim comes from. Never invent AL object ids or version numbers: look them up with get_object. Start with search(), read pages with cat(path), browse with ls(path)."
1569
+ instructions: "BC Observatory: an agent-first knowledge base of Microsoft Dynamics 365 Business Central (Learn hubs, AL objects from the code for BC28-30, localizations, roadmap features, videos, community posts, code changes: merged pull requests of Microsoft's Business Central repositories, BCApps joined to the AL objects they changed, under changes/, and AL Language extension releases (the marketplace changelog, under releases/)). Every page carries a trust tier (official = Microsoft, community = everyone else) and a review state: say which tier a claim comes from. Never invent AL object ids or version numbers: look them up with get_object. Start with search(), read pages with cat(path), browse with ls(path). search() also finds Microsoft Learn articles by title, summary and the identifiers they document (type learn): those are not pages here, open their url on learn.microsoft.com."
1539
1570
  });
1540
1571
  server.registerTool(
1541
1572
  "search",
1542
1573
  {
1543
1574
  title: "Search the knowledge base",
1544
- description: "Search every page, and the fields, published events, global procedures and enum values of AL objects, ranked like the site's search. Takes names ('Sales Header'), references ('t36', 'cu 80', 'table 11300 BE'), bare ids ('36') and symbol prefixes ('event: OnAfterPost', 'field: Posting Date', 'proc: CopyToTempLines'); a symbol hit's path ends in its anchor (#event-<Name>, #field-<id>, #proc-<Name>, #value-<ordinal>); cat(path) reads that member alone. Hybrid by default: exact matches stay on top and the rest is fused with a meaning ranking; each hit says [keyword], [meaning] or [both]; mode 'keyword' or 'semantic' for one list. Filters: type (topic, app, feature, object, localization, video, post, change, source, digest, release), tier (official, community), system (finance, sales, ... or an alias such as 'g/l'), country ('BE'), app ('Subscription Billing'), object_type ('table'), kind (page, field, event, proc, value).",
1575
+ description: "Search every page, and the fields, published events, global procedures and enum values of AL objects, ranked like the site's search. Takes names ('Sales Header'), references ('t36', 'cu 80', 'table 11300 BE'), bare ids ('36') and symbol prefixes ('event: OnAfterPost', 'field: Posting Date', 'proc: CopyToTempLines'); a symbol hit's path ends in its anchor (#event-<Name>, #field-<id>, #proc-<Name>, #value-<ordinal>); cat(path) reads that member alone. Learn articles too: type learn, a url, not a path. Hybrid by default: exact matches stay on top and the rest is fused with a meaning ranking; each hit says [keyword], [meaning] or [both]; mode 'keyword' or 'semantic' for one list. Filters: type (topic, learn, app, feature, object, localization, video, post, change, source, digest, release), tier (official, community), system (finance, sales, ... or an alias such as 'g/l'), country ('BE'), app ('Subscription Billing'), object_type ('table'), kind (page, field, event, proc, value).",
1545
1576
  inputSchema: { query: z.string(), type: z.string().optional(), tier: z.string().optional(), system: z.string().optional(), country: z.string().optional(), app: z.string().optional(), object_type: z.string().optional(), kind: z.enum(["page", "field", "event", "proc", "value"]).optional(), limit: z.number().int().min(1).max(50).optional(), mode: z.enum(["hybrid", "keyword", "semantic"]).optional() }
1546
1577
  },
1547
1578
  async (a) => text(await toolSearch(a))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bc-observatory",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "MCP server for BC Observatory: an agent-first, cross-referenced knowledge base of Microsoft Dynamics 365 Business Central (Learn, code, roadmap, videos, community blogs).",
5
5
  "type": "module",
6
6
  "bin": {