@noodleseed/agent-kit 0.55.0 → 0.56.1

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 +65 -23
  12. package/skills/claude-code/examples/customer-auth/src/server.ts +42 -3
  13. package/skills/claude-code/examples/customer-auth/test/server.test.ts +19 -6
  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 +26 -6
  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 +65 -23
  30. package/skills/codex/examples/customer-auth/src/server.ts +42 -3
  31. package/skills/codex/examples/customer-auth/test/server.test.ts +19 -6
  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 +26 -6
  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.1",
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.1 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.1 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.1 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.1 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.1 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.1 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.1 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.1 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
  ```
@@ -65,7 +68,7 @@ public `${user}` expression scope:
65
68
  ```ts
66
69
  auth: customerAuth.oidc({
67
70
  issuer: 'https://id.noodleseed.dev',
68
- audience: 'https://org.cloud.noodleseed.dev/app/mcp',
71
+ audience: 'noodleseed-customer-auth-prod',
69
72
  claims: {
70
73
  id: 'sub',
71
74
  email: 'email',
@@ -90,7 +93,7 @@ auth: customerAuth.federatedOidc({
90
93
  issuers: [
91
94
  {
92
95
  issuer: 'https://id.customer-a.com',
93
- audience: 'https://org.cloud.noodleseed.dev/app/mcp',
96
+ audience: 'noodleseed-customer-auth-prod',
94
97
  routing: {
95
98
  endpoints: {
96
99
  customer_api: { claim: 'tenant.api_base_url' },
@@ -99,7 +102,7 @@ auth: customerAuth.federatedOidc({
99
102
  },
100
103
  {
101
104
  issuer: 'https://login.customer-b.com',
102
- audience: 'https://org.cloud.noodleseed.dev/app/mcp',
105
+ audience: 'noodleseed-customer-auth-prod',
103
106
  routing: {
104
107
  endpoints: {
105
108
  customer_api: { claim: 'organization.routes.customer_api' },
@@ -110,21 +113,56 @@ auth: customerAuth.federatedOidc({
110
113
  }),
111
114
  ```
112
115
 
113
- At runtime, Noodle Seed validates both the configured audience and exact MCP resource, projects the route
114
- into private request state, applies its policy, and freezes it for the call. Missing, malformed, or
116
+ At runtime, Noodle Seed validates the configured stable audience, associates the caller with the exact
117
+ transport-derived MCP resource, projects the route into private request state, applies its policy, and
118
+ freezes it for the call. Missing, malformed, or
115
119
  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.
120
+ Resolved URLs never enter artifacts, `${user}`, logs, model output, widgets, public confirmation review,
121
+ broker cache keys, or delegated exchange assertions.
118
122
 
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`.
123
+ Routed reads work in tools, including declared nested calls. Routed actions require exact
124
+ `annotations.confirm: true`; otherwise they fail with `customer_endpoint_action_unsupported`. Routed
125
+ resources, prompts, and ambient context fail with `customer_endpoint_surface_unsupported`.
126
+
127
+ The flagship's routed action uses the normal TypeScript action helper:
128
+
129
+ ```ts
130
+ tool('archive_org_app', {
131
+ authorization: {
132
+ requiredScopes: ['org_apps:write'],
133
+ allowedRoles: ['org_admin'],
134
+ },
135
+ annotations: annotations.openAction({ destructive: false, confirm: true }),
136
+ // input, output, and the normal connectors.app_api.archiveOrgApp(...) call...
137
+ });
138
+ ```
139
+
140
+ The flagship also opts into the current stateless hosted MCP path:
141
+
142
+ ```ts
143
+ interactions: {
144
+ confirmationFallback: 'host',
145
+ },
146
+ ```
147
+
148
+ A bidirectional client that negotiated form elicitation can complete the standard confirmation exchange
149
+ instead. The explicit host fallback trusts the MCP host to have collected native write approval before the
150
+ tool call reaches Noodle Seed; it is never inferred from client identity and does not replace auth, policy,
151
+ or accurate action/destructive annotations. Omit the fallback when connected hosts are not trusted to
152
+ provide that approval. If neither standard confirmation nor the fallback is available, the action fails
153
+ closed with `interaction_unavailable`.
154
+
155
+ Preparation stores only sorted route `{ key, fingerprint }` bindings in its private server-held
156
+ continuation; the public review exposes none of them. Acceptance re-resolves the current request route and
157
+ returns `invalid_continuation` if it is missing or changed, before policy, credentials, or egress. A match
158
+ reuses the current frozen snapshot for the action and all nested or later reads.
122
159
 
123
160
  The application developer owns the direct/federated authorization server. It must publish its path-inserted
124
161
  RFC 8414 document as direct HTTP 200 JSON with exact issuer and HTTPS authorization/token/registration/JWKS
125
162
  endpoints, authorization-code and refresh grants, PKCE S256, public-client auth method `none`, RFC 8707
126
- resource handling, and public signing keys. Access tokens must include the exact MCP resource audience; there
127
- is no audience fallback.
163
+ resource handling, and public signing keys. It validates each exact MCP resource on authorize, code exchange,
164
+ and refresh, then maps approved versions of this app/environment to `noodleseed-customer-auth-prod`. Other
165
+ apps and environments use distinct audiences.
128
166
 
129
167
  Run `noodle auth doctor src/server.ts` before sharing. Its bounded, read-only probes never register a client.
130
168
  Adding the embedded assistant does not choose or rewrite MCP customer auth.
@@ -180,7 +218,8 @@ noodle validate examples/customer-auth/src/server.ts
180
218
 
181
219
  Against a deployed customer-protected environment, set a short-lived real customer token only in
182
220
  `NOODLE_CUSTOMER_TOKEN` and add `--live --org <org> --app <app> --env <env>`. The live doctor performs
183
- credential exchanges without invoking either business tool.
221
+ credential exchanges without invoking any business tool. Add `--version 1` when testing a pinned version;
222
+ the reported customer resource must match that versioned MCP endpoint.
184
223
 
185
224
  ## Run locally
186
225
 
@@ -487,13 +526,16 @@ https://cloud.noodleseed.dev/o/noodleseed/customer-auth/mcp
487
526
  - Tool `list_my_organizations`: calls `GET /api/organizations` and returns the organizations the signed-in
488
527
  customer is a member of. Takes no arguments; the org set is scoped by the verified customer session.
489
528
  - Tool `list_org_apps`: calls `GET /api/organizations/{org_id}/apps` for one organization `org_id`.
529
+ - Tool `archive_org_app`: after confirmation, calls
530
+ `POST /api/organizations/{org_id}/apps/{app_id}/archive`.
490
531
 
491
532
  ## Auth boundary
492
533
 
493
- Noodle Seed verifies the configured OIDC issuer, audience, and exact MCP resource before reading identity or
494
- routing claims. Public caller identity contains the user/role/scope projection; the customer route remains
495
- private request state.
534
+ Noodle Seed verifies the configured OIDC issuer and stable audience, then binds the exact transport-derived
535
+ MCP resource before reading identity or routing claims. Public caller identity contains the user/role/scope
536
+ projection; the customer route remains private request state.
496
537
 
497
538
  Connector-backed tools ask the broker for a route-bound delegated credential; only the endpoint key and
498
539
  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.
540
+ token never reach tools, connectors, widgets, model output, or downstream systems. Confirmed actions keep
541
+ 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,9 +86,10 @@ 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
- audience: 'https://org.cloud.noodleseed.dev/app/mcp',
92
+ audience: 'noodleseed-customer-auth-prod',
79
93
  claims: {
80
94
  id: 'sub',
81
95
  email: 'email',
@@ -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
  );
@@ -26,7 +26,7 @@ describe('customer-auth example', () => {
26
26
  expect(manifest.server.auth).toEqual({
27
27
  kind: 'oidc',
28
28
  issuer: 'https://id.noodleseed.dev',
29
- audience: 'https://org.cloud.noodleseed.dev/app/mcp',
29
+ audience: 'noodleseed-customer-auth-prod',
30
30
  claims: {
31
31
  id: 'sub',
32
32
  email: 'email',
@@ -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.1 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.1 hash:efffbf82007f935d -->
7
7
 
8
8
  # publishing-mcp-integrations
9
9
 
@@ -108,14 +108,14 @@ More: `auth.kind` is `bearer` | `apiKey` (needs `header`) | `clientCredentials`
108
108
 
109
109
  For `customerAuth.oidc(...)` and `.federatedOidc(...)`, the application developer owns the standards-compliant authorization server. Noodle verifies its access tokens; it does not proxy discovery, create OAuth clients, or repair the upstream server. For issuer `https://id.example.com/oauth`, publish the path-inserted RFC 8414 document at `https://id.example.com/.well-known/oauth-authorization-server/oauth` as direct unauthenticated HTTP 200 JSON — never a login redirect.
110
110
 
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"]`. Implement RFC 8707 `resource`, mint access-token `aud` for the exact MCP resource URL, and 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.
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
113
  ## Auth-derived customer API endpoints
114
114
 
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,9 +153,10 @@ 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
- audience: 'https://org.cloud.noodleseed.dev/app/mcp',
159
+ audience: 'noodleseed-customer-records-prod',
152
160
  routing: {
153
161
  endpoints: {
154
162
  customer_api: { claim: 'tenant.api_base_url' },
@@ -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
 
@@ -267,7 +287,7 @@ export async function tokenEndpoint(req: Request): Promise<Response> {
267
287
  }
268
288
  ```
269
289
 
270
- 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. 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.
290
+ 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.
271
291
 
272
292
  ## Design tools for the model
273
293
 
@@ -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.1 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.1 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.1 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.1 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.1 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.1 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.1 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.1 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.1 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.1 hash:cc54a67f21c0ecdb -->
7
7
 
8
8
  # embedding-mcp-assistants
9
9