Skip to main content
Glama
Raistlin82

SAP OData to MCP Server

by Raistlin82

discover-service-entities

Explore SAP service entities and their capabilities to understand available data for querying, creating, updating, or deleting operations within SAP systems.

Instructions

List all entities and their capabilities within a specific SAP service. Use this after finding a service to understand what data you can work with.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
serviceIdYesThe SAP service ID to explore
showCapabilitiesNoShow CRUD capabilities for each entity

Implementation Reference

  • Input validation schema for 'discover-service-entities' tool, defining serviceId (required), entityPattern (optional regex filter), includeProperties and includeNavigations (optional booleans). Used for safe input sanitization.
    export const EntityDiscoverySchema = z.object({ serviceId: serviceName, entityPattern: z .string() .min(1) .max(200) .regex(/^[a-zA-Z0-9_\-*?.[\]]*$/, 'Invalid entity pattern') .optional(), includeProperties: z.boolean().optional(), includeNavigations: z.boolean().optional(), });
  • ValidationSchemas object maps tool names to their Zod schemas, including 'discover-service-entities': EntityDiscoverySchema. Used by validateMCPToolInput function.
    export const ValidationSchemas = { // Discovery tools 'search-sap-services': ServiceDiscoverySchema, 'discover-service-entities': EntityDiscoverySchema, 'get-entity-schema': EntitySchemaSchema, // CRUD operation tools 'execute-entity-operation': EntityOperationSchema, 'sap-odata-read': EntityReadSchema, 'sap-odata-create': EntityCreateSchema, 'sap-odata-update': EntityUpdateSchema, 'sap-odata-delete': EntityDeleteSchema, // System tools 'sap-health-check': HealthCheckSchema, 'sap-system-info': SystemInfoSchema, } as const;
  • getServiceMetadata fetches and parses service $metadata to extract entityTypes array, which is core logic for discovering entities in a service. Likely called by the tool handler.
    private async getServiceMetadata(service: ODataService): Promise<ServiceMetadata> { try { const destination = await this.sapClient.getDestination({ type: 'design-time', operation: 'discovery', }); const response = await executeHttpRequest(destination, { method: 'GET', url: service.metadataUrl, headers: { Accept: 'application/xml', }, }); return this.parseMetadata(response.data, service.odataVersion); } catch (error) { this.logger.error(`Failed to get metadata for service ${service.id}:`, error); throw error; } } private parseMetadata(metadataXml: string, odataVersion: string): ServiceMetadata {
  • Registers sap-service-metadata resource that provides entity list for serviceId via sap://service/{serviceId}/metadata URI. Provides entity discovery data, possibly used alongside or by the tool.
    // Workflow guide removed - now using routing-rules.json in hierarchical-tool-registry this.mcpServer.registerResource( 'sap-service-metadata', new ResourceTemplate('sap://service/{serviceId}/metadata', { list: undefined }), { title: 'SAP Service Metadata', description: 'Metadata information for SAP OData services', }, async (uri, variables) => { // variables: Record<string, unknown> from SDK const serviceId = typeof variables.serviceId === 'string' ? variables.serviceId : ''; const service = this.discoveredServices.find(s => s.id === serviceId); if (!service) { throw new Error(`Service not found: ${serviceId}`); } return { contents: [ { uri: uri.href, text: JSON.stringify( { service: { id: service.id, title: service.title, description: service.description, url: service.url, version: service.version, }, entities: service.metadata?.entityTypes?.map(entity => ({ name: entity.name, entitySet: entity.entitySet, properties: entity.properties, keys: entity.keys, operations: { creatable: entity.creatable, updatable: entity.updatable, deletable: entity.deletable, }, })) || [], }, null, 2 ), mimeType: 'application/json', }, ], }; } ); this.mcpServer.registerResource(
  • Parses $metadata XML to extract detailed EntityType information (name, properties, keys, navigations), essential for the tool's output.
    xmlDoc: Document, entitySets: Array<{ [key: string]: string | null }> ): EntityType[] { const entityTypes: EntityType[] = []; const nodes = xmlDoc.querySelectorAll('EntityType'); nodes.forEach((node: Element) => { const entitySet = entitySets.find( entitySet => entitySet.entitytype?.split('.')[1] === node.getAttribute('Name') ); const entityType: EntityType = { name: node.getAttribute('Name') || '', namespace: node.parentElement?.getAttribute('Namespace') || '', entitySet: entitySet?.name, creatable: entitySet?.creatable?.toLowerCase() === 'true', updatable: entitySet?.updatable?.toLowerCase() === 'true', deletable: entitySet?.deletable?.toLowerCase() === 'true', addressable: entitySet?.addressable?.toLowerCase() === 'true', properties: [], navigationProperties: [], keys: [], }; // Extract properties const propNodes = node.querySelectorAll('Property'); propNodes.forEach((propNode: Element) => { entityType.properties.push({ name: propNode.getAttribute('Name') || '', type: propNode.getAttribute('Type') || '', nullable: propNode.getAttribute('Nullable') !== 'false', maxLength: propNode.getAttribute('MaxLength') ?? undefined, }); }); // Extract keys const keyNodes = node.querySelectorAll('Key PropertyRef'); keyNodes.forEach((keyNode: Element) => { entityType.keys.push(keyNode.getAttribute('Name') || ''); }); entityTypes.push(entityType); }); return entityTypes; }

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/Raistlin82/btp-sap-odata-to-mcp-server-optimized'

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