wormclaude 1.0.118 → 1.0.120
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/theme.js +1 -1
- package/dist/tools.js +14 -5
- package/package.json +1 -1
- package/skills/scan/SKILL.md +37 -0
- package/skills/build-mcp-app/SKILL.md +0 -393
- package/skills/build-mcp-app/references/abuse-protection.md +0 -60
- package/skills/build-mcp-app/references/apps-sdk-messages.md +0 -227
- package/skills/build-mcp-app/references/directory-checklist.md +0 -18
- package/skills/build-mcp-app/references/iframe-sandbox.md +0 -164
- package/skills/build-mcp-app/references/payload-budgeting.md +0 -54
- package/skills/build-mcp-app/references/widget-templates.md +0 -249
- package/skills/build-mcp-server/SKILL.md +0 -222
- package/skills/build-mcp-server/references/auth.md +0 -108
- package/skills/build-mcp-server/references/deploy-cloudflare-workers.md +0 -106
- package/skills/build-mcp-server/references/elicitation.md +0 -129
- package/skills/build-mcp-server/references/remote-http-scaffold.md +0 -211
- package/skills/build-mcp-server/references/resources-and-prompts.md +0 -122
- package/skills/build-mcp-server/references/server-capabilities.md +0 -164
- package/skills/build-mcp-server/references/tool-design.md +0 -189
- package/skills/build-mcp-server/references/versions.md +0 -25
- package/skills/build-mcpb/SKILL.md +0 -200
- package/skills/build-mcpb/references/local-security.md +0 -149
- package/skills/build-mcpb/references/manifest-schema.md +0 -156
- package/skills/docx/script/__init__.py +0 -1
- package/skills/docx/script/accept_chages.py +0 -135
- package/skills/docx/script/comment.py +0 -318
- package/skills/docx/script/office/helpers/__init__.py +0 -0
- package/skills/docx/script/office/helpers/merge_runs.py +0 -199
- package/skills/docx/script/office/helpers/simplify_redlines.py +0 -197
- package/skills/docx/script/office/pack.py +0 -159
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/skills/docx/script/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/skills/docx/script/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/skills/docx/script/office/schemas/mce/mc.xsd +0 -75
- package/skills/docx/script/office/schemas/microsoft/wml-2010.xsd +0 -560
- package/skills/docx/script/office/schemas/microsoft/wml-2012.xsd +0 -67
- package/skills/docx/script/office/schemas/microsoft/wml-2018.xsd +0 -14
- package/skills/docx/script/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/skills/docx/script/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/skills/docx/script/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/skills/docx/script/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/skills/docx/script/office/soffice.py +0 -183
- package/skills/docx/script/office/unpack.py +0 -132
- package/skills/docx/script/office/validate.py +0 -117
- package/skills/docx/script/office/validators/__init__.py +0 -15
- package/skills/docx/script/office/validators/base.py +0 -851
- package/skills/docx/script/office/validators/docx.py +0 -446
- package/skills/docx/script/office/validators/pptx.py +0 -275
- package/skills/docx/script/office/validators/redlining.py +0 -247
- package/skills/docx/script/templates/comments.xml +0 -3
- package/skills/docx/script/templates/commentsExtended.xml +0 -3
- package/skills/docx/script/templates/commentsExtensible.xml +0 -3
- package/skills/docx/script/templates/commentsIds.xml +0 -3
- package/skills/docx/script/templates/people.xml +0 -3
- package/skills/docx/skill.md +0 -593
- package/skills/explain.md +0 -14
- package/skills/frontend-design/SKILL.md +0 -42
- package/skills/pdf/FORMS.md +0 -294
- package/skills/pdf/REFERENCE.md +0 -612
- package/skills/pdf/SKILL.md +0 -314
- package/skills/pdf/scripts/check_bounding_boxes.py +0 -65
- package/skills/pdf/scripts/check_fillable_fields.py +0 -11
- package/skills/pdf/scripts/convert_pdf_to_images.py +0 -33
- package/skills/pdf/scripts/create_validation_image.py +0 -37
- package/skills/pdf/scripts/extract_form_field_info.py +0 -122
- package/skills/pdf/scripts/extract_form_structure.py +0 -115
- package/skills/pdf/scripts/fill_fillable_fields.py +0 -98
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -107
- package/skills/playground/SKILL.md +0 -77
- package/skills/playground/templates/code-map.md +0 -158
- package/skills/playground/templates/concept-map.md +0 -73
- package/skills/playground/templates/data-explorer.md +0 -67
- package/skills/playground/templates/design-playground.md +0 -67
- package/skills/playground/templates/diff-review.md +0 -179
- package/skills/playground/templates/document-critique.md +0 -171
- package/skills/pptx/SKILL.md +0 -230
- package/skills/pptx/editing.md +0 -205
- package/skills/pptx/pptxgenjs.md +0 -437
- package/skills/pptx/scripts/__init__.py +0 -0
- package/skills/pptx/scripts/add_slide.py +0 -195
- package/skills/pptx/scripts/clean.py +0 -286
- package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
- package/skills/pptx/scripts/office/helpers/merge_runs.py +0 -199
- package/skills/pptx/scripts/office/helpers/simplify_redlines.py +0 -197
- package/skills/pptx/scripts/office/pack.py +0 -159
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/skills/pptx/scripts/office/schemas/mce/mc.xsd +0 -75
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/skills/pptx/scripts/office/soffice.py +0 -183
- package/skills/pptx/scripts/office/unpack.py +0 -132
- package/skills/pptx/scripts/office/validate.py +0 -117
- package/skills/pptx/scripts/office/validators/__init__.py +0 -15
- package/skills/pptx/scripts/office/validators/base.py +0 -851
- package/skills/pptx/scripts/office/validators/docx.py +0 -446
- package/skills/pptx/scripts/office/validators/pptx.py +0 -275
- package/skills/pptx/scripts/office/validators/redlining.py +0 -247
- package/skills/pptx/scripts/thumbnail.py +0 -289
- package/skills/recon.md +0 -16
- package/skills/security-audit/SKILL.md +0 -26
- package/skills/talent-creator/SKILL.md +0 -486
- package/skills/talent-creator/agents/analyzer.md +0 -274
- package/skills/talent-creator/agents/comparator.md +0 -202
- package/skills/talent-creator/agents/grader.md +0 -223
- package/skills/talent-creator/assets/eval_review.html +0 -146
- package/skills/talent-creator/eval-viewer/generate_review.py +0 -471
- package/skills/talent-creator/eval-viewer/viewer.html +0 -1325
- package/skills/talent-creator/references/schemas.md +0 -430
- package/skills/talent-creator/scripts/__init__.py +0 -0
- package/skills/talent-creator/scripts/aggregate_benchmark.py +0 -401
- package/skills/talent-creator/scripts/generate_report.py +0 -326
- package/skills/talent-creator/scripts/improve_description.py +0 -247
- package/skills/talent-creator/scripts/package_skill.py +0 -136
- package/skills/talent-creator/scripts/quick_validate.py +0 -146
- package/skills/talent-creator/scripts/run_eval.py +0 -310
- package/skills/talent-creator/scripts/run_loop.py +0 -328
- package/skills/talent-creator/scripts/utils.py +0 -47
- package/skills/xlsx/SKILL.md +0 -300
- package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- package/skills/xlsx/scripts/office/helpers/merge_runs.py +0 -199
- package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +0 -197
- package/skills/xlsx/scripts/office/pack.py +0 -159
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +0 -75
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +0 -560
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +0 -67
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +0 -14
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/skills/xlsx/scripts/office/soffice.py +0 -183
- package/skills/xlsx/scripts/office/unpack.py +0 -132
- package/skills/xlsx/scripts/office/validate.py +0 -117
- package/skills/xlsx/scripts/office/validators/__init__.py +0 -15
- package/skills/xlsx/scripts/office/validators/base.py +0 -851
- package/skills/xlsx/scripts/office/validators/docx.py +0 -446
- package/skills/xlsx/scripts/office/validators/pptx.py +0 -275
- package/skills/xlsx/scripts/office/validators/redlining.py +0 -247
- package/skills/xlsx/scripts/recalc.py +0 -184
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
# ext-apps messaging — widget ↔ host ↔ server
|
|
2
|
-
|
|
3
|
-
The `@modelcontextprotocol/ext-apps` package gives you the `App` class (browser side) plus the `registerAppTool`/`registerAppResource` helpers (server side). The messaging is two-way and persistent.
|
|
4
|
-
|
|
5
|
-
## Construction
|
|
6
|
-
|
|
7
|
-
```js
|
|
8
|
-
const app = new App(
|
|
9
|
-
{ name: "MyWidget", version: "1.0.0" },
|
|
10
|
-
{}, // capabilities
|
|
11
|
-
{ autoResize: true }, // options
|
|
12
|
-
);
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
`autoResize: true` hooks up a `ResizeObserver` that fires `ui/notifications/size-changed` so the host iframe's height follows your rendered content. Without it the frame stays fixed-height and tall renders get clipped — turn it on for any widget whose height depends on the data.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Widget → Host
|
|
20
|
-
|
|
21
|
-
### `app.sendMessage({ role, content })`
|
|
22
|
-
|
|
23
|
-
Push a visible message into the conversation. This is the path by which user actions become conversation turns.
|
|
24
|
-
|
|
25
|
-
```js
|
|
26
|
-
app.sendMessage({
|
|
27
|
-
role: "user",
|
|
28
|
-
content: [{ type: "text", text: "User selected order #1234" }],
|
|
29
|
-
});
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
The message shows up in chat and WormClaude responds to it. Use `role: "user"` — the widget is speaking on the user's behalf.
|
|
33
|
-
|
|
34
|
-
### `app.updateModelContext({ content })`
|
|
35
|
-
|
|
36
|
-
Update WormClaude's context **silently**, with no visible message. Use it for state that's worth knowing but doesn't deserve its own chat bubble.
|
|
37
|
-
|
|
38
|
-
```js
|
|
39
|
-
app.updateModelContext({
|
|
40
|
-
content: [{ type: "text", text: "Currently viewing: orders from last 30 days" }],
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### `app.callServerTool({ name, arguments })`
|
|
45
|
-
|
|
46
|
-
Call a tool on your MCP server directly, going around WormClaude. Returns the tool result.
|
|
47
|
-
|
|
48
|
-
```js
|
|
49
|
-
const result = await app.callServerTool({
|
|
50
|
-
name: "fetch_order_details",
|
|
51
|
-
arguments: { orderId: "1234" },
|
|
52
|
-
});
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Use it for data fetches that don't need WormClaude's reasoning — pagination, detail lookups, refreshes.
|
|
56
|
-
|
|
57
|
-
### `app.openLink({ url })`
|
|
58
|
-
|
|
59
|
-
Open a URL in a new browser tab, brokered by the host. **Required** for any outbound navigation — the iframe sandbox blocks `window.open()` and `<a target="_blank">`.
|
|
60
|
-
|
|
61
|
-
```js
|
|
62
|
-
await app.openLink({ url: "https://example.com/cart" });
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
For anchors in rendered HTML, catch the click:
|
|
66
|
-
|
|
67
|
-
```js
|
|
68
|
-
card.querySelector("a").addEventListener("click", (e) => {
|
|
69
|
-
e.preventDefault();
|
|
70
|
-
app.openLink({ url: e.currentTarget.href });
|
|
71
|
-
});
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### `app.downloadFile({ name, mimeType, content })`
|
|
75
|
-
|
|
76
|
-
A host-brokered download (the sandbox blocks a direct `<a download>`). `content` is a base64 string.
|
|
77
|
-
|
|
78
|
-
```js
|
|
79
|
-
const csv = rows.map((r) => Object.values(r).join(",")).join("\n");
|
|
80
|
-
app.downloadFile({
|
|
81
|
-
name: "export.csv",
|
|
82
|
-
mimeType: "text/csv",
|
|
83
|
-
content: btoa(unescape(encodeURIComponent(csv))),
|
|
84
|
-
});
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### `app.requestDisplayMode({ mode })`
|
|
88
|
-
|
|
89
|
-
Ask the host to move the widget between `"inline"`, `"pip"`, and `"fullscreen"`. Check `getHostContext().availableDisplayModes` first, and hide the control when a mode isn't on offer. The host answers by firing `onhostcontextchanged` with a new `displayMode` and `containerDimensions` — re-render at the new size.
|
|
90
|
-
|
|
91
|
-
```js
|
|
92
|
-
if (app.getHostContext()?.availableDisplayModes?.includes("fullscreen")) {
|
|
93
|
-
expandBtn.hidden = false;
|
|
94
|
-
expandBtn.onclick = () => app.requestDisplayMode({ mode: "fullscreen" });
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## Host → Widget
|
|
101
|
-
|
|
102
|
-
### `app.ontoolresult = ({ content }) => {...}`
|
|
103
|
-
|
|
104
|
-
Fires when the tool handler's return value is fed to the widget. This is the main data-in path.
|
|
105
|
-
|
|
106
|
-
```js
|
|
107
|
-
app.ontoolresult = ({ content }) => {
|
|
108
|
-
const data = JSON.parse(content[0].text);
|
|
109
|
-
renderUI(data);
|
|
110
|
-
};
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**Set this BEFORE `await app.connect()`** — the result can land right after the connection opens.
|
|
114
|
-
|
|
115
|
-
### `app.ontoolinput = ({ arguments }) => {...}`
|
|
116
|
-
|
|
117
|
-
Fires with the arguments WormClaude passed to the tool. Handy when the widget needs to know what was asked for (e.g., to highlight the search term).
|
|
118
|
-
|
|
119
|
-
### `app.ontoolinputpartial = ({ arguments }) => {...}` / `app.ontoolcancelled = () => {...}`
|
|
120
|
-
|
|
121
|
-
`ontoolinputpartial` fires while WormClaude is still streaming arguments — use it to show a skeleton ("Preparing: <title>…") before the result arrives. `ontoolcancelled` fires when the call is aborted; clear the skeleton then.
|
|
122
|
-
|
|
123
|
-
### `app.getHostContext()` / `app.onhostcontextchanged = (ctx) => {...}`
|
|
124
|
-
|
|
125
|
-
Read the host context and subscribe to its changes. Call `getHostContext()` **after** `connect()`, and subscribe for live updates (the user flips on dark mode, expands to fullscreen).
|
|
126
|
-
|
|
127
|
-
| `ctx.` field | Use |
|
|
128
|
-
|---|---|
|
|
129
|
-
| `theme` | `"light"` / `"dark"` — toggle a `.dark` class |
|
|
130
|
-
| `styles.variables` | Host CSS tokens — pass to `applyHostStyleVariables()` so colors/fonts match host chrome |
|
|
131
|
-
| `displayMode` / `availableDisplayModes` | Current mode and which `requestDisplayMode` targets are valid |
|
|
132
|
-
| `containerDimensions.{maxHeight,width}` | Size your render to this instead of hard-coded px |
|
|
133
|
-
| `deviceCapabilities.touch` | Switch hover-only affordances to tap (`pointerdown`) |
|
|
134
|
-
| `safeAreaInsets` | Padding for notches / composer overlay |
|
|
135
|
-
|
|
136
|
-
```js
|
|
137
|
-
const applyTheme = (t) =>
|
|
138
|
-
document.documentElement.classList.toggle("dark", t === "dark");
|
|
139
|
-
|
|
140
|
-
app.onhostcontextchanged = (ctx) => applyTheme(ctx.theme);
|
|
141
|
-
await app.connect();
|
|
142
|
-
applyTheme(app.getHostContext()?.theme);
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
Keep colors in CSS custom properties with a `:root.dark {}` override block, and set `color-scheme: light | dark` so native form controls follow along.
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## Server → Widget (progress)
|
|
150
|
-
|
|
151
|
-
For long-running operations, emit progress notifications. The client supplies a `progressToken` in the request's `_meta`, and the server emits against it.
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
// In the tool handler
|
|
155
|
-
async ({ query }, extra) => {
|
|
156
|
-
const token = extra._meta?.progressToken;
|
|
157
|
-
for (let i = 0; i < steps.length; i++) {
|
|
158
|
-
if (token !== undefined) {
|
|
159
|
-
await extra.sendNotification({
|
|
160
|
-
method: "notifications/progress",
|
|
161
|
-
params: { progressToken: token, progress: i, total: steps.length, message: steps[i].name },
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
await steps[i].run();
|
|
165
|
-
}
|
|
166
|
-
return { content: [{ type: "text", text: "Complete" }] };
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
No `{ notify }` destructure — `extra` is `RequestHandlerExtra`; progress goes through `sendNotification`.
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
174
|
-
## Lifecycle
|
|
175
|
-
|
|
176
|
-
1. WormClaude calls a tool that declares `_meta.ui.resourceUri`
|
|
177
|
-
2. The host fetches the resource (your HTML) and mounts a **fresh iframe** for this call
|
|
178
|
-
3. The widget script runs, registers handlers, and calls `await app.connect()`
|
|
179
|
-
4. The host feeds the tool's return value in → `ontoolresult` fires
|
|
180
|
-
5. The widget renders and the user interacts with it
|
|
181
|
-
6. The widget calls `sendMessage` / `updateModelContext` / `callServerTool` as needed
|
|
182
|
-
7. The iframe stays in the transcript; **the next call to the same tool mounts another iframe** beside it
|
|
183
|
-
|
|
184
|
-
There's no explicit "submit and close" — each instance is long-lived, and instances are never reused across calls.
|
|
185
|
-
|
|
186
|
-
### Supersession
|
|
187
|
-
|
|
188
|
-
Since earlier instances stay mounted, a click on a stale widget can `sendMessage` after a newer one has rendered. Catch this with a `BroadcastChannel` and make the older instances inert:
|
|
189
|
-
|
|
190
|
-
```js
|
|
191
|
-
let superseded = false;
|
|
192
|
-
const seq = Date.now() + Math.random();
|
|
193
|
-
const bc = new BroadcastChannel("my-widget");
|
|
194
|
-
bc.onmessage = (e) => {
|
|
195
|
-
if (e.data?.seq > seq) {
|
|
196
|
-
superseded = true;
|
|
197
|
-
document.body.classList.add("superseded"); // opacity:.45; pointer-events:none
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
bc.postMessage({ seq });
|
|
201
|
-
|
|
202
|
-
// Guard outbound calls:
|
|
203
|
-
function safeSend(msg) {
|
|
204
|
-
if (!superseded) app.sendMessage(msg);
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## Sandbox & CSP gotchas
|
|
211
|
-
|
|
212
|
-
The iframe runs under both an HTML `sandbox` attribute **and** a strict Content-Security-Policy. In practice almost nothing external gets through — widgets should be fully self-contained.
|
|
213
|
-
|
|
214
|
-
| Symptom | Cause | Fix |
|
|
215
|
-
|---|---|---|
|
|
216
|
-
| Widget is a blank rectangle, nothing renders | CDN `import` of ext-apps blocked (transitive SDK fetches) | **Inline** the `ext-apps/app-with-deps` bundle — see `iframe-sandbox.md` |
|
|
217
|
-
| Widget renders but JS doesn't run | Inline event handlers blocked | Use `addEventListener` — never `onclick="..."` in HTML |
|
|
218
|
-
| `eval` / `new Function` errors | Script-src restriction | Don't use them; use JSON.parse for data |
|
|
219
|
-
| `fetch()` to your API fails | Cross-origin blocked | Route through `app.callServerTool()` instead |
|
|
220
|
-
| External CSS doesn't load | `style-src` restriction | Inline styles in a `<style>` tag |
|
|
221
|
-
| Fonts don't load | `font-src` restriction | Use system fonts (`font: 14px system-ui`) |
|
|
222
|
-
| External `<img src>` broken | CSP `img-src` + referrer hotlink blocking | Fetch server-side, inline as `data:` URL in the tool result payload |
|
|
223
|
-
| `window.open()` does nothing | Sandbox lacks `allow-popups` | Use `app.openLink({url})` |
|
|
224
|
-
| `<a target="_blank">` does nothing | Same | Intercept click → `preventDefault()` → `app.openLink` |
|
|
225
|
-
| Edited HTML doesn't appear in Desktop | Desktop caches UI resources | Fully quit (⌘Q) + relaunch, not just window-close |
|
|
226
|
-
|
|
227
|
-
When you're unsure, open the **iframe's own** devtools console (not the main app's) — CSP violations are logged there. See `iframe-sandbox.md` for the bundle-inlining pattern.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Connector-directory submission checklist
|
|
2
|
-
|
|
3
|
-
Run through this before submitting a remote MCP app to the WormClaude connector
|
|
4
|
-
directory. Every item is a hard review criterion.
|
|
5
|
-
|
|
6
|
-
| Area | Requirement |
|
|
7
|
-
|---|---|
|
|
8
|
-
| **Auth** | OAuth (DCR or CIMD) or **`none`** (authless). Static bearer tokens are private-deploy only and block listing. Authless is fine for public-data servers — the server holds any upstream API keys itself. |
|
|
9
|
-
| **Tool annotations** | Every tool sets `annotations.title` plus the relevant hints: `readOnlyHint: true` for fetch/search tools, `destructiveHint` / `idempotentHint` for writes, `openWorldHint: true` when the tool reaches an external system. |
|
|
10
|
-
| **Tool names** | ≤ 64 characters, snake/kebab case. |
|
|
11
|
-
| **Widget layout** | Inline height ≤ 500px, no nested scroll containers, 44pt minimum touch targets, WCAG-AA contrast in both themes. |
|
|
12
|
-
| **Theming** | `html, body { background: transparent }`, `<meta name="color-scheme" content="light dark">`, and pull in host CSS tokens via `applyHostStyleVariables`. |
|
|
13
|
-
| **External links** | Use `app.openLink`. List each origin (e.g. `https://api.example.com`) in the connector's *Allowed link URIs* so the link skips the confirm modal. |
|
|
14
|
-
| **Helper tools** | Widget-only tools (geometry/image fetchers) carry `_meta.ui.visibility: ["app"]` so they stay out of WormClaude's tool list. |
|
|
15
|
-
| **Screenshots** | 3–5 PNGs, ≥ 1000px wide, cropped to the app response alone — no prompt text in frame. |
|
|
16
|
-
|
|
17
|
-
See `abuse-protection.md` for rate-limit and IP-tiering guidance once the
|
|
18
|
-
authless endpoint goes public.
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# Iframe sandbox constraints
|
|
2
|
-
|
|
3
|
-
MCP-app widgets run inside a sandboxed `<iframe>` in the host (WormClaude Desktop,
|
|
4
|
-
claude.ai). The sandbox and CSP attributes tightly constrain what the widget can
|
|
5
|
-
do. Each item below was seen failing as a silently blank iframe until the fix
|
|
6
|
-
went in — the error shows up only in the iframe's own devtools console, never
|
|
7
|
-
the host's.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Problem → fix table
|
|
12
|
-
|
|
13
|
-
| Symptom | Root cause | Fix |
|
|
14
|
-
|---|---|---|
|
|
15
|
-
| Widget renders as blank rectangle, no error | CSP `script-src` blocks esm.sh fetching transitive `@modelcontextprotocol/sdk` deps | Inline the `ext-apps/app-with-deps` bundle into the HTML |
|
|
16
|
-
| `window.open()` does nothing | Sandbox lacks `allow-popups` | Use `app.openLink({ url })` |
|
|
17
|
-
| `<a target="_blank">` does nothing | Same | `e.preventDefault()` + `app.openLink({ url })` on click |
|
|
18
|
-
| External `<img src>` broken | CSP `img-src` + referrer hotlink blocking | Fetch server-side, ship as `data:` URL in the tool result payload |
|
|
19
|
-
| Widget edits don't appear after server restart | Host caches UI resources | Fully quit the host (⌘Q / Alt+F4) and relaunch |
|
|
20
|
-
| Top-level `await` throws | Older iframe contexts | Wrap module body in an async IIFE |
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
## Inlining the ext-apps bundle
|
|
25
|
-
|
|
26
|
-
`@modelcontextprotocol/ext-apps` provides a self-contained browser build via the
|
|
27
|
-
`app-with-deps` export (~300KB). It's minified ESM that ends in `export{…}`; to
|
|
28
|
-
consume it from an inline `<script type="module">` block, rewrite that export
|
|
29
|
-
statement into a global assignment at build time:
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
import { readFileSync } from "node:fs";
|
|
33
|
-
import { createRequire } from "node:module";
|
|
34
|
-
const require = createRequire(import.meta.url);
|
|
35
|
-
|
|
36
|
-
const bundle = readFileSync(
|
|
37
|
-
require.resolve("@modelcontextprotocol/ext-apps/app-with-deps"),
|
|
38
|
-
"utf8",
|
|
39
|
-
).replace(/export\{([^}]+)\};?\s*$/, (_, body) =>
|
|
40
|
-
"globalThis.ExtApps={" +
|
|
41
|
-
body.split(",").map((pair) => {
|
|
42
|
-
const [local, exported] = pair.split(" as ").map((s) => s.trim());
|
|
43
|
-
return `${exported ?? local}:${local}`;
|
|
44
|
-
}).join(",") + "};",
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
const widgetHtml = readFileSync("./widgets/widget.html", "utf8")
|
|
48
|
-
.replace("/*__EXT_APPS_BUNDLE__*/", () => bundle);
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Widget side:
|
|
52
|
-
|
|
53
|
-
```html
|
|
54
|
-
<script type="module">
|
|
55
|
-
/*__EXT_APPS_BUNDLE__*/
|
|
56
|
-
const { App } = globalThis.ExtApps;
|
|
57
|
-
(async () => {
|
|
58
|
-
const app = new App({ name: "…", version: "…" }, {});
|
|
59
|
-
// …
|
|
60
|
-
})();
|
|
61
|
-
</script>
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Using the `() => bundle` replacer form (instead of a bare string) matters —
|
|
65
|
-
`String.replace` treats `$…` sequences specially in a string replacement, and
|
|
66
|
-
the minified bundle is packed with them.
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Outbound links
|
|
71
|
-
|
|
72
|
-
```js
|
|
73
|
-
// ✗ blocked
|
|
74
|
-
window.open(url, "_blank");
|
|
75
|
-
// ✗ blocked
|
|
76
|
-
<a href="…" target="_blank">…</a>
|
|
77
|
-
|
|
78
|
-
// ✓ host-mediated
|
|
79
|
-
await app.openLink({ url });
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Intercept anchor clicks:
|
|
83
|
-
|
|
84
|
-
```js
|
|
85
|
-
el.addEventListener("click", (e) => {
|
|
86
|
-
e.preventDefault();
|
|
87
|
-
app.openLink({ url: el.href });
|
|
88
|
-
});
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
## External images
|
|
94
|
-
|
|
95
|
-
The default CSP `img-src` (plus many CDN referrer policies) stops
|
|
96
|
-
`<img src="https://external-cdn/…">` from loading. Inline them server-side in
|
|
97
|
-
the tool handler:
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
async function toDataUrl(url: string): Promise<string | undefined> {
|
|
101
|
-
try {
|
|
102
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
103
|
-
if (!res.ok) return undefined;
|
|
104
|
-
const buf = Buffer.from(await res.arrayBuffer());
|
|
105
|
-
const mime = res.headers.get("content-type") ?? "image/jpeg";
|
|
106
|
-
return `data:${mime};base64,${buf.toString("base64")}`;
|
|
107
|
-
} catch {
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// in the tool handler
|
|
113
|
-
const inlined = await Promise.all(
|
|
114
|
-
items.map(async (it) =>
|
|
115
|
-
it.thumb ? { ...it, thumb: await toDataUrl(it.thumb) ?? it.thumb } : it,
|
|
116
|
-
),
|
|
117
|
-
);
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Add `referrerpolicy="no-referrer"` on the `<img>` as a fallback for any URL
|
|
121
|
-
that ends up not inlined.
|
|
122
|
-
|
|
123
|
-
---
|
|
124
|
-
|
|
125
|
-
## Theme & host styles
|
|
126
|
-
|
|
127
|
-
The host draws the iframe inside its own card chrome — so paint a **transparent** background and pull in the host CSS tokens so the widget blends in across light/dark and across different hosts.
|
|
128
|
-
|
|
129
|
-
```html
|
|
130
|
-
<meta name="color-scheme" content="light dark" />
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
```css
|
|
134
|
-
:root {
|
|
135
|
-
--ink: var(--color-text-primary, #0f1111);
|
|
136
|
-
--sub: var(--color-text-secondary, #5a6270);
|
|
137
|
-
--line: var(--color-border-default, #e3e6ea);
|
|
138
|
-
}
|
|
139
|
-
html, body { background: transparent; color: var(--ink); }
|
|
140
|
-
:root.dark .thumb { mix-blend-mode: normal; } /* multiply → images vanish in dark */
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
```js
|
|
144
|
-
const { App, applyHostStyleVariables } = globalThis.ExtApps;
|
|
145
|
-
|
|
146
|
-
function applyHostContext(ctx) {
|
|
147
|
-
document.documentElement.classList.toggle("dark", ctx?.theme === "dark");
|
|
148
|
-
if (ctx?.styles?.variables) applyHostStyleVariables(ctx.styles.variables);
|
|
149
|
-
}
|
|
150
|
-
app.onhostcontextchanged = applyHostContext;
|
|
151
|
-
await app.connect();
|
|
152
|
-
applyHostContext(app.getHostContext());
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
`applyHostStyleVariables` writes the host's `--color-*` / `--font-*` / `--border-radius-*` tokens onto `:root`; the hex values above act as fallbacks for hosts that don't provide them.
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
## Debugging
|
|
160
|
-
|
|
161
|
-
The iframe has a console of its own. In WormClaude Desktop, open DevTools (View →
|
|
162
|
-
Toggle Developer Tools), then flip the context dropdown (top-left of the Console
|
|
163
|
-
tab) from "top" to the widget's iframe. CSP violations, uncaught exceptions, and
|
|
164
|
-
import errors all show up there — the host's main console stays quiet.
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# Payload budgeting
|
|
2
|
-
|
|
3
|
-
Hosts cap tool-result text. claude.ai and WormClaude Desktop truncate at roughly
|
|
4
|
-
**150,000 characters**; Claude Code at ~25k tokens. Once a tool result goes over
|
|
5
|
-
the cap, the host swaps in a file-pointer string in place of your JSON. The
|
|
6
|
-
widget then gets non-JSON in `ontoolresult`, `JSON.parse` throws, and the user
|
|
7
|
-
sees something like *"Bad payload: SyntaxError: Unexpected token 'E'"* — with no
|
|
8
|
-
clue that size was the culprit.
|
|
9
|
-
|
|
10
|
-
## Symptom → cause
|
|
11
|
-
|
|
12
|
-
| Symptom | Likely cause |
|
|
13
|
-
|---|---|
|
|
14
|
-
| Widget shows a JSON parse error on `content[0].text` | Result topped the host cap; the host swapped in a file-pointer string |
|
|
15
|
-
| Works for one query, breaks for "all of X" | Row count × column count went past the cap |
|
|
16
|
-
| Works in MCP Inspector, breaks in Desktop | The Inspector has no cap; Desktop does |
|
|
17
|
-
|
|
18
|
-
## Strategy
|
|
19
|
-
|
|
20
|
-
Cap your own payload at ~130KB and step down in this order:
|
|
21
|
-
|
|
22
|
-
1. **Send full rows** while `JSON.stringify(rows).length` stays under the cap.
|
|
23
|
-
2. **Prune columns** down to the ones the rendering spec actually references.
|
|
24
|
-
Scan the spec for both `field: "..."` keys *and* `datum.X` / `datum['X']`
|
|
25
|
-
inside expression strings — when the spec aliases a column through a
|
|
26
|
-
`calculate` transform, the alias shows up as `field:` but the source column
|
|
27
|
-
only appears as `datum.X`, and dropping it leaves the widget with NaN.
|
|
28
|
-
3. **Truncate rows** only as a last resort, and put `{ truncated: N }` in the
|
|
29
|
-
payload so the widget can label it.
|
|
30
|
-
|
|
31
|
-
```ts
|
|
32
|
-
const MAX = 130_000;
|
|
33
|
-
let out = rows;
|
|
34
|
-
if (JSON.stringify(out).length > MAX) {
|
|
35
|
-
const keep = referencedFields(spec); // field: + datum.X refs
|
|
36
|
-
out = rows.map((r) => pick(r, keep));
|
|
37
|
-
if (JSON.stringify(out).length > MAX) {
|
|
38
|
-
const per = JSON.stringify(out[0] ?? {}).length || 1;
|
|
39
|
-
out = out.slice(0, Math.floor(MAX / per));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Heavy assets go via `callServerTool`, not the result
|
|
45
|
-
|
|
46
|
-
Geometry, image bytes, or any blob the widget needs but WormClaude doesn't should
|
|
47
|
-
come from a separate tool the widget calls after it mounts:
|
|
48
|
-
|
|
49
|
-
```js
|
|
50
|
-
const topo = await app.callServerTool({ name: "get-topojson", arguments: { level } });
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Tag that helper tool with `_meta.ui.visibility: ["app"]` so it stays out of
|
|
54
|
-
WormClaude's tool list.
|