analyze_ui5_project
Analyze SAPUI5 project configuration by examining ui5.yaml, manifest.json, and package.json files to understand project structure and dependencies.
Instructions
Analyze SAPUI5 project configuration from ui5.yaml, manifest.json, and package.json.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- Main handler function that executes the analyze_ui5_project tool logic. It analyzes SAPUI5 project configuration by checking for ui5.yaml, manifest.json, and package.json files, extracting UI5 version, models, routing configuration, namespace, and controller patterns.
async handler(_args, { context }) { const root = context.rootDir; // Manifest can exist at project root or under webapp in typical UI5 layouts. const ui5YamlExists = await fileExists("ui5.yaml", root); const manifestPath = (await fileExists("webapp/manifest.json", root)) ? "webapp/manifest.json" : "manifest.json"; const manifestExists = await fileExists(manifestPath, root); const packageJsonExists = await fileExists("package.json", root); let ui5Yaml = ""; let manifest = {}; let packageJson = {}; if (ui5YamlExists) { ui5Yaml = await readTextFile("ui5.yaml", root); } if (manifestExists) { manifest = await readJsonFile(manifestPath, root); } if (packageJsonExists) { packageJson = await readJsonFile("package.json", root); } const models = Object.keys(manifest?.["sap.ui5"]?.models ?? {}); const routingConfig = manifest?.["sap.ui5"]?.routing ?? {}; const routes = Array.isArray(routingConfig.routes) ? routingConfig.routes.length : 0; const targets = routingConfig.targets ? Object.keys(routingConfig.targets).length : 0; const ui5Version = // Resolution priority keeps output deterministic across project variants. extractUi5VersionFromYaml(ui5Yaml) ?? manifest?.["sap.ui5"]?.dependencies?.minUI5Version ?? packageJson?.ui5?.version ?? packageJson?.dependencies?.["@openui5/sap.ui.core"] ?? null; const namespace = manifest?.["sap.app"]?.id ?? packageJson?.name ?? null; const controllerPattern = await detectControllerPattern(root); return outputSchema.parse({ detectedFiles: { ui5Yaml: ui5YamlExists, manifestJson: manifestExists, packageJson: packageJsonExists }, ui5Version, models, routing: { hasRouting: routes > 0 || targets > 0, routes, targets }, namespace, controllerPattern }); } - Output schema definition using Zod that validates the structure of the analysis results, including detected files, UI5 version, models, routing info, namespace, and controller pattern.
const outputSchema = z.object({ detectedFiles: z.object({ ui5Yaml: z.boolean(), manifestJson: z.boolean(), packageJson: z.boolean() }), ui5Version: z.string().nullable(), models: z.array(z.string()), routing: z.object({ hasRouting: z.boolean(), routes: z.number().int().nonnegative(), targets: z.number().int().nonnegative() }), namespace: z.string().nullable(), controllerPattern: z.string() }); - src/tools/index.js:1-1 (registration)Imports the analyzeUi5ProjectTool from the project module and includes it in the allTools array (line 55) for registration with the MCP server.
import { analyzeUi5ProjectTool } from "./project/analyzeProject.js"; - src/server/toolRegistry.js:37-133 (registration)ToolRegistry.applyToServer method that registers each tool with the MCP server, wrapping handlers with telemetry, error handling, and response formatting.
applyToServer(server, context) { for (const tool of this.tools) { server.registerTool( tool.name, { title: tool.title ?? tool.name, description: tool.description, inputSchema: tool.inputSchema ?? z.object({}).strict(), outputSchema: tool.outputSchema, annotations: tool.annotations }, async (args, extra) => { const invocationId = context.telemetry?.nextInvocationId?.(tool.name) ?? null; const startedAt = new Date().toISOString(); const startedAtMs = Date.now(); try { // Handlers receive parsed args plus shared runtime context. const output = await tool.handler(args ?? {}, { context, extra }); await context.telemetry?.recordToolExecution?.({ invocationId, toolName: tool.name, status: "success", startedAt, finishedAt: new Date().toISOString(), durationMs: Date.now() - startedAtMs, args: args ?? {}, result: output }); if (tool.outputSchema) { // Structured payload enables deterministic client parsing. return { structuredContent: output, content: [{ type: "text", text: JSON.stringify(output, null, 2) }] }; } if (typeof output === "string") { return { content: [{ type: "text", text: output }] }; } return { content: [{ type: "text", text: JSON.stringify(output, null, 2) }] }; } catch (error) { // Normalize all failures to stable machine-readable error shape. const normalized = normalizeError(error); await context.telemetry?.recordToolExecution?.({ invocationId, toolName: tool.name, status: "error", startedAt, finishedAt: new Date().toISOString(), durationMs: Date.now() - startedAtMs, args: args ?? {}, error: { code: normalized.code, message: normalized.message } }); logger.error(`Tool failed: ${tool.name}`, { code: normalized.code, message: normalized.message, details: normalized.details }); return { isError: true, content: [ { type: "text", text: JSON.stringify( { error: { code: normalized.code, message: normalized.message, details: normalized.details } }, null, 2 ) } ] }; } } ); } } } - Helper functions used by the handler: extractUi5VersionFromYaml (lines 87-94) parses version from YAML content, and detectControllerPattern (lines 96-123) searches for controller patterns in the project.
function extractUi5VersionFromYaml(ui5Yaml) { if (!ui5Yaml) { return null; } const versionMatch = ui5Yaml.match(/version:\s*["']?([0-9]+\.[0-9]+(?:\.[0-9]+)?|latest)["']?/i); return versionMatch?.[1] ?? null; } async function detectControllerPattern(rootDir) { try { // Lightweight project-level detection by searching source text patterns. const extendMatches = await searchFiles("Controller.extend(", { root: rootDir, maxResults: 1, fileExtensions: [".js"] }); if (extendMatches.length > 0) { return "Controller.extend"; } const classMatches = await searchFiles("extends Controller", { root: rootDir, maxResults: 1, fileExtensions: [".js"] }); if (classMatches.length > 0) { return "ES6 class extends Controller"; } } catch (error) { throw new ToolError(`Failed to inspect controller patterns: ${error.message}`, { code: "ANALYZE_PROJECT_FAILED" }); } return "unknown"; }