holon-ui 0.0.1-dev.2 → 0.0.1-dev.3

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
@@ -62,17 +62,16 @@ const order = await apps.orders.tools.lookup({ id: "A-1" }, { timeout: 5000 });
62
62
 
63
63
  An endpoint binding takes `endpoint`, and optionally `authProvider`, `fetch`, and `scoped: true` to close the session after every call. Otherwise a client keeps one session open; call `close()` when the page no longer needs it.
64
64
 
65
- A tool can stop and ask for input through an MCP elicitation. Bind `elicit` to answer it, and the call returns its usual typed output:
65
+ A tool can stop and ask for input through an MCP elicitation. Pass `elicit` to answer it, and the call returns its usual typed output:
66
66
 
67
67
  ```typescript
68
- const apps = createApps({
69
- orders: {
70
- endpoint: "https://orders.example.com/mcp",
71
- elicit: async ({ params }) => ({ action: "accept", content: await showForm(params) }),
72
- },
68
+ const { shipped } = await apps.orders.tools.ship({ id: "A-1" }, {
69
+ elicit: async params => ({ action: "accept", content: await showForm(params) }),
73
70
  });
74
71
  ```
75
72
 
73
+ Without `elicit`, the call throws `ToolCallError` with the question on `.result`.
74
+
76
75
  ## Read and watch resources
77
76
 
78
77
  A server types a JSON resource by registering it with `mimeType: "application/json"` and an `"sh.holon/outputSchema"` JSON Schema in its `_meta`:
@@ -88,7 +87,7 @@ await apps.orders.resources["orders://summary"].watch(value => render(value.open
88
87
 
89
88
  ## Give tools to the page's agent
90
89
 
91
- `exposeTools` registers the tools of an endpoint-bound client with the page's WebMCP context (`navigator.modelContext`), except those whose `_meta.ui.visibility` omits `"model"`. Calls from the agent go through the client, so they are validated. An agent can only supply arguments, so a question the binding's `elicit` does not answer reaches it as a failed result naming the question. It returns `false` when the browser has no WebMCP context.
90
+ `exposeTools` registers the tools of an endpoint-bound client with the page's WebMCP context (`navigator.modelContext`), except those whose `_meta.ui.visibility` omits `"model"`. Calls from the agent go through the client, so they are validated. An agent can only supply arguments, so a question reaches it as a failed call naming the question; to answer for it, pass an `execute` that calls `callTool` with `elicit`. It returns `false` when the browser has no WebMCP context.
92
91
 
93
92
  ```typescript
94
93
  await exposeTools(apps.orders, { signal: lifetime.signal });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "holon-ui",
3
- "version": "0.0.1-dev.2",
3
+ "version": "0.0.1-dev.3",
4
4
  "description": "Typed MCP clients for browser views: MCP Apps host bindings and WebMCP tool exposure.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,7 +39,7 @@
39
39
  "fixtures": "node scripts/fixtures.mjs"
40
40
  },
41
41
  "dependencies": {
42
- "holon-sh": "^0.0.1-dev.5"
42
+ "holon-sh": "^0.0.1-dev.6"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "@modelcontextprotocol/ext-apps": ">=1.0.0"
package/runtime.mjs CHANGED
@@ -33,10 +33,9 @@ export async function exposeTools(app, options = {}) {
33
33
  const { signal } = options;
34
34
  const execute = options.execute ?? (async (name, args) => {
35
35
  const result = await app.callTool({ name, arguments: args }, { signal });
36
- if (result.resultType !== "input_required") return result;
37
- // An agent can only supply arguments, so a question it receives would end the call anyway. Bind elicit to answer it.
38
- const questions = Object.values(result.inputRequests ?? {}).flatMap(request => request.params?.message ?? []);
39
- return { isError: true, content: [{ type: "text", text: [`${name} needs input this page cannot provide`, ...questions].join(": ") }] };
36
+ // An agent can only supply arguments, so it cannot answer. A custom execute can.
37
+ if (result.resultType === "input_required") throw new ToolCallError("page", name, result);
38
+ return result;
40
39
  });
41
40
  const { tools } = await app.listTools({ signal });
42
41
  for (const tool of tools) {