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