@noodleseed/agent-kit 0.49.0 → 0.51.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 (40) hide show
  1. package/manifest.json +251 -251
  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 +25 -4
  12. package/skills/claude-code/examples/customer-auth/src/server.ts +5 -0
  13. package/skills/claude-code/examples/customer-auth/test/server.test.ts +11 -0
  14. package/skills/claude-code/examples/hello/README.md +3 -2
  15. package/skills/claude-code/executing-noodle-plans/SKILL.md +1 -1
  16. package/skills/claude-code/publishing-mcp-integrations/SKILL.md +1 -1
  17. package/skills/claude-code/references/authoring-workflow.md +23 -0
  18. package/skills/claude-code/references/embedded-assistant.md +14 -3
  19. package/skills/claude-code/references/feedback.md +6 -3
  20. package/skills/claude-code/reporting-noodle-feedback/SKILL.md +1 -1
  21. package/skills/claude-code/verifying-mcp-delivery/SKILL.md +1 -1
  22. package/skills/codex/SKILL.md +1 -1
  23. package/skills/codex/authoring-mcp-servers/SKILL.md +1 -1
  24. package/skills/codex/building-mcp-apps/SKILL.md +1 -1
  25. package/skills/codex/connecting-apis-to-mcp/SKILL.md +1 -1
  26. package/skills/codex/debugging-mcp-delivery/SKILL.md +1 -1
  27. package/skills/codex/deploying-mcp-services/SKILL.md +1 -1
  28. package/skills/codex/designing-mcp-products/SKILL.md +1 -1
  29. package/skills/codex/embedding-mcp-assistants/SKILL.md +1 -1
  30. package/skills/codex/examples/customer-auth/README.md +25 -4
  31. package/skills/codex/examples/customer-auth/src/server.ts +5 -0
  32. package/skills/codex/examples/customer-auth/test/server.test.ts +11 -0
  33. package/skills/codex/examples/hello/README.md +3 -2
  34. package/skills/codex/executing-noodle-plans/SKILL.md +1 -1
  35. package/skills/codex/publishing-mcp-integrations/SKILL.md +1 -1
  36. package/skills/codex/references/authoring-workflow.md +23 -0
  37. package/skills/codex/references/embedded-assistant.md +14 -3
  38. package/skills/codex/references/feedback.md +6 -3
  39. package/skills/codex/reporting-noodle-feedback/SKILL.md +1 -1
  40. 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.49.0",
3
+ "version": "0.51.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.49.0 hash:cd6ca0d915e6acb9 -->
6
+ <!-- noodle-skill version:0.51.0 hash:cd6ca0d915e6acb9 -->
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.49.0 hash:0b2fd8c7e43fc69f -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:f7fa54992c8d7692 -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:1e86b8704f407bd3 -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:aa715bae12041d7c -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:93e735b7ffb45df1 -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:76cce86729cffbee -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:cc54a67f21c0ecdb -->
6
+ <!-- noodle-skill version:0.51.0 hash:cc54a67f21c0ecdb -->
7
7
 
8
8
  # embedding-mcp-assistants
9
9
 
@@ -15,7 +15,9 @@ surface for org discovery:
15
15
 
16
16
  - `list_my_organizations` lists the NoodleSeed.com organizations the signed-in customer belongs to (no
17
17
  arguments — the org set comes from the verified customer session).
18
- - `list_org_apps` lists apps for one of those organizations through the dev app API.
18
+ - `list_org_apps` lists apps for one of those organizations through the dev app API. It is visible and
19
+ callable only when the verified customer has the `org_apps:read` scope and either the `org_admin` or
20
+ `org_member` role.
19
21
 
20
22
  The two tools chain: `list_my_organizations` surfaces the `org_id`s the customer can act on, and
21
23
  `list_org_apps` takes one of those `org_id`s. There is no NoodleSeed-specific SDK helper. The downstream API
@@ -74,9 +76,10 @@ auth: customerAuth.firebase({
74
76
  email: 'email',
75
77
  name: 'name',
76
78
  tenant: 'firebase.tenant',
77
- orgs: 'claims.orgs',
78
- roles: 'claims.roles',
79
- },
79
+ orgs: 'claims.orgs',
80
+ roles: 'claims.roles',
81
+ scopes: 'claims.scopes',
82
+ },
80
83
  }),
81
84
  ```
82
85
 
@@ -92,6 +95,24 @@ auth: {
92
95
  },
93
96
  ```
94
97
 
98
+ The mapped `roles` and `scopes` paths are read only after Firebase verifies the ID token. The restricted
99
+ tool declares its rule beside the rest of its public contract:
100
+
101
+ ```ts
102
+ tool('list_org_apps', {
103
+ authorization: {
104
+ requiredScopes: ['org_apps:read'],
105
+ allowedRoles: ['org_admin', 'org_member'],
106
+ },
107
+ // input, output, and fulfilment...
108
+ });
109
+ ```
110
+
111
+ Every required scope must be present and at least one allowed role must match. When both lists are declared,
112
+ both conditions apply. A restricted tool is omitted from `tools/list` for an ineligible customer and a
113
+ direct `tools/call` still fails closed. Do not use unverified page context, request arguments, connector
114
+ responses, or an arbitrary generic `roles` claim as authorization input.
115
+
95
116
  Tool code calls the connector normally:
96
117
 
97
118
  ```ts
@@ -80,6 +80,7 @@ export default server(
80
80
  tenant: 'firebase.tenant',
81
81
  orgs: 'claims.orgs',
82
82
  roles: 'claims.roles',
83
+ scopes: 'claims.scopes',
83
84
  },
84
85
  }),
85
86
  instructions:
@@ -117,6 +118,10 @@ export default server(
117
118
  tool('list_org_apps', {
118
119
  title: 'List organization apps',
119
120
  description: 'List NoodleSeed.com apps for an organization from the dev app API.',
121
+ authorization: {
122
+ requiredScopes: ['org_apps:read'],
123
+ allowedRoles: ['org_admin', 'org_member'],
124
+ },
120
125
  input: z.object({
121
126
  org_id: z.string().meta({ title: 'Organization' }),
122
127
  skip: z.number().int().min(0).optional().meta({ title: 'Starting item' }),
@@ -29,6 +29,17 @@ describe('customer-auth example', () => {
29
29
  projectId: '${env.FIREBASE_PROJECT_ID}',
30
30
  apiKey: '${env.FIREBASE_WEB_API_KEY}',
31
31
  authDomain: '${env.FIREBASE_AUTH_DOMAIN}',
32
+ user: {
33
+ roles: 'claims.roles',
34
+ scopes: 'claims.scopes',
35
+ },
36
+ });
37
+ expect(manifest.tools.find((tool) => tool.name === 'list_org_apps')?.authorization).toEqual({
38
+ requiredScopes: ['org_apps:read'],
39
+ allowedRoles: ['org_admin', 'org_member'],
32
40
  });
41
+ expect(
42
+ manifest.tools.find((tool) => tool.name === 'list_my_organizations')?.authorization,
43
+ ).toBeUndefined();
33
44
  });
34
45
  });
@@ -14,8 +14,9 @@ test-first task, review, recovery, and final-verification loop.
14
14
  If that agent discovers a Noodle Seed product gap while working, the installed skill prepares a
15
15
  sanitized `noodle feedback` proposal, discovers current fields from `noodle commands --json`, runs
16
16
  `--dry-run --json` to inspect the exact normalized submission, diagnostics, and private destination,
17
- then shows a POSIX-safely quoted live command. It submits once without `--dry-run` only after explicit
18
- user approval of that exact preview; it never auto-logs in or retry-loops.
17
+ and includes its known `--agent` and `--model` identity without guessing unavailable values. It then
18
+ shows a POSIX-safely quoted live command and submits once without `--dry-run` only after explicit user
19
+ approval of that exact preview; it never auto-logs in or retry-loops.
19
20
  Every `--json` command writes its canonical success or failure envelope to stdout and leaves stderr
20
21
  empty. One-shot commands write one envelope; streaming commands write NDJSON snapshot, event, and
21
22
  terminal-failure envelopes so agents can parse each line independently.
@@ -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.49.0 hash:6a9f132ddb79352e -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:efffbf82007f935d -->
6
+ <!-- noodle-skill version:0.51.0 hash:efffbf82007f935d -->
7
7
 
8
8
  # publishing-mcp-integrations
9
9
 
@@ -8,6 +8,7 @@
8
8
  - Connectors
9
9
  - HTTP connector example (full server)
10
10
  - Customer OAuth for remote MCP clients
11
+ - Per-tool authorization
11
12
  - Delegated downstream auth (call your API as the signed-in user)
12
13
  - Invocation context
13
14
  - Compute connector example
@@ -108,6 +109,28 @@ For `customerAuth.oidc(...)` and `.federatedOidc(...)`, the application develope
108
109
 
109
110
  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.
110
111
 
112
+ ## Per-tool authorization
113
+
114
+ Keep endpoint authentication in `customerAuth.*(...)`, then narrow individual tools with the optional typed `authorization` rule. Every `requiredScopes` value is required; any one `allowedRoles` value is sufficient; when both lists are present, both conditions apply. Omit `authorization` for an unrestricted tool. Do not invent a policy expression language or infer authorization from tool arguments, page context, connector output, email domains, or other unverified data.
115
+
116
+ ```ts
117
+ auth: customerAuth.oidc({
118
+ issuer: 'https://id.example.com',
119
+ audience: 'https://api.example.com/mcp',
120
+ claims: { roles: 'permissions.roles', scopes: 'permissions.scopes' },
121
+ }),
122
+
123
+ tool('list_org_apps', {
124
+ authorization: {
125
+ requiredScopes: ['org_apps:read'],
126
+ allowedRoles: ['org_admin', 'org_member'],
127
+ },
128
+ // input, output, and fulfilment...
129
+ })
130
+ ```
131
+
132
+ Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Eligible tools remain in authored order in `tools/list`; ineligible tools are omitted and a guessed direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
133
+
111
134
  ## Delegated downstream auth (call your API as the signed-in user)
112
135
 
113
136
  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:
@@ -156,7 +156,12 @@ export async function POST(request: Request) {
156
156
  clientId: process.env.NOODLE_ASSISTANT_CLIENT_ID!,
157
157
  clientSecret: process.env.NOODLE_ASSISTANT_CLIENT_SECRET!,
158
158
  origin: process.env.PUBLIC_APP_ORIGIN!,
159
- user: { id: user.id, email: user.email, roles: user.roles },
159
+ user: {
160
+ id: user.id,
161
+ email: user.email,
162
+ roles: user.roles,
163
+ scopes: user.scopes,
164
+ },
160
165
  context,
161
166
  // Saved, backend-verified user preferences outrank browser hints.
162
167
  preferences: { locale: user.locale, timeZone: user.timeZone },
@@ -165,7 +170,7 @@ export async function POST(request: Request) {
165
170
  }
166
171
  ```
167
172
 
168
- Authenticate before exchange. Source `origin` from trusted server configuration or strictly match the request origin against the same exact allowlist; never accept an arbitrary request header. Treat page context as untrusted model context, never authorization. Forward the helper response unchanged.
173
+ Authenticate before exchange. Pass backend-verified `user.roles` and OAuth-style `user.scopes` separately; they govern the same per-tool authorization rules as verified MCP bearer claims. Source `origin` from trusted server configuration or strictly match the request origin against the same exact allowlist; never accept an arbitrary request header. Treat page context as untrusted model context, never authorization. Forward the helper response unchanged.
169
174
 
170
175
  `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.
171
176
 
@@ -206,7 +211,13 @@ The embedding developer defines what authenticated session context the assistant
206
211
  ```ts
207
212
  const session = await createAssistantSession({
208
213
  serviceUrl, clientId, clientSecret, origin,
209
- user: { id: user.id, email: user.email, name: user.name },
214
+ user: {
215
+ id: user.id,
216
+ email: user.email,
217
+ name: user.name,
218
+ roles: user.roles,
219
+ scopes: user.scopes,
220
+ },
210
221
  claims: { displayName: user.name, accountTier: account.tier, region: account.region },
211
222
  });
212
223
  ```
@@ -27,7 +27,7 @@ Do not batch several findings into one proposal, and do not re-propose the same
27
27
  ## Approval workflow
28
28
 
29
29
  1. Discover the current positional arguments, flags, choices, defaults, and limits from `noodle commands --json`; `noodle feedback --help` is the human-readable view. Do not guess or rely on a remembered catalog.
30
- 2. Draft one finding, then sanitize its title and message using the rules below.
30
+ 2. Draft one finding, then sanitize its title and message using the rules below. When your coding-agent name is known, add `--agent`; add `--model` only when the exact model identifier is also known. These fields are client-reported provenance: never guess either value.
31
31
  3. Run the proposal with `--dry-run --json`. This local preview needs no login and sends nothing. Parse `{"ok":true,"data":{"mode":"preview","willSubmit":false,"destination":"Noodle Seed private feedback tracker","submission":{...}}}`.
32
32
  4. Inspect the complete `submission`, including its normalized defaults and automatically attached diagnostics. Show the user the exact previewed proposal, its `destination`, and a POSIX-safely quoted live command containing the same fields but without `--dry-run`.
33
33
  5. Ask for explicit approval of that exact previewed proposal. If the user changes any field, preview the changed proposal again before asking.
@@ -38,16 +38,19 @@ Do not batch several findings into one proposal, and do not re-propose the same
38
38
  ```sh
39
39
  noodle feedback 'resources list --json omits the truncated flag the docs promise' \
40
40
  --title 'resources list --json missing truncated flag' \
41
- --type fix --severity P2 --area cli --dry-run --json
41
+ --type fix --severity P2 --area cli --agent 'coding-agent' --model 'model-id' \
42
+ --dry-run --json
42
43
  ```
43
44
 
44
- This is a preview example only. Build the exact command for the finding using current `noodle commands --json` metadata, POSIX-quote every user-controlled value, and inspect the returned submission instead of reconstructing it. The message is required (1–4000 chars). The CLI attaches only the disclosed light diagnostics automatically: CLI version, OS/platform, Node version. Nothing else is collected. After approval, the live success envelope is `{ok:true,data:{reference,labels}}`; a `429` means the per-user hourly budget (5) is spent — report that it was not sent and never retry-loop.
45
+ This is a preview example only: replace `coding-agent` and `model-id` with your known coding-agent identity, or omit both when unavailable. Build the exact command for the finding using current `noodle commands --json` metadata, POSIX-quote every user-controlled value, and inspect the returned submission instead of reconstructing it. The message is required (1–4000 chars). The CLI attaches only the disclosed light diagnostics automatically: CLI version, OS/platform, Node version. Agent/model provenance is included only through the explicit client-reported flags. Nothing else is collected. After approval, the live success envelope is `{ok:true,data:{reference,labels}}`; a `429` means the per-user hourly budget (5) is spent — report that it was not sent and never retry-loop.
45
46
 
46
47
  ## Choose the structured fields
47
48
 
48
49
  - `--type` — `fix` (bug/regression/wrong output), `feat` (missing capability), `docs` (misleading or absent docs/examples), `chore` (tooling/setup friction). Default `feat`.
49
50
  - `--severity` — `P0` only for a security-relevant defect; `P1` a workflow is blocked with no workaround; `P2` blocked but a workaround exists; `P3` (default) papercut or idea.
50
51
  - `--area` — one of `docs analytics connectors self-service conformance ci deploys distribution console dx plugins cli compiler multi-surface enterprise policy`. Use `cli` for command behavior, `dx` for authoring/agent ergonomics; omit when unsure.
52
+ - `--agent` — your known coding-agent product name (1–64 chars). Omit when unavailable; the CLI does not auto-detect it.
53
+ - `--model` — the exact known model identifier (1–64 chars). Requires `--agent`; omit rather than guessing.
51
54
  - `--title` — one line, ≤120 chars, stating the defect or idea (defaults to the message’s first line).
52
55
 
53
56
  ## Sanitization rules (hard requirements)
@@ -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.49.0 hash:0f404109f4845683 -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:6ef6ef551e26b78e -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:cd6ca0d915e6acb9 -->
6
+ <!-- noodle-skill version:0.51.0 hash:cd6ca0d915e6acb9 -->
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.49.0 hash:0b2fd8c7e43fc69f -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:f7fa54992c8d7692 -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:1e86b8704f407bd3 -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:aa715bae12041d7c -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:93e735b7ffb45df1 -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:76cce86729cffbee -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:cc54a67f21c0ecdb -->
6
+ <!-- noodle-skill version:0.51.0 hash:cc54a67f21c0ecdb -->
7
7
 
8
8
  # embedding-mcp-assistants
9
9
 
@@ -15,7 +15,9 @@ surface for org discovery:
15
15
 
16
16
  - `list_my_organizations` lists the NoodleSeed.com organizations the signed-in customer belongs to (no
17
17
  arguments — the org set comes from the verified customer session).
18
- - `list_org_apps` lists apps for one of those organizations through the dev app API.
18
+ - `list_org_apps` lists apps for one of those organizations through the dev app API. It is visible and
19
+ callable only when the verified customer has the `org_apps:read` scope and either the `org_admin` or
20
+ `org_member` role.
19
21
 
20
22
  The two tools chain: `list_my_organizations` surfaces the `org_id`s the customer can act on, and
21
23
  `list_org_apps` takes one of those `org_id`s. There is no NoodleSeed-specific SDK helper. The downstream API
@@ -74,9 +76,10 @@ auth: customerAuth.firebase({
74
76
  email: 'email',
75
77
  name: 'name',
76
78
  tenant: 'firebase.tenant',
77
- orgs: 'claims.orgs',
78
- roles: 'claims.roles',
79
- },
79
+ orgs: 'claims.orgs',
80
+ roles: 'claims.roles',
81
+ scopes: 'claims.scopes',
82
+ },
80
83
  }),
81
84
  ```
82
85
 
@@ -92,6 +95,24 @@ auth: {
92
95
  },
93
96
  ```
94
97
 
98
+ The mapped `roles` and `scopes` paths are read only after Firebase verifies the ID token. The restricted
99
+ tool declares its rule beside the rest of its public contract:
100
+
101
+ ```ts
102
+ tool('list_org_apps', {
103
+ authorization: {
104
+ requiredScopes: ['org_apps:read'],
105
+ allowedRoles: ['org_admin', 'org_member'],
106
+ },
107
+ // input, output, and fulfilment...
108
+ });
109
+ ```
110
+
111
+ Every required scope must be present and at least one allowed role must match. When both lists are declared,
112
+ both conditions apply. A restricted tool is omitted from `tools/list` for an ineligible customer and a
113
+ direct `tools/call` still fails closed. Do not use unverified page context, request arguments, connector
114
+ responses, or an arbitrary generic `roles` claim as authorization input.
115
+
95
116
  Tool code calls the connector normally:
96
117
 
97
118
  ```ts
@@ -80,6 +80,7 @@ export default server(
80
80
  tenant: 'firebase.tenant',
81
81
  orgs: 'claims.orgs',
82
82
  roles: 'claims.roles',
83
+ scopes: 'claims.scopes',
83
84
  },
84
85
  }),
85
86
  instructions:
@@ -117,6 +118,10 @@ export default server(
117
118
  tool('list_org_apps', {
118
119
  title: 'List organization apps',
119
120
  description: 'List NoodleSeed.com apps for an organization from the dev app API.',
121
+ authorization: {
122
+ requiredScopes: ['org_apps:read'],
123
+ allowedRoles: ['org_admin', 'org_member'],
124
+ },
120
125
  input: z.object({
121
126
  org_id: z.string().meta({ title: 'Organization' }),
122
127
  skip: z.number().int().min(0).optional().meta({ title: 'Starting item' }),
@@ -29,6 +29,17 @@ describe('customer-auth example', () => {
29
29
  projectId: '${env.FIREBASE_PROJECT_ID}',
30
30
  apiKey: '${env.FIREBASE_WEB_API_KEY}',
31
31
  authDomain: '${env.FIREBASE_AUTH_DOMAIN}',
32
+ user: {
33
+ roles: 'claims.roles',
34
+ scopes: 'claims.scopes',
35
+ },
36
+ });
37
+ expect(manifest.tools.find((tool) => tool.name === 'list_org_apps')?.authorization).toEqual({
38
+ requiredScopes: ['org_apps:read'],
39
+ allowedRoles: ['org_admin', 'org_member'],
32
40
  });
41
+ expect(
42
+ manifest.tools.find((tool) => tool.name === 'list_my_organizations')?.authorization,
43
+ ).toBeUndefined();
33
44
  });
34
45
  });
@@ -14,8 +14,9 @@ test-first task, review, recovery, and final-verification loop.
14
14
  If that agent discovers a Noodle Seed product gap while working, the installed skill prepares a
15
15
  sanitized `noodle feedback` proposal, discovers current fields from `noodle commands --json`, runs
16
16
  `--dry-run --json` to inspect the exact normalized submission, diagnostics, and private destination,
17
- then shows a POSIX-safely quoted live command. It submits once without `--dry-run` only after explicit
18
- user approval of that exact preview; it never auto-logs in or retry-loops.
17
+ and includes its known `--agent` and `--model` identity without guessing unavailable values. It then
18
+ shows a POSIX-safely quoted live command and submits once without `--dry-run` only after explicit user
19
+ approval of that exact preview; it never auto-logs in or retry-loops.
19
20
  Every `--json` command writes its canonical success or failure envelope to stdout and leaves stderr
20
21
  empty. One-shot commands write one envelope; streaming commands write NDJSON snapshot, event, and
21
22
  terminal-failure envelopes so agents can parse each line independently.
@@ -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.49.0 hash:6a9f132ddb79352e -->
6
+ <!-- noodle-skill version:0.51.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.49.0 hash:efffbf82007f935d -->
6
+ <!-- noodle-skill version:0.51.0 hash:efffbf82007f935d -->
7
7
 
8
8
  # publishing-mcp-integrations
9
9
 
@@ -8,6 +8,7 @@
8
8
  - Connectors
9
9
  - HTTP connector example (full server)
10
10
  - Customer OAuth for remote MCP clients
11
+ - Per-tool authorization
11
12
  - Delegated downstream auth (call your API as the signed-in user)
12
13
  - Invocation context
13
14
  - Compute connector example
@@ -108,6 +109,28 @@ For `customerAuth.oidc(...)` and `.federatedOidc(...)`, the application develope
108
109
 
109
110
  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.
110
111
 
112
+ ## Per-tool authorization
113
+
114
+ Keep endpoint authentication in `customerAuth.*(...)`, then narrow individual tools with the optional typed `authorization` rule. Every `requiredScopes` value is required; any one `allowedRoles` value is sufficient; when both lists are present, both conditions apply. Omit `authorization` for an unrestricted tool. Do not invent a policy expression language or infer authorization from tool arguments, page context, connector output, email domains, or other unverified data.
115
+
116
+ ```ts
117
+ auth: customerAuth.oidc({
118
+ issuer: 'https://id.example.com',
119
+ audience: 'https://api.example.com/mcp',
120
+ claims: { roles: 'permissions.roles', scopes: 'permissions.scopes' },
121
+ }),
122
+
123
+ tool('list_org_apps', {
124
+ authorization: {
125
+ requiredScopes: ['org_apps:read'],
126
+ allowedRoles: ['org_admin', 'org_member'],
127
+ },
128
+ // input, output, and fulfilment...
129
+ })
130
+ ```
131
+
132
+ Role values are trusted only from the explicitly configured claim path (or the platform-private bridge role claim). Direct OIDC scopes default to standard `scope`, `scp`, or `scopes` claims unless `claims.scopes` is configured. Embedded-assistant backends pass verified `user.roles` and `user.scopes` separately during `createAssistantSession(...)`; page context never grants either. Claim values must be a string or string array; malformed or oversized values fail closed. Eligible tools remain in authored order in `tools/list`; ineligible tools are omitted and a guessed direct call is still denied before argument validation or connector execution. Scope denials use MCP OAuth step-up metadata without disclosing role names.
133
+
111
134
  ## Delegated downstream auth (call your API as the signed-in user)
112
135
 
113
136
  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:
@@ -156,7 +156,12 @@ export async function POST(request: Request) {
156
156
  clientId: process.env.NOODLE_ASSISTANT_CLIENT_ID!,
157
157
  clientSecret: process.env.NOODLE_ASSISTANT_CLIENT_SECRET!,
158
158
  origin: process.env.PUBLIC_APP_ORIGIN!,
159
- user: { id: user.id, email: user.email, roles: user.roles },
159
+ user: {
160
+ id: user.id,
161
+ email: user.email,
162
+ roles: user.roles,
163
+ scopes: user.scopes,
164
+ },
160
165
  context,
161
166
  // Saved, backend-verified user preferences outrank browser hints.
162
167
  preferences: { locale: user.locale, timeZone: user.timeZone },
@@ -165,7 +170,7 @@ export async function POST(request: Request) {
165
170
  }
166
171
  ```
167
172
 
168
- Authenticate before exchange. Source `origin` from trusted server configuration or strictly match the request origin against the same exact allowlist; never accept an arbitrary request header. Treat page context as untrusted model context, never authorization. Forward the helper response unchanged.
173
+ Authenticate before exchange. Pass backend-verified `user.roles` and OAuth-style `user.scopes` separately; they govern the same per-tool authorization rules as verified MCP bearer claims. Source `origin` from trusted server configuration or strictly match the request origin against the same exact allowlist; never accept an arbitrary request header. Treat page context as untrusted model context, never authorization. Forward the helper response unchanged.
169
174
 
170
175
  `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.
171
176
 
@@ -206,7 +211,13 @@ The embedding developer defines what authenticated session context the assistant
206
211
  ```ts
207
212
  const session = await createAssistantSession({
208
213
  serviceUrl, clientId, clientSecret, origin,
209
- user: { id: user.id, email: user.email, name: user.name },
214
+ user: {
215
+ id: user.id,
216
+ email: user.email,
217
+ name: user.name,
218
+ roles: user.roles,
219
+ scopes: user.scopes,
220
+ },
210
221
  claims: { displayName: user.name, accountTier: account.tier, region: account.region },
211
222
  });
212
223
  ```