Skip to main content
Glama
runninghare

TypeScript Definition Finder

by runninghare

find_typescript_definition

Locate TypeScript symbol definitions in your codebase by providing file path, symbol name, and line content to find original definition files and code.

Instructions

Use /ts-def to trigger this tool. This tool can find the definition of a TypeScript symbol in your codebase. When you encounter an imported symbol (e.g., 'import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"'), this tool will locate its original definition file and code. Simply provide the current file path, the symbol you want to find (e.g., 'StdioServerTransport'), and the line content containing that symbol.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathYesThe absolute path to the current typescript file (e.g., '/remote/.../src/index.ts')
symbolYesThe TypeScript symbol (variable, class name, interface, type, etc.) you want to find the definition of. This symbol must be present in the line_content.
line_contentYesThe entire line containing the symbol you want to find the definition of. The line content will be used to find both the line number in the file and the exact position of the symbol.

Implementation Reference

  • The main handler function for the 'find_typescript_definition' tool. It uses TypeScript's LanguageService to locate the definition of a symbol at a given position in a file, falling back to type definitions or quick info.
    export function findDefinition(filePath: string, column: number, line_content?: string) {
      let line = 0;
      let results: Record<string, any>[] = []; 
      try {
        const fileContent = readFileSync(filePath, 'utf8');
        
        // If pattern is provided, find the line number
        if (line_content) {
          const lines = fileContent.split('\n');
          for (let i = 0; i < lines.length; i++) {
            if (lines[i].includes(line_content)) {
              line = i + 1; // Convert to 1-based line number
              break;
            }
          }
        }
        
        // Create the language service host
        const servicesHost: ts.LanguageServiceHost = {
          getScriptFileNames: () => [filePath],
          getScriptVersion: () => '1',
          getScriptSnapshot: (fileName) => {
            if (fileName === filePath) {
              return ts.ScriptSnapshot.fromString(fileContent);
            }
            if (existsSync(fileName)) {
              return ts.ScriptSnapshot.fromString(readFileSync(fileName, 'utf8'));
            }
            return undefined;
          },
          getCurrentDirectory: () => process.cwd(),
          getCompilationSettings: () => ({
            target: ts.ScriptTarget.Latest,
            module: ts.ModuleKind.CommonJS,
            esModuleInterop: true,
          }),
          getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),
          fileExists: ts.sys.fileExists,
          readFile: ts.sys.readFile,
          readDirectory: ts.sys.readDirectory,
        };
    
        // Create the language service
        const services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
        
        // Get source file and find position
        const sourceFile = services.getProgram()?.getSourceFile(filePath);
        if (!sourceFile) {
          throw new Error('Could not get source file');
        }
    
        const position = sourceFile.getPositionOfLineAndCharacter(line - 1, column - 1);
        
        // Try to get definition
        const definitions = services.getDefinitionAtPosition(filePath, position);
        
        if (!definitions || definitions.length === 0) {
          // Try to get type definition instead
          const typeDefinitions = services.getTypeDefinitionAtPosition(filePath, position);
          if (!typeDefinitions || typeDefinitions.length === 0) {
            // Get quick info as a fallback
            const quickInfo = services.getQuickInfoAtPosition(filePath, position);
            if (quickInfo) {
              console.log('Quick Info:');
              console.log(`Kind: ${quickInfo.kind}`);
              console.log(`Documentation: ${ts.displayPartsToString(quickInfo.documentation)}`);
              console.log(`Type: ${ts.displayPartsToString(quickInfo.displayParts)}`);
            } else {
              console.log('No definition or information found at the specified position');
            }
            return;
          }
          logDefinitions(typeDefinitions, 'Type Definition', services, results);
          return;
        }
    
        logDefinitions(definitions, 'Definition', services, results);
        return results;
      } catch (error) {
        console.error('Error finding definition:', error instanceof Error ? error.message : String(error));
      }
    }
  • Helper function to process definition locations, extract code snippets with context, and format results.
    function logDefinitions(
      definitions: readonly ts.DefinitionInfo[],
      definitionType: string,
      services: ts.LanguageService,
      resultStr: Record<string, any>[]
    ) {
        // resultStr += `\nFound ${definitions.length} ${definitionType}(s):\n`;
    
      definitions.forEach((def, index) => {
        // resultStr += `\n${definitionType} ${index + 1}:\n`;
        const result: Record<string, any> = {
            file: def.fileName,
            type: definitionType,
        }
        // resultStr += `File: ${def.fileName}\n`;
    
        // Read the file content
        const content = existsSync(def.fileName) ? readFileSync(def.fileName, 'utf8') : null;
        if (!content) {
        //   resultStr += 'Could not read file contents\n';
          return;
        }
    
        // Create a source file for position calculations
        const sourceFile = ts.createSourceFile(
          def.fileName,
          content,
          ts.ScriptTarget.Latest,
          true
        );
    
        // Get start and end positions
        const start = sourceFile.getLineAndCharacterOfPosition(def.textSpan.start);
        const end = sourceFile.getLineAndCharacterOfPosition(def.textSpan.start + def.textSpan.length);
    
        result.location = `Line ${start.line + 1}, Column ${start.character + 1}`;
        // resultStr += `Location: Line ${start.line + 1}, Column ${start.character + 1}\n`;
    
        // Split content into lines and find the definition
        const lines = content.split('\n');
        const startLine = start.line;
        let endLine = end.line;
    
        // Try to find the complete definition by looking at structure
        const baseIndent = getIndentation(lines[startLine]);
        for (let i = endLine + 1; i < lines.length; i++) {
          const line = lines[i];
          if (line.trim() === '') continue;
          const indent = getIndentation(line);
          if (indent <= baseIndent) {
            break;
          }
          endLine = i;
        }
    
        // Add some context
        const contextBefore = 1;
        const contextAfter = 1;
        const displayStartLine = Math.max(0, startLine - contextBefore);
        const displayEndLine = Math.min(lines.length - 1, endLine + contextAfter);
    
        // resultStr += '\nDefinition:\n';
        let codeSnippet = '';
        for (let i = displayStartLine; i <= displayEndLine; i++) {
          const lineNum = (i + 1).toString().padStart(4);
          const marker = i === startLine ? ' >' : i > startLine && i <= endLine ? ' +' : '  ';
          codeSnippet += `${lineNum}${marker} ${lines[i]}\n`;
        }
        result.codeSnippet = codeSnippet;
        resultStr.push(result);
      });
    }
    
    function getIndentation(line: string): number {
      const match = line.match(/^[\s\t]*/);
      return match ? match[0].length : 0;
    } 
  • Input schema defining parameters for the tool: file_path, line_content, column_number.
    "inputSchema": {
      "type": "object",
      "properties": {
        "file_path": {
          "type": "string",
          "description": "The absolute path to the current typescript file (e.g., '/remote/.../src/index.ts')",
          "required": true
        },
        "line_content": {
          "type": "string",
          "description": "Pass the entire line of the symbol you want to find the definition of. The line content will be used to find the line number in the file instead of directly passing line number which AI Editor often has trouble with. The first line matching the content will be used.",
          "required": true
        },
        "column_number": {
            "type": "number",
            "description": "The column number of the symbol (1-based indexing). For instance, you want to find the definition of StdioServerTransport, and the column number of symbol 'StdioServerTransport' in line 'import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"' is 12, you should pass 12 as the column_number.",
            "required": true
        }
      },
      "required": ["file_path", "line_content", "column_number"]
    }
  • src/index.ts:29-55 (registration)
    Tool registration in the tools array used by ListToolsRequestSchema handler.
    const tools =  [
        {
            "name": "find_typescript_definition",
            "description": "Use /ts-def to trigger this tool. This tool can find the definition of a TypeScript symbol in your codebase. When you encounter an imported symbol (e.g., 'import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"'), this tool will locate its original definition file and code. Simply provide the current file path, the line content and the symbol's column number, and it will return the source definition location and code snippet.",
            "inputSchema": {
              "type": "object",
              "properties": {
                "file_path": {
                  "type": "string",
                  "description": "The absolute path to the current typescript file (e.g., '/remote/.../src/index.ts')",
                  "required": true
                },
                "line_content": {
                  "type": "string",
                  "description": "Pass the entire line of the symbol you want to find the definition of. The line content will be used to find the line number in the file instead of directly passing line number which AI Editor often has trouble with. The first line matching the content will be used.",
                  "required": true
                },
                "column_number": {
                    "type": "number",
                    "description": "The column number of the symbol (1-based indexing). For instance, you want to find the definition of StdioServerTransport, and the column number of symbol 'StdioServerTransport' in line 'import { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"' is 12, you should pass 12 as the column_number.",
                    "required": true
                }
              },
              "required": ["file_path", "line_content", "column_number"]
            }
        }
      ];
  • MCP CallToolRequestSchema dispatch handler that invokes the findDefinition function for this tool.
    if (request.params.name === "find_typescript_definition") {
      if (!request.params.arguments) {
        throw new McpError(ErrorCode.InvalidParams, "Missing arguments");
      }
      const { file_path, column_number, line_content } = request.params.arguments;
      var results = findDefinition(file_path as string, column_number as number, line_content as string);
    } else {
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. While it explains what the tool does (finds definitions) and provides usage examples, it doesn't disclose important behavioral traits like error conditions, performance characteristics, rate limits, authentication needs, or what happens when symbols aren't found. For a tool with zero annotation coverage, this leaves significant gaps in understanding its behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, starting with the triggering mechanism and core purpose. Each sentence adds value: the first explains triggering, the second states the purpose, the third provides usage context, and the fourth explains parameter usage. There's minimal redundancy, though the final sentence could be slightly more concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given there's no output schema and no annotations, the description provides adequate basic information about what the tool does and how to use it. However, for a tool that performs code analysis (potentially complex), it lacks information about return format, error handling, and limitations. The description is complete enough for basic understanding but leaves important contextual gaps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so the schema already fully documents all three parameters. The description adds some context about how parameters relate ('symbol must be present in the line_content') and provides examples, but doesn't add significant semantic meaning beyond what's already in the schema descriptions. This meets the baseline expectation when schema coverage is complete.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'find the definition of a TypeScript symbol in your codebase' with specific examples. It uses the verb 'find' and identifies the resource 'TypeScript symbol definition'. However, since there are no sibling tools mentioned, it cannot demonstrate differentiation from alternatives.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for when to use the tool: 'When you encounter an imported symbol... this tool will locate its original definition file and code.' It gives specific examples and explains the triggering mechanism ('/ts-def'). However, it doesn't explicitly state when NOT to use it or mention any alternatives since no siblings exist.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/runninghare/ts-def-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server