/ tools / LSPTool / schemas.ts
schemas.ts
  1  import { z } from 'zod/v4'
  2  import { lazySchema } from '../../utils/lazySchema.js'
  3  
  4  /**
  5   * Discriminated union of all LSP operations
  6   * Uses 'operation' as the discriminator field
  7   */
  8  export const lspToolInputSchema = lazySchema(() => {
  9    /**
 10     * Go to Definition operation
 11     * Finds the definition location of a symbol at the given position
 12     */
 13    const goToDefinitionSchema = z.strictObject({
 14      operation: z.literal('goToDefinition'),
 15      filePath: z.string().describe('The absolute or relative path to the file'),
 16      line: z
 17        .number()
 18        .int()
 19        .positive()
 20        .describe('The line number (1-based, as shown in editors)'),
 21      character: z
 22        .number()
 23        .int()
 24        .positive()
 25        .describe('The character offset (1-based, as shown in editors)'),
 26    })
 27  
 28    /**
 29     * Find References operation
 30     * Finds all references to a symbol at the given position
 31     */
 32    const findReferencesSchema = z.strictObject({
 33      operation: z.literal('findReferences'),
 34      filePath: z.string().describe('The absolute or relative path to the file'),
 35      line: z
 36        .number()
 37        .int()
 38        .positive()
 39        .describe('The line number (1-based, as shown in editors)'),
 40      character: z
 41        .number()
 42        .int()
 43        .positive()
 44        .describe('The character offset (1-based, as shown in editors)'),
 45    })
 46  
 47    /**
 48     * Hover operation
 49     * Gets hover information (documentation, type info) for a symbol at the given position
 50     */
 51    const hoverSchema = z.strictObject({
 52      operation: z.literal('hover'),
 53      filePath: z.string().describe('The absolute or relative path to the file'),
 54      line: z
 55        .number()
 56        .int()
 57        .positive()
 58        .describe('The line number (1-based, as shown in editors)'),
 59      character: z
 60        .number()
 61        .int()
 62        .positive()
 63        .describe('The character offset (1-based, as shown in editors)'),
 64    })
 65  
 66    /**
 67     * Document Symbol operation
 68     * Gets all symbols (functions, classes, variables) in a document
 69     */
 70    const documentSymbolSchema = z.strictObject({
 71      operation: z.literal('documentSymbol'),
 72      filePath: z.string().describe('The absolute or relative path to the file'),
 73      line: z
 74        .number()
 75        .int()
 76        .positive()
 77        .describe('The line number (1-based, as shown in editors)'),
 78      character: z
 79        .number()
 80        .int()
 81        .positive()
 82        .describe('The character offset (1-based, as shown in editors)'),
 83    })
 84  
 85    /**
 86     * Workspace Symbol operation
 87     * Searches for symbols across the entire workspace
 88     */
 89    const workspaceSymbolSchema = z.strictObject({
 90      operation: z.literal('workspaceSymbol'),
 91      filePath: z.string().describe('The absolute or relative path to the file'),
 92      line: z
 93        .number()
 94        .int()
 95        .positive()
 96        .describe('The line number (1-based, as shown in editors)'),
 97      character: z
 98        .number()
 99        .int()
100        .positive()
101        .describe('The character offset (1-based, as shown in editors)'),
102    })
103  
104    /**
105     * Go to Implementation operation
106     * Finds the implementation locations of an interface or abstract method
107     */
108    const goToImplementationSchema = z.strictObject({
109      operation: z.literal('goToImplementation'),
110      filePath: z.string().describe('The absolute or relative path to the file'),
111      line: z
112        .number()
113        .int()
114        .positive()
115        .describe('The line number (1-based, as shown in editors)'),
116      character: z
117        .number()
118        .int()
119        .positive()
120        .describe('The character offset (1-based, as shown in editors)'),
121    })
122  
123    /**
124     * Prepare Call Hierarchy operation
125     * Prepares a call hierarchy item at the given position (first step for call hierarchy)
126     */
127    const prepareCallHierarchySchema = z.strictObject({
128      operation: z.literal('prepareCallHierarchy'),
129      filePath: z.string().describe('The absolute or relative path to the file'),
130      line: z
131        .number()
132        .int()
133        .positive()
134        .describe('The line number (1-based, as shown in editors)'),
135      character: z
136        .number()
137        .int()
138        .positive()
139        .describe('The character offset (1-based, as shown in editors)'),
140    })
141  
142    /**
143     * Incoming Calls operation
144     * Finds all functions/methods that call the function at the given position
145     */
146    const incomingCallsSchema = z.strictObject({
147      operation: z.literal('incomingCalls'),
148      filePath: z.string().describe('The absolute or relative path to the file'),
149      line: z
150        .number()
151        .int()
152        .positive()
153        .describe('The line number (1-based, as shown in editors)'),
154      character: z
155        .number()
156        .int()
157        .positive()
158        .describe('The character offset (1-based, as shown in editors)'),
159    })
160  
161    /**
162     * Outgoing Calls operation
163     * Finds all functions/methods called by the function at the given position
164     */
165    const outgoingCallsSchema = z.strictObject({
166      operation: z.literal('outgoingCalls'),
167      filePath: z.string().describe('The absolute or relative path to the file'),
168      line: z
169        .number()
170        .int()
171        .positive()
172        .describe('The line number (1-based, as shown in editors)'),
173      character: z
174        .number()
175        .int()
176        .positive()
177        .describe('The character offset (1-based, as shown in editors)'),
178    })
179  
180    return z.discriminatedUnion('operation', [
181      goToDefinitionSchema,
182      findReferencesSchema,
183      hoverSchema,
184      documentSymbolSchema,
185      workspaceSymbolSchema,
186      goToImplementationSchema,
187      prepareCallHierarchySchema,
188      incomingCallsSchema,
189      outgoingCallsSchema,
190    ])
191  })
192  
193  /**
194   * TypeScript type for LSPTool input
195   */
196  export type LSPToolInput = z.infer<ReturnType<typeof lspToolInputSchema>>
197  
198  /**
199   * Type guard to check if an operation is a valid LSP operation
200   */
201  export function isValidLSPOperation(
202    operation: string,
203  ): operation is LSPToolInput['operation'] {
204    return [
205      'goToDefinition',
206      'findReferences',
207      'hover',
208      'documentSymbol',
209      'workspaceSymbol',
210      'goToImplementation',
211      'prepareCallHierarchy',
212      'incomingCalls',
213      'outgoingCalls',
214    ].includes(operation)
215  }