@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.
Files changed (29) hide show
  1. package/README.md +7 -5
  2. package/manifest.json +904 -191
  3. package/package.json +1 -1
  4. package/skills/claude-code/SKILL.md +16 -14
  5. package/skills/claude-code/authoring-mcp-servers/SKILL.md +48 -0
  6. package/skills/claude-code/building-mcp-apps/SKILL.md +48 -0
  7. package/skills/claude-code/connecting-apis-to-mcp/SKILL.md +47 -0
  8. package/skills/claude-code/debugging-mcp-delivery/SKILL.md +48 -0
  9. package/skills/claude-code/deploying-mcp-services/SKILL.md +47 -0
  10. package/skills/claude-code/designing-mcp-products/SKILL.md +47 -0
  11. package/skills/claude-code/embedding-mcp-assistants/SKILL.md +47 -0
  12. package/skills/claude-code/examples/customer-auth/README.md +143 -1
  13. package/skills/claude-code/publishing-mcp-integrations/SKILL.md +47 -0
  14. package/skills/claude-code/references/embedded-assistant.md +142 -15
  15. package/skills/claude-code/reporting-noodle-feedback/SKILL.md +46 -0
  16. package/skills/claude-code/verifying-mcp-delivery/SKILL.md +47 -0
  17. package/skills/codex/SKILL.md +16 -14
  18. package/skills/codex/authoring-mcp-servers/SKILL.md +48 -0
  19. package/skills/codex/building-mcp-apps/SKILL.md +48 -0
  20. package/skills/codex/connecting-apis-to-mcp/SKILL.md +47 -0
  21. package/skills/codex/debugging-mcp-delivery/SKILL.md +48 -0
  22. package/skills/codex/deploying-mcp-services/SKILL.md +47 -0
  23. package/skills/codex/designing-mcp-products/SKILL.md +47 -0
  24. package/skills/codex/embedding-mcp-assistants/SKILL.md +47 -0
  25. package/skills/codex/examples/customer-auth/README.md +143 -1
  26. package/skills/codex/publishing-mcp-integrations/SKILL.md +47 -0
  27. package/skills/codex/references/embedded-assistant.md +142 -15
  28. package/skills/codex/reporting-noodle-feedback/SKILL.md +46 -0
  29. 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 React wrapper:
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 DOM-free client. It keeps the session token in memory, streams the same typed events, and never registers a custom element:
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 pendingId: string | undefined;
270
- let requestedSchema: unknown;
271
- assistant.subscribe((event) => {
272
- renderAssistantEvent(event);
273
- if (event.event === 'view_available') {
274
- renderRegisteredView(event.data.resourceUri, event.data.result);
275
- }
276
- if ((event.event === 'tool_proposed' || event.event === 'input_requested') && typeof event.data.id === 'string') {
277
- pendingId = event.data.id;
278
- requestedSchema = event.event === 'input_requested' ? event.data.requestedSchema : undefined;
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 (pendingId) {
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(pendingId, resolution);
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
- `view_available` 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. It 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.
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.