@aibranch/llm-meta-widget 0.9.1 → 0.10.1
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
CHANGED
|
@@ -402,7 +402,7 @@ translate directly.
|
|
|
402
402
|
| `api_key_uuid:` | `"ollama-local"` | Hub API-key uuid to invoke |
|
|
403
403
|
| `element_path:` | `"/llm_meta_widget_assets/llm-meta-widget.js"` | The bundled element the page loads. Served by the gem's engine; override to load it from elsewhere |
|
|
404
404
|
| `actions_schema_id:` | `"ai-actions"` | DOM id of the Class-3 schema block |
|
|
405
|
-
| `page_card_id:` | `"ai-page"` |
|
|
405
|
+
| `page_card_id:` | `"ai-page"` | **Fallback only.** A card is normally found by its media type (see "The page card"); this id is read when the page has no block with that type. Its tools replace the `actions_schema_id` block |
|
|
406
406
|
| `prompts_global:` | `"aiPrompts"` | Global object holding page prompt handlers, keyed by prompt name |
|
|
407
407
|
| `resources_global:` | `"aiResources"` | Global object holding page resource readers, keyed by resource URI |
|
|
408
408
|
| `state_global:` | `"aiState"` | Global window object holding Class-3 state readers, each `{ description, read }` (see the breaking change in 0.8.0) |
|
|
@@ -424,7 +424,7 @@ an MCP server puts in its Server Card, so the page and the server describe
|
|
|
424
424
|
themselves the same way.
|
|
425
425
|
|
|
426
426
|
```html
|
|
427
|
-
<script type="application/json"
|
|
427
|
+
<script type="application/mcp-static-primitives+json">
|
|
428
428
|
{ "_meta": { "io.modelcontextprotocol/static-primitives": {
|
|
429
429
|
"tools": [ { "name": "set_text", "description": "…", "inputSchema": { } } ],
|
|
430
430
|
"prompts": [ { "name": "annotate", "description": "Annotate this page" } ],
|
|
@@ -466,6 +466,21 @@ fetch from the web by itself — which page resources are not. The form is
|
|
|
466
466
|
`page:<name>` with no path, because a page has only one namespace. Expect this to
|
|
467
467
|
change.
|
|
468
468
|
|
|
469
|
+
**The card is found by its media type.** A client matches
|
|
470
|
+
`script[type="application/mcp-static-primitives+json"]`, so the element needs no
|
|
471
|
+
`id`. This is how a client that was never told about your page can still read
|
|
472
|
+
it, and it follows SEP-0000's "HTML-delivery mode". The `page_card_id:` option
|
|
473
|
+
is kept only for a host that cannot set the type; it is read when no block with
|
|
474
|
+
the media type is present.
|
|
475
|
+
|
|
476
|
+
**A document may hold several blocks.** A page built from components can let
|
|
477
|
+
each component declare its own primitives, and the arrays join by primitive
|
|
478
|
+
type. Declaring the same tool or prompt `name`, or the same resource `uri`,
|
|
479
|
+
twice is an error: every copy is dropped and a message is written to the
|
|
480
|
+
console. The first one does not win, because which block comes first depends on
|
|
481
|
+
the order the page's parts are rendered in, and that is not something the author
|
|
482
|
+
chose.
|
|
483
|
+
|
|
469
484
|
**The card is optional.** A page that declares nothing keeps working exactly as
|
|
470
485
|
before, and a page that declares no `tools` still uses its `ai-actions` block.
|
|
471
486
|
|
|
@@ -560,6 +560,25 @@ function parseSseFrame(raw) {
|
|
|
560
560
|
return { name, data };
|
|
561
561
|
}
|
|
562
562
|
var STATIC_PRIMITIVES_META = "io.modelcontextprotocol/static-primitives";
|
|
563
|
+
var PAGE_CARD_MEDIA_TYPE = "application/mcp-static-primitives+json";
|
|
564
|
+
function mergeStaticPrimitivesCards(cards, warn = console.warn) {
|
|
565
|
+
const merged = { tools: [], prompts: [], resources: [] };
|
|
566
|
+
const keyOf = { tools: "name", prompts: "name", resources: "uri" };
|
|
567
|
+
for (const kind of ["tools", "prompts", "resources"]) {
|
|
568
|
+
const key = keyOf[kind];
|
|
569
|
+
const all = [];
|
|
570
|
+
for (const card of cards || []) all.push(...card && card[kind] || []);
|
|
571
|
+
const counts = /* @__PURE__ */ new Map();
|
|
572
|
+
for (const entry of all) counts.set(entry[key], (counts.get(entry[key]) || 0) + 1);
|
|
573
|
+
for (const [id, n] of counts) {
|
|
574
|
+
if (n > 1) {
|
|
575
|
+
warn(`[llm-meta-widget] the page declares ${kind} "${id}" ${n} times; dropping every copy \u2014 remove the duplicate to say which one you mean`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
merged[kind] = all.filter((entry) => counts.get(entry[key]) === 1);
|
|
579
|
+
}
|
|
580
|
+
return merged;
|
|
581
|
+
}
|
|
563
582
|
function readStaticPrimitivesCard(text, warn = console.warn) {
|
|
564
583
|
const empty = { tools: [], prompts: [], resources: [] };
|
|
565
584
|
if (typeof text !== "string" || text.trim() === "") return empty;
|
|
@@ -3900,8 +3919,18 @@ function boot(cfg) {
|
|
|
3900
3919
|
model: MODEL
|
|
3901
3920
|
});
|
|
3902
3921
|
}
|
|
3903
|
-
var
|
|
3904
|
-
var pageCard
|
|
3922
|
+
var typedCards = document.querySelectorAll('script[type="' + PAGE_CARD_MEDIA_TYPE + '"]');
|
|
3923
|
+
var pageCard;
|
|
3924
|
+
if (typedCards.length) {
|
|
3925
|
+
pageCard = mergeStaticPrimitivesCards(
|
|
3926
|
+
Array.prototype.map.call(typedCards, function(el) {
|
|
3927
|
+
return readStaticPrimitivesCard(el.textContent);
|
|
3928
|
+
})
|
|
3929
|
+
);
|
|
3930
|
+
} else {
|
|
3931
|
+
var pageCardEl = document.getElementById(PAGE_CARD_ID);
|
|
3932
|
+
pageCard = readStaticPrimitivesCard(pageCardEl ? pageCardEl.textContent : null);
|
|
3933
|
+
}
|
|
3905
3934
|
var actionsSchemaEl = document.getElementById(ACTIONS_SCHEMA_ID);
|
|
3906
3935
|
var localTools = pageCard.tools.length ? pageCard.tools : actionsSchemaEl ? JSON.parse(actionsSchemaEl.textContent) : [];
|
|
3907
3936
|
var remoteToolsEl = document.getElementById(REMOTE_TOOLS_SCHEMA_ID);
|
|
@@ -879,6 +879,43 @@ export const STATIC_PRIMITIVES_META = "io.modelcontextprotocol/static-primitives
|
|
|
879
879
|
// typo in it should cost that one entry, not the assistant. Everything
|
|
880
880
|
// unparseable returns empty arrays and says so on the console, which is how
|
|
881
881
|
// aiState already treats a reader missing its description.
|
|
882
|
+
// The media type that locates a card inside an HTML document. It is the
|
|
883
|
+
// locator, so it is NOT configurable: a client that has to be told where to
|
|
884
|
+
// look cannot discover anything. SEP-0000, "HTML-delivery mode".
|
|
885
|
+
export const PAGE_CARD_MEDIA_TYPE = "application/mcp-static-primitives+json"
|
|
886
|
+
|
|
887
|
+
// Several blocks may appear in one document, so that a page assembled from
|
|
888
|
+
// components can let each component declare its own primitives. The arrays
|
|
889
|
+
// concatenate by primitive type.
|
|
890
|
+
//
|
|
891
|
+
// A duplicate identifier across blocks is an ERROR, not a precedence puzzle:
|
|
892
|
+
// the spec says a client should report it and should not silently pick one. So
|
|
893
|
+
// every copy of a duplicated identifier is dropped and the author is told.
|
|
894
|
+
// Picking the first would make which component wins depend on DOM order, which
|
|
895
|
+
// is not something a page author controls when the components are partials.
|
|
896
|
+
export function mergeStaticPrimitivesCards(cards, warn = console.warn) {
|
|
897
|
+
const merged = { tools: [], prompts: [], resources: [] }
|
|
898
|
+
const keyOf = { tools: "name", prompts: "name", resources: "uri" }
|
|
899
|
+
|
|
900
|
+
for (const kind of [ "tools", "prompts", "resources" ]) {
|
|
901
|
+
const key = keyOf[kind]
|
|
902
|
+
const all = []
|
|
903
|
+
for (const card of cards || []) all.push(...((card && card[kind]) || []))
|
|
904
|
+
|
|
905
|
+
const counts = new Map()
|
|
906
|
+
for (const entry of all) counts.set(entry[key], (counts.get(entry[key]) || 0) + 1)
|
|
907
|
+
|
|
908
|
+
for (const [ id, n ] of counts) {
|
|
909
|
+
if (n > 1) {
|
|
910
|
+
warn(`[llm-meta-widget] the page declares ${kind} "${id}" ${n} times; ` +
|
|
911
|
+
"dropping every copy — remove the duplicate to say which one you mean")
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
merged[kind] = all.filter((entry) => counts.get(entry[key]) === 1)
|
|
915
|
+
}
|
|
916
|
+
return merged
|
|
917
|
+
}
|
|
918
|
+
|
|
882
919
|
export function readStaticPrimitivesCard(text, warn = console.warn) {
|
|
883
920
|
const empty = { tools: [], prompts: [], resources: [] }
|
|
884
921
|
if (typeof text !== "string" || text.trim() === "") return empty
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aibranch/llm-meta-widget",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"//version": "Must stay in step with lib/llm_meta_widget/version.rb. Two independently versioned copies of the same client is the drift docs/custom-element-distribution.md warns about: bump both, release both.",
|
|
5
5
|
"description": "Embeddable browser chat widget: a custom element that discovers tools at page, origin and hub scope and dispatches each tool call to the right one. Two lines of HTML on any host, Rails or not.",
|
|
6
6
|
"keywords": [
|