Skip to main content
Glama
utils.py4.9 kB
"""Utility functions for jons-mcp-java.""" import hashlib from pathlib import Path from urllib.parse import quote, unquote, urlparse def path_to_uri(path: str | Path) -> str: """Convert a file path to a file:// URI.""" if isinstance(path, str): path = Path(path) # Resolve to absolute path path = path.resolve() # Convert to URI format # On Unix, paths start with / so we get file:/// path_str = str(path) if not path_str.startswith("/"): path_str = "/" + path_str # Quote special characters but preserve / encoded = quote(path_str, safe="/") return f"file://{encoded}" def uri_to_path(uri: str) -> Path: """Convert a file:// URI to a file path.""" parsed = urlparse(uri) if parsed.scheme != "file": raise ValueError(f"Expected file:// URI, got: {uri}") # Decode percent-encoded characters path_str = unquote(parsed.path) return Path(path_str) def format_locations(response: dict | list | None) -> dict: """ Normalize LSP Location response to a consistent format. LSP methods like definition can return: - null/None - Single Location object - Array of Location objects - Array of LocationLink objects This normalizes to: {"locations": [...]} """ if response is None: return {"locations": []} if isinstance(response, dict): # Single Location or LocationLink return {"locations": [_normalize_location(response)]} if isinstance(response, list): return {"locations": [_normalize_location(loc) for loc in response]} return {"locations": []} def _normalize_location(loc: dict) -> dict: """Normalize a Location or LocationLink to a common format.""" # LocationLink has targetUri/targetRange, Location has uri/range if "targetUri" in loc: # LocationLink uri = loc["targetUri"] range_obj = loc.get("targetSelectionRange") or loc.get("targetRange", {}) else: # Location uri = loc.get("uri", "") range_obj = loc.get("range", {}) # Convert URI to path for easier consumption try: path = str(uri_to_path(uri)) except ValueError: path = uri start = range_obj.get("start", {}) end = range_obj.get("end", {}) return { "path": path, "uri": uri, "line": start.get("line", 0), "character": start.get("character", 0), "end_line": end.get("line", 0), "end_character": end.get("character", 0), } def get_workspace_data_dir(project_root: Path) -> Path: """ Generate unique workspace data directory for a project. Each JDT.LS instance requires its own -data directory. We hash the project path to avoid conflicts. """ path_hash = hashlib.md5(str(project_root.resolve()).encode()).hexdigest()[:8] project_name = project_root.name return Path.home() / ".cache" / "jdtls-workspaces" / f"{project_name}-{path_hash}" def format_symbol(symbol: dict, include_children: bool = True) -> dict: """Format a DocumentSymbol or SymbolInformation to a common format.""" # DocumentSymbol has range, SymbolInformation has location if "location" in symbol: # SymbolInformation loc = symbol["location"] uri = loc.get("uri", "") range_obj = loc.get("range", {}) try: path = str(uri_to_path(uri)) except ValueError: path = uri else: # DocumentSymbol (no path, just range within current file) path = None range_obj = symbol.get("range", {}) start = range_obj.get("start", {}) result = { "name": symbol.get("name", ""), "kind": symbol.get("kind", 0), "kind_name": SYMBOL_KINDS.get(symbol.get("kind", 0), "Unknown"), "line": start.get("line", 0), "character": start.get("character", 0), } if path: result["path"] = path # Include container name if present (SymbolInformation) if "containerName" in symbol: result["container"] = symbol["containerName"] # Include children if present (DocumentSymbol hierarchy) if include_children and "children" in symbol: result["children"] = [ format_symbol(child, include_children=True) for child in symbol["children"] ] return result # LSP Symbol kinds (from LSP spec) SYMBOL_KINDS = { 1: "File", 2: "Module", 3: "Namespace", 4: "Package", 5: "Class", 6: "Method", 7: "Property", 8: "Field", 9: "Constructor", 10: "Enum", 11: "Interface", 12: "Function", 13: "Variable", 14: "Constant", 15: "String", 16: "Number", 17: "Boolean", 18: "Array", 19: "Object", 20: "Key", 21: "Null", 22: "EnumMember", 23: "Struct", 24: "Event", 25: "Operator", 26: "TypeParameter", }

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/jonmmease/jons-mcp-java'

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