@get-technology-inc/jamf-docs-mcp-server 5.8.0 → 5.10.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/README.md +99 -0
- package/dist/core/apps/generated/app-html.d.ts +2 -2
- package/dist/core/apps/generated/app-html.d.ts.map +1 -1
- package/dist/core/apps/generated/app-html.js +2 -2
- package/dist/core/apps/generated/app-html.js.map +1 -1
- package/dist/core/apps/index.d.ts +1 -1
- package/dist/core/apps/index.d.ts.map +1 -1
- package/dist/core/apps/index.js +35 -0
- package/dist/core/apps/index.js.map +1 -1
- package/dist/core/schemas/output.d.ts +5 -0
- package/dist/core/schemas/output.d.ts.map +1 -1
- package/dist/core/schemas/output.js +15 -0
- package/dist/core/schemas/output.js.map +1 -1
- package/dist/core/services/article-service.d.ts.map +1 -1
- package/dist/core/services/article-service.js +21 -1
- package/dist/core/services/article-service.js.map +1 -1
- package/dist/core/services/cache-key.d.ts +6 -1
- package/dist/core/services/cache-key.d.ts.map +1 -1
- package/dist/core/services/cache-key.js +2 -1
- package/dist/core/services/cache-key.js.map +1 -1
- package/dist/core/services/content-parser.d.ts.map +1 -1
- package/dist/core/services/content-parser.js +74 -0
- package/dist/core/services/content-parser.js.map +1 -1
- package/dist/core/services/ft-internal-link.d.ts +22 -0
- package/dist/core/services/ft-internal-link.d.ts.map +1 -1
- package/dist/core/services/ft-internal-link.js +132 -4
- package/dist/core/services/ft-internal-link.js.map +1 -1
- package/dist/core/services/static-search-service.d.ts +40 -0
- package/dist/core/services/static-search-service.d.ts.map +1 -0
- package/dist/core/services/static-search-service.js +144 -0
- package/dist/core/services/static-search-service.js.map +1 -0
- package/dist/core/tools/glossary-lookup.d.ts.map +1 -1
- package/dist/core/tools/glossary-lookup.js +8 -0
- package/dist/core/tools/glossary-lookup.js.map +1 -1
- package/dist/core/tools/search.d.ts.map +1 -1
- package/dist/core/tools/search.js +79 -9
- package/dist/core/tools/search.js.map +1 -1
- package/package.json +9 -7
|
@@ -73,8 +73,39 @@ export function collectInternalLinkMapIds(html) {
|
|
|
73
73
|
}
|
|
74
74
|
return [...mapIds];
|
|
75
75
|
}
|
|
76
|
-
function indexTocNodes(nodes, into, ancestors) {
|
|
76
|
+
function indexTocNodes(nodes, into, ancestors, parent) {
|
|
77
77
|
for (const node of nodes) {
|
|
78
|
+
// Only a node a reader could actually open joins the navigation tree: it
|
|
79
|
+
// needs an id to key on, a contentId to be addressed by, a URL to go to and
|
|
80
|
+
// a title to be labelled with. A node missing any of those is a grouping
|
|
81
|
+
// heading, and putting one in the tree would mean publishing a link to
|
|
82
|
+
// nothing and counting it among a page's neighbours.
|
|
83
|
+
//
|
|
84
|
+
// Note this is a *stricter* test than the ancestry index below applies. A
|
|
85
|
+
// breadcrumb rung is text, so an unopenable one is harmless and Jamf's own
|
|
86
|
+
// grouping titles belong there; a navigation entry is a destination, and an
|
|
87
|
+
// unopenable one is a dead end. Different requirement, different rule.
|
|
88
|
+
//
|
|
89
|
+
// Measured against the live Jamf Pro map: 0 of 792 nodes are missing any of
|
|
90
|
+
// the four, so in practice nothing takes this branch. It is here because
|
|
91
|
+
// `FtTocNode` is a bare cast over `response.json()` with no validation
|
|
92
|
+
// behind it, and because a future map that does group its topics should
|
|
93
|
+
// lose the grouping, not the topics under it.
|
|
94
|
+
const openable = node.tocId !== '' && node.contentId !== '' && node.prettyUrl !== '' && (node.title ?? '') !== '';
|
|
95
|
+
if (openable) {
|
|
96
|
+
into.nodeByTocId[node.tocId] = {
|
|
97
|
+
title: node.title ?? '',
|
|
98
|
+
url: buildDisplayUrl(node.prettyUrl),
|
|
99
|
+
};
|
|
100
|
+
if (parent === undefined) {
|
|
101
|
+
into.rootTocIds.push(node.tocId);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
into.parentTocId[node.tocId] = parent;
|
|
105
|
+
(into.childTocIds[parent] ??= []).push(node.tocId);
|
|
106
|
+
}
|
|
107
|
+
into.tocIdByContentId[node.contentId] = node.tocId;
|
|
108
|
+
}
|
|
78
109
|
// Both fields are declared required, but `FtTocNode` is a bare cast over
|
|
79
110
|
// `response.json()` with no runtime validation behind it — the same reason
|
|
80
111
|
// `title` and `children` are optional on that type. An empty value here
|
|
@@ -94,16 +125,27 @@ function indexTocNodes(nodes, into, ancestors) {
|
|
|
94
125
|
const title = node.title ?? '';
|
|
95
126
|
const childAncestors = title === '' ? ancestors : [...ancestors, title];
|
|
96
127
|
// Absent `children` means a leaf, the same as an empty list.
|
|
97
|
-
|
|
128
|
+
// A node that did not join the tree passes its own parent down, so its
|
|
129
|
+
// children attach one rung higher rather than being orphaned under an id
|
|
130
|
+
// nothing can reach.
|
|
131
|
+
indexTocNodes(node.children ?? [], into, childAncestors, openable ? node.tocId : parent);
|
|
98
132
|
}
|
|
99
133
|
}
|
|
100
134
|
async function loadMapTocIndex(cache, mapId, ttl) {
|
|
101
|
-
const key = cacheKey('ft-tocindex-
|
|
135
|
+
const key = cacheKey('ft-tocindex-v3', { mapId });
|
|
102
136
|
const cached = await cache.get(key);
|
|
103
137
|
if (cached !== null) {
|
|
104
138
|
return cached;
|
|
105
139
|
}
|
|
106
|
-
const index = {
|
|
140
|
+
const index = {
|
|
141
|
+
urlByTocId: {},
|
|
142
|
+
ancestorsByContentId: {},
|
|
143
|
+
nodeByTocId: {},
|
|
144
|
+
childTocIds: {},
|
|
145
|
+
parentTocId: {},
|
|
146
|
+
tocIdByContentId: {},
|
|
147
|
+
rootTocIds: [],
|
|
148
|
+
};
|
|
107
149
|
indexTocNodes(await fetchMapToc(mapId), index, []);
|
|
108
150
|
await cache.set(key, index, ttl);
|
|
109
151
|
return index;
|
|
@@ -135,6 +177,92 @@ export async function buildInternalLinkResolver(options) {
|
|
|
135
177
|
}));
|
|
136
178
|
return (mapId, tocId) => indexes.get(mapId)?.urlByTocId[tocId];
|
|
137
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* How many neighbours a navigation list carries.
|
|
182
|
+
*
|
|
183
|
+
* The counts alongside them are the totals, so a capped list still says how
|
|
184
|
+
* much it is not showing. The cap exists because the sibling set of a
|
|
185
|
+
* top-level page is every top-level page — for Jamf Pro that is twenty-odd
|
|
186
|
+
* links, on the structured channel, on every article fetch.
|
|
187
|
+
*/
|
|
188
|
+
const MAX_NAV_LINKS = 8;
|
|
189
|
+
/**
|
|
190
|
+
* Where a topic sits among its neighbours in its map.
|
|
191
|
+
*
|
|
192
|
+
* The complement of {@link fetchTopicAncestors}: that answers "what is above
|
|
193
|
+
* this page", this answers "what is beside and below it". Both read the same
|
|
194
|
+
* cached index, so an article that already resolved a breadcrumb pays nothing
|
|
195
|
+
* for this.
|
|
196
|
+
*
|
|
197
|
+
* It exists because the Fluid Topics API serves one topic per call while the
|
|
198
|
+
* website concatenates a topic and its children into a single page. Nine of
|
|
199
|
+
* the nine `<h2>` sections on the rendered "Computer Configuration Profiles"
|
|
200
|
+
* page are separate topics in the map — measured, not assumed. A reader who
|
|
201
|
+
* opens that page in a viewer gets the parent's introduction and no route to
|
|
202
|
+
* the nine procedures underneath it unless something publishes the tree, and
|
|
203
|
+
* the tree is only in the TOC.
|
|
204
|
+
*
|
|
205
|
+
* Returns `undefined` rather than an empty shape when the topic is not in the
|
|
206
|
+
* index: absent means "this map does not place it", which is different from
|
|
207
|
+
* "it has no neighbours".
|
|
208
|
+
*/
|
|
209
|
+
export async function fetchTopicNavigation(options) {
|
|
210
|
+
const { cache, mapId, contentId, ttl, logger } = options;
|
|
211
|
+
let index;
|
|
212
|
+
try {
|
|
213
|
+
index = await loadMapTocIndex(cache, mapId, ttl);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
217
|
+
logger?.warning(`ft-internal-link: TOC index unavailable for map ${mapId},` +
|
|
218
|
+
` this article will have no navigation: ${reason}`);
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
const tocId = index.tocIdByContentId[contentId];
|
|
222
|
+
if (tocId === undefined) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
const self = index.nodeByTocId[tocId];
|
|
226
|
+
if (self === undefined) {
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
// Constructed field by field, not returned by reference.
|
|
230
|
+
//
|
|
231
|
+
// The index is a cache entry, so what comes back is whatever shape was
|
|
232
|
+
// written when it was stored — including fields a later version of this file
|
|
233
|
+
// dropped. `NavigationLinkSchema` is strict, so one stale extra key fails
|
|
234
|
+
// output validation for the whole tool and the article does not render at
|
|
235
|
+
// all. Naming the two fields makes the published shape independent of how
|
|
236
|
+
// the index happens to be stored, which is the property that actually needs
|
|
237
|
+
// to hold; the namespace version protects against a *missing* field, and
|
|
238
|
+
// cannot protect against an extra one.
|
|
239
|
+
const link = (id) => {
|
|
240
|
+
const node = index.nodeByTocId[id];
|
|
241
|
+
return node === undefined ? undefined : { title: node.title, url: node.url };
|
|
242
|
+
};
|
|
243
|
+
const parentId = index.parentTocId[tocId];
|
|
244
|
+
const parent = parentId !== undefined ? link(parentId) : undefined;
|
|
245
|
+
// A root topic's siblings are the other roots — which is why `rootTocIds` is
|
|
246
|
+
// indexed at all. Without it every top-level page in a product reported no
|
|
247
|
+
// neighbours, which is the opposite of true.
|
|
248
|
+
const siblingIds = (parentId !== undefined ? index.childTocIds[parentId] : index.rootTocIds) ?? [];
|
|
249
|
+
const siblings = siblingIds.filter((id) => id !== tocId).map(link).filter(isLink);
|
|
250
|
+
const children = (index.childTocIds[tocId] ?? []).map(link).filter(isLink);
|
|
251
|
+
return {
|
|
252
|
+
self: { title: self.title, url: self.url },
|
|
253
|
+
...(parent !== undefined ? { parent } : {}),
|
|
254
|
+
siblings: siblings.slice(0, MAX_NAV_LINKS),
|
|
255
|
+
children: children.slice(0, MAX_NAV_LINKS),
|
|
256
|
+
// The totals, not the array lengths. A truncated list that does not say so
|
|
257
|
+
// is one a reader will treat as exhaustive.
|
|
258
|
+
siblingCount: siblings.length,
|
|
259
|
+
childCount: children.length,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/** Narrows away an id that is somehow not in the node table. */
|
|
263
|
+
function isLink(value) {
|
|
264
|
+
return value !== undefined;
|
|
265
|
+
}
|
|
138
266
|
export async function fetchTopicAncestors(options) {
|
|
139
267
|
const { cache, mapId, contentId, ttl, logger } = options;
|
|
140
268
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ft-internal-link.js","sourceRoot":"","sources":["../../../src/core/services/ft-internal-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,kEAAkE;AAElE,uEAAuE;AACvE,MAAM,CAAC,MAAM,mBAAmB,GAAG,kBAAkB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GACjC,QAAQ,mBAAmB,0BAA0B,CAAC;AAQxD,mEAAmE;AACnE,MAAM,CAAC,MAAM,iBAAiB,GAAyB,GAAG,EAAE,CAAC,SAAS,CAAC;AAEvE,kEAAkE;AAElE,MAAM,WAAW,GAAG,kBAAkB,CAAC;AACvC,MAAM,cAAc,GAAG,qCAAqC,CAAC;AAC7D,MAAM,cAAc,GAAG,qCAAqC,CAAC;AAE7D;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,IAAY;IACpD,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;YAC5E,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;AACrB,CAAC;
|
|
1
|
+
{"version":3,"file":"ft-internal-link.js","sourceRoot":"","sources":["../../../src/core/services/ft-internal-link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Searching the non-Fluid-Topics sources.
|
|
3
|
+
*
|
|
4
|
+
* These results are kept in their own block rather than merged into the
|
|
5
|
+
* Fluid Topics ranking. `SearchResult` has no score field and Fluid Topics
|
|
6
|
+
* does not return one — `search.ts` says so where it builds a result — so
|
|
7
|
+
* there is nothing on either side to fuse two orderings on. Inventing a
|
|
8
|
+
* comparable number would produce an interleaving that looks authoritative
|
|
9
|
+
* and is not. A labelled second block says exactly what it is: other places
|
|
10
|
+
* this query matched.
|
|
11
|
+
*
|
|
12
|
+
* The index is titles, recovered from each source's sitemap. That is one
|
|
13
|
+
* request per source for 808 support articles and 98 concepts pages, against
|
|
14
|
+
* ~900 page fetches to read the real headings — and a title is what a
|
|
15
|
+
* ranked pointer needs.
|
|
16
|
+
*/
|
|
17
|
+
import { type StaticDocSource } from '../constants/sources.js';
|
|
18
|
+
import type { ServerContext } from '../types/context.js';
|
|
19
|
+
/** One page of a static source, as the index holds it. */
|
|
20
|
+
export interface StaticSearchEntry {
|
|
21
|
+
title: string;
|
|
22
|
+
url: string;
|
|
23
|
+
/** Display name of the source it belongs to. */
|
|
24
|
+
source: string;
|
|
25
|
+
}
|
|
26
|
+
/** A hit, with the source that produced it. */
|
|
27
|
+
export interface StaticSearchHit extends StaticSearchEntry {
|
|
28
|
+
/** Fuse's distance, 0 = exact. Comparable within this block only. */
|
|
29
|
+
score: number;
|
|
30
|
+
}
|
|
31
|
+
/** Build one source's title index for a locale. */
|
|
32
|
+
export declare function loadStaticIndex(ctx: ServerContext, source: StaticDocSource, locale: string): Promise<StaticSearchEntry[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Search every static source that publishes the requested locale.
|
|
35
|
+
*
|
|
36
|
+
* Best-effort per source: one unreachable sitemap costs its own hits, not
|
|
37
|
+
* the block, and never the Fluid Topics results this runs alongside.
|
|
38
|
+
*/
|
|
39
|
+
export declare function searchStaticSources(ctx: ServerContext, query: string, locale: string, limit?: number): Promise<StaticSearchHit[]>;
|
|
40
|
+
//# sourceMappingURL=static-search-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-search-service.d.ts","sourceRoot":"","sources":["../../../src/core/services/static-search-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,EAAsB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAEnF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;CACf;AAsCD,mDAAmD;AACnD,wBAAsB,eAAe,CACnC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,eAAe,EACvB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAc9B;AAyCD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,aAAa,EAClB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,SAAI,GACR,OAAO,CAAC,eAAe,EAAE,CAAC,CAqB5B"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Searching the non-Fluid-Topics sources.
|
|
3
|
+
*
|
|
4
|
+
* These results are kept in their own block rather than merged into the
|
|
5
|
+
* Fluid Topics ranking. `SearchResult` has no score field and Fluid Topics
|
|
6
|
+
* does not return one — `search.ts` says so where it builds a result — so
|
|
7
|
+
* there is nothing on either side to fuse two orderings on. Inventing a
|
|
8
|
+
* comparable number would produce an interleaving that looks authoritative
|
|
9
|
+
* and is not. A labelled second block says exactly what it is: other places
|
|
10
|
+
* this query matched.
|
|
11
|
+
*
|
|
12
|
+
* The index is titles, recovered from each source's sitemap. That is one
|
|
13
|
+
* request per source for 808 support articles and 98 concepts pages, against
|
|
14
|
+
* ~900 page fetches to read the real headings — and a title is what a
|
|
15
|
+
* ranked pointer needs.
|
|
16
|
+
*/
|
|
17
|
+
import Fuse from 'fuse.js';
|
|
18
|
+
import { httpGetText } from '../http-client.js';
|
|
19
|
+
import { cacheKey } from './cache-key.js';
|
|
20
|
+
import { titleFromSlug } from './sitemap-service.js';
|
|
21
|
+
import { STATIC_DOC_SOURCES } from '../constants/sources.js';
|
|
22
|
+
/**
|
|
23
|
+
* Path segments that are not articles.
|
|
24
|
+
*
|
|
25
|
+
* `browse` is in concepts.jamf.com's sitemap (ten entries, one per locale)
|
|
26
|
+
* and is a JS-only shell — thirteen characters once tags are stripped. The
|
|
27
|
+
* integration notes claimed these were absent from the sitemap and therefore
|
|
28
|
+
* excluded for free; they are not.
|
|
29
|
+
*/
|
|
30
|
+
const NON_ARTICLE_SEGMENTS = new Set(['browse', 'about', 'ecosystem', 'collections']);
|
|
31
|
+
/**
|
|
32
|
+
* Turn a sitemap path into an index entry.
|
|
33
|
+
*
|
|
34
|
+
* Intercom article slugs are prefixed with the numeric id
|
|
35
|
+
* (`10631322-get-started-with-jamf-now`), which is stripped before casing —
|
|
36
|
+
* leaving it in produces "10631322 Get Started With Jamf Now".
|
|
37
|
+
*/
|
|
38
|
+
function entryFor(source, url, locale) {
|
|
39
|
+
let segments;
|
|
40
|
+
try {
|
|
41
|
+
segments = new URL(url).pathname.split('/').filter(Boolean);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const [entryLocale, section, ...rest] = segments;
|
|
47
|
+
if (entryLocale !== locale) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (section === undefined || NON_ARTICLE_SEGMENTS.has(section)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
// A section index page is a container, not an article.
|
|
54
|
+
if (rest.length === 0) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
const slug = (rest[rest.length - 1] ?? '').replace(/^\d+-/, '');
|
|
58
|
+
if (slug === '') {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
return { title: titleFromSlug(slug), url, source: source.name };
|
|
62
|
+
}
|
|
63
|
+
/** Build one source's title index for a locale. */
|
|
64
|
+
export async function loadStaticIndex(ctx, source, locale) {
|
|
65
|
+
const key = cacheKey('static-search-index', { source: source.id, locale });
|
|
66
|
+
const cached = await ctx.cache.get(key);
|
|
67
|
+
if (cached !== null) {
|
|
68
|
+
return cached;
|
|
69
|
+
}
|
|
70
|
+
const xml = await httpGetText(`${source.baseUrl}/sitemap.xml`);
|
|
71
|
+
const entries = [];
|
|
72
|
+
for (const match of xml.matchAll(/<loc>\s*([^<\s]+)\s*<\/loc>/g)) {
|
|
73
|
+
const entry = entryFor(source, match[1] ?? '', locale);
|
|
74
|
+
if (entry !== null) {
|
|
75
|
+
entries.push(entry);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
await ctx.cache.set(key, entries, ctx.config.cacheTtl.products);
|
|
79
|
+
return entries;
|
|
80
|
+
}
|
|
81
|
+
// ─── Fuse index, per server ─────────────────────────────────────
|
|
82
|
+
/**
|
|
83
|
+
* Per-server Fuse indexes, keyed `sourceId:locale`.
|
|
84
|
+
*
|
|
85
|
+
* Same shape as the glossary's: a WeakMap on the CacheProvider so each
|
|
86
|
+
* ServerContext gets its own and it is collected with the context, rather
|
|
87
|
+
* than a module-level map that would outlive a request in a runtime where
|
|
88
|
+
* module scope persists.
|
|
89
|
+
*/
|
|
90
|
+
const indexByServer = new WeakMap();
|
|
91
|
+
const FUSE_OPTIONS = {
|
|
92
|
+
keys: [{ name: 'title', weight: 1 }],
|
|
93
|
+
threshold: 0.35,
|
|
94
|
+
includeScore: true,
|
|
95
|
+
ignoreLocation: true,
|
|
96
|
+
minMatchCharLength: 3,
|
|
97
|
+
};
|
|
98
|
+
function fuseFor(ctx, key, entries) {
|
|
99
|
+
let perServer = indexByServer.get(ctx.cache);
|
|
100
|
+
if (perServer === undefined) {
|
|
101
|
+
perServer = new Map();
|
|
102
|
+
indexByServer.set(ctx.cache, perServer);
|
|
103
|
+
}
|
|
104
|
+
const existing = perServer.get(key);
|
|
105
|
+
// Rebuild when the underlying array is a different one — the cache TTL
|
|
106
|
+
// expiring is what replaces it.
|
|
107
|
+
if (existing?.source === entries) {
|
|
108
|
+
return existing.fuse;
|
|
109
|
+
}
|
|
110
|
+
const fuse = new Fuse(entries, FUSE_OPTIONS);
|
|
111
|
+
perServer.set(key, { source: entries, fuse });
|
|
112
|
+
return fuse;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Search every static source that publishes the requested locale.
|
|
116
|
+
*
|
|
117
|
+
* Best-effort per source: one unreachable sitemap costs its own hits, not
|
|
118
|
+
* the block, and never the Fluid Topics results this runs alongside.
|
|
119
|
+
*/
|
|
120
|
+
export async function searchStaticSources(ctx, query, locale, limit = 3) {
|
|
121
|
+
const log = ctx.logger.createLogger('static-search');
|
|
122
|
+
const hits = [];
|
|
123
|
+
for (const source of Object.values(STATIC_DOC_SOURCES)) {
|
|
124
|
+
const sourceLocale = source.locales[locale];
|
|
125
|
+
if (sourceLocale === undefined) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const entries = await loadStaticIndex(ctx, source, sourceLocale);
|
|
130
|
+
if (entries.length === 0) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const results = fuseFor(ctx, `${source.id}:${sourceLocale}`, entries).search(query, { limit });
|
|
134
|
+
for (const result of results) {
|
|
135
|
+
hits.push({ ...result.item, score: result.score ?? 1 });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
log.warning(`Could not search ${source.name}: ${String(error)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return hits;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=static-search-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-search-service.js","sourceRoot":"","sources":["../../../src/core/services/static-search-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glossary-lookup.d.ts","sourceRoot":"","sources":["../../../src/core/tools/glossary-lookup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"glossary-lookup.d.ts","sourceRoot":"","sources":["../../../src/core/tools/glossary-lookup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AA0FzD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CA6ItF"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { GlossaryLookupInputSchema } from '../schemas/index.js';
|
|
6
6
|
import { GlossaryLookupOutputSchema } from '../schemas/output.js';
|
|
7
|
+
import { appToolMeta } from '../apps/index.js';
|
|
7
8
|
import { ResponseFormat, OutputMode, JAMF_PRODUCTS, TOKEN_CONFIG } from '../constants.js';
|
|
8
9
|
import { lookupGlossaryTerm } from '../services/glossary.js';
|
|
9
10
|
import { sanitizeMarkdownText, sanitizeMarkdownUrl, getSafeErrorMessage } from '../utils/sanitize.js';
|
|
@@ -85,6 +86,13 @@ export function registerGlossaryLookupTool(server, ctx) {
|
|
|
85
86
|
description: TOOL_DESCRIPTION,
|
|
86
87
|
inputSchema: GlossaryLookupInputSchema,
|
|
87
88
|
outputSchema: GlossaryLookupOutputSchema,
|
|
89
|
+
// Hosts supporting the MCP Apps extension render this result in the
|
|
90
|
+
// shared viewer; others ignore the metadata and get the markdown. The
|
|
91
|
+
// glossary view reuses the search view's components wholesale — a term
|
|
92
|
+
// is a title, a definition is a snippet — so it was the cheapest way to
|
|
93
|
+
// check that the component set generalises past the three views it was
|
|
94
|
+
// drawn against.
|
|
95
|
+
_meta: appToolMeta(),
|
|
88
96
|
annotations: {
|
|
89
97
|
readOnlyHint: true,
|
|
90
98
|
destructiveHint: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"glossary-lookup.js","sourceRoot":"","sources":["../../../src/core/tools/glossary-lookup.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"glossary-lookup.js","sourceRoot":"","sources":["../../../src/core/tools/glossary-lookup.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAuB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/core/tools/search.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAuB,MAAM,iBAAiB,CAAC;AAsM/E;;;;;;;;GAQG;AACH,UAAU,aAAa;IACrB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EAWnD,CAAC;AAkBF,eAAO,MAAM,gBAAgB,QAqDX,CAAC;AAuOnB,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI,CA0L9E"}
|
|
@@ -10,6 +10,7 @@ import { searchDocumentation } from '../services/search-service.js';
|
|
|
10
10
|
import { generateSearchSuggestions, formatSearchSuggestions } from '../services/search-suggestions.js';
|
|
11
11
|
import { sanitizeMarkdownText, sanitizeMarkdownUrl, getSafeErrorMessage } from '../utils/sanitize.js';
|
|
12
12
|
import { reportProgress } from '../utils/progress.js';
|
|
13
|
+
import { searchStaticSources, } from '../services/static-search-service.js';
|
|
13
14
|
function formatFiltersLine(filters) {
|
|
14
15
|
const parts = [];
|
|
15
16
|
if (filters.product !== undefined) {
|
|
@@ -249,6 +250,33 @@ function activeSearchFilters(params) {
|
|
|
249
250
|
};
|
|
250
251
|
return Object.keys(filters).length > 0 ? filters : undefined;
|
|
251
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Render the other-source hits as a labelled trailer.
|
|
255
|
+
*
|
|
256
|
+
* Below the results and clearly separated, because these come from a
|
|
257
|
+
* different ranking that shares no scale with the one above — and because
|
|
258
|
+
* two of the three sources are not product documentation.
|
|
259
|
+
*/
|
|
260
|
+
function renderOtherSources(hits) {
|
|
261
|
+
if (hits.length === 0) {
|
|
262
|
+
return '';
|
|
263
|
+
}
|
|
264
|
+
const bySource = new Map();
|
|
265
|
+
for (const hit of hits) {
|
|
266
|
+
bySource.set(hit.source, [...(bySource.get(hit.source) ?? []), hit]);
|
|
267
|
+
}
|
|
268
|
+
let out = '\n---\n\n## Also found outside the product documentation\n\n';
|
|
269
|
+
for (const [source, sourceHits] of bySource) {
|
|
270
|
+
out += `**${source}**\n\n`;
|
|
271
|
+
for (const hit of sourceHits) {
|
|
272
|
+
out += `- [${sanitizeMarkdownText(hit.title)}](${sanitizeMarkdownUrl(hit.url)})\n`;
|
|
273
|
+
}
|
|
274
|
+
out += '\n';
|
|
275
|
+
}
|
|
276
|
+
out += '*Matched on title, ranked separately from the results above — '
|
|
277
|
+
+ 'Fluid Topics returns no score to rank them against.*\n';
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
252
280
|
function buildSearchStructuredContent(query, results, pagination, extras) {
|
|
253
281
|
return {
|
|
254
282
|
query,
|
|
@@ -284,7 +312,17 @@ function buildSearchStructuredContent(query, results, pagination, extras) {
|
|
|
284
312
|
...(extras?.filterRelaxation !== undefined ? { filterRelaxation: extras.filterRelaxation } : {}),
|
|
285
313
|
...(extras?.versionNote !== undefined ? { versionNote: extras.versionNote } : {}),
|
|
286
314
|
...(extras?.paginationNote !== undefined ? { paginationNote: extras.paginationNote } : {}),
|
|
287
|
-
...(extras?.truncatedContent !== undefined ? { truncatedContent: extras.truncatedContent } : {})
|
|
315
|
+
...(extras?.truncatedContent !== undefined ? { truncatedContent: extras.truncatedContent } : {}),
|
|
316
|
+
// Omitted rather than emitted empty: "nothing matched elsewhere" and
|
|
317
|
+
// "the other sources were not reachable" are different answers, and an
|
|
318
|
+
// empty array would claim the first.
|
|
319
|
+
...(extras?.otherSources !== undefined && extras.otherSources.length > 0
|
|
320
|
+
? {
|
|
321
|
+
otherSources: extras.otherSources.map(hit => ({
|
|
322
|
+
title: hit.title, url: hit.url, source: hit.source,
|
|
323
|
+
})),
|
|
324
|
+
}
|
|
325
|
+
: {})
|
|
288
326
|
};
|
|
289
327
|
}
|
|
290
328
|
/**
|
|
@@ -292,18 +330,37 @@ function buildSearchStructuredContent(query, results, pagination, extras) {
|
|
|
292
330
|
*/
|
|
293
331
|
function buildNoResultsResponse(query, hasProductFilter, hasTopicFilter, locale) {
|
|
294
332
|
const suggestions = generateSearchSuggestions(query, hasProductFilter, hasTopicFilter);
|
|
333
|
+
/**
|
|
334
|
+
* Queries a client can run, and nothing else.
|
|
335
|
+
*
|
|
336
|
+
* `generateSearchSuggestions` already separates what is runnable
|
|
337
|
+
* (`simplifiedQuery`, `alternativeKeywords`) from what is advice (`tips`,
|
|
338
|
+
* and the locale note below), and this array used to flatten all of it
|
|
339
|
+
* together. A structured client cannot tell the two apart afterwards, so
|
|
340
|
+
* the MCP App rendered "Try removing filters to broaden your search" as a
|
|
341
|
+
* clickable search and, when clicked, searched Jamf for that sentence. The
|
|
342
|
+
* `Try: ` prefix had the same fault one step earlier: it made the one
|
|
343
|
+
* genuinely runnable entry un-runnable, because the prefix went into the
|
|
344
|
+
* query too.
|
|
345
|
+
*
|
|
346
|
+
* Nothing is lost by dropping the prose here. The advice reaches the model
|
|
347
|
+
* through `formatSearchSuggestions` on the text channel below, which is the
|
|
348
|
+
* channel prose belongs on; `structuredContent.suggestions` is read only by
|
|
349
|
+
* clients that are going to *do* something with each entry.
|
|
350
|
+
*/
|
|
295
351
|
const suggestionTexts = [
|
|
296
|
-
...(
|
|
297
|
-
|
|
298
|
-
: []),
|
|
299
|
-
...(suggestions.simplifiedQuery !== null ? [`Try: ${suggestions.simplifiedQuery}`] : []),
|
|
300
|
-
...suggestions.alternativeKeywords,
|
|
301
|
-
...suggestions.tips
|
|
352
|
+
...(suggestions.simplifiedQuery !== null ? [suggestions.simplifiedQuery] : []),
|
|
353
|
+
...suggestions.alternativeKeywords
|
|
302
354
|
];
|
|
355
|
+
// The locale caveat is advice, so it rides the text channel with the rest of
|
|
356
|
+
// the advice rather than being mixed into the runnable list above.
|
|
357
|
+
const localeNote = locale !== undefined && locale !== DEFAULT_LOCALE
|
|
358
|
+
? `\n\nNot all documentation is available in "${locale}". Try searching with language: "${DEFAULT_LOCALE}".`
|
|
359
|
+
: '';
|
|
303
360
|
return {
|
|
304
361
|
content: [{
|
|
305
362
|
type: 'text',
|
|
306
|
-
text: formatSearchSuggestions(query, suggestions)
|
|
363
|
+
text: `${formatSearchSuggestions(query, suggestions)}${localeNote}`
|
|
307
364
|
}],
|
|
308
365
|
structuredContent: {
|
|
309
366
|
query,
|
|
@@ -403,6 +460,18 @@ export function registerSearchTool(server, ctx) {
|
|
|
403
460
|
maxTokens: params.maxTokens ?? TOKEN_CONFIG.DEFAULT_MAX_TOKENS
|
|
404
461
|
});
|
|
405
462
|
await reportProgress(extra, { progress: 1, total: 3, message: 'Processing results...' });
|
|
463
|
+
// The non-Fluid-Topics sources, as their own block.
|
|
464
|
+
//
|
|
465
|
+
// Deliberately not merged into the ranking above. Fluid Topics
|
|
466
|
+
// returns no score — see the relevanceNote below, which says so —
|
|
467
|
+
// and neither does this server's SearchResult, so there is nothing
|
|
468
|
+
// on either side to fuse two orderings on. Interleaving them on an
|
|
469
|
+
// invented number would look authoritative and would not be. Never
|
|
470
|
+
// allowed to fail the search it runs beside.
|
|
471
|
+
const otherSources = await searchStaticSources(ctx, params.query, params.language ?? DEFAULT_LOCALE).catch((error) => {
|
|
472
|
+
ctx.logger.createLogger('search').warning(`Other-source search failed: ${String(error)}`);
|
|
473
|
+
return [];
|
|
474
|
+
});
|
|
406
475
|
const { results, pagination, tokenInfo, filterRelaxation, versionNote, paginationNote, truncatedContent } = searchResult;
|
|
407
476
|
// Build response
|
|
408
477
|
const filters = buildFilterSummary(params);
|
|
@@ -431,6 +500,7 @@ export function registerSearchTool(server, ctx) {
|
|
|
431
500
|
versionNote,
|
|
432
501
|
paginationNote,
|
|
433
502
|
truncatedContent,
|
|
503
|
+
otherSources,
|
|
434
504
|
});
|
|
435
505
|
if (params.responseFormat === ResponseFormat.JSON) {
|
|
436
506
|
// Add relevance note only in JSON format.
|
|
@@ -463,7 +533,7 @@ export function registerSearchTool(server, ctx) {
|
|
|
463
533
|
const formatFn = params.outputMode === OutputMode.COMPACT
|
|
464
534
|
? formatSearchResultsAsCompact
|
|
465
535
|
: formatSearchResultsAsMarkdown;
|
|
466
|
-
const markdown = appendMarkdownNotices(formatFn(params.query, results, filters, pagination, tokenInfo), { filterRelaxation, versionNote, paginationNote, truncatedContent });
|
|
536
|
+
const markdown = appendMarkdownNotices(formatFn(params.query, results, filters, pagination, tokenInfo), { filterRelaxation, versionNote, paginationNote, truncatedContent }) + renderOtherSources(otherSources);
|
|
467
537
|
await reportProgress(extra, { progress: 3, total: 3 });
|
|
468
538
|
return {
|
|
469
539
|
content: [{
|