@noodleseed/agent-kit 0.55.0 → 0.56.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 (38) hide show
  1. package/manifest.json +249 -249
  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 +52 -13
  12. package/skills/claude-code/examples/customer-auth/src/server.ts +41 -2
  13. package/skills/claude-code/examples/customer-auth/test/server.test.ts +18 -5
  14. package/skills/claude-code/executing-noodle-plans/SKILL.md +1 -1
  15. package/skills/claude-code/publishing-mcp-integrations/SKILL.md +1 -1
  16. package/skills/claude-code/references/authoring-workflow.md +23 -3
  17. package/skills/claude-code/references/compile-errors.md +2 -1
  18. package/skills/claude-code/references/examples.md +1 -1
  19. package/skills/claude-code/reporting-noodle-feedback/SKILL.md +1 -1
  20. package/skills/claude-code/verifying-mcp-delivery/SKILL.md +1 -1
  21. package/skills/codex/SKILL.md +1 -1
  22. package/skills/codex/authoring-mcp-servers/SKILL.md +1 -1
  23. package/skills/codex/building-mcp-apps/SKILL.md +1 -1
  24. package/skills/codex/connecting-apis-to-mcp/SKILL.md +1 -1
  25. package/skills/codex/debugging-mcp-delivery/SKILL.md +1 -1
  26. package/skills/codex/deploying-mcp-services/SKILL.md +1 -1
  27. package/skills/codex/designing-mcp-products/SKILL.md +1 -1
  28. package/skills/codex/embedding-mcp-assistants/SKILL.md +1 -1
  29. package/skills/codex/examples/customer-auth/README.md +52 -13
  30. package/skills/codex/examples/customer-auth/src/server.ts +41 -2
  31. package/skills/codex/examples/customer-auth/test/server.test.ts +18 -5
  32. package/skills/codex/executing-noodle-plans/SKILL.md +1 -1
  33. package/skills/codex/publishing-mcp-integrations/SKILL.md +1 -1
  34. package/skills/codex/references/authoring-workflow.md +23 -3
  35. package/skills/codex/references/compile-errors.md +2 -1
  36. package/skills/codex/references/examples.md +1 -1
  37. package/skills/codex/reporting-noodle-feedback/SKILL.md +1 -1
  38. 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.55.0",
3
+ "version": "0.56.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.55.0 hash:ec5bfcd0d8165205 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:0b2fd8c7e43fc69f -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:f7fa54992c8d7692 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:1e86b8704f407bd3 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:aa715bae12041d7c -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:93e735b7ffb45df1 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:76cce86729cffbee -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:cc54a67f21c0ecdb -->
6
+ <!-- noodle-skill version:0.56.0 hash:cc54a67f21c0ecdb -->
7
7
 
8
8
  # embedding-mcp-assistants
9
9
 
@@ -2,23 +2,26 @@
2
2
 
3
3
  This curated example owns the customer/end-user authentication capability slot. It proves that a SaaS app
4
4
  can protect an MCP endpoint with direct OIDC, retain role/scope-based tool authorization, and route ordinary
5
- read-only connector calls to the API origin selected by the verified customer's identity provider.
5
+ reads and confirmed actions to the API origin selected by the verified customer's identity provider.
6
6
 
7
7
  It also owns the customer-branded embedded-assistant presentation showcase. Embedded sessions lack
8
8
  direct/federated MCP OIDC endpoint claims, so these routed tools fail with `connector_route_unavailable`;
9
9
  static connectors still support embedded delegated exchange. Exercise routed tools through the MCP endpoint.
10
10
 
11
11
  The public developer entrypoint is [`src/server.ts`](src/server.ts). It exposes a deliberately small MCP
12
- surface for organization discovery:
12
+ surface for organization discovery and app lifecycle operations:
13
13
 
14
14
  - `list_my_organizations` lists the NoodleSeed.com organizations the signed-in customer belongs to (no
15
15
  arguments — the org set comes from the verified customer session).
16
16
  - `list_org_apps` lists apps for one of those organizations through that tenant's API. It is visible and
17
17
  callable only when the verified customer has the `org_apps:read` scope and either the `org_admin` or
18
18
  `org_member` role.
19
+ - `archive_org_app` archives one app only after exact runtime confirmation. It requires the
20
+ `org_apps:write` scope and `org_admin` role.
19
21
 
20
- The two tools chain: `list_my_organizations` surfaces the `org_id`s the customer can act on, and
21
- `list_org_apps` takes one of those `org_id`s. Tool code remains independent of the selected origin.
22
+ The tools chain: `list_my_organizations` surfaces the `org_id`s the customer can act on,
23
+ `list_org_apps` takes one of those ids, and `archive_org_app` accepts the selected app id. Tool code remains
24
+ independent of the selected origin.
22
25
 
23
26
  ## Declare the customer endpoint
24
27
 
@@ -48,11 +51,11 @@ const api = connector('noodleseed_app_api')
48
51
  tokenUrl: 'https://id.noodleseed.dev/oauth/token',
49
52
  clientId: variable('CUSTOMER_API_CLIENT_ID'),
50
53
  clientSecret: secret('CUSTOMER_API_CLIENT_SECRET'),
51
- scopes: ['organizations:read', 'org_apps:read'],
54
+ scopes: ['organizations:read', 'org_apps:read', 'org_apps:write'],
52
55
  audience: 'noodleseed-customer-api',
53
56
  },
54
57
  operations: {
55
- // read operations...
58
+ // read and action operations...
56
59
  },
57
60
  });
58
61
  ```
@@ -113,12 +116,45 @@ auth: customerAuth.federatedOidc({
113
116
  At runtime, Noodle Seed validates both the configured audience and exact MCP resource, projects the route
114
117
  into private request state, applies its policy, and freezes it for the call. Missing, malformed, or
115
118
  disallowed claims return `connector_route_unavailable` before credential lookup or connector egress.
116
- Resolved URLs never enter artifacts, `${user}`, logs, model output, widgets, confirmation review, broker cache
117
- keys, or delegated exchange assertions.
119
+ Resolved URLs never enter artifacts, `${user}`, logs, model output, widgets, public confirmation review,
120
+ broker cache keys, or delegated exchange assertions.
118
121
 
119
- This release permits routed reads in tools, including declared nested calls. Routed actions fail with
120
- `customer_endpoint_action_unsupported`; routed resources, prompts, and ambient context fail with
121
- `customer_endpoint_surface_unsupported`.
122
+ Routed reads work in tools, including declared nested calls. Routed actions require exact
123
+ `annotations.confirm: true`; otherwise they fail with `customer_endpoint_action_unsupported`. Routed
124
+ resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
125
+
126
+ The flagship's routed action uses the normal TypeScript action helper:
127
+
128
+ ```ts
129
+ tool('archive_org_app', {
130
+ authorization: {
131
+ requiredScopes: ['org_apps:write'],
132
+ allowedRoles: ['org_admin'],
133
+ },
134
+ annotations: annotations.openAction({ destructive: false, confirm: true }),
135
+ // input, output, and the normal connectors.app_api.archiveOrgApp(...) call...
136
+ });
137
+ ```
138
+
139
+ The flagship also opts into the current stateless hosted MCP path:
140
+
141
+ ```ts
142
+ interactions: {
143
+ confirmationFallback: 'host',
144
+ },
145
+ ```
146
+
147
+ A bidirectional client that negotiated form elicitation can complete the standard confirmation exchange
148
+ instead. The explicit host fallback trusts the MCP host to have collected native write approval before the
149
+ tool call reaches Noodle Seed; it is never inferred from client identity and does not replace auth, policy,
150
+ or accurate action/destructive annotations. Omit the fallback when connected hosts are not trusted to
151
+ provide that approval. If neither standard confirmation nor the fallback is available, the action fails
152
+ closed with `interaction_unavailable`.
153
+
154
+ Preparation stores only sorted route `{ key, fingerprint }` bindings in its private server-held
155
+ continuation; the public review exposes none of them. Acceptance re-resolves the current request route and
156
+ returns `invalid_continuation` if it is missing or changed, before policy, credentials, or egress. A match
157
+ reuses the current frozen snapshot for the action and all nested or later reads.
122
158
 
123
159
  The application developer owns the direct/federated authorization server. It must publish its path-inserted
124
160
  RFC 8414 document as direct HTTP 200 JSON with exact issuer and HTTPS authorization/token/registration/JWKS
@@ -180,7 +216,7 @@ noodle validate examples/customer-auth/src/server.ts
180
216
 
181
217
  Against a deployed customer-protected environment, set a short-lived real customer token only in
182
218
  `NOODLE_CUSTOMER_TOKEN` and add `--live --org <org> --app <app> --env <env>`. The live doctor performs
183
- credential exchanges without invoking either business tool.
219
+ credential exchanges without invoking any business tool.
184
220
 
185
221
  ## Run locally
186
222
 
@@ -487,6 +523,8 @@ https://cloud.noodleseed.dev/o/noodleseed/customer-auth/mcp
487
523
  - Tool `list_my_organizations`: calls `GET /api/organizations` and returns the organizations the signed-in
488
524
  customer is a member of. Takes no arguments; the org set is scoped by the verified customer session.
489
525
  - Tool `list_org_apps`: calls `GET /api/organizations/{org_id}/apps` for one organization `org_id`.
526
+ - Tool `archive_org_app`: after confirmation, calls
527
+ `POST /api/organizations/{org_id}/apps/{app_id}/archive`.
490
528
 
491
529
  ## Auth boundary
492
530
 
@@ -496,4 +534,5 @@ private request state.
496
534
 
497
535
  Connector-backed tools ask the broker for a route-bound delegated credential; only the endpoint key and
498
536
  fingerprint enter broker cache/single-flight state or the assertion. The route claim and inbound MCP bearer
499
- token never reach tools, connectors, widgets, model output, or downstream systems.
537
+ token never reach tools, connectors, widgets, model output, or downstream systems. Confirmed actions keep
538
+ the same URL-blind binding only in private continuation state and reject acceptance-time drift.
@@ -25,7 +25,7 @@ const noodleseedApi = connector('noodleseed_app_api')
25
25
  tokenUrl: 'https://id.noodleseed.dev/oauth/token',
26
26
  clientId: variable('CUSTOMER_API_CLIENT_ID'),
27
27
  clientSecret: secret('CUSTOMER_API_CLIENT_SECRET'),
28
- scopes: ['organizations:read', 'org_apps:read'],
28
+ scopes: ['organizations:read', 'org_apps:read', 'org_apps:write'],
29
29
  audience: 'noodleseed-customer-api',
30
30
  },
31
31
  operations: {
@@ -53,6 +53,19 @@ const noodleseedApi = connector('noodleseed_app_api')
53
53
  organizations: '${response.organizations}',
54
54
  },
55
55
  },
56
+ archive_org_app: {
57
+ type: 'action',
58
+ method: 'POST',
59
+ path: '/api/organizations/${args.org_id}/apps/${args.app_id}/archive',
60
+ input: z.object({
61
+ org_id: z.string(),
62
+ app_id: z.string(),
63
+ }),
64
+ output: z.object({ archived: z.boolean() }),
65
+ response: {
66
+ archived: '${response.archived}',
67
+ },
68
+ },
56
69
  },
57
70
  });
58
71
 
@@ -73,6 +86,7 @@ export default server(
73
86
  },
74
87
  },
75
88
  use: { app_api: noodleseedApi },
89
+ interactions: { confirmationFallback: 'host' },
76
90
  auth: customerAuth.oidc({
77
91
  issuer: 'https://id.noodleseed.dev',
78
92
  audience: 'https://org.cloud.noodleseed.dev/app/mcp',
@@ -91,7 +105,7 @@ export default server(
91
105
  },
92
106
  }),
93
107
  instructions:
94
- 'Direct/federated MCP OIDC demo. The customer IdP proves identity and privately selects the tenant API base URL for read-only MCP calls, while the broker supplies delegated credentials.',
108
+ 'Direct/federated MCP OIDC demo. The customer IdP proves identity and privately selects the tenant API base URL, while the broker supplies delegated credentials and confirmed actions stay bound to the reviewed route.',
95
109
  assistant: embeddedAssistant({
96
110
  model: openAICompatible({
97
111
  baseUrl: variable('ASSISTANT_MODEL_BASE_URL'),
@@ -169,5 +183,30 @@ export default server(
169
183
  };
170
184
  },
171
185
  }),
186
+ tool('archive_org_app', {
187
+ title: 'Archive organization app',
188
+ description: 'Archive one NoodleSeed.com app through its customer API after confirmation.',
189
+ authorization: {
190
+ requiredScopes: ['org_apps:write'],
191
+ allowedRoles: ['org_admin'],
192
+ },
193
+ input: z.object({
194
+ org_id: z.string().meta({ title: 'Organization' }),
195
+ app_id: z.string().meta({ title: 'App' }),
196
+ }),
197
+ output: z.object({
198
+ archived: z.boolean(),
199
+ }),
200
+ annotations: annotations.openAction({ destructive: false, confirm: true }),
201
+ fulfil({ input, connectors }) {
202
+ const result = connectors.app_api.archiveOrgApp({
203
+ org_id: input.org_id,
204
+ app_id: input.app_id,
205
+ });
206
+ return {
207
+ archived: result.archived,
208
+ };
209
+ },
210
+ }),
172
211
  ],
173
212
  );
@@ -41,6 +41,7 @@ describe('customer-auth example', () => {
41
41
  },
42
42
  },
43
43
  });
44
+ expect(manifest.server.interactions).toEqual({ confirmationFallback: 'host' });
44
45
  const catalog = app.toConnectorCatalog();
45
46
  expect(catalog?.connectors).toHaveLength(1);
46
47
  expect(catalog?.connectors[0]?.http).toMatchObject({
@@ -56,11 +57,11 @@ describe('customer-auth example', () => {
56
57
  clientSecret: 'CUSTOMER_API_CLIENT_SECRET',
57
58
  },
58
59
  });
59
- expect(
60
- Object.values(catalog?.connectors[0]?.http?.operations ?? {}).every(
61
- (operation) => operation.type === 'read',
62
- ),
63
- ).toBe(true);
60
+ expect(catalog?.connectors[0]?.operations).toMatchObject({
61
+ list_org_apps: { type: 'read' },
62
+ list_organizations: { type: 'read' },
63
+ archive_org_app: { type: 'action', method: 'POST' },
64
+ });
64
65
  expect(catalog?.connectors[0]?.http).not.toHaveProperty('allowedOrigins');
65
66
  expect(JSON.stringify({ manifest, catalog })).not.toContain('tenant-a.api.noodleseed.dev');
66
67
  expect(manifest.tools.find((tool) => tool.name === 'list_org_apps')?.authorization).toEqual({
@@ -70,5 +71,17 @@ describe('customer-auth example', () => {
70
71
  expect(
71
72
  manifest.tools.find((tool) => tool.name === 'list_my_organizations')?.authorization,
72
73
  ).toBeUndefined();
74
+ expect(manifest.tools.find((tool) => tool.name === 'archive_org_app')).toMatchObject({
75
+ authorization: {
76
+ requiredScopes: ['org_apps:write'],
77
+ allowedRoles: ['org_admin'],
78
+ },
79
+ annotations: {
80
+ readOnlyHint: false,
81
+ destructiveHint: false,
82
+ openWorldHint: true,
83
+ confirm: true,
84
+ },
85
+ });
73
86
  });
74
87
  });
@@ -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.55.0 hash:6a9f132ddb79352e -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:efffbf82007f935d -->
6
+ <!-- noodle-skill version:0.56.0 hash:efffbf82007f935d -->
7
7
 
8
8
  # publishing-mcp-integrations
9
9
 
@@ -115,7 +115,7 @@ That metadata must expose HTTPS `authorization_endpoint`, `token_endpoint`, `jwk
115
115
  Use a customer endpoint when the verified IdP selects a different API base URL for each SaaS customer. The claim contains the complete base URL; tool input and `${user}` do not select it. Declare one named policy, use that reference as the connector `baseUrl`, and map its claim path in direct OIDC or on every federated issuer:
116
116
 
117
117
  ```ts
118
- import { connector, customerAuth, customerEndpoint, secret, server, tool, variable, z } from '@noodleseed/one';
118
+ import { annotations, connector, customerAuth, customerEndpoint, secret, server, tool, variable, z } from '@noodleseed/one';
119
119
 
120
120
  const customerApi = customerEndpoint('customer_api', {
121
121
  allowedHttpsHostSuffixes: ['api.noodleseed.dev'],
@@ -137,6 +137,13 @@ const api = connector('customer_api_connector').version('1.0.0').http({
137
137
  input: z.object({}),
138
138
  output: z.object({ records: z.array(z.unknown()).max(100) }),
139
139
  },
140
+ archive_record: {
141
+ type: 'action',
142
+ method: 'POST',
143
+ path: '/records/${args.record_id}/archive',
144
+ input: z.object({ record_id: z.string() }),
145
+ output: z.object({ archived: z.boolean() }),
146
+ },
140
147
  },
141
148
  });
142
149
 
@@ -146,6 +153,7 @@ export default server(
146
153
  title: 'Customer records',
147
154
  version: '1.0.0',
148
155
  use: { api },
156
+ interactions: { confirmationFallback: 'host' },
149
157
  auth: customerAuth.oidc({
150
158
  issuer: 'https://id.noodleseed.dev',
151
159
  audience: 'https://org.cloud.noodleseed.dev/app/mcp',
@@ -166,17 +174,29 @@ export default server(
166
174
  return { records: result.records };
167
175
  },
168
176
  }),
177
+ tool('archive_record', {
178
+ description: 'Archive one record for the signed-in customer.',
179
+ input: z.object({ record_id: z.string() }),
180
+ output: z.object({ archived: z.boolean() }),
181
+ annotations: annotations.openAction({ destructive: false, confirm: true }),
182
+ fulfil({ input, connectors }) {
183
+ const result = connectors.api.archiveRecord({ record_id: input.record_id });
184
+ return { archived: result.archived };
185
+ },
186
+ }),
169
187
  ],
170
188
  );
171
189
  ```
172
190
 
173
191
  `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.
174
192
 
193
+ 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.
194
+
175
195
  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.
176
196
 
177
- Resolved customer URLs are private routing authority: they never enter the manifest/artifact, `${user}`, logs, model output, widgets, confirmation review, cache keys, or delegated-token-exchange assertions. Routed read operations may be used by tools, including their declared nested connector calls. Customer-routed action operations fail compilation with `customer_endpoint_action_unsupported`; routed resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported` in this release.
197
+ 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`.
178
198
 
179
- At runtime, a missing, malformed, or disallowed claim returns the same safe `connector_route_unavailable` tool error before credential lookup or connector egress. `tools/list` remains based only on roles and scopes, so route availability neither reveals tenant topology nor changes the existing authorization filter.
199
+ 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` remains based only on roles and scopes, so route availability neither reveals tenant topology nor changes the existing authorization filter.
180
200
 
181
201
  ## Per-tool authorization
182
202
 
@@ -67,9 +67,10 @@ Run `noodle validate` (add `--json` for the machine-readable envelope, `--fix-pr
67
67
  | `customer_endpoint_mapping_required` | Add the endpoint key at the cited auth routing path; every direct/federated issuer must map every customer endpoint used by the app. |
68
68
  | `customer_endpoint_unknown_mapping` | Remove the unknown or unused auth routing key, or use that exact declared `customerEndpoint` key from a reachable connector operation. |
69
69
  | `customer_endpoint_bridge_unsupported` | Replace the Firebase/Microsoft bridge with direct or federated OIDC before using auth-derived customer connector endpoints. |
70
- | `customer_endpoint_action_unsupported` | Use a read operation for this customer-routed call until route-bound action confirmation support is available. |
70
+ | `customer_endpoint_action_unsupported` | Set exact `annotations.confirm: true` on the enclosing tool, or keep the customer-routed operation read-only; action hints alone do not enable confirmation. |
71
71
  | `customer_endpoint_surface_unsupported` | Move the customer-routed call into a tool fulfilment; routed resources, prompts, and ambient providers are unsupported. |
72
72
  | `customer_endpoint_credential_source_unsupported` | Remove the manifest connection binding; a customer-routed connector uses its declared delegated token exchange auth or no auth. |
73
73
  | `customer_endpoint_policy_conflict` | Give every reachable declaration of this endpoint key one identical policy, or rename keys whose allowed origins differ. |
74
+ | `customer_endpoint_routing_inconsistent` | Regenerate the connector catalog so every action route includes all of its ordinary customer endpoint dependencies. |
74
75
  | `unused_connector_alias` | A declared connector alias is never called; remove the unused `use` entry or wire it into a tool. |
75
76
  | `arg_mismatch` | A connector call is missing or adds arguments; match the operation signature under `expected`/`got`. |
@@ -14,7 +14,7 @@ Paths are relative to this skill directory. Assets (images/fonts) are omitted fr
14
14
  | `acme-discovery` | Top-of-funnel discovery→handoff: a discovery carousel, a `create_handoff` deep link, and a design-first UX spec + wireframe. | `examples/acme-discovery/src/server.ts` + `design/` |
15
15
  | `acme-tasks` | A two-way productivity app designed around its top-3 prioritized flows (capture/prioritize/complete), with a design-first flow spec + wireframe. | `examples/acme-tasks/src/server.ts` + `design/` |
16
16
  | `acme-bistro` | End-to-end ordering with a payment-only handoff; ships a gold-standard `design/` set (UX doc, wireframe with compliance audit, API contract). | `examples/acme-bistro/src/server.ts` + `design/` |
17
- | `customer-auth` | End-user (customer) auth via OIDC/Firebase bridge with delegated credentials. | `examples/customer-auth/src/server.ts` |
17
+ | `customer-auth` | End-user OIDC, private customer API routing, route-bound confirmed actions, roles/scopes, and delegated credentials. | `examples/customer-auth/src/server.ts` |
18
18
  | `gmail-multi-account` | One curated Gmail connector reused by two account bindings, canonical account arrays, exact mutation confirmation, and an accompanying personal-automation skill. | `examples/gmail-multi-account/src/server.ts` |
19
19
  | `google-bigquery` | Keyless Google Workload Identity Federation with optional service-account impersonation, a BigQuery REST connector, and complete developer/operator setup. | `examples/google-bigquery/src/server.ts` |
20
20
 
@@ -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.55.0 hash:0f404109f4845683 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:6ef6ef551e26b78e -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:ec5bfcd0d8165205 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:0b2fd8c7e43fc69f -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:f7fa54992c8d7692 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:1e86b8704f407bd3 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:aa715bae12041d7c -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:93e735b7ffb45df1 -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:76cce86729cffbee -->
6
+ <!-- noodle-skill version:0.56.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.55.0 hash:cc54a67f21c0ecdb -->
6
+ <!-- noodle-skill version:0.56.0 hash:cc54a67f21c0ecdb -->
7
7
 
8
8
  # embedding-mcp-assistants
9
9