change_abap_class
Update the source code of an ABAP class in SAP. Locks the object, writes the new source, activates, and unlocks. Read the current source with get_class first.
Instructions
Modify an existing ABAP class in the SAP system. Locks the object, writes the new source, activates, and unlocks. Use get_class first to read the current source.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Class name (e.g. ZCL_MY_CLASS) | |
| source | Yes | Complete new ABAP class source code including CLASS definition and IMPLEMENTATION. | |
| system_id | No | SAP system ID (e.g. DEV). Omit to use default system. |
Implementation Reference
- src/adt-client.ts:939-1036 (handler)The `changeAbapClass` method on the AdtClient class handles the core execution logic for the 'change_abap_class' tool. It locks the class, writes the new source code (with auto-transport detection), unlocks, and activates the class.
async changeAbapClass(name: string, source: string, transport?: string): Promise<string> { await this.fetchStatefulCsrf(); const log: string[] = []; const nameLower = name.toLowerCase(); const nameUpper = name.toUpperCase(); try { // 1. Lock const lockResp = await this.http.post( `/sap/bc/adt/oo/classes/${nameLower}?_action=LOCK&accessMode=MODIFY`, "", { headers: this.statefulHeaders(), responseType: "text", validateStatus: () => true } ); const lockData = lockResp.data as string; const lockMatch = lockData.match(/<LOCK_HANDLE>([^<]+)<\/LOCK_HANDLE>/); if (!lockMatch?.[1]) { log.push(`Failed to lock class ${nameUpper} (HTTP ${lockResp.status})`); log.push(lockData.substring(0, 500)); return log.join("\n"); } const lockHandle = lockMatch[1]; log.push(`Locked ${nameUpper} for editing`); // 2. Write source (auto-detect transport if not provided) let corrNr = transport ?? ""; let writeResp = await this.http.put( `/sap/bc/adt/oo/classes/${nameLower}/source/main?lockHandle=${encodeURIComponent(lockHandle)}&corrNr=${corrNr}`, source, { headers: this.statefulHeaders({ "Content-Type": "text/plain; charset=utf-8" }), responseType: "text", validateStatus: () => true, } ); if (writeResp.status >= 400 && !transport) { const trMatch = (writeResp.data as string).match(/request\s+([A-Z]{3}K\d{6})/); if (trMatch) { corrNr = trMatch[1]; log.push(`Auto-detected transport ${corrNr}`); writeResp = await this.http.put( `/sap/bc/adt/oo/classes/${nameLower}/source/main?lockHandle=${encodeURIComponent(lockHandle)}&corrNr=${corrNr}`, source, { headers: this.statefulHeaders({ "Content-Type": "text/plain; charset=utf-8" }), responseType: "text", validateStatus: () => true, } ); } } if (writeResp.status >= 400) { log.push(`Write failed (HTTP ${writeResp.status}): ${(writeResp.data as string).substring(0, 500)}`); } else { log.push("Source code written"); } // 3. Unlock (must unlock before activation) await this.http.post( `/sap/bc/adt/oo/classes/${nameLower}?_action=UNLOCK&lockHandle=${encodeURIComponent(lockHandle)}`, "", { headers: this.statefulHeaders(), responseType: "text", validateStatus: () => true } ); log.push("Unlocked"); // 4. Activate const activateBody = `<?xml version="1.0" encoding="UTF-8"?> <adtcore:objectReferences xmlns:adtcore="http://www.sap.com/adt/core"> <adtcore:objectReference adtcore:uri="/sap/bc/adt/oo/classes/${nameLower}" adtcore:name="${nameUpper}"/> </adtcore:objectReferences>`; const actResp = await this.http.post( "/sap/bc/adt/activation?method=activate&preauditRequested=true", activateBody, { headers: this.statefulHeaders({ "Content-Type": "application/xml", Accept: "application/xml", }), responseType: "text", validateStatus: () => true, } ); const actData = actResp.data as string; if (actData.includes('activationExecuted="true"')) { log.push("Activated successfully"); } else { const msgMatch = actData.match(/<msg:shortText>([\s\S]*?)<\/msg:shortText>/); log.push(`Activation: ${msgMatch?.[1] ?? `HTTP ${actResp.status}`}`); } } finally { await this.endStatefulSession(); } return log.join("\n"); } - src/mcp-server.ts:1321-1325 (handler)The call handler in mcp-server.ts dispatches 'change_abap_class' to `client.changeAbapClass()`, parsing args using ChangeClassSchema.
case "change_abap_class": { const { name: className, source } = ChangeClassSchema.parse(args); const log = await client.changeAbapClass(className, source); return { content: [{ type: "text", text: log }] }; } - src/mcp-server.ts:49-52 (schema)The ChangeClassSchema defines input validation for the tool (name and source fields).
const ChangeClassSchema = z.object({ name: z.string(), source: z.string(), }); - src/mcp-server.ts:329-341 (registration)The tool 'change_abap_class' is registered in the ListToolsRequestSchema handler with its name, description, and inputSchema.
{ name: "change_abap_class", description: "Modify an existing ABAP class in the SAP system. Locks the object, writes the new source, activates, and unlocks. Use get_class first to read the current source.", inputSchema: { type: "object" as const, properties: { name: { type: "string", description: "Class name (e.g. ZCL_MY_CLASS)" }, source: { type: "string", description: "Complete new ABAP class source code including CLASS definition and IMPLEMENTATION." }, ...SYSTEM_ID_PROP, }, required: ["name", "source"], }, },