wiki-formant 0.63.0 → 0.64.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 +1 -1
- package/dist/block-views.d.ts +6 -3
- package/dist/block-views.js +3 -3
- package/dist/blocks.js +1 -6
- package/dist/category.d.ts +0 -4
- package/dist/combobox.js +1 -3
- package/dist/conformance.d.ts +2 -2
- package/dist/conformance.js +9 -9
- package/dist/crawlers.js +1 -5
- package/dist/headings.js +1 -8
- package/dist/maintenance.d.ts +4 -3
- package/dist/maps.js +7 -1
- package/dist/markdown.d.ts +5 -0
- package/dist/markdown.js +33 -19
- package/dist/mcp.d.ts +16 -0
- package/dist/mcp.js +59 -69
- package/dist/pagination.d.ts +0 -17
- package/dist/pagination.js +0 -19
- package/dist/react-server.d.ts +0 -34
- package/dist/react-server.js +0 -17
- package/dist/stats-figures.d.ts +1 -0
- package/dist/stats-figures.js +1 -1
- package/dist/stats.js +1 -2
- package/dist/taxonomy.js +5 -1
- package/dist/tiptap.d.ts +0 -1
- package/dist/tiptap.js +0 -1
- package/dist/validation.js +1 -1
- package/dist/versioning.d.ts +0 -1
- package/dist/versioning.js +0 -5
- package/dist/well-known.d.ts +1 -0
- package/dist/well-known.js +2 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -821,7 +821,7 @@ The dispatch stays a `switch` over the wiki's own union, so a new type is a comp
|
|
|
821
821
|
|
|
822
822
|
An editor that draws each block in place, with a list of its own (miow's), hosts the same fields in `<BlockFieldsKit kit={KIT}>`, without `<BlockEditor>`. Only the two container fields need the full editor, since they hold a block list.
|
|
823
823
|
|
|
824
|
-
A `testimonial` is `{ quote, author, role?, avatarUrl? }`, drawn by `TestimonialView` as `figure.testimonial`: the words in a `blockquote`, then a `figcaption` holding the photo and a `span` of `strong` (who) and `small` (their role). radix-wiki and miow each had one under its own field names; a site with sized images passes `
|
|
824
|
+
A `testimonial` is `{ quote, author, role?, avatarUrl? }`, drawn by `TestimonialView` as `figure.testimonial`: the words in a `blockquote`, then a `figcaption` holding the photo and a `span` of `strong` (who) and `small` (their role). radix-wiki and miow each had one under its own field names; a site with sized images passes `photo`, the `<img>` attributes (`src`, `srcSet`, `sizes`) it made from `avatarUrl`. They are data, because a server component renders the view.
|
|
825
825
|
|
|
826
826
|
A banner with no text of its own shows `BANNER_MESSAGES` from `wiki-formant/text`, a sentence per variant that names no wiki; `BannerView` takes it unless `message` overrides it.
|
|
827
827
|
|
package/dist/block-views.d.ts
CHANGED
|
@@ -109,11 +109,14 @@ export interface TestimonialViewProps {
|
|
|
109
109
|
author: string;
|
|
110
110
|
role?: string;
|
|
111
111
|
avatarUrl?: string;
|
|
112
|
-
/**
|
|
113
|
-
|
|
112
|
+
/**
|
|
113
|
+
* The photo's sized `<img>` attributes, for a site with an image pipeline.
|
|
114
|
+
* Data, not a function: a server component renders this client view.
|
|
115
|
+
*/
|
|
116
|
+
photo?: Pick<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'srcSet' | 'sizes'>;
|
|
114
117
|
}
|
|
115
118
|
/** Someone's words over who said them, with their photo when there is one. */
|
|
116
|
-
export declare function TestimonialView({ quote, author, role, avatarUrl,
|
|
119
|
+
export declare function TestimonialView({ quote, author, role, avatarUrl, photo }: TestimonialViewProps): import("react").JSX.Element | null;
|
|
117
120
|
/**
|
|
118
121
|
* A maintenance notice.
|
|
119
122
|
*
|
package/dist/block-views.js
CHANGED
|
@@ -115,11 +115,11 @@ export function StatsView({ items, columns = 4, }) {
|
|
|
115
115
|
return (_jsx("div", { className: cx('stat-grid', `stat-grid-${columns}`), children: rows.map((item, i) => (_jsxs("div", { className: "stat-card", children: [_jsxs("span", { className: "stat-value", children: [item.value, item.suffix && _jsx("span", { className: "stat-suffix", children: item.suffix })] }), _jsx("span", { className: "stat-label", children: item.label })] }, item.id ?? i))) }));
|
|
116
116
|
}
|
|
117
117
|
/** Someone's words over who said them, with their photo when there is one. */
|
|
118
|
-
export function TestimonialView({ quote, author, role, avatarUrl,
|
|
118
|
+
export function TestimonialView({ quote, author, role, avatarUrl, photo }) {
|
|
119
119
|
if (!quote.trim())
|
|
120
120
|
return null;
|
|
121
|
-
const
|
|
122
|
-
return (_jsxs("figure", { className: "testimonial", children: [_jsx("blockquote", { children: quote }), _jsxs("figcaption", { children: [
|
|
121
|
+
const src = avatarUrl && safeLinkHref(avatarUrl);
|
|
122
|
+
return (_jsxs("figure", { className: "testimonial", children: [_jsx("blockquote", { children: quote }), _jsxs("figcaption", { children: [src && _jsx("img", { src: src, ...photo, alt: "", loading: "lazy" }), _jsxs("span", { children: [_jsx("strong", { children: author }), role && _jsx("small", { children: role })] })] })] }));
|
|
123
123
|
}
|
|
124
124
|
// ---- banner -----------------------------------------------------------------
|
|
125
125
|
/**
|
package/dist/blocks.js
CHANGED
|
@@ -145,12 +145,7 @@ export async function mapBlockTreeAsync(blocks, map, shape) {
|
|
|
145
145
|
* so a code block inside an infobox shipped unhighlighted.
|
|
146
146
|
*/
|
|
147
147
|
export function someBlock(blocks, predicate, containers) {
|
|
148
|
-
return blocks.some(
|
|
149
|
-
const groups = containers(block);
|
|
150
|
-
if (!groups)
|
|
151
|
-
return predicate(block);
|
|
152
|
-
return groups.some(group => someBlock(group, predicate, containers));
|
|
153
|
-
});
|
|
148
|
+
return leafBlocks(blocks, containers).some(predicate);
|
|
154
149
|
}
|
|
155
150
|
// ---- the shared leaf renderers ----------------------------------------------
|
|
156
151
|
/**
|
package/dist/category.d.ts
CHANGED
|
@@ -58,7 +58,3 @@ export declare function categoryView<P extends FacetablePage & {
|
|
|
58
58
|
sortHref: (to: "title" | "recent") => string;
|
|
59
59
|
sections: CategorySection[];
|
|
60
60
|
}>;
|
|
61
|
-
export type CategoryView<P> = Awaited<ReturnType<typeof categoryView<P & FacetablePage & {
|
|
62
|
-
tagPath: string;
|
|
63
|
-
slug: string;
|
|
64
|
-
}>>>;
|
package/dist/combobox.js
CHANGED
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
// the attribute that announces the highlighted row as the reader arrows
|
|
11
11
|
// through it. A combobox without it is a text field that silently changes what
|
|
12
12
|
// Enter does.
|
|
13
|
-
/** The id of one option row
|
|
14
|
-
* highlighted row into view has to find it by the same id the input points at. */
|
|
15
|
-
/** @internal */
|
|
13
|
+
/** The id of one option row, which `aria-activedescendant` names. @internal */
|
|
16
14
|
export function optionId(baseId, listKey, index) {
|
|
17
15
|
return `${listId(baseId, listKey)}-opt-${index}`;
|
|
18
16
|
}
|
package/dist/conformance.d.ts
CHANGED
|
@@ -139,6 +139,8 @@ export declare function agentCardParity(t: Tester): Promise<void>;
|
|
|
139
139
|
* will pass is a check that measures nothing.
|
|
140
140
|
*/
|
|
141
141
|
export declare function conditionalGetChecks(t: Tester, paths: readonly string[]): Promise<void>;
|
|
142
|
+
/** The JSON descriptors every S10 origin serves: the two agent-card paths, the OpenAPI pair, the MCP manifest and the server card. */
|
|
143
|
+
export declare const S10_DESCRIPTORS: readonly string[];
|
|
142
144
|
/**
|
|
143
145
|
* A JSON descriptor is revalidatable — an agent card, an OpenAPI document, a
|
|
144
146
|
* registry manifest, a server card.
|
|
@@ -149,8 +151,6 @@ export declare function conditionalGetChecks(t: Tester, paths: readonly string[]
|
|
|
149
151
|
* document moving, which is worse than none. The body-derived ETag is the whole
|
|
150
152
|
* validator, and it is enough.
|
|
151
153
|
*/
|
|
152
|
-
/** The JSON descriptors every S10 origin serves: the two agent-card paths, the OpenAPI pair, the MCP manifest and the server card. */
|
|
153
|
-
export declare const S10_DESCRIPTORS: readonly string[];
|
|
154
154
|
export declare function descriptorChecks(t: Tester, paths?: readonly string[]): Promise<void>;
|
|
155
155
|
/**
|
|
156
156
|
* Documents that project one corpus at different depths carry different ETags.
|
package/dist/conformance.js
CHANGED
|
@@ -327,6 +327,15 @@ export async function conditionalGetChecks(t, paths) {
|
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
}
|
|
330
|
+
/** The JSON descriptors every S10 origin serves: the two agent-card paths, the OpenAPI pair, the MCP manifest and the server card. */
|
|
331
|
+
export const S10_DESCRIPTORS = [
|
|
332
|
+
'.well-known/agent-card.json',
|
|
333
|
+
'.well-known/agent.json',
|
|
334
|
+
'.well-known/openapi.json',
|
|
335
|
+
'openapi.json',
|
|
336
|
+
'.well-known/mcp.json',
|
|
337
|
+
'api/mcp/server-card',
|
|
338
|
+
];
|
|
330
339
|
/**
|
|
331
340
|
* A JSON descriptor is revalidatable — an agent card, an OpenAPI document, a
|
|
332
341
|
* registry manifest, a server card.
|
|
@@ -337,15 +346,6 @@ export async function conditionalGetChecks(t, paths) {
|
|
|
337
346
|
* document moving, which is worse than none. The body-derived ETag is the whole
|
|
338
347
|
* validator, and it is enough.
|
|
339
348
|
*/
|
|
340
|
-
/** The JSON descriptors every S10 origin serves: the two agent-card paths, the OpenAPI pair, the MCP manifest and the server card. */
|
|
341
|
-
export const S10_DESCRIPTORS = [
|
|
342
|
-
'.well-known/agent-card.json',
|
|
343
|
-
'.well-known/agent.json',
|
|
344
|
-
'.well-known/openapi.json',
|
|
345
|
-
'openapi.json',
|
|
346
|
-
'.well-known/mcp.json',
|
|
347
|
-
'api/mcp/server-card',
|
|
348
|
-
];
|
|
349
349
|
export async function descriptorChecks(t, paths = S10_DESCRIPTORS) {
|
|
350
350
|
for (const path of paths) {
|
|
351
351
|
const url = path.startsWith('http') ? path : `${t.base}/${path.replace(/^\//, '')}`;
|
package/dist/crawlers.js
CHANGED
|
@@ -53,10 +53,6 @@ export function detectAiBot(userAgent) {
|
|
|
53
53
|
}
|
|
54
54
|
return null;
|
|
55
55
|
}
|
|
56
|
-
/** Every token that needs its own robots.txt group — which is all of them. @internal */
|
|
57
|
-
export function aiCrawlerTokens() {
|
|
58
|
-
return AI_CRAWLERS.map(c => c.token);
|
|
59
|
-
}
|
|
60
56
|
/**
|
|
61
57
|
* The agent surface an S10 origin serves, allowed in every group. All three
|
|
62
58
|
* `robots.ts` files listed these by hand, and a path missing from one list is
|
|
@@ -110,7 +106,7 @@ export function crawlerRules(opts) {
|
|
|
110
106
|
// honestly as itself was told the endpoint was off limits. The roster is
|
|
111
107
|
// for indexing policy, not for hiding a documented endpoint.
|
|
112
108
|
{ userAgent: '*', allow: [...new Set([...[opts.allow].flat(), ...aiAllow])], disallow },
|
|
113
|
-
...
|
|
109
|
+
...AI_CRAWLERS.map(({ token: userAgent }) => ({ userAgent, allow: aiAllow, disallow })),
|
|
114
110
|
...SEARCH_ENGINES.map(userAgent => ({ userAgent, allow: '/', disallow: searchDisallow })),
|
|
115
111
|
];
|
|
116
112
|
}
|
package/dist/headings.js
CHANGED
|
@@ -79,14 +79,7 @@ export function injectHeadingIds(html, options = {}) {
|
|
|
79
79
|
* consumers use only the injector above.
|
|
80
80
|
*/
|
|
81
81
|
export function headingsFrom(html) {
|
|
82
|
-
|
|
83
|
-
for (const [, tag, attrs, content] of html.matchAll(HEADING)) {
|
|
84
|
-
const id = getAttr(attrs ?? '', 'id');
|
|
85
|
-
const text = stripTags(content ?? '');
|
|
86
|
-
if (id && text)
|
|
87
|
-
out.push({ id, text, level: Number(tag[1]) });
|
|
88
|
-
}
|
|
89
|
-
return out;
|
|
82
|
+
return headingOutline(html).filter(h => h.id);
|
|
90
83
|
}
|
|
91
84
|
/**
|
|
92
85
|
* Every heading in `html`, in document order, whether or not it carries an id —
|
package/dist/maintenance.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type SourceListItem = string | {
|
|
|
3
3
|
name?: string;
|
|
4
4
|
url?: string;
|
|
5
5
|
};
|
|
6
|
-
|
|
6
|
+
interface SourceEntry {
|
|
7
7
|
lastChecked?: string | null;
|
|
8
8
|
note?: string;
|
|
9
9
|
list?: SourceListItem[];
|
|
@@ -13,11 +13,11 @@ export type BacklogEntry = string | {
|
|
|
13
13
|
item: string;
|
|
14
14
|
status?: string;
|
|
15
15
|
};
|
|
16
|
-
|
|
16
|
+
type FeedbackEntry = string | {
|
|
17
17
|
date?: string;
|
|
18
18
|
note: string;
|
|
19
19
|
};
|
|
20
|
-
|
|
20
|
+
interface RunEntry {
|
|
21
21
|
date?: string;
|
|
22
22
|
category?: string;
|
|
23
23
|
summary?: string;
|
|
@@ -55,3 +55,4 @@ export declare function render(state: LogState, routine: string, newId?: () => s
|
|
|
55
55
|
type: "content";
|
|
56
56
|
text: string;
|
|
57
57
|
}[];
|
|
58
|
+
export {};
|
package/dist/maps.js
CHANGED
|
@@ -54,7 +54,13 @@ export function toMapEmbedUrl(url) {
|
|
|
54
54
|
if (/google\.[a-z.]+\/maps/.test(url)) {
|
|
55
55
|
const place = url.match(/\/place\/([^/@]+)/);
|
|
56
56
|
if (place) {
|
|
57
|
-
|
|
57
|
+
// A stray `%` makes decodeURIComponent throw; the segment is then used as pasted.
|
|
58
|
+
let name = place[1];
|
|
59
|
+
try {
|
|
60
|
+
name = decodeURIComponent(name);
|
|
61
|
+
}
|
|
62
|
+
catch { }
|
|
63
|
+
const q = encodeURIComponent(name.replace(/\+/g, ' '));
|
|
58
64
|
return `https://maps.google.com/maps?q=${q}&output=embed`;
|
|
59
65
|
}
|
|
60
66
|
}
|
package/dist/markdown.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One pass, so a decoded `&` never starts a second entity (`&lt;` is the
|
|
3
|
+
* text `<`), and a numeric reference past U+10FFFF stays as written rather
|
|
4
|
+
* than throwing out of the twin's route.
|
|
5
|
+
*/
|
|
1
6
|
export declare function decodeEntities(s: string): string;
|
|
2
7
|
/** Inline-level HTML → markdown. Applied inside cells, list items, headings. */
|
|
3
8
|
export declare function inlineToMarkdown(html: string): string;
|
package/dist/markdown.js
CHANGED
|
@@ -37,11 +37,18 @@ const NAMED_ENTITIES = {
|
|
|
37
37
|
mu: 'μ', nu: 'ν', pi: 'π', rho: 'ρ', sigma: 'σ', tau: 'τ', phi: 'φ', omega: 'ω',
|
|
38
38
|
Delta: 'Δ', Sigma: 'Σ', Omega: 'Ω', Lambda: 'Λ', Phi: 'Φ', Pi: 'Π',
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* One pass, so a decoded `&` never starts a second entity (`&lt;` is the
|
|
42
|
+
* text `<`), and a numeric reference past U+10FFFF stays as written rather
|
|
43
|
+
* than throwing out of the twin's route.
|
|
44
|
+
*/
|
|
40
45
|
export function decodeEntities(s) {
|
|
41
|
-
return s
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
return s.replace(/&(?:#x([0-9a-fA-F]{1,6})|#(\d{1,7})|([a-zA-Z][a-zA-Z0-9]{1,9}));/g, (m, hex, dec, name) => {
|
|
47
|
+
if (name)
|
|
48
|
+
return NAMED_ENTITIES[name] ?? m;
|
|
49
|
+
const code = hex ? parseInt(hex, 16) : Number(dec);
|
|
50
|
+
return code <= 0x10ffff ? String.fromCodePoint(code) : m;
|
|
51
|
+
});
|
|
45
52
|
}
|
|
46
53
|
/** Inline-level HTML → markdown. Applied inside cells, list items, headings. */
|
|
47
54
|
export function inlineToMarkdown(html) {
|
|
@@ -71,7 +78,7 @@ export function tableToMarkdown(html) {
|
|
|
71
78
|
const pad = (r) => [...r, ...Array(width - r.length).fill('')];
|
|
72
79
|
// A table whose first row is all <th> has a header; otherwise synthesise an
|
|
73
80
|
// empty one, since GFM has no headerless table form.
|
|
74
|
-
const headed = /<th\b/i.test(html.slice(0, html.
|
|
81
|
+
const headed = /<th\b/i.test(html.slice(0, html.search(/<\/tr>/i) + 1));
|
|
75
82
|
const [head, ...body] = headed
|
|
76
83
|
? [pad(rows[0]), ...rows.slice(1).map(pad)]
|
|
77
84
|
: [Array(width).fill(''), ...rows.map(pad)];
|
|
@@ -83,6 +90,8 @@ export function tableToMarkdown(html) {
|
|
|
83
90
|
}
|
|
84
91
|
// The spaces either side go with it: at a block boundary they are not text.
|
|
85
92
|
const BLOCK_TOKEN = /[ \t]*\u0000(\d+)\u0000[ \t]*/g;
|
|
93
|
+
/** A list that holds no other list. */
|
|
94
|
+
const LIST = /<(ul|ol)\b[^>]*>((?:(?!<(?:ul|ol)\b)[\s\S])*?)<\/\1>/gi;
|
|
86
95
|
/** Block-level HTML → markdown. */
|
|
87
96
|
export function htmlToMarkdown(html) {
|
|
88
97
|
let out = html;
|
|
@@ -102,21 +111,26 @@ export function htmlToMarkdown(html) {
|
|
|
102
111
|
].join('\n');
|
|
103
112
|
// Tables first — their inner markup must not be eaten by the generic rules.
|
|
104
113
|
out = out.replace(/<table\b[^>]*>[\s\S]*?<\/table>/gi, m => block(tableToMarkdown(m)));
|
|
105
|
-
// Lists
|
|
106
|
-
//
|
|
107
|
-
// a
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
// Lists, innermost first: a list holding no other list converts, and the
|
|
115
|
+
// pass repeats until none is left, so a list nested in one of its own kind
|
|
116
|
+
// is a block before its parent reads it. A lazy match from the outer `<ul>`
|
|
117
|
+
// ended at the inner `</ul>` and ran both lists onto one line. `<ol>`
|
|
118
|
+
// numbering is computed per-list, which is why this cannot be a single regex
|
|
119
|
+
// with a `$1` backreference — inside a replace callback `$1` is a literal,
|
|
120
|
+
// not a substitution. (That exact bug shipped once and rendered every
|
|
121
|
+
// ordered list as a column of `1. $1`.)
|
|
122
|
+
for (let prev = ''; prev !== out;) {
|
|
123
|
+
prev = out;
|
|
124
|
+
out = out.replace(LIST, (_m, tag, body) => {
|
|
125
|
+
const ordered = tag.toLowerCase() === 'ol';
|
|
126
|
+
const items = [...body.matchAll(/<li\b[^>]*>([\s\S]*?)<\/li>/gi)].map((i, n) => listItem(ordered ? `${n + 1}.` : '-', i[1]));
|
|
127
|
+
return block(items.join('\n'));
|
|
128
|
+
});
|
|
129
|
+
}
|
|
117
130
|
out = out
|
|
118
|
-
.replace(
|
|
119
|
-
|
|
131
|
+
.replace(
|
|
132
|
+
// `<code>` is optional: a stored block may be `<pre><code>` or a bare `<pre>`.
|
|
133
|
+
/<pre\b[^>]*>\s*(?:<code\b[^>]*>)?([\s\S]*?)(?:<\/code>\s*)?<\/pre>/gi, (_m, code) => block(`\`\`\`\n${decodeEntities(code).trim()}\n\`\`\``))
|
|
120
134
|
.replace(/<blockquote\b[^>]*>([\s\S]*?)<\/blockquote>/gi, (_m, body) => block(inlineToMarkdown(body)
|
|
121
135
|
.split('\n')
|
|
122
136
|
.map(l => `> ${l}`)
|
package/dist/mcp.d.ts
CHANGED
|
@@ -105,6 +105,22 @@ export interface McpTool {
|
|
|
105
105
|
};
|
|
106
106
|
handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<unknown>;
|
|
107
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* A tool as every surface advertises it — `tools/list`, the agent card and
|
|
110
|
+
* `/.well-known/mcp.json`. The handler and the A2A skill stay behind.
|
|
111
|
+
*
|
|
112
|
+
* One projection because the two copies drifted: the manifest's had no
|
|
113
|
+
* `outputSchema`, so a descriptor said a tool answers with prose while
|
|
114
|
+
* `tools/list` said it answers with `structuredContent`.
|
|
115
|
+
*/
|
|
116
|
+
export declare function toolCard(tool: {
|
|
117
|
+
name: string;
|
|
118
|
+
title?: string;
|
|
119
|
+
description: string;
|
|
120
|
+
inputSchema: unknown;
|
|
121
|
+
outputSchema?: unknown;
|
|
122
|
+
annotations?: unknown;
|
|
123
|
+
}): Record<string, unknown>;
|
|
108
124
|
export interface McpResource {
|
|
109
125
|
uri: string;
|
|
110
126
|
name: string;
|
package/dist/mcp.js
CHANGED
|
@@ -67,6 +67,24 @@ function requestProtocol(request) {
|
|
|
67
67
|
/** JSON-RPC batching was removed in 2025-06-18 and stays removed; it is legal only below that. */
|
|
68
68
|
const allowsBatch = (v) => v === '2025-03-26' || v === '2024-11-05';
|
|
69
69
|
import { clientKey, rateLimit, rateLimitHeaders, withRateLimit } from './rate-limit.js';
|
|
70
|
+
/**
|
|
71
|
+
* A tool as every surface advertises it — `tools/list`, the agent card and
|
|
72
|
+
* `/.well-known/mcp.json`. The handler and the A2A skill stay behind.
|
|
73
|
+
*
|
|
74
|
+
* One projection because the two copies drifted: the manifest's had no
|
|
75
|
+
* `outputSchema`, so a descriptor said a tool answers with prose while
|
|
76
|
+
* `tools/list` said it answers with `structuredContent`.
|
|
77
|
+
*/
|
|
78
|
+
export function toolCard(tool) {
|
|
79
|
+
return {
|
|
80
|
+
name: tool.name,
|
|
81
|
+
...(tool.title ? { title: tool.title } : {}),
|
|
82
|
+
description: tool.description,
|
|
83
|
+
inputSchema: tool.inputSchema,
|
|
84
|
+
...(tool.outputSchema ? { outputSchema: tool.outputSchema } : {}),
|
|
85
|
+
...(tool.annotations ? { annotations: tool.annotations } : {}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
70
88
|
/**
|
|
71
89
|
* A caller-fixable failure inside a handler (page not found, empty input).
|
|
72
90
|
* Reported as a tool result with `isError` so the model sees the text and can
|
|
@@ -151,6 +169,15 @@ function typeOf(v) {
|
|
|
151
169
|
return 'null';
|
|
152
170
|
return typeof v;
|
|
153
171
|
}
|
|
172
|
+
/** A JSON-RPC success envelope. The error twin is `rpcError` below. */
|
|
173
|
+
const ok = (id, result) => ({ jsonrpc: '2.0', id, result });
|
|
174
|
+
/**
|
|
175
|
+
* The -32602 a lookup miss answers with: what was asked for, then everything
|
|
176
|
+
* this server does expose, as prose one retry can act on.
|
|
177
|
+
*/
|
|
178
|
+
const notFound = (id, lead, kind, names, dataKey) => rpcError(id, -32602, `${lead} This server exposes: ${names.length ? quote(names) : `no ${kind}`}.`, {
|
|
179
|
+
[dataKey]: names,
|
|
180
|
+
});
|
|
154
181
|
const rpcError = (id, code, message, data) => ({
|
|
155
182
|
jsonrpc: '2.0',
|
|
156
183
|
id,
|
|
@@ -265,6 +292,13 @@ const capabilitiesOf = (config) => ({
|
|
|
265
292
|
...(config.prompts?.length ? { prompts: {} } : {}),
|
|
266
293
|
});
|
|
267
294
|
async function handleRpc(req, config, dispatch) {
|
|
295
|
+
// The body is the caller's JSON, typed or not: a `null` in a batch would
|
|
296
|
+
// throw here and take the whole response with it, CORS headers included.
|
|
297
|
+
const entry = req;
|
|
298
|
+
if (!entry || typeof entry !== 'object' || typeof entry.method !== 'string') {
|
|
299
|
+
const id = entry?.id;
|
|
300
|
+
return rpcError(typeof id === 'string' || typeof id === 'number' ? id : null, -32600, 'Invalid Request: expected a JSON-RPC object with a string "method".');
|
|
301
|
+
}
|
|
268
302
|
const { id, method, params } = req;
|
|
269
303
|
const p = (params ?? {});
|
|
270
304
|
const resources = config.resources ?? [];
|
|
@@ -277,104 +311,60 @@ async function handleRpc(req, config, dispatch) {
|
|
|
277
311
|
try {
|
|
278
312
|
switch (method) {
|
|
279
313
|
case 'initialize':
|
|
280
|
-
return {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
serverInfo: config.serverInfo,
|
|
287
|
-
instructions: config.instructions,
|
|
288
|
-
},
|
|
289
|
-
};
|
|
314
|
+
return ok(id, {
|
|
315
|
+
protocolVersion: negotiateProtocol(p.protocolVersion),
|
|
316
|
+
capabilities: capabilitiesOf(config),
|
|
317
|
+
serverInfo: config.serverInfo,
|
|
318
|
+
instructions: config.instructions,
|
|
319
|
+
});
|
|
290
320
|
// What `initialize` tells a legacy client, asked the modern way. The
|
|
291
321
|
// transport adds `resultType`, the cache hints and `_meta` serverInfo.
|
|
292
322
|
case 'server/discover':
|
|
293
|
-
return {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
capabilities: capabilitiesOf(config),
|
|
299
|
-
instructions: config.instructions,
|
|
300
|
-
},
|
|
301
|
-
};
|
|
323
|
+
return ok(id, {
|
|
324
|
+
supportedVersions: [...MCP_PROTOCOL_VERSIONS],
|
|
325
|
+
capabilities: capabilitiesOf(config),
|
|
326
|
+
instructions: config.instructions,
|
|
327
|
+
});
|
|
302
328
|
// Notifications have no response at all.
|
|
303
329
|
case 'notifications/initialized':
|
|
304
330
|
case 'notifications/cancelled':
|
|
305
331
|
return null;
|
|
306
332
|
case 'ping':
|
|
307
|
-
return
|
|
333
|
+
return ok(id, {});
|
|
308
334
|
case 'tools/list':
|
|
309
|
-
return {
|
|
310
|
-
jsonrpc: '2.0',
|
|
311
|
-
id,
|
|
312
|
-
result: {
|
|
313
|
-
tools: config.tools.map(({ name, title, description, inputSchema, outputSchema, annotations }) => ({
|
|
314
|
-
name,
|
|
315
|
-
...(title ? { title } : {}),
|
|
316
|
-
description,
|
|
317
|
-
inputSchema,
|
|
318
|
-
...(outputSchema ? { outputSchema } : {}),
|
|
319
|
-
...(annotations ? { annotations } : {}),
|
|
320
|
-
})),
|
|
321
|
-
},
|
|
322
|
-
};
|
|
335
|
+
return ok(id, { tools: config.tools.map(toolCard) });
|
|
323
336
|
case 'resources/list':
|
|
324
|
-
return {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
result: {
|
|
328
|
-
resources: resources.map(({ uri, name, description, mimeType }) => ({
|
|
329
|
-
uri,
|
|
330
|
-
name,
|
|
331
|
-
description,
|
|
332
|
-
mimeType,
|
|
333
|
-
})),
|
|
334
|
-
},
|
|
335
|
-
};
|
|
337
|
+
return ok(id, {
|
|
338
|
+
resources: resources.map(({ uri, name, description, mimeType }) => ({ uri, name, description, mimeType })),
|
|
339
|
+
});
|
|
336
340
|
// Declared `resources` makes a client ask for templates too. There are
|
|
337
341
|
// none to give — every resource here is a fixed URI — but an empty list
|
|
338
342
|
// is the answer to that question, where -32601 reads as a broken server.
|
|
339
343
|
case 'resources/templates/list':
|
|
340
|
-
return
|
|
344
|
+
return ok(id, { resourceTemplates: [] });
|
|
341
345
|
case 'resources/read': {
|
|
342
346
|
const { uri } = p;
|
|
343
347
|
const resource = uri ? resources.find(r => r.uri === uri) : undefined;
|
|
344
348
|
const content = resource ? await resource.read() : null;
|
|
345
349
|
if (content == null) {
|
|
346
|
-
const uris = resources.map(r => r.uri);
|
|
347
350
|
const lead = uri ? `Unknown resource "${uri}".` : 'Missing "uri" in params.';
|
|
348
|
-
return
|
|
351
|
+
return notFound(id, lead, 'resources', resources.map(r => r.uri), 'availableResources');
|
|
349
352
|
}
|
|
350
|
-
return {
|
|
351
|
-
jsonrpc: '2.0',
|
|
352
|
-
id,
|
|
353
|
-
result: { contents: [{ uri, mimeType: resource.mimeType, text: content }] },
|
|
354
|
-
};
|
|
353
|
+
return ok(id, { contents: [{ uri, mimeType: resource.mimeType, text: content }] });
|
|
355
354
|
}
|
|
356
355
|
case 'prompts/list':
|
|
357
|
-
return {
|
|
358
|
-
jsonrpc: '2.0',
|
|
359
|
-
id,
|
|
360
|
-
result: { prompts: prompts.map(({ name, description }) => ({ name, description })) },
|
|
361
|
-
};
|
|
356
|
+
return ok(id, { prompts: prompts.map(({ name, description }) => ({ name, description })) });
|
|
362
357
|
case 'prompts/get': {
|
|
363
358
|
const { name } = p;
|
|
364
359
|
const prompt = name ? prompts.find(x => x.name === name) : undefined;
|
|
365
360
|
if (!prompt) {
|
|
366
|
-
const names = prompts.map(x => x.name);
|
|
367
361
|
const lead = name ? `Unknown prompt "${name}".` : 'Missing "name" in params.';
|
|
368
|
-
return
|
|
362
|
+
return notFound(id, lead, 'prompts', prompts.map(x => x.name), 'availablePrompts');
|
|
369
363
|
}
|
|
370
|
-
return {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
description: prompt.description,
|
|
375
|
-
messages: [{ role: 'user', content: { type: 'text', text: prompt.text } }],
|
|
376
|
-
},
|
|
377
|
-
};
|
|
364
|
+
return ok(id, {
|
|
365
|
+
description: prompt.description,
|
|
366
|
+
messages: [{ role: 'user', content: { type: 'text', text: prompt.text } }],
|
|
367
|
+
});
|
|
378
368
|
}
|
|
379
369
|
case 'tools/call': {
|
|
380
370
|
const { name, arguments: rawArgs = {} } = p;
|
package/dist/pagination.d.ts
CHANGED
|
@@ -42,20 +42,3 @@ export declare function listEnvelope<T>(pages: T[], total: number, page: number,
|
|
|
42
42
|
note?: string;
|
|
43
43
|
pages: T[];
|
|
44
44
|
};
|
|
45
|
-
/**
|
|
46
|
-
* The entries either side of the current one in an already-ordered list.
|
|
47
|
-
*
|
|
48
|
-
* The ordering is the caller's, deliberately — it is the one thing here that is
|
|
49
|
-
* never portable. A wiki's sequence is its section's configured sort, a
|
|
50
|
-
* knowledge base's is a taxonomy walk, and a company log's is `updatedAt` desc.
|
|
51
|
-
*
|
|
52
|
-
* `null` on both sides when the page is not in the list, so a page reached by a
|
|
53
|
-
* URL its own section does not list renders no nav rather than a wrong one.
|
|
54
|
-
*
|
|
55
|
-
* No query. Callers already hold the ordered siblings for something else, a
|
|
56
|
-
* related-pages panel or a section listing, so the neighbours cost no lookups.
|
|
57
|
-
*/
|
|
58
|
-
export declare function adjacentPages<T>(ordered: readonly T[], isCurrent: (page: T) => boolean): {
|
|
59
|
-
prev: T | null;
|
|
60
|
-
next: T | null;
|
|
61
|
-
};
|
package/dist/pagination.js
CHANGED
|
@@ -52,22 +52,3 @@ export function listEnvelope(pages, total, page, pageSize, emptyNote) {
|
|
|
52
52
|
pages,
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
56
|
-
* The entries either side of the current one in an already-ordered list.
|
|
57
|
-
*
|
|
58
|
-
* The ordering is the caller's, deliberately — it is the one thing here that is
|
|
59
|
-
* never portable. A wiki's sequence is its section's configured sort, a
|
|
60
|
-
* knowledge base's is a taxonomy walk, and a company log's is `updatedAt` desc.
|
|
61
|
-
*
|
|
62
|
-
* `null` on both sides when the page is not in the list, so a page reached by a
|
|
63
|
-
* URL its own section does not list renders no nav rather than a wrong one.
|
|
64
|
-
*
|
|
65
|
-
* No query. Callers already hold the ordered siblings for something else, a
|
|
66
|
-
* related-pages panel or a section listing, so the neighbours cost no lookups.
|
|
67
|
-
*/
|
|
68
|
-
export function adjacentPages(ordered, isCurrent) {
|
|
69
|
-
const i = ordered.findIndex(isCurrent);
|
|
70
|
-
if (i < 0)
|
|
71
|
-
return { prev: null, next: null };
|
|
72
|
-
return { prev: ordered[i - 1] ?? null, next: ordered[i + 1] ?? null };
|
|
73
|
-
}
|
package/dist/react-server.d.ts
CHANGED
|
@@ -195,40 +195,6 @@ export declare function Breadcrumbs({ items, base, className, link: Link, lastAs
|
|
|
195
195
|
export declare function BreadcrumbsRow({ actions, className, ...props }: BreadcrumbsProps & {
|
|
196
196
|
actions?: ReactNode;
|
|
197
197
|
}): import("react").JSX.Element;
|
|
198
|
-
/** One end of the foot nav: where it goes and what it is called. */
|
|
199
|
-
export interface PageNavRef {
|
|
200
|
-
title: string;
|
|
201
|
-
href: string;
|
|
202
|
-
}
|
|
203
|
-
export interface PageNavProps {
|
|
204
|
-
prev?: PageNavRef | null;
|
|
205
|
-
next?: PageNavRef | null;
|
|
206
|
-
link?: WikiLinkComponent;
|
|
207
|
-
/** The landmark's accessible name. */
|
|
208
|
-
label?: string;
|
|
209
|
-
prevLabel?: string;
|
|
210
|
-
nextLabel?: string;
|
|
211
|
-
/**
|
|
212
|
-
* The arrows. `ReactNode` rather than a string because both wikis draw them
|
|
213
|
-
* with an icon component, and this package is not acquiring an icon peer to
|
|
214
|
-
* hold two glyphs.
|
|
215
|
-
*/
|
|
216
|
-
prevGlyph?: ReactNode;
|
|
217
|
-
nextGlyph?: ReactNode;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* The sequential read at the foot of an article — the move the infobox rail's
|
|
221
|
-
* lateral links do not cover.
|
|
222
|
-
*
|
|
223
|
-
* Ordering is entirely the caller's; pair this with `adjacentPages` from
|
|
224
|
-
* `wiki-formant/pagination` over a list you already hold.
|
|
225
|
-
*
|
|
226
|
-
* The `page-nav__*` class names are NOT props: they are the convention every
|
|
227
|
-
* stylesheet implements, and making them configurable is how a convention
|
|
228
|
-
* forks. The empty `<div>` holds the first article's left column so the next
|
|
229
|
-
* link stays in the right one.
|
|
230
|
-
*/
|
|
231
|
-
export declare function PageNav({ prev, next, link: Link, label, prevLabel, nextLabel, prevGlyph, nextGlyph, }: PageNavProps): import("react").JSX.Element | null;
|
|
232
198
|
export interface InfoboxAsideProps {
|
|
233
199
|
/** The landmark's accessible name, e.g. `Key facts about ${title}`. */
|
|
234
200
|
label: string;
|
package/dist/react-server.js
CHANGED
|
@@ -148,23 +148,6 @@ export function Breadcrumbs({ items, base, className = '', link: Link = Anchor,
|
|
|
148
148
|
export function BreadcrumbsRow({ actions, className = '', ...props }) {
|
|
149
149
|
return (_jsxs("div", { className: `breadcrumbs-row ${actions ? 'spread' : ''} ${className}`.trim(), children: [_jsx(Breadcrumbs, { ...props }), actions] }));
|
|
150
150
|
}
|
|
151
|
-
/**
|
|
152
|
-
* The sequential read at the foot of an article — the move the infobox rail's
|
|
153
|
-
* lateral links do not cover.
|
|
154
|
-
*
|
|
155
|
-
* Ordering is entirely the caller's; pair this with `adjacentPages` from
|
|
156
|
-
* `wiki-formant/pagination` over a list you already hold.
|
|
157
|
-
*
|
|
158
|
-
* The `page-nav__*` class names are NOT props: they are the convention every
|
|
159
|
-
* stylesheet implements, and making them configurable is how a convention
|
|
160
|
-
* forks. The empty `<div>` holds the first article's left column so the next
|
|
161
|
-
* link stays in the right one.
|
|
162
|
-
*/
|
|
163
|
-
export function PageNav({ prev, next, link: Link = Anchor, label = 'Article navigation', prevLabel = 'Previous', nextLabel = 'Next', prevGlyph = '\u2190', nextGlyph = '\u2192', }) {
|
|
164
|
-
if (!prev && !next)
|
|
165
|
-
return null;
|
|
166
|
-
return (_jsxs("nav", { className: "page-nav", "aria-label": label, children: [prev ? (_jsxs(Link, { href: prev.href, className: "page-nav-link", rel: "prev", children: [_jsxs("span", { className: "page-nav-label", children: [prevGlyph, prevLabel] }), _jsx("span", { className: "page-nav-title", children: prev.title })] })) : (_jsx("div", {})), next && (_jsxs(Link, { href: next.href, className: "page-nav-link page-nav-link--end", rel: "next", children: [_jsxs("span", { className: "page-nav-label page-nav-label--end", children: [nextLabel, nextGlyph] }), _jsx("span", { className: "page-nav-title", children: next.title })] }))] }));
|
|
167
|
-
}
|
|
168
151
|
/**
|
|
169
152
|
* The facts panel beside an article. Named, because two of the three asides
|
|
170
153
|
* this replaced had no accessible name, and a complementary landmark without
|
package/dist/stats-figures.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { Digest } from './analytics.js';
|
|
3
|
+
export declare const count: (n: number) => string;
|
|
3
4
|
/**
|
|
4
5
|
* The figures as `.stats-figures button[aria-pressed]`, each with its change
|
|
5
6
|
* as `.stats-delta[data-better]` for a stylesheet to colour, and the pressed one per
|
package/dist/stats-figures.js
CHANGED
|
@@ -8,7 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
8
8
|
import { startAuthentication, startRegistration } from '@simplewebauthn/browser';
|
|
9
9
|
import { useMemo, useState } from 'react';
|
|
10
10
|
import { TimeChart, stepDecimals } from './chart.js';
|
|
11
|
-
const count = (n) => n.toLocaleString('en');
|
|
11
|
+
export const count = (n) => n.toLocaleString('en');
|
|
12
12
|
// Whole seconds and percentages, unless an axis tick's step needs more to tell it from the next.
|
|
13
13
|
const places = (step) => (step ? stepDecimals(step) : 0);
|
|
14
14
|
const duration = (s, step) => (s < 60 ? `${s.toFixed(places(step))}s` : `${Math.floor(s / 60)}m ${Math.round(s % 60)}s`);
|
package/dist/stats.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { STATS_FILTERS } from './analytics.js';
|
|
3
|
-
import { PasskeyButton, PasskeySignOut, StatsFigures } from './stats-figures.js';
|
|
3
|
+
import { PasskeyButton, PasskeySignOut, StatsFigures, count } from './stats-figures.js';
|
|
4
4
|
/** The windows the stats page offers, in days; `Infinity` is all time. */
|
|
5
5
|
const STATS_RANGES = [1, 7, 30, 90, 365, Infinity];
|
|
6
6
|
/** A window as its `?days=` value. */
|
|
@@ -31,7 +31,6 @@ const href = (days, filter) => {
|
|
|
31
31
|
q.set(k, filter[k]);
|
|
32
32
|
return `?${q}`;
|
|
33
33
|
};
|
|
34
|
-
const count = (n) => n.toLocaleString('en');
|
|
35
34
|
const regions = new Intl.DisplayNames(['en'], { type: 'region' });
|
|
36
35
|
const country = (code) => {
|
|
37
36
|
try {
|
package/dist/taxonomy.js
CHANGED
|
@@ -37,7 +37,11 @@ export function hrefBuilder(prefix = '') {
|
|
|
37
37
|
/** Categories mounted at the root. */
|
|
38
38
|
export const defaultHref = hrefBuilder();
|
|
39
39
|
const DEFAULT_ALPHA_INDEX_MIN_PAGES = 40;
|
|
40
|
-
|
|
40
|
+
/** A key's value as text. Metadata is untyped JSON, and a number or a list stored under a key must not throw. */
|
|
41
|
+
const metaValue = (page, key) => {
|
|
42
|
+
const value = page.metadata?.[key];
|
|
43
|
+
return typeof value === 'string' ? value.trim() : typeof value === 'number' ? String(value) : '';
|
|
44
|
+
};
|
|
41
45
|
/** Bucket for titles that don't start with a letter (numerals, `$CAPER`). @internal */
|
|
42
46
|
export function firstLetter(title) {
|
|
43
47
|
const ch = title.trim()[0]?.toUpperCase() ?? '';
|
package/dist/tiptap.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { Extension, Node as TiptapNode } from '@tiptap/core';
|
|
3
|
-
/** Local `cn`. Both wikis import one; the package will not depend on one. */
|
|
4
3
|
/**
|
|
5
4
|
* An embed's address, if it is https on a host the sanitiser and the CSP
|
|
6
5
|
* admit; otherwise null, and the node draws an empty frame. The editor draws
|
package/dist/tiptap.js
CHANGED
|
@@ -23,7 +23,6 @@ import { useClickOutside } from './react.js';
|
|
|
23
23
|
import { cx } from './html.js';
|
|
24
24
|
import { slugifyHeading, uniqueHeadingId } from './headings.js';
|
|
25
25
|
import { CODE_LANGS, DEFAULT_LANG } from './blocks.js';
|
|
26
|
-
/** Local `cn`. Both wikis import one; the package will not depend on one. */
|
|
27
26
|
// ---- iframe -----------------------------------------------------------------
|
|
28
27
|
/**
|
|
29
28
|
* An embed's address, if it is https on a host the sanitiser and the CSP
|
package/dist/validation.js
CHANGED
|
@@ -56,7 +56,7 @@ export function validateLinkGroups(groups) {
|
|
|
56
56
|
Array.isArray(g.links) &&
|
|
57
57
|
g.links.every(l => isRecord(l) && typeof l.label === 'string' && okUrl(l.href))));
|
|
58
58
|
}
|
|
59
|
-
/** `[{ label, language?, code }]` — a code-tabs block's tabs. `code`
|
|
59
|
+
/** `[{ label, language?, code }]` — a code-tabs block's tabs. `code` is source text, escaped when it renders. @internal */
|
|
60
60
|
export function validateCodeTabs(tabs) {
|
|
61
61
|
return (Array.isArray(tabs) &&
|
|
62
62
|
tabs.every(t => isRecord(t) &&
|
package/dist/versioning.d.ts
CHANGED
|
@@ -10,4 +10,3 @@ export declare function formatVersion(version: SemVer): string;
|
|
|
10
10
|
export declare function incrementVersion(version: SemVer, changeType: ChangeType): SemVer;
|
|
11
11
|
/** Bump a version string in one step — the call site almost always wants this. */
|
|
12
12
|
export declare function bump(version: string | null | undefined, changeType: ChangeType): string;
|
|
13
|
-
export declare function compareVersions(a: string, b: string): number;
|
package/dist/versioning.js
CHANGED
|
@@ -33,8 +33,3 @@ export function incrementVersion(version, changeType) {
|
|
|
33
33
|
export function bump(version, changeType) {
|
|
34
34
|
return formatVersion(incrementVersion(parseVersion(version), changeType));
|
|
35
35
|
}
|
|
36
|
-
export function compareVersions(a, b) {
|
|
37
|
-
const x = parseVersion(a);
|
|
38
|
-
const y = parseVersion(b);
|
|
39
|
-
return x.major - y.major || x.minor - y.minor || x.patch - y.patch;
|
|
40
|
-
}
|
package/dist/well-known.d.ts
CHANGED
package/dist/well-known.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// Nothing here touches a framework: these return plain values, and the caller
|
|
10
10
|
// wraps them in whatever its router wants. That keeps this package free of a
|
|
11
11
|
// Next dependency and keeps each app's route a two-liner.
|
|
12
|
-
import { MCP_PROTOCOL_VERSION, MCP_PROTOCOL_VERSIONS } from './mcp.js';
|
|
12
|
+
import { MCP_PROTOCOL_VERSION, MCP_PROTOCOL_VERSIONS, toolCard } from './mcp.js';
|
|
13
13
|
import { MCP_RATE_LIMIT_TEXT } from './rate-limit.js';
|
|
14
14
|
import { descriptorResponse } from './http.js';
|
|
15
15
|
/**
|
|
@@ -195,13 +195,7 @@ export function mcpManifest(config) {
|
|
|
195
195
|
rateLimit: MCP_RATE_LIMIT_TEXT,
|
|
196
196
|
},
|
|
197
197
|
...extra,
|
|
198
|
-
tools: tools.map(
|
|
199
|
-
name,
|
|
200
|
-
...(title ? { title } : {}),
|
|
201
|
-
description,
|
|
202
|
-
inputSchema,
|
|
203
|
-
...(annotations ? { annotations } : {}),
|
|
204
|
-
})),
|
|
198
|
+
tools: tools.map(toolCard),
|
|
205
199
|
};
|
|
206
200
|
}
|
|
207
201
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wiki-formant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.0",
|
|
4
4
|
"description": "The portable half of a wiki: derived taxonomy and facet controls, a version-negotiating MCP transport, markdown twins, block rendering, a rich-text editor engine, conditional-GET plumbing, and the headers and CSP every origin sends.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|