Skip to main content
Glama

getTopLevelDeclarations

Extract and filter top-level declarations in PureScript code to retrieve names, types, and full source code. Supports regex-based filtering for precise analysis of functions, data types, or classes.

Instructions

Get detailed information about all main definitions in PureScript code: names, types (function/data/class), and full source code. Includes filtering options to find specific items. More comprehensive than getTopLevelDeclarationNames.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
codeNoPureScript code string.
filePathNoAbsolute path to the PureScript file. Only absolute paths are supported.
filtersNoOptional filters to apply to the declarations.

Implementation Reference

  • The main handler function for the getTopLevelDeclarations tool. It uses Tree-sitter to parse the input PureScript code (from file or string), executes a comprehensive Tree-sitter query to match various top-level declaration types (functions, data, classes, etc.), extracts the name, mapped type (e.g., DeclValue, DeclData), and full source text for each declaration, handles special cases for instance and derive/fixity declarations, applies optional regex filters on name/type/value, and returns the filtered list of declarations as JSON in MCP content format.
    "getTopLevelDeclarations": async (args) => { if (!treeSitterInitialized) throw new Error("Tree-sitter not initialized."); const code = await getCodeFromInput(args, true); // true for module-oriented const tree = purescriptTsParser.parse(code); const querySource = ` [ (function name: (variable) @name.function) @DeclValue (data name: (type) @name.data_type) @DeclData (class_declaration (class_head (class_name (type) @name.class))) @DeclClass (type_alias name: (type) @name.type_alias) @DeclType (newtype name: (type) @name.newtype) @DeclNewtype (foreign_import name: (variable) @name.foreign) @DeclForeign (signature name: (variable) @name.signature) @DeclSignature (class_instance (instance_head (class_name) @name.instance_class (type_name)? @name.instance_type)) @DeclInstanceChain (kind_value_declaration name: (type) @name.kind_sig) @DeclKindSignature (derive_declaration) @DeclDerive (type_role_declaration (type) @name.role_type (type_role)+ @name.role_value) @DeclRole (operator_declaration (operator) @name.operator) @DeclFixity ] `; const query = new Query(PureScriptLanguage, querySource); const matches = query.matches(tree.rootNode); const rawDeclarations = []; for (const match of matches) { const mainCapture = match.captures.find(c => c.name.startsWith("Decl")); if (!mainCapture) continue; const declNode = mainCapture.node; const mappedType = mainCapture.name; const value = declNode.text; // This is the full text of the declaration node // Create a map of captures for efficient lookup // Store the full capture object {name, node} as node properties (like .text) are needed const allCapturesMap = new Map(match.captures.map(c => [c.name, c])); let finalName; // Prioritized list of capture names that directly provide the 'name' const singleNameCaptureKeys = [ "name.function", "name.data_type", "name.class", "name.type_alias", "name.newtype", "name.foreign", "name.signature", "name.kind_sig", "name.role_type", "name.operator" ]; let foundSingleName = false; for (const key of singleNameCaptureKeys) { if (allCapturesMap.has(key)) { finalName = allCapturesMap.get(key).node.text; foundSingleName = true; break; } } if (!foundSingleName) { if (allCapturesMap.has("name.instance_class")) { finalName = allCapturesMap.get("name.instance_class").node.text; if (allCapturesMap.has("name.instance_type")) { finalName += ` ${allCapturesMap.get("name.instance_type").node.text}`; } } else if (mappedType === "DeclDerive" || mappedType === "DeclFixity") { // declNode is mainCapture.node, which is already available const firstIdentNode = declNode.descendantsOfType("identifier")[0] || declNode.descendantsOfType("type")[0] || declNode.descendantsOfType("operator")[0]; finalName = firstIdentNode ? firstIdentNode.text : `complex_${mappedType.toLowerCase().replace('decl', '')}`; } else { finalName = "unknown"; // Default if no other specific name found } } rawDeclarations.push({ name: finalName, type: mappedType, value, treeSitterType: declNode.type }); // Removed node property } let declarations = rawDeclarations; // Use rawDeclarations directly without consolidation // Apply filters if provided if (args.filters) { const { name, type, value } = args.filters; if (name) { const nameRegex = new RegExp(name); declarations = declarations.filter(d => nameRegex.test(d.name)); } if (type) { const typeRegex = new RegExp(type); declarations = declarations.filter(d => typeRegex.test(d.type)); } if (value) { const valueRegex = new RegExp(value); declarations = declarations.filter(d => valueRegex.test(d.value)); } } return { content: [{ type: "text", text: JSON.stringify(declarations, null, 2) }] }; },
  • The tool definition object in the TOOL_DEFINITIONS array, which includes the name, description, and detailed inputSchema specifying parameters: filePath or code (exactly one), and optional filters object with name/type/value regex strings. This is served by the tools/list MCP method.
    name: "getTopLevelDeclarations", description: "Get detailed information about all main definitions in PureScript code: names, types (function/data/class), and full source code. Includes filtering options to find specific items. More comprehensive than getTopLevelDeclarationNames.", inputSchema: { type: "object", properties: { filePath: { type: "string", description: "Absolute path to the PureScript file. Only absolute paths are supported." }, code: { type: "string", description: "PureScript code string." }, filters: { type: "object", properties: { name: { type: "string", description: "Regex to filter declarations by name." }, type: { type: "string", description: "Regex to filter declarations by their mapped type (e.g., DeclData, DeclValue)." }, value: { type: "string", description: "Regex to filter declarations by their full text value." } }, additionalProperties: false, description: "Optional filters to apply to the declarations." } }, additionalProperties: false, description: "Exactly one of 'filePath' or 'code' must be provided. Filters are optional." } }, // End of Phase 1 tools
  • The getCodeFromInput helper function used by the handler (and others) to retrieve code from either filePath (absolute) or inline code string, validating exactly one is provided for module-oriented tools.
    async function getCodeFromInput(args, isModuleOriented = true) { if (isModuleOriented) { const hasFilePath = args && typeof args.filePath === 'string'; const hasCode = args && typeof args.code === 'string'; if ((hasFilePath && hasCode) || (!hasFilePath && !hasCode)) { throw new Error("Invalid input: Exactly one of 'filePath' or 'code' must be provided for module-oriented tools."); } if (hasFilePath) { if (!path.isAbsolute(args.filePath)) { throw new Error(`Invalid filePath: '${args.filePath}' is not an absolute path. Only absolute paths are supported.`); } try { return await fs.readFile(args.filePath, 'utf-8'); } catch (e) { throw new Error(`Failed to read file at ${args.filePath}: ${e.message}`); } } return args.code; } else { // Snippet-oriented if (!args || typeof args.code !== 'string') { throw new Error("Invalid input: 'code' (string) is required for snippet-oriented tools."); } return args.code; } }
  • index.js:922-1017 (registration)
    The tool handler is registered in the INTERNAL_TOOL_HANDLERS object literal at key 'getTopLevelDeclarations', which is used by the tools/call MCP method dispatcher to invoke the handler.
    "getTopLevelDeclarations": async (args) => { if (!treeSitterInitialized) throw new Error("Tree-sitter not initialized."); const code = await getCodeFromInput(args, true); // true for module-oriented const tree = purescriptTsParser.parse(code); const querySource = ` [ (function name: (variable) @name.function) @DeclValue (data name: (type) @name.data_type) @DeclData (class_declaration (class_head (class_name (type) @name.class))) @DeclClass (type_alias name: (type) @name.type_alias) @DeclType (newtype name: (type) @name.newtype) @DeclNewtype (foreign_import name: (variable) @name.foreign) @DeclForeign (signature name: (variable) @name.signature) @DeclSignature (class_instance (instance_head (class_name) @name.instance_class (type_name)? @name.instance_type)) @DeclInstanceChain (kind_value_declaration name: (type) @name.kind_sig) @DeclKindSignature (derive_declaration) @DeclDerive (type_role_declaration (type) @name.role_type (type_role)+ @name.role_value) @DeclRole (operator_declaration (operator) @name.operator) @DeclFixity ] `; const query = new Query(PureScriptLanguage, querySource); const matches = query.matches(tree.rootNode); const rawDeclarations = []; for (const match of matches) { const mainCapture = match.captures.find(c => c.name.startsWith("Decl")); if (!mainCapture) continue; const declNode = mainCapture.node; const mappedType = mainCapture.name; const value = declNode.text; // This is the full text of the declaration node // Create a map of captures for efficient lookup // Store the full capture object {name, node} as node properties (like .text) are needed const allCapturesMap = new Map(match.captures.map(c => [c.name, c])); let finalName; // Prioritized list of capture names that directly provide the 'name' const singleNameCaptureKeys = [ "name.function", "name.data_type", "name.class", "name.type_alias", "name.newtype", "name.foreign", "name.signature", "name.kind_sig", "name.role_type", "name.operator" ]; let foundSingleName = false; for (const key of singleNameCaptureKeys) { if (allCapturesMap.has(key)) { finalName = allCapturesMap.get(key).node.text; foundSingleName = true; break; } } if (!foundSingleName) { if (allCapturesMap.has("name.instance_class")) { finalName = allCapturesMap.get("name.instance_class").node.text; if (allCapturesMap.has("name.instance_type")) { finalName += ` ${allCapturesMap.get("name.instance_type").node.text}`; } } else if (mappedType === "DeclDerive" || mappedType === "DeclFixity") { // declNode is mainCapture.node, which is already available const firstIdentNode = declNode.descendantsOfType("identifier")[0] || declNode.descendantsOfType("type")[0] || declNode.descendantsOfType("operator")[0]; finalName = firstIdentNode ? firstIdentNode.text : `complex_${mappedType.toLowerCase().replace('decl', '')}`; } else { finalName = "unknown"; // Default if no other specific name found } } rawDeclarations.push({ name: finalName, type: mappedType, value, treeSitterType: declNode.type }); // Removed node property } let declarations = rawDeclarations; // Use rawDeclarations directly without consolidation // Apply filters if provided if (args.filters) { const { name, type, value } = args.filters; if (name) { const nameRegex = new RegExp(name); declarations = declarations.filter(d => nameRegex.test(d.name)); } if (type) { const typeRegex = new RegExp(type); declarations = declarations.filter(d => typeRegex.test(d.type)); } if (value) { const valueRegex = new RegExp(value); declarations = declarations.filter(d => valueRegex.test(d.value)); } } return { content: [{ type: "text", text: JSON.stringify(declarations, null, 2) }] }; },
  • index.js:557-785 (registration)
    The TOOL_DEFINITIONS array (used by tools/list) includes the schema and metadata for getTopLevelDeclarations.
    const TOOL_DEFINITIONS = [ { name: "get_server_status", description: "Check if IDE server processes are running to avoid resource conflicts. Shows status of Tree-sitter parser (lightweight code analysis) and purs IDE server (process for type checking). ALWAYS use this before starting new IDE servers to prevent running multiple processes simultaneously.", inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: "echo", description: "Simple test tool that echoes back your input. Use to verify the MCP server is responding correctly.", inputSchema: { type: "object", properties: { message: { type: "string"}}, required: ["message"], additionalProperties: false }, }, { name: "query_purescript_ast", description: "[DEPRECATED] Parses PureScript code and executes a Tree-sitter query against its AST. Prefer specific AST query tools.", inputSchema: { type: "object", properties: { purescript_code: { type: "string" }, tree_sitter_query: { type: "string" }}, required: ["purescript_code", "tree_sitter_query"], additionalProperties: false }, }, // --- Phase 1: Core AST Query Tools --- // Module Information { name: "getModuleName", description: "Extract the module name (like 'Data.List' or 'Main') from PureScript code. Works on files or code snippets without needing the IDE server. Useful for understanding code structure.", inputSchema: { type: "object", properties: { filePath: { type: "string", description: "Absolute path to the PureScript file. Only absolute paths are supported." }, code: { type: "string", description: "PureScript code string." } }, additionalProperties: false, description: "Exactly one of 'filePath' or 'code' must be provided." } }, { name: "getImports", description: "Find all import statements in PureScript code (like 'import Data.List', 'import Prelude'). Shows what external modules the code depends on. Works without the IDE server.", inputSchema: { type: "object", properties: { filePath: { type: "string", description: "Absolute path to the PureScript file. Only absolute paths are supported." }, code: { type: "string", description: "PureScript code string." } }, additionalProperties: false, description: "Exactly one of 'filePath' or 'code' must be provided." } }, { name: "getTopLevelDeclarationNames", description: "List all main definitions in PureScript code: function names, data types, type classes, etc. Gets just the names (like 'myFunction', 'MyDataType'). Fast analysis without needing IDE server.", inputSchema: { type: "object", properties: { filePath: { type: "string", description: "Absolute path to the PureScript file. Only absolute paths are supported." }, code: { type: "string", description: "PureScript code string." } }, additionalProperties: false, description: "Exactly one of 'filePath' or 'code' must be provided." } }, // Function and Value Declarations { name: "getFunctionNames", description: "Extract only function names from PureScript code snippets. Focuses specifically on functions, ignoring data types and classes. Quick analysis for code understanding.", inputSchema: { type: "object", properties: { code: { type: "string", description: "PureScript code snippet." } }, required: ["code"], additionalProperties: false } }, // Expressions and Literals // Control Flow Analysis { name: "getWhereBindings", description: "Find 'where' clauses in PureScript functions. These contain local helper functions and variables. Useful for understanding function implementation details.", inputSchema: { type: "object", properties: { code: { type: "string", description: "PureScript code snippet." } }, required: ["code"], additionalProperties: false } }, { name: "getTopLevelDeclarations", description: "Get detailed information about all main definitions in PureScript code: names, types (function/data/class), and full source code. Includes filtering options to find specific items. More comprehensive than getTopLevelDeclarationNames.", inputSchema: { type: "object", properties: { filePath: { type: "string", description: "Absolute path to the PureScript file. Only absolute paths are supported." }, code: { type: "string", description: "PureScript code string." }, filters: { type: "object", properties: { name: { type: "string", description: "Regex to filter declarations by name." }, type: { type: "string", description: "Regex to filter declarations by their mapped type (e.g., DeclData, DeclValue)." }, value: { type: "string", description: "Regex to filter declarations by their full text value." } }, additionalProperties: false, description: "Optional filters to apply to the declarations." } }, additionalProperties: false, description: "Exactly one of 'filePath' or 'code' must be provided. Filters are optional." } }, // End of Phase 1 tools { name: "start_purs_ide_server", description: "Start the PureScript IDE server for type checking, auto-completion, and error detection. Automatically stops any existing server to prevent conflicts. Only run one at a time. Required for all pursIde* tools to work. Automatically selects a random available port to avoid conflicts - the port number is returned in the response. Only accepts absolute paths.", inputSchema: { type: "object", properties: { project_path: { type: "string", description: "Absolute path to the PureScript project directory. Only absolute paths are supported." }, output_directory: { type: "string", default: "output/" }, source_globs: { type: "array", items: { type: "string" }, default: ["src/**/*.purs", ".spago/*/*/src/**/*.purs", "test/**/*.purs"]}, log_level: { type: "string", enum: ["all", "debug", "perf", "none"], default: "none" } }, required: ["project_path"], additionalProperties: false }, }, { name: "stop_purs_ide_server", description: "Stop the PureScript IDE server to free up system resources. Use when you're done with type checking or want to switch projects. All pursIde* tools will stop working after this.", inputSchema: { type: "object", properties: {}, additionalProperties: false }, }, { name: "query_purs_ide", description: "Send raw commands to the PureScript IDE server. PREREQUISITE: IDE server must be running (use start_purs_ide_server first). Advanced tool - prefer specific pursIde* tools for common tasks.", inputSchema: { type: "object", properties: { purs_ide_command: { type: "string" }, purs_ide_params: { type: "object" }}, required: ["purs_ide_command"], additionalProperties: false }, }, { name: "generate_dependency_graph", description: "Create a dependency graph showing which functions/types use which others in PureScript modules. PREREQUISITES: IDE server must be running and modules must be loaded. Useful for understanding code relationships and refactoring impact.", inputSchema: { type: "object", properties: { target_modules: { type: "array", items: { type: "string" }, description: "Array of module names." }, max_concurrent_requests: { type: "integer", description: "Max concurrent 'usages' requests.", default: 5 } }, required: ["target_modules"], additionalProperties: false }, }, // --- purs ide direct command wrappers --- { name: "pursIdeLoad", description: "Load PureScript modules into the IDE server for type checking and completions. PREREQUISITE: IDE server must be running. ALWAYS run this first after starting the IDE server before using other pursIde* tools.", inputSchema: { type: "object", properties: { modules: { type: "array", items: { type: "string" }, description: "Optional: specific modules to load. If omitted, attempts to load all compiled modules." } }, additionalProperties: false } }, { name: "pursIdeType", description: "Look up the type signature of functions, variables, or values in PureScript code. PREREQUISITES: IDE server running and modules loaded. Helpful for understanding what a function expects and returns.", inputSchema: { type: "object", properties: { search: { type: "string", description: "Identifier name to search for." }, filters: { type: "array", items: { type: "object" }, description: "Optional: Array of Filter objects." }, currentModule: { type: "string", description: "Optional: Current module context." } }, required: ["search"], additionalProperties: false } }, { name: "pursIdeCwd", description: "Get the current working directory that the IDE server is using. PREREQUISITE: IDE server must be running. Useful for understanding the project context.", inputSchema: { type: "object", properties: {}, additionalProperties: false } }, { name: "pursIdeReset", description: "Clear all loaded modules from the IDE server's memory. PREREQUISITE: IDE server must be running. Use when switching projects or after major code changes. You'll need to run pursIdeLoad again after this.", inputSchema: { type: "object", properties: {}, additionalProperties: false } }, { name: "pursIdeQuit", description: "Gracefully shut down the IDE server and free up resources. PREREQUISITE: IDE server must be running. Same effect as stop_purs_ide_server but uses the server's built-in quit command first.", inputSchema: { type: "object", properties: {}, additionalProperties: false } }, { name: "pursIdeRebuild", description: "Quickly recompile a single PureScript module and check for errors. PREREQUISITES: IDE server running and modules loaded. Much faster than full project rebuild. Use when editing code to get immediate feedback.", inputSchema: { type: "object", properties: { file: { type: "string", description: "Path to the module to rebuild, or 'data:' prefixed source code." }, actualFile: { type: "string", description: "Optional: Real path if 'file' is 'data:' or a temp file." }, codegen: { type: "array", items: { type: "string" }, description: "Optional: Codegen targets (e.g., 'js', 'corefn'). Defaults to ['js']." } }, required: ["file"], additionalProperties: false } }, { name: "pursIdeUsages", description: "Find everywhere a specific function, type, or value is used across the project. PREREQUISITES: IDE server running and modules loaded. Essential for refactoring - shows impact of changes. If you plan to refactor, get usages before refactoring so you can make changes to all places that function is used.", inputSchema: { type: "object", properties: { module: { type: "string", description: "Module where the identifier is defined." }, namespace: { type: "string", enum: ["value", "type", "kind"], description: "Namespace of the identifier." }, identifier: { type: "string", description: "The identifier to find usages for." } }, required: ["module", "namespace", "identifier"], additionalProperties: false } }, { name: "pursIdeList", description: "List available modules in the project or imports in a specific file. PREREQUISITES: IDE server running and modules loaded. Helps understand project structure and dependencies.", inputSchema: { type: "object", properties: { listType: { type: "string", enum: ["availableModules", "import"], description: "Type of list to retrieve." }, file: { type: "string", description: "Path to the .purs file (required for 'import' listType)." } }, required: ["listType"], additionalProperties: false } } ];

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/avi892nash/purescript-mcp-tools'

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