Skip to main content
Glama

pursIdeReset

Clear all loaded modules from the PureScript IDE server's memory to aid in switching projects or handling major code changes. Ensures a clean slate before reloading with pursIdeLoad.

Instructions

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.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The handler function for the 'pursIdeReset' MCP tool. It sends a 'reset' command to the underlying 'purs ide' server via sendCommandToPursIde and formats the response as MCP content.
    "pursIdeReset": async () => { const result = await sendCommandToPursIde({ command: "reset" }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; },
  • The tool schema definition for 'pursIdeReset' in the TOOL_DEFINITIONS array, used by the MCP 'tools/list' method. Defines no input parameters.
    { 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 } },
  • index.js:791-1127 (registration)
    The INTERNAL_TOOL_HANDLERS object registers the 'pursIdeReset' handler function, which is used in 'tools/call' dispatch to map tool names to their executors.
    const INTERNAL_TOOL_HANDLERS = { "get_server_status": internalHandleGetServerStatus, "echo": internalHandleEcho, "query_purescript_ast": internalHandleQueryPurescriptAst, "start_purs_ide_server": internalHandleStartPursIdeServer, "stop_purs_ide_server": internalHandleStopPursIdeServer, "query_purs_ide": internalHandleQueryPursIde, "generate_dependency_graph": internalHandleGenerateDependencyGraph, // --- Phase 1: Core AST Query Tool Handlers (to be added) --- "getModuleName": async (args) => { if (!treeSitterInitialized) throw new Error("Tree-sitter not initialized."); const code = await getCodeFromInput(args, true); const tree = purescriptTsParser.parse(code); // Corrected query to capture the full text of the qualified_module node const query = new Query(PureScriptLanguage, `(purescript name: (qualified_module) @module.qname)`); const captures = query.captures(tree.rootNode); if (captures.length > 0 && captures[0].name === 'module.qname') { // The text of the qualified_module node itself is the full module name return { content: [{ type: "text", text: JSON.stringify(captures[0].node.text.replace(/\s+/g, ''), null, 2) }] }; } return { content: [{ type: "text", text: JSON.stringify(null, null, 2) }] }; }, "getFunctionNames": async (args) => { if (!treeSitterInitialized) throw new Error("Tree-sitter not initialized."); const code = await getCodeFromInput(args, false); const tree = purescriptTsParser.parse(code); const query = new Query(PureScriptLanguage, `(function name: (variable) @func.name)`); const captures = query.captures(tree.rootNode); const functionNames = captures.map(capture => capture.node.text); return { content: [{ type: "text", text: JSON.stringify(functionNames, null, 2) }] }; }, // Stubs for other Phase 1 handlers - to be implemented "getImports": async (args) => { if (!treeSitterInitialized) throw new Error("Tree-sitter not initialized."); const code = await getCodeFromInput(args, true); const tree = purescriptTsParser.parse(code); const query = new Query(PureScriptLanguage, `(import module: (qualified_module) @import.path)`); const captures = query.captures(tree.rootNode); const imports = []; for (const capture of captures) { if (capture.name === 'import.path') { const moduleNodes = capture.node.children.filter(child => child.type === 'module'); if (moduleNodes.length > 0) { const fullPath = moduleNodes.map(n => n.text).join('.'); const moduleName = moduleNodes[0].text; const submoduleName = moduleNodes.length > 1 ? moduleNodes[1].text : undefined; imports.push({ module: moduleName, submodule: submoduleName, fullPath: fullPath }); } } } return { content: [{ type: "text", text: JSON.stringify(imports, null, 2) }] }; }, "getTopLevelDeclarationNames": 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) (data name: (type) @name) (class_declaration (class_head (class_name (type) @name))) (type_alias name: (type) @name) (foreign_import name: (variable) @name) (signature name: (variable) @name) (class_instance (instance_name) @name) (kind_value_declaration name: (type) @name) ] `; const query = new Query(PureScriptLanguage, querySource); const captures = query.captures(tree.rootNode); const declarationNames = captures.map(capture => capture.node.text).filter(Boolean); // Deduplicate names const uniqueNames = [...new Set(declarationNames)]; return { content: [{ type: "text", text: JSON.stringify(uniqueNames, null, 2) }] }; }, "getIntegerLiterals": async (args) => { if (!treeSitterInitialized) throw new Error("Tree-sitter not initialized."); const code = await getCodeFromInput(args, false); const tree = purescriptTsParser.parse(code); const integerLiterals = []; const query = new Query(PureScriptLanguage, `(integer) @integer.literal`); const captures = query.captures(tree.rootNode); captures.forEach(capture => { if (capture.name === 'integer.literal') { integerLiterals.push(parseInt(capture.node.text, 10)); } }); return { content: [{ type: "text", text: JSON.stringify(integerLiterals, null, 2) }] }; }, "getWhereBindings": async (args) => { if (!treeSitterInitialized) throw new Error("Tree-sitter not initialized."); const code = await getCodeFromInput(args, false); // Snippet-oriented const tree = purescriptTsParser.parse(code); const whereClausesText = []; // Query for 'where' keyword followed by a 'declarations' block within a function or let binding const querySource = ` (function (where) @where_keyword (declarations) @declarations_block) `; // Also consider 'let' bindings with 'where' clauses, though less common for top-level 'where' // (let_binding (where) @where_keyword (declarations) @declarations_block) const query = new Query(PureScriptLanguage, querySource); const matches = query.matches(tree.rootNode); for (const match of matches) { const whereKeywordNode = match.captures.find(c => c.name === 'where_keyword')?.node; const declarationsNode = match.captures.find(c => c.name === 'declarations_block')?.node; if (whereKeywordNode && declarationsNode) { // Construct the text from "where" keyword to the end of the declarations block // This requires careful handling of start and end positions if they are not contiguous in the source text string // For simplicity, if they are siblings and in order, we can take text from start of 'where' to end of 'declarations' // A safer way is to combine their individual texts if they represent the full conceptual block const fullWhereClauseText = `${whereKeywordNode.text} ${declarationsNode.text}`; whereClausesText.push(fullWhereClauseText.trim()); } } // Deduplicate, as some complex structures might yield multiple partial matches const uniqueWhereClauses = [...new Set(whereClausesText)]; return { content: [{ type: "text", text: JSON.stringify(uniqueWhereClauses, null, 2) }] }; }, "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) }] }; }, // --- New purs ide command wrapper handlers --- "pursIdeLoad": async (args) => { const params = args || {}; // If args is null/undefined, pass empty object for default load all const result = await sendCommandToPursIde({ command: "load", params }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }, "pursIdeType": async (args) => { if (!args || typeof args.search !== 'string') { throw new Error("Invalid input: 'search' (string) is required for pursIdeType."); } const params = { search: args.search, filters: args.filters || [], // Default to empty filters array currentModule: args.currentModule }; const result = await sendCommandToPursIde({ command: "type", params }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }, "pursIdeCwd": async () => { const result = await sendCommandToPursIde({ command: "cwd" }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }, "pursIdeReset": async () => { const result = await sendCommandToPursIde({ command: "reset" }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }, "pursIdeQuit": async () => { let quitMessage = "purs ide server quit command initiated."; let pursIdeResponded = false; if (pursIdeProcess && pursIdeIsReady) { logToStderr("[pursIdeQuit] Attempting to send 'quit' command to purs ide server.", "debug"); sendCommandToPursIde({ command: "quit" }) .then(res => { pursIdeResponded = true; logToStderr(`[pursIdeQuit] purs ide server responded to quit command: ${JSON.stringify(res)}`, 'debug'); }) .catch(err => { logToStderr(`[pursIdeQuit] Error/No response from purs ide server for quit command: ${err.message}`, 'warn'); }); // Wait a short period to allow purs ide server to shut down gracefully // or for the sendCommandToPursIde to potentially resolve/reject. await delay(250); // Increased slightly to 250ms } else { quitMessage = "No purs ide server was running or ready to send quit command to."; logToStderr("[pursIdeQuit] " + quitMessage, "info"); } // Ensure our managed process is stopped regardless of purs ide's response if (pursIdeProcess) { logToStderr("[pursIdeQuit] Ensuring managed purs ide process is stopped.", "debug"); pursIdeProcess.kill(); pursIdeProcess = null; pursIdeIsReady = false; logPursIdeOutput("Managed purs ide server process stopped via pursIdeQuit tool.", "info"); quitMessage += " Managed purs ide process has been stopped."; } else { if (!quitMessage.includes("No purs ide server was running")) { quitMessage += " No managed purs ide process was found running to stop."; } } if (pursIdeResponded) { quitMessage += " purs ide server acknowledged quit."; } else { quitMessage += " purs ide server may not have acknowledged quit before process termination."; } return { content: [{ type: "text", text: JSON.stringify({ status_message: quitMessage, resultType: "success" }, null, 2) }] }; }, "pursIdeRebuild": async (args) => { if (!args || typeof args.file !== 'string') { throw new Error("Invalid input: 'file' (string) is required for pursIdeRebuild."); } const params = { file: args.file, actualFile: args.actualFile, codegen: args.codegen // purs ide server defaults to js if undefined }; const result = await sendCommandToPursIde({ command: "rebuild", params }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }, "pursIdeUsages": async (args) => { if (!args || typeof args.module !== 'string' || typeof args.namespace !== 'string' || typeof args.identifier !== 'string') { throw new Error("Invalid input: 'module', 'namespace', and 'identifier' (strings) are required for pursIdeUsages."); } const params = { module: args.module, namespace: args.namespace, identifier: args.identifier }; const result = await sendCommandToPursIde({ command: "usages", params }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; }, "pursIdeList": async (args) => { if (!args || typeof args.listType !== 'string') { throw new Error("Invalid input: 'listType' (string) is required for pursIdeList."); } const params = { type: args.listType }; if (args.listType === "import") { if (typeof args.file !== 'string') { throw new Error("'file' (string) is required when listType is 'import'."); } params.file = args.file; } const result = await sendCommandToPursIde({ command: "list", params }); return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; } };
  • Helper function sendCommandToPursIde used by the pursIdeReset handler to communicate the 'reset' command to the purs ide server process.
    function sendCommandToPursIde(commandPayload) { return new Promise((resolve, reject) => { if (!pursIdeProcess || !pursIdeIsReady || !pursIdeServerPort) { return reject(new Error("purs ide server is not running or not ready.")); } const client = new net.Socket(); let responseData = ''; client.connect(pursIdeServerPort, '127.0.0.1', () => { logToStderr(`[MCP Client->purs ide]: Sending command: ${JSON.stringify(commandPayload).substring(0,100)}...`, 'debug'); client.write(JSON.stringify(commandPayload) + '\n'); }); client.on('data', (data) => { responseData += data.toString(); if (responseData.includes('\n')) { const completeResponses = responseData.split('\n').filter(Boolean); responseData = ''; if (completeResponses.length > 0) { try { resolve(JSON.parse(completeResponses[0].trim())); } catch (e) { reject(new Error(`Failed to parse JSON response from purs ide: ${e.message}. Raw: ${completeResponses[0]}`)); } } client.end(); } }); client.on('end', () => { if (responseData.trim()) { try { resolve(JSON.parse(responseData.trim())); } catch (e) { reject(new Error(`Failed to parse JSON response from purs ide on end: ${e.message}. Raw: ${responseData}`));} } }); client.on('close', () => { logToStderr(`[MCP Client->purs ide]: Connection closed.`, 'debug'); }); client.on('error', (err) => reject(new Error(`TCP connection error with purs ide server: ${err.message}`))); }); }
  • index.js:1158-1164 (registration)
    The MCP 'tools/list' request handler returns the list of tools including 'pursIdeReset' schema from TOOL_DEFINITIONS.
    if (method === 'tools/list') { const toolsToExclude = ['query_purescript_ast', 'query_purs_ide']; // Keep query_purs_ide for now, for direct access if needed const filteredToolDefinitions = TOOL_DEFINITIONS.filter( tool => !toolsToExclude.includes(tool.name) ); return createSuccessResponse(id, { tools: filteredToolDefinitions }); }

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