@noodleseed/one 0.138.0 → 0.139.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 (70) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/import.d.ts +4 -0
  4. package/dist/commands/import.d.ts.map +1 -0
  5. package/dist/commands/import.js +198 -0
  6. package/dist/commands/import.js.map +1 -0
  7. package/dist/commands/project-setup.d.ts +1 -1
  8. package/dist/commands/project-setup.d.ts.map +1 -1
  9. package/dist/commands/project-setup.js +2 -48
  10. package/dist/commands/project-setup.js.map +1 -1
  11. package/dist/commands/service-ops.d.ts.map +1 -1
  12. package/dist/commands/service-ops.js +44 -51
  13. package/dist/commands/service-ops.js.map +1 -1
  14. package/dist/mcp-import.d.ts +22 -0
  15. package/dist/mcp-import.d.ts.map +1 -0
  16. package/dist/mcp-import.js +91 -0
  17. package/dist/mcp-import.js.map +1 -0
  18. package/dist/self-host-init.d.ts +23 -0
  19. package/dist/self-host-init.d.ts.map +1 -0
  20. package/dist/self-host-init.js +315 -0
  21. package/dist/self-host-init.js.map +1 -0
  22. package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +3 -3
  23. package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +11 -2
  24. package/node_modules/@noodle-borg/agent-kit/dist/skill-connect-refs.js +31 -1
  25. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js +3 -2
  26. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  27. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-guide.d.ts +1 -0
  28. package/node_modules/@noodle-borg/assistant-gateway/dist/assistant-guide.js +35 -1
  29. package/node_modules/@noodle-borg/assistant-gateway/package.json +1 -1
  30. package/node_modules/@noodle-borg/authoring/dist/connections.js +1 -0
  31. package/node_modules/@noodle-borg/authoring/dist/connectors.d.ts +25 -3
  32. package/node_modules/@noodle-borg/authoring/dist/connectors.js +82 -7
  33. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +1 -1
  34. package/node_modules/@noodle-borg/authoring/dist/model-tool-visibility.js +33 -0
  35. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +5 -0
  36. package/node_modules/@noodle-borg/authoring/dist/server.js +7 -2
  37. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-account.js +13 -1
  38. package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-bootstrap-authoring.js +30 -1
  39. package/node_modules/@noodle-borg/connector-defs/dist/call-graph.js +1 -1
  40. package/node_modules/@noodle-borg/connector-defs/dist/compile-mcp.js +95 -0
  41. package/node_modules/@noodle-borg/connector-defs/dist/compile.js +4 -0
  42. package/node_modules/@noodle-borg/connector-defs/dist/customer-routing.js +1 -1
  43. package/node_modules/@noodle-borg/connector-defs/dist/index.d.ts +1 -1
  44. package/node_modules/@noodle-borg/connector-defs/dist/schema.d.ts +642 -3
  45. package/node_modules/@noodle-borg/connector-defs/dist/schema.js +143 -1
  46. package/node_modules/@noodle-borg/connector-http/dist/index.d.ts +1 -0
  47. package/node_modules/@noodle-borg/connector-http/dist/index.js +1 -0
  48. package/node_modules/@noodle-borg/connector-http/dist/mcp/config.js +76 -0
  49. package/node_modules/@noodle-borg/connector-http/dist/mcp/egress.js +125 -0
  50. package/node_modules/@noodle-borg/connector-http/dist/mcp/failure.js +48 -0
  51. package/node_modules/@noodle-borg/connector-http/dist/mcp/index.d.ts +3 -0
  52. package/node_modules/@noodle-borg/connector-http/dist/mcp/index.js +2 -0
  53. package/node_modules/@noodle-borg/connector-http/dist/mcp/mcp-connector.d.ts +12 -0
  54. package/node_modules/@noodle-borg/connector-http/dist/mcp/mcp-connector.js +125 -0
  55. package/node_modules/@noodle-borg/connector-http/dist/mcp/result.js +55 -0
  56. package/node_modules/@noodle-borg/connector-http/dist/mcp/types.d.ts +52 -0
  57. package/node_modules/@noodle-borg/connector-http/dist/mcp/types.js +10 -0
  58. package/node_modules/@noodle-borg/connector-http/package.json +4 -1
  59. package/node_modules/@noodle-borg/openapi-import/dist/index.js +1 -0
  60. package/node_modules/@noodle-borg/openapi-import/dist/mcp/index.js +4 -0
  61. package/node_modules/@noodle-borg/openapi-import/dist/mcp/probe.js +165 -0
  62. package/node_modules/@noodle-borg/openapi-import/dist/mcp/render.js +99 -0
  63. package/node_modules/@noodle-borg/openapi-import/dist/mcp/snapshot.js +290 -0
  64. package/node_modules/@noodle-borg/openapi-import/package.json +6 -2
  65. package/node_modules/@noodle-borg/runtime/dist/connector/types.d.ts +2 -0
  66. package/node_modules/@noodle-borg/runtime/dist/operation-execution.js +1 -0
  67. package/node_modules/@noodle-borg/service/dist/routes/assistant-agent.js +8 -5
  68. package/node_modules/@noodle-borg/service/package.json +1 -1
  69. package/node_modules/@noodleseed/assistant/package.json +1 -1
  70. package/package.json +2 -2
@@ -40,7 +40,7 @@
40
40
  "@noodle-borg/module": "0.0.0",
41
41
  "@noodle-borg/protocol": "0.0.0",
42
42
  "@noodle-borg/runtime": "0.0.0",
43
- "@noodleseed/assistant": "1.23.0"
43
+ "@noodleseed/assistant": "1.24.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^24.13.1",
@@ -20,6 +20,7 @@ export function bind(connector, options) {
20
20
  : {}),
21
21
  ...(connector.definitions !== undefined ? { definitions: connector.definitions } : {}),
22
22
  ...(connector.httpDef !== undefined ? { httpDef: connector.httpDef } : {}),
23
+ ...(connector.mcpDef !== undefined ? { mcpDef: connector.mcpDef } : {}),
23
24
  binding: { profile: options.profile, connection: options.connection },
24
25
  };
25
26
  }
@@ -1,5 +1,5 @@
1
1
  import { type CustomerEndpointRef, type JsonSchema, type OperationSignature } from '@noodle-borg/compiler';
2
- import { type ConnectorFile, type CredentialProfileDef, type HttpConnectorDef } from '@noodle-borg/connector-defs';
2
+ import { type ConnectorFile, type CredentialProfileDef, type HttpConnectorDef, type McpConnectorDef } from '@noodle-borg/connector-defs';
3
3
  import { z } from 'zod';
4
4
  import { type ConfigRef } from './config.js';
5
5
  import type { ConnectionBinding } from './connection-types.js';
@@ -86,7 +86,24 @@ export type HttpConnectorOptions = Omit<HttpConnectorDef['http'], 'baseUrl' | 'a
86
86
  readonly credentialProfiles?: HttpConnectorDef['credentialProfiles'];
87
87
  readonly operations: Readonly<Record<string, HttpOperationOptionsWithRefs>>;
88
88
  };
89
- type DeclaredCredentialProfileKeys<Options extends HttpConnectorOptions> = Options extends {
89
+ export type McpAuthOptions = Extract<HttpAuthOptions, {
90
+ readonly kind: 'bearer' | 'apiKey' | 'clientCredentials';
91
+ }>;
92
+ type McpOperationOptionsWithRefs = Omit<McpConnectorDef['operations'][string], 'auth' | 'input' | 'output'> & {
93
+ readonly auth?: McpAuthOptions;
94
+ readonly input?: OperationIoOptions;
95
+ readonly output?: OperationIoOptions;
96
+ };
97
+ export type McpConnectorOptions = Omit<McpConnectorDef['mcp'], 'endpoint' | 'allowedOrigins' | 'auth'> & {
98
+ readonly endpoint: string | ConfigRef;
99
+ readonly allowedOrigins?: readonly (string | ConfigRef)[];
100
+ readonly auth?: McpAuthOptions;
101
+ readonly credentialProfiles?: McpConnectorDef['credentialProfiles'];
102
+ readonly operations: Readonly<Record<string, McpOperationOptionsWithRefs>>;
103
+ };
104
+ type DeclaredCredentialProfileKeys<Options extends {
105
+ readonly credentialProfiles?: unknown;
106
+ }> = Options extends {
90
107
  readonly credentialProfiles: infer Profiles;
91
108
  } ? Extract<keyof Profiles, string> : never;
92
109
  export interface ConnectorOperationOptions {
@@ -142,6 +159,8 @@ export interface ConnectorRef<CredentialProfileKey extends string = string> {
142
159
  readonly definitions?: Readonly<Record<string, ConnectorOpDefinition>>;
143
160
  /** HTTP fulfilment (present for connectors declared with `.http()`), emitted to the catalog verbatim. */
144
161
  readonly httpDef?: HttpConnectorDef;
162
+ /** MCP fulfilment (present for connectors declared with `.mcp()`), emitted to the catalog verbatim. */
163
+ readonly mcpDef?: McpConnectorDef;
145
164
  }
146
165
  /**
147
166
  * The connector-catalog document the SDK emits for the compiler. It is the canonical `ConnectorFile` from
@@ -160,8 +179,9 @@ export declare class ConnectorBuilder<CredentialProfileKey extends string = neve
160
179
  readonly operations: Readonly<Record<string, OperationSignature>>;
161
180
  readonly definitions: Readonly<Record<string, ConnectorOpDefinition>>;
162
181
  readonly httpDef?: HttpConnectorDef;
182
+ readonly mcpDef?: McpConnectorDef;
163
183
  readonly credentialProfiles?: Readonly<Record<CredentialProfileKey, CredentialProfileDef>>;
164
- constructor(id: string, version: string, operations?: Readonly<Record<string, OperationSignature>>, definitions?: Readonly<Record<string, ConnectorOpDefinition>>, httpDef?: HttpConnectorDef, credentialProfiles?: Readonly<Record<CredentialProfileKey, CredentialProfileDef>>);
184
+ constructor(id: string, version: string, operations?: Readonly<Record<string, OperationSignature>>, definitions?: Readonly<Record<string, ConnectorOpDefinition>>, httpDef?: HttpConnectorDef, credentialProfiles?: Readonly<Record<CredentialProfileKey, CredentialProfileDef>>, mcpDef?: McpConnectorDef);
165
185
  /** Declare the credential profiles supported by a curated signature ref or compute connector. */
166
186
  credentials<const Profiles extends Readonly<Record<string, CredentialProfileDef>>>(profiles: Profiles): ConnectorBuilder<Extract<keyof Profiles, string>>;
167
187
  /** Declare an operation by signature only (fulfilment supplied elsewhere, e.g. a catalog by `id@version`). */
@@ -173,6 +193,8 @@ export declare class ConnectorBuilder<CredentialProfileKey extends string = neve
173
193
  * `.provides()`-d. The typed options are validated against the canonical schema at author time.
174
194
  */
175
195
  http<const Options extends HttpConnectorOptions>(options: Options): ConnectorBuilder<DeclaredCredentialProfileKeys<Options>>;
196
+ /** Declare a governed remote MCP connector from an imported, frozen upstream tool snapshot. */
197
+ mcp<const Options extends McpConnectorOptions>(options: Options): ConnectorBuilder<DeclaredCredentialProfileKeys<Options>>;
176
198
  /** Declare a sandboxed-compute operation: `run` is your function, shipped as the connector's `code`. */
177
199
  compute(name: string, options: ComputeOperationOptions): ConnectorBuilder<CredentialProfileKey>;
178
200
  }
@@ -48,8 +48,9 @@ export class ConnectorBuilder {
48
48
  operations;
49
49
  definitions;
50
50
  httpDef;
51
+ mcpDef;
51
52
  credentialProfiles;
52
- constructor(id, version, operations = {}, definitions = {}, httpDef, credentialProfiles) {
53
+ constructor(id, version, operations = {}, definitions = {}, httpDef, credentialProfiles, mcpDef) {
53
54
  this.id = id;
54
55
  this.version = version;
55
56
  this.operations = operations;
@@ -57,20 +58,22 @@ export class ConnectorBuilder {
57
58
  // Assign conditionally (not `= undefined`) to satisfy exactOptionalPropertyTypes.
58
59
  if (httpDef !== undefined)
59
60
  this.httpDef = httpDef;
60
- const declaredProfiles = credentialProfiles ?? httpDef?.credentialProfiles;
61
+ if (mcpDef !== undefined)
62
+ this.mcpDef = mcpDef;
63
+ const declaredProfiles = credentialProfiles ?? httpDef?.credentialProfiles ?? mcpDef?.credentialProfiles;
61
64
  if (declaredProfiles !== undefined) {
62
65
  this.credentialProfiles = declaredProfiles;
63
66
  }
64
67
  }
65
68
  /** Declare the credential profiles supported by a curated signature ref or compute connector. */
66
69
  credentials(profiles) {
67
- if (this.httpDef !== undefined) {
68
- throw new Error(`connector "${this.id}" is HTTP — declare credentialProfiles inside .http(...)`);
70
+ if (this.httpDef !== undefined || this.mcpDef !== undefined) {
71
+ throw new Error(`connector "${this.id}" owns its transport — declare credentialProfiles inside the transport options`);
69
72
  }
70
73
  if (this.credentialProfiles !== undefined) {
71
74
  throw new Error(`connector "${this.id}" already declares credential profiles`);
72
75
  }
73
- return new ConnectorBuilder(this.id, this.version, this.operations, this.definitions, undefined, { ...profiles });
76
+ return new ConnectorBuilder(this.id, this.version, this.operations, this.definitions, undefined, { ...profiles }, undefined);
74
77
  }
75
78
  /** Declare an operation by signature only (fulfilment supplied elsewhere, e.g. a catalog by `id@version`). */
76
79
  operation(name, options) {
@@ -81,7 +84,7 @@ export class ConnectorBuilder {
81
84
  input: toOperationIoSchema(options.input, 'input', `${this.id}.${name}`),
82
85
  output: toOperationIoSchema(options.output, 'output', `${this.id}.${name}`),
83
86
  },
84
- }, this.definitions, this.httpDef, this.credentialProfiles);
87
+ }, this.definitions, this.httpDef, this.credentialProfiles, this.mcpDef);
85
88
  }
86
89
  /**
87
90
  * Declare this as an **HTTP connector**: transport (`baseUrl`/`allowedOrigins`), `bearer`/`apiKey` auth
@@ -93,6 +96,9 @@ export class ConnectorBuilder {
93
96
  if (this.credentialProfiles !== undefined) {
94
97
  throw new Error(`connector "${this.id}" already uses .credentials(...) — HTTP connectors declare credentialProfiles inside .http(...)`);
95
98
  }
99
+ if (this.mcpDef !== undefined) {
100
+ throw new Error(`connector "${this.id}" is already an .mcp() connector — a connector has one engine`);
101
+ }
96
102
  if (Object.keys(this.definitions).length > 0) {
97
103
  throw new Error(`connector "${this.id}" already has .compute() operations — a connector is HTTP or compute, not both`);
98
104
  }
@@ -120,11 +126,80 @@ export class ConnectorBuilder {
120
126
  }
121
127
  return new ConnectorBuilder(this.id, this.version, signatures, this.definitions, def);
122
128
  }
129
+ /** Declare a governed remote MCP connector from an imported, frozen upstream tool snapshot. */
130
+ mcp(options) {
131
+ if (this.credentialProfiles !== undefined) {
132
+ throw new Error(`connector "${this.id}" already uses .credentials(...) — MCP connectors declare credentialProfiles inside .mcp(...)`);
133
+ }
134
+ if (this.httpDef !== undefined) {
135
+ throw new Error(`connector "${this.id}" is already an .http() connector — a connector has one engine`);
136
+ }
137
+ if (Object.keys(this.definitions).length > 0) {
138
+ throw new Error(`connector "${this.id}" already has .compute() operations — a connector is MCP or compute, not both`);
139
+ }
140
+ const operations = {};
141
+ for (const [name, operation] of Object.entries(options.operations)) {
142
+ const output = operation.result === 'text' && operation.output === undefined
143
+ ? {
144
+ type: 'object',
145
+ properties: { text: { type: 'string' } },
146
+ required: ['text'],
147
+ additionalProperties: false,
148
+ }
149
+ : toOperationIoSchema(operation.output, 'output', `${this.id}.${name}`);
150
+ operations[name] = {
151
+ ...operation,
152
+ result: operation.result ?? 'structured',
153
+ input: toOperationIoSchema(operation.input, 'input', `${this.id}.${name}`),
154
+ output,
155
+ ...(operation.auth === undefined
156
+ ? {}
157
+ : {
158
+ auth: normalizeAuth(operation.auth, `connectors.${this.id}.operations.${name}.auth`),
159
+ }),
160
+ };
161
+ }
162
+ const { credentialProfiles, operations: _operations, ...mcpOptions } = options;
163
+ const def = {
164
+ id: this.id,
165
+ version: this.version,
166
+ kind: 'custom',
167
+ ...(credentialProfiles === undefined ? {} : { credentialProfiles }),
168
+ mcp: {
169
+ ...mcpOptions,
170
+ endpoint: normalizeVariableOrString(options.endpoint, `connectors.${this.id}.mcp.endpoint`),
171
+ ...(options.allowedOrigins === undefined
172
+ ? {}
173
+ : {
174
+ allowedOrigins: options.allowedOrigins.map((origin, index) => normalizeVariableOrString(origin, `connectors.${this.id}.mcp.allowedOrigins.${index}`)),
175
+ }),
176
+ ...(options.auth === undefined
177
+ ? {}
178
+ : {
179
+ auth: normalizeAuth(options.auth, `connectors.${this.id}.mcp.auth`),
180
+ }),
181
+ },
182
+ operations,
183
+ };
184
+ validateCatalog({ connectors: [def] });
185
+ const signatures = {};
186
+ for (const [name, operation] of Object.entries(operations)) {
187
+ signatures[name] = {
188
+ type: operation.type,
189
+ input: normalizeOperationIoSchema(operation.input),
190
+ output: normalizeOperationIoSchema(operation.output),
191
+ };
192
+ }
193
+ return new ConnectorBuilder(this.id, this.version, signatures, this.definitions, undefined, undefined, def);
194
+ }
123
195
  /** Declare a sandboxed-compute operation: `run` is your function, shipped as the connector's `code`. */
124
196
  compute(name, options) {
125
197
  if (this.httpDef) {
126
198
  throw new Error(`connector "${this.id}" is already an .http() connector — a connector is HTTP or compute, not both`);
127
199
  }
200
+ if (this.mcpDef) {
201
+ throw new Error(`connector "${this.id}" is already an .mcp() connector — a connector is MCP or compute, not both`);
202
+ }
128
203
  const type = options.type ?? 'read';
129
204
  const input = toOperationIoSchema(options.input, 'input', `${this.id}.${name}`);
130
205
  const output = toOperationIoSchema(options.output, 'output', `${this.id}.${name}`);
@@ -139,7 +214,7 @@ export class ConnectorBuilder {
139
214
  ...(options.limits ? { limits: options.limits } : {}),
140
215
  ...(options.calls ? { calls: options.calls } : {}),
141
216
  },
142
- }, undefined, this.credentialProfiles);
217
+ }, undefined, this.credentialProfiles, undefined);
143
218
  }
144
219
  }
145
220
  function rejectUnsafeActionRetry(id, options) {
@@ -5,7 +5,7 @@ export type { AgentCapabilityKind, AgentCapabilityRef, AgentExampleSource, Agent
5
5
  export { type AssistantAccess, type AssistantCapability, type AssistantLabels, type AssistantLayoutMode, type AssistantModel, type AssistantPosition, type AssistantPresentationOptions, type AssistantPresentationTone, type AssistantSurfaceConfig, type AssistantSurfaceMode, type AssistantThemeMode, type AssistantUiOptions, type AuthenticatedWebsiteAccess, type AuthenticatedWebsiteInput, authenticatedWebsite, type CapabilityRef, type EmbeddedAssistantConfig, type EmbeddedAssistantOptions, embeddedAssistant, type NoodleManagedModel, noodleManaged, type OpenAICompatibleModel, type OpenAICompatibleModelInput, openAICompatible, type PublicWebsiteAccess, type PublicWebsiteInput, publicWebsite, } from './assistant.js';
6
6
  export { type ConfigRef, type ConfigRefKind, secret, variable, } from './config.js';
7
7
  export { type BoundConnectorRef, bind, type ConnectionBinding, type ConnectionRef, type ConnectionSource, clientCredentials, connection, externalExchange, type GoogleWorkloadIdentityAccess, googleWorkloadIdentity, managedSecret, } from './connections.js';
8
- export { type ComputeHost, type ComputeLimits, type ComputeOperationOptions, ConnectorBuilder, type ConnectorCatalogDoc, type ConnectorOpDefinition, type ConnectorOperationOptions, type ConnectorRef, connector, type HttpConnectorOptions, } from './connectors.js';
8
+ export { type ComputeHost, type ComputeLimits, type ComputeOperationOptions, ConnectorBuilder, type ConnectorCatalogDoc, type ConnectorOpDefinition, type ConnectorOperationOptions, type ConnectorRef, connector, type HttpConnectorOptions, type McpAuthOptions, type McpConnectorOptions, } from './connectors.js';
9
9
  export type { AmbientContextOptions, AmbientProviderContext, ServerContextOptions, } from './context.js';
10
10
  export { type CustomerAuth, type CustomerAuthClaimMap, type CustomerAuthRouting, type CustomerEndpointClaimMapping, customerAuth, type FederatedOidcIssuer, } from './customer-auth.js';
11
11
  export { type CustomerEndpointPolicy, type CustomerEndpointRef, customerEndpoint, } from './customer-endpoint.js';
@@ -0,0 +1,33 @@
1
+ const ANNOTATION = 'x-noodleseed-model-latest-message-includes-any';
2
+ const MAX_PHRASES = 32;
3
+ const MAX_PHRASE_CHARS = 128;
4
+ export function manifestToolAnnotations(options) {
5
+ if (Object.hasOwn(options.annotations ?? {}, ANNOTATION)) {
6
+ throw new Error(`Use modelVisibility.latestMessageIncludesAny instead of the reserved ${ANNOTATION} annotation.`);
7
+ }
8
+ if (options.modelVisibility === undefined) {
9
+ return options.annotations ? { annotations: { ...options.annotations } } : {};
10
+ }
11
+ const phrases = options.modelVisibility.latestMessageIncludesAny;
12
+ if (phrases.length < 1 || phrases.length > MAX_PHRASES) {
13
+ throw new Error(`modelVisibility.latestMessageIncludesAny must contain 1-${MAX_PHRASES} literal phrases.`);
14
+ }
15
+ const trimmed = phrases.map((phrase) => phrase.trim());
16
+ const normalized = trimmed.map(normalize);
17
+ if (trimmed.some((phrase, index) => phrase.length > MAX_PHRASE_CHARS || normalized[index]?.length === 0)) {
18
+ throw new Error(`modelVisibility.latestMessageIncludesAny phrases must contain text and be at most ${MAX_PHRASE_CHARS} characters.`);
19
+ }
20
+ if (new Set(normalized).size !== normalized.length) {
21
+ throw new Error('modelVisibility.latestMessageIncludesAny phrases must be unique.');
22
+ }
23
+ return { annotations: { ...options.annotations, [ANNOTATION]: trimmed } };
24
+ }
25
+ function normalize(value) {
26
+ return value
27
+ .normalize('NFKC')
28
+ .toLocaleLowerCase('en-US')
29
+ .replace(/[^\p{L}\p{N}]+/gu, ' ')
30
+ .trim()
31
+ .replace(/\s+/gu, ' ');
32
+ }
33
+ //# sourceMappingURL=model-tool-visibility.js.map
@@ -132,6 +132,11 @@ export interface ToolOptions {
132
132
  readonly input: JsonSchema | z.ZodType;
133
133
  readonly output?: JsonSchema | z.ZodType;
134
134
  readonly annotations?: Readonly<Record<string, unknown>>;
135
+ /** Deterministic model-discovery constraints. These do not grant invocation authority. */
136
+ readonly modelVisibility?: {
137
+ /** Show this tool to the model only when the latest user message contains one of these literals. */
138
+ readonly latestMessageIncludesAny: readonly string[];
139
+ };
135
140
  /**
136
141
  * Tool-surface visibility (SEP-1865 `_meta.ui.visibility`). Default `['model', 'app']`. Set `['app']`
137
142
  * for a **UI-only helper** — discoverable with app-only metadata so hosts can hide it from the model
@@ -10,6 +10,7 @@ import { manifestContext } from './context.js';
10
10
  import { manifestCustomerAuth } from './customer-auth.js';
11
11
  import { toJsonSchema } from './json-schema.js';
12
12
  import { manifestKnowledge } from './knowledge.js';
13
+ import { manifestToolAnnotations } from './model-tool-visibility.js';
13
14
  import { recordFulfilment, recordTool, } from './recording.js';
14
15
  const WIDGET_RESULT_META_KEY = '__noodleResultMeta';
15
16
  export function server(name, options, definitions = []) {
@@ -142,7 +143,7 @@ class ServerBuilder {
142
143
  ...(tool.options.contextProvider ? { contextProvider: true } : {}),
143
144
  inputSchema: toJsonSchema(tool.options.input, 'input'),
144
145
  ...(tool.options.output ? { outputSchema: toJsonSchema(tool.options.output) } : {}),
145
- ...(tool.options.annotations ? { annotations: { ...tool.options.annotations } } : {}),
146
+ ...manifestToolAnnotations(tool.options),
146
147
  ...(tool.options.visibility?.length ? { visibility: [...tool.options.visibility] } : {}),
147
148
  fulfilment: {
148
149
  steps: recorded.steps,
@@ -251,7 +252,7 @@ class ServerBuilder {
251
252
  }
252
253
  toConnectorCatalog() {
253
254
  // `use ∪ provides` is the authoritative emit set: each connector carries its own fulfilment — an
254
- // HTTP def (`.http()`) or compute definitions (`.compute()`). Signature-only connectors are skipped.
255
+ // HTTP/MCP transport def or compute definitions. Signature-only connectors are skipped.
255
256
  const connectors = [];
256
257
  const seen = new Set();
257
258
  for (const c of [...Object.values(this.connectors), ...Object.values(this.provided)]) {
@@ -262,6 +263,10 @@ class ServerBuilder {
262
263
  connectors.push(c.httpDef);
263
264
  continue;
264
265
  }
266
+ if (c.mcpDef) {
267
+ connectors.push(c.mcpDef);
268
+ continue;
269
+ }
265
270
  const definitions = c.definitions;
266
271
  if (definitions && Object.keys(definitions).length > 0) {
267
272
  const credentialProfiles = c.credentialProfiles;
@@ -110,7 +110,7 @@ export const CATALOG_ACCOUNT = [
110
110
  },
111
111
  {
112
112
  name: 'init',
113
- summary: 'Write a noodle.service.yaml from a named profile.',
113
+ summary: 'Initialize a service profile or the local open-core Compose stack.',
114
114
  arguments: [],
115
115
  flags: [
116
116
  {
@@ -128,6 +128,18 @@ export const CATALOG_ACCOUNT = [
128
128
  type: 'boolean',
129
129
  summary: 'Overwrite a changed existing config.',
130
130
  },
131
+ {
132
+ ...OPTIONAL_FLAG,
133
+ name: 'compose',
134
+ type: 'boolean',
135
+ summary: 'Generate the local Docker Compose state for the open-core profile.',
136
+ },
137
+ {
138
+ ...OPTIONAL_FLAG,
139
+ name: 'replace-secrets',
140
+ type: 'boolean',
141
+ summary: 'Rotate all generated secrets; requires --compose.',
142
+ },
131
143
  ],
132
144
  },
133
145
  {
@@ -129,7 +129,7 @@ export const CATALOG_BOOTSTRAP_AUTHORING = [
129
129
  name: 'import',
130
130
  section: 'build',
131
131
  helpRank: 8,
132
- summary: 'Scaffold starter code from an OpenAPI spec.',
132
+ summary: 'Scaffold starter code from OpenAPI or a frozen upstream MCP tool snapshot.',
133
133
  arguments: [],
134
134
  flags: [],
135
135
  subcommands: [
@@ -158,6 +158,35 @@ export const CATALOG_BOOTSTRAP_AUTHORING = [
158
158
  FORCE_FLAG,
159
159
  ],
160
160
  },
161
+ {
162
+ name: 'mcp',
163
+ summary: 'Import a remote MCP tool snapshot into a governed TypeScript project.',
164
+ arguments: [
165
+ {
166
+ name: 'url',
167
+ type: 'string',
168
+ summary: 'Remote Streamable HTTP MCP endpoint.',
169
+ required: true,
170
+ variadic: false,
171
+ sensitive: false,
172
+ constraints: {},
173
+ },
174
+ ],
175
+ flags: [
176
+ stringFlag('output', '<dir>', 'Generated project directory.', {
177
+ constraints: { default: 'noodle-mcp-app' },
178
+ }),
179
+ stringFlag('name', '<slug>', 'Generated server and connector name.', {
180
+ constraints: { default: 'mcp-app' },
181
+ }),
182
+ stringFlag('prefix', '<name>', 'Prefix imported operation and outward tool names.'),
183
+ stringFlag('header-env', '<header>=<ENV_NAME>', 'Read one import credential from the named environment variable; never persist its value.'),
184
+ booleanFlag('check', 'Compare the live tool surface with the saved snapshot without writing.'),
185
+ FORCE_FLAG,
186
+ JSON_FLAG,
187
+ ],
188
+ jsonOutput: { mode: 'single' },
189
+ },
161
190
  ],
162
191
  local: true,
163
192
  },
@@ -8,7 +8,7 @@ export function validateComputeCallGraph(defs, targets, errors) {
8
8
  graph.set(key, graph.get(key) ?? new Set());
9
9
  paths.set(key, `connectors.${def.id}.operations.${opName}.calls`);
10
10
  }
11
- if ('http' in def)
11
+ if ('http' in def || 'mcp' in def)
12
12
  continue;
13
13
  for (const [opName, op] of Object.entries(def.operations)) {
14
14
  const from = operationIdentityKey(def.id, def.version, opName);
@@ -0,0 +1,95 @@
1
+ import { McpConnector, } from '@noodle-borg/connector-http';
2
+ import { collectVariablesFromHttpAuth } from './auth-variables.js';
3
+ import { addAuthBinding, toAuthScheme } from './compile-auth.js';
4
+ import { toSignature } from './compile-http.js';
5
+ import { catalogCredentialMetadata } from './credential-profiles.js';
6
+ import { collectVariableExpression, isVariableExpression } from './variables.js';
7
+ /** Compile one frozen upstream MCP tool snapshot into the ordinary Connector runtime port. */
8
+ export function compileMcpConnector(def, mode, errors, catalog, connectors, secretBindings, variableBindings) {
9
+ const endpoint = def.mcp.endpoint;
10
+ const allowedOrigins = def.mcp.allowedOrigins ?? (isVariableExpression(endpoint) ? [] : [endpoint]);
11
+ collectVariableExpression(endpoint, variableBindings);
12
+ for (const origin of allowedOrigins)
13
+ collectVariableExpression(origin, variableBindings);
14
+ if (def.mcp.auth !== undefined)
15
+ collectVariablesFromHttpAuth(def.mcp.auth, variableBindings);
16
+ if (isVariableExpression(endpoint) && def.mcp.allowedOrigins === undefined) {
17
+ errors.push({
18
+ code: 'variable_mcp_endpoint_requires_allowed_origins',
19
+ path: `connectors.${def.id}.mcp.allowedOrigins`,
20
+ message: 'mcp.allowedOrigins is required when mcp.endpoint is a managed variable',
21
+ });
22
+ return;
23
+ }
24
+ const signatures = {};
25
+ const operations = {};
26
+ for (const [operationName, operation] of Object.entries(def.operations)) {
27
+ const base = `connectors.${def.id}.operations.${operationName}`;
28
+ const signature = toSignature(operation.type, operation.input, operation.output);
29
+ signatures[operationName] = signature;
30
+ if (operation.auth !== undefined)
31
+ collectVariablesFromHttpAuth(operation.auth, variableBindings);
32
+ operations[operationName] = {
33
+ upstreamTool: operation.tool,
34
+ signature,
35
+ ...(operation.result === 'structured' ? { upstreamOutputSchema: signature.output } : {}),
36
+ ...(operation.auth === undefined ? {} : { auth: mcpAuthScheme(operation.auth) }),
37
+ ...(operation.limits?.maxResponseBytes === undefined
38
+ ? {}
39
+ : { maxResponseBytes: operation.limits.maxResponseBytes }),
40
+ ...(operation.fake === undefined ? {} : { fake: operation.fake }),
41
+ };
42
+ addAuthBinding({
43
+ auth: operation.auth,
44
+ connectorId: def.id,
45
+ connectorVersion: def.version,
46
+ operation: operationName,
47
+ path: `${base}.auth`,
48
+ allowedOrigins: literalOrigins(allowedOrigins),
49
+ secretBindings,
50
+ errors,
51
+ });
52
+ }
53
+ catalog.push({
54
+ id: def.id,
55
+ version: def.version,
56
+ kind: def.kind ?? 'custom',
57
+ ...catalogCredentialMetadata(def),
58
+ operations: signatures,
59
+ });
60
+ addAuthBinding({
61
+ auth: def.mcp.auth,
62
+ connectorId: def.id,
63
+ connectorVersion: def.version,
64
+ path: `connectors.${def.id}.mcp.auth`,
65
+ allowedOrigins: literalOrigins(allowedOrigins),
66
+ secretBindings,
67
+ errors,
68
+ });
69
+ const config = {
70
+ id: def.id,
71
+ version: def.version,
72
+ endpoint,
73
+ ...(def.mcp.allowedOrigins === undefined ? {} : { allowedOrigins: def.mcp.allowedOrigins }),
74
+ ...(def.mcp.auth === undefined ? {} : { auth: mcpAuthScheme(def.mcp.auth) }),
75
+ ...(def.mcp.timeoutMs === undefined ? {} : { timeoutMs: def.mcp.timeoutMs }),
76
+ ...(def.mcp.maxResponseBytes === undefined
77
+ ? {}
78
+ : { maxResponseBytes: def.mcp.maxResponseBytes }),
79
+ ...(def.mcp.protocol === undefined ? {} : { protocol: def.mcp.protocol }),
80
+ ...(mode === 'fake' ? { fakeMode: true } : {}),
81
+ operations,
82
+ };
83
+ connectors.push(new McpConnector(config));
84
+ }
85
+ function mcpAuthScheme(auth) {
86
+ const scheme = toAuthScheme(auth);
87
+ if (scheme.kind === 'cookie') {
88
+ throw new Error('delegated session cookie auth is not valid for an MCP connector');
89
+ }
90
+ return scheme;
91
+ }
92
+ function literalOrigins(origins) {
93
+ return origins.filter((origin) => !isVariableExpression(origin));
94
+ }
95
+ //# sourceMappingURL=compile-mcp.js.map
@@ -3,6 +3,7 @@ import { CodeConnector, DEFAULT_LIMITS, QuickJsComputeEngine, } from '@noodle-bo
3
3
  import { parse as parseYaml } from 'yaml';
4
4
  import { validateComputeCallGraph } from './call-graph.js';
5
5
  import { compileHttpConnector, toSignature } from './compile-http.js';
6
+ import { compileMcpConnector } from './compile-mcp.js';
6
7
  import { catalogCredentialMetadata } from './credential-profiles.js';
7
8
  import { collectCatalogCustomerRouting } from './customer-routing.js';
8
9
  import { connectorIdentityKey } from './identity-key.js';
@@ -62,6 +63,9 @@ export function compileConnectors(source, options = {}) {
62
63
  if ('http' in def) {
63
64
  compileHttpConnector(def, mode, errors, catalog, connectors, secretBindings, variableBindings);
64
65
  }
66
+ else if ('mcp' in def) {
67
+ compileMcpConnector(def, mode, errors, catalog, connectors, secretBindings, variableBindings);
68
+ }
65
69
  else {
66
70
  compileComputeConnector(def, computeEngine, targets, errors, catalog, connectors);
67
71
  }
@@ -57,7 +57,7 @@ export function collectCatalogCustomerRouting(definitions, errors) {
57
57
  actionEndpoints.add(def.http.baseUrl.name);
58
58
  }
59
59
  }
60
- else {
60
+ else if (!('mcp' in def)) {
61
61
  const computeOperation = def.operations[operationName];
62
62
  if (computeOperation?.calls === undefined) {
63
63
  visiting.delete(key);
@@ -1,4 +1,4 @@
1
1
  export { type ClientCredentialsBinding, type ClientCredentialsCustomBinding, type CompileConnectorsOptions, type CompileConnectorsResult, type ConnectorCompileError, compileConnectors, type DelegatedOAuthBinding, type DelegatedTokenExchangeBinding, type SecretBinding, } from './compile.js';
2
2
  export { type DelegatedTokenExchangeIdentityContext, delegatedTokenExchangeIdentityErrors, type ServerCustomerIdentitySources, } from './delegated-token-exchange-identity.js';
3
- export { type ComputeConnectorDef, type ComputeOperationDef, type ConnectorDef, type ConnectorFile, type CredentialProfileDef, connectorFileSchema, type HttpAuthDef, type HttpConnectorDef, type HttpOperationDef, } from './schema.js';
3
+ export { type ComputeConnectorDef, type ComputeOperationDef, type ConnectorDef, type ConnectorFile, type CredentialProfileDef, connectorFileSchema, type HttpAuthDef, type HttpConnectorDef, type HttpOperationDef, type McpConnectorDef, type McpOperationDef, } from './schema.js';
4
4
  //# sourceMappingURL=index.d.ts.map