import { z } from 'zod/v4' import { lazySchema } from '../../utils/lazySchema.js' /** * Discriminated union of all LSP operations * Uses 'operation' as the discriminator field */ export const lspToolInputSchema = lazySchema(() => { /** * Go to Definition operation * Finds the definition location of a symbol at the given position */ const goToDefinitionSchema = z.strictObject({ operation: z.literal('goToDefinition'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Find References operation * Finds all references to a symbol at the given position */ const findReferencesSchema = z.strictObject({ operation: z.literal('findReferences'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Hover operation * Gets hover information (documentation, type info) for a symbol at the given position */ const hoverSchema = z.strictObject({ operation: z.literal('hover'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Document Symbol operation * Gets all symbols (functions, classes, variables) in a document */ const documentSymbolSchema = z.strictObject({ operation: z.literal('documentSymbol'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Workspace Symbol operation * Searches for symbols across the entire workspace */ const workspaceSymbolSchema = z.strictObject({ operation: z.literal('workspaceSymbol'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Go to Implementation operation * Finds the implementation locations of an interface or abstract method */ const goToImplementationSchema = z.strictObject({ operation: z.literal('goToImplementation'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Prepare Call Hierarchy operation * Prepares a call hierarchy item at the given position (first step for call hierarchy) */ const prepareCallHierarchySchema = z.strictObject({ operation: z.literal('prepareCallHierarchy'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Incoming Calls operation * Finds all functions/methods that call the function at the given position */ const incomingCallsSchema = z.strictObject({ operation: z.literal('incomingCalls'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) /** * Outgoing Calls operation * Finds all functions/methods called by the function at the given position */ const outgoingCallsSchema = z.strictObject({ operation: z.literal('outgoingCalls'), filePath: z.string().describe('The absolute or relative path to the file'), line: z .number() .int() .positive() .describe('The line number (1-based, as shown in editors)'), character: z .number() .int() .positive() .describe('The character offset (1-based, as shown in editors)'), }) return z.discriminatedUnion('operation', [ goToDefinitionSchema, findReferencesSchema, hoverSchema, documentSymbolSchema, workspaceSymbolSchema, goToImplementationSchema, prepareCallHierarchySchema, incomingCallsSchema, outgoingCallsSchema, ]) }) /** * TypeScript type for LSPTool input */ export type LSPToolInput = z.infer> /** * Type guard to check if an operation is a valid LSP operation */ export function isValidLSPOperation( operation: string, ): operation is LSPToolInput['operation'] { return [ 'goToDefinition', 'findReferences', 'hover', 'documentSymbol', 'workspaceSymbol', 'goToImplementation', 'prepareCallHierarchy', 'incomingCalls', 'outgoingCalls', ].includes(operation) }