@noodleseed/agent-kit 0.39.0 → 0.41.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 +7 -5
- package/manifest.json +904 -191
- package/package.json +1 -1
- package/skills/claude-code/SKILL.md +16 -14
- package/skills/claude-code/authoring-mcp-servers/SKILL.md +48 -0
- package/skills/claude-code/building-mcp-apps/SKILL.md +48 -0
- package/skills/claude-code/connecting-apis-to-mcp/SKILL.md +47 -0
- package/skills/claude-code/debugging-mcp-delivery/SKILL.md +48 -0
- package/skills/claude-code/deploying-mcp-services/SKILL.md +47 -0
- package/skills/claude-code/designing-mcp-products/SKILL.md +47 -0
- package/skills/claude-code/embedding-mcp-assistants/SKILL.md +47 -0
- package/skills/claude-code/examples/customer-auth/README.md +143 -1
- package/skills/claude-code/publishing-mcp-integrations/SKILL.md +47 -0
- package/skills/claude-code/references/embedded-assistant.md +142 -15
- package/skills/claude-code/reporting-noodle-feedback/SKILL.md +46 -0
- package/skills/claude-code/verifying-mcp-delivery/SKILL.md +47 -0
- package/skills/codex/SKILL.md +16 -14
- package/skills/codex/authoring-mcp-servers/SKILL.md +48 -0
- package/skills/codex/building-mcp-apps/SKILL.md +48 -0
- package/skills/codex/connecting-apis-to-mcp/SKILL.md +47 -0
- package/skills/codex/debugging-mcp-delivery/SKILL.md +48 -0
- package/skills/codex/deploying-mcp-services/SKILL.md +47 -0
- package/skills/codex/designing-mcp-products/SKILL.md +47 -0
- package/skills/codex/embedding-mcp-assistants/SKILL.md +47 -0
- package/skills/codex/examples/customer-auth/README.md +143 -1
- package/skills/codex/publishing-mcp-integrations/SKILL.md +47 -0
- package/skills/codex/references/embedded-assistant.md +142 -15
- package/skills/codex/reporting-noodle-feedback/SKILL.md +46 -0
- package/skills/codex/verifying-mcp-delivery/SKILL.md +47 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: embedding-mcp-assistants
|
|
3
|
+
description: "Use when embedding a Noodle assistant into an existing SaaS or web application with browser, identity, session, and credential boundaries."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- noodle-skill version:0.41.0 hash:5d8f40f904d6ab4b -->
|
|
7
|
+
|
|
8
|
+
# embedding-mcp-assistants
|
|
9
|
+
|
|
10
|
+
Deliver the requested assistant embed with identity and credential separation proven at the tested level.
|
|
11
|
+
|
|
12
|
+
## Use when
|
|
13
|
+
|
|
14
|
+
- Embed the Noodle assistant in an existing web app.
|
|
15
|
+
- Wire browser mounting and session exchange.
|
|
16
|
+
|
|
17
|
+
## Do not use when
|
|
18
|
+
|
|
19
|
+
- Do not use to build a standalone MCP App.
|
|
20
|
+
- Do not use when the request is only server authoring or deployment.
|
|
21
|
+
|
|
22
|
+
## Required inputs
|
|
23
|
+
|
|
24
|
+
- Application origin and mounting point.
|
|
25
|
+
- Identity/session boundary.
|
|
26
|
+
- Requested local or hosted evidence level.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
Read and follow the canonical playbook `references/embedded-assistant.md` at `../noodle-seed/references/embedded-assistant.md`. It owns the workflow; do not recreate it here or load the command catalog speculatively.
|
|
31
|
+
Load `references/authoring-workflow.md` at `../noodle-seed/references/authoring-workflow.md` only when the playbook or observed evidence names that concern.
|
|
32
|
+
|
|
33
|
+
## Verification evidence
|
|
34
|
+
|
|
35
|
+
The embed works at the requested boundary without forwarding inbound credentials to business backends.
|
|
36
|
+
|
|
37
|
+
## Recovery paths
|
|
38
|
+
|
|
39
|
+
Localize failures to origin, session exchange, browser mount, MCP surface, or hosted configuration.
|
|
40
|
+
|
|
41
|
+
## Stop conditions
|
|
42
|
+
|
|
43
|
+
Stop when unavailable identity, origin, or hosted authority blocks the next evidence layer.
|
|
44
|
+
|
|
45
|
+
## Handoff contract
|
|
46
|
+
|
|
47
|
+
Pass the selected outcome, explicit target, changed files, commands run, passing evidence, first unproven evidence layer, sanitized failure, remaining authority, and exact next action. The receiving skill continues from that layer; do not restart discovery or discard prior proof.
|
|
@@ -169,7 +169,7 @@ customer backend. The model URL, model name, and model API key remain managed by
|
|
|
169
169
|
|
|
170
170
|
The customer's authenticated backend calls `createAssistantSession(...)` from
|
|
171
171
|
`@noodleseed/assistant/server`, passing the already-verified user and browser origin. The browser then uses
|
|
172
|
-
the returned short-lived session through the Web Component or
|
|
172
|
+
the returned short-lived session through the managed Web Component/React renderer or a customer-owned UI:
|
|
173
173
|
|
|
174
174
|
```bash
|
|
175
175
|
pnpm add @noodleseed/assistant
|
|
@@ -185,6 +185,148 @@ import { NoodleAssistant } from '@noodleseed/assistant/react';
|
|
|
185
185
|
/>;
|
|
186
186
|
```
|
|
187
187
|
|
|
188
|
+
For an entirely application-owned React renderer, use the renderer-free hook. It creates no custom element
|
|
189
|
+
and returns the AI SDK transcript plus the canonical client commands:
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
'use client';
|
|
193
|
+
|
|
194
|
+
import { useState } from 'react';
|
|
195
|
+
import { useNoodleAssistant } from '@noodleseed/assistant/react/client';
|
|
196
|
+
|
|
197
|
+
export function CustomerAssistant({ principalKey }: { principalKey: string }) {
|
|
198
|
+
const [draft, setDraft] = useState('');
|
|
199
|
+
const { client, messages, status, error } = useNoodleAssistant({
|
|
200
|
+
sessionEndpoint: '/api/noodle-assistant/session',
|
|
201
|
+
principalKey,
|
|
202
|
+
});
|
|
203
|
+
const busy = status === 'submitted' || status === 'streaming';
|
|
204
|
+
const settle = (operation: Promise<void>) => {
|
|
205
|
+
void operation.catch(() => {
|
|
206
|
+
// The hook exposes this same structured failure through `error`.
|
|
207
|
+
});
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<section aria-label="Assistant" aria-busy={busy}>
|
|
212
|
+
{messages.map((message) => (
|
|
213
|
+
<article key={message.id} data-role={message.role}>
|
|
214
|
+
{message.parts.map((part, index) => {
|
|
215
|
+
if (part.type === 'text') return <p key={index}>{part.text}</p>;
|
|
216
|
+
if (part.type === 'data-confirmation') {
|
|
217
|
+
const review = part.data;
|
|
218
|
+
return (
|
|
219
|
+
<section key={review.id} aria-label="Review proposed action">
|
|
220
|
+
<h3>{review.title ?? 'Review proposed action'}</h3>
|
|
221
|
+
{review.description ? <p>{review.description}</p> : null}
|
|
222
|
+
<pre aria-label="Proposed action arguments">
|
|
223
|
+
{JSON.stringify(review.arguments ?? {}, null, 2)}
|
|
224
|
+
</pre>
|
|
225
|
+
<button
|
|
226
|
+
disabled={busy || review.status !== 'pending'}
|
|
227
|
+
onClick={() => settle(client.respond(review.id, { action: 'accept' }))}
|
|
228
|
+
>
|
|
229
|
+
Confirm
|
|
230
|
+
</button>
|
|
231
|
+
<button
|
|
232
|
+
disabled={busy || review.status !== 'pending'}
|
|
233
|
+
onClick={() => settle(client.respond(review.id, { action: 'decline' }))}
|
|
234
|
+
>
|
|
235
|
+
Don't proceed
|
|
236
|
+
</button>
|
|
237
|
+
</section>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
if (part.type === 'data-input-request') {
|
|
241
|
+
const request = part.data;
|
|
242
|
+
return (
|
|
243
|
+
<section key={request.id} aria-label="Assistant needs input">
|
|
244
|
+
<p>{request.message}</p>
|
|
245
|
+
<p>This renderer has not implemented the requested form.</p>
|
|
246
|
+
<button
|
|
247
|
+
disabled={busy || request.status !== 'pending'}
|
|
248
|
+
onClick={() => settle(client.respond(request.id, { action: 'decline' }))}
|
|
249
|
+
>
|
|
250
|
+
Cancel request
|
|
251
|
+
</button>
|
|
252
|
+
</section>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
if (part.type === 'data-tool-result') {
|
|
256
|
+
return (
|
|
257
|
+
<pre key={part.data.id} aria-label={`${part.data.tool} result`}>
|
|
258
|
+
{JSON.stringify(part.data.result, null, 2)}
|
|
259
|
+
</pre>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
if (part.type === 'data-view') {
|
|
263
|
+
return (
|
|
264
|
+
<p key={part.data.id}>
|
|
265
|
+
Trusted app view available: {part.data.title ?? part.data.resourceUri}
|
|
266
|
+
</p>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return <p key={index}>Unsupported assistant content.</p>;
|
|
270
|
+
})}
|
|
271
|
+
</article>
|
|
272
|
+
))}
|
|
273
|
+
{error ? <p role="alert">{error.message}</p> : null}
|
|
274
|
+
<form
|
|
275
|
+
onSubmit={(event) => {
|
|
276
|
+
event.preventDefault();
|
|
277
|
+
const message = draft.trim();
|
|
278
|
+
if (!message) return;
|
|
279
|
+
setDraft('');
|
|
280
|
+
settle(client.sendMessage(message));
|
|
281
|
+
}}
|
|
282
|
+
>
|
|
283
|
+
<input
|
|
284
|
+
aria-label="Message"
|
|
285
|
+
value={draft}
|
|
286
|
+
onChange={(event) => setDraft(event.currentTarget.value)}
|
|
287
|
+
/>
|
|
288
|
+
{busy ? (
|
|
289
|
+
<button type="button" onClick={() => client.abort()}>
|
|
290
|
+
Stop
|
|
291
|
+
</button>
|
|
292
|
+
) : (
|
|
293
|
+
<button type="submit">Send</button>
|
|
294
|
+
)}
|
|
295
|
+
</form>
|
|
296
|
+
</section>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
`principalKey` stays in the browser. Change it whenever the authenticated user or tenant changes; the hook
|
|
302
|
+
then aborts and clears the prior session and transcript. The sample fails closed on input requests until its
|
|
303
|
+
fallback is replaced with a form generated from `requestedSchema`. A production renderer must show the
|
|
304
|
+
complete confirmation review and both decisions. For `data-view`, map `resourceUri` or `tool` and the
|
|
305
|
+
bounded/redacted result to a component already trusted by this application. Never inject `part.data.html`,
|
|
306
|
+
assign it to `srcdoc`, or fetch a `ui://` URI.
|
|
307
|
+
|
|
308
|
+
Outside React, subscribe to the DOM-free client directly. It exposes the same conversation as headless AI
|
|
309
|
+
SDK `UIMessage` state, including typed confirmation, input, tool-result, and linked-view parts:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import { createAssistantClient } from '@noodleseed/assistant/client';
|
|
313
|
+
|
|
314
|
+
const assistant = createAssistantClient({
|
|
315
|
+
sessionEndpoint: '/api/noodle-assistant/session',
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
assistant.subscribeChat((state) => {
|
|
319
|
+
renderUIMessageState(state);
|
|
320
|
+
for (const message of state.messages) {
|
|
321
|
+
for (const part of message.parts) {
|
|
322
|
+
if (part.type === 'data-confirmation' && part.data.status === 'pending') {
|
|
323
|
+
renderConfirmation(part.data, (response) => assistant.respond(part.data.id, response));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
188
330
|
`theme="auto"` follows the SaaS application. The server-level `branding` block is inherited by both MCP App
|
|
189
331
|
widgets and the assistant; documented `--ns-assistant-*` semantic CSS variables remain the final integration
|
|
190
332
|
escape hatch. There is no second assistant branding declaration.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: publishing-mcp-integrations
|
|
3
|
+
description: "Use when preparing, reviewing, or submitting a Noodle Seed MCP integration to a host or app directory."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- noodle-skill version:0.41.0 hash:efffbf82007f935d -->
|
|
7
|
+
|
|
8
|
+
# publishing-mcp-integrations
|
|
9
|
+
|
|
10
|
+
Produce complete submission evidence with host-review uncertainty stated explicitly.
|
|
11
|
+
|
|
12
|
+
## Use when
|
|
13
|
+
|
|
14
|
+
- Prepare this MCP integration for a directory.
|
|
15
|
+
- Review or submit the host listing.
|
|
16
|
+
|
|
17
|
+
## Do not use when
|
|
18
|
+
|
|
19
|
+
- Do not use for ordinary deployment.
|
|
20
|
+
- Do not submit when the user requested preparation or review only.
|
|
21
|
+
|
|
22
|
+
## Required inputs
|
|
23
|
+
|
|
24
|
+
- Target directory.
|
|
25
|
+
- Current deployment and verification evidence.
|
|
26
|
+
- Requested review, preparation, or submission boundary.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
Read and follow the canonical playbook `references/publishing.md` at `../noodle-seed/references/publishing.md`. It owns the workflow; do not recreate it here or load the command catalog speculatively.
|
|
31
|
+
Load `references/app-directory-compliance.md` at `../noodle-seed/references/app-directory-compliance.md` only when the playbook or observed evidence names that concern.
|
|
32
|
+
|
|
33
|
+
## Verification evidence
|
|
34
|
+
|
|
35
|
+
Required product, policy, deployment, media, and test evidence is present or explicitly missing.
|
|
36
|
+
|
|
37
|
+
## Recovery paths
|
|
38
|
+
|
|
39
|
+
Return missing implementation or evidence to its owning skill without restarting discovery.
|
|
40
|
+
|
|
41
|
+
## Stop conditions
|
|
42
|
+
|
|
43
|
+
Stop before external submission without explicit user authorization.
|
|
44
|
+
|
|
45
|
+
## Handoff contract
|
|
46
|
+
|
|
47
|
+
Pass the selected outcome, explicit target, changed files, commands run, passing evidence, first unproven evidence layer, sanitized failure, remaining authority, and exact next action. The receiving skill continues from that layer; do not restart discovery or discard prior proof.
|
|
@@ -248,7 +248,125 @@ Or import the package root once and mount `<noodle-assistant session-endpoint="/
|
|
|
248
248
|
|
|
249
249
|
The component renders a custom element and must mount client-side. In a Next.js App Router tree, put the mount in a `"use client"` component; from a server component or the Pages Router, load it with `next/dynamic` and `ssr: false`.
|
|
250
250
|
|
|
251
|
-
For a customer-owned renderer, use the
|
|
251
|
+
For a customer-owned React renderer, use the renderer-free hook. It owns client lifetime and React subscription while `client` remains the one command surface:
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
"use client";
|
|
255
|
+
|
|
256
|
+
import { useState } from "react";
|
|
257
|
+
import { useNoodleAssistant } from "@noodleseed/assistant/react/client";
|
|
258
|
+
|
|
259
|
+
export function CustomAssistant({ principalKey }: { principalKey: string }) {
|
|
260
|
+
const [draft, setDraft] = useState("");
|
|
261
|
+
const { client, messages, status, error } = useNoodleAssistant({
|
|
262
|
+
sessionEndpoint: "/api/assistant/session",
|
|
263
|
+
principalKey,
|
|
264
|
+
clientContext: () => ({
|
|
265
|
+
locale: navigator.language,
|
|
266
|
+
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
const busy = status === "submitted" || status === "streaming";
|
|
270
|
+
const settle = (operation: Promise<void>) => {
|
|
271
|
+
void operation.catch(() => {
|
|
272
|
+
// The hook exposes this same structured failure through `error`.
|
|
273
|
+
});
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
return (
|
|
277
|
+
<section aria-label="Assistant" aria-busy={busy}>
|
|
278
|
+
{messages.map((message) => (
|
|
279
|
+
<article key={message.id} data-role={message.role}>
|
|
280
|
+
{message.parts.map((part, index) => {
|
|
281
|
+
if (part.type === "text") return <p key={index}>{part.text}</p>;
|
|
282
|
+
if (part.type === "data-confirmation") {
|
|
283
|
+
const review = part.data;
|
|
284
|
+
return (
|
|
285
|
+
<section key={review.id} aria-label="Review proposed action">
|
|
286
|
+
<h3>{review.title ?? "Review proposed action"}</h3>
|
|
287
|
+
{review.description ? <p>{review.description}</p> : null}
|
|
288
|
+
<pre aria-label="Proposed action arguments">
|
|
289
|
+
{JSON.stringify(review.arguments ?? {}, null, 2)}
|
|
290
|
+
</pre>
|
|
291
|
+
<button
|
|
292
|
+
disabled={busy || review.status !== "pending"}
|
|
293
|
+
onClick={() => settle(client.respond(review.id, { action: "accept" }))}
|
|
294
|
+
>
|
|
295
|
+
Confirm
|
|
296
|
+
</button>
|
|
297
|
+
<button
|
|
298
|
+
disabled={busy || review.status !== "pending"}
|
|
299
|
+
onClick={() => settle(client.respond(review.id, { action: "decline" }))}
|
|
300
|
+
>
|
|
301
|
+
Don't proceed
|
|
302
|
+
</button>
|
|
303
|
+
</section>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
if (part.type === "data-input-request") {
|
|
307
|
+
const request = part.data;
|
|
308
|
+
return (
|
|
309
|
+
<section key={request.id} aria-label="Assistant needs input">
|
|
310
|
+
<p>{request.message}</p>
|
|
311
|
+
<p>This renderer has not implemented the requested form.</p>
|
|
312
|
+
<button
|
|
313
|
+
disabled={busy || request.status !== "pending"}
|
|
314
|
+
onClick={() => settle(client.respond(request.id, { action: "decline" }))}
|
|
315
|
+
>
|
|
316
|
+
Cancel request
|
|
317
|
+
</button>
|
|
318
|
+
</section>
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
if (part.type === "data-tool-result") {
|
|
322
|
+
return (
|
|
323
|
+
<pre key={part.data.id} aria-label={`${part.data.tool} result`}>
|
|
324
|
+
{JSON.stringify(part.data.result, null, 2)}
|
|
325
|
+
</pre>
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
if (part.type === "data-view") {
|
|
329
|
+
return (
|
|
330
|
+
<p key={part.data.id}>
|
|
331
|
+
Trusted app view available: {part.data.title ?? part.data.resourceUri}
|
|
332
|
+
</p>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
return <p key={index}>Unsupported assistant content.</p>;
|
|
336
|
+
})}
|
|
337
|
+
</article>
|
|
338
|
+
))}
|
|
339
|
+
{error ? <p role="alert">{error.message}</p> : null}
|
|
340
|
+
<form
|
|
341
|
+
onSubmit={(event) => {
|
|
342
|
+
event.preventDefault();
|
|
343
|
+
const message = draft.trim();
|
|
344
|
+
if (!message) return;
|
|
345
|
+
setDraft("");
|
|
346
|
+
settle(client.sendMessage(message));
|
|
347
|
+
}}
|
|
348
|
+
>
|
|
349
|
+
<input
|
|
350
|
+
aria-label="Message"
|
|
351
|
+
value={draft}
|
|
352
|
+
onChange={(event) => setDraft(event.currentTarget.value)}
|
|
353
|
+
/>
|
|
354
|
+
{busy ? (
|
|
355
|
+
<button type="button" onClick={() => client.abort()}>Stop</button>
|
|
356
|
+
) : (
|
|
357
|
+
<button type="submit">Send</button>
|
|
358
|
+
)}
|
|
359
|
+
</form>
|
|
360
|
+
</section>
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
`principalKey` is a browser-local identity for the authenticated user/tenant and is never sent to Noodle. Change it whenever that principal changes; the hook then aborts and clears the previous session and transcript. The hook does not register `<noodle-assistant>` or render Noodle markup.
|
|
366
|
+
|
|
367
|
+
The sample fails closed on input requests until you replace that branch with a form generated from `requestedSchema`. A custom renderer must show the complete confirmation review and both decisions, handle every part it supports, and surface an explicit unsupported state for the rest. For `data-view`, map `resourceUri` or `tool` plus the bounded/redacted `result` to a component already trusted by the application. Never inject `part.data.html`, assign it to `srcdoc`, or fetch a `ui://` URI; the managed element alone supplies Noodle’s sandbox host. Do not wrap this client in another chat transport or invent user messages for interaction continuations.
|
|
368
|
+
|
|
369
|
+
Outside React, use the same DOM-free client directly. It keeps the session token in memory, exposes a React-free `UIMessage` transcript with typed parts, and never registers a custom element:
|
|
252
370
|
|
|
253
371
|
```ts
|
|
254
372
|
import { createAssistantClient } from "@noodleseed/assistant/client";
|
|
@@ -266,30 +384,39 @@ assistant.updateModelContext({
|
|
|
266
384
|
structuredContent: { widget: { name: 'time-off', lifecycle: 'mounted' } },
|
|
267
385
|
});
|
|
268
386
|
|
|
269
|
-
let
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
387
|
+
let pending: { id: string; requestedSchema?: Readonly<Record<string, unknown>> } | undefined;
|
|
388
|
+
assistant.subscribeChat((state) => {
|
|
389
|
+
renderUIMessageState(state);
|
|
390
|
+
pending = undefined;
|
|
391
|
+
for (const message of state.messages) {
|
|
392
|
+
for (const part of message.parts) {
|
|
393
|
+
if (part.type === 'data-confirmation' && part.data.status === 'pending') {
|
|
394
|
+
pending = { id: part.data.id };
|
|
395
|
+
}
|
|
396
|
+
if (part.type === 'data-input-request' && part.data.status === 'pending') {
|
|
397
|
+
pending = { id: part.data.id, requestedSchema: part.data.requestedSchema };
|
|
398
|
+
}
|
|
399
|
+
if (part.type === 'data-view') {
|
|
400
|
+
renderRegisteredView(part.data.resourceUri, part.data.result);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
279
403
|
}
|
|
280
404
|
});
|
|
281
405
|
|
|
282
406
|
await assistant.sendMessage("Book next Thursday and Friday off");
|
|
283
|
-
if (
|
|
407
|
+
if (pending) {
|
|
408
|
+
const requestedSchema = pending.requestedSchema;
|
|
284
409
|
const resolution = requestedSchema
|
|
285
410
|
? { action: 'accept' as const, content: await renderPortableForm(requestedSchema) }
|
|
286
411
|
: { action: 'accept' as const };
|
|
287
|
-
await assistant.respond(
|
|
412
|
+
await assistant.respond(pending.id, resolution);
|
|
288
413
|
}
|
|
289
414
|
// The same pending id also accepts { action: 'decline' } or { action: 'cancel' }.
|
|
290
415
|
```
|
|
291
416
|
|
|
292
|
-
`
|
|
417
|
+
`subscribeChat` immediately emits a detached `{ messages, status, error? }` snapshot and then emits as `UIMessage.parts` change. Text uses `text`; Noodle confirmations, input requests, tool results, and linked views use `data-confirmation`, `data-input-request`, `data-tool-result`, and `data-view`. Interaction data moves through pending/submitting/accepted/declined/cancelled. Use raw `subscribe(...)` only for transport/session lifecycle events that are not transcript content.
|
|
418
|
+
|
|
419
|
+
`data-view` means a completed tool has a linked MCP App view. It carries the call/interaction id, tool, `ui://` identity, optional title, bounded/redacted public result, and—on current services—the self-contained bridged document. The standard element is an MCP Apps host and mounts that document behind a double iframe; a customer renderer ignores it and maps the identity/result to an application-trusted component. The standard element supports lifecycle, app tool/resource calls, ui/message, ui/update-model-context, links, resize, and inline/fullscreen; sampling, tasks, downloads, and remote DOM are not advertised. It also dispatches `assistant-view-available` for a customer-owned renderer.
|
|
293
420
|
|
|
294
421
|
`clientContext` and typed `pageContext` are recomputed for each turn. `updateContext(...)` remains the legacy session-exchange context; `updatePageContext(...)` replaces the fresh per-turn application hint. `updateModelContext({ content, structuredContent })` publishes one cohesive renderer snapshot for later message turns without starting a turn; every call replaces the prior snapshot rather than merging fields. These are untrusted data, not conversation history or authorization input, and the boundaries reject credential-shaped or unbounded updates. A message may re-exchange once after a pre-execution `401`; the client never auto-retries interaction decisions. `tool_proposed.arguments` is a complete schema-aware review projection and, for connector-backed tools, names the sole exact connector version/operation/resolved arguments. Sensitive/write-only fields are redacted; truncating or omitting any non-sensitive action field fails closed. Accept is bound to the server-held action and claims at most one execution attempt—clients cannot replace it. Normal terminal outcomes scrub private arguments and continuations immediately; only an accepted action still executing retains them for the one-hour unknown-outcome recovery window, after which it records `interaction_outcome_unknown` and scrubs. Without downstream idempotency this is not an exactly-once business-effect guarantee. To reconcile a lost response, explicitly repeat the same id and decision: the service returns its durable stored outcome without re-execution.
|
|
295
422
|
|
|
@@ -297,7 +424,7 @@ if (pendingId) {
|
|
|
297
424
|
|
|
298
425
|
- Node.js 20+ for `@noodleseed/assistant/server`.
|
|
299
426
|
- The package ships ESM and CommonJS with full export conditions; no bundler aliases, `transpilePackages`, or ambient type shims are needed. If resolution fails, the installed package version is outdated: update `@noodleseed/assistant` instead of adding workarounds.
|
|
300
|
-
- TypeScript `moduleResolution` `bundler` or `node16` recommended; classic `node` also resolves the `/client`, `/react`, and `/server` subpaths.
|
|
427
|
+
- TypeScript `moduleResolution` `bundler` or `node16` recommended; classic `node` also resolves the `/client`, `/react`, `/react/client`, and `/server` subpaths.
|
|
301
428
|
|
|
302
429
|
## Verify the boundary
|
|
303
430
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reporting-noodle-feedback
|
|
3
|
+
description: "Use when a Noodle Seed bug, misleading instruction, missing capability, or concrete product improvement should be proposed to the user."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- noodle-skill version:0.41.0 hash:1e23938b39956ef8 -->
|
|
7
|
+
|
|
8
|
+
# reporting-noodle-feedback
|
|
9
|
+
|
|
10
|
+
Offer one sanitized feedback command and submit it only after informed explicit approval.
|
|
11
|
+
|
|
12
|
+
## Use when
|
|
13
|
+
|
|
14
|
+
- Report a Noodle Seed bug or documentation gap.
|
|
15
|
+
- Propose a concrete Noodle product improvement.
|
|
16
|
+
|
|
17
|
+
## Do not use when
|
|
18
|
+
|
|
19
|
+
- Do not use for generic project bugs.
|
|
20
|
+
- Do not send customer code, identifiers, logs, or secrets.
|
|
21
|
+
|
|
22
|
+
## Required inputs
|
|
23
|
+
|
|
24
|
+
- One distinct finding.
|
|
25
|
+
- Sanitized observed and expected behavior.
|
|
26
|
+
- User approval for the exact command.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
Read and follow the canonical playbook `references/feedback.md` at `../noodle-seed/references/feedback.md`. It owns the workflow; do not recreate it here or load the command catalog speculatively.
|
|
31
|
+
|
|
32
|
+
## Verification evidence
|
|
33
|
+
|
|
34
|
+
The user saw the exact sanitized command; only a returned reference proves submission.
|
|
35
|
+
|
|
36
|
+
## Recovery paths
|
|
37
|
+
|
|
38
|
+
If login or rate limits block submission, report that nothing was sent and do not retry-loop.
|
|
39
|
+
|
|
40
|
+
## Stop conditions
|
|
41
|
+
|
|
42
|
+
Stop before running the command until the user explicitly approves it.
|
|
43
|
+
|
|
44
|
+
## Handoff contract
|
|
45
|
+
|
|
46
|
+
Pass the selected outcome, explicit target, changed files, commands run, passing evidence, first unproven evidence layer, sanitized failure, remaining authority, and exact next action. The receiving skill continues from that layer; do not restart discovery or discard prior proof.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verifying-mcp-delivery
|
|
3
|
+
description: "Use when proving a Noodle Seed MCP project works at a named compile, local, connector, App, host, deployment, or production evidence level."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- noodle-skill version:0.41.0 hash:6ef6ef551e26b78e -->
|
|
7
|
+
|
|
8
|
+
# verifying-mcp-delivery
|
|
9
|
+
|
|
10
|
+
Report the highest evidence level actually rerun without upgrading weaker proof.
|
|
11
|
+
|
|
12
|
+
## Use when
|
|
13
|
+
|
|
14
|
+
- Verify this MCP delivery before handoff.
|
|
15
|
+
- Prove which delivery layers currently pass.
|
|
16
|
+
|
|
17
|
+
## Do not use when
|
|
18
|
+
|
|
19
|
+
- Do not use as a substitute for fixing a known failure.
|
|
20
|
+
- Do not infer hosted health from local success.
|
|
21
|
+
|
|
22
|
+
## Required inputs
|
|
23
|
+
|
|
24
|
+
- Requested evidence level.
|
|
25
|
+
- Current target.
|
|
26
|
+
- Existing evidence and its freshness.
|
|
27
|
+
|
|
28
|
+
## Workflow
|
|
29
|
+
|
|
30
|
+
Read and follow the canonical playbook `references/verify-and-recover.md` at `../noodle-seed/references/verify-and-recover.md`. It owns the workflow; do not recreate it here or load the command catalog speculatively.
|
|
31
|
+
Load `references/test-in-hosts.md` at `../noodle-seed/references/test-in-hosts.md` only when the playbook or observed evidence names that concern.
|
|
32
|
+
|
|
33
|
+
## Verification evidence
|
|
34
|
+
|
|
35
|
+
Every dependency below the requested level passes now, or the first unproven layer is explicit.
|
|
36
|
+
|
|
37
|
+
## Recovery paths
|
|
38
|
+
|
|
39
|
+
Hand a concrete failing layer to debugging-mcp-delivery with all passing evidence preserved.
|
|
40
|
+
|
|
41
|
+
## Stop conditions
|
|
42
|
+
|
|
43
|
+
Stop after the requested level passes or the first bounded failure is isolated.
|
|
44
|
+
|
|
45
|
+
## Handoff contract
|
|
46
|
+
|
|
47
|
+
Pass the selected outcome, explicit target, changed files, commands run, passing evidence, first unproven evidence layer, sanitized failure, remaining authority, and exact next action. The receiving skill continues from that layer; do not restart discovery or discard prior proof.
|