@ethisyscore/plugin-ui 1.71.0 → 1.71.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.
@@ -154,6 +154,103 @@ declare function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Pr
154
154
  */
155
155
  declare function useToolInvokerMap(tools: readonly string[]): ToolInvoker;
156
156
 
157
+ /**
158
+ * A single tool's wire contract: the arguments it takes and the payload it returns.
159
+ *
160
+ * ## The gap this closes
161
+ *
162
+ * `BaseMcpService.tool<TRes>(name: string, args?: unknown)` checks nothing. The tool name is any
163
+ * string, the arguments are `unknown`, and `TRes` is whatever the caller asserts. Every part of the
164
+ * boundary is a claim rather than a check, and all three parts have been wrong in production within
165
+ * a single module:
166
+ *
167
+ * - **The result lied.** `tech-list-tool-mappings` returned a bare array while the caller read
168
+ * `response.items`, so the table rendered its headers with no rows. No error anywhere.
169
+ * - **The result lied again.** `tech-auto-match-tool-mappings` returned no payload at all while the
170
+ * caller read `response.items.filter(...)`.
171
+ * - **The arguments were mislabelled.** A service sent `{ connectorId, moduleKey }` where the
172
+ * command declared `ConnectorSlug`, so the command rejected every call. Both values were strings,
173
+ * so even the service interface it implemented could not catch the swap.
174
+ *
175
+ * None of those produced a type error, and none produced a runtime error either - the surface just
176
+ * quietly did nothing, which is the most expensive failure mode there is.
177
+ *
178
+ * ## Phantom, not runtime
179
+ *
180
+ * The contract carries no values. `__args` and `__result` exist only so a declaration can name
181
+ * types; {@link mcpTool} returns a frozen empty object, so both are ABSENT at runtime whatever their
182
+ * declared types say. Reading one gets `undefined`, never a value to build on - that absence is the
183
+ * safety property here, not the types themselves.
184
+ */
185
+ interface McpToolContract<TArgs = unknown, TResult = unknown> {
186
+ /** Phantom: the arguments this tool accepts. Never present at runtime. */
187
+ readonly __args: TArgs;
188
+ /** Phantom: the payload this tool returns. Never present at runtime. */
189
+ readonly __result: TResult;
190
+ }
191
+ /** A plugin's declared tool surface: tool name to contract. */
192
+ type McpToolContracts = Record<string, McpToolContract>;
193
+ /** The arguments type for one tool in a contract map. */
194
+ type McpToolArgs<TTools extends McpToolContracts, K extends keyof TTools> = TTools[K]["__args"];
195
+ /** The result type for one tool in a contract map. */
196
+ type McpToolResult<TTools extends McpToolContracts, K extends keyof TTools> = TTools[K]["__result"];
197
+ /**
198
+ * Declares one tool's contract.
199
+ *
200
+ * Returns a marker object rather than nothing so the declaration can be a real value: the same
201
+ * object literal drives the types AND the hook registration (see {@link useTypedToolInvokerMap}),
202
+ * which is what stops the registered tool list from drifting away from the declared surface.
203
+ *
204
+ * @example
205
+ * export const TOOLS = {
206
+ * "tech-list-tool-mappings": mcpTool<{ connectorId: string; moduleKey: string }, MappingsResponse>(),
207
+ * "tech-auto-match-tool-mappings": mcpTool<{ connectorId: string }, AutoMatchResponse>(),
208
+ * } as const;
209
+ */
210
+ declare function mcpTool<TArgs, TResult>(): McpToolContract<TArgs, TResult>;
211
+ /**
212
+ * Base class for a service whose tool calls are checked against a declared contract map.
213
+ *
214
+ * Deliberately a SEPARATE class from `BaseMcpService` rather than an overload on it. An added
215
+ * overload would have to keep accepting `(name: string, args?: unknown)` for the services that have
216
+ * not adopted contracts, and TypeScript would then resolve a mistyped call to that looser signature
217
+ * instead of rejecting it - so a typo in a tool name would still compile. Opting in by changing base
218
+ * class means the check cannot be silently bypassed.
219
+ *
220
+ * `BaseMcpService` is untouched; nothing has to migrate.
221
+ *
222
+ * @example
223
+ * class ConnectorMappingService extends TypedMcpService<typeof TOOLS> {
224
+ * async getMappings(connectorId: string, moduleKey: string) {
225
+ * // name, args and result all checked against TOOLS
226
+ * return this.tool("tech-list-tool-mappings", { connectorId, moduleKey });
227
+ * }
228
+ * }
229
+ */
230
+ declare abstract class TypedMcpService<TTools extends McpToolContracts> {
231
+ private readonly invoker;
232
+ protected constructor(invoker: ToolInvoker);
233
+ /**
234
+ * Invokes a declared tool. The name must be a key of the contract map, the arguments must match
235
+ * its declared shape, and the result is its declared type - no call-site assertion.
236
+ */
237
+ protected tool<K extends keyof TTools & string>(name: K, args: McpToolArgs<TTools, K>): Promise<McpToolResult<TTools, K>>;
238
+ }
239
+ /**
240
+ * Composes a {@link ToolInvoker} over every tool in a contract declaration.
241
+ *
242
+ * Takes the declaration OBJECT, not a list of names, and that is the point: `useToolInvokerMap`
243
+ * throws `No MCP invoker registered for tool "x"` at runtime when a service calls a tool the list
244
+ * forgot, and the list and the service were two places to keep in step. Deriving the names from the
245
+ * contract map makes that drift impossible - if a tool is declared it is registered, and if it is
246
+ * not declared the call does not typecheck.
247
+ *
248
+ * Rules of hooks: pass a module-level `as const` declaration. `Object.keys` on a stable object
249
+ * yields a stable, constant-length list, which is what the underlying hook requires. Building the
250
+ * declaration inside a component would vary the hook count between renders and break.
251
+ */
252
+ declare function useTypedToolInvokerMap<TTools extends McpToolContracts>(contracts: TTools): ToolInvoker;
253
+
157
254
  /**
158
255
  * Plugin shim for the monolith's `useAuthenticatedQuery`.
159
256
  *
@@ -1207,4 +1304,4 @@ interface MenuConfigHostSidebarOptions {
1207
1304
  */
1208
1305
  declare function menuConfigToHostSidebarInput(config: HeaderMenuItem[], opts: MenuConfigHostSidebarOptions): HostSidebarHookInput;
1209
1306
 
1210
- export { type AuthenticatedQueryResult, BaseMcpService, DEFAULT_MAX_UPLOAD_BYTES, type DefinePlatformReactPluginOverlayOptions, type DefinePlatformReactPluginPageOptions, EHX_PLUGIN_PORTAL_ATTR, EHX_PLUGIN_PORTAL_CLASS, EHX_PLUGIN_ROOT_CLASS, HostSidebarHookInput, HostSidebarInput, type InsetLease, type ManagedLifecycle, type MenuConfigHostSidebarOptions, MissingViewFallback, OVERLAY_HOST_CONTRACT_VERSION, type OverlayHostApi, type OverlayHostCapabilities, OverlayHostContext, type PlatformReactOverlayProps, type PluginOverlayDefinerConfig, type PluginPageDefinerConfig, PluginPortalScope, PluginStyleScope, type ReactRouterShimOptions, SurfaceBaseContext, type SurfaceBaseValue, TemplateContext, type TemplateManifest, type ToHostSidebarInputOptions, type ToolInvoker, type UseMcpUploadOptions, type UseMcpUploadResult, type UseMcpUploadTarget, type ViewComponent, buildSurfaceUrl, createPluginOverlayDefiner, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginOverlay, definePlatformReactPluginPage, deriveHostEntityToken, injectPluginStyles, isComponent, menuConfigToHostSidebarInput, normaliseSlug, notViaMcp, resolveSurfaceBase, surfacePathFor, toHostSidebarInput, useAuthenticatedQueries, useAuthenticatedQuery, useManagedLifecycle, useMcpUpload, useOverlayHost, usePluginRealtime, useSurfaceUrl, useToolInvoker, useToolInvokerMap, useView };
1307
+ export { type AuthenticatedQueryResult, BaseMcpService, DEFAULT_MAX_UPLOAD_BYTES, type DefinePlatformReactPluginOverlayOptions, type DefinePlatformReactPluginPageOptions, EHX_PLUGIN_PORTAL_ATTR, EHX_PLUGIN_PORTAL_CLASS, EHX_PLUGIN_ROOT_CLASS, HostSidebarHookInput, HostSidebarInput, type InsetLease, type ManagedLifecycle, type McpToolArgs, type McpToolContract, type McpToolContracts, type McpToolResult, type MenuConfigHostSidebarOptions, MissingViewFallback, OVERLAY_HOST_CONTRACT_VERSION, type OverlayHostApi, type OverlayHostCapabilities, OverlayHostContext, type PlatformReactOverlayProps, type PluginOverlayDefinerConfig, type PluginPageDefinerConfig, PluginPortalScope, PluginStyleScope, type ReactRouterShimOptions, SurfaceBaseContext, type SurfaceBaseValue, TemplateContext, type TemplateManifest, type ToHostSidebarInputOptions, type ToolInvoker, TypedMcpService, type UseMcpUploadOptions, type UseMcpUploadResult, type UseMcpUploadTarget, type ViewComponent, buildSurfaceUrl, createPluginOverlayDefiner, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginOverlay, definePlatformReactPluginPage, deriveHostEntityToken, injectPluginStyles, isComponent, mcpTool, menuConfigToHostSidebarInput, normaliseSlug, notViaMcp, resolveSurfaceBase, surfacePathFor, toHostSidebarInput, useAuthenticatedQueries, useAuthenticatedQuery, useManagedLifecycle, useMcpUpload, useOverlayHost, usePluginRealtime, useSurfaceUrl, useToolInvoker, useToolInvokerMap, useTypedToolInvokerMap, useView };
@@ -154,6 +154,103 @@ declare function useToolInvoker<TReq, TRes>(toolName: string): (req: TReq) => Pr
154
154
  */
155
155
  declare function useToolInvokerMap(tools: readonly string[]): ToolInvoker;
156
156
 
157
+ /**
158
+ * A single tool's wire contract: the arguments it takes and the payload it returns.
159
+ *
160
+ * ## The gap this closes
161
+ *
162
+ * `BaseMcpService.tool<TRes>(name: string, args?: unknown)` checks nothing. The tool name is any
163
+ * string, the arguments are `unknown`, and `TRes` is whatever the caller asserts. Every part of the
164
+ * boundary is a claim rather than a check, and all three parts have been wrong in production within
165
+ * a single module:
166
+ *
167
+ * - **The result lied.** `tech-list-tool-mappings` returned a bare array while the caller read
168
+ * `response.items`, so the table rendered its headers with no rows. No error anywhere.
169
+ * - **The result lied again.** `tech-auto-match-tool-mappings` returned no payload at all while the
170
+ * caller read `response.items.filter(...)`.
171
+ * - **The arguments were mislabelled.** A service sent `{ connectorId, moduleKey }` where the
172
+ * command declared `ConnectorSlug`, so the command rejected every call. Both values were strings,
173
+ * so even the service interface it implemented could not catch the swap.
174
+ *
175
+ * None of those produced a type error, and none produced a runtime error either - the surface just
176
+ * quietly did nothing, which is the most expensive failure mode there is.
177
+ *
178
+ * ## Phantom, not runtime
179
+ *
180
+ * The contract carries no values. `__args` and `__result` exist only so a declaration can name
181
+ * types; {@link mcpTool} returns a frozen empty object, so both are ABSENT at runtime whatever their
182
+ * declared types say. Reading one gets `undefined`, never a value to build on - that absence is the
183
+ * safety property here, not the types themselves.
184
+ */
185
+ interface McpToolContract<TArgs = unknown, TResult = unknown> {
186
+ /** Phantom: the arguments this tool accepts. Never present at runtime. */
187
+ readonly __args: TArgs;
188
+ /** Phantom: the payload this tool returns. Never present at runtime. */
189
+ readonly __result: TResult;
190
+ }
191
+ /** A plugin's declared tool surface: tool name to contract. */
192
+ type McpToolContracts = Record<string, McpToolContract>;
193
+ /** The arguments type for one tool in a contract map. */
194
+ type McpToolArgs<TTools extends McpToolContracts, K extends keyof TTools> = TTools[K]["__args"];
195
+ /** The result type for one tool in a contract map. */
196
+ type McpToolResult<TTools extends McpToolContracts, K extends keyof TTools> = TTools[K]["__result"];
197
+ /**
198
+ * Declares one tool's contract.
199
+ *
200
+ * Returns a marker object rather than nothing so the declaration can be a real value: the same
201
+ * object literal drives the types AND the hook registration (see {@link useTypedToolInvokerMap}),
202
+ * which is what stops the registered tool list from drifting away from the declared surface.
203
+ *
204
+ * @example
205
+ * export const TOOLS = {
206
+ * "tech-list-tool-mappings": mcpTool<{ connectorId: string; moduleKey: string }, MappingsResponse>(),
207
+ * "tech-auto-match-tool-mappings": mcpTool<{ connectorId: string }, AutoMatchResponse>(),
208
+ * } as const;
209
+ */
210
+ declare function mcpTool<TArgs, TResult>(): McpToolContract<TArgs, TResult>;
211
+ /**
212
+ * Base class for a service whose tool calls are checked against a declared contract map.
213
+ *
214
+ * Deliberately a SEPARATE class from `BaseMcpService` rather than an overload on it. An added
215
+ * overload would have to keep accepting `(name: string, args?: unknown)` for the services that have
216
+ * not adopted contracts, and TypeScript would then resolve a mistyped call to that looser signature
217
+ * instead of rejecting it - so a typo in a tool name would still compile. Opting in by changing base
218
+ * class means the check cannot be silently bypassed.
219
+ *
220
+ * `BaseMcpService` is untouched; nothing has to migrate.
221
+ *
222
+ * @example
223
+ * class ConnectorMappingService extends TypedMcpService<typeof TOOLS> {
224
+ * async getMappings(connectorId: string, moduleKey: string) {
225
+ * // name, args and result all checked against TOOLS
226
+ * return this.tool("tech-list-tool-mappings", { connectorId, moduleKey });
227
+ * }
228
+ * }
229
+ */
230
+ declare abstract class TypedMcpService<TTools extends McpToolContracts> {
231
+ private readonly invoker;
232
+ protected constructor(invoker: ToolInvoker);
233
+ /**
234
+ * Invokes a declared tool. The name must be a key of the contract map, the arguments must match
235
+ * its declared shape, and the result is its declared type - no call-site assertion.
236
+ */
237
+ protected tool<K extends keyof TTools & string>(name: K, args: McpToolArgs<TTools, K>): Promise<McpToolResult<TTools, K>>;
238
+ }
239
+ /**
240
+ * Composes a {@link ToolInvoker} over every tool in a contract declaration.
241
+ *
242
+ * Takes the declaration OBJECT, not a list of names, and that is the point: `useToolInvokerMap`
243
+ * throws `No MCP invoker registered for tool "x"` at runtime when a service calls a tool the list
244
+ * forgot, and the list and the service were two places to keep in step. Deriving the names from the
245
+ * contract map makes that drift impossible - if a tool is declared it is registered, and if it is
246
+ * not declared the call does not typecheck.
247
+ *
248
+ * Rules of hooks: pass a module-level `as const` declaration. `Object.keys` on a stable object
249
+ * yields a stable, constant-length list, which is what the underlying hook requires. Building the
250
+ * declaration inside a component would vary the hook count between renders and break.
251
+ */
252
+ declare function useTypedToolInvokerMap<TTools extends McpToolContracts>(contracts: TTools): ToolInvoker;
253
+
157
254
  /**
158
255
  * Plugin shim for the monolith's `useAuthenticatedQuery`.
159
256
  *
@@ -1207,4 +1304,4 @@ interface MenuConfigHostSidebarOptions {
1207
1304
  */
1208
1305
  declare function menuConfigToHostSidebarInput(config: HeaderMenuItem[], opts: MenuConfigHostSidebarOptions): HostSidebarHookInput;
1209
1306
 
1210
- export { type AuthenticatedQueryResult, BaseMcpService, DEFAULT_MAX_UPLOAD_BYTES, type DefinePlatformReactPluginOverlayOptions, type DefinePlatformReactPluginPageOptions, EHX_PLUGIN_PORTAL_ATTR, EHX_PLUGIN_PORTAL_CLASS, EHX_PLUGIN_ROOT_CLASS, HostSidebarHookInput, HostSidebarInput, type InsetLease, type ManagedLifecycle, type MenuConfigHostSidebarOptions, MissingViewFallback, OVERLAY_HOST_CONTRACT_VERSION, type OverlayHostApi, type OverlayHostCapabilities, OverlayHostContext, type PlatformReactOverlayProps, type PluginOverlayDefinerConfig, type PluginPageDefinerConfig, PluginPortalScope, PluginStyleScope, type ReactRouterShimOptions, SurfaceBaseContext, type SurfaceBaseValue, TemplateContext, type TemplateManifest, type ToHostSidebarInputOptions, type ToolInvoker, type UseMcpUploadOptions, type UseMcpUploadResult, type UseMcpUploadTarget, type ViewComponent, buildSurfaceUrl, createPluginOverlayDefiner, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginOverlay, definePlatformReactPluginPage, deriveHostEntityToken, injectPluginStyles, isComponent, menuConfigToHostSidebarInput, normaliseSlug, notViaMcp, resolveSurfaceBase, surfacePathFor, toHostSidebarInput, useAuthenticatedQueries, useAuthenticatedQuery, useManagedLifecycle, useMcpUpload, useOverlayHost, usePluginRealtime, useSurfaceUrl, useToolInvoker, useToolInvokerMap, useView };
1307
+ export { type AuthenticatedQueryResult, BaseMcpService, DEFAULT_MAX_UPLOAD_BYTES, type DefinePlatformReactPluginOverlayOptions, type DefinePlatformReactPluginPageOptions, EHX_PLUGIN_PORTAL_ATTR, EHX_PLUGIN_PORTAL_CLASS, EHX_PLUGIN_ROOT_CLASS, HostSidebarHookInput, HostSidebarInput, type InsetLease, type ManagedLifecycle, type McpToolArgs, type McpToolContract, type McpToolContracts, type McpToolResult, type MenuConfigHostSidebarOptions, MissingViewFallback, OVERLAY_HOST_CONTRACT_VERSION, type OverlayHostApi, type OverlayHostCapabilities, OverlayHostContext, type PlatformReactOverlayProps, type PluginOverlayDefinerConfig, type PluginPageDefinerConfig, PluginPortalScope, PluginStyleScope, type ReactRouterShimOptions, SurfaceBaseContext, type SurfaceBaseValue, TemplateContext, type TemplateManifest, type ToHostSidebarInputOptions, type ToolInvoker, TypedMcpService, type UseMcpUploadOptions, type UseMcpUploadResult, type UseMcpUploadTarget, type ViewComponent, buildSurfaceUrl, createPluginOverlayDefiner, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginOverlay, definePlatformReactPluginPage, deriveHostEntityToken, injectPluginStyles, isComponent, mcpTool, menuConfigToHostSidebarInput, normaliseSlug, notViaMcp, resolveSurfaceBase, surfacePathFor, toHostSidebarInput, useAuthenticatedQueries, useAuthenticatedQuery, useManagedLifecycle, useMcpUpload, useOverlayHost, usePluginRealtime, useSurfaceUrl, useToolInvoker, useToolInvokerMap, useTypedToolInvokerMap, useView };
@@ -302,6 +302,26 @@ function useToolInvokerMap(tools) {
302
302
  tools.map((t) => invokers[t])
303
303
  );
304
304
  }
305
+ function mcpTool() {
306
+ return Object.freeze({});
307
+ }
308
+ var TypedMcpService = class {
309
+ constructor(invoker) {
310
+ this.invoker = invoker;
311
+ }
312
+ invoker;
313
+ /**
314
+ * Invokes a declared tool. The name must be a key of the contract map, the arguments must match
315
+ * its declared shape, and the result is its declared type - no call-site assertion.
316
+ */
317
+ async tool(name, args) {
318
+ return await this.invoker(name, args);
319
+ }
320
+ };
321
+ function useTypedToolInvokerMap(contracts) {
322
+ const names = useMemo(() => Object.freeze(Object.keys(contracts).sort()), [contracts]);
323
+ return useToolInvokerMap(names);
324
+ }
305
325
  function useAuthenticatedQuery(options) {
306
326
  const result = useQuery(options);
307
327
  return {
@@ -863,6 +883,6 @@ function menuConfigToHostSidebarInput(config, opts) {
863
883
  return { mode: "actions", actions };
864
884
  }
865
885
 
866
- export { BaseMcpService, DEFAULT_MAX_UPLOAD_BYTES, EHX_PLUGIN_PORTAL_ATTR, EHX_PLUGIN_PORTAL_CLASS, EHX_PLUGIN_ROOT_CLASS, HOST_CHROME_CONTRACT_VERSION, HostSidebarActionsContext, MissingViewFallback, OVERLAY_HOST_CONTRACT_VERSION, OverlayHostContext, PluginPortalScope, PluginStyleScope, SIDEBAR_MODE, SurfaceBaseContext, TemplateContext, buildSurfaceUrl, createPluginOverlayDefiner, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginOverlay, definePlatformReactPluginPage, deriveHostEntityToken, injectPluginStyles, isComponent, isHostChromeCompatible, menuConfigToHostSidebarInput, normaliseSlug, notViaMcp, reportHostChromeDiagnostic, resolveSurfaceBase, setHostChromeDiagnosticSink, surfacePathFor, toHostSidebarInput, useAuthenticatedQueries, useAuthenticatedQuery, useHostSidebarActions, useManagedLifecycle, useMcpUpload, useOverlayHost, usePluginRealtime, useSurfaceUrl, useToolInvoker, useToolInvokerMap, useView };
886
+ export { BaseMcpService, DEFAULT_MAX_UPLOAD_BYTES, EHX_PLUGIN_PORTAL_ATTR, EHX_PLUGIN_PORTAL_CLASS, EHX_PLUGIN_ROOT_CLASS, HOST_CHROME_CONTRACT_VERSION, HostSidebarActionsContext, MissingViewFallback, OVERLAY_HOST_CONTRACT_VERSION, OverlayHostContext, PluginPortalScope, PluginStyleScope, SIDEBAR_MODE, SurfaceBaseContext, TemplateContext, TypedMcpService, buildSurfaceUrl, createPluginOverlayDefiner, createPluginPageDefiner, createReactRouterShim, createUseView, definePlatformReactPluginOverlay, definePlatformReactPluginPage, deriveHostEntityToken, injectPluginStyles, isComponent, isHostChromeCompatible, mcpTool, menuConfigToHostSidebarInput, normaliseSlug, notViaMcp, reportHostChromeDiagnostic, resolveSurfaceBase, setHostChromeDiagnosticSink, surfacePathFor, toHostSidebarInput, useAuthenticatedQueries, useAuthenticatedQuery, useHostSidebarActions, useManagedLifecycle, useMcpUpload, useOverlayHost, usePluginRealtime, useSurfaceUrl, useToolInvoker, useToolInvokerMap, useTypedToolInvokerMap, useView };
867
887
  //# sourceMappingURL=index.js.map
868
888
  //# sourceMappingURL=index.js.map