@noodleseed/one 0.165.0 → 0.167.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 (126) hide show
  1. package/dist/commands/author-loop.d.ts.map +1 -1
  2. package/dist/commands/author-loop.js +18 -3
  3. package/dist/commands/author-loop.js.map +1 -1
  4. package/dist/commands/deploy-ops.d.ts.map +1 -1
  5. package/dist/commands/deploy-ops.js +21 -2
  6. package/dist/commands/deploy-ops.js.map +1 -1
  7. package/dist/commands/deploy-status-ops.d.ts +2 -0
  8. package/dist/commands/deploy-status-ops.d.ts.map +1 -1
  9. package/dist/commands/deploy-status-ops.js +27 -0
  10. package/dist/commands/deploy-status-ops.js.map +1 -1
  11. package/dist/control-plane-request.d.ts +2 -0
  12. package/dist/control-plane-request.d.ts.map +1 -1
  13. package/dist/control-plane-request.js +18 -0
  14. package/dist/control-plane-request.js.map +1 -1
  15. package/dist/deploy.d.ts +2 -1
  16. package/dist/deploy.d.ts.map +1 -1
  17. package/dist/deploy.js +15 -0
  18. package/dist/deploy.js.map +1 -1
  19. package/dist/dev.d.ts +2 -0
  20. package/dist/dev.d.ts.map +1 -1
  21. package/dist/dev.js +12 -1
  22. package/dist/dev.js.map +1 -1
  23. package/dist/devtools-auth-session.d.ts +5 -0
  24. package/dist/devtools-auth-session.d.ts.map +1 -1
  25. package/dist/devtools-auth-session.js +41 -2
  26. package/dist/devtools-auth-session.js.map +1 -1
  27. package/dist/devtools-auth-types.d.ts +2 -1
  28. package/dist/devtools-auth-types.d.ts.map +1 -1
  29. package/dist/devtools-auth-types.js.map +1 -1
  30. package/dist/devtools-auth-ui.d.ts +1 -1
  31. package/dist/devtools-auth-ui.d.ts.map +1 -1
  32. package/dist/devtools-auth-ui.js +13 -5
  33. package/dist/devtools-auth-ui.js.map +1 -1
  34. package/dist/devtools-harness.d.ts +1 -0
  35. package/dist/devtools-harness.d.ts.map +1 -1
  36. package/dist/devtools-harness.js +12 -7
  37. package/dist/devtools-harness.js.map +1 -1
  38. package/dist/devtools-preview.d.ts +1 -0
  39. package/dist/devtools-preview.d.ts.map +1 -1
  40. package/dist/devtools-preview.js +21 -6
  41. package/dist/devtools-preview.js.map +1 -1
  42. package/dist/devtools-rpc-forwarder.d.ts +1 -0
  43. package/dist/devtools-rpc-forwarder.d.ts.map +1 -1
  44. package/dist/devtools-rpc-forwarder.js +2 -1
  45. package/dist/devtools-rpc-forwarder.js.map +1 -1
  46. package/dist/preview-session.d.ts +1 -0
  47. package/dist/preview-session.d.ts.map +1 -1
  48. package/dist/preview-session.js +1 -0
  49. package/dist/preview-session.js.map +1 -1
  50. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +3 -3
  51. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-auth-ref.js +5 -2
  52. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  53. package/node_modules/@noodle-borg/agent-packaging/dist/product-skill-types.d.ts +1 -0
  54. package/node_modules/@noodle-borg/agent-packaging/dist/product-skill-validation.js +4 -0
  55. package/node_modules/@noodle-borg/app-package/dist/schema.js +1 -0
  56. package/node_modules/@noodle-borg/app-package/dist/types.d.ts +1 -0
  57. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +3 -1
  58. package/node_modules/@noodle-borg/authoring/dist/server.js +7 -6
  59. package/node_modules/@noodle-borg/capabilities/dist/index.js +7 -0
  60. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-local-authoring.js +8 -0
  61. package/node_modules/@noodle-borg/compiler/dist/app-package/compile.js +9 -1
  62. package/node_modules/@noodle-borg/compiler/dist/artifact/catalog-emission.js +3 -0
  63. package/node_modules/@noodle-borg/compiler/dist/artifact/types.d.ts +2 -0
  64. package/node_modules/@noodle-borg/compiler/dist/artifact/version.d.ts +2 -1
  65. package/node_modules/@noodle-borg/compiler/dist/artifact/version.js +2 -1
  66. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +5 -0
  67. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +12 -2
  68. package/node_modules/@noodle-borg/module/dist/customer-auth-access.d.ts +7 -0
  69. package/node_modules/@noodle-borg/module/dist/customer-auth-access.js +6 -0
  70. package/node_modules/@noodle-borg/module/dist/index.d.ts +1 -0
  71. package/node_modules/@noodle-borg/module/dist/index.js +1 -0
  72. package/node_modules/@noodle-borg/protocol/dist/handlers/tools.js +3 -0
  73. package/node_modules/@noodle-borg/protocol/dist/index.d.ts +1 -0
  74. package/node_modules/@noodle-borg/protocol/dist/index.js +1 -0
  75. package/node_modules/@noodle-borg/protocol/dist/mapping.d.ts +5 -1
  76. package/node_modules/@noodle-borg/protocol/dist/mapping.js +15 -7
  77. package/node_modules/@noodle-borg/protocol/dist/sdk-server.d.ts +2 -0
  78. package/node_modules/@noodle-borg/protocol/dist/tool-discovery.d.ts +14 -0
  79. package/node_modules/@noodle-borg/protocol/dist/tool-discovery.js +12 -0
  80. package/node_modules/@noodle-borg/protocol/dist/v2/server.js +3 -0
  81. package/node_modules/@noodle-borg/service/dist/build-info.js +4 -2
  82. package/node_modules/@noodle-borg/service/dist/customer-auth-audience-binding.js +16 -5
  83. package/node_modules/@noodle-borg/service/dist/deployment-access-mode.js +24 -0
  84. package/node_modules/@noodle-borg/service/dist/deployment-activation-precondition.js +27 -0
  85. package/node_modules/@noodle-borg/service/dist/deployment-authentication.js +19 -0
  86. package/node_modules/@noodle-borg/service/dist/deployment-execution.js +2 -2
  87. package/node_modules/@noodle-borg/service/dist/deployment-record-version.js +55 -0
  88. package/node_modules/@noodle-borg/service/dist/developer-mcp/inspection.js +15 -1
  89. package/node_modules/@noodle-borg/service/dist/registry-access.js +47 -30
  90. package/node_modules/@noodle-borg/service/dist/registry-compile.js +42 -1
  91. package/node_modules/@noodle-borg/service/dist/registry-deploy-transaction.js +105 -15
  92. package/node_modules/@noodle-borg/service/dist/registry-rollback.js +15 -1
  93. package/node_modules/@noodle-borg/service/dist/registry-state.d.ts +3 -6
  94. package/node_modules/@noodle-borg/service/dist/registry-state.js +15 -7
  95. package/node_modules/@noodle-borg/service/dist/registry-status.js +5 -3
  96. package/node_modules/@noodle-borg/service/dist/registry-targets.js +43 -28
  97. package/node_modules/@noodle-borg/service/dist/registry-types.d.ts +4 -2
  98. package/node_modules/@noodle-borg/service/dist/registry.js +68 -72
  99. package/node_modules/@noodle-borg/service/dist/routes/access.js +7 -0
  100. package/node_modules/@noodle-borg/service/dist/routes/auth-doctor-live.js +3 -2
  101. package/node_modules/@noodle-borg/service/dist/routes/control-plane.js +1 -0
  102. package/node_modules/@noodle-borg/service/dist/routes/deploy-preflight.js +2 -2
  103. package/node_modules/@noodle-borg/service/dist/serve-resource-auth.js +1 -1
  104. package/node_modules/@noodle-borg/service/dist/store/in-memory.d.ts +3 -7
  105. package/node_modules/@noodle-borg/service/dist/store/in-memory.js +13 -4
  106. package/node_modules/@noodle-borg/service/dist/store/json-file.d.ts +3 -7
  107. package/node_modules/@noodle-borg/service/dist/store/json-file.js +15 -6
  108. package/node_modules/@noodle-borg/service/dist/store/records.js +15 -2
  109. package/node_modules/@noodle-borg/service/dist/store.d.ts +23 -8
  110. package/node_modules/@noodle-borg/service/package.json +1 -1
  111. package/node_modules/@noodle-borg/transport-http/dist/handler.d.ts +2 -3
  112. package/node_modules/@noodle-borg/transport-http/dist/handler.js +3 -4
  113. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.d.ts +3 -2
  114. package/node_modules/@noodle-borg/transport-http/dist/identity-authorization.js +23 -12
  115. package/node_modules/@noodle-borg/transport-http/dist/index.d.ts +1 -0
  116. package/node_modules/@noodle-borg/transport-http/dist/serve-request.js +3 -0
  117. package/node_modules/@noodle-borg/transport-http/dist/target-authentication.d.ts +15 -0
  118. package/node_modules/@noodle-borg/transport-http/dist/target-authentication.js +9 -0
  119. package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.d.ts +23 -0
  120. package/node_modules/@noodle-borg/wire-contracts/dist/control-plane.js +34 -4
  121. package/node_modules/@noodle-borg/wire-contracts/dist/index.d.ts +6 -0
  122. package/node_modules/@noodle-borg/wire-contracts/dist/index.js +4 -1
  123. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.d.ts +41 -0
  124. package/node_modules/@noodle-borg/wire-contracts/dist/service-info.js +26 -0
  125. package/package.json +2 -2
  126. package/node_modules/@noodle-borg/service/dist/routes/access-mode.js +0 -7
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  type ToolAuthorizationManifest = {
3
+ readonly discovery?: 'authorized' | 'public' | undefined;
3
4
  readonly requiredScopes?: readonly string[] | undefined;
4
5
  readonly allowedRoles?: readonly string[] | undefined;
5
6
  };
@@ -504,6 +505,7 @@ export declare const manifestV1Schema: z.ZodObject<{
504
505
  title: z.ZodOptional<z.ZodString>;
505
506
  description: z.ZodString;
506
507
  authorization: z.ZodOptional<z.ZodType<ToolAuthorizationManifest, unknown, z.core.$ZodTypeInternals<ToolAuthorizationManifest, unknown>>>;
508
+ securitySchemes: z.ZodOptional<z.ZodNever>;
507
509
  inputSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
508
510
  outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
509
511
  annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -1164,6 +1166,7 @@ export declare const manifestV2Schema: z.ZodObject<{
1164
1166
  title: z.ZodOptional<z.ZodString>;
1165
1167
  description: z.ZodString;
1166
1168
  authorization: z.ZodOptional<z.ZodType<ToolAuthorizationManifest, unknown, z.core.$ZodTypeInternals<ToolAuthorizationManifest, unknown>>>;
1169
+ securitySchemes: z.ZodOptional<z.ZodNever>;
1167
1170
  inputSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1168
1171
  outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1169
1172
  annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -1729,6 +1732,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1729
1732
  title: z.ZodOptional<z.ZodString>;
1730
1733
  description: z.ZodString;
1731
1734
  authorization: z.ZodOptional<z.ZodType<ToolAuthorizationManifest, unknown, z.core.$ZodTypeInternals<ToolAuthorizationManifest, unknown>>>;
1735
+ securitySchemes: z.ZodOptional<z.ZodNever>;
1732
1736
  inputSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
1733
1737
  outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
1734
1738
  annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -2388,6 +2392,7 @@ export declare const manifestSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
2388
2392
  title: z.ZodOptional<z.ZodString>;
2389
2393
  description: z.ZodString;
2390
2394
  authorization: z.ZodOptional<z.ZodType<ToolAuthorizationManifest, unknown, z.core.$ZodTypeInternals<ToolAuthorizationManifest, unknown>>>;
2395
+ securitySchemes: z.ZodOptional<z.ZodNever>;
2391
2396
  inputSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
2392
2397
  outputSchema: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
2393
2398
  annotations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -70,25 +70,35 @@ const roleSchema = z
70
70
  const authorizationListSchema = (item) => z.array(item).min(1).max(MAX_TOOL_AUTHORIZATION_VALUES);
71
71
  const requiredScopesSchema = authorizationListSchema(scopeTokenSchema);
72
72
  const allowedRolesSchema = authorizationListSchema(roleSchema);
73
- const toolAuthorizationSchema = z.union([
73
+ const toolAuthorizationSchema = z
74
+ .union([
74
75
  z
75
76
  .object({
77
+ discovery: z.enum(['authorized', 'public']).optional(),
76
78
  requiredScopes: requiredScopesSchema,
77
79
  allowedRoles: allowedRolesSchema.optional(),
78
80
  })
79
81
  .strict(),
80
82
  z
81
83
  .object({
84
+ discovery: z.enum(['authorized', 'public']).optional(),
82
85
  requiredScopes: requiredScopesSchema.optional(),
83
86
  allowedRoles: allowedRolesSchema,
84
87
  })
85
88
  .strict(),
86
- ]);
89
+ ])
90
+ .overwrite((authorization) => {
91
+ if (authorization.discovery !== 'authorized')
92
+ return authorization;
93
+ const { discovery: _discovery, ...normalized } = authorization;
94
+ return normalized;
95
+ });
87
96
  const toolSchema = z.object({
88
97
  name: nameSchema,
89
98
  title: z.string().trim().min(1).optional(),
90
99
  description: z.string().min(1),
91
100
  authorization: toolAuthorizationSchema.optional(),
101
+ securitySchemes: z.never().optional(),
92
102
  inputSchema: jsonSchemaSchema,
93
103
  outputSchema: jsonSchemaSchema.optional(),
94
104
  annotations: z.record(z.string(), z.unknown()).optional(),
@@ -0,0 +1,7 @@
1
+ import type { AccessMode } from './contract.js';
2
+ export declare function usesCustomerAuthentication(input: {
3
+ readonly schemaVersion: number;
4
+ readonly accessMode: AccessMode | undefined;
5
+ readonly hasServerAuth: boolean;
6
+ }): boolean;
7
+ //# sourceMappingURL=customer-auth-access.d.ts.map
@@ -0,0 +1,6 @@
1
+ export function usesCustomerAuthentication(input) {
2
+ if (input.accessMode === 'customers')
3
+ return true;
4
+ return input.accessMode === 'mixed' && input.schemaVersion === 2 && input.hasServerAuth;
5
+ }
6
+ //# sourceMappingURL=customer-auth-access.js.map
@@ -1,5 +1,6 @@
1
1
  export * from './asset-store.js';
2
2
  export * from './contract.js';
3
+ export * from './customer-auth-access.js';
3
4
  export * from './deployment-activation.js';
4
5
  export * from './deployment-automation.js';
5
6
  export * from './endpoint-url.js';
@@ -1,5 +1,6 @@
1
1
  export * from './asset-store.js';
2
2
  export * from './contract.js';
3
+ export * from './customer-auth-access.js';
3
4
  export * from './deployment-activation.js';
4
5
  export * from './deployment-automation.js';
5
6
  export * from './endpoint-url.js';
@@ -15,6 +15,9 @@ export function registerTools(server, artifact, deps, context) {
15
15
  server.setRequestHandler(ListToolsRequestSchema, async () => mapToolsList(artifact, context.caller, {
16
16
  knowledgeTools: await knowledgeToolsEnabled(artifact, deps),
17
17
  intentCapture: context.intentCapture?.enabled === true,
18
+ ...(context.toolAuthentication === undefined
19
+ ? {}
20
+ : { authentication: context.toolAuthentication }),
18
21
  }));
19
22
  server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
20
23
  const toolName = request.params.name;
@@ -8,6 +8,7 @@ export { buildMcpServer, type ProtocolRequestContext, type ProtocolToolDispatchC
8
8
  export { handleStatelessHttp } from './stateless.js';
9
9
  export { evaluateToolAuthorization, filterAuthorizedTools, TOOL_AUTHORIZATION_DENIED, type ToolAuthorizationCaller, type ToolAuthorizationDecision, type ToolAuthorizationRuleClass, toolAuthorizationRuleClass, toolAuthorizationRuleFingerprint, } from './tool-authorization.js';
10
10
  export { toolRequiresConfirmation } from './tool-confirmation.js';
11
+ export { filterDiscoverableTools, type ToolAuthenticationPolicy } from './tool-discovery.js';
11
12
  export { createDualEraMcpHandler, createPlatformDualEraMcpHandler, } from './v2/handler.js';
12
13
  export { LEGACY_MCP_PROTOCOL_VERSION, type McpProtocolEra, type McpProtocolMode, MODERN_MCP_PROTOCOL_VERSION, SERVED_MCP_PROTOCOL_VERSIONS, type ServedMcpProtocolVersion, } from './v2/versions.js';
13
14
  export { injectWidgetBridge } from './widget/inject.js';
@@ -7,6 +7,7 @@ export { buildMcpServer, } from './sdk-server.js';
7
7
  export { handleStatelessHttp } from './stateless.js';
8
8
  export { evaluateToolAuthorization, filterAuthorizedTools, TOOL_AUTHORIZATION_DENIED, toolAuthorizationRuleClass, toolAuthorizationRuleFingerprint, } from './tool-authorization.js';
9
9
  export { toolRequiresConfirmation } from './tool-confirmation.js';
10
+ export { filterDiscoverableTools } from './tool-discovery.js';
10
11
  export { createDualEraMcpHandler, createPlatformDualEraMcpHandler, } from './v2/handler.js';
11
12
  export { LEGACY_MCP_PROTOCOL_VERSION, MODERN_MCP_PROTOCOL_VERSION, SERVED_MCP_PROTOCOL_VERSIONS, } from './v2/versions.js';
12
13
  export { injectWidgetBridge } from './widget/inject.js';
@@ -1,6 +1,7 @@
1
1
  import type { ArtifactTool, RuntimeArtifact } from '@noodle-borg/compiler';
2
2
  import type { JsonRpcErrorObject } from './jsonrpc.js';
3
- import { type ToolAuthorizationCaller } from './tool-authorization.js';
3
+ import type { ToolAuthorizationCaller } from './tool-authorization.js';
4
+ import { type ToolAuthenticationPolicy, type ToolSecurityScheme } from './tool-discovery.js';
4
5
  import { type WidgetDomainProjection } from './widget/domain-projection.js';
5
6
  /**
6
7
  * Artifact → MCP wire mapping. The official `@modelcontextprotocol/sdk` owns JSON-RPC framing and
@@ -16,6 +17,7 @@ export interface ToolDescriptor {
16
17
  readonly inputSchema: Record<string, unknown>;
17
18
  readonly outputSchema?: Record<string, unknown>;
18
19
  readonly annotations?: Record<string, unknown>;
20
+ readonly securitySchemes?: readonly ToolSecurityScheme[];
19
21
  /** MCP `_meta` extension bag (e.g. MCP Apps `{ ui: { resourceUri } }` linking the tool to its widget). */
20
22
  readonly _meta?: Record<string, unknown>;
21
23
  }
@@ -44,6 +46,7 @@ export { mapExecutionError } from './execution-error-mapping.js';
44
46
  /** Map a resolved artifact tool to an MCP tool descriptor (carrying its `_meta` extension bag, if any). */
45
47
  export declare function mapTool(tool: ArtifactTool, options?: {
46
48
  readonly intentCapture?: boolean;
49
+ readonly authentication?: ToolAuthenticationPolicy;
47
50
  }): ToolDescriptor;
48
51
  /**
49
52
  * Map the artifact's tools to a `tools/list` result. App-only widget helpers remain discoverable here with
@@ -53,6 +56,7 @@ export declare function mapTool(tool: ArtifactTool, options?: {
53
56
  export declare function mapToolsList(artifact: RuntimeArtifact, caller?: ToolAuthorizationCaller, options?: {
54
57
  readonly knowledgeTools?: boolean;
55
58
  readonly intentCapture?: boolean;
59
+ readonly authentication?: ToolAuthenticationPolicy;
56
60
  }): ToolsListResult;
57
61
  /**
58
62
  * Map a successful tool output to a `tools/call` result. Structured (object) output is returned as
@@ -1,21 +1,27 @@
1
1
  import { splitResultMeta } from '@noodle-borg/runtime';
2
2
  import { artifactDeclaresContext, assertNoContextToolCollision, CONTEXT_TOOL_DESCRIPTOR, } from './context-tool.js';
3
3
  import { projectIntentCaptureInput } from './intent-capture.js';
4
- import { filterAuthorizedTools } from './tool-authorization.js';
4
+ import { filterDiscoverableTools, toolSecuritySchemes, } from './tool-discovery.js';
5
5
  import { projectWidgetResourceMeta, } from './widget/domain-projection.js';
6
6
  import { injectWidgetBridge } from './widget/inject.js';
7
7
  export { mapExecutionError } from './execution-error-mapping.js';
8
8
  /** Map a resolved artifact tool to an MCP tool descriptor (carrying its `_meta` extension bag, if any). */
9
9
  export function mapTool(tool, options) {
10
- return {
10
+ return withToolAuthentication({
11
11
  name: tool.name,
12
12
  ...(tool.title !== undefined ? { title: tool.title } : {}),
13
13
  description: tool.description,
14
14
  inputSchema: projectIntentCaptureInput(withPortableInteractionInput(tool), options?.intentCapture === true),
15
15
  ...(tool.outputSchema !== undefined ? { outputSchema: tool.outputSchema } : {}),
16
16
  ...(tool.annotations !== undefined ? { annotations: tool.annotations } : {}),
17
- ...(tool._meta !== undefined ? { _meta: tool._meta } : {}),
18
- };
17
+ ...(tool._meta === undefined ? {} : { _meta: tool._meta }),
18
+ }, tool, options?.authentication);
19
+ }
20
+ function withToolAuthentication(descriptor, tool, policy) {
21
+ if (policy !== 'mixed-customer')
22
+ return descriptor;
23
+ const securitySchemes = toolSecuritySchemes(tool);
24
+ return { ...descriptor, securitySchemes, _meta: { ...descriptor._meta, securitySchemes } };
19
25
  }
20
26
  const PORTABLE_INTERACTION_INPUT_SCHEMA = {
21
27
  type: 'object',
@@ -65,10 +71,12 @@ export function mapToolsList(artifact, caller, options) {
65
71
  assertNoContextToolCollision(artifact);
66
72
  return {
67
73
  tools: [
68
- ...filterAuthorizedTools(artifact.tools, caller).map((tool) => mapTool(tool, options)),
69
- ...(artifactDeclaresContext(artifact) ? [CONTEXT_TOOL_DESCRIPTOR] : []),
74
+ ...filterDiscoverableTools(artifact.tools, caller).map((tool) => mapTool(tool, options)),
75
+ ...(artifactDeclaresContext(artifact)
76
+ ? [withToolAuthentication(CONTEXT_TOOL_DESCRIPTOR, {}, options?.authentication)]
77
+ : []),
70
78
  ...(options?.knowledgeTools === true
71
- ? knowledgeToolDescriptors(artifact, options?.intentCapture === true)
79
+ ? knowledgeToolDescriptors(artifact, options?.intentCapture === true).map((descriptor) => withToolAuthentication(descriptor, {}, options?.authentication))
72
80
  : []),
73
81
  ],
74
82
  };
@@ -18,6 +18,8 @@ export interface ServedArtifact {
18
18
  readonly appPackageSnapshot?: AppPackageSnapshotV1;
19
19
  }
20
20
  export interface ProtocolRequestContext {
21
+ /** Resolved endpoint policy used only to derive MCP discovery metadata. */
22
+ readonly toolAuthentication?: import('./tool-discovery.js').ToolAuthenticationPolicy;
21
23
  readonly caller?: CallerIdentity;
22
24
  /** Internal hosting attribution, never MCP payload metadata or caller-visible context. */
23
25
  readonly publicAdmission?: NonNullable<ExecuteDeps['publicAdmission']>;
@@ -0,0 +1,14 @@
1
+ import type { ArtifactTool } from '@noodle-borg/compiler';
2
+ import { type ToolAuthorizationCaller } from './tool-authorization.js';
3
+ /** The hosting plane opts into schemes only for the resolved mixed customer policy. */
4
+ export type ToolAuthenticationPolicy = 'mixed-customer';
5
+ export type ToolSecurityScheme = {
6
+ readonly type: 'noauth';
7
+ } | {
8
+ readonly type: 'oauth2';
9
+ readonly scopes: readonly string[];
10
+ };
11
+ /** Descriptor publication only; execution and non-MCP capability filters remain authorization-only. */
12
+ export declare function filterDiscoverableTools(tools: readonly ArtifactTool[], caller: ToolAuthorizationCaller | undefined): readonly ArtifactTool[];
13
+ export declare function toolSecuritySchemes(tool: Pick<ArtifactTool, 'authorization'>): readonly ToolSecurityScheme[];
14
+ //# sourceMappingURL=tool-discovery.d.ts.map
@@ -0,0 +1,12 @@
1
+ import { evaluateToolAuthorization } from './tool-authorization.js';
2
+ /** Descriptor publication only; execution and non-MCP capability filters remain authorization-only. */
3
+ export function filterDiscoverableTools(tools, caller) {
4
+ return tools.filter((tool) => tool.authorization?.discovery === 'public' ||
5
+ evaluateToolAuthorization(tool.authorization, caller).allow);
6
+ }
7
+ export function toolSecuritySchemes(tool) {
8
+ return tool.authorization === undefined
9
+ ? [{ type: 'noauth' }]
10
+ : [{ type: 'oauth2', scopes: tool.authorization.requiredScopes ?? [] }];
11
+ }
12
+ //# sourceMappingURL=tool-discovery.js.map
@@ -93,6 +93,9 @@ function registerV2Tools(server, artifact, deps, context, era) {
93
93
  const mapped = mapToolsList(artifact, context.caller, {
94
94
  knowledgeTools: await knowledgeToolsEnabled(artifact, deps),
95
95
  intentCapture: context.intentCapture?.enabled === true,
96
+ ...(context.toolAuthentication === undefined
97
+ ? {}
98
+ : { authentication: context.toolAuthentication }),
96
99
  });
97
100
  return (era === 'modern' ? shapeModernToolsList(mapped) : mapped);
98
101
  });
@@ -1,12 +1,14 @@
1
+ import { MIXED_CUSTOMER_AUTH_FEATURE_VERSION, serviceInfoResponseSchema, } from '@noodle-borg/wire-contracts';
1
2
  export function serviceInfoPayload(buildInfo, developerMcp) {
2
- return {
3
+ return serviceInfoResponseSchema.parse({
3
4
  ok: true,
4
5
  status: 'ok',
5
6
  ...buildInfo,
7
+ features: { mixedCustomerAuth: MIXED_CUSTOMER_AUTH_FEATURE_VERSION },
6
8
  ...(developerMcp
7
9
  ? { developerPlugin: { mcpCapabilityVersion: DEVELOPER_MCP_CAPABILITY_VERSION } }
8
10
  : {}),
9
- };
11
+ });
10
12
  }
11
13
  export function cliCompatibilityError(buildInfo, cliVersion) {
12
14
  const supported = buildInfo.compatiblePackageVersions?.['@noodleseed/one'];
@@ -1,3 +1,12 @@
1
+ import { usesCustomerAuthentication } from '@noodle-borg/module';
2
+ /** Match durable ownership, including an omitted stored projection when compilation declares auth. */
3
+ export function requiresCustomerAuthProjection(record, compiledAuth) {
4
+ return usesCustomerAuthentication({
5
+ schemaVersion: record.schemaVersion,
6
+ accessMode: record.accessMode,
7
+ hasServerAuth: record.serverAuth != null || compiledAuth !== undefined,
8
+ });
9
+ }
1
10
  /** Safe lifecycle error; tenant identities and configured values never enter its public message. */
2
11
  export class CustomerAuthAudienceConflictError extends Error {
3
12
  code = 'customer_auth_audience_conflict';
@@ -51,7 +60,7 @@ export function isTenantAuthConfig(value) {
51
60
  }
52
61
  /** Customer-serving records must exactly project the compiler-authoritative auth declaration. */
53
62
  export function hasExactCustomerAuthProjection(record, compiledAuth) {
54
- if (record.accessMode !== 'customers')
63
+ if (!requiresCustomerAuthProjection(record, compiledAuth))
55
64
  return true;
56
65
  if (compiledAuth === undefined || !isTenantAuthConfig(record.serverAuth))
57
66
  return false;
@@ -65,7 +74,7 @@ export function hasExactCustomerAuthProjection(record, compiledAuth) {
65
74
  }
66
75
  /** Require an exact compiler-authenticated snapshot before clearing customer app archive stamps. */
67
76
  export function assertCustomerAuthRestorePrecondition(records, precondition) {
68
- const candidates = records.filter((record) => record.active && record.accessMode === 'customers');
77
+ const candidates = records.filter((record) => record.active && requiresCustomerAuthProjection(record));
69
78
  const projections = precondition?.customerAuthProjections ?? [];
70
79
  if (candidates.length !== projections.length) {
71
80
  throw new CustomerAuthAudienceProjectionError();
@@ -107,7 +116,7 @@ export function findActiveCustomerAuthAudienceConflict(records, tenant, auth) {
107
116
  for (const record of records) {
108
117
  if (!record.active ||
109
118
  record.archivedAt !== undefined ||
110
- record.accessMode !== 'customers' ||
119
+ !requiresCustomerAuthProjection(record) ||
111
120
  sameTenant(record, tenant)) {
112
121
  continue;
113
122
  }
@@ -124,7 +133,9 @@ export function findActiveCustomerAuthAudienceConflict(records, tenant, auth) {
124
133
  export function assertUniqueActiveCustomerAuthAudienceBindings(records) {
125
134
  const owners = new Map();
126
135
  for (const record of records) {
127
- if (!record.active || record.archivedAt !== undefined || record.accessMode !== 'customers') {
136
+ if (!record.active ||
137
+ record.archivedAt !== undefined ||
138
+ !requiresCustomerAuthProjection(record)) {
128
139
  continue;
129
140
  }
130
141
  if (!isTenantAuthConfig(record.serverAuth)) {
@@ -143,7 +154,7 @@ export function assertUniqueActiveCustomerAuthAudienceBindings(records) {
143
154
  }
144
155
  /** Shared registry guard with an optimized durable-store path and an in-memory fallback. */
145
156
  export async function hasActiveCustomerAuthAudienceConflict(store, records, record, auth) {
146
- if (record.accessMode !== 'customers' || auth === undefined || auth.kind === 'bridge')
157
+ if (!requiresCustomerAuthProjection(record, auth) || auth === undefined || auth.kind === 'bridge')
147
158
  return false;
148
159
  const tenant = { org: record.orgSlug, app: record.appSlug, env: record.environment };
149
160
  const conflict = store?.findActiveCustomerAuthAudienceConflict !== undefined
@@ -0,0 +1,24 @@
1
+ import { parse as parseYaml } from 'yaml';
2
+ export function isIdentityAccessMode(accessMode, manifest) {
3
+ if (accessMode === 'mixed' && manifest !== undefined) {
4
+ try {
5
+ const parsed = parseYaml(manifest);
6
+ if (typeof parsed === 'object' && parsed !== null && 'server' in parsed) {
7
+ const server = parsed.server;
8
+ if (typeof server === 'object' &&
9
+ server !== null &&
10
+ 'auth' in server &&
11
+ server.auth != null)
12
+ return true;
13
+ }
14
+ }
15
+ catch {
16
+ /* Compilation owns malformed manifest diagnostics. */
17
+ }
18
+ }
19
+ return (accessMode === 'owner-only' ||
20
+ accessMode === 'org-members' ||
21
+ accessMode === 'customers' ||
22
+ accessMode === 'authenticated');
23
+ }
24
+ //# sourceMappingURL=deployment-access-mode.js.map
@@ -0,0 +1,27 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ /** Compare inside the lifecycle lock/transaction, before any projection or active-pointer writes. */
3
+ export function matchesDeploymentActivation(target, precondition) {
4
+ if (target.schemaVersion !== (precondition?.expectedSchemaVersion ?? 1))
5
+ return false;
6
+ if (precondition === undefined)
7
+ return true;
8
+ if (target.accessMode !== precondition.expectedAccessMode)
9
+ return false;
10
+ const expected = precondition.expectedRevision;
11
+ if (expected === undefined)
12
+ return true;
13
+ // These are all persisted compile inputs plus the authority and lifecycle state used by the target.
14
+ // Active status may legitimately change on repeated activation; it is not an executable input.
15
+ return [
16
+ 'manifest',
17
+ 'connectors',
18
+ 'hostedAssets',
19
+ 'serverAuth',
20
+ 'serverVersion',
21
+ 'ownerSubject',
22
+ 'createdBySubject',
23
+ 'orgMembershipSources',
24
+ 'archivedAt',
25
+ ].every((key) => isDeepStrictEqual(target[key], expected[key]));
26
+ }
27
+ //# sourceMappingURL=deployment-activation-precondition.js.map
@@ -0,0 +1,19 @@
1
+ import { usesCustomerAuthentication } from '@noodle-borg/module';
2
+ import { deploymentRecordVersionError } from './deployment-record-version.js';
3
+ /** Read the effective authentication authority only when this runtime understands the record. */
4
+ export function deploymentAuthenticationFor(record, compiledAuth) {
5
+ if (deploymentRecordVersionError(record) !== undefined || record.accessMode === undefined) {
6
+ return undefined;
7
+ }
8
+ if (record.accessMode === 'public')
9
+ return 'none';
10
+ if (usesCustomerAuthentication({
11
+ schemaVersion: record.schemaVersion,
12
+ accessMode: record.accessMode,
13
+ hasServerAuth: record.serverAuth !== undefined || compiledAuth !== undefined,
14
+ })) {
15
+ return 'customer';
16
+ }
17
+ return 'platform';
18
+ }
19
+ //# sourceMappingURL=deployment-authentication.js.map
@@ -1,8 +1,8 @@
1
1
  import { cspFaultsInManifest } from '@noodle-borg/compiler';
2
2
  import { noopLogger } from '@noodle-borg/transport-http';
3
3
  import { BUSINESS_SETUP_MESSAGE, BusinessOnboarding } from './business-onboarding.js';
4
+ import { isIdentityAccessMode } from './deployment-access-mode.js';
4
5
  import { manifestUsesUserRoot } from './registry-access.js';
5
- import { isIdentityAccessMode } from './routes/access-mode.js';
6
6
  import { provisionPublicEmbed } from './routes/deploy-public-embed.js';
7
7
  import { deploymentActivationRejection } from './routes/deployment-activation-response.js';
8
8
  /** Shared post-authorization deploy flow. Both API deploys and installations retain registry activation hooks. */
@@ -42,7 +42,7 @@ export async function executeAuthorizedDeployment(input, dependencies) {
42
42
  }
43
43
  // Identity access modes bind the data plane to verified identity, so the deployer must be authenticated.
44
44
  // (Localhost dev runs the gate open; such deploys are rejected for lack of an owner/audit actor.)
45
- if (isIdentityAccessMode(accessMode) && !identity) {
45
+ if (isIdentityAccessMode(accessMode, manifest) && !identity) {
46
46
  return reject(401, {
47
47
  error: `${accessMode} deployments require an authenticated deployer`,
48
48
  });
@@ -0,0 +1,55 @@
1
+ import { sameDeploymentScope } from './deployment-versioning.js';
2
+ const VERSION_ERROR = {
3
+ code: 'unsupported_deployment_record_version',
4
+ path: 'schemaVersion',
5
+ message: 'Deployment record version is not supported by this runtime.',
6
+ };
7
+ export class UnsupportedDeploymentRecordVersionError extends Error {
8
+ code = VERSION_ERROR.code;
9
+ constructor() {
10
+ super(VERSION_ERROR.message);
11
+ this.name = 'UnsupportedDeploymentRecordVersionError';
12
+ }
13
+ }
14
+ /** R1 serves the two understood policy versions; future policy remains unavailable. */
15
+ export function deploymentRecordVersionError(record) {
16
+ return record.schemaVersion === 1 || record.schemaVersion === 2 ? undefined : VERSION_ERROR;
17
+ }
18
+ /** A stale writer cannot replace or deactivate a newer policy, even after registry preflight. */
19
+ export function assertDeploymentAppendVersion(records, candidate) {
20
+ if (records.some((record) => record.schemaVersion > candidate.schemaVersion &&
21
+ (record.deploymentId === candidate.deploymentId ||
22
+ (candidate.active &&
23
+ record.active &&
24
+ record.archivedAt === undefined &&
25
+ sameDeploymentScope(record, candidate)))))
26
+ throw new UnsupportedDeploymentRecordVersionError();
27
+ }
28
+ export function unsupportedDeploymentRecordFailure() {
29
+ return { ok: false, errors: [VERSION_ERROR] };
30
+ }
31
+ /** Reject a deployment compiled against an operator policy that has since changed. */
32
+ export class DeploymentPolicyChangedError extends Error {
33
+ code = 'deployment_policy_changed';
34
+ constructor() {
35
+ super('The active deployment policy changed. Retry the deployment.');
36
+ }
37
+ }
38
+ export function assertDeploymentAppendPolicy(records, candidate, precondition) {
39
+ if (precondition === undefined)
40
+ return;
41
+ const current = records.find((record) => record.active && record.archivedAt === undefined && sameDeploymentScope(record, candidate));
42
+ const expected = precondition.active;
43
+ if (expected === null
44
+ ? current !== undefined
45
+ : current === undefined ||
46
+ current.deploymentId !== expected.deploymentId ||
47
+ current.schemaVersion !== expected.schemaVersion ||
48
+ current.accessMode !== expected.accessMode ||
49
+ current.ownerSubject !== expected.ownerSubject ||
50
+ current.manifest !== expected.manifest ||
51
+ JSON.stringify(current.serverAuth) !== JSON.stringify(expected.serverAuth)) {
52
+ throw new DeploymentPolicyChangedError();
53
+ }
54
+ }
55
+ //# sourceMappingURL=deployment-record-version.js.map
@@ -8,7 +8,21 @@ export async function inspectServiceDeployment(registry, tenant, baseUrl, endpoi
8
8
  return {
9
9
  ok: true,
10
10
  target: status.target,
11
- deployment: status.deployment,
11
+ // Keep the Developer MCP port projection explicit as status gains additive operator fields.
12
+ deployment: {
13
+ deploymentId: status.deployment.deploymentId,
14
+ endpointUrl: status.deployment.endpointUrl,
15
+ active: status.deployment.active,
16
+ serverName: status.deployment.serverName,
17
+ createdAt: status.deployment.createdAt,
18
+ ...(status.deployment.createdByEmail === undefined
19
+ ? {}
20
+ : { createdByEmail: status.deployment.createdByEmail }),
21
+ accessMode: status.deployment.accessMode,
22
+ ...(status.deployment.ownerSubject === undefined
23
+ ? {}
24
+ : { ownerSubject: status.deployment.ownerSubject }),
25
+ },
12
26
  health: {
13
27
  state: status.health.state,
14
28
  missingSecrets: status.config.missingSecrets,