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