Skip to content

example(math_ui): a toolkit that ships a tool and its user interface [TOO-1999] - #925

Open
EricGustin wants to merge 8 commits into
ericgustin/too-1936-worker-resource-endpointsfrom
ericgustin/math-sumlist-ui-demo
Open

example(math_ui): a toolkit that ships a tool and its user interface [TOO-1999]#925
EricGustin wants to merge 8 commits into
ericgustin/too-1936-worker-resource-endpointsfrom
ericgustin/math-sumlist-ui-demo

Conversation

@EricGustin

@EricGustin EricGustin commented Aug 27, 2026

Copy link
Copy Markdown
Member

Stacked on #922, so it sits on all six of the M1 arcade-mcp PRs. Not part of an issue: this is the demo for the milestone.

A runnable example of the whole path M1 opens. A toolkit declares a static resource, the worker serves it under /worker/resources/*, and a caller reads back the bytes the toolkit shipped.

What it adds

File What it does
arcade_math/tools/arithmetic.py @tool sum_list, which resolves as Math.SumList
arcade_math/ui/resources.py @resource(path="sum-list.html"), which resolves as ui://math/0.1.0/sum-list.html
arcade_math/ui/sum_list.html The document, self-contained with no external assets

The author writes only the path. The framework builds the URI at registration from the toolkit's name and installed version. The mime type is text/html;profile=mcp-app, which the MCP Apps extension pins exactly and the reference host compares with string equality.

It runs

Total tools loaded: 1
Total resources loaded: 1
Worker routes enabled at /worker/* (ARCADE_WORKER_SECRET is set)

POST /worker/resources/list:

{"resources":[{"name":"sum_list_ui","title":"Sum a list of numbers","uri":"ui://math/0.1.0/sum-list.html","description":"Interface for the SumList tool: enter numbers, see the running total.","mimeType":"text/html;profile=mcp-app"}]}

POST /worker/resources/read returns the 3072-byte document under contents[0].text with the same mime type. The engine's own client reads the same resource through the manual test in ArcadeAI/monorepo#3659. Full commands in the README.

Two things it deliberately does not do

The tool does not advertise its interface. A host learns a tool has one by reading _meta.ui.resourceUri, and ToolDefinition has no _meta field, so nothing connects Math.SumList to its URI except the two being in the same toolkit. Nothing renders the document either. Both sit above this leg.

Two things building it surfaced

Resources are invisible to local-file discovery. analyze_files_for_tools scans for @tool only and never calls add_toolkit, so pointing the server at a directory of loose Python files finds the tools and silently none of the resources. Only the installed-package path registers them. That is consistent with the hosted worker, which installs packages, but a Builder iterating locally would watch a declaration disappear without a word.

The URI authority is not cased like the tool name. The tool is Math.SumList and the URI is ui://math/..., because qualification uses toolkit.name while the tool's fully-qualified name is display-cased. Both are stable and RFC 3986 calls an authority case-insensitive, so nothing breaks. Worth a decision rather than an accident, since a gateway correlating a tool with its interface will see both spellings.

Neither is fixed here. Flagging them where they were found.

Verification

uvx ruff@0.6.7 check and format --check clean. Verified by running it: worker up, both endpoints answering, and the engine client reading the document back with the mime type byte-identical to the listing.


Note

Low Risk
Documentation and isolated example code only; no changes to auth, worker routing, or production libraries in this diff.

Overview
Adds a new examples/toolkits/math_ui demo package that shows a toolkit shipping both a tool and a host-renderable UI over the worker /worker/resources/* path.

The arcade_math package registers @tool sum_list (Math.SumList) and @resource for sum-list.html, which resolves to ui://math/0.1.0/sum-list.html with mime type text/html;profile=mcp-app. The HTML is bundled via Hatch force-include and loaded at registration as in-memory text. pyproject.toml wires monorepo uv editable sources and a dev extra for arcade-mcp-server.

The README documents local worker startup (JWT + list/read curls) and points to the engine’s manual end-to-end resource test. The example intentionally does not link the tool to the resource via _meta.ui.resourceUri or render the UI in-process.

Reviewed by Cursor Bugbot for commit 26b6e67. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Adds a runnable example of the whole path this milestone opens: a toolkit
declares a static resource, the worker serves it under /worker/resources/*, and
a caller reads back the bytes the toolkit shipped.

SumList adds a list of numbers and resolves as Math.SumList. sum-list.html is
the interface for it and resolves as ui://math/0.1.0/sum-list.html, built at
registration from the toolkit's name and installed version rather than typed by
the author. The mime type is text/html;profile=mcp-app, which the MCP Apps
extension pins exactly.

The document is self-contained with no external assets, so it renders without
the content-security-policy allowlists that a resource cannot yet declare.

The distribution is named arcade_math so the tool reads as Math.SumList. That
shadows the published toolkit of the same name, so the README says to install it
in its own environment. Examples are excluded from the release workflow.

The README carries the commands end to end: install, start the worker, mint a
token, call both endpoints, and read the same resource from the engine's own
client.

Two things the example deliberately does not do, both above this leg: the tool
does not advertise its interface, because ToolDefinition has no _meta field, and
nothing renders the document.
@EricGustin
EricGustin force-pushed the ericgustin/math-sumlist-ui-demo branch from 1176927 to 2755613 Compare August 27, 2026 23:09

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@EricGustin EricGustin changed the title example(math_ui): a toolkit that ships a tool and its user interface example(math_ui): a toolkit that ships a tool and its user interface [TOO-1999] Aug 29, 2026

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant