GetTableContents
Fetch contents of ABAP database tables or CDS views for data preview. Returns rows similar to SE16/SE16N.
Instructions
[read-only] Retrieve contents (data preview) of an ABAP database table or CDS view. Returns rows of data like SE16/SE16N.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| table_name | Yes | Name of the ABAP table | |
| max_rows | No | Maximum number of rows to retrieve |
Implementation Reference
- The main handler function that executes the GetTableContents tool logic. It takes a table name (required) and max_rows (optional, default 100), calls the ADT client's getTableContents method, parses the XML response using parseSqlQueryXml, and returns the data as JSON.
export async function handleGetTableContents( context: HandlerContext, args: any, ) { const { connection, logger } = context; try { if (!args?.table_name) { throw new McpError(ErrorCode.InvalidParams, 'Table name is required'); } const tableName = args.table_name; const maxRows = args.max_rows || 100; logger?.info(`Reading table contents: ${tableName} (max_rows=${maxRows})`); const client = createAdtClient(connection, logger); const response = await client .getUtils() .getTableContents({ table_name: tableName, max_rows: maxRows }); if (response.status === 200 && response.data) { logger?.info('Table contents request completed successfully'); const parsedData = parseSqlQueryXml( response.data, `SELECT * FROM ${tableName}`, maxRows, logger, ); logger?.debug( `Parsed table data: rows=${parsedData.rows.length}/${parsedData.total_rows ?? 0}, columns=${parsedData.columns.length}`, ); return { isError: false, content: [ { type: 'text', text: JSON.stringify(parsedData, null, 2), }, ], }; } else { throw new McpError( ErrorCode.InternalError, `Failed to read table contents. Status: ${response.status}`, ); } } catch (error) { logger?.error('Failed to read table contents', error as any); return { isError: true, content: [ { type: 'text', text: `ADT error: ${String(error)}`, }, ], }; } } - Tool definition (name, description, input schema) for GetTableContents. Defines the tool name, availability ('onprem', 'cloud'), and input schema with table_name (string, required) and max_rows (number, optional).
export const TOOL_DEFINITION = { name: 'GetTableContents', available_in: ['onprem', 'cloud'] as const, description: '[read-only] Retrieve contents (data preview) of an ABAP database table or CDS view. Returns rows of data like SE16/SE16N.', inputSchema: { table_name: z.string().describe('Name of the ABAP table'), max_rows: z .number() .optional() .describe('Maximum number of rows to retrieve'), }, } as const; - src/lib/handlers/groups/ReadOnlyHandlersGroup.ts:146-152 (registration)Registration of GetTableContents in the ReadOnlyHandlersGroup. The tool definition and handler are wired together in the getAllEntries() method, binding the handler to this.context.
private getAllEntries(): HandlerEntry[] { return [ // Existing readonly handlers { toolDefinition: GetTableContents_Tool, handler: (args: any) => handleGetTableContents(this.context, args), }, - src/lib/handlers/groups/ReadOnlyHandlersGroup.ts:85-88 (registration)Import of TOOL_DEFINITION (as GetTableContents_Tool) and handleGetTableContents from the handler file.
import { TOOL_DEFINITION as GetTableContents_Tool, handleGetTableContents, } from '../../../handlers/table/readonly/handleGetTableContents'; - The parseSqlQueryXml helper function used by handleGetTableContents to parse SAP ADT XML responses (column metadata + row data) into a structured JSON format.
export function parseSqlQueryXml( xmlData: string, sqlQuery: string, rowNumber: number, logger?: ILogger, ): SqlQueryResponse { try { // Extract basic information const totalRowsMatch = xmlData.match( /<dataPreview:totalRows>(\d+)<\/dataPreview:totalRows>/, ); const totalRows = totalRowsMatch ? parseInt(totalRowsMatch[1], 10) : 0; const queryTimeMatch = xmlData.match( /<dataPreview:queryExecutionTime>([\d.]+)<\/dataPreview:queryExecutionTime>/, ); const queryExecutionTime = queryTimeMatch ? parseFloat(queryTimeMatch[1]) : 0; // Extract column metadata const columns: Array<{ name: string; type: string; description?: string; length?: number; }> = []; const columnMatches = xmlData.match(/<dataPreview:metadata[^>]*>/g); if (columnMatches) { columnMatches.forEach((match) => { const nameMatch = match.match(/dataPreview:name="([^"]+)"/); const typeMatch = match.match(/dataPreview:type="([^"]+)"/); const descMatch = match.match(/dataPreview:description="([^"]+)"/); const lengthMatch = match.match(/dataPreview:length="(\d+)"/); if (nameMatch) { columns.push({ name: nameMatch[1], type: typeMatch ? typeMatch[1] : 'UNKNOWN', description: descMatch ? descMatch[1] : '', length: lengthMatch ? parseInt(lengthMatch[1], 10) : undefined, }); } }); } // Extract row data const rows: Array<Record<string, any>> = []; // Find all column sections const columnSections = xmlData.match( /<dataPreview:columns>.*?<\/dataPreview:columns>/gs, ); if (columnSections && columnSections.length > 0) { // Extract data for each column const columnData: Record<string, (string | null)[]> = {}; columnSections.forEach((section, index) => { if (index < columns.length) { const columnName = columns[index].name; const dataMatches = section.match( /<dataPreview:data[^>]*>(.*?)<\/dataPreview:data>/g, ); if (dataMatches) { columnData[columnName] = dataMatches.map((match) => { const content = match.replace(/<[^>]+>/g, ''); return content || null; }); } else { columnData[columnName] = []; } } }); // Convert column-based data to row-based data const maxRowCount = Math.max( ...Object.values(columnData).map((arr) => arr.length), 0, ); for (let rowIndex = 0; rowIndex < maxRowCount; rowIndex++) { const row: Record<string, any> = {}; columns.forEach((column) => { const columnValues = columnData[column.name] || []; row[column.name] = columnValues[rowIndex] || null; }); rows.push(row); } } return { sql_query: sqlQuery, row_number: rowNumber, execution_time: queryExecutionTime, total_rows: totalRows, columns, rows, }; } catch (parseError) { logger?.error('Failed to parse SQL query XML:', parseError as any); // Return basic structure on parse error return { sql_query: sqlQuery, row_number: rowNumber, columns: [], rows: [], error: 'Failed to parse XML response', } as any; } }