@noodleseed/agent-kit 0.41.0 → 0.43.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 (58) hide show
  1. package/manifest.json +275 -259
  2. package/package.json +1 -1
  3. package/skills/claude-code/SKILL.md +6 -5
  4. package/skills/claude-code/authoring-mcp-servers/SKILL.md +1 -1
  5. package/skills/claude-code/building-mcp-apps/SKILL.md +1 -1
  6. package/skills/claude-code/connecting-apis-to-mcp/SKILL.md +1 -1
  7. package/skills/claude-code/debugging-mcp-delivery/SKILL.md +1 -1
  8. package/skills/claude-code/deploying-mcp-services/SKILL.md +1 -1
  9. package/skills/claude-code/designing-mcp-products/SKILL.md +1 -1
  10. package/skills/claude-code/embedding-mcp-assistants/SKILL.md +1 -1
  11. package/skills/claude-code/examples/acme-bistro/src/server.ts +5 -1
  12. package/skills/claude-code/examples/acme-discovery/src/server.ts +5 -1
  13. package/skills/claude-code/examples/acme-tasks/src/server.ts +4 -1
  14. package/skills/claude-code/examples/customer-auth/src/server.ts +5 -1
  15. package/skills/claude-code/examples/food-ordering/src/server.ts +14 -5
  16. package/skills/claude-code/examples/gmail-multi-account/src/server.ts +20 -3
  17. package/skills/claude-code/examples/hello/README.md +7 -2
  18. package/skills/claude-code/examples/hello/src/server.ts +3 -0
  19. package/skills/claude-code/examples/weather/src/server.ts +10 -2
  20. package/skills/claude-code/publishing-mcp-integrations/SKILL.md +1 -1
  21. package/skills/claude-code/references/agent-contract.md +4 -3
  22. package/skills/claude-code/references/authoring-workflow.md +1 -60
  23. package/skills/claude-code/references/build-an-mcp-server.md +1 -1
  24. package/skills/claude-code/references/cli-commands.md +1 -1
  25. package/skills/claude-code/references/connect-an-api.md +4 -0
  26. package/skills/claude-code/references/embedded-assistant.md +3 -3
  27. package/skills/claude-code/references/feedback.md +11 -9
  28. package/skills/claude-code/references/tool-design.md +103 -0
  29. package/skills/claude-code/reporting-noodle-feedback/SKILL.md +6 -6
  30. package/skills/claude-code/verifying-mcp-delivery/SKILL.md +1 -1
  31. package/skills/codex/SKILL.md +6 -5
  32. package/skills/codex/authoring-mcp-servers/SKILL.md +1 -1
  33. package/skills/codex/building-mcp-apps/SKILL.md +1 -1
  34. package/skills/codex/connecting-apis-to-mcp/SKILL.md +1 -1
  35. package/skills/codex/debugging-mcp-delivery/SKILL.md +1 -1
  36. package/skills/codex/deploying-mcp-services/SKILL.md +1 -1
  37. package/skills/codex/designing-mcp-products/SKILL.md +1 -1
  38. package/skills/codex/embedding-mcp-assistants/SKILL.md +1 -1
  39. package/skills/codex/examples/acme-bistro/src/server.ts +5 -1
  40. package/skills/codex/examples/acme-discovery/src/server.ts +5 -1
  41. package/skills/codex/examples/acme-tasks/src/server.ts +4 -1
  42. package/skills/codex/examples/customer-auth/src/server.ts +5 -1
  43. package/skills/codex/examples/food-ordering/src/server.ts +14 -5
  44. package/skills/codex/examples/gmail-multi-account/src/server.ts +20 -3
  45. package/skills/codex/examples/hello/README.md +7 -2
  46. package/skills/codex/examples/hello/src/server.ts +3 -0
  47. package/skills/codex/examples/weather/src/server.ts +10 -2
  48. package/skills/codex/publishing-mcp-integrations/SKILL.md +1 -1
  49. package/skills/codex/references/agent-contract.md +4 -3
  50. package/skills/codex/references/authoring-workflow.md +1 -60
  51. package/skills/codex/references/build-an-mcp-server.md +1 -1
  52. package/skills/codex/references/cli-commands.md +1 -1
  53. package/skills/codex/references/connect-an-api.md +4 -0
  54. package/skills/codex/references/embedded-assistant.md +3 -3
  55. package/skills/codex/references/feedback.md +11 -9
  56. package/skills/codex/references/tool-design.md +103 -0
  57. package/skills/codex/reporting-noodle-feedback/SKILL.md +6 -6
  58. package/skills/codex/verifying-mcp-delivery/SKILL.md +1 -1
@@ -189,6 +189,7 @@ export default server(
189
189
  },
190
190
  [
191
191
  tool('weather_briefing', {
192
+ title: 'Weather briefing',
192
193
  description:
193
194
  'Look up a city, fetch its current weather, and return a human-readable briefing. Runs a ' +
194
195
  'three-step flow: geocode the city, fetch the forecast, then derive the briefing in a sandboxed compute step.',
@@ -233,14 +234,21 @@ export default server(
233
234
  // the whole array; the compute connector narrows each element to the two fields the model speaks
234
235
  // from. Append new tools AFTER existing ones so `tools[0]` stays stable for host harnesses.
235
236
  tool('search_places', {
237
+ title: 'Search places',
236
238
  description:
237
239
  'Search a place name and return the matching locations as a list of { id, label } options.',
238
- input: z.object({ query: z.string() }),
240
+ // Bound the list at the source: `limit` is capped in the schema and passed through to the
241
+ // upstream `count` parameter, so the model can never pull an unbounded page into its context.
242
+ // `noodle check` reports an unbounded array output as `tool_design_output_bounds`.
243
+ input: z.object({
244
+ query: z.string(),
245
+ limit: z.number().int().min(1).max(10).default(5),
246
+ }),
239
247
  output: z.object({
240
248
  places: z.array(z.object({ id: z.string(), label: z.string() })),
241
249
  }),
242
250
  fulfil: ({ input, connectors }) => {
243
- const found = connectors.geo.search_list({ name: input.query });
251
+ const found = connectors.geo.search_list({ name: input.query, count: input.limit });
244
252
  const narrowed = connectors.places.narrow({ results: found.results });
245
253
  return { places: narrowed.places };
246
254
  },
@@ -3,7 +3,7 @@ name: publishing-mcp-integrations
3
3
  description: "Use when preparing, reviewing, or submitting a Noodle Seed MCP integration to a host or app directory."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.41.0 hash:efffbf82007f935d -->
6
+ <!-- noodle-skill version:0.43.0 hash:efffbf82007f935d -->
7
7
 
8
8
  # publishing-mcp-integrations
9
9
 
@@ -1,6 +1,6 @@
1
1
  # Agent contract: --json, exit codes, output modes
2
2
 
3
- The cold-agent-path commands (`init`, `validate`, `test`, `check`, `tools`/`resources`/`prompts`, `deploy`, `metrics`, `events`, `agents`) are agent-native and return the envelope below; hosted admin/ops commands (`status`, `inspect`, `smoke`, `logs`, `update`) are still being normalized. Decide what to do next by parsing machine state — do not scrape human prose.
3
+ Every `--json` command returns the canonical envelope below on stdout and keeps stderr empty. Decide what to do next by parsing machine state — do not scrape human prose.
4
4
 
5
5
  ## Contents
6
6
 
@@ -11,12 +11,13 @@ The cold-agent-path commands (`init`, `validate`, `test`, `check`, `tools`/`reso
11
11
 
12
12
  ## Response envelope
13
13
 
14
- A `--json` command returns exactly one JSON object:
14
+ A one-shot `--json` command returns exactly one JSON object on stdout; stderr stays empty:
15
15
 
16
16
  - **Success**: `{ ok: true, data, warnings? }` — `data` is the command payload; `warnings?` is an optional array of non-fatal notes.
17
- - **Failure**: `{ ok: false, error: { code, message, cause?, fix, next, requestId? } }` — `code` is the stable machine code to branch on, `message` is human text, `cause?` is the underlying error, `fix` states the correction, `next` names the command to run next, `requestId?` correlates a hosted call.
17
+ - **Failure**: `{ ok: false, error: { code, message, cause?, fix, next, requestId?, retryable?, retryAfterSeconds? } }` — `code` is the stable machine code to branch on, `message` is human text, `cause?` is the underlying error, `fix` states the correction, `next` names the command to run next, `requestId?` correlates a hosted call, and retry metadata tells automation whether and when to retry.
18
18
  - **Field errors** carry a dotted `path`: multi-error commands (e.g. `noodle validate`) nest them under `error.errors[]`, each `{ code, path, message }`. The top-level `error` still carries `code`/`message`/`fix`/`next`; the per-field `path`s live in `error.errors[]`.
19
19
  - **Repair prose is isolated**: ready-to-apply repair text appears only under `error.fixPrompt` (surfaced by `--fix-prompt`), never mixed into `message` or `data`.
20
+ - **Streams are NDJSON envelopes**: the initial snapshot is `{ ok: true, data: { kind: "snapshot", snapshot } }`, subsequent records are `{ ok: true, data: { kind: "event", event } }`, and a terminal failure is the ordinary `{ ok: false, error }` envelope on its own line. Parse each line independently.
20
21
 
21
22
  ## Exit codes
22
23
 
@@ -8,7 +8,6 @@
8
8
  - Connectors
9
9
  - HTTP connector example (full server)
10
10
  - Delegated downstream auth (call your API as the signed-in user)
11
- - Design tools for the model
12
11
  - Invocation context
13
12
  - Compute connector example
14
13
  - Tests
@@ -173,65 +172,7 @@ Diagnose statically with `noodle auth doctor`; set a short-lived real customer t
173
172
 
174
173
  ## Design tools for the model
175
174
 
176
- Design tools around what a user says, not 1:1 around API endpoints. A raw wrapper per endpoint (`get_task`, `list_tasks`, `close_task`) forces the model to orchestrate low-level calls and to know identifiers the user never sees — an MCP connector, but not a usable product. Instead:
177
-
178
- - **Shape by intent.** Name and scope tools for the job to be done — "find my overdue tasks", "complete the task matching this text" — combining multiple backing calls in one recorded flow (`when(...)`) where it helps.
179
- - **Prefer names/text over raw IDs.** When an action needs an id the user does not know, pair the id-taking operation with a find/search operation that returns model-friendly summaries (id + a human label), so the model resolves text → id itself. Write descriptions that tell the model when to use each tool and how they chain.
180
- - **Return only what the model needs.** Map the response to a small, typed `output` (a few labelled fields), not the raw API payload.
181
-
182
- This example pairs a name resolver with an id-taking action: the model calls `find_tasks` to turn the user’s words into an id, then `complete_task`. It is compile-verified on every `pnpm test`.
183
-
184
- ```ts
185
- import { connector, secret, server, tool, z } from '@noodleseed/one';
186
-
187
- const tasks = connector('tasks').version('1.0.0').http({
188
- baseUrl: 'https://api.tasks.example',
189
- allowedOrigins: ['https://api.tasks.example'],
190
- auth: { kind: 'bearer', secret: secret('TASKS_TOKEN') },
191
- operations: {
192
- search_tasks: {
193
- type: 'read',
194
- method: 'GET',
195
- path: '/tasks',
196
- query: ['query'],
197
- input: z.object({ query: z.string() }),
198
- output: z.object({ matches: z.array(z.unknown()) }),
199
- response: { matches: '${response.results}' },
200
- },
201
- close_task: {
202
- type: 'action',
203
- method: 'POST',
204
- path: '/tasks/{id}/close',
205
- input: z.object({ id: z.string() }),
206
- output: z.object({ ok: z.boolean() }),
207
- response: { ok: '${response.ok}' },
208
- },
209
- },
210
- });
211
-
212
- export default server('todo', { title: 'Tasks', version: '1.0.0', use: { tasks } }, [
213
- tool('find_tasks', {
214
- description: 'Find tasks whose text matches a query — call this first to resolve a task the user names by text into its id, then pass that id to complete_task.',
215
- input: z.object({ query: z.string() }),
216
- output: z.object({ matches: z.array(z.object({ id: z.string(), title: z.string() })) }),
217
- fulfil: ({ input, connectors }) => {
218
- const found = connectors.tasks.search_tasks({ query: input.query });
219
- return { matches: found.matches };
220
- },
221
- }),
222
- tool('complete_task', {
223
- description: 'Mark a task complete by its id (get the id from find_tasks).',
224
- input: z.object({ id: z.string() }),
225
- output: z.object({ ok: z.boolean() }),
226
- fulfil: ({ input, connectors }) => {
227
- const result = connectors.tasks.close_task({ id: input.id });
228
- return { ok: result.ok };
229
- },
230
- }),
231
- ]);
232
- ```
233
-
234
- The model never sees a task id from the user; `find_tasks` returns `{ id, title }` summaries it can pick from, then `complete_task` acts by id. Keep write actions (`complete_task`) separate and explicitly described so the host can gate them.
175
+ Shape tools around what a user says, not 1:1 around API endpoints. `references/tool-design.md` owns the doctrine: intent-shaped tools, titles and annotations, bounded outputs, a small tool surface, and deliberate context.
235
176
 
236
177
  ## Invocation context
237
178
 
@@ -22,7 +22,7 @@ Establish only the inputs needed for the requested stopping point. Follow `refer
22
22
  1. **Confirm conversational fit.** Name one to three focused jobs where saying the request is easier than navigating the underlying system, and identify the data or action the model cannot provide by itself.
23
23
  2. **Define the product contract.** For each job, write the user phrase, the intent-shaped tool or resource, its minimal typed input, the useful output, read/write effect, and backing operation. Design for user intent, not a 1:1 API endpoint wrapper.
24
24
  3. **Choose the smallest implementation.** Use native tools, resources, or prompts for local/static behavior; add a connector only when external data or actions are required. Keep response output small and model-readable.
25
- 4. **Author in TypeScript.** Follow `references/authoring-workflow.md` for connector and flow patterns and `references/sdk-surface.md` for exact builders. These are this route’s complete canonical support set; use the router lookup catalog only when observed evidence names a different concern.
25
+ 4. **Author in TypeScript.** Follow `references/authoring-workflow.md` for connector and flow patterns, `references/tool-design.md` for the model-facing tool surface, and `references/sdk-surface.md` for exact builders. These are this route’s complete canonical support set; use the router lookup catalog only when observed evidence names a different concern.
26
26
  5. **Validate and repair.** Run `noodle validate --json`. Parse `error.errors[]`, repair the cited `path`, and rerun validation. Consult the lookup catalog only for the specific reported error code; do not open another reference speculatively.
27
27
  6. **Run the local smoke.** After validation succeeds, run `noodle test --json` and repair any failure at that evidence layer.
28
28
  7. **Prove external behavior.** For connector-backed reads, set credentials through the effective local target and run a safe representative `noodle tools call`. Confirm populated mapped fields from real output, not merely successful registration.
@@ -27,7 +27,7 @@ Developer-facing `noodle` commands, grouped by area. Local authoring commands (`
27
27
  | `noodle import` | Import an OpenAPI spec into a starter `server.ts`. |
28
28
  | `noodle export` | Compile locally and write the portable manifest JSON (no service). |
29
29
  | `noodle validate` | Author-time compile/schema/connector check; no service (`--json`, `--fix-prompt`). |
30
- | `noodle check` | Check MCP Apps/widget readiness; no service. |
30
+ | `noodle check` | Check tool design (`tool_design_*`) and MCP Apps/widget readiness; no service. `--min-severity warn` shows only what needs fixing. |
31
31
  | `noodle test` | Local compile plus a loopback MCP smoke. |
32
32
 
33
33
  ## Local run & inspect
@@ -157,8 +157,12 @@ Local `dev`, smoke commands, secrets, and variables resolve one effective target
157
157
  noodle secrets set SOME_API_KEY --runtime local --from-env SOME_API_KEY
158
158
  # Explicit flags remain available when intentionally testing a different local target:
159
159
  noodle secrets set SOME_API_KEY --runtime local --scope env --org <org> --app <app> --env <env> --from-env SOME_API_KEY
160
+ # Configuring a deployed environment is a different store — say so:
161
+ noodle secrets set SOME_API_KEY --runtime cloud --scope env --org <org> --app <app> --env <env> --from-env SOME_API_KEY
160
162
  ```
161
163
 
164
+ `--runtime` is required whenever `--org`, `--app`, and `--env` together name a complete hosted target: a command that looks remote must never fall back to writing `.env.noodle`. Omitting it fails with `runtime_required` before anything is written.
165
+
162
166
  Local secrets live in `./.env.noodle` (never commit it). A required `secret(...)` or `variable(...)` that cannot resolve fails boot closed. `noodle tools call` / `noodle test` / `noodle dev` / `noodle devtools` stop before exposing an empty endpoint and print the exact effective target plus recovery command.
163
167
 
164
168
  ### Prove real output
@@ -91,9 +91,9 @@ Give every business action a portable `tool(..., { title: "Complete task", descr
91
91
  Local MCP authoring and tests need no account, but an external browser embed needs an active assistant-enabled deployment before a backend client can be created. Set the model values on the target org/app/env, then deploy:
92
92
 
93
93
  ```sh
94
- noodle variables set ASSISTANT_MODEL_BASE_URL --scope env --org <org> --app <app> --env <env> --value <https-model-base-url>
95
- noodle variables set ASSISTANT_MODEL --scope env --org <org> --app <app> --env <env> --value <model>
96
- noodle secrets set ASSISTANT_MODEL_API_KEY --scope env --org <org> --app <app> --env <env> --from-env ASSISTANT_MODEL_API_KEY
94
+ noodle variables set ASSISTANT_MODEL_BASE_URL --scope env --runtime cloud --org <org> --app <app> --env <env> --value <https-model-base-url>
95
+ noodle variables set ASSISTANT_MODEL --scope env --runtime cloud --org <org> --app <app> --env <env> --value <model>
96
+ noodle secrets set ASSISTANT_MODEL_API_KEY --scope env --runtime cloud --org <org> --app <app> --env <env> --from-env ASSISTANT_MODEL_API_KEY
97
97
  noodle deploy --org <org> --app <app> --env <env>
98
98
  ```
99
99
 
@@ -1,6 +1,6 @@
1
1
  # Send product feedback
2
2
 
3
- When you — the coding agent — discover a way Noodle Seed could be better, prepare one sanitized feedback proposal. Feedback crosses the customer project boundary and lands in Noodle Seed’s private tracker, so the user must make an informed choice. Show the user the exact command and ask for explicit user approval. Do not run or submit it until approval is given.
3
+ When you — the coding agent — discover a way Noodle Seed could be better, prepare one sanitized feedback proposal. Feedback crosses the customer project boundary and lands in the Noodle Seed private feedback tracker, so the user must make an informed choice. Preview the exact normalized submission locally, show it with the exact live command, and ask for explicit user approval. Do not submit it until approval is given.
4
4
 
5
5
  ## Contents
6
6
 
@@ -26,20 +26,22 @@ Do not batch several findings into one proposal, and do not re-propose the same
26
26
 
27
27
  ## Approval workflow
28
28
 
29
- 1. Draft and sanitize the title, message, labels, and exact shell command.
30
- 2. Show the user the exact command and explain that it sends the shown content plus CLI version, OS/platform, and Node version to Noodle Seed’s private tracker.
31
- 3. Ask for explicit user approval. Do not run, submit, or send anything until the user clearly approves that exact proposal.
32
- 4. After approval, run it once. If authentication is missing, report that feedback was not sent and offer the normal `noodle login` path; never sign in or retry without direction.
29
+ 1. Discover the current positional arguments, flags, choices, defaults, and limits from `noodle commands --json`; `noodle feedback --help` is the human-readable view. Do not guess or rely on a remembered catalog.
30
+ 2. Draft one finding, then sanitize its title and message using the rules below.
31
+ 3. Run the proposal with `--dry-run --json`. This local preview needs no login and sends nothing. Parse `{"ok":true,"data":{"mode":"preview","willSubmit":false,"destination":"Noodle Seed private feedback tracker","submission":{...}}}`.
32
+ 4. Inspect the complete `submission`, including its normalized defaults and automatically attached diagnostics. Show the user the exact previewed proposal, its `destination`, and a POSIX-safely quoted live command containing the same fields but without `--dry-run`.
33
+ 5. Ask for explicit approval of that exact previewed proposal. If the user changes any field, preview the changed proposal again before asking.
34
+ 6. Only after approval, submit it once by running the disclosed live command without `--dry-run`. Never auto-login and never retry-loop. If authentication fails before the request or a rate limit denies it, report that nothing was sent. For `feedback_recording_failed`, report that no reference was returned and the outcome may be unknown; do not retry because the private issue might already exist.
33
35
 
34
36
  ## The command
35
37
 
36
38
  ```sh
37
- noodle feedback "resources list --json omits the truncated flag the docs promise" \
38
- --title "resources list --json missing truncated flag" \
39
- --type fix --severity P2 --area cli --json
39
+ noodle feedback 'resources list --json omits the truncated flag the docs promise' \
40
+ --title 'resources list --json missing truncated flag' \
41
+ --type fix --severity P2 --area cli --dry-run --json
40
42
  ```
41
43
 
42
- This is an example only; build the exact command for the finding and show it before execution. Read `noodle feedback --help` for human-readable constraints or inspect `noodle commands --json` for the same machine-readable usage, enum, and length metadata instead of guessing accepted values. The message is required (1–4000 chars). Pass `--json` and parse the envelope: success is `{ok:true,data:{reference,labels}}`; a `429` means the per-user hourly budget (5) is spent — report that it was not sent and never retry-loop. The CLI attaches only the disclosed light diagnostics automatically: CLI version, OS/platform, Node version. Nothing else is collected.
44
+ This is a preview example only. Build the exact command for the finding using current `noodle commands --json` metadata, POSIX-quote every user-controlled value, and inspect the returned submission instead of reconstructing it. The message is required (1–4000 chars). The CLI attaches only the disclosed light diagnostics automatically: CLI version, OS/platform, Node version. Nothing else is collected. After approval, the live success envelope is `{ok:true,data:{reference,labels}}`; a `429` means the per-user hourly budget (5) is spent — report that it was not sent and never retry-loop.
43
45
 
44
46
  ## Choose the structured fields
45
47
 
@@ -0,0 +1,103 @@
1
+ # Tool design
2
+
3
+ ## Contents
4
+
5
+ - Design tools for the model
6
+ - Title and annotations
7
+ - Bound every output
8
+ - Keep the tool surface small
9
+ - Provision context deliberately
10
+ - Errors an agent can act on
11
+ - What `noodle check` reports
12
+
13
+ ## Design tools for the model
14
+
15
+ Design tools around what a user says, not 1:1 around API endpoints. A raw wrapper per endpoint (`get_task`, `list_tasks`, `close_task`) forces the model to orchestrate low-level calls and to know identifiers the user never sees — an MCP connector, but not a usable product. Instead:
16
+
17
+ - **Shape by intent.** Name and scope tools for the job to be done — "find my overdue tasks", "complete the task matching this text" — combining multiple backing calls in one recorded flow (`when(...)`) where it helps.
18
+ - **Prefer names/text over raw IDs.** When an action needs an id the user does not know, pair the id-taking operation with a find/search operation that returns model-friendly summaries (id + a human label), so the model resolves text → id itself. Write descriptions that tell the model when to use each tool and how they chain.
19
+ - **Return only what the model needs.** Map the response to a small, typed `output` (a few labelled fields), not the raw API payload.
20
+
21
+ This example pairs a name resolver with an id-taking action: the model calls `find_tasks` to turn the user’s words into an id, then `complete_task`. It is compile-verified on every `pnpm test`.
22
+
23
+ ```ts
24
+ import { connector, secret, server, tool, z } from '@noodleseed/one';
25
+
26
+ const tasks = connector('tasks').version('1.0.0').http({
27
+ baseUrl: 'https://api.tasks.example',
28
+ allowedOrigins: ['https://api.tasks.example'],
29
+ auth: { kind: 'bearer', secret: secret('TASKS_TOKEN') },
30
+ operations: {
31
+ search_tasks: {
32
+ type: 'read',
33
+ method: 'GET',
34
+ path: '/tasks',
35
+ query: ['query'],
36
+ input: z.object({ query: z.string() }),
37
+ output: z.object({ matches: z.array(z.unknown()) }),
38
+ response: { matches: '${response.results}' },
39
+ },
40
+ close_task: {
41
+ type: 'action',
42
+ method: 'POST',
43
+ path: '/tasks/{id}/close',
44
+ input: z.object({ id: z.string() }),
45
+ output: z.object({ ok: z.boolean() }),
46
+ response: { ok: '${response.ok}' },
47
+ },
48
+ },
49
+ });
50
+
51
+ export default server('todo', { title: 'Tasks', version: '1.0.0', use: { tasks } }, [
52
+ tool('find_tasks', {
53
+ description: 'Find tasks whose text matches a query — call this first to resolve a task the user names by text into its id, then pass that id to complete_task.',
54
+ input: z.object({ query: z.string() }),
55
+ output: z.object({ matches: z.array(z.object({ id: z.string(), title: z.string() })) }),
56
+ fulfil: ({ input, connectors }) => {
57
+ const found = connectors.tasks.search_tasks({ query: input.query });
58
+ return { matches: found.matches };
59
+ },
60
+ }),
61
+ tool('complete_task', {
62
+ description: 'Mark a task complete by its id (get the id from find_tasks).',
63
+ input: z.object({ id: z.string() }),
64
+ output: z.object({ ok: z.boolean() }),
65
+ fulfil: ({ input, connectors }) => {
66
+ const result = connectors.tasks.close_task({ id: input.id });
67
+ return { ok: result.ok };
68
+ },
69
+ }),
70
+ ]);
71
+ ```
72
+
73
+ The model never sees a task id from the user; `find_tasks` returns `{ id, title }` summaries it can pick from, then `complete_task` acts by id. Keep write actions (`complete_task`) separate and explicitly described so the host can gate them.
74
+
75
+ ## Title and annotations
76
+
77
+ Every model-visible tool needs a `title` — the action name hosts show in tool pickers and confirmation prompts — and `annotations`. `annotations.readOnly()` is a closed-world safe read; `annotations.action()` affects the world; `annotations.localAction()` affects only this app's data; `annotations.openAction()` reaches the open internet. Keep reads and writes in separate tools: one tool that both lists and mutates cannot be annotated honestly, so no host can gate it correctly. Missing titles and hints are also the most common consumer-directory rejection.
78
+
79
+ ## Bound every output
80
+
81
+ Always declare `output`. Without it the model has to parse prose and hosts have no structured result to render. Then bound any list: cap the array with `z.array(item).max(50)`, or take a bounded pagination input (`limit`, `cursor`). An unbounded list either exhausts the context window or is truncated somewhere you do not control. Map the response to the few labelled fields the model needs, never the raw upstream payload — every field you pass through is context paid for on every later turn.
82
+
83
+ ## Keep the tool surface small
84
+
85
+ `noodle check` warns above 20 model-visible tools. That is a documented heuristic, not a host limit: no host publishes a hard number, and the real threshold depends on how distinct your descriptions are. Collapse variants that differ only by a filter into one intent-shaped tool with a typed enum, and mark widget-only helpers `visibility: ['app']` so they stay callable from the app surface without entering the model's list.
86
+
87
+ ## Provision context deliberately
88
+
89
+ Most “the model guessed wrong” bugs are missing context, not a missing tool. Every invocation already carries a server-authoritative instant plus locale and time zone, so never ask the model for today's date. Set `server(..., { context: { defaults: { locale, timeZone } } })` for ambient defaults, and mark one zero-input tool `contextProvider: true` when the model needs portable application context such as workspace, plan, or permissions. `references/authoring-workflow.md` owns the full invocation-context contract.
90
+
91
+ ## Errors an agent can act on
92
+
93
+ An agent cannot recover from “Request failed”. Say which argument was wrong, which tool resolves it, and whether retrying helps. Error text is part of the tool interface and is read far more often by a model than by a human.
94
+
95
+ ## What `noodle check` reports
96
+
97
+ - `tool_design_titles` — a model-visible tool has no `title`.
98
+ - `tool_design_output_shape` — a model-visible tool has no `output` schema.
99
+ - `tool_design_output_bounds` — an output array has no `maxItems` and the tool takes no pagination input.
100
+ - `tool_design_surface_budget` — more than 20 model-visible tools.
101
+ - `tool_design_context` — which tool provides application context (informational).
102
+
103
+ All five are warnings, never failures, so they never change the exit code. Use `noodle check --min-severity warn` to see only what needs fixing and `noodle check --json` to consume them programmatically.
@@ -3,11 +3,11 @@ name: reporting-noodle-feedback
3
3
  description: "Use when a Noodle Seed bug, misleading instruction, missing capability, or concrete product improvement should be proposed to the user."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.41.0 hash:1e23938b39956ef8 -->
6
+ <!-- noodle-skill version:0.43.0 hash:0f404109f4845683 -->
7
7
 
8
8
  # reporting-noodle-feedback
9
9
 
10
- Offer one sanitized feedback command and submit it only after informed explicit approval.
10
+ Preview one sanitized feedback proposal and submit it once only after informed explicit approval.
11
11
 
12
12
  ## Use when
13
13
 
@@ -23,7 +23,7 @@ Offer one sanitized feedback command and submit it only after informed explicit
23
23
 
24
24
  - One distinct finding.
25
25
  - Sanitized observed and expected behavior.
26
- - User approval for the exact command.
26
+ - User approval for the exact dry-run preview and live command.
27
27
 
28
28
  ## Workflow
29
29
 
@@ -31,15 +31,15 @@ Read and follow the canonical playbook `references/feedback.md` at `../noodle-se
31
31
 
32
32
  ## Verification evidence
33
33
 
34
- The user saw the exact sanitized command; only a returned reference proves submission.
34
+ The user saw the exact sanitized preview, diagnostics, destination, and live command; only a returned reference proves submission.
35
35
 
36
36
  ## Recovery paths
37
37
 
38
- If login or rate limits block submission, report that nothing was sent and do not retry-loop.
38
+ If login or rate limits block the one live submission, report that nothing was sent. A recording failure has an unknown outcome: report no reference and never auto-login or retry-loop.
39
39
 
40
40
  ## Stop conditions
41
41
 
42
- Stop before running the command until the user explicitly approves it.
42
+ Stop after the local dry-run and before the live command until the user explicitly approves it.
43
43
 
44
44
  ## Handoff contract
45
45
 
@@ -3,7 +3,7 @@ name: verifying-mcp-delivery
3
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
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.41.0 hash:6ef6ef551e26b78e -->
6
+ <!-- noodle-skill version:0.43.0 hash:6ef6ef551e26b78e -->
7
7
 
8
8
  # verifying-mcp-delivery
9
9