@noodleseed/agent-kit 0.101.0 → 0.103.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/manifest.json +367 -367
- package/package.json +1 -1
- package/skills/claude-code/SKILL.md +1 -1
- package/skills/claude-code/authoring-mcp-servers/SKILL.md +1 -1
- package/skills/claude-code/authoring-mcp-servers/references/authoring-workflow.md +9 -3
- package/skills/claude-code/building-mcp-apps/SKILL.md +1 -1
- package/skills/claude-code/connecting-apis-to-mcp/SKILL.md +1 -1
- package/skills/claude-code/connecting-apis-to-mcp/references/authoring-workflow.md +9 -3
- package/skills/claude-code/creating-product-agent-guides/SKILL.md +1 -1
- package/skills/claude-code/debugging-mcp-delivery/SKILL.md +1 -1
- package/skills/claude-code/deploying-mcp-services/SKILL.md +1 -1
- package/skills/claude-code/designing-mcp-products/SKILL.md +1 -1
- package/skills/claude-code/designing-mcp-products/references/authoring-workflow.md +9 -3
- package/skills/claude-code/embedding-mcp-assistants/SKILL.md +1 -1
- package/skills/claude-code/embedding-mcp-assistants/references/authoring-workflow.md +9 -3
- package/skills/claude-code/examples/acme-bistro/README.md +1 -1
- package/skills/claude-code/examples/acme-bistro/src/server.ts +7 -7
- package/skills/claude-code/examples/customer-auth/README.md +29 -41
- package/skills/claude-code/examples/customer-auth/src/server.ts +15 -4
- package/skills/claude-code/examples/customer-auth/test/server.test.ts +6 -1
- package/skills/claude-code/examples/weather/src/server.ts +3 -3
- package/skills/claude-code/executing-noodle-plans/SKILL.md +1 -1
- package/skills/claude-code/publishing-mcp-integrations/SKILL.md +1 -1
- package/skills/claude-code/references/authoring-workflow.md +9 -3
- package/skills/claude-code/references/compile-errors.md +2 -0
- package/skills/claude-code/reporting-noodle-feedback/SKILL.md +1 -1
- package/skills/claude-code/verifying-mcp-delivery/SKILL.md +1 -1
- package/skills/claude-code/wrapping-existing-applications/SKILL.md +1 -1
- package/skills/claude-code/wrapping-existing-applications/references/authoring-workflow.md +9 -3
- package/skills/codex/SKILL.md +1 -1
- package/skills/codex/authoring-mcp-servers/SKILL.md +1 -1
- package/skills/codex/authoring-mcp-servers/references/authoring-workflow.md +9 -3
- package/skills/codex/building-mcp-apps/SKILL.md +1 -1
- package/skills/codex/connecting-apis-to-mcp/SKILL.md +1 -1
- package/skills/codex/connecting-apis-to-mcp/references/authoring-workflow.md +9 -3
- package/skills/codex/creating-product-agent-guides/SKILL.md +1 -1
- package/skills/codex/debugging-mcp-delivery/SKILL.md +1 -1
- package/skills/codex/deploying-mcp-services/SKILL.md +1 -1
- package/skills/codex/designing-mcp-products/SKILL.md +1 -1
- package/skills/codex/designing-mcp-products/references/authoring-workflow.md +9 -3
- package/skills/codex/embedding-mcp-assistants/SKILL.md +1 -1
- package/skills/codex/embedding-mcp-assistants/references/authoring-workflow.md +9 -3
- package/skills/codex/examples/acme-bistro/README.md +1 -1
- package/skills/codex/examples/acme-bistro/src/server.ts +7 -7
- package/skills/codex/examples/customer-auth/README.md +29 -41
- package/skills/codex/examples/customer-auth/src/server.ts +15 -4
- package/skills/codex/examples/customer-auth/test/server.test.ts +6 -1
- package/skills/codex/examples/weather/src/server.ts +3 -3
- package/skills/codex/executing-noodle-plans/SKILL.md +1 -1
- package/skills/codex/publishing-mcp-integrations/SKILL.md +1 -1
- package/skills/codex/references/authoring-workflow.md +9 -3
- package/skills/codex/references/compile-errors.md +2 -0
- package/skills/codex/reporting-noodle-feedback/SKILL.md +1 -1
- package/skills/codex/verifying-mcp-delivery/SKILL.md +1 -1
- package/skills/codex/wrapping-existing-applications/SKILL.md +1 -1
- package/skills/codex/wrapping-existing-applications/references/authoring-workflow.md +9 -3
|
@@ -47,6 +47,9 @@ Declare typed connectors:
|
|
|
47
47
|
- **HTTP**: `connector("id").version("1.0.0").http({ baseUrl, allowedOrigins, auth, operations })`; map request/response with `${args...}` / `${response...}`. Use `${execution.id}` for provider idempotency, never model input. Optional `evidence: { outcome: "${response.execution_outcome}", reference: "${response.id}" }` classifies completed/rejected/accepted/unknown; reference is a bounded opaque ID, never a bearer URL or payload. No automatic action retry.
|
|
48
48
|
- **MCP**: `connector("id").version("1.0.0").mcp({ endpoint, allowedOrigins, auth?, operations })` where each operation freezes the separate upstream `tool` wire name plus input/output schema. Import with `noodle import mcp`; do not hand-copy a live surface or call `tools/list` at runtime.
|
|
49
49
|
- **Compute**: `connector("id").version("1.0.0").compute(name, { input, output, calls?, run })` — a self-contained, sandboxed function (no imports/closure capture) that may call allowlisted operations via `callOperation`.
|
|
50
|
+
- **Explicit helpers**: `run: (input, host) => ...` can use `host.time.parse(iso)`, `host.time.format(epochMs)`, `host.time.parts(epochMs, timeZone)` and `host.digest(text, "hex")` (or `"base32hex"`). They convert bounded explicit data; there is no ambient clock. Pass trusted `context.temporal.instant` through fulfilment when current time is needed. `examples/weather` demonstrates a stable digest fallback for provider records without IDs.
|
|
51
|
+
- **Expected HTTP errors**: declare `responses: { "409": { response: { status: "conflict" }, evidence: { outcome: "rejected" } } }` alongside the ordinary success `response`. Explicit 4xx overrides exclude 401/403/429; validate against the same output schema. Undeclared errors still fail. Overrides never inherit successful completion evidence or introduce action retries.
|
|
52
|
+
- **Coordinated external actions**: a compute action may declare `coordination: { connectionId, namespace, key: "${args.resource}", reference: "${execution.id}" }` with a bounded execution deadline. It requires an installed application with a live bound connection and durable service coordination. Read `host.coordination.acquired` before writing; a blocked invocation may inspect `previous` through a declared read and call `host.resolveCoordination()` only after exact source proof, then require a fresh invocation/confirmation. The runtime permits at most one nested action when acquired and zero when blocked, even after recovery. Report explicit completed/rejected/unknown evidence with `host.reportOutcome(...)`; a successful return alone never unlocks uncertainty. Do not create an external-record collection for this.
|
|
50
53
|
|
|
51
54
|
Tools record connector calls into a flow; recording is not execution. Do not branch on runtime outputs with native `if` — use declarative `when(...)` conditions.
|
|
52
55
|
|
|
@@ -219,7 +222,7 @@ Every federated issuer must repeat every endpoint key used by the app, although
|
|
|
219
222
|
|
|
220
223
|
Resolved customer URLs are private routing authority: they never enter the manifest/artifact, `${user}`, logs, model output, widgets, public confirmation review, cache keys, or delegated-token-exchange assertions. Routed reads may be used by tools, including declared nested calls. A routed action—including one reached through a connector wrapper—requires exact `annotations.confirm: true`; omitted or `false` fails with `customer_endpoint_action_unsupported`. Routed resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
|
|
221
224
|
|
|
222
|
-
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list`
|
|
225
|
+
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list` follows the scope/role discovery rule and its explicit public-descriptor opt-in, so route availability neither reveals tenant topology nor changes execution authorization.
|
|
223
226
|
|
|
224
227
|
## Per-tool authorization
|
|
225
228
|
|
|
@@ -241,7 +244,10 @@ tool('list_org_apps', {
|
|
|
241
244
|
})
|
|
242
245
|
```
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
Use `authorization.discovery: 'public'` on a nonempty scope/role rule to expose its MCP descriptor before sign-in. Omission and explicit 'authorized' preserve filtered discovery and compile identically. Visibility grants no execution or product-skill eligibility; scopes remain ALL and roles ANY. Noodle derives securitySchemes; never author them.
|
|
248
|
+
For mixed customer preview, keep server customerAuth and adopt on the same org/app/env, endpoint, issuer and audience; keep the separate public Help endpoint during the pilot, and never create overlapping active issuer/audience ownership. Before any mixed deploy or access change, leave only Help unrestricted, add authorization to every customer tool, and validate plus preview with `noodle dev --access mixed`. For an existing customer-only app, first run `noodle deployments list --org <org> --app <app> --env <env> --json` and inspect an inactive `customers` record for the exact server version. If none exists, record the active ID, redeploy the unchanged secured source to the same version with `--access customers`, then list and inspect again to prove that ID is now inactive; recording it alone preserves nothing. Next deploy the policy-prepared source with `--access customers`, verify the original rollback record remains inactive, and only then run `noodle access set mixed ... --version <version>`. On pilot failure use `noodle rollback <deployment-id> --org <org> --app <app> --env <env> --reason <text>`. This app-history rollback differs from the compatible hosted service-release floor. Invalid supplied credentials fail, with no platform-human fallback. Broker exchange remains required. Local Devtools retries after successful sign-in and leaves Help available after cancellation without executing the protected tool. After deployment, test anonymous discovery, sign-in, cancellation, authenticated retry and expiry in the actual host before customer cutover; local/SDK checks do not prove ChatGPT or another host.
|
|
249
|
+
|
|
250
|
+
Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Tools retain authored order in `tools/list`; protected descriptors are omitted unless explicitly public, and an unauthorized direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
|
|
245
251
|
|
|
246
252
|
## Delegated downstream auth (call your API as the signed-in user)
|
|
247
253
|
|
|
@@ -409,7 +415,7 @@ Adapt the representative arguments and assertions when business contracts change
|
|
|
409
415
|
|
|
410
416
|
Author managed config as `secret("NAME")` / `variable("NAME")` and operate it with `noodle secrets set` / `noodle variables set` (scoped org/app/env). Never inline secret values in `server.ts`, tests, or generated files.
|
|
411
417
|
|
|
412
|
-
For business-editable configuration, declare `const
|
|
418
|
+
For business-editable configuration, declare `const guestExperience = variable("GUEST_EXPERIENCE", { schema: z.object({ notice: z.string().max(500) }), default: { notice: "Welcome" }, portal: { label: "Guest experience" }, requiredFor: ["show_menu"] })` and register it in `server(..., { variables: [guestExperience], ... }, tools)`. Use the whole ref or `guestExperience.field("notice")` in ordinary fulfilment/connector arguments. `.field()` selects a schema-declared object property, can chain for nested objects, and retains the parent variable and full configuration snapshot; it never reads operator data at author time. Keep confirmed action arguments shallow enough for complete review; do not label ordinary settings sensitive to bypass the review. The default is reusable safe intent; each business supplies its own values without editing source. Only explicit `portal` metadata exposes a setting. Name-only references and declared variables without `portal` remain technical configuration; secrets remain credential slots. See the bundled `acme-bistro` source.
|
|
413
419
|
|
|
414
420
|
Business schemas must be bounded: booleans, finite bounded numbers, bounded text, string enums, bounded arrays, and closed objects. Put defaults in the declaration, not inside Zod schema defaults; custom transforms/refinements and arbitrary schema code cannot run in the shared runtime. `requiredFor` names existing tools whose invocation needs the value; unresolved settings must leave only dependent capabilities unavailable. Publisher compilation does not require a future buyer’s values. Application code enforces business rules server-side; exposing a control or placing the rule in a prompt is insufficient.
|
|
415
421
|
|
|
@@ -65,6 +65,8 @@ Run `noodle validate` (add `--json` for the machine-readable envelope, `--fix-pr
|
|
|
65
65
|
| `connector_not_in_catalog` | The referenced connector is not in the resolved catalog; add it to the project connectors or correct the reference. |
|
|
66
66
|
| `unknown_operation` | The connector has no such operation; use an operation declared on that connector (see `didYouMean`/`suggestions`). |
|
|
67
67
|
| `connector_binding_required` | Bind the connector alias with `bind(connector, { profile, connection })`; credential-requiring operations cannot use an unbound alias. |
|
|
68
|
+
| `ambiguous_nested_connector_binding` | Keep one exact bound alias for each connector/version reached by compute; do not let a nested call guess between authorized accounts. |
|
|
69
|
+
| `invalid_connector_call_graph` | Regenerate the system-owned connector catalog from TypeScript and correct missing, cyclic, excessive or mismatched declared compute calls. |
|
|
68
70
|
| `unsupported_credential_profile` | Select a credential profile declared by the connector and accepted by the operation; use the reported suggestions instead of inventing a profile name. |
|
|
69
71
|
| `credential_scope_mismatch` | Declare a connection source whose scopes include every operation-required scope, or select an external exchange provider that can mint them. |
|
|
70
72
|
| `credential_audience_mismatch` | Set the connection source audience to the operation-required audience exactly, or use an external exchange provider that can mint it. |
|
|
@@ -3,7 +3,7 @@ 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.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:660cc6ad9469d90f -->
|
|
7
7
|
|
|
8
8
|
# reporting-noodle-feedback
|
|
9
9
|
|
|
@@ -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.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:292253cbaed9a3c5 -->
|
|
7
7
|
|
|
8
8
|
# verifying-mcp-delivery
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: wrapping-existing-applications
|
|
|
3
3
|
description: "Use when an existing application has no stable usable API and needs a read-only, identity-first Noodle Seed integration plan before implementation."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:379ab9f64878f1fe -->
|
|
7
7
|
|
|
8
8
|
# wrapping-existing-applications
|
|
9
9
|
|
|
@@ -47,6 +47,9 @@ Declare typed connectors:
|
|
|
47
47
|
- **HTTP**: `connector("id").version("1.0.0").http({ baseUrl, allowedOrigins, auth, operations })`; map request/response with `${args...}` / `${response...}`. Use `${execution.id}` for provider idempotency, never model input. Optional `evidence: { outcome: "${response.execution_outcome}", reference: "${response.id}" }` classifies completed/rejected/accepted/unknown; reference is a bounded opaque ID, never a bearer URL or payload. No automatic action retry.
|
|
48
48
|
- **MCP**: `connector("id").version("1.0.0").mcp({ endpoint, allowedOrigins, auth?, operations })` where each operation freezes the separate upstream `tool` wire name plus input/output schema. Import with `noodle import mcp`; do not hand-copy a live surface or call `tools/list` at runtime.
|
|
49
49
|
- **Compute**: `connector("id").version("1.0.0").compute(name, { input, output, calls?, run })` — a self-contained, sandboxed function (no imports/closure capture) that may call allowlisted operations via `callOperation`.
|
|
50
|
+
- **Explicit helpers**: `run: (input, host) => ...` can use `host.time.parse(iso)`, `host.time.format(epochMs)`, `host.time.parts(epochMs, timeZone)` and `host.digest(text, "hex")` (or `"base32hex"`). They convert bounded explicit data; there is no ambient clock. Pass trusted `context.temporal.instant` through fulfilment when current time is needed. `examples/weather` demonstrates a stable digest fallback for provider records without IDs.
|
|
51
|
+
- **Expected HTTP errors**: declare `responses: { "409": { response: { status: "conflict" }, evidence: { outcome: "rejected" } } }` alongside the ordinary success `response`. Explicit 4xx overrides exclude 401/403/429; validate against the same output schema. Undeclared errors still fail. Overrides never inherit successful completion evidence or introduce action retries.
|
|
52
|
+
- **Coordinated external actions**: a compute action may declare `coordination: { connectionId, namespace, key: "${args.resource}", reference: "${execution.id}" }` with a bounded execution deadline. It requires an installed application with a live bound connection and durable service coordination. Read `host.coordination.acquired` before writing; a blocked invocation may inspect `previous` through a declared read and call `host.resolveCoordination()` only after exact source proof, then require a fresh invocation/confirmation. The runtime permits at most one nested action when acquired and zero when blocked, even after recovery. Report explicit completed/rejected/unknown evidence with `host.reportOutcome(...)`; a successful return alone never unlocks uncertainty. Do not create an external-record collection for this.
|
|
50
53
|
|
|
51
54
|
Tools record connector calls into a flow; recording is not execution. Do not branch on runtime outputs with native `if` — use declarative `when(...)` conditions.
|
|
52
55
|
|
|
@@ -219,7 +222,7 @@ Every federated issuer must repeat every endpoint key used by the app, although
|
|
|
219
222
|
|
|
220
223
|
Resolved customer URLs are private routing authority: they never enter the manifest/artifact, `${user}`, logs, model output, widgets, public confirmation review, cache keys, or delegated-token-exchange assertions. Routed reads may be used by tools, including declared nested calls. A routed action—including one reached through a connector wrapper—requires exact `annotations.confirm: true`; omitted or `false` fails with `customer_endpoint_action_unsupported`. Routed resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
|
|
221
224
|
|
|
222
|
-
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list`
|
|
225
|
+
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list` follows the scope/role discovery rule and its explicit public-descriptor opt-in, so route availability neither reveals tenant topology nor changes execution authorization.
|
|
223
226
|
|
|
224
227
|
## Per-tool authorization
|
|
225
228
|
|
|
@@ -241,7 +244,10 @@ tool('list_org_apps', {
|
|
|
241
244
|
})
|
|
242
245
|
```
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
Use `authorization.discovery: 'public'` on a nonempty scope/role rule to expose its MCP descriptor before sign-in. Omission and explicit 'authorized' preserve filtered discovery and compile identically. Visibility grants no execution or product-skill eligibility; scopes remain ALL and roles ANY. Noodle derives securitySchemes; never author them.
|
|
248
|
+
For mixed customer preview, keep server customerAuth and adopt on the same org/app/env, endpoint, issuer and audience; keep the separate public Help endpoint during the pilot, and never create overlapping active issuer/audience ownership. Before any mixed deploy or access change, leave only Help unrestricted, add authorization to every customer tool, and validate plus preview with `noodle dev --access mixed`. For an existing customer-only app, first run `noodle deployments list --org <org> --app <app> --env <env> --json` and inspect an inactive `customers` record for the exact server version. If none exists, record the active ID, redeploy the unchanged secured source to the same version with `--access customers`, then list and inspect again to prove that ID is now inactive; recording it alone preserves nothing. Next deploy the policy-prepared source with `--access customers`, verify the original rollback record remains inactive, and only then run `noodle access set mixed ... --version <version>`. On pilot failure use `noodle rollback <deployment-id> --org <org> --app <app> --env <env> --reason <text>`. This app-history rollback differs from the compatible hosted service-release floor. Invalid supplied credentials fail, with no platform-human fallback. Broker exchange remains required. Local Devtools retries after successful sign-in and leaves Help available after cancellation without executing the protected tool. After deployment, test anonymous discovery, sign-in, cancellation, authenticated retry and expiry in the actual host before customer cutover; local/SDK checks do not prove ChatGPT or another host.
|
|
249
|
+
|
|
250
|
+
Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Tools retain authored order in `tools/list`; protected descriptors are omitted unless explicitly public, and an unauthorized direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
|
|
245
251
|
|
|
246
252
|
## Delegated downstream auth (call your API as the signed-in user)
|
|
247
253
|
|
|
@@ -409,7 +415,7 @@ Adapt the representative arguments and assertions when business contracts change
|
|
|
409
415
|
|
|
410
416
|
Author managed config as `secret("NAME")` / `variable("NAME")` and operate it with `noodle secrets set` / `noodle variables set` (scoped org/app/env). Never inline secret values in `server.ts`, tests, or generated files.
|
|
411
417
|
|
|
412
|
-
For business-editable configuration, declare `const
|
|
418
|
+
For business-editable configuration, declare `const guestExperience = variable("GUEST_EXPERIENCE", { schema: z.object({ notice: z.string().max(500) }), default: { notice: "Welcome" }, portal: { label: "Guest experience" }, requiredFor: ["show_menu"] })` and register it in `server(..., { variables: [guestExperience], ... }, tools)`. Use the whole ref or `guestExperience.field("notice")` in ordinary fulfilment/connector arguments. `.field()` selects a schema-declared object property, can chain for nested objects, and retains the parent variable and full configuration snapshot; it never reads operator data at author time. Keep confirmed action arguments shallow enough for complete review; do not label ordinary settings sensitive to bypass the review. The default is reusable safe intent; each business supplies its own values without editing source. Only explicit `portal` metadata exposes a setting. Name-only references and declared variables without `portal` remain technical configuration; secrets remain credential slots. See the bundled `acme-bistro` source.
|
|
413
419
|
|
|
414
420
|
Business schemas must be bounded: booleans, finite bounded numbers, bounded text, string enums, bounded arrays, and closed objects. Put defaults in the declaration, not inside Zod schema defaults; custom transforms/refinements and arbitrary schema code cannot run in the shared runtime. `requiredFor` names existing tools whose invocation needs the value; unresolved settings must leave only dependent capabilities unavailable. Publisher compilation does not require a future buyer’s values. Application code enforces business rules server-side; exposing a control or placing the rule in a prompt is insufficient.
|
|
415
421
|
|
package/skills/codex/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ 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
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:d3ceb1902ef4bb72 -->
|
|
7
7
|
|
|
8
8
|
# Noodle Seed
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: authoring-mcp-servers
|
|
|
3
3
|
description: "Use when creating or extending a headless Noodle Seed MCP server, tool, resource, prompt, or typed model-facing capability."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:dd57a15df15d10b2 -->
|
|
7
7
|
|
|
8
8
|
# authoring-mcp-servers
|
|
9
9
|
|
|
@@ -47,6 +47,9 @@ Declare typed connectors:
|
|
|
47
47
|
- **HTTP**: `connector("id").version("1.0.0").http({ baseUrl, allowedOrigins, auth, operations })`; map request/response with `${args...}` / `${response...}`. Use `${execution.id}` for provider idempotency, never model input. Optional `evidence: { outcome: "${response.execution_outcome}", reference: "${response.id}" }` classifies completed/rejected/accepted/unknown; reference is a bounded opaque ID, never a bearer URL or payload. No automatic action retry.
|
|
48
48
|
- **MCP**: `connector("id").version("1.0.0").mcp({ endpoint, allowedOrigins, auth?, operations })` where each operation freezes the separate upstream `tool` wire name plus input/output schema. Import with `noodle import mcp`; do not hand-copy a live surface or call `tools/list` at runtime.
|
|
49
49
|
- **Compute**: `connector("id").version("1.0.0").compute(name, { input, output, calls?, run })` — a self-contained, sandboxed function (no imports/closure capture) that may call allowlisted operations via `callOperation`.
|
|
50
|
+
- **Explicit helpers**: `run: (input, host) => ...` can use `host.time.parse(iso)`, `host.time.format(epochMs)`, `host.time.parts(epochMs, timeZone)` and `host.digest(text, "hex")` (or `"base32hex"`). They convert bounded explicit data; there is no ambient clock. Pass trusted `context.temporal.instant` through fulfilment when current time is needed. `examples/weather` demonstrates a stable digest fallback for provider records without IDs.
|
|
51
|
+
- **Expected HTTP errors**: declare `responses: { "409": { response: { status: "conflict" }, evidence: { outcome: "rejected" } } }` alongside the ordinary success `response`. Explicit 4xx overrides exclude 401/403/429; validate against the same output schema. Undeclared errors still fail. Overrides never inherit successful completion evidence or introduce action retries.
|
|
52
|
+
- **Coordinated external actions**: a compute action may declare `coordination: { connectionId, namespace, key: "${args.resource}", reference: "${execution.id}" }` with a bounded execution deadline. It requires an installed application with a live bound connection and durable service coordination. Read `host.coordination.acquired` before writing; a blocked invocation may inspect `previous` through a declared read and call `host.resolveCoordination()` only after exact source proof, then require a fresh invocation/confirmation. The runtime permits at most one nested action when acquired and zero when blocked, even after recovery. Report explicit completed/rejected/unknown evidence with `host.reportOutcome(...)`; a successful return alone never unlocks uncertainty. Do not create an external-record collection for this.
|
|
50
53
|
|
|
51
54
|
Tools record connector calls into a flow; recording is not execution. Do not branch on runtime outputs with native `if` — use declarative `when(...)` conditions.
|
|
52
55
|
|
|
@@ -219,7 +222,7 @@ Every federated issuer must repeat every endpoint key used by the app, although
|
|
|
219
222
|
|
|
220
223
|
Resolved customer URLs are private routing authority: they never enter the manifest/artifact, `${user}`, logs, model output, widgets, public confirmation review, cache keys, or delegated-token-exchange assertions. Routed reads may be used by tools, including declared nested calls. A routed action—including one reached through a connector wrapper—requires exact `annotations.confirm: true`; omitted or `false` fails with `customer_endpoint_action_unsupported`. Routed resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
|
|
221
224
|
|
|
222
|
-
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list`
|
|
225
|
+
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list` follows the scope/role discovery rule and its explicit public-descriptor opt-in, so route availability neither reveals tenant topology nor changes execution authorization.
|
|
223
226
|
|
|
224
227
|
## Per-tool authorization
|
|
225
228
|
|
|
@@ -241,7 +244,10 @@ tool('list_org_apps', {
|
|
|
241
244
|
})
|
|
242
245
|
```
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
Use `authorization.discovery: 'public'` on a nonempty scope/role rule to expose its MCP descriptor before sign-in. Omission and explicit 'authorized' preserve filtered discovery and compile identically. Visibility grants no execution or product-skill eligibility; scopes remain ALL and roles ANY. Noodle derives securitySchemes; never author them.
|
|
248
|
+
For mixed customer preview, keep server customerAuth and adopt on the same org/app/env, endpoint, issuer and audience; keep the separate public Help endpoint during the pilot, and never create overlapping active issuer/audience ownership. Before any mixed deploy or access change, leave only Help unrestricted, add authorization to every customer tool, and validate plus preview with `noodle dev --access mixed`. For an existing customer-only app, first run `noodle deployments list --org <org> --app <app> --env <env> --json` and inspect an inactive `customers` record for the exact server version. If none exists, record the active ID, redeploy the unchanged secured source to the same version with `--access customers`, then list and inspect again to prove that ID is now inactive; recording it alone preserves nothing. Next deploy the policy-prepared source with `--access customers`, verify the original rollback record remains inactive, and only then run `noodle access set mixed ... --version <version>`. On pilot failure use `noodle rollback <deployment-id> --org <org> --app <app> --env <env> --reason <text>`. This app-history rollback differs from the compatible hosted service-release floor. Invalid supplied credentials fail, with no platform-human fallback. Broker exchange remains required. Local Devtools retries after successful sign-in and leaves Help available after cancellation without executing the protected tool. After deployment, test anonymous discovery, sign-in, cancellation, authenticated retry and expiry in the actual host before customer cutover; local/SDK checks do not prove ChatGPT or another host.
|
|
249
|
+
|
|
250
|
+
Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Tools retain authored order in `tools/list`; protected descriptors are omitted unless explicitly public, and an unauthorized direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
|
|
245
251
|
|
|
246
252
|
## Delegated downstream auth (call your API as the signed-in user)
|
|
247
253
|
|
|
@@ -409,7 +415,7 @@ Adapt the representative arguments and assertions when business contracts change
|
|
|
409
415
|
|
|
410
416
|
Author managed config as `secret("NAME")` / `variable("NAME")` and operate it with `noodle secrets set` / `noodle variables set` (scoped org/app/env). Never inline secret values in `server.ts`, tests, or generated files.
|
|
411
417
|
|
|
412
|
-
For business-editable configuration, declare `const
|
|
418
|
+
For business-editable configuration, declare `const guestExperience = variable("GUEST_EXPERIENCE", { schema: z.object({ notice: z.string().max(500) }), default: { notice: "Welcome" }, portal: { label: "Guest experience" }, requiredFor: ["show_menu"] })` and register it in `server(..., { variables: [guestExperience], ... }, tools)`. Use the whole ref or `guestExperience.field("notice")` in ordinary fulfilment/connector arguments. `.field()` selects a schema-declared object property, can chain for nested objects, and retains the parent variable and full configuration snapshot; it never reads operator data at author time. Keep confirmed action arguments shallow enough for complete review; do not label ordinary settings sensitive to bypass the review. The default is reusable safe intent; each business supplies its own values without editing source. Only explicit `portal` metadata exposes a setting. Name-only references and declared variables without `portal` remain technical configuration; secrets remain credential slots. See the bundled `acme-bistro` source.
|
|
413
419
|
|
|
414
420
|
Business schemas must be bounded: booleans, finite bounded numbers, bounded text, string enums, bounded arrays, and closed objects. Put defaults in the declaration, not inside Zod schema defaults; custom transforms/refinements and arbitrary schema code cannot run in the shared runtime. `requiredFor` names existing tools whose invocation needs the value; unresolved settings must leave only dependent capabilities unavailable. Publisher compilation does not require a future buyer’s values. Application code enforces business rules server-side; exposing a control or placing the rule in a prompt is insufficient.
|
|
415
421
|
|
|
@@ -3,7 +3,7 @@ name: building-mcp-apps
|
|
|
3
3
|
description: "Use when a Noodle Seed MCP App, widget, interactive card, visual interaction, or host-visible UI is the primary requested outcome."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:98c7b07c82a7d7ce -->
|
|
7
7
|
|
|
8
8
|
# building-mcp-apps
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: connecting-apis-to-mcp
|
|
|
3
3
|
description: "Use when all four API-evidence inputs exist—and only then: API base URL, authentication scheme, representative safe read, and observed response."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:8020811f1769c538 -->
|
|
7
7
|
|
|
8
8
|
# connecting-apis-to-mcp
|
|
9
9
|
|
|
@@ -47,6 +47,9 @@ Declare typed connectors:
|
|
|
47
47
|
- **HTTP**: `connector("id").version("1.0.0").http({ baseUrl, allowedOrigins, auth, operations })`; map request/response with `${args...}` / `${response...}`. Use `${execution.id}` for provider idempotency, never model input. Optional `evidence: { outcome: "${response.execution_outcome}", reference: "${response.id}" }` classifies completed/rejected/accepted/unknown; reference is a bounded opaque ID, never a bearer URL or payload. No automatic action retry.
|
|
48
48
|
- **MCP**: `connector("id").version("1.0.0").mcp({ endpoint, allowedOrigins, auth?, operations })` where each operation freezes the separate upstream `tool` wire name plus input/output schema. Import with `noodle import mcp`; do not hand-copy a live surface or call `tools/list` at runtime.
|
|
49
49
|
- **Compute**: `connector("id").version("1.0.0").compute(name, { input, output, calls?, run })` — a self-contained, sandboxed function (no imports/closure capture) that may call allowlisted operations via `callOperation`.
|
|
50
|
+
- **Explicit helpers**: `run: (input, host) => ...` can use `host.time.parse(iso)`, `host.time.format(epochMs)`, `host.time.parts(epochMs, timeZone)` and `host.digest(text, "hex")` (or `"base32hex"`). They convert bounded explicit data; there is no ambient clock. Pass trusted `context.temporal.instant` through fulfilment when current time is needed. `examples/weather` demonstrates a stable digest fallback for provider records without IDs.
|
|
51
|
+
- **Expected HTTP errors**: declare `responses: { "409": { response: { status: "conflict" }, evidence: { outcome: "rejected" } } }` alongside the ordinary success `response`. Explicit 4xx overrides exclude 401/403/429; validate against the same output schema. Undeclared errors still fail. Overrides never inherit successful completion evidence or introduce action retries.
|
|
52
|
+
- **Coordinated external actions**: a compute action may declare `coordination: { connectionId, namespace, key: "${args.resource}", reference: "${execution.id}" }` with a bounded execution deadline. It requires an installed application with a live bound connection and durable service coordination. Read `host.coordination.acquired` before writing; a blocked invocation may inspect `previous` through a declared read and call `host.resolveCoordination()` only after exact source proof, then require a fresh invocation/confirmation. The runtime permits at most one nested action when acquired and zero when blocked, even after recovery. Report explicit completed/rejected/unknown evidence with `host.reportOutcome(...)`; a successful return alone never unlocks uncertainty. Do not create an external-record collection for this.
|
|
50
53
|
|
|
51
54
|
Tools record connector calls into a flow; recording is not execution. Do not branch on runtime outputs with native `if` — use declarative `when(...)` conditions.
|
|
52
55
|
|
|
@@ -219,7 +222,7 @@ Every federated issuer must repeat every endpoint key used by the app, although
|
|
|
219
222
|
|
|
220
223
|
Resolved customer URLs are private routing authority: they never enter the manifest/artifact, `${user}`, logs, model output, widgets, public confirmation review, cache keys, or delegated-token-exchange assertions. Routed reads may be used by tools, including declared nested calls. A routed action—including one reached through a connector wrapper—requires exact `annotations.confirm: true`; omitted or `false` fails with `customer_endpoint_action_unsupported`. Routed resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
|
|
221
224
|
|
|
222
|
-
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list`
|
|
225
|
+
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list` follows the scope/role discovery rule and its explicit public-descriptor opt-in, so route availability neither reveals tenant topology nor changes execution authorization.
|
|
223
226
|
|
|
224
227
|
## Per-tool authorization
|
|
225
228
|
|
|
@@ -241,7 +244,10 @@ tool('list_org_apps', {
|
|
|
241
244
|
})
|
|
242
245
|
```
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
Use `authorization.discovery: 'public'` on a nonempty scope/role rule to expose its MCP descriptor before sign-in. Omission and explicit 'authorized' preserve filtered discovery and compile identically. Visibility grants no execution or product-skill eligibility; scopes remain ALL and roles ANY. Noodle derives securitySchemes; never author them.
|
|
248
|
+
For mixed customer preview, keep server customerAuth and adopt on the same org/app/env, endpoint, issuer and audience; keep the separate public Help endpoint during the pilot, and never create overlapping active issuer/audience ownership. Before any mixed deploy or access change, leave only Help unrestricted, add authorization to every customer tool, and validate plus preview with `noodle dev --access mixed`. For an existing customer-only app, first run `noodle deployments list --org <org> --app <app> --env <env> --json` and inspect an inactive `customers` record for the exact server version. If none exists, record the active ID, redeploy the unchanged secured source to the same version with `--access customers`, then list and inspect again to prove that ID is now inactive; recording it alone preserves nothing. Next deploy the policy-prepared source with `--access customers`, verify the original rollback record remains inactive, and only then run `noodle access set mixed ... --version <version>`. On pilot failure use `noodle rollback <deployment-id> --org <org> --app <app> --env <env> --reason <text>`. This app-history rollback differs from the compatible hosted service-release floor. Invalid supplied credentials fail, with no platform-human fallback. Broker exchange remains required. Local Devtools retries after successful sign-in and leaves Help available after cancellation without executing the protected tool. After deployment, test anonymous discovery, sign-in, cancellation, authenticated retry and expiry in the actual host before customer cutover; local/SDK checks do not prove ChatGPT or another host.
|
|
249
|
+
|
|
250
|
+
Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Tools retain authored order in `tools/list`; protected descriptors are omitted unless explicitly public, and an unauthorized direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
|
|
245
251
|
|
|
246
252
|
## Delegated downstream auth (call your API as the signed-in user)
|
|
247
253
|
|
|
@@ -409,7 +415,7 @@ Adapt the representative arguments and assertions when business contracts change
|
|
|
409
415
|
|
|
410
416
|
Author managed config as `secret("NAME")` / `variable("NAME")` and operate it with `noodle secrets set` / `noodle variables set` (scoped org/app/env). Never inline secret values in `server.ts`, tests, or generated files.
|
|
411
417
|
|
|
412
|
-
For business-editable configuration, declare `const
|
|
418
|
+
For business-editable configuration, declare `const guestExperience = variable("GUEST_EXPERIENCE", { schema: z.object({ notice: z.string().max(500) }), default: { notice: "Welcome" }, portal: { label: "Guest experience" }, requiredFor: ["show_menu"] })` and register it in `server(..., { variables: [guestExperience], ... }, tools)`. Use the whole ref or `guestExperience.field("notice")` in ordinary fulfilment/connector arguments. `.field()` selects a schema-declared object property, can chain for nested objects, and retains the parent variable and full configuration snapshot; it never reads operator data at author time. Keep confirmed action arguments shallow enough for complete review; do not label ordinary settings sensitive to bypass the review. The default is reusable safe intent; each business supplies its own values without editing source. Only explicit `portal` metadata exposes a setting. Name-only references and declared variables without `portal` remain technical configuration; secrets remain credential slots. See the bundled `acme-bistro` source.
|
|
413
419
|
|
|
414
420
|
Business schemas must be bounded: booleans, finite bounded numbers, bounded text, string enums, bounded arrays, and closed objects. Put defaults in the declaration, not inside Zod schema defaults; custom transforms/refinements and arbitrary schema code cannot run in the shared runtime. `requiredFor` names existing tools whose invocation needs the value; unresolved settings must leave only dependent capabilities unavailable. Publisher compilation does not require a future buyer’s values. Application code enforces business rules server-side; exposing a control or placing the rule in a prompt is insufficient.
|
|
415
421
|
|
|
@@ -3,7 +3,7 @@ name: creating-product-agent-guides
|
|
|
3
3
|
description: "Use when a Noodle Seed MCP server needs a new or revised product agent guide, App Package skill, or explicit product-skill regeneration."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:0e06e11254563b6a -->
|
|
7
7
|
|
|
8
8
|
# creating-product-agent-guides
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: debugging-mcp-delivery
|
|
|
3
3
|
description: "Use when an existing Noodle Seed MCP project has a concrete validation, runtime, connector, App, host, deployment, or production failure."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:8d75ad13f4b3120a -->
|
|
7
7
|
|
|
8
8
|
# debugging-mcp-delivery
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: deploying-mcp-services
|
|
|
3
3
|
description: "Use when the user explicitly requests a Noodle Seed hosted link, configuration write, deployment, access change, rollback, or connection write."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:3a1e14ff9b98ddbd -->
|
|
7
7
|
|
|
8
8
|
# deploying-mcp-services
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: designing-mcp-products
|
|
|
3
3
|
description: "Use when a Noodle Seed MCP product idea needs conversational fit, user benefit, scope, interaction, or evidence design before implementation."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:a21b2ed9997f9454 -->
|
|
7
7
|
|
|
8
8
|
# designing-mcp-products
|
|
9
9
|
|
|
@@ -47,6 +47,9 @@ Declare typed connectors:
|
|
|
47
47
|
- **HTTP**: `connector("id").version("1.0.0").http({ baseUrl, allowedOrigins, auth, operations })`; map request/response with `${args...}` / `${response...}`. Use `${execution.id}` for provider idempotency, never model input. Optional `evidence: { outcome: "${response.execution_outcome}", reference: "${response.id}" }` classifies completed/rejected/accepted/unknown; reference is a bounded opaque ID, never a bearer URL or payload. No automatic action retry.
|
|
48
48
|
- **MCP**: `connector("id").version("1.0.0").mcp({ endpoint, allowedOrigins, auth?, operations })` where each operation freezes the separate upstream `tool` wire name plus input/output schema. Import with `noodle import mcp`; do not hand-copy a live surface or call `tools/list` at runtime.
|
|
49
49
|
- **Compute**: `connector("id").version("1.0.0").compute(name, { input, output, calls?, run })` — a self-contained, sandboxed function (no imports/closure capture) that may call allowlisted operations via `callOperation`.
|
|
50
|
+
- **Explicit helpers**: `run: (input, host) => ...` can use `host.time.parse(iso)`, `host.time.format(epochMs)`, `host.time.parts(epochMs, timeZone)` and `host.digest(text, "hex")` (or `"base32hex"`). They convert bounded explicit data; there is no ambient clock. Pass trusted `context.temporal.instant` through fulfilment when current time is needed. `examples/weather` demonstrates a stable digest fallback for provider records without IDs.
|
|
51
|
+
- **Expected HTTP errors**: declare `responses: { "409": { response: { status: "conflict" }, evidence: { outcome: "rejected" } } }` alongside the ordinary success `response`. Explicit 4xx overrides exclude 401/403/429; validate against the same output schema. Undeclared errors still fail. Overrides never inherit successful completion evidence or introduce action retries.
|
|
52
|
+
- **Coordinated external actions**: a compute action may declare `coordination: { connectionId, namespace, key: "${args.resource}", reference: "${execution.id}" }` with a bounded execution deadline. It requires an installed application with a live bound connection and durable service coordination. Read `host.coordination.acquired` before writing; a blocked invocation may inspect `previous` through a declared read and call `host.resolveCoordination()` only after exact source proof, then require a fresh invocation/confirmation. The runtime permits at most one nested action when acquired and zero when blocked, even after recovery. Report explicit completed/rejected/unknown evidence with `host.reportOutcome(...)`; a successful return alone never unlocks uncertainty. Do not create an external-record collection for this.
|
|
50
53
|
|
|
51
54
|
Tools record connector calls into a flow; recording is not execution. Do not branch on runtime outputs with native `if` — use declarative `when(...)` conditions.
|
|
52
55
|
|
|
@@ -219,7 +222,7 @@ Every federated issuer must repeat every endpoint key used by the app, although
|
|
|
219
222
|
|
|
220
223
|
Resolved customer URLs are private routing authority: they never enter the manifest/artifact, `${user}`, logs, model output, widgets, public confirmation review, cache keys, or delegated-token-exchange assertions. Routed reads may be used by tools, including declared nested calls. A routed action—including one reached through a connector wrapper—requires exact `annotations.confirm: true`; omitted or `false` fails with `customer_endpoint_action_unsupported`. Routed resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
|
|
221
224
|
|
|
222
|
-
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list`
|
|
225
|
+
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list` follows the scope/role discovery rule and its explicit public-descriptor opt-in, so route availability neither reveals tenant topology nor changes execution authorization.
|
|
223
226
|
|
|
224
227
|
## Per-tool authorization
|
|
225
228
|
|
|
@@ -241,7 +244,10 @@ tool('list_org_apps', {
|
|
|
241
244
|
})
|
|
242
245
|
```
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
Use `authorization.discovery: 'public'` on a nonempty scope/role rule to expose its MCP descriptor before sign-in. Omission and explicit 'authorized' preserve filtered discovery and compile identically. Visibility grants no execution or product-skill eligibility; scopes remain ALL and roles ANY. Noodle derives securitySchemes; never author them.
|
|
248
|
+
For mixed customer preview, keep server customerAuth and adopt on the same org/app/env, endpoint, issuer and audience; keep the separate public Help endpoint during the pilot, and never create overlapping active issuer/audience ownership. Before any mixed deploy or access change, leave only Help unrestricted, add authorization to every customer tool, and validate plus preview with `noodle dev --access mixed`. For an existing customer-only app, first run `noodle deployments list --org <org> --app <app> --env <env> --json` and inspect an inactive `customers` record for the exact server version. If none exists, record the active ID, redeploy the unchanged secured source to the same version with `--access customers`, then list and inspect again to prove that ID is now inactive; recording it alone preserves nothing. Next deploy the policy-prepared source with `--access customers`, verify the original rollback record remains inactive, and only then run `noodle access set mixed ... --version <version>`. On pilot failure use `noodle rollback <deployment-id> --org <org> --app <app> --env <env> --reason <text>`. This app-history rollback differs from the compatible hosted service-release floor. Invalid supplied credentials fail, with no platform-human fallback. Broker exchange remains required. Local Devtools retries after successful sign-in and leaves Help available after cancellation without executing the protected tool. After deployment, test anonymous discovery, sign-in, cancellation, authenticated retry and expiry in the actual host before customer cutover; local/SDK checks do not prove ChatGPT or another host.
|
|
249
|
+
|
|
250
|
+
Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Tools retain authored order in `tools/list`; protected descriptors are omitted unless explicitly public, and an unauthorized direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
|
|
245
251
|
|
|
246
252
|
## Delegated downstream auth (call your API as the signed-in user)
|
|
247
253
|
|
|
@@ -409,7 +415,7 @@ Adapt the representative arguments and assertions when business contracts change
|
|
|
409
415
|
|
|
410
416
|
Author managed config as `secret("NAME")` / `variable("NAME")` and operate it with `noodle secrets set` / `noodle variables set` (scoped org/app/env). Never inline secret values in `server.ts`, tests, or generated files.
|
|
411
417
|
|
|
412
|
-
For business-editable configuration, declare `const
|
|
418
|
+
For business-editable configuration, declare `const guestExperience = variable("GUEST_EXPERIENCE", { schema: z.object({ notice: z.string().max(500) }), default: { notice: "Welcome" }, portal: { label: "Guest experience" }, requiredFor: ["show_menu"] })` and register it in `server(..., { variables: [guestExperience], ... }, tools)`. Use the whole ref or `guestExperience.field("notice")` in ordinary fulfilment/connector arguments. `.field()` selects a schema-declared object property, can chain for nested objects, and retains the parent variable and full configuration snapshot; it never reads operator data at author time. Keep confirmed action arguments shallow enough for complete review; do not label ordinary settings sensitive to bypass the review. The default is reusable safe intent; each business supplies its own values without editing source. Only explicit `portal` metadata exposes a setting. Name-only references and declared variables without `portal` remain technical configuration; secrets remain credential slots. See the bundled `acme-bistro` source.
|
|
413
419
|
|
|
414
420
|
Business schemas must be bounded: booleans, finite bounded numbers, bounded text, string enums, bounded arrays, and closed objects. Put defaults in the declaration, not inside Zod schema defaults; custom transforms/refinements and arbitrary schema code cannot run in the shared runtime. `requiredFor` names existing tools whose invocation needs the value; unresolved settings must leave only dependent capabilities unavailable. Publisher compilation does not require a future buyer’s values. Application code enforces business rules server-side; exposing a control or placing the rule in a prompt is insufficient.
|
|
415
421
|
|
|
@@ -3,7 +3,7 @@ name: embedding-mcp-assistants
|
|
|
3
3
|
description: "Use when embedding a Noodle assistant into an existing SaaS or web application with browser, identity, session, and credential boundaries."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
<!-- noodle-skill version:0.
|
|
6
|
+
<!-- noodle-skill version:0.103.0 hash:d554d661ed7ac7be -->
|
|
7
7
|
|
|
8
8
|
# embedding-mcp-assistants
|
|
9
9
|
|
|
@@ -47,6 +47,9 @@ Declare typed connectors:
|
|
|
47
47
|
- **HTTP**: `connector("id").version("1.0.0").http({ baseUrl, allowedOrigins, auth, operations })`; map request/response with `${args...}` / `${response...}`. Use `${execution.id}` for provider idempotency, never model input. Optional `evidence: { outcome: "${response.execution_outcome}", reference: "${response.id}" }` classifies completed/rejected/accepted/unknown; reference is a bounded opaque ID, never a bearer URL or payload. No automatic action retry.
|
|
48
48
|
- **MCP**: `connector("id").version("1.0.0").mcp({ endpoint, allowedOrigins, auth?, operations })` where each operation freezes the separate upstream `tool` wire name plus input/output schema. Import with `noodle import mcp`; do not hand-copy a live surface or call `tools/list` at runtime.
|
|
49
49
|
- **Compute**: `connector("id").version("1.0.0").compute(name, { input, output, calls?, run })` — a self-contained, sandboxed function (no imports/closure capture) that may call allowlisted operations via `callOperation`.
|
|
50
|
+
- **Explicit helpers**: `run: (input, host) => ...` can use `host.time.parse(iso)`, `host.time.format(epochMs)`, `host.time.parts(epochMs, timeZone)` and `host.digest(text, "hex")` (or `"base32hex"`). They convert bounded explicit data; there is no ambient clock. Pass trusted `context.temporal.instant` through fulfilment when current time is needed. `examples/weather` demonstrates a stable digest fallback for provider records without IDs.
|
|
51
|
+
- **Expected HTTP errors**: declare `responses: { "409": { response: { status: "conflict" }, evidence: { outcome: "rejected" } } }` alongside the ordinary success `response`. Explicit 4xx overrides exclude 401/403/429; validate against the same output schema. Undeclared errors still fail. Overrides never inherit successful completion evidence or introduce action retries.
|
|
52
|
+
- **Coordinated external actions**: a compute action may declare `coordination: { connectionId, namespace, key: "${args.resource}", reference: "${execution.id}" }` with a bounded execution deadline. It requires an installed application with a live bound connection and durable service coordination. Read `host.coordination.acquired` before writing; a blocked invocation may inspect `previous` through a declared read and call `host.resolveCoordination()` only after exact source proof, then require a fresh invocation/confirmation. The runtime permits at most one nested action when acquired and zero when blocked, even after recovery. Report explicit completed/rejected/unknown evidence with `host.reportOutcome(...)`; a successful return alone never unlocks uncertainty. Do not create an external-record collection for this.
|
|
50
53
|
|
|
51
54
|
Tools record connector calls into a flow; recording is not execution. Do not branch on runtime outputs with native `if` — use declarative `when(...)` conditions.
|
|
52
55
|
|
|
@@ -219,7 +222,7 @@ Every federated issuer must repeat every endpoint key used by the app, although
|
|
|
219
222
|
|
|
220
223
|
Resolved customer URLs are private routing authority: they never enter the manifest/artifact, `${user}`, logs, model output, widgets, public confirmation review, cache keys, or delegated-token-exchange assertions. Routed reads may be used by tools, including declared nested calls. A routed action—including one reached through a connector wrapper—requires exact `annotations.confirm: true`; omitted or `false` fails with `customer_endpoint_action_unsupported`. Routed resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
|
|
221
224
|
|
|
222
|
-
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list`
|
|
225
|
+
At runtime, an initially missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. Preparation stores only sorted route `{ key, fingerprint }` bindings in the private server-held continuation. Acceptance re-resolves the current request routes; a missing or changed binding returns `invalid_continuation` before policy, credentials, or egress, then the matching frozen snapshot is reused for the action and nested/later calls. `tools/list` follows the scope/role discovery rule and its explicit public-descriptor opt-in, so route availability neither reveals tenant topology nor changes execution authorization.
|
|
223
226
|
|
|
224
227
|
## Per-tool authorization
|
|
225
228
|
|
|
@@ -241,7 +244,10 @@ tool('list_org_apps', {
|
|
|
241
244
|
})
|
|
242
245
|
```
|
|
243
246
|
|
|
244
|
-
|
|
247
|
+
Use `authorization.discovery: 'public'` on a nonempty scope/role rule to expose its MCP descriptor before sign-in. Omission and explicit 'authorized' preserve filtered discovery and compile identically. Visibility grants no execution or product-skill eligibility; scopes remain ALL and roles ANY. Noodle derives securitySchemes; never author them.
|
|
248
|
+
For mixed customer preview, keep server customerAuth and adopt on the same org/app/env, endpoint, issuer and audience; keep the separate public Help endpoint during the pilot, and never create overlapping active issuer/audience ownership. Before any mixed deploy or access change, leave only Help unrestricted, add authorization to every customer tool, and validate plus preview with `noodle dev --access mixed`. For an existing customer-only app, first run `noodle deployments list --org <org> --app <app> --env <env> --json` and inspect an inactive `customers` record for the exact server version. If none exists, record the active ID, redeploy the unchanged secured source to the same version with `--access customers`, then list and inspect again to prove that ID is now inactive; recording it alone preserves nothing. Next deploy the policy-prepared source with `--access customers`, verify the original rollback record remains inactive, and only then run `noodle access set mixed ... --version <version>`. On pilot failure use `noodle rollback <deployment-id> --org <org> --app <app> --env <env> --reason <text>`. This app-history rollback differs from the compatible hosted service-release floor. Invalid supplied credentials fail, with no platform-human fallback. Broker exchange remains required. Local Devtools retries after successful sign-in and leaves Help available after cancellation without executing the protected tool. After deployment, test anonymous discovery, sign-in, cancellation, authenticated retry and expiry in the actual host before customer cutover; local/SDK checks do not prove ChatGPT or another host.
|
|
249
|
+
|
|
250
|
+
Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Tools retain authored order in `tools/list`; protected descriptors are omitted unless explicitly public, and an unauthorized direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
|
|
245
251
|
|
|
246
252
|
## Delegated downstream auth (call your API as the signed-in user)
|
|
247
253
|
|
|
@@ -409,7 +415,7 @@ Adapt the representative arguments and assertions when business contracts change
|
|
|
409
415
|
|
|
410
416
|
Author managed config as `secret("NAME")` / `variable("NAME")` and operate it with `noodle secrets set` / `noodle variables set` (scoped org/app/env). Never inline secret values in `server.ts`, tests, or generated files.
|
|
411
417
|
|
|
412
|
-
For business-editable configuration, declare `const
|
|
418
|
+
For business-editable configuration, declare `const guestExperience = variable("GUEST_EXPERIENCE", { schema: z.object({ notice: z.string().max(500) }), default: { notice: "Welcome" }, portal: { label: "Guest experience" }, requiredFor: ["show_menu"] })` and register it in `server(..., { variables: [guestExperience], ... }, tools)`. Use the whole ref or `guestExperience.field("notice")` in ordinary fulfilment/connector arguments. `.field()` selects a schema-declared object property, can chain for nested objects, and retains the parent variable and full configuration snapshot; it never reads operator data at author time. Keep confirmed action arguments shallow enough for complete review; do not label ordinary settings sensitive to bypass the review. The default is reusable safe intent; each business supplies its own values without editing source. Only explicit `portal` metadata exposes a setting. Name-only references and declared variables without `portal` remain technical configuration; secrets remain credential slots. See the bundled `acme-bistro` source.
|
|
413
419
|
|
|
414
420
|
Business schemas must be bounded: booleans, finite bounded numbers, bounded text, string enums, bounded arrays, and closed objects. Put defaults in the declaration, not inside Zod schema defaults; custom transforms/refinements and arbitrary schema code cannot run in the shared runtime. `requiredFor` names existing tools whose invocation needs the value; unresolved settings must leave only dependent capabilities unavailable. Publisher compilation does not require a future buyer’s values. Application code enforces business rules server-side; exposing a control or placing the rule in a prompt is insufficient.
|
|
415
421
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Fictional [`menu/cart`](src/server.ts) with off-app payment: card data stays outside the app.
|
|
4
4
|
|
|
5
|
-
`
|
|
5
|
+
`GUEST_EXPERIENCE` settings use `guestExperience.field('notice')` to select a typed business value; native `guest_requests` demonstrate status, field exposure and notes. `submit_guest_request` requires an authorized installation and returns a receipt
|
|
6
6
|
for staff review. The installed skill's `references/authoring-workflow.md` owns setup and source contracts.
|
|
7
7
|
External collections declare a bounded connector scan; provider changes remain ordinary tools.
|
|
8
8
|
|
|
@@ -20,11 +20,11 @@ const menu = [
|
|
|
20
20
|
|
|
21
21
|
const itemId = z.enum(['stone_pizza', 'roast_bowl', 'house_salad', 'lemon_tart', 'sparkling']);
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
const
|
|
25
|
-
schema: z.string().max(500),
|
|
26
|
-
default: 'Ask us about dietary requirements before placing your order.',
|
|
27
|
-
portal: { label: '
|
|
23
|
+
// Operators configure the notice without redeploying.
|
|
24
|
+
const guestExperience = variable('GUEST_EXPERIENCE', {
|
|
25
|
+
schema: z.object({ notice: z.string().max(500) }),
|
|
26
|
+
default: { notice: 'Ask us about dietary requirements before placing your order.' },
|
|
27
|
+
portal: { label: 'Guest experience', group: 'Guest experience' },
|
|
28
28
|
requiredFor: ['show_menu'],
|
|
29
29
|
});
|
|
30
30
|
|
|
@@ -86,7 +86,7 @@ export default server(
|
|
|
86
86
|
// Reusable business-record intent. Storage, lifecycle, access, and public intake bind separately.
|
|
87
87
|
collections: [guestRequests],
|
|
88
88
|
use: { records: noodlePlatform.records.v1 },
|
|
89
|
-
variables: [
|
|
89
|
+
variables: [guestExperience],
|
|
90
90
|
},
|
|
91
91
|
[
|
|
92
92
|
tool('submit_guest_request', {
|
|
@@ -123,7 +123,7 @@ export default server(
|
|
|
123
123
|
fulfil: ({ input }) => ({
|
|
124
124
|
status: `Acme Bistro menu is ready for ${input.customer}. Build the order here; pay at checkout.`,
|
|
125
125
|
customer: input.customer,
|
|
126
|
-
serviceNotice,
|
|
126
|
+
serviceNotice: guestExperience.field('notice'),
|
|
127
127
|
items: menu,
|
|
128
128
|
}),
|
|
129
129
|
viewTitle: 'Order at Acme Bistro',
|