@noodleseed/agent-kit 0.58.1 → 0.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/manifest.json +243 -243
  2. package/package.json +1 -1
  3. package/skills/claude-code/SKILL.md +1 -1
  4. package/skills/claude-code/authoring-mcp-servers/SKILL.md +1 -1
  5. package/skills/claude-code/building-mcp-apps/SKILL.md +1 -1
  6. package/skills/claude-code/connecting-apis-to-mcp/SKILL.md +1 -1
  7. package/skills/claude-code/debugging-mcp-delivery/SKILL.md +1 -1
  8. package/skills/claude-code/deploying-mcp-services/SKILL.md +1 -1
  9. package/skills/claude-code/designing-mcp-products/SKILL.md +1 -1
  10. package/skills/claude-code/embedding-mcp-assistants/SKILL.md +1 -1
  11. package/skills/claude-code/examples/customer-auth/README.md +20 -9
  12. package/skills/claude-code/executing-noodle-plans/SKILL.md +1 -1
  13. package/skills/claude-code/publishing-mcp-integrations/SKILL.md +1 -1
  14. package/skills/claude-code/references/authoring-workflow.md +7 -3
  15. package/skills/claude-code/references/embedded-assistant.md +31 -2
  16. package/skills/claude-code/reporting-noodle-feedback/SKILL.md +1 -1
  17. package/skills/claude-code/verifying-mcp-delivery/SKILL.md +1 -1
  18. package/skills/codex/SKILL.md +1 -1
  19. package/skills/codex/authoring-mcp-servers/SKILL.md +1 -1
  20. package/skills/codex/building-mcp-apps/SKILL.md +1 -1
  21. package/skills/codex/connecting-apis-to-mcp/SKILL.md +1 -1
  22. package/skills/codex/debugging-mcp-delivery/SKILL.md +1 -1
  23. package/skills/codex/deploying-mcp-services/SKILL.md +1 -1
  24. package/skills/codex/designing-mcp-products/SKILL.md +1 -1
  25. package/skills/codex/embedding-mcp-assistants/SKILL.md +1 -1
  26. package/skills/codex/examples/customer-auth/README.md +20 -9
  27. package/skills/codex/executing-noodle-plans/SKILL.md +1 -1
  28. package/skills/codex/publishing-mcp-integrations/SKILL.md +1 -1
  29. package/skills/codex/references/authoring-workflow.md +7 -3
  30. package/skills/codex/references/embedded-assistant.md +31 -2
  31. package/skills/codex/reporting-noodle-feedback/SKILL.md +1 -1
  32. package/skills/codex/verifying-mcp-delivery/SKILL.md +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noodleseed/agent-kit",
3
- "version": "0.58.1",
3
+ "version": "0.59.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",
@@ -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.58.1 hash:ec5bfcd0d8165205 -->
6
+ <!-- noodle-skill version:0.59.0 hash:ec5bfcd0d8165205 -->
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.58.1 hash:0b2fd8c7e43fc69f -->
6
+ <!-- noodle-skill version:0.59.0 hash:0b2fd8c7e43fc69f -->
7
7
 
8
8
  # authoring-mcp-servers
9
9
 
@@ -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.58.1 hash:f7fa54992c8d7692 -->
6
+ <!-- noodle-skill version:0.59.0 hash:f7fa54992c8d7692 -->
7
7
 
8
8
  # building-mcp-apps
9
9
 
@@ -3,7 +3,7 @@ name: connecting-apis-to-mcp
3
3
  description: "Use when credentials, an API URL, an OpenAPI document, or an observed response must become real Noodle Seed MCP behavior."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.58.1 hash:1e86b8704f407bd3 -->
6
+ <!-- noodle-skill version:0.59.0 hash:1e86b8704f407bd3 -->
7
7
 
8
8
  # connecting-apis-to-mcp
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.58.1 hash:aa715bae12041d7c -->
6
+ <!-- noodle-skill version:0.59.0 hash:aa715bae12041d7c -->
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.58.1 hash:93e735b7ffb45df1 -->
6
+ <!-- noodle-skill version:0.59.0 hash:93e735b7ffb45df1 -->
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.58.1 hash:76cce86729cffbee -->
6
+ <!-- noodle-skill version:0.59.0 hash:76cce86729cffbee -->
7
7
 
8
8
  # designing-mcp-products
9
9
 
@@ -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.58.1 hash:cc54a67f21c0ecdb -->
6
+ <!-- noodle-skill version:0.59.0 hash:cc54a67f21c0ecdb -->
7
7
 
8
8
  # embedding-mcp-assistants
9
9
 
@@ -4,9 +4,9 @@ This curated example owns the customer/end-user authentication capability slot.
4
4
  can protect an MCP endpoint with direct OIDC, retain role/scope-based tool authorization, and route ordinary
5
5
  reads and confirmed actions to the API origin selected by the verified customer's identity provider.
6
6
 
7
- It also owns the customer-branded embedded-assistant presentation showcase. Embedded sessions lack
8
- direct/federated MCP OIDC endpoint claims, so these routed tools fail with `connector_route_unavailable`;
9
- static connectors still support embedded delegated exchange. Exercise routed tools through the MCP endpoint.
7
+ It also owns the customer-branded embedded-assistant presentation showcase. Direct MCP calls obtain the
8
+ route from the verified OIDC claim; embedded sessions obtain it from the authenticated customer backend's
9
+ session exchange. Both paths keep the URL outside tool/model/browser-visible state.
10
10
 
11
11
  The public developer entrypoint is [`src/server.ts`](src/server.ts). It exposes a deliberately small MCP
12
12
  surface for organization discovery and app lifecycle operations:
@@ -60,6 +60,12 @@ const api = connector('noodleseed_app_api')
60
60
  });
61
61
  ```
62
62
 
63
+ At both connector and operation level, auth must be omitted or use `delegatedTokenExchange`. The compiler
64
+ validates the concrete connector definition emitted from TypeScript, including connector defaults and
65
+ operation overrides, and reports the exact failing auth path and kind. Do not keep a bearer, API-key,
66
+ client-credentials, or managed-provider fallback for local mode; use operation fakes while leaving auth
67
+ declarative.
68
+
63
69
  ## Map the endpoint from verified OIDC
64
70
 
65
71
  The IdP claim contains the complete base URL, including an optional base path. Routing is separate from the
@@ -165,7 +171,9 @@ and refresh, then maps approved versions of this app/environment to `noodleseed-
165
171
  apps and environments use distinct audiences.
166
172
 
167
173
  Run `noodle auth doctor src/server.ts` before sharing. Its bounded, read-only probes never register a client.
168
- Adding the embedded assistant does not choose or rewrite MCP customer auth.
174
+ Adding the embedded assistant does not choose or rewrite MCP customer auth. Its authenticated backend may
175
+ bind `routing.endpoints.customer_api` during assistant-session exchange from server-owned membership data;
176
+ direct MCP requests continue to resolve the same endpoint from the configured verified OIDC claim.
169
177
 
170
178
  ## Per-tool authorization remains independent
171
179
 
@@ -325,9 +333,11 @@ The embedded assistant uses a customer-supplied OpenAI Chat Completions-compatib
325
333
  managed values at the Noodle deployment environment; none of these values belongs in the customer web
326
334
  application environment, and the API key never reaches the browser:
327
335
 
328
- The assistant session carries a verified user, tenant, deployment, roles, and scopes, but not the IdP's
329
- `tenant.api_base_url` claim. These routed operations are MCP-only; do not copy the route into page context,
330
- session claims, tool input, or model instructions.
336
+ The assistant session carries a verified user, tenant, deployment, roles, and scopes. For this flagship's
337
+ routed tools, the embedding backend resolves the signed-in user's cluster from server-owned membership data
338
+ and passes `routing: { endpoints: { customer_api: cluster.apiBaseUrl } }` to
339
+ `createAssistantSession`. Noodle validates and privately stores that route; it is not returned to the
340
+ browser. Do not copy the route into page context, session claims, tool input, or model instructions.
331
341
 
332
342
  ```bash
333
343
  noodle variables set ASSISTANT_MODEL_BASE_URL https://model.example.com/v1 --scope env
@@ -530,8 +540,9 @@ After deployment, use the assistant doctor to verify the embed client, model, an
530
540
  noodle assistant doctor --user-id <real-test-user> --origin "$PUBLIC_APP_ORIGIN" --org <org> --app <app> --env <env>
531
541
  ```
532
542
 
533
- This does not add an OIDC customer route. Routed operations succeed only through a direct/federated
534
- customer-authenticated MCP request with an allowed endpoint claim.
543
+ The doctor does not invent or test an application-specific customer route. Prove routed assistant tools by
544
+ having the authenticated embedding backend pass the user's server-verified endpoint during session
545
+ exchange, then invoke one representative safe read.
535
546
 
536
547
  If the application deliberately sends a first turn on mount, do not combine a persistent "sent" ref with a
537
548
  mount effect. React Strict Mode can abort that provisional request and then suppress the stable remount.
@@ -3,7 +3,7 @@ name: executing-noodle-plans
3
3
  description: "Use when the user asks to execute an approved, decision-complete implementation plan for a Noodle Seed project task by task with test-first changes, review, recovery, and final verification."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.58.1 hash:6a9f132ddb79352e -->
6
+ <!-- noodle-skill version:0.59.0 hash:6a9f132ddb79352e -->
7
7
 
8
8
  # Execute a Noodle Seed implementation plan
9
9
 
@@ -3,7 +3,7 @@ name: publishing-mcp-integrations
3
3
  description: "Use when preparing, reviewing, or submitting a Noodle Seed MCP integration to a host or app directory."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.58.1 hash:efffbf82007f935d -->
6
+ <!-- noodle-skill version:0.59.0 hash:efffbf82007f935d -->
7
7
 
8
8
  # publishing-mcp-integrations
9
9
 
@@ -110,6 +110,8 @@ For `customerAuth.oidc(...)` and `.federatedOidc(...)`, the application develope
110
110
 
111
111
  That metadata must expose HTTPS `authorization_endpoint`, `token_endpoint`, `jwks_uri`, and RFC 7591 `registration_endpoint`; advertise authorization-code and refresh-token grants, Dynamic Client Registration, PKCE with `code_challenge_methods_supported: ["S256"]`, and public clients with `token_endpoint_auth_methods_supported: ["none"]`. Validate the exact RFC 8707 `resource` on authorize, code exchange, and refresh, then map approved versions of one app/environment to the stable audience configured in `customerAuth`; use distinct audiences across apps and environments. Publish only public signing keys in JWKS. Run `noodle auth doctor src/server.ts`; its issuer-readiness probes perform bounded read-only GET checks and never register a client. A successful `noodle deploy --access customers` reports the same readiness without turning a diagnostic failure into a failed deployment.
112
112
 
113
+ The MCP client, or Devtools during local testing, opens the configured `authorization_endpoint`; Noodle does not submit the login form or manage the authorization server's session or CSRF cookies. Keep the login and consent UI plus credential POST on the same origin as the authorization endpoint. If a separate-site UI is unavoidable, the authorization server must explicitly trust the UI origin, use exact credentialed CORS where browser JavaScript calls it, issue the session and CSRF cookies needed by cross-site POSTs as `SameSite=None; Secure`, and return a CSRF token that the UI submits with credentials. Cookies cannot be shared across unrelated registrable domains. Browser privacy controls may still block this cross-site flow, so a same-origin route or reverse proxy remains the reliable design.
114
+
113
115
  Verify customer OAuth in three explicit layers. First run `noodle auth doctor src/server.ts --json`; a pass proves metadata and JWKS readiness, but it does not prove that registration or token issuance succeeds. Next run `noodle test src/server.ts --json`; for a protected app it proves that anonymous MCP access fails closed with exact protected-resource metadata and returns `interactiveRequired: true`. Finally run `noodle devtools src/server.ts`, complete sign-in, and make one authenticated `tools/list` request or one representative safe read. Only that final layer proves issuer, signature, stable audience, and exact-resource binding together. Never report a passing doctor or anonymous boundary smoke as working end-to-end authentication.
114
116
 
115
117
  ## Auth-derived customer API endpoints
@@ -192,6 +194,8 @@ export default server(
192
194
 
193
195
  `customerEndpoint` accepts exactly one non-empty policy arm: exact HTTPS origins, or HTTPS hostname suffixes. Exact policies may authorize an explicit non-default port; suffix policies allow port 443 and match only the exact host or dot-boundary subdomains. Do not add connector `allowedOrigins` to a customer-routed connector. Its fixed credential/token endpoints are validated independently and cannot come from caller claims.
194
196
 
197
+ Customer-routed connector auth must be omitted or use `delegatedTokenExchange` at both connector and operation level. The compiler validates the concrete connector definition emitted from TypeScript, including connector defaults and operation overrides. Do not emit bearer, API-key, client-credentials, or managed-provider fallbacks for local mode; use operation fakes while leaving auth declarative. `customer_endpoint_unsupported_auth` reports the exact failing path and auth kind.
198
+
195
199
  On a bidirectional MCP transport whose client negotiated form elicitation, Noodle sends the standard confirmation form. The current stateless hosted MCP transport cannot initiate that exchange, so the example explicitly declares `server.interactions.confirmationFallback: 'host'`. That fallback trusts the MCP host to have collected native write approval before the tool call reaches Noodle; it is never inferred from client identity and does not replace authentication, authorization, policy, or accurate action/destructive annotations. Omit it when connected hosts are not trusted to provide that approval; confirmation then fails closed with `interaction_unavailable` when the standard exchange is unavailable.
196
200
 
197
201
  Every federated issuer must repeat every endpoint key used by the app, although each issuer may choose a different claim path. Endpoint names use lowercase letters, numbers, and underscores. Resolved claims must be exact absolute HTTPS URLs of at most 2,048 UTF-8 bytes with no userinfo, query, fragment, IP literal, special-use host, or unsafe whitespace/control characters. The runtime preserves a canonical optional base path.
@@ -226,7 +230,7 @@ Role values are trusted only from the explicitly configured claim path (or the p
226
230
 
227
231
  Use delegated connector auth when the downstream API must enforce its own per-user authorization — a shared service credential plus a forwarded user id would bypass it. Three shapes exist; pick by who owns the downstream:
228
232
 
229
- - **`delegatedTokenExchange`** — your own API. The platform signs a short-lived, verifiable assertion of the signed-in user and exchanges it at a token endpoint you implement (RFC 8693). It works with verified customer OIDC identities and the built-in Firebase/Microsoft adapters; no per-user OAuth enrollment. Embedded-assistant sessions support this exchange for static connectors. Customer-routed connectors require a direct or federated MCP OIDC request and fail closed in embedded-assistant sessions because those sessions carry no IdP endpoint claim.
233
+ - **`delegatedTokenExchange`** — your own API. The platform signs a short-lived, verifiable assertion of the signed-in user and exchanges it at a token endpoint you implement (RFC 8693). It works with verified customer OIDC identities and the built-in Firebase/Microsoft adapters; no per-user OAuth enrollment. Embedded-assistant sessions can bind customer-routed connectors when the authenticated embedding backend resolves each route from server-owned tenancy data and passes it during session exchange. Browser input, page context, session claims, and tool arguments cannot supply or override that private route authority.
230
234
  - **`delegatedOAuth` with `provider: "firebase" | "microsoft"`** — Noodle-managed bridge providers using stored per-user refresh tokens. Requires the matching `customerAuth` bridge; any other provider string is the compile error `unsupported_delegated_provider`.
231
235
  - **`delegatedSessionCookie`** — Firebase-managed session-cookie apps only; not a generic mechanism.
232
236
 
@@ -258,7 +262,7 @@ scope=time_off (space-joined, when configured)
258
262
  audience=example-api (when configured)
259
263
  ```
260
264
 
261
- The `subject_token` claims: `iss` (platform issuer; JWKS at `{iss}/.well-known/jwks.json`), `sub` (verified user id), `aud` (your configured audience or the tokenUrl), `email`, `name`, `claims` (declared session claims), `tenant` (`org/app/env`), `deployment`, `iat`, `exp` (about 120 s), `jti`. A routed exchange adds `route: { key, fingerprint }`, never the customer URL; credential cache and single-flight keys include that route binding. Respond with `{ "access_token": "...", "token_type": "Bearer", "expires_in": 900 }`; the broker caches per user + connector + scopes + route until `expires_in` minus 300 s and presents the token downstream as `Authorization: Bearer`.
265
+ The `subject_token` claims: `iss` (platform issuer; JWKS at `{iss}/.well-known/jwks.json`), `sub` (verified user id), `aud` (your configured audience or the tokenUrl), `email`, `name`, `claims` (declared session claims), `tenant` (`org/app/env`), `deployment`, `customer_identity: { version: 1, issuer }`, `iat`, `exp` (about 120 s), `jti`. Key downstream users by `(customer_identity.issuer, sub)`, never by `sub` alone. Firebase uses `https://securetoken.google.com/<project-id>`; Microsoft uses `https://login.microsoftonline.com/<tenant-id>/v2.0`. A routed exchange adds `route: { key, fingerprint }`, never the customer URL; credential cache and single-flight keys include that route binding. Respond with `{ "access_token": "...", "token_type": "Bearer", "expires_in": 900 }`; the broker caches per user + connector + scopes + route until `expires_in` minus 300 s and presents the token downstream as `Authorization: Bearer`.
262
266
 
263
267
  ### The downstream token endpoint (your backend)
264
268
 
@@ -302,7 +306,7 @@ Local customer OIDC sign-in and delegated-exchange assertion trust are two disti
302
306
 
303
307
  **Never trust the Devtools issuer in production: anyone holding the local private key could impersonate a customer.**
304
308
 
305
- Diagnose statically with `noodle auth doctor`; set a short-lived real customer token only in `NOODLE_CUSTOMER_TOKEN` and add `--live --org <org> --app <app> --env <env>` to perform one exchange per delegated binding without invoking a business tool. Add `--version <version>` to test that exact pinned MCP resource. Common failures include structured `credential_unavailable` reasons such as `caller_identity_not_customer`. Direct/federated OIDC verification assigns the customer identity at the trusted verifier boundary; never ask an IdP to mint a Noodle-specific classification claim.
309
+ Diagnose statically with `noodle auth doctor`; set a short-lived real customer token only in `NOODLE_CUSTOMER_TOKEN` and add `--live --org <org> --app <app> --env <env>` to perform one exchange per delegated binding without invoking a business tool. Add `--version <version>` to test that exact pinned MCP resource. Common failures include structured `credential_unavailable` reasons such as `caller_identity_not_customer`. For a Firebase/Microsoft bridge, `caller_issuer_missing` means the session predates issuer binding: reconnect once through customer authentication, then retry. No IdP custom claim or `server.ts` change is required. Direct/federated OIDC verification assigns the customer identity at the trusted verifier boundary; never ask an IdP to mint a Noodle-specific classification claim.
306
310
 
307
311
  ## Design tools for the model
308
312
 
@@ -128,13 +128,13 @@ noodle assistant clients create --name web --org <org> --app <app> --env <env>
128
128
 
129
129
  The CLI writes `{ clientId, clientSecret }` to a mode-`0600` file and prints only its path. Move the values into the SaaS backend secret manager without printing or committing them. Rotation invalidates the previous secret.
130
130
 
131
- Validate the active deployment, backend credential, exact origin, and delegated credential exchanges without invoking a business tool:
131
+ Validate the active deployment, backend credential, exact origin, and delegated credential exchanges that do not require an application-specific customer route:
132
132
 
133
133
  ```sh
134
134
  noodle assistant doctor --origin "$PUBLIC_APP_ORIGIN" --org <org> --app <app> --env <env>
135
135
  ```
136
136
 
137
- The doctor reads `NOODLE_ASSISTANT_CLIENT_ID` / `NOODLE_ASSISTANT_CLIENT_SECRET` or the saved mode-0600 client file and never prints the secret. Pass `--user-id <real-test-user>` only when the downstream exchange requires an existing application user.
137
+ The doctor reads `NOODLE_ASSISTANT_CLIENT_ID` / `NOODLE_ASSISTANT_CLIENT_SECRET` or the saved mode-0600 client file and never prints the secret. Pass `--user-id <real-test-user>` only when the downstream exchange requires an existing application user. The assistant doctor does not supply application-specific routes; after the backend mints a routed session, invoke one representative safe read to verify its route-bound exchange and connector together.
138
138
 
139
139
  ## Integrate the customer backend
140
140
 
@@ -171,6 +171,32 @@ Authenticate before exchange. Pass backend-verified `user.roles` and OAuth-style
171
171
 
172
172
  `serviceUrl` is the Noodle Seed control-plane base URL: the value `noodle assistant clients create` prints, also stored as `serviceUrl` in `deployment.json`. It is NOT the deployment MCP endpoint (`url`, which ends in `/v1/mcp` and rejects session exchange). Never probe or guess endpoints with real credentials.
173
173
 
174
+ ### Route customer endpoints from the backend
175
+
176
+ When a connector uses `customerEndpoint("customer_api", ...)`, resolve the signed-in user's API base URL from authenticated, server-owned tenancy data and bind it during session exchange:
177
+
178
+ ```ts
179
+ const user = await requireCurrentUser(request);
180
+ const account = await requireAccountMembership(user.id);
181
+
182
+ const session = await createAssistantSession({
183
+ serviceUrl,
184
+ clientId,
185
+ clientSecret,
186
+ origin,
187
+ user: { id: user.id, email: user.email },
188
+ routing: {
189
+ endpoints: {
190
+ customer_api: account.clusterApiBaseUrl,
191
+ },
192
+ },
193
+ });
194
+ ```
195
+
196
+ The browser does not send `routing`. Authenticate the user and validate account/cluster membership before selecting the URL. Never read it from page context, request headers, session claims, tool arguments, or model output. The endpoint key must match the authored `customerEndpoint` name. Noodle validates the canonical HTTPS URL against the active artifact policy, stores it only in the private short-lived session, and omits it from the session response and caller identity.
197
+
198
+ Routing is optional: static tools continue to work, while a tool whose endpoint was omitted fails closed with `connector_route_unavailable` before credential or connector egress. A confirmed routed action binds a URL-blind fingerprint at proposal time and rejects a missing or changed route on acceptance.
199
+
174
200
  ## Ground time and ambient facts
175
201
 
176
202
  Every assistant turn receives a server-authoritative instant and user-local date/time. Locale and IANA time zone resolve in this order: backend-verified `preferences` from session exchange, fresh per-turn browser `clientContext` hints, `server.context.defaults`, then platform defaults (`en-US`/`UTC`). Browser hints affect presentation and relative-date interpretation only; they are untrusted and never authorize a tool.
@@ -515,6 +541,7 @@ Devtools privacy gate: default model and connector exercises to synthetic or moc
515
541
  - An expired turn re-exchanges once; interaction decisions never auto-retry. An explicit same-decision repeat returns the stored outcome without executing again.
516
542
  - Accept, decline, and cancel are single-use. Only accept executes; the server ignores replacement tool arguments.
517
543
  - Wrong-origin and malformed-origin requests fail closed.
544
+ - Browser-controlled fields cannot select or override `routing.endpoints`; a customer-routed connector uses only the backend-verified session route.
518
545
  - Run the production-equivalent host build after regenerating environment bindings.
519
546
  - In a real browser, submit with the keyboard, inspect console and network failures, complete session exchange and one tool turn, and render one linked App before claiming the host works.
520
547
 
@@ -530,6 +557,8 @@ Devtools privacy gate: default model and connector exercises to synthetic or moc
530
557
  | Validate rejects an origin | Non-loopback HTTP origin in `allowedOrigins` | Use the exact HTTPS production origin; HTTP is only for `localhost`/`127.0.0.1` |
531
558
  | Session exchange returns 404 | `serviceUrl` points at the deployment MCP endpoint | Use the control-plane service URL printed by `noodle assistant clients create` |
532
559
  | Session exchange returns 403 `origin is not allowed` | Request origin differs from `allowedOrigins` character-for-character | Align the exact scheme/host/port on both sides and redeploy |
560
+ | Session exchange returns `400` with `invalid assistant routing` | The authenticated backend supplied an unknown endpoint name or a malformed/policy-disallowed URL | Resolve the route from server-owned membership, use the exact authored endpoint name, and ensure the canonical HTTPS URL satisfies its active `customerEndpoint` policy; the error never reflects the URL |
561
+ | A routed assistant tool returns `connector_route_unavailable` | The authenticated backend omitted that endpoint during session exchange | Pass the server-verified route as `routing.endpoints.<name>` when minting a new session; keep it out of browser input |
533
562
  | Host session 503 | A required backend environment name is absent or mapped into the wrong deployment environment | Run `noodle assistant embed --check --json`, repair the host CI mapping, then probe the session route again |
534
563
  | `HEAD` on a widget or session path looks broken | The route contract is `GET` for the hosted sandbox/widget document or `POST` for session exchange; `HEAD` is not the product flow | Exercise the documented method and inspect its response instead of inferring readiness from `HEAD` |
535
564
  | Local server reports `listen EPERM` | The coding sandbox blocked loopback binding before application behavior ran | Rerun the same local/browser test with approved loopback permissions; do not change product code |
@@ -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.58.1 hash:0f404109f4845683 -->
6
+ <!-- noodle-skill version:0.59.0 hash:0f404109f4845683 -->
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.58.1 hash:6ef6ef551e26b78e -->
6
+ <!-- noodle-skill version:0.59.0 hash:6ef6ef551e26b78e -->
7
7
 
8
8
  # verifying-mcp-delivery
9
9
 
@@ -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.58.1 hash:ec5bfcd0d8165205 -->
6
+ <!-- noodle-skill version:0.59.0 hash:ec5bfcd0d8165205 -->
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.58.1 hash:0b2fd8c7e43fc69f -->
6
+ <!-- noodle-skill version:0.59.0 hash:0b2fd8c7e43fc69f -->
7
7
 
8
8
  # authoring-mcp-servers
9
9
 
@@ -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.58.1 hash:f7fa54992c8d7692 -->
6
+ <!-- noodle-skill version:0.59.0 hash:f7fa54992c8d7692 -->
7
7
 
8
8
  # building-mcp-apps
9
9
 
@@ -3,7 +3,7 @@ name: connecting-apis-to-mcp
3
3
  description: "Use when credentials, an API URL, an OpenAPI document, or an observed response must become real Noodle Seed MCP behavior."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.58.1 hash:1e86b8704f407bd3 -->
6
+ <!-- noodle-skill version:0.59.0 hash:1e86b8704f407bd3 -->
7
7
 
8
8
  # connecting-apis-to-mcp
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.58.1 hash:aa715bae12041d7c -->
6
+ <!-- noodle-skill version:0.59.0 hash:aa715bae12041d7c -->
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.58.1 hash:93e735b7ffb45df1 -->
6
+ <!-- noodle-skill version:0.59.0 hash:93e735b7ffb45df1 -->
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.58.1 hash:76cce86729cffbee -->
6
+ <!-- noodle-skill version:0.59.0 hash:76cce86729cffbee -->
7
7
 
8
8
  # designing-mcp-products
9
9
 
@@ -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.58.1 hash:cc54a67f21c0ecdb -->
6
+ <!-- noodle-skill version:0.59.0 hash:cc54a67f21c0ecdb -->
7
7
 
8
8
  # embedding-mcp-assistants
9
9
 
@@ -4,9 +4,9 @@ This curated example owns the customer/end-user authentication capability slot.
4
4
  can protect an MCP endpoint with direct OIDC, retain role/scope-based tool authorization, and route ordinary
5
5
  reads and confirmed actions to the API origin selected by the verified customer's identity provider.
6
6
 
7
- It also owns the customer-branded embedded-assistant presentation showcase. Embedded sessions lack
8
- direct/federated MCP OIDC endpoint claims, so these routed tools fail with `connector_route_unavailable`;
9
- static connectors still support embedded delegated exchange. Exercise routed tools through the MCP endpoint.
7
+ It also owns the customer-branded embedded-assistant presentation showcase. Direct MCP calls obtain the
8
+ route from the verified OIDC claim; embedded sessions obtain it from the authenticated customer backend's
9
+ session exchange. Both paths keep the URL outside tool/model/browser-visible state.
10
10
 
11
11
  The public developer entrypoint is [`src/server.ts`](src/server.ts). It exposes a deliberately small MCP
12
12
  surface for organization discovery and app lifecycle operations:
@@ -60,6 +60,12 @@ const api = connector('noodleseed_app_api')
60
60
  });
61
61
  ```
62
62
 
63
+ At both connector and operation level, auth must be omitted or use `delegatedTokenExchange`. The compiler
64
+ validates the concrete connector definition emitted from TypeScript, including connector defaults and
65
+ operation overrides, and reports the exact failing auth path and kind. Do not keep a bearer, API-key,
66
+ client-credentials, or managed-provider fallback for local mode; use operation fakes while leaving auth
67
+ declarative.
68
+
63
69
  ## Map the endpoint from verified OIDC
64
70
 
65
71
  The IdP claim contains the complete base URL, including an optional base path. Routing is separate from the
@@ -165,7 +171,9 @@ and refresh, then maps approved versions of this app/environment to `noodleseed-
165
171
  apps and environments use distinct audiences.
166
172
 
167
173
  Run `noodle auth doctor src/server.ts` before sharing. Its bounded, read-only probes never register a client.
168
- Adding the embedded assistant does not choose or rewrite MCP customer auth.
174
+ Adding the embedded assistant does not choose or rewrite MCP customer auth. Its authenticated backend may
175
+ bind `routing.endpoints.customer_api` during assistant-session exchange from server-owned membership data;
176
+ direct MCP requests continue to resolve the same endpoint from the configured verified OIDC claim.
169
177
 
170
178
  ## Per-tool authorization remains independent
171
179
 
@@ -325,9 +333,11 @@ The embedded assistant uses a customer-supplied OpenAI Chat Completions-compatib
325
333
  managed values at the Noodle deployment environment; none of these values belongs in the customer web
326
334
  application environment, and the API key never reaches the browser:
327
335
 
328
- The assistant session carries a verified user, tenant, deployment, roles, and scopes, but not the IdP's
329
- `tenant.api_base_url` claim. These routed operations are MCP-only; do not copy the route into page context,
330
- session claims, tool input, or model instructions.
336
+ The assistant session carries a verified user, tenant, deployment, roles, and scopes. For this flagship's
337
+ routed tools, the embedding backend resolves the signed-in user's cluster from server-owned membership data
338
+ and passes `routing: { endpoints: { customer_api: cluster.apiBaseUrl } }` to
339
+ `createAssistantSession`. Noodle validates and privately stores that route; it is not returned to the
340
+ browser. Do not copy the route into page context, session claims, tool input, or model instructions.
331
341
 
332
342
  ```bash
333
343
  noodle variables set ASSISTANT_MODEL_BASE_URL https://model.example.com/v1 --scope env
@@ -530,8 +540,9 @@ After deployment, use the assistant doctor to verify the embed client, model, an
530
540
  noodle assistant doctor --user-id <real-test-user> --origin "$PUBLIC_APP_ORIGIN" --org <org> --app <app> --env <env>
531
541
  ```
532
542
 
533
- This does not add an OIDC customer route. Routed operations succeed only through a direct/federated
534
- customer-authenticated MCP request with an allowed endpoint claim.
543
+ The doctor does not invent or test an application-specific customer route. Prove routed assistant tools by
544
+ having the authenticated embedding backend pass the user's server-verified endpoint during session
545
+ exchange, then invoke one representative safe read.
535
546
 
536
547
  If the application deliberately sends a first turn on mount, do not combine a persistent "sent" ref with a
537
548
  mount effect. React Strict Mode can abort that provisional request and then suppress the stable remount.
@@ -3,7 +3,7 @@ name: executing-noodle-plans
3
3
  description: "Use when the user asks to execute an approved, decision-complete implementation plan for a Noodle Seed project task by task with test-first changes, review, recovery, and final verification."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.58.1 hash:6a9f132ddb79352e -->
6
+ <!-- noodle-skill version:0.59.0 hash:6a9f132ddb79352e -->
7
7
 
8
8
  # Execute a Noodle Seed implementation plan
9
9
 
@@ -3,7 +3,7 @@ name: publishing-mcp-integrations
3
3
  description: "Use when preparing, reviewing, or submitting a Noodle Seed MCP integration to a host or app directory."
4
4
  ---
5
5
 
6
- <!-- noodle-skill version:0.58.1 hash:efffbf82007f935d -->
6
+ <!-- noodle-skill version:0.59.0 hash:efffbf82007f935d -->
7
7
 
8
8
  # publishing-mcp-integrations
9
9
 
@@ -110,6 +110,8 @@ For `customerAuth.oidc(...)` and `.federatedOidc(...)`, the application develope
110
110
 
111
111
  That metadata must expose HTTPS `authorization_endpoint`, `token_endpoint`, `jwks_uri`, and RFC 7591 `registration_endpoint`; advertise authorization-code and refresh-token grants, Dynamic Client Registration, PKCE with `code_challenge_methods_supported: ["S256"]`, and public clients with `token_endpoint_auth_methods_supported: ["none"]`. Validate the exact RFC 8707 `resource` on authorize, code exchange, and refresh, then map approved versions of one app/environment to the stable audience configured in `customerAuth`; use distinct audiences across apps and environments. Publish only public signing keys in JWKS. Run `noodle auth doctor src/server.ts`; its issuer-readiness probes perform bounded read-only GET checks and never register a client. A successful `noodle deploy --access customers` reports the same readiness without turning a diagnostic failure into a failed deployment.
112
112
 
113
+ The MCP client, or Devtools during local testing, opens the configured `authorization_endpoint`; Noodle does not submit the login form or manage the authorization server's session or CSRF cookies. Keep the login and consent UI plus credential POST on the same origin as the authorization endpoint. If a separate-site UI is unavoidable, the authorization server must explicitly trust the UI origin, use exact credentialed CORS where browser JavaScript calls it, issue the session and CSRF cookies needed by cross-site POSTs as `SameSite=None; Secure`, and return a CSRF token that the UI submits with credentials. Cookies cannot be shared across unrelated registrable domains. Browser privacy controls may still block this cross-site flow, so a same-origin route or reverse proxy remains the reliable design.
114
+
113
115
  Verify customer OAuth in three explicit layers. First run `noodle auth doctor src/server.ts --json`; a pass proves metadata and JWKS readiness, but it does not prove that registration or token issuance succeeds. Next run `noodle test src/server.ts --json`; for a protected app it proves that anonymous MCP access fails closed with exact protected-resource metadata and returns `interactiveRequired: true`. Finally run `noodle devtools src/server.ts`, complete sign-in, and make one authenticated `tools/list` request or one representative safe read. Only that final layer proves issuer, signature, stable audience, and exact-resource binding together. Never report a passing doctor or anonymous boundary smoke as working end-to-end authentication.
114
116
 
115
117
  ## Auth-derived customer API endpoints
@@ -192,6 +194,8 @@ export default server(
192
194
 
193
195
  `customerEndpoint` accepts exactly one non-empty policy arm: exact HTTPS origins, or HTTPS hostname suffixes. Exact policies may authorize an explicit non-default port; suffix policies allow port 443 and match only the exact host or dot-boundary subdomains. Do not add connector `allowedOrigins` to a customer-routed connector. Its fixed credential/token endpoints are validated independently and cannot come from caller claims.
194
196
 
197
+ Customer-routed connector auth must be omitted or use `delegatedTokenExchange` at both connector and operation level. The compiler validates the concrete connector definition emitted from TypeScript, including connector defaults and operation overrides. Do not emit bearer, API-key, client-credentials, or managed-provider fallbacks for local mode; use operation fakes while leaving auth declarative. `customer_endpoint_unsupported_auth` reports the exact failing path and auth kind.
198
+
195
199
  On a bidirectional MCP transport whose client negotiated form elicitation, Noodle sends the standard confirmation form. The current stateless hosted MCP transport cannot initiate that exchange, so the example explicitly declares `server.interactions.confirmationFallback: 'host'`. That fallback trusts the MCP host to have collected native write approval before the tool call reaches Noodle; it is never inferred from client identity and does not replace authentication, authorization, policy, or accurate action/destructive annotations. Omit it when connected hosts are not trusted to provide that approval; confirmation then fails closed with `interaction_unavailable` when the standard exchange is unavailable.
196
200
 
197
201
  Every federated issuer must repeat every endpoint key used by the app, although each issuer may choose a different claim path. Endpoint names use lowercase letters, numbers, and underscores. Resolved claims must be exact absolute HTTPS URLs of at most 2,048 UTF-8 bytes with no userinfo, query, fragment, IP literal, special-use host, or unsafe whitespace/control characters. The runtime preserves a canonical optional base path.
@@ -226,7 +230,7 @@ Role values are trusted only from the explicitly configured claim path (or the p
226
230
 
227
231
  Use delegated connector auth when the downstream API must enforce its own per-user authorization — a shared service credential plus a forwarded user id would bypass it. Three shapes exist; pick by who owns the downstream:
228
232
 
229
- - **`delegatedTokenExchange`** — your own API. The platform signs a short-lived, verifiable assertion of the signed-in user and exchanges it at a token endpoint you implement (RFC 8693). It works with verified customer OIDC identities and the built-in Firebase/Microsoft adapters; no per-user OAuth enrollment. Embedded-assistant sessions support this exchange for static connectors. Customer-routed connectors require a direct or federated MCP OIDC request and fail closed in embedded-assistant sessions because those sessions carry no IdP endpoint claim.
233
+ - **`delegatedTokenExchange`** — your own API. The platform signs a short-lived, verifiable assertion of the signed-in user and exchanges it at a token endpoint you implement (RFC 8693). It works with verified customer OIDC identities and the built-in Firebase/Microsoft adapters; no per-user OAuth enrollment. Embedded-assistant sessions can bind customer-routed connectors when the authenticated embedding backend resolves each route from server-owned tenancy data and passes it during session exchange. Browser input, page context, session claims, and tool arguments cannot supply or override that private route authority.
230
234
  - **`delegatedOAuth` with `provider: "firebase" | "microsoft"`** — Noodle-managed bridge providers using stored per-user refresh tokens. Requires the matching `customerAuth` bridge; any other provider string is the compile error `unsupported_delegated_provider`.
231
235
  - **`delegatedSessionCookie`** — Firebase-managed session-cookie apps only; not a generic mechanism.
232
236
 
@@ -258,7 +262,7 @@ scope=time_off (space-joined, when configured)
258
262
  audience=example-api (when configured)
259
263
  ```
260
264
 
261
- The `subject_token` claims: `iss` (platform issuer; JWKS at `{iss}/.well-known/jwks.json`), `sub` (verified user id), `aud` (your configured audience or the tokenUrl), `email`, `name`, `claims` (declared session claims), `tenant` (`org/app/env`), `deployment`, `iat`, `exp` (about 120 s), `jti`. A routed exchange adds `route: { key, fingerprint }`, never the customer URL; credential cache and single-flight keys include that route binding. Respond with `{ "access_token": "...", "token_type": "Bearer", "expires_in": 900 }`; the broker caches per user + connector + scopes + route until `expires_in` minus 300 s and presents the token downstream as `Authorization: Bearer`.
265
+ The `subject_token` claims: `iss` (platform issuer; JWKS at `{iss}/.well-known/jwks.json`), `sub` (verified user id), `aud` (your configured audience or the tokenUrl), `email`, `name`, `claims` (declared session claims), `tenant` (`org/app/env`), `deployment`, `customer_identity: { version: 1, issuer }`, `iat`, `exp` (about 120 s), `jti`. Key downstream users by `(customer_identity.issuer, sub)`, never by `sub` alone. Firebase uses `https://securetoken.google.com/<project-id>`; Microsoft uses `https://login.microsoftonline.com/<tenant-id>/v2.0`. A routed exchange adds `route: { key, fingerprint }`, never the customer URL; credential cache and single-flight keys include that route binding. Respond with `{ "access_token": "...", "token_type": "Bearer", "expires_in": 900 }`; the broker caches per user + connector + scopes + route until `expires_in` minus 300 s and presents the token downstream as `Authorization: Bearer`.
262
266
 
263
267
  ### The downstream token endpoint (your backend)
264
268
 
@@ -302,7 +306,7 @@ Local customer OIDC sign-in and delegated-exchange assertion trust are two disti
302
306
 
303
307
  **Never trust the Devtools issuer in production: anyone holding the local private key could impersonate a customer.**
304
308
 
305
- Diagnose statically with `noodle auth doctor`; set a short-lived real customer token only in `NOODLE_CUSTOMER_TOKEN` and add `--live --org <org> --app <app> --env <env>` to perform one exchange per delegated binding without invoking a business tool. Add `--version <version>` to test that exact pinned MCP resource. Common failures include structured `credential_unavailable` reasons such as `caller_identity_not_customer`. Direct/federated OIDC verification assigns the customer identity at the trusted verifier boundary; never ask an IdP to mint a Noodle-specific classification claim.
309
+ Diagnose statically with `noodle auth doctor`; set a short-lived real customer token only in `NOODLE_CUSTOMER_TOKEN` and add `--live --org <org> --app <app> --env <env>` to perform one exchange per delegated binding without invoking a business tool. Add `--version <version>` to test that exact pinned MCP resource. Common failures include structured `credential_unavailable` reasons such as `caller_identity_not_customer`. For a Firebase/Microsoft bridge, `caller_issuer_missing` means the session predates issuer binding: reconnect once through customer authentication, then retry. No IdP custom claim or `server.ts` change is required. Direct/federated OIDC verification assigns the customer identity at the trusted verifier boundary; never ask an IdP to mint a Noodle-specific classification claim.
306
310
 
307
311
  ## Design tools for the model
308
312