Skip to main content
Glama
usage_references.py6.47 kB
# tools/usage_references.py import xmltodict from typing import Optional, Dict, List from requests.exceptions import HTTPError, RequestException from .utils import AdtError, make_session, SAP_URL, SAP_CLIENT # JSON schema for Gemini function-calling get_usage_references_definition = { "name": "get_usage_references", "description": ( "Retrieve where-used references for an ABAP object " "(class, program, include, function_module, interface, table, structure). " "By default, it looks at the very first character of the source." ), "parameters": { "type": "object", "properties": { "object_type": { "type": "string", "description": ( "One of: 'class','program','include','function_module'," "'interface','table','structure'" ) }, "object_name": { "type": "string", "description": "The ABAP object name, e.g. 'ZMY_CLASS'" }, "function_group": { "type": "string", "description": "Function group name (required if object_type is 'function_module')" }, "start_position": { "type": "object", "description": "Starting position in code (row, col). Defaults to {row:1,col:0}.", "properties": { "row": {"type": "integer"}, "col": {"type": "integer"} }, "default": {"row": 1, "col": 0} }, "end_position": { "type": "object", "description": "Optional end position in code (row, col).", "properties": { "row": {"type": "integer"}, "col": {"type": "integer"} } } }, "required": ["object_type", "object_name"] } } def _build_source_path( object_type: str, object_name: str, function_group: Optional[str] ) -> str: # Normalize ADT codes to our categories ot = object_type.lower() # accept raw ADT codes or semantic names if ot.startswith("clas"): ot = "class" elif ot.startswith("prog/p"): ot = "program" elif ot.startswith("prog/i"): ot = "include" elif ot.startswith("intf"): ot = "interface" elif ot.startswith("tabl"): ot = "table" elif ot.startswith("ttyp"): ot = "structure" elif ot.startswith("function_module") or ot.startswith("fu") or ot.startswith("func"): ot = "function_module" if ot == "class": return f"/sap/bc/adt/oo/classes/{object_name}/source/main" if ot == "program": return f"/sap/bc/adt/programs/programs/{object_name}/source/main" if ot == "include": return f"/sap/bc/adt/programs/includes/{object_name}/source/main" if ot == "interface": return f"/sap/bc/adt/oo/interfaces/{object_name}/source/main" if ot == "table": return f"/sap/bc/adt/ddic/tables/{object_name}/source/main" if ot == "structure": return f"/sap/bc/adt/ddic/structures/{object_name}/source/main" if ot == "function_module": if not function_group: raise ValueError("function_group is required for 'function_module'") return ( f"/sap/bc/adt/functions/groups/{function_group}" f"/fmodules/{object_name}/source/main" ) raise ValueError(f"Unsupported object_type: {object_type}") def _fetch_csrf_token(session, full_url: str) -> str: resp = session.get( full_url, params={"sap-client": SAP_CLIENT}, headers={"X-CSRF-Token": "Fetch", "Accept": "text/plain"} ) try: resp.raise_for_status() except HTTPError as e: raise AdtError(resp.status_code, resp.text) from e token = resp.headers.get("X-CSRF-Token") if not token: raise AdtError(resp.status_code, "Missing CSRF token") return token def get_usage_references( object_type: str, object_name: str, function_group: Optional[str] = None, start_position: Optional[Dict[str,int]] = None, end_position: Optional[Dict[str,int]] = None ) -> List[Dict[str,str]]: print(f"Fetching usage references for {object_type}/{object_name}") # default to beginning of file if start_position is None: start_position = {"row": 1, "col": 0} r, c = start_position.get("row"), start_position.get("col") session = make_session() # Build the source path & CSRF token src_path = _build_source_path(object_type, object_name, function_group) print(f"Source path: {src_path}") full_src = f"{SAP_URL.rstrip('/')}{src_path}" token = _fetch_csrf_token(session, full_src) # Build fragment & URI param frag = f"start={r},{c}" if end_position: frag += f";end={end_position['row']},{end_position['col']}" uri_param = f"{src_path}?version=active#{frag}" # Prepare POST endpoint = f"{SAP_URL.rstrip('/')}/sap/bc/adt/repository/informationsystem/usageReferences" headers = { "X-CSRF-Token": token, "Accept": "application/vnd.sap.adt.repository.usagereferences.result.v1+xml", "Content-Type": "application/vnd.sap.adt.repository.usagereferences.request.v1+xml" } body = ( '<?xml version="1.0" encoding="utf-8"?>' '<usageReferenceRequest xmlns="http://www.sap.com/adt/ris/usageReferences"/>' ) resp = session.post( endpoint, params={"sap-client": SAP_CLIENT, "uri": uri_param}, headers=headers, data=body ) print(resp) try: resp.raise_for_status() except HTTPError as e: raise AdtError(resp.status_code, resp.text) from e # Parse XML doc = xmltodict.parse(resp.text) root = doc.get("usageReferences:usageReferenceResult", {}) ro = root.get("usageReferences:referencedObjects") if not ro: return [] raw = ro.get("usageReferences:referencedObject", []) entries = raw if isinstance(raw, list) else [raw] result: List[Dict[str,str]] = [] for n in entries: adt = n.get("usageReferences:adtObject") or {} result.append({ "name": adt.get("@adtcore:name", ""), "type": adt.get("@adtcore:type", ""), "uri": n.get("@uri", "") }) return result

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/YahorNovik/mcp-adt'

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