integration(slug).array() previously typed the handler context field as a single client because the declaration interface fixed mode to the union. A Mode type parameter narrows it, so fan-out fields type as client arrays.
Unified · Split
| @@ -14,18 +14,21 @@ export type AppIntegrationClient = { · 13 unmodified lines | |||
| 14 | 14 | ||
| 15 | export type IntegrationMode = "one" | "many"; | 15 | export type IntegrationMode = "one" | "many"; |
| 16 | 16 | ||
| 17 | export interface IntegrationDeclaration<Slug extends string = string> { | 17 | export interface IntegrationDeclaration< |
| 18 | Slug extends string = string, | ||
| 19 | Mode extends IntegrationMode = IntegrationMode, | ||
| 20 | > { | ||
| 18 | readonly kind: "integration"; | 21 | readonly kind: "integration"; |
| 19 | readonly slug: Slug; | 22 | readonly slug: Slug; |
| 20 | readonly mode: IntegrationMode; | 23 | readonly mode: Mode; |
| 21 | readonly description?: string; | 24 | readonly description?: string; |
| 22 | /** | 25 | /** |
| 23 | * Ask the caller to choose a set of connections. Future author-chosen binding modes should not | 26 | * Ask the caller to choose a set of connections. Future author-chosen binding modes should not |
| 24 | * project optional caller fields: optional fields are suggestions, and model callers under-fill | 27 | * project optional caller fields: optional fields are suggestions, and model callers under-fill |
| 25 | * them. | 28 | * them. |
| 26 | */ | 29 | */ |
| 27 | readonly array: () => IntegrationDeclaration<Slug>; | 30 | readonly array: () => IntegrationDeclaration<Slug, "many">; |
| 28 | readonly describe: (text: string) => IntegrationDeclaration<Slug>; | 31 | readonly describe: (text: string) => IntegrationDeclaration<Slug, Mode>; |
| 29 | } | 32 | } |
| 30 | 33 | ||
| 31 | export type IntegrationClients<TIntegrations> = | 34 | export type IntegrationClients<TIntegrations> = |
| @@ -41,9 +44,12 @@ export type IntegrationClients<TIntegrations> = · 9 unmodified lines | |||
| 41 | 44 | ||
| 42 | export type IntegrationDeclarations = Readonly<Record<string, IntegrationDeclaration>>; | 45 | export type IntegrationDeclarations = Readonly<Record<string, IntegrationDeclaration>>; |
| 43 | 46 | ||
| 44 | interface IntegrationDeclarationState<Slug extends string = string> { | 47 | interface IntegrationDeclarationState< |
| 48 | Slug extends string = string, | ||
| 49 | Mode extends IntegrationMode = IntegrationMode, | ||
| 50 | > { | ||
| 45 | readonly slug: Slug; | 51 | readonly slug: Slug; |
| 46 | readonly mode: IntegrationMode; | 52 | readonly mode: Mode; |
| 47 | readonly description?: string; | 53 | readonly description?: string; |
| 48 | } | 54 | } |
| 49 | 55 | ||
| @@ -53,15 +59,15 @@ export interface SerializedIntegrationDeclaration { · 3 unmodified lines | |||
| 53 | readonly description?: string; | 59 | readonly description?: string; |
| 54 | } | 60 | } |
| 55 | 61 | ||
| 56 | const makeIntegrationDeclaration = <Slug extends string>( | 62 | const makeIntegrationDeclaration = <Slug extends string, Mode extends IntegrationMode>( |
| 57 | state: IntegrationDeclarationState<Slug>, | 63 | state: IntegrationDeclarationState<Slug, Mode>, |
| 58 | ): IntegrationDeclaration<Slug> => { | 64 | ): IntegrationDeclaration<Slug, Mode> => { |
| 59 | const declaration = { | 65 | const declaration = { |
| 60 | kind: "integration" as const, | 66 | kind: "integration" as const, |
| 61 | slug: state.slug, | 67 | slug: state.slug, |
| 62 | mode: state.mode, | 68 | mode: state.mode, |
| 63 | ...(state.description !== undefined ? { description: state.description } : {}), | 69 | ...(state.description !== undefined ? { description: state.description } : {}), |
| 64 | array: () => makeIntegrationDeclaration({ ...state, mode: "many" }), | 70 | array: () => makeIntegrationDeclaration({ ...state, mode: "many" as const }), |
| 65 | describe: (text: string) => makeIntegrationDeclaration({ ...state, description: text }), | 71 | describe: (text: string) => makeIntegrationDeclaration({ ...state, description: text }), |
| 66 | }; | 72 | }; |
| 67 | return Object.freeze(declaration); | 73 | return Object.freeze(declaration); |
| @@ -105,8 +111,8 @@ export interface DefinedTool< · 37 unmodified lines | |||
| 105 | readonly "~executorAppTool": true; | 111 | readonly "~executorAppTool": true; |
| 106 | } | 112 | } |
| 107 | 113 | ||
| 108 | export const integration = <Slug extends string>(slug: Slug): IntegrationDeclaration<Slug> => | 114 | export const integration = <Slug extends string>(slug: Slug): IntegrationDeclaration<Slug, "one"> => |
| 109 | makeIntegrationDeclaration({ slug, mode: "one" }); | 115 | makeIntegrationDeclaration({ slug, mode: "one" as const }); |
| 110 | 116 | ||
| 111 | export const defineTool = < | 117 | export const defineTool = < |
| 112 | TInputSchema extends ToolSchema, | 118 | TInputSchema extends ToolSchema, |