sap_docs_get
Retrieve full SAP documentation, UI5 control API details, wdi5 testing guides, or SAP Community posts using specific IDs. Works with library, document, or community post IDs to provide targeted content for developers.
Instructions
Retrieve specific SAP documentation, UI5 control API details, wdi5 testing docs, or SAP Community posts. Use the IDs returned from sap_docs_search or sap_community_search to get full content. Works with library IDs, document IDs, and community post IDs (e.g., 'community-12345').
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| library_id | Yes | Library or document ID from sap_docs_search results. Can be a library ID like '/sapui5', '/cap', '/wdi5', '/openui5-api', '/cloud-mta-build-tool', '/ui5-tooling', '/ui5-webcomponents', '/cloud-sdk-js', '/cloud-sdk-java', '/cloud-sdk-ai-js', '/cloud-sdk-ai-java' for general docs, or a specific document ID like '/openui5-api/sap/m/Button' for detailed control API documentation, or '/wdi5/authentication' for testing docs. For community posts, use IDs like 'community-12345' from sap_community_search results. | |
| topic | No | Optional topic filter to narrow down results within a library. Examples: 'properties', 'events', 'methods', 'aggregations', 'routing', 'authentication', 'annotations', 'testing', 'locators', 'pageObjects'. Most useful with library IDs rather than specific document IDs. |
Implementation Reference
- src/lib/BaseServerHandler.ts:544-603 (handler)Dispatch handler specifically for 'sap_docs_get' (legacy alias for 'fetch'). Extracts parameters, calls fetchLibraryDocumentation from localDocs.ts, handles errors and logging, returns formatted ChatGPT-compatible response.if (name === "sap_docs_get" || name === "fetch") { // Handle both old format (library_id) and new ChatGPT format (id) const library_id = (args as any).library_id || (args as any).id; const topic = (args as any).topic || ""; if (!library_id) { const timing = logger.logToolStart(name, 'missing_id', clientMetadata); logger.logToolError(name, timing.requestId, timing.startTime, new Error('Missing id parameter')); return createErrorResponse( `Missing required parameter: id. Please provide a document ID from search results.`, timing.requestId ); } // Enhanced logging with timing const searchKey = library_id + (topic ? `/${topic}` : ''); const timing = logger.logToolStart(name, searchKey, clientMetadata); try { const text = await fetchLibraryDocumentation(library_id, topic); if (!text) { logger.logToolSuccess(name, timing.requestId, timing.startTime, 0); return createErrorResponse( `Nothing found for ${library_id}`, timing.requestId ); } // Transform document content to ChatGPT-compatible format const config = getDocUrlConfig(library_id); const docUrl = config ? generateDocumentationUrl(library_id, '', text, config) : null; const document: DocumentResult = { id: library_id, title: library_id.replace(/^\//, '').replace(/\//g, ' > ') + (topic ? ` (${topic})` : ''), text: text, url: docUrl || `#${library_id}`, metadata: { source: 'sap-docs', library: library_id, topic: topic || undefined, contentLength: text.length } }; logger.logToolSuccess(name, timing.requestId, timing.startTime, 1, { contentLength: text.length, libraryId: library_id, topic: topic || undefined }); return createDocumentResponse(document); } catch (error) { logger.logToolError(name, timing.requestId, timing.startTime, error); return createErrorResponse( `Error retrieving documentation for ${library_id}. Please try again later.`, timing.requestId ); } }
- src/lib/localDocs.ts:1392-1699 (handler)Core execution logic for fetching SAP documentation. Loads index, resolves library ID and topic to source files, reads content, applies library-specific formatting (JSDoc extraction for UI5 API, sample formatting, URL generation), returns markdown content.export async function fetchLibraryDocumentation( libraryIdOrDocId: string, topic = "" ): Promise<string | null> { // Check if this is a community post ID if (libraryIdOrDocId.startsWith('community-')) { return await getCommunityPost(libraryIdOrDocId); } const index = await loadIndex(); // Check if this is a specific document ID const allDocs: Array<{lib: any, doc: any}> = []; for (const lib of Object.values(index)) { for (const doc of lib.docs) { allDocs.push({ lib, doc }); // Try exact match first (for section documents with fragments) if (doc.id === libraryIdOrDocId) { const sourcePath = getSourcePath(lib.id); if (!sourcePath) { throw new Error(`Unknown library ID: ${lib.id}`); } const abs = path.join(PROJECT_ROOT, "sources", sourcePath, doc.relFile); const content = await fs.readFile(abs, "utf8"); // For JavaScript API files, format the content for better readability if (doc.relFile && doc.relFile.endsWith('.js') && lib.id === '/openui5-api') { return formatJSDocContent(content, doc.title || ''); } // For sample files, format them appropriately else if (lib.id === '/openui5-samples') { return formatSampleContent(content, doc.relFile, doc.title || ''); } // For documented libraries, add URL context else if (getDocUrlConfig(lib.id)) { const documentationUrl = generateDocumentationUrl(lib.id, doc.relFile, content); const libName = lib.id.replace('/', '').toUpperCase(); return `**Source:** ${libName} Documentation **URL:** ${documentationUrl || 'Documentation URL not available'} **File:** ${doc.relFile} --- ${content} --- *This content is from the ${libName} documentation. Visit the URL above for the latest version and interactive examples.*`; } else { return content; } } } } // If no exact match found and the ID contains a fragment, try stripping the fragment // This handles cases where fragments are used for navigation but don't exist as separate documents if (libraryIdOrDocId.includes('#')) { const baseId = libraryIdOrDocId.split('#')[0]; // Try to find a document with the base ID (without fragment) for (const lib of Object.values(index)) { for (const doc of lib.docs) { if (doc.id === baseId) { const sourcePath = getSourcePath(lib.id); if (!sourcePath) { throw new Error(`Unknown library ID: ${lib.id}`); } const abs = path.join(PROJECT_ROOT, "sources", sourcePath, doc.relFile); const content = await fs.readFile(abs, "utf8"); // For JavaScript API files, format the content for better readability if (doc.relFile && doc.relFile.endsWith('.js') && lib.id === '/openui5-api') { return formatJSDocContent(content, doc.title || ''); } // For sample files, format them appropriately else if (lib.id === '/openui5-samples') { return formatSampleContent(content, doc.relFile, doc.title || ''); } // For documented libraries, add URL context else if (getDocUrlConfig(lib.id)) { const documentationUrl = generateDocumentationUrl(lib.id, doc.relFile, content); const libName = lib.id.replace('/', '').toUpperCase(); return `**Source:** ${libName} Documentation **URL:** ${documentationUrl || 'Documentation URL not available'} **File:** ${doc.relFile} --- ${content} --- *This content is from the ${libName} documentation. Visit the URL above for the latest version and interactive examples.*`; } else { return content; } } } } // Try library-level lookup with base ID const baseLib = index[baseId]; if (baseLib) { const term = topic.toLowerCase(); const targets = term ? baseLib.docs.filter( (d) => d.title.toLowerCase().includes(term) || d.description.toLowerCase().includes(term) ) : baseLib.docs; if (!targets.length) return `No topic "${topic}" found inside ${baseId}.`; const parts: string[] = []; for (const doc of targets) { const sourcePath = getSourcePath(baseLib.id); if (!sourcePath) { throw new Error(`Unknown library ID: ${baseLib.id}`); } const abs = path.join(PROJECT_ROOT, "sources", sourcePath, doc.relFile); const content = await fs.readFile(abs, "utf8"); // For JavaScript API files, format the content for better readability if (doc.relFile && doc.relFile.endsWith('.js') && baseLib.id === '/openui5-api') { const formattedContent = formatJSDocContent(content, doc.title || ''); parts.push(formattedContent); } // For sample files, format them appropriately else if (baseLib.id === '/openui5-samples') { const formattedContent = formatSampleContent(content, doc.relFile, doc.title || ''); parts.push(formattedContent); } // For documented libraries, add URL context else if (getDocUrlConfig(baseLib.id)) { const documentationUrl = generateDocumentationUrl(baseLib.id, doc.relFile, content); const libName = baseLib.id.replace('/', '').toUpperCase(); const formattedContent = `**Source:** ${libName} Documentation **URL:** ${documentationUrl || 'Documentation URL not available'} **File:** ${doc.relFile} --- ${content} --- *This content is from the ${libName} documentation. Visit the URL above for the latest version and interactive examples.*`; parts.push(formattedContent); } else { parts.push(content); } } return parts.join("\n\n---\n\n"); } } // If not a specific document ID, treat as library ID with optional topic const lib = index[libraryIdOrDocId]; if (!lib) return null; // If topic is provided, first try to construct the full document ID if (topic) { const fullDocId = `${libraryIdOrDocId}/${topic}`; // Try to find exact document match first for (const doc of lib.docs) { if (doc.id === fullDocId) { const sourcePath = getSourcePath(lib.id); if (!sourcePath) { throw new Error(`Unknown library ID: ${lib.id}`); } const abs = path.join(PROJECT_ROOT, "sources", sourcePath, doc.relFile); const content = await fs.readFile(abs, "utf8"); // Format the content appropriately based on library type if (doc.relFile && doc.relFile.endsWith('.js') && lib.id === '/openui5-api') { return formatJSDocContent(content, doc.title || ''); } else if (lib.id === '/openui5-samples') { return formatSampleContent(content, doc.relFile, doc.title || ''); } else if (getDocUrlConfig(lib.id)) { const documentationUrl = generateDocumentationUrl(lib.id, doc.relFile, content); const libName = lib.id.replace('/', '').toUpperCase(); return `**Source:** ${libName} Documentation **URL:** ${documentationUrl || 'Documentation URL not available'} **File:** ${doc.relFile} --- ${content} --- *This content is from the ${libName} documentation. Visit the URL above for the latest version and interactive examples.*`; } else { return content; } } } // If exact match not found, fall back to topic keyword search const term = topic.toLowerCase(); const targets = lib.docs.filter( (d) => d.title.toLowerCase().includes(term) || d.description.toLowerCase().includes(term) ); if (targets.length > 0) { // Process the filtered documents const parts: string[] = []; for (const doc of targets) { const sourcePath = getSourcePath(lib.id); if (!sourcePath) { throw new Error(`Unknown library ID: ${lib.id}`); } const abs = path.join(PROJECT_ROOT, "sources", sourcePath, doc.relFile); const content = await fs.readFile(abs, "utf8"); if (doc.relFile && doc.relFile.endsWith('.js') && lib.id === '/openui5-api') { const formattedContent = formatJSDocContent(content, doc.title || ''); parts.push(formattedContent); } else if (lib.id === '/openui5-samples') { const formattedContent = formatSampleContent(content, doc.relFile, doc.title || ''); parts.push(formattedContent); } else if (getDocUrlConfig(lib.id)) { const documentationUrl = generateDocumentationUrl(lib.id, doc.relFile, content); const libName = lib.id.replace('/', '').toUpperCase(); const formattedContent = `**Source:** ${libName} Documentation **URL:** ${documentationUrl || 'Documentation URL not available'} **File:** ${doc.relFile} --- ${content} --- *This content is from the ${libName} documentation. Visit the URL above for the latest version and interactive examples.*`; parts.push(formattedContent); } else { parts.push(content); } } return parts.join("\n\n---\n\n"); } return `No topic "${topic}" found inside ${libraryIdOrDocId}.`; } // No topic provided, return all library documents const targets = lib.docs; if (!targets.length) return `No documents found inside ${libraryIdOrDocId}.`; const parts: string[] = []; for (const doc of targets) { const sourcePath = getSourcePath(lib.id); if (!sourcePath) { throw new Error(`Unknown library ID: ${lib.id}`); } const abs = path.join(PROJECT_ROOT, "sources", sourcePath, doc.relFile); const content = await fs.readFile(abs, "utf8"); // For JavaScript API files, format the content for better readability if (doc.relFile && doc.relFile.endsWith('.js') && lib.id === '/openui5-api') { const formattedContent = formatJSDocContent(content, doc.title || ''); parts.push(formattedContent); } // For sample files, format them appropriately else if (lib.id === '/openui5-samples') { const formattedContent = formatSampleContent(content, doc.relFile, doc.title || ''); parts.push(formattedContent); } // For documented libraries, add URL context else if (getDocUrlConfig(lib.id)) { const documentationUrl = generateDocumentationUrl(lib.id, doc.relFile, content); const libName = lib.id.replace('/', '').toUpperCase(); const formattedContent = `**Source:** ${libName} Documentation **URL:** ${documentationUrl || 'Documentation URL not available'} **File:** ${doc.relFile} --- ${content} --- *This content is from the ${libName} documentation. Visit the URL above for the latest version and interactive examples.*`; parts.push(formattedContent); } else { parts.push(content); } } return parts.join("\n\n---\n\n"); }
- src/lib/BaseServerHandler.ts:360-389 (schema)Input schema definition for 'fetch' tool, which 'sap_docs_get' uses as legacy alias. Defines required 'id' parameter (maps to library_id).name: "fetch", description: `GET SPECIFIC DOCS: fetch(id="result_id") FUNCTION NAME: fetch RETRIEVES: Full content from search results WORKS WITH: Document IDs returned by search ChatGPT COMPATIBLE: • Uses "id" parameter (required by ChatGPT) • Returns structured JSON content • Includes full document text and metadata`, inputSchema: { type: "object", properties: { id: { type: "string", description: "Unique document ID from search results. Use exact IDs returned by search.", examples: [ "/cap/guides/domain-modeling", "/sapui5/controls/button-properties", "/openui5-api/sap/m/Button", "/abap-docs-758/inline-declarations", "community-12345" ] } }, required: ["id"] } },
- src/lib/BaseServerHandler.ts:222-391 (registration)Tool registration in ListTools handler. 'fetch' is explicitly registered (sap_docs_get handled as alias in CallTool handler).srv.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "sap_community_search", description: `SEARCH SAP COMMUNITY: sap_community_search(query="search terms") FUNCTION NAME: sap_community_search (or mcp_sap-docs-remote_sap_community_search) FINDS: Blog posts, discussions, solutions from SAP Community INCLUDES: Engagement data (kudos), ranked by "Best Match" TYPICAL WORKFLOW: 1. sap_community_search(query="your problem + error code") 2. fetch(id="community-12345") for full posts BEST FOR TROUBLESHOOTING: • Include error codes: "415 error", "500 error" • Be specific: "CAP action binary upload 415" • Use real scenarios: "wizard implementation issues"`, inputSchema: { type: "object", properties: { query: { type: "string", description: "Search terms for SAP Community. Include error codes and specific technical details.", examples: [ "CAP action parameter binary file upload 415 error", "wizard implementation best practices", "fiori elements authentication", "UI5 deployment issues", "wdi5 test automation problems" ] } }, required: ["query"] } }, { name: "sap_help_search", description: `SEARCH SAP HELP PORTAL: sap_help_search(query="product + topic") FUNCTION NAME: sap_help_search (or mcp_sap-docs-remote_sap_help_search) SEARCHES: Official SAP Help Portal (help.sap.com) COVERS: Product guides, implementation guides, technical documentation TYPICAL WORKFLOW: 1. sap_help_search(query="product name + configuration topic") 2. sap_help_get(result_id="sap-help-12345abc") BEST PRACTICES: • Include product names: "S/4HANA", "BTP", "Fiori" • Add specific tasks: "configuration", "setup", "deployment" • Use official SAP terminology`, inputSchema: { type: "object", properties: { query: { type: "string", description: "Search terms for SAP Help Portal. Include product names and specific topics.", examples: [ "S/4HANA configuration", "Fiori Launchpad setup", "BTP integration", "ABAP development guide", "SAP Analytics Cloud setup" ] } }, required: ["query"] } }, { name: "sap_help_get", description: `GET SAP HELP PAGE: sap_help_get(result_id="sap-help-12345abc") FUNCTION NAME: sap_help_get (or mcp_sap-docs-remote_sap_help_get) RETRIEVES: Complete SAP Help Portal page content REQUIRES: Exact result_id from sap_help_search USAGE PATTERN: 1. Get ID from sap_help_search results 2. Use exact ID (don't modify the format) 3. Receive full page content + metadata`, inputSchema: { type: "object", properties: { result_id: { type: "string", description: "Exact ID from sap_help_search results. Copy the ID exactly as returned.", examples: [ "sap-help-12345abc", "sap-help-98765def" ] } }, required: ["result_id"] } }, { name: "search", description: `SEARCH SAP DOCS: search(query="search terms") FUNCTION NAME: search COVERS: ABAP (all versions), UI5, CAP, wdi5, OpenUI5 APIs, Cloud SDK AUTO-DETECTS: ABAP versions from query (e.g. "LOOP 7.57", defaults to 7.58) TYPICAL WORKFLOW: 1. search(query="your search terms") 2. fetch(id="result_id_from_step_1") QUERY TIPS: • Be specific: "CAP action binary parameter" not just "CAP" • Include error codes: "415 error CAP action" • Use technical terms: "LargeBinary MediaType XMLHttpRequest" • For ABAP: Include version like "7.58" or "latest"`, inputSchema: { type: "object", properties: { query: { type: "string", description: "Search terms using natural language. Be specific and include technical terms.", examples: [ "CAP binary data LargeBinary MediaType", "UI5 button properties", "wdi5 testing locators", "ABAP SELECT statements 7.58", "415 error CAP action parameter" ] } }, required: ["query"] } }, { name: "fetch", description: `GET SPECIFIC DOCS: fetch(id="result_id") FUNCTION NAME: fetch RETRIEVES: Full content from search results WORKS WITH: Document IDs returned by search ChatGPT COMPATIBLE: • Uses "id" parameter (required by ChatGPT) • Returns structured JSON content • Includes full document text and metadata`, inputSchema: { type: "object", properties: { id: { type: "string", description: "Unique document ID from search results. Use exact IDs returned by search.", examples: [ "/cap/guides/domain-modeling", "/sapui5/controls/button-properties", "/openui5-api/sap/m/Button", "/abap-docs-758/inline-declarations", "community-12345" ] } }, required: ["id"] } }, ]
- src/lib/localDocs.ts:158-246 (helper)Helper function for formatting OpenUI5 API JavaScript files into readable markdown with extracted JSDoc sections.function formatJSDocContent(content: string, controlName: string): string { const lines = content.split('\n'); const result: string[] = []; result.push(`# ${controlName} - OpenUI5 Control API`); result.push(''); // Extract main JSDoc comment const mainJSDocMatch = content.match(/\/\*\*\s*([\s\S]*?)\*\//); if (mainJSDocMatch) { const cleanDoc = mainJSDocMatch[1] .split('\n') .map(line => line.replace(/^\s*\*\s?/, '')) .join('\n') .trim(); result.push('## Description'); result.push(''); result.push(cleanDoc); result.push(''); } // Extract metadata section const metadataMatch = content.match(/metadata\s*:\s*\{([\s\S]*?)\n\s*\}/); if (metadataMatch) { result.push('## Control Metadata'); result.push(''); result.push('```javascript'); result.push('metadata: {'); result.push(metadataMatch[1]); result.push('}'); result.push('```'); result.push(''); } // Extract properties const propertiesMatch = content.match(/properties\s*:\s*\{([\s\S]*?)\n\s*\}/); if (propertiesMatch) { result.push('## Properties'); result.push(''); result.push('```javascript'); result.push(propertiesMatch[1]); result.push('```'); result.push(''); } // Extract events const eventsMatch = content.match(/events\s*:\s*\{([\s\S]*?)\n\s*\}/); if (eventsMatch) { result.push('## Events'); result.push(''); result.push('```javascript'); result.push(eventsMatch[1]); result.push('```'); result.push(''); } // Extract aggregations const aggregationsMatch = content.match(/aggregations\s*:\s*\{([\s\S]*?)\n\s*\}/); if (aggregationsMatch) { result.push('## Aggregations'); result.push(''); result.push('```javascript'); result.push(aggregationsMatch[1]); result.push('```'); result.push(''); } // Extract associations const associationsMatch = content.match(/associations\s*:\s*\{([\s\S]*?)\n\s*\}/); if (associationsMatch) { result.push('## Associations'); result.push(''); result.push('```javascript'); result.push(associationsMatch[1]); result.push('```'); result.push(''); } result.push('---'); result.push(''); result.push('### Full Source Code'); result.push(''); result.push('```javascript'); result.push(content); result.push('```'); return result.join('\n'); }