@noodleseed/agent-kit 0.9.0 → 0.11.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 CHANGED
@@ -8,9 +8,10 @@ into a project's `.agents/` and `.claude/` skill directories via `noodle agents
8
8
 
9
9
  ## Contents
10
10
 
11
- - `skills/codex/` — the Codex (Codex CLI / AGENTS.md) skill: a `SKILL.md` router plus ten
12
- `references/*.md` files (sdk-surface, cli-commands, compile-errors, authoring-workflow,
13
- widgets-and-apps, test-in-hosts, troubleshooting, deploy-and-ops, publishing, examples).
11
+ - `skills/codex/` — the Codex (Codex CLI / AGENTS.md) skill: a `SKILL.md` router plus eleven
12
+ `references/*.md` files (sdk-surface, cli-commands, agent-contract, compile-errors,
13
+ authoring-workflow, widgets-and-apps, test-in-hosts, troubleshooting, deploy-and-ops, publishing,
14
+ examples).
14
15
  - `skills/claude-code/` — the Claude Code skill, same hierarchical tree.
15
16
  - `manifest.json` — `{ packageVersion, files: [{ path, sha256, agentTarget }] }`. The CLI verifies every
16
17
  file's sha256 against this manifest before writing it into a user's project; a mismatch refuses the
package/manifest.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
- "packageVersion": "0.9.0",
2
+ "packageVersion": "0.11.0",
3
3
  "files": [
4
4
  {
5
5
  "path": "skills/codex/SKILL.md",
6
- "sha256": "f2481e82944f3369735b3f09965454d94333966546ae2718d0ac6dd5be186467",
6
+ "sha256": "54f2a9d84455361127f40fe079715ec69c1ef737b491fc2ace896cb754ff4fe3",
7
7
  "agentTarget": "codex"
8
8
  },
9
9
  {
10
10
  "path": "skills/codex/references/sdk-surface.md",
11
- "sha256": "959fa5fee6d2f0fbd82d8f8d39424deb5f6a4e1be2c1bfa4950a1a253c178045",
11
+ "sha256": "d5da0c1ec33aa8d2770e145293174e99cff0c73264756dafe121e818ade14abf",
12
12
  "agentTarget": "codex"
13
13
  },
14
14
  {
@@ -16,19 +16,24 @@
16
16
  "sha256": "c50bf5ec6f43bd9fb7115ea9c9eb8de33c93e123f3e2d396785fe75ba094d40d",
17
17
  "agentTarget": "codex"
18
18
  },
19
+ {
20
+ "path": "skills/codex/references/agent-contract.md",
21
+ "sha256": "8e884a90f7c155a9f1d3773c7c037f88a98ac8d5c5d23e8100349ae82d068e6d",
22
+ "agentTarget": "codex"
23
+ },
19
24
  {
20
25
  "path": "skills/codex/references/compile-errors.md",
21
- "sha256": "977d9b0022f7bb280cff1ff5737890f16eba1fee632868678dbdb45f9526e79a",
26
+ "sha256": "22692f2fda9c7d8ef0aa54e58148d22cf740bf722ceb79a942f6f146009a6bda",
22
27
  "agentTarget": "codex"
23
28
  },
24
29
  {
25
30
  "path": "skills/codex/references/authoring-workflow.md",
26
- "sha256": "d88ee3cd351ce4a438bc5c2ecd2afae56b191d1a53954562bb38e116e554047f",
31
+ "sha256": "ad5866ad739885592eb729fdd0bf18d602b886021309e28565a090d66f7c2e9a",
27
32
  "agentTarget": "codex"
28
33
  },
29
34
  {
30
35
  "path": "skills/codex/references/widgets-and-apps.md",
31
- "sha256": "10d644eb421deee8a99a366f18c848ca769116ec051b7c35a4ee7e2c7b5e2f0d",
36
+ "sha256": "dea06536f780f0bb6d26dc63a6f1f35e2d6eb97f7a9c1c8574ed7377ccfe37a8",
32
37
  "agentTarget": "codex"
33
38
  },
34
39
  {
@@ -43,7 +48,7 @@
43
48
  },
44
49
  {
45
50
  "path": "skills/codex/references/deploy-and-ops.md",
46
- "sha256": "33c902d900b60202331cbbcc09b683d91e5c23a07ec7c11c66e48e1a9726f33b",
51
+ "sha256": "0175101939ca5ea6823a18ce83a32311f5d9937ef8469a431dd0576ad0997fe4",
47
52
  "agentTarget": "codex"
48
53
  },
49
54
  {
@@ -53,17 +58,17 @@
53
58
  },
54
59
  {
55
60
  "path": "skills/codex/references/examples.md",
56
- "sha256": "560d7874eaccd209b2c358060327bce0051ad73cf9d12bdf13636c202835c83f",
61
+ "sha256": "9f3ce479669040f0c58c9876b2bbca90821c69a760d0b020a56072bd6e549d73",
57
62
  "agentTarget": "codex"
58
63
  },
59
64
  {
60
65
  "path": "skills/claude-code/SKILL.md",
61
- "sha256": "0871a7834077a3970960f341baa4bff37836b530ae227a1381c31a59ecc232f8",
66
+ "sha256": "fa975102477cd02ecdb083b4b8f747fc3fee3a7bcb5b725fe63ca9504258ba39",
62
67
  "agentTarget": "claude-code"
63
68
  },
64
69
  {
65
70
  "path": "skills/claude-code/references/sdk-surface.md",
66
- "sha256": "959fa5fee6d2f0fbd82d8f8d39424deb5f6a4e1be2c1bfa4950a1a253c178045",
71
+ "sha256": "d5da0c1ec33aa8d2770e145293174e99cff0c73264756dafe121e818ade14abf",
67
72
  "agentTarget": "claude-code"
68
73
  },
69
74
  {
@@ -71,19 +76,24 @@
71
76
  "sha256": "c50bf5ec6f43bd9fb7115ea9c9eb8de33c93e123f3e2d396785fe75ba094d40d",
72
77
  "agentTarget": "claude-code"
73
78
  },
79
+ {
80
+ "path": "skills/claude-code/references/agent-contract.md",
81
+ "sha256": "8e884a90f7c155a9f1d3773c7c037f88a98ac8d5c5d23e8100349ae82d068e6d",
82
+ "agentTarget": "claude-code"
83
+ },
74
84
  {
75
85
  "path": "skills/claude-code/references/compile-errors.md",
76
- "sha256": "977d9b0022f7bb280cff1ff5737890f16eba1fee632868678dbdb45f9526e79a",
86
+ "sha256": "22692f2fda9c7d8ef0aa54e58148d22cf740bf722ceb79a942f6f146009a6bda",
77
87
  "agentTarget": "claude-code"
78
88
  },
79
89
  {
80
90
  "path": "skills/claude-code/references/authoring-workflow.md",
81
- "sha256": "d88ee3cd351ce4a438bc5c2ecd2afae56b191d1a53954562bb38e116e554047f",
91
+ "sha256": "ad5866ad739885592eb729fdd0bf18d602b886021309e28565a090d66f7c2e9a",
82
92
  "agentTarget": "claude-code"
83
93
  },
84
94
  {
85
95
  "path": "skills/claude-code/references/widgets-and-apps.md",
86
- "sha256": "10d644eb421deee8a99a366f18c848ca769116ec051b7c35a4ee7e2c7b5e2f0d",
96
+ "sha256": "dea06536f780f0bb6d26dc63a6f1f35e2d6eb97f7a9c1c8574ed7377ccfe37a8",
87
97
  "agentTarget": "claude-code"
88
98
  },
89
99
  {
@@ -98,7 +108,7 @@
98
108
  },
99
109
  {
100
110
  "path": "skills/claude-code/references/deploy-and-ops.md",
101
- "sha256": "33c902d900b60202331cbbcc09b683d91e5c23a07ec7c11c66e48e1a9726f33b",
111
+ "sha256": "0175101939ca5ea6823a18ce83a32311f5d9937ef8469a431dd0576ad0997fe4",
102
112
  "agentTarget": "claude-code"
103
113
  },
104
114
  {
@@ -108,7 +118,7 @@
108
118
  },
109
119
  {
110
120
  "path": "skills/claude-code/references/examples.md",
111
- "sha256": "560d7874eaccd209b2c358060327bce0051ad73cf9d12bdf13636c202835c83f",
121
+ "sha256": "9f3ce479669040f0c58c9876b2bbca90821c69a760d0b020a56072bd6e549d73",
112
122
  "agentTarget": "claude-code"
113
123
  }
114
124
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/agent-kit",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "private": false,
5
5
  "description": "Self-checking, self-updating agent skills for the Noodle Seed CLI. Authored in this repo by @noodle-borg/agent-kit; this is the published, independently-versioned canonical skills artifact the CLI fetches and verifies.",
6
6
  "license": "Apache-2.0",
@@ -1,8 +1,8 @@
1
1
  ---
2
2
  name: noodle-seed
3
3
  description: Use when building, validating, testing, deploying, or operating a local or hosted Noodle Seed MCP server or app authored in TypeScript with the noodle CLI.
4
- version: 0.9.0
5
- hash: afde8ab3db819772
4
+ version: 0.11.0
5
+ hash: 5aa15e5bf381543c
6
6
  ---
7
7
 
8
8
  # Noodle Seed
@@ -13,17 +13,23 @@ Use this skill for project-local Noodle Seed authoring in Claude Code; preserve
13
13
 
14
14
  ## Golden path
15
15
 
16
- 1. `noodle validate` author-time compile/schema/connector check. On failure, fix the cited errors (see `references/compile-errors.md`) and re-validate; do not freeform re-edit.
17
- 2. `noodle test` — local compile plus a loopback MCP smoke.
18
- 3. `noodle dev` local loopback runtime that serves and hot-reloads the manifest.
19
- 4. `noodle check` / `noodle devtools` MCP Apps/widget readiness and preview (see `references/widgets-and-apps.md`).
20
- 5. `noodle deploy` after `noodle login` + `noodle link` (see `references/deploy-and-ops.md`).
21
- 6. Prove it in a real host `noodle connect <client>` (see `references/test-in-hosts.md`); debug symptoms with `references/troubleshooting.md`.
16
+ This CLI is agent-native: the cold-agent-path commands speak the `--json` envelope (hosted admin/ops commands are still being normalized). Drive the loop by parsing machine state, not human prose. The full envelope, exit codes, and output modes are in `references/agent-contract.md`.
17
+
18
+ 1. **Discover** — `noodle commands --json`: every command, subcommand, flag, and exit code (don't read source).
19
+ 2. **Author** edit `src/server.ts` (the configured entrypoint); follow the capability recipe in `references/sdk-surface.md` and `references/examples.md`.
20
+ 3. **Validate** — `noodle validate --json`; on failure `{ok:false,error:{code,message,fix,next,errors:[{code,path,message}]}}` the per-field detail is in `error.errors[]`.
21
+ 4. **Repair** fix each `error.errors[]` entry at its `path`, then re-run `noodle validate --json`; `noodle validate --fix-prompt` emits ready-to-apply repair prose. Never freeform re-edit (see `references/compile-errors.md`).
22
+ 5. **Smoke** — `noodle test --json`: local compile plus a loopback MCP smoke.
23
+ 6. **Apps/widgets** — `noodle check --json` (add `--target chatgpt|claude`), then `noodle devtools` (see `references/widgets-and-apps.md`).
24
+ 7. **Deploy** — `noodle deploy`; auth fails clean with `error.next` = `noodle login` (see `references/deploy-and-ops.md`).
25
+ 8. **Wire into a host** — `noodle connect <codex|claude-code|chatgpt>` (prove it in a real host per `references/test-in-hosts.md`; debug symptoms with `references/troubleshooting.md`).
26
+ 9. **Health** — `noodle metrics --agent-output`: a health verdict plus the exact next command per attention item.
22
27
 
23
28
  ## References
24
29
 
25
30
  Load these on demand:
26
31
 
32
+ - `references/agent-contract.md` — the `--json` envelope, exit codes, and the three output modes.
27
33
  - `references/sdk-surface.md` — what to import from `@noodleseed/one` and which builder to use.
28
34
  - `references/cli-commands.md` — every `noodle` command, grouped by area.
29
35
  - `references/compile-errors.md` — fix `noodle validate` errors by code.
@@ -0,0 +1,43 @@
1
+ # Agent contract: --json, exit codes, output modes
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.
4
+
5
+ ## Contents
6
+
7
+ - Response envelope
8
+ - Exit codes
9
+ - Output modes
10
+ - Repair loop
11
+
12
+ ## Response envelope
13
+
14
+ A `--json` command returns exactly one JSON object:
15
+
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.
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
+ - **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
+
21
+ ## Exit codes
22
+
23
+ Branch on the process exit code before parsing the body:
24
+
25
+ | Code | Meaning |
26
+ | :-- | :-- |
27
+ | `0` | ok |
28
+ | `1` | failure (command ran, the work failed) |
29
+ | `2` | usage (bad flags or arguments) |
30
+ | `3` | auth (login or permission required) |
31
+ | `4` | unreachable (service or network) |
32
+ | `5` | mcp/tool-call error (a `tools`/`resources`/`prompts`/`test` smoke call failed) |
33
+
34
+ ## Output modes
35
+
36
+ Two kinds of output — never mix them:
37
+
38
+ - `--json` — **machine state**: the envelope above. Use it to decide what to do next.
39
+ - `--fix-prompt` / `--agent-output` (aliases) — **agent-readable text**, not the envelope: a ready-to-apply repair prompt for authoring commands (`validate`/`test`/`check`), or an operational `health` verdict (`ok`/`attention`) with `attention[]` next-commands for ops commands (`metrics`/`doctor`/`alerts`). Use it to author a fix or judge a running deployment.
40
+
41
+ ## Repair loop
42
+
43
+ On a `validate` failure: parse `error.code` + `path`, fix exactly that field in `src/server.ts`, then re-run `noodle validate --json`. Never freeform re-edit. Repeat until `ok: true`, then `noodle test --json`.
@@ -7,6 +7,7 @@
7
7
  - Repair loop
8
8
  - Connectors
9
9
  - HTTP connector example
10
+ - Worked example (full server)
10
11
  - Compute connector example
11
12
  - Tests
12
13
  - Secrets and variables
@@ -39,6 +40,8 @@ HTTP connector auth variants: `bearer` (`{ kind: "bearer", secret: secret("API_T
39
40
 
40
41
  ## HTTP connector example
41
42
 
43
+ The operation mapping in detail: `request` templates the outbound call, `response` maps the HTTP body into your typed `output`.
44
+
42
45
  ```ts
43
46
  import { connector, secret, variable } from '@noodleseed/one';
44
47
 
@@ -71,6 +74,46 @@ const crm = connector('crm').version('1.0.0').http({
71
74
 
72
75
  Use `${args.field}` for tool/operation inputs and `${response.body.path}` for response mapping. Prefer explicit `allowedOrigins`; OAuth token/session URLs must also be on an allowed origin. For client credentials use `{ kind: "oauth2ClientCredentials", tokenUrl, clientId, clientSecret, scopes? }`; for per-customer downstream calls use `delegatedOAuth` or `delegatedSessionCookie` with a matching `customerAuth` server option.
73
76
 
77
+ ## Worked example (full server)
78
+
79
+ Declare the API as data, bind it with `use`, then record a call in a tool. `auth` reads a managed `secret(...)` — never inline a key. Map the HTTP body into your typed `output` with `${response...}`.
80
+
81
+ ```ts
82
+ import { connector, secret, server, tool, z } from '@noodleseed/one';
83
+
84
+ const crm = connector('acme_crm')
85
+ .version('1.0.0')
86
+ .http({
87
+ baseUrl: 'https://api.acme.example',
88
+ allowedOrigins: ['https://api.acme.example'],
89
+ auth: { kind: 'apiKey', header: 'X-Api-Key', secret: secret('ACME_CRM_TOKEN') },
90
+ operations: {
91
+ get_ticket: {
92
+ type: 'read',
93
+ method: 'GET',
94
+ path: '/v1/tickets/{id}',
95
+ input: { id: { type: 'string', required: true } },
96
+ output: { subject: { type: 'string' }, status: { type: 'string' } },
97
+ response: { subject: '${response.data.subject}', status: '${response.data.state}' },
98
+ },
99
+ },
100
+ });
101
+
102
+ export default server('support', { title: 'Support', version: '1.0.0', use: { crm } }, [
103
+ tool('ticket_status', {
104
+ description: 'Look up a support ticket by id.',
105
+ input: z.object({ ticket_id: z.string() }),
106
+ output: z.object({ subject: z.string(), status: z.string() }),
107
+ fulfil: ({ input, connectors }) => {
108
+ const ticket = connectors.crm.get_ticket({ id: input.ticket_id });
109
+ return { subject: ticket.subject, status: ticket.status };
110
+ },
111
+ }),
112
+ ]);
113
+ ```
114
+
115
+ More: `auth.kind` is `bearer` | `apiKey` (needs `header`) | `oauth2ClientCredentials` | `delegatedOAuth` | `delegatedSessionCookie`. Use `.compute(name, { input, output, run })` for a sandboxed transform; `provides:` (instead of `use:`) exposes a connector only to compute `callOperation`; and `noodle import openapi <file>` generates a connector from an OpenAPI spec.
116
+
74
117
  ## Compute connector example
75
118
 
76
119
  ```ts
@@ -3,39 +3,11 @@
3
3
  ## Contents
4
4
 
5
5
  - The repair loop
6
- - JSON shape
7
- - Fix prompt
8
6
  - Error codes
9
7
 
10
8
  ## The repair loop
11
9
 
12
- Run `noodle validate` (add `--json` for machine-readable output, `--fix-prompt` for an agent repair prompt). Each error carries a `code`, a dotted `path` to the offending field, and a `message`; many also carry `expected`/`got`, `didYouMean`/`suggestions`, and a `docAnchor`. Fix the specific error the `path` locates, then re-validate. Do not freeform re-edit. Once `noodle validate` passes, run `noodle test`, then `noodle dev`.
13
-
14
- ## JSON shape
15
-
16
- Use `noodle validate --json` in automated repair loops. Treat `errors[]` as the work queue and change only the cited file/path before re-running validation.
17
-
18
- ```json
19
- {
20
- "ok": false,
21
- "errors": [
22
- {
23
- "code": "unknown_operation",
24
- "path": "tools.search.fulfil.steps.0.operation",
25
- "message": "Unknown connector operation.",
26
- "expected": "operation declared on connector alias `crm`",
27
- "got": "crm.find_customer",
28
- "didYouMean": "crm.findCustomer",
29
- "suggestions": ["crm.findCustomer"],
30
- "docAnchor": "connectors.operations"
31
- }
32
- ]
33
- }
34
- ```
35
-
36
- ## Fix prompt
37
-
38
- `noodle validate --fix-prompt` prints a compact agent repair prompt with the same structured errors. Use it when delegating a repair pass, but still inspect the resulting code and re-run `noodle validate --json`.
10
+ Run `noodle validate` (add `--json` for the machine-readable envelope, `--fix-prompt` for an agent repair prompt). On failure the envelope is `{ok:false,error:{code,message,fix,next,errors:[{code,path,message}]}}`: each entry in `error.errors[]` carries a `code`, a dotted `path` to the offending field, and a `message`; many also carry `expected`/`got`, `didYouMean`/`suggestions`, and a `docAnchor` (the full envelope is in `agent-contract.md`). Fix the specific error the `path` locates, then re-validate. Do not freeform re-edit. Once `noodle validate` passes, run `noodle test`, then `noodle dev`.
39
11
 
40
12
  ## Error codes
41
13
 
@@ -5,10 +5,12 @@
5
5
  - Authenticate
6
6
  - Link and target
7
7
  - Deploy and inspect
8
+ - Connect into a host
8
9
  - Access modes
9
10
  - Org and members
10
11
  - Config and observability
11
12
  - Agent-safe CLI recipes
13
+ - Analytics
12
14
 
13
15
  ## Authenticate
14
16
 
@@ -22,6 +24,24 @@
22
24
 
23
25
  `noodle deploy` deploys the server. Then `noodle open` (latest URL), `noodle status`, `noodle inspect` (metadata, no secrets), `noodle smoke` (readiness diagnostics), and `noodle rollback <deploymentId>` to revert.
24
26
 
27
+ ## Connect into a host
28
+
29
+ Once deployed, register the server as a tool in a host with `noodle connect <host>` (`claude-code`, `codex`, `chatgpt`, `cursor`, `vscode`, `claude`, `inspector`) — it prints the exact config to paste.
30
+
31
+ - **Claude Code / Claude Desktop** (verified) — add the `mcpServers` block, or one-shot `claude mcp add-json noodle-server '<json>'`:
32
+
33
+ ```json
34
+ {
35
+ "mcpServers": {
36
+ "noodle-server": { "type": "https", "url": "https://<app>.mcp.noodleseed.dev" }
37
+ }
38
+ }
39
+ ```
40
+
41
+ - **Codex / Cursor / VS Code** — the same `mcpServers` block is emitted as a starting point (these hosts' config formats are not officially documented). Wiring a deployed Noodle server into Codex means registering that block in Codex's MCP config.
42
+ - **ChatGPT / Claude.ai** — no config file: open the host's Settings → Connectors → Add custom connector, paste the MCP URL, then authenticate.
43
+ - `noodle connect codex|claude-code --write` writes the project-local agent files (only these two targets).
44
+
25
45
  ## Access modes
26
46
 
27
47
  `noodle access set owner-only|org-members|authenticated|customers` controls who can call the deployed server. Hosted access is identity-based; never add static data-plane keys.
@@ -6,7 +6,7 @@ Flagship examples (one per capability). Read the matching example for the patter
6
6
  | :-- | :-- |
7
7
  | `hello` | Minimal TypeScript quickstart — a single tool, no connectors/widgets. |
8
8
  | `weather` | HTTP connectors, multi-step flows, and the sandboxed compute connector. |
9
- | `restaurant-pickup` | MCP Apps widgets, React `view` authoring, assets, branding, and handoff. |
9
+ | `food-ordering` | Consumer ordering MCP App widgets, app-only helpers, cart state, assets, branding, and handoff. |
10
10
  | `customer-auth` | End-user (customer) auth via OIDC/Firebase bridge with delegated credentials. |
11
11
  | `stateful-draft` | Durable, caller-scoped widget state handles with optimistic revisions. |
12
12
  | `perplexity` | A real SaaS API with bearer auth and a managed `secret`. |
@@ -3,43 +3,51 @@
3
3
  Import these from `@noodleseed/one`. They are declarative builders that emit manifest data — do not hand-author the manifest or runtime artifacts. React view helpers come from `@noodleseed/one/react` (`generateHelpers`); the hook surface is documented in `widgets-and-apps.md`.
4
4
  Platform helper connectors are explicit subpath imports from `@noodleseed/one/platform` (`noodlePlatform`, `noodlePlatformCatalog`) when an app needs first-party hosted state APIs.
5
5
 
6
- ## Server & tools
6
+ ## Contents
7
+
8
+ - Exports by area
9
+ - Authoring signatures
10
+ - Recipes
11
+
12
+ ## Exports by area
13
+
14
+ ### Server & tools
7
15
 
8
16
  - `server(name, options, definitions)` — the server/app root.
9
17
  - `tool(name, { description, input, output, fulfil })` — a model-visible tool.
10
18
  - `toolWithWidget(name, { ..., view })` — a model-visible tool that renders an MCP Apps widget.
11
19
  - `toolForWidget(name, { ... })` — a widget-only helper tool, hidden from the model.
12
20
 
13
- ## Widgets & assets
21
+ ### Widgets & assets
14
22
 
15
23
  - `widget(...)` — declare a widget/view component.
16
24
  - `asset("./path")` — reference a packaged asset (e.g. an image).
17
25
  - `annotations(...)` — tool/Apps annotation metadata.
18
26
 
19
- ## Connectors & flows
27
+ ### Connectors & flows
20
28
 
21
29
  - `connector("id").version(...).http({...})` or `.compute(...)` — declarative data connectors.
22
30
  - `when(...)` — declarative conditions for recorded flows (no native branching on runtime values).
23
31
 
24
- ## Resources & prompts
32
+ ### Resources & prompts
25
33
 
26
34
  - `resource(name, { ... })` — an MCP resource.
27
35
  - `prompt(name, { ... })` — an MCP prompt.
28
36
 
29
- ## Managed config
37
+ ### Managed config
30
38
 
31
39
  - `secret("NAME")` — reference a managed secret (operated via `noodle secrets`).
32
40
  - `variable("NAME")` — reference a managed variable (operated via `noodle variables`).
33
41
 
34
- ## Customer auth
42
+ ### Customer auth
35
43
 
36
44
  - `customerAuth.oidc(...)`, `.firebase(...)`, `.microsoft(...)`, or `.bridge(...)` — end-user/customer identity for `--access customers` deployments.
37
45
 
38
- ## Sessions
46
+ ### Sessions
39
47
 
40
48
  - `handoffSession(...)` — typed cross-host handoff session envelopes.
41
49
 
42
- ## Schemas
50
+ ### Schemas
43
51
 
44
52
  - `z` — Zod, for input/output schemas (compiles to JSON Schema 2020-12).
45
53
 
@@ -52,4 +60,121 @@ Platform helper connectors are explicit subpath imports from `@noodleseed/one/pl
52
60
  - `resource(name, { uri, description?, mimeType?, fulfil })` and `prompt(name, { description?, arguments?, fulfil })` expose MCP resources/prompts.
53
61
  - `widget(name, { title, view, csp?, domain?, permissions? })` declares reusable view metadata; `asset("./path")` packages local files.
54
62
  - `customerAuth.*(...)` belongs in `server` options when deployed customer callers need verified identity; inspect `examples/customer-auth` or `examples/sharepoint` before using it.
55
- - `state` defines durable widget state handles; `handoff` declares allowed external domains for safe host handoff.
63
+ - `state` defines durable widget state handles; `handoff` declares allowed external domains for safe host handoff.
64
+
65
+ ## Recipes
66
+
67
+ Minimal, complete, compiling recipes — author in `src/server.ts`, then `noodle validate`. Inside a `fulfil`, `ctx.input` (a prompt’s arguments or a templated resource’s URI variables) and `ctx.connectors` are **symbolic**: reference them to record a flow. Recording is not execution, so never branch on their runtime values with native `if` — use `when(...)`.
68
+
69
+ ### Resource
70
+
71
+ `resource(name, { uri, title?, description?, mimeType?, fulfil })`. `fulfil` returns `{ contents: [{ uri, mimeType, text }] }`. Use a fixed URI for a constant document, or a `{var}` template whose variable arrives on `ctx.input`.
72
+
73
+ ```ts
74
+ import { resource } from '@noodleseed/one';
75
+
76
+ // Fixed-URI resource: one constant document the model can read.
77
+ resource('changelog', {
78
+ uri: 'docs://changelog',
79
+ title: 'Changelog',
80
+ mimeType: 'text/markdown',
81
+ fulfil: () => ({
82
+ contents: [
83
+ { uri: 'docs://changelog', mimeType: 'text/markdown', text: 'Changelog: 1.0.0 first release' },
84
+ ],
85
+ }),
86
+ });
87
+
88
+ // {var} URI-template resource: the URI variable arrives on ctx.input (a symbolic ref).
89
+ resource('ticket', {
90
+ uri: 'tickets://{id}',
91
+ title: 'Support ticket',
92
+ mimeType: 'text/markdown',
93
+ fulfil: (ctx) => ({
94
+ contents: [
95
+ { uri: `tickets://${ctx.input.id}`, mimeType: 'text/markdown', text: `Ticket ${ctx.input.id}` },
96
+ ],
97
+ }),
98
+ });
99
+ ```
100
+
101
+ ### Prompt
102
+
103
+ `prompt(name, { title?, description?, arguments?, fulfil })`. `arguments` is a Zod object (each key becomes a `prompts/list` descriptor) or an explicit `[{ name, description?, required? }]` list. `fulfil` returns `{ messages: [{ role, content: { type: 'text', text } }] }`; supplied argument values arrive on `ctx.input`.
104
+
105
+ ```ts
106
+ import { prompt, z } from '@noodleseed/one';
107
+
108
+ prompt('summarize_ticket', {
109
+ title: 'Summarize ticket',
110
+ description: 'Draft a short summary of a support ticket.',
111
+ // A Zod object: each key becomes a prompts/list descriptor (or pass [{ name, description?, required? }]).
112
+ arguments: z.object({
113
+ ticket_id: z.string().describe('Ticket to summarize'),
114
+ tone: z.enum(['concise', 'detailed']).default('concise'),
115
+ }),
116
+ // Argument values arrive on ctx.input; return the prompts/get messages shape.
117
+ fulfil: (ctx) => ({
118
+ messages: [
119
+ {
120
+ role: 'user',
121
+ content: {
122
+ type: 'text',
123
+ text: `Summarize ticket ${ctx.input.ticket_id} in a ${ctx.input.tone} tone.`,
124
+ },
125
+ },
126
+ ],
127
+ }),
128
+ });
129
+ ```
130
+
131
+ ### Non-trivial tool: ctx connectors, annotations, visibility, async
132
+
133
+ `ctx` is `{ input, user, connectors }`. Bind connectors with `use` on the server, then call one inside `fulfil` to record a step. `annotations.readOnly()` / `annotations.action()` set the tool hints; `visibility` defaults to `['model', 'app']` — set `['app']` to hide a helper from the model. `fulfil` may be `async` (the compiler awaits it while recording).
134
+
135
+ ```ts
136
+ import { annotations, connector, server, tool, z } from '@noodleseed/one';
137
+
138
+ // A tool-facing HTTP connector, bound to the server via `use`, reachable as ctx.connectors.crm.
139
+ const crm = connector('crm')
140
+ .version('1.0.0')
141
+ .http({
142
+ baseUrl: 'https://crm.example.com',
143
+ allowedOrigins: ['https://crm.example.com'],
144
+ operations: {
145
+ get_ticket: {
146
+ type: 'read',
147
+ method: 'GET',
148
+ path: '/tickets',
149
+ query: ['id'],
150
+ input: { id: { type: 'string', required: true } },
151
+ output: { subject: { type: 'string' }, status: { type: 'string' } },
152
+ response: { subject: '${response.subject}', status: '${response.status}' },
153
+ },
154
+ },
155
+ });
156
+
157
+ export default server('support', { title: 'Support', version: '1.0.0', use: { crm } }, [
158
+ tool('get_ticket', {
159
+ description: 'Fetch a support ticket by id.',
160
+ input: z.object({ id: z.string() }),
161
+ output: z.object({ subject: z.string(), status: z.string() }),
162
+ annotations: annotations.readOnly(), // read-only hint for hosts
163
+ visibility: ['model', 'app'], // default; use ['app'] to hide the tool from the model
164
+ // ctx is { input, user, connectors }. A connector call records one flow step (a Ref) —
165
+ // recording is not execution, so never branch on the result with native if (use when).
166
+ fulfil: ({ input, connectors }) => {
167
+ const found = connectors.crm.get_ticket({ id: input.id });
168
+ return { subject: found.subject, status: found.status };
169
+ },
170
+ }),
171
+ tool('echo', {
172
+ description: 'Echo text back.',
173
+ input: z.object({ text: z.string() }),
174
+ output: z.object({ echo: z.string() }),
175
+ annotations: annotations.action(), // mutating / world-affecting hint
176
+ // fulfil may be async — the compiler awaits it while recording the flow.
177
+ fulfil: async ({ input }) => ({ echo: input.text }),
178
+ }),
179
+ ]);
180
+ ```