change_abap_program
Modify an existing ABAP program in SAP systems by locking, writing new source code, activating, and unlocking the object. Use after retrieving current source to apply changes safely.
Instructions
Modify an existing ABAP program/report in the SAP system. Locks the object, writes the new source, activates, and unlocks. Use get_abap_program first to read the current source.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Program name (e.g. ZHANZ_TEST) | |
| source | Yes | Complete new ABAP source code. Must start with REPORT statement. | |
| system_id | No | SAP system ID (e.g. DEV). Omit to use default system. |
Implementation Reference
- src/mcp-server.ts:287-298 (registration)Tool 'change_abap_program' is registered in the ListToolsRequestSchema handler with its name, description, and input schema. It requires 'name' (program name) and 'source' (complete new ABAP source code).
name: "change_abap_program", description: "Modify an existing ABAP program/report in the SAP system. Locks the object, writes the new source, activates, and unlocks. Use get_abap_program first to read the current source.", inputSchema: { type: "object" as const, properties: { name: { type: "string", description: "Program name (e.g. ZHANZ_TEST)" }, source: { type: "string", description: "Complete new ABAP source code. Must start with REPORT statement." }, ...SYSTEM_ID_PROP, }, required: ["name", "source"], }, }, - src/mcp-server.ts:1303-1307 (handler)The CallToolRequestSchema switch case for 'change_abap_program' parses args with ChangeAbapProgramSchema, then calls client.changeAbapProgram(progName, source) and returns the result as text content.
case "change_abap_program": { const { name: progName, source } = ChangeAbapProgramSchema.parse(args); const log = await client.changeAbapProgram(progName, source); return { content: [{ type: "text", text: log }] }; } - src/mcp-server.ts:39-42 (schema)ChangeAbapProgramSchema defines validation: name (string) and source (string) are required.
const ChangeAbapProgramSchema = z.object({ name: z.string(), source: z.string(), }); - src/adt-client.ts:738-835 (handler)The AdtClient.changeAbapProgram method implements the actual SAP ADT REST API logic: fetches CSRF token, locks the program, writes the new source (with optional auto-detected transport), unlocks, and activates the program. Returns a log of all steps.
async changeAbapProgram(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/programs/programs/${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 program ${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/programs/programs/${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/programs/programs/${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/programs/programs/${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/programs/programs/${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"); }