aem-mcp-server 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,6 +28,11 @@ This project is designed for non-technical persons who want to manage AEM via na
28
28
  - Node.js 18+
29
29
  - Access to an AEM instance (local or remote)
30
30
 
31
+ ### Add AEM MCP to AI IDE
32
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=AEM3&config=JTdCJTIyY29tbWFuZCUyMiUzQSUyMm5weCUyMC0teWVzJTIwLXAlMjBhZW0tbWNwLXNlcnZlciUyMC0tJTIwYWVtLW1jcCUyMC1IJTNEaHR0cCUzQSUyRiUyRmxvY2FsaG9zdCUzQTQ1MDIlMjIlMkMlMjJ0eXBlJTIyJTNBJTIyc3RyZWFtYWJsZS1odHRwJTIyJTJDJTIyc3RyZWFtYWJsZSUyMiUzQXRydWUlMkMlMjJ1cmwlMjIlM0ElMjJodHRwJTNBJTJGJTJGMTI3LjAuMC4xJTNBODUwMiUyRm1jcCUyMiU3RA%3D%3D)
33
+
34
+ OR:
35
+
31
36
  ### Installation
32
37
  ```sh
33
38
  npm install aem-mcp-server -g
@@ -1 +1 @@
1
- "use strict";const i="https://ims-na1.adobelogin.com/ims/token",s="openid,AdobeID,read_organizations,additional_info.projectedProductContext,aem_author_read,aem_author_write",c=t=>t?Array.isArray(t)?t.join(","):t:s;export async function getAccessToken(t,r,n){if(!t||!r)throw new Error("Client ID and Client Secret must be provided");const o=c(n),a=new URLSearchParams({client_id:t,client_secret:r,grant_type:"client_credentials",scope:o}),e=await fetch(i,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:a.toString()});if(!e.ok)throw new Error(`IMS token request failed: ${e.status} ${await e.text()}`);return(await e.json()).access_token}
1
+ "use strict";const i="https://ims-na1.adobelogin.com/ims/token",a="openid,AdobeID,read_organizations,additional_info.projectedProductContext,aem_author_read,aem_author_write",c=e=>e?Array.isArray(e)?e.join(","):e:a;export async function getAccessToken(e,n,r){if(!e||!n)throw new Error("Client ID and Client Secret must be provided");const o=c(r),s=new URLSearchParams({client_id:e,client_secret:n,grant_type:"client_credentials",scope:o}),t=await fetch(i,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s.toString()});if(!t.ok)throw new Error(`IMS token request failed: ${t.status} ${await t.text()}`);return t.json()}
@@ -1 +1 @@
1
- "use strict";import{getAEMConfig as j,isValidContentPath as g,isValidLocale as P}from"./aem.config.js";import{AEM_ERROR_CODES as h,createAEMError as d,createSuccessResponse as i,handleAEMHttpError as u,safeExecute as p,validateComponentOperation as b}from"./aem.errors.js";import{AEMFetch as w}from"./aem.fetch.js";export class AEMConnector{isInitialized;config;aemConfig;fetch;constructor(e){this.isInitialized=!1,this.config=this.loadConfig(e),this.aemConfig=j({}),this.fetch=new w({host:this.config.aem.host,username:this.config.aem.serviceUser.username,password:this.config.aem.serviceUser.password,timeout:this.aemConfig.queries.timeoutMs})}async init(){try{await this.fetch.init(),this.isInitialized=!0}catch{this.isInitialized=!1}}loadConfig(e={}){return{aem:{host:e.host||"http://localhost:4502",author:e.host||"http://localhost:4502",publish:"http://localhost:4503",serviceUser:{username:e.user||"admin",password:e.pass||"admin"},endpoints:{content:"/content",dam:"/content/dam",query:"/bin/querybuilder.json",crxde:"/crx/de",jcr:""}},mcp:{name:"AEM MCP Server",version:"1.0.0"}}}async testConnection(){try{console.log("Testing AEM connection to:",this.config.aem.host);const e=`${this.config.aem.host}/libs/granite/core/content/login.html`,t=await this.fetch.get(e,{timeout:5e3});return console.log("\u2705 AEM connection successful! Status:",t.status),!0}catch(e){return console.error("\u274C AEM connection failed:",e.message),!1}}async validateComponent(e){return p(async()=>{const t=e.pagePath||e.page_path,{locale:n,component:s,props:a}=e;if(b(n,t,s,a),!P(n,this.aemConfig))throw d(h.INVALID_LOCALE,`Locale '${n}' is not supported`,{locale:n,allowedLocales:this.aemConfig.validation.allowedLocales});if(!g(t,this.aemConfig))throw d(h.INVALID_PATH,`Path '${t}' is not within allowed content roots`,{path:t,allowedRoots:Object.values(this.aemConfig.contentPaths)});const r=`${t}.json`,o=await this.fetch.get(r,{params:{":depth":"2"},timeout:this.aemConfig.queries.timeoutMs}),c=this.validateComponentProps(o.data,s,a);return i({message:"Component validation completed successfully",pageData:o.data,component:s,locale:n,validation:c,configUsed:{allowedLocales:this.aemConfig.validation.allowedLocales}},"validateComponent")},"validateComponent")}validateComponentProps(e,t,n){const s=[],a=[];return t==="text"&&!n.text&&!n.richText&&s.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&a.push("Image component requires fileReference or src property"),{valid:a.length===0,errors:a,warnings:s,componentType:t,propsValidated:Object.keys(n).length}}async updateComponent(e){return p(async()=>{if(!e.componentPath||typeof e.componentPath!="string")throw d(h.INVALID_PARAMETERS,"Component path is required and must be a string");if(!e.properties||typeof e.properties!="object")throw d(h.INVALID_PARAMETERS,"Properties are required and must be an object");if(!g(e.componentPath,this.aemConfig))throw d(h.INVALID_PATH,`Component path '${e.componentPath}' is not within allowed content roots`,{path:e.componentPath,allowedRoots:Object.values(this.aemConfig.contentPaths)});const t=`${e.componentPath}.json`,n=await this.fetch.get(t);try{await n.json()}catch(o){throw o.response?.status===404?d(h.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):u(o,"updateComponent")}const s=new URLSearchParams;Object.entries(e.properties).forEach(([o,c])=>{c==null?s.append(`${o}@Delete`,""):Array.isArray(c)?c.forEach(l=>{s.append(`${o}`,l.toString())}):typeof c=="object"?s.append(o,JSON.stringify(c)):s.append(o,c.toString())});const a=await this.fetch.post(e.componentPath,s),r=await this.fetch.get(`${e.componentPath}.json`);return i({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:r.data,response:a.data,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async undoChanges(e){return i({message:"undoChanges is not implemented. Please use AEM version history for undo/rollback.",request:e,timestamp:new Date().toISOString()},"undoChanges")}async scanPageComponents(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(r,o)=>{!r||typeof r!="object"||(r["sling:resourceType"]&&s.push({path:o,resourceType:r["sling:resourceType"],properties:{...r}}),Object.entries(r).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const m=o?`${o}/${c}`:c;a(l,m)}}))};return n.data["jcr:content"]?a(n.data["jcr:content"],"jcr:content"):a(n.data,e),i({pagePath:e,components:s,totalComponents:s.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return p(async()=>{const t=await this.fetch.get("/content.json",{":depth":"2"}),n=[];return Object.entries(t).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&a["jcr:content"]&&n.push({name:s,path:`/content/${s}`,title:a["jcr:content"]["jcr:title"]||s,template:a["jcr:content"]["cq:template"],lastModified:a["jcr:content"]["cq:lastModified"]})}),i({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(e){return p(async()=>{const t=`/content/${e}.json`,n=await this.fetch.get(t,{":depth":"3"}),s=[];return Object.entries(n).forEach(([a,r])=>{a.startsWith("jcr:")||a.startsWith("sling:")||r&&typeof r=="object"&&r["jcr:content"]&&s.push({name:a,path:`/content/${a}`,title:r["jcr:content"]["jcr:title"]||a,language:r["jcr:content"]["jcr:language"]||"en"})}),i({site:e,languageMasters:s},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(e,t){return p(async()=>{const n=`${t}.json`,s=await this.fetch.get(n,{":depth":"2"}),a=[];return Object.entries(s).forEach(([r,o])=>{r.startsWith("jcr:")||r.startsWith("sling:")||o&&typeof o=="object"&&a.push({name:r,title:o["jcr:content"]?.["jcr:title"]||r,language:o["jcr:content"]?.["jcr:language"]||r})}),i({site:e,languageMasterPath:t,availableLocales:a},"fetchAvailableLocales")},"fetchAvailableLocales")}async replicateAndPublish(e,t,n){return p(async()=>i({message:"Replication simulated",selectedLocales:e,componentData:t,localizedOverrides:n},"replicateAndPublish"),"replicateAndPublish")}async getAllTextContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(r,o)=>{!r||typeof r!="object"||((r.text||r["jcr:title"]||r["jcr:description"])&&s.push({path:o,title:r["jcr:title"],text:r.text,description:r["jcr:description"]}),Object.entries(r).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const m=o?`${o}/${c}`:c;a(l,m)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,textContent:s},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(e){return p(async()=>this.getAllTextContent(e),"getPageTextContent")}async getPageImages(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(r,o)=>{!r||typeof r!="object"||((r.fileReference||r.src)&&s.push({path:o,fileReference:r.fileReference,src:r.src,alt:r.alt||r.altText,title:r["jcr:title"]||r.title}),Object.entries(r).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const m=o?`${o}/${c}`:c;a(l,m)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,images:s},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return p(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t);return i({pagePath:e,content:n},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return p(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),s=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([a,r])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:")||a.startsWith("cq:")||a.startsWith("rep:")||a.startsWith("oak:")||a==="jcr:content")&&r&&typeof r=="object"){const o=`${e}/${a}`;s.push({name:a,path:o,primaryType:r["jcr:primaryType"]||"nt:unstructured",title:r["jcr:content"]?.["jcr:title"]||r["jcr:title"]||a,lastModified:r["jcr:content"]?.["cq:lastModified"]||r["cq:lastModified"],resourceType:r["jcr:content"]?.["sling:resourceType"]||r["sling:resourceType"]})}}),s}catch(n){if(n.response?.status===404||n.response?.status===403)return((await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":"1","p.limit":"1000","p.hits":"full"})).hits||[]).map(a=>({name:a.name||a.path?.split("/").pop(),path:a.path,primaryType:a["jcr:primaryType"]||"cq:Page",title:a["jcr:content/jcr:title"]||a.title||a.name,lastModified:a["jcr:content/cq:lastModified"],resourceType:a["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return p(async()=>{try{const s=await this.fetch.get(`${e}.${t}.json`),a=[],r=(o,c,l)=>{l>t||a.length>=n||Object.entries(o).forEach(([m,f])=>{if(!(a.length>=n)&&!(m.startsWith("jcr:")||m.startsWith("sling:")||m.startsWith("cq:")||m.startsWith("rep:")||m.startsWith("oak:"))&&f&&typeof f=="object"){const y=`${c}/${m}`;f["jcr:primaryType"]==="cq:Page"&&a.push({name:m,path:y,primaryType:"cq:Page",title:f["jcr:content"]?.["jcr:title"]||m,template:f["jcr:content"]?.["cq:template"],lastModified:f["jcr:content"]?.["cq:lastModified"],lastModifiedBy:f["jcr:content"]?.["cq:lastModifiedBy"],resourceType:f["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&r(f,y,l+1)}})};return s&&typeof s=="object"&&r(s,e,0),i({siteRoot:e,pages:a,pageCount:a.length,depth:t,limit:n,totalChildrenScanned:a.length},"listPages")}catch(s){if(console.warn("JSON API failed, falling back to QueryBuilder:",s.message),s.response?.status===404||s.response?.status===403){const a=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),r=(a.hits||[]).map(o=>({name:o.name||o.path?.split("/").pop(),path:o.path,primaryType:"cq:Page",title:o["jcr:content/jcr:title"]||o.title||o.name,template:o["jcr:content/cq:template"],lastModified:o["jcr:content/cq:lastModified"],lastModifiedBy:o["jcr:content/cq:lastModifiedBy"],resourceType:o["jcr:content/sling:resourceType"],type:"page"}));return i({siteRoot:e,pages:r,pageCount:r.length,depth:t,limit:n,totalChildrenScanned:a.total||r.length,fallbackUsed:"QueryBuilder"},"listPages")}throw s}},"listPages")}async executeJCRQuery(e,t=20){return p(async()=>{if(!e||e.trim().length===0)throw new Error("Query is required and must be a non-empty string. Note: Only QueryBuilder fulltext is supported, not JCR SQL2.");const n=e.toLowerCase();if(/drop|delete|update|insert|exec|script|\.|<script/i.test(n)||e.length>1e3)throw new Error("Query contains potentially unsafe patterns or is too long");const s=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:s.hits||[],total:s.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return p(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),s={title:n["jcr:title"],description:n["jcr:description"],template:n["cq:template"],lastModified:n["cq:lastModified"],lastModifiedBy:n["jcr:createdBy"],created:n["jcr:created"],createdBy:n["jcr:createdBy"],primaryType:n["jcr:primaryType"],resourceType:n["sling:resourceType"],tags:n["cq:tags"]||[],properties:n};return i({pagePath:e,properties:s},"getPageProperties")},"getPageProperties")}async searchContent(e){return p(async()=>{const t=await this.fetch.get(this.config.aem.endpoints.query,e);return i({params:e,results:t.hits||[],total:t.total||0,rawResponse:t},"searchContent")},"searchContent")}async getAssetMetadata(e){return p(async()=>{const t=`${e}.json`,n=await this.fetch.get(t),s=n["jcr:content"]?.metadata||{};return i({assetPath:e,metadata:s,fullData:n},"getAssetMetadata")},"getAssetMetadata")}async createPage(e){return p(async()=>{const{parentPath:t,title:n,template:s,name:a,properties:r}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const o=a||n.replace(/\s+/g,"-").toLowerCase(),c=`${t}/${o}`;return await this.fetch.post(c,{"jcr:primaryType":"cq:Page","jcr:title":n,"cq:template":s,...r}),i({success:!0,pagePath:c,title:n,template:s,properties:r,timestamp:new Date().toISOString()},"createPage")},"createPage")}async deletePage(e){return p(async()=>{const{pagePath:t}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{try{await this.fetch.post("/bin/wcmcommand",{cmd:"deletePage",path:t,force:e.force?"true":"false"}),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(r){throw r}}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return p(async()=>{const{pagePath:t,componentPath:n,componentType:s,resourceType:a,properties:r={},name:o}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const c=o||`${s}_${Date.now()}`,l=n||`${t}/jcr:content/${c}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":a,...r,":operation":"import",":contentType":"json",":replace":"true"}),i({success:!0,componentPath:l,componentType:s,resourceType:a,properties:r,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async deleteComponent(e){return p(async()=>{const{componentPath:t}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(a){throw a}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(e){return p(async()=>{const{contentPaths:t,unpublishTree:n=!1}=e;if(!t||Array.isArray(t)&&t.length===0)throw d(h.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const s=[];for(const a of Array.isArray(t)?t:[t])try{const r=new URLSearchParams;r.append("cmd","Deactivate"),r.append("path",a),r.append("ignoredeactivated","false"),r.append("onlymodified","false"),n&&r.append("deep","true");const o=await this.fetch.post("/bin/replicate.json",r);s.push({path:a,success:!0,response:o})}catch(r){s.push({path:a,success:!1,error:r.message})}return i({success:s.every(a=>a.success),results:s,unpublishedPaths:t,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(e){return p(async()=>{const{pagePath:t,activateTree:n=!1}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Activate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,activatedPath:t,activateTree:n,response:a,timestamp:new Date().toISOString()},"activatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,activatedPath:t,activateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw u(s,"activatePage")}}},"activatePage")}async deactivatePage(e){return p(async()=>{const{pagePath:t,deactivateTree:n=!1}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,timestamp:new Date().toISOString()},"deactivatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw u(s,"deactivatePage")}}},"deactivatePage")}async uploadAsset(e){return p(async()=>{const{parentPath:t,fileName:n,fileContent:s,mimeType:a,metadata:r={}}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const o=`${t}/${n}`;try{const c=new URLSearchParams;typeof s=="string"?c.append("file",s):c.append("file",s.toString()),c.append("fileName",n),c.append(":operation","import"),c.append(":contentType","json"),c.append(":replace","true"),c.append("jcr:primaryType","dam:Asset"),a&&c.append("jcr:content/jcr:mimeType",a),Object.entries(r).forEach(([f,y])=>{c.append(`jcr:content/metadata/${f}`,String(y))});const l=await this.fetch.post(o,c),m=await this.fetch.get(`${o}.json`);return i({success:!0,assetPath:o,fileName:n,mimeType:a,metadata:r,uploadResponse:l,assetData:m,timestamp:new Date().toISOString()},"uploadAsset")}catch(c){try{const l=await this.fetch.post("/api/assets"+t,{fileName:n,fileContent:s,mimeType:a,metadata:r});return i({success:!0,assetPath:o,fileName:n,mimeType:a,metadata:r,uploadResponse:l,fallbackUsed:"DAM API",timestamp:new Date().toISOString()},"uploadAsset")}catch{throw u(c,"uploadAsset")}}},"uploadAsset")}async updateAsset(e){return p(async()=>{const{assetPath:t,metadata:n,fileContent:s,mimeType:a}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const r=new URLSearchParams;s&&(r.append("file",s),a&&r.append("jcr:content/jcr:mimeType",a)),n&&typeof n=="object"&&Object.entries(n).forEach(([o,c])=>{r.append(`jcr:content/metadata/${o}`,String(c))});try{const o=await this.fetch.post(t,r),c=await this.fetch.get(`${t}.json`);return i({success:!0,assetPath:t,updatedMetadata:n,updateResponse:o,assetData:c,timestamp:new Date().toISOString()},"updateAsset")}catch(o){throw u(o,"updateAsset")}},"updateAsset")}async deleteAsset(e){return p(async()=>{const{assetPath:t,force:n=!1}=e;if(!g(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.fetch.delete(t),i({success:!0,deletedPath:t,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}async getTemplateStructure(e){return p(async()=>{try{const t=await this.fetch.get(`${e}.infinity.json`),n={path:e,properties:t["jcr:content"]||{},policies:t["jcr:content"]?.policies||{},structure:t["jcr:content"]?.structure||{},initialContent:t["jcr:content"]?.initial||{},allowedComponents:[],allowedPaths:t["jcr:content"]?.allowedPaths||[]},s=(a,r="")=>{if(!(!a||typeof a!="object")){if(a.components){const o=Object.keys(a.components);n.allowedComponents.push(...o)}Object.entries(a).forEach(([o,c])=>{typeof c=="object"&&c!==null&&!o.startsWith("jcr:")&&s(c,r?`${r}/${o}`:o)})}};return s(n.policies),n.allowedComponents=[...new Set(n.allowedComponents)],i({templatePath:e,structure:n,fullData:t},"getTemplateStructure")}catch(t){throw u(t,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(e){return p(async()=>{const{updates:t,validateFirst:n=!0,continueOnError:s=!1}=e;if(!Array.isArray(t)||t.length===0)throw d(h.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const a=[];if(n)for(const o of t)try{await this.fetch.get(`${o.componentPath}.json`)}catch(c){if(c.response?.status===404&&(a.push({componentPath:o.componentPath,success:!1,error:`Component not found: ${o.componentPath}`,phase:"validation"}),!s))return i({success:!1,message:"Bulk update failed during validation phase",results:a,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let r=0;for(const o of t)try{const c=await this.updateComponent({componentPath:o.componentPath,properties:o.properties});a.push({componentPath:o.componentPath,success:!0,result:c,phase:"update"}),r++}catch(c){if(a.push({componentPath:o.componentPath,success:!1,error:c.message,phase:"update"}),!s)break}return i({success:r===t.length,message:`Bulk update completed: ${r}/${t.length} successful`,results:a,totalUpdates:t.length,successfulUpdates:r,failedUpdates:t.length-r},"bulkUpdateComponents")},"bulkUpdateComponents")}async getNodeContent(e,t=1){return p(async()=>{const n=`${e}.json`,s=await this.fetch.get(n,{":depth":t.toString()});return{path:e,depth:t,content:s,timestamp:new Date().toISOString()}},"getNodeContent")}}
1
+ "use strict";import{getAEMConfig as P,isValidContentPath as u,isValidLocale as b}from"./aem.config.js";import{AEM_ERROR_CODES as h,createAEMError as d,createSuccessResponse as i,handleAEMHttpError as y,safeExecute as p,validateComponentOperation as w}from"./aem.errors.js";import{AEMFetch as C}from"./aem.fetch.js";import{LOGGER as g}from"../utils/logger.js";export class AEMConnector{isInitialized;isAEMaaCS;config;aemConfig;fetch;constructor(e){this.isInitialized=!1,this.config=this.loadConfig(e),this.aemConfig=P({}),this.isAEMaaCS=this.isConfigAEMaaCS(),this.fetch=new C({host:this.config.aem.host,auth:this.config.aem.auth,timeout:this.aemConfig.queries.timeoutMs})}async init(){try{await this.fetch.init(),this.isInitialized=!0}catch{this.isInitialized=!1}}isConfigAEMaaCS(){return!!(this.config.aem.auth.clientId&&this.config.aem.auth.clientSecret)}loadConfig(e={}){return{aem:{host:e.host||"http://localhost:4502",author:e.host||"http://localhost:4502",publish:"http://localhost:4503",auth:{username:e.user||"admin",password:e.pass||"admin"},endpoints:{content:"/content",dam:"/content/dam",query:"/bin/querybuilder.json",crxde:"/crx/de",jcr:""}},mcp:{name:"NAEM MCP Server",version:"1.0.0"}}}async testConnection(){const e=await this.testAEMConnection(),t=e?await this.testAuthConnection():!1;return{aem:e,auth:t}}async testAEMConnection(){try{this.isInitialized||await this.init(),g.log("Testing AEM connection to:",this.config.aem.host);const e=`${this.config.aem.host}/libs/granite/core/content/login.html`,t=await this.fetch.get(e,{timeout:5e3});return g.log("\u2705 AEM connection successful! Status:",t.status),!0}catch(e){return g.error("\u274C AEM connection failed:",e.message),!1}}async testAuthConnection(){try{this.isInitialized||await this.init();const e=`${this.config.aem.host}/libs/granite/security/content/userinfo.json`;g.log("Testing AEM authentication connection to:",this.config.aem.host);const t=await this.fetch.get(e,{timeout:5e3});return g.log("\u2705 AEM authentication connection successful! Status:",t.status),!0}catch(e){return g.error("\u274C AEM authentication connection failed:",e.message),!1}}async validateComponent(e){return p(async()=>{const t=e.pagePath||e.page_path,{locale:n,component:s,props:a}=e;if(w(n,t,s,a),!b(n,this.aemConfig))throw d(h.INVALID_LOCALE,`Locale '${n}' is not supported`,{locale:n,allowedLocales:this.aemConfig.validation.allowedLocales});if(!u(t,this.aemConfig))throw d(h.INVALID_PATH,`Path '${t}' is not within allowed content roots`,{path:t,allowedRoots:Object.values(this.aemConfig.contentPaths)});const o=`${t}.json`,r=await this.fetch.get(o,{params:{":depth":"2"},timeout:this.aemConfig.queries.timeoutMs}),c=this.validateComponentProps(r.data,s,a);return i({message:"Component validation completed successfully",pageData:r.data,component:s,locale:n,validation:c,configUsed:{allowedLocales:this.aemConfig.validation.allowedLocales}},"validateComponent")},"validateComponent")}validateComponentProps(e,t,n){const s=[],a=[];return t==="text"&&!n.text&&!n.richText&&s.push("Text component should have text or richText property"),t==="image"&&!n.fileReference&&!n.src&&a.push("Image component requires fileReference or src property"),{valid:a.length===0,errors:a,warnings:s,componentType:t,propsValidated:Object.keys(n).length}}async updateComponent(e){return p(async()=>{if(!e.componentPath||typeof e.componentPath!="string")throw d(h.INVALID_PARAMETERS,"Component path is required and must be a string");if(!e.properties||typeof e.properties!="object")throw d(h.INVALID_PARAMETERS,"Properties are required and must be an object");if(!u(e.componentPath,this.aemConfig))throw d(h.INVALID_PATH,`Component path '${e.componentPath}' is not within allowed content roots`,{path:e.componentPath,allowedRoots:Object.values(this.aemConfig.contentPaths)});const t=`${e.componentPath}.json`,n=await this.fetch.get(t);try{await n.json()}catch(r){throw r.response?.status===404?d(h.COMPONENT_NOT_FOUND,`Component not found at path: ${e.componentPath}`,{componentPath:e.componentPath}):y(r,"updateComponent")}const s=new URLSearchParams;Object.entries(e.properties).forEach(([r,c])=>{c==null?s.append(`${r}@Delete`,""):Array.isArray(c)?c.forEach(l=>{s.append(`${r}`,l.toString())}):typeof c=="object"?s.append(r,JSON.stringify(c)):s.append(r,c.toString())});const a=await this.fetch.post(e.componentPath,s),o=await this.fetch.get(`${e.componentPath}.json`);return i({message:"Component updated successfully",path:e.componentPath,properties:e.properties,updatedProperties:o.data,response:a.data,verification:{success:!0,propertiesChanged:Object.keys(e.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async undoChanges(e){return i({message:"undoChanges is not implemented. Please use AEM version history for undo/rollback.",request:e,timestamp:new Date().toISOString()},"undoChanges")}async scanPageComponents(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||(o["sling:resourceType"]&&s.push({path:r,resourceType:o["sling:resourceType"],properties:{...o}}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const m=r?`${r}/${c}`:c;a(l,m)}}))};return n.data["jcr:content"]?a(n.data["jcr:content"],"jcr:content"):a(n.data,e),i({pagePath:e,components:s,totalComponents:s.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return p(async()=>{const t=await this.fetch.get("/content.json",{":depth":"2"}),n=[];return Object.entries(t).forEach(([s,a])=>{s.startsWith("jcr:")||s.startsWith("sling:")||a&&typeof a=="object"&&a["jcr:content"]&&n.push({name:s,path:`/content/${s}`,title:a["jcr:content"]["jcr:title"]||s,template:a["jcr:content"]["cq:template"],lastModified:a["jcr:content"]["cq:lastModified"]})}),i({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(e){return p(async()=>{const t=`/content/${e}.json`,n=await this.fetch.get(t,{":depth":"3"}),s=[];return Object.entries(n).forEach(([a,o])=>{a.startsWith("jcr:")||a.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&s.push({name:a,path:`/content/${a}`,title:o["jcr:content"]["jcr:title"]||a,language:o["jcr:content"]["jcr:language"]||"en"})}),i({site:e,languageMasters:s},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(e,t){return p(async()=>{const n=`${t}.json`,s=await this.fetch.get(n,{":depth":"2"}),a=[];return Object.entries(s).forEach(([o,r])=>{o.startsWith("jcr:")||o.startsWith("sling:")||r&&typeof r=="object"&&a.push({name:o,title:r["jcr:content"]?.["jcr:title"]||o,language:r["jcr:content"]?.["jcr:language"]||o})}),i({site:e,languageMasterPath:t,availableLocales:a},"fetchAvailableLocales")},"fetchAvailableLocales")}async replicateAndPublish(e,t,n){return p(async()=>i({message:"Replication simulated",selectedLocales:e,componentData:t,localizedOverrides:n},"replicateAndPublish"),"replicateAndPublish")}async getAllTextContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||((o.text||o["jcr:title"]||o["jcr:description"])&&s.push({path:r,title:o["jcr:title"],text:o.text,description:o["jcr:description"]}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const m=r?`${r}/${c}`:c;a(l,m)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,textContent:s},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(e){return p(async()=>this.getAllTextContent(e),"getPageTextContent")}async getPageImages(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t),s=[],a=(o,r)=>{!o||typeof o!="object"||((o.fileReference||o.src)&&s.push({path:r,fileReference:o.fileReference,src:o.src,alt:o.alt||o.altText,title:o["jcr:title"]||o.title}),Object.entries(o).forEach(([c,l])=>{if(typeof l=="object"&&l!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const m=r?`${r}/${c}`:c;a(l,m)}}))};return n["jcr:content"]?a(n["jcr:content"],"jcr:content"):a(n,e),i({pagePath:e,images:s},"getPageImages")},"getPageImages")}async updateImagePath(e,t){return p(async()=>this.updateComponent({componentPath:e,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(e){return p(async()=>{const t=`${e}.infinity.json`,n=await this.fetch.get(t);return i({pagePath:e,content:n},"getPageContent")},"getPageContent")}async listChildren(e,t=1){return p(async()=>{try{const n=await this.fetch.get(`${e}.${t}.json`),s=[];return n&&typeof n=="object"&&Object.entries(n).forEach(([a,o])=>{if(!(a.startsWith("jcr:")||a.startsWith("sling:")||a.startsWith("cq:")||a.startsWith("rep:")||a.startsWith("oak:")||a==="jcr:content")&&o&&typeof o=="object"){const r=`${e}/${a}`;s.push({name:a,path:r,primaryType:o["jcr:primaryType"]||"nt:unstructured",title:o["jcr:content"]?.["jcr:title"]||o["jcr:title"]||a,lastModified:o["jcr:content"]?.["cq:lastModified"]||o["cq:lastModified"],resourceType:o["jcr:content"]?.["sling:resourceType"]||o["sling:resourceType"]})}}),s}catch(n){if(n.response?.status===404||n.response?.status===403)return((await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":"1","p.limit":"1000","p.hits":"full"})).hits||[]).map(a=>({name:a.name||a.path?.split("/").pop(),path:a.path,primaryType:a["jcr:primaryType"]||"cq:Page",title:a["jcr:content/jcr:title"]||a.title||a.name,lastModified:a["jcr:content/cq:lastModified"],resourceType:a["jcr:content/sling:resourceType"]}));throw n}},"listChildren")}async listPages(e,t=1,n=20){return p(async()=>{try{const s=await this.fetch.get(`${e}.${t}.json`),a=[],o=(r,c,l)=>{l>t||a.length>=n||Object.entries(r).forEach(([m,f])=>{if(!(a.length>=n)&&!(m.startsWith("jcr:")||m.startsWith("sling:")||m.startsWith("cq:")||m.startsWith("rep:")||m.startsWith("oak:"))&&f&&typeof f=="object"){const j=`${c}/${m}`;f["jcr:primaryType"]==="cq:Page"&&a.push({name:m,path:j,primaryType:"cq:Page",title:f["jcr:content"]?.["jcr:title"]||m,template:f["jcr:content"]?.["cq:template"],lastModified:f["jcr:content"]?.["cq:lastModified"],lastModifiedBy:f["jcr:content"]?.["cq:lastModifiedBy"],resourceType:f["jcr:content"]?.["sling:resourceType"],type:"page"}),l<t&&o(f,j,l+1)}})};return s&&typeof s=="object"&&o(s,e,0),i({siteRoot:e,pages:a,pageCount:a.length,depth:t,limit:n,totalChildrenScanned:a.length},"listPages")}catch(s){if(g.warn("JSON API failed, falling back to QueryBuilder:",s.message),s.response?.status===404||s.response?.status===403){const a=await this.fetch.get("/bin/querybuilder.json",{path:e,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":n.toString(),"p.hits":"full"}),o=(a.hits||[]).map(r=>({name:r.name||r.path?.split("/").pop(),path:r.path,primaryType:"cq:Page",title:r["jcr:content/jcr:title"]||r.title||r.name,template:r["jcr:content/cq:template"],lastModified:r["jcr:content/cq:lastModified"],lastModifiedBy:r["jcr:content/cq:lastModifiedBy"],resourceType:r["jcr:content/sling:resourceType"],type:"page"}));return i({siteRoot:e,pages:o,pageCount:o.length,depth:t,limit:n,totalChildrenScanned:a.total||o.length,fallbackUsed:"QueryBuilder"},"listPages")}throw s}},"listPages")}async executeJCRQuery(e,t=20){return p(async()=>{if(!e||e.trim().length===0)throw new Error("Query is required and must be a non-empty string. Note: Only QueryBuilder fulltext is supported, not JCR SQL2.");const n=e.toLowerCase();if(/drop|delete|update|insert|exec|script|\.|<script/i.test(n)||e.length>1e3)throw new Error("Query contains potentially unsafe patterns or is too long");const s=await this.fetch.get("/bin/querybuilder.json",{path:"/content",type:"cq:Page",fulltext:e,"p.limit":t});return{query:e,results:s.hits||[],total:s.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(e){return p(async()=>{const t=`${e}/jcr:content.json`,n=await this.fetch.get(t),s={title:n["jcr:title"],description:n["jcr:description"],template:n["cq:template"],lastModified:n["cq:lastModified"],lastModifiedBy:n["jcr:createdBy"],created:n["jcr:created"],createdBy:n["jcr:createdBy"],primaryType:n["jcr:primaryType"],resourceType:n["sling:resourceType"],tags:n["cq:tags"]||[],properties:n};return i({pagePath:e,properties:s},"getPageProperties")},"getPageProperties")}async searchContent(e){return p(async()=>{const t=await this.fetch.get(this.config.aem.endpoints.query,e);return i({params:e,results:t.hits||[],total:t.total||0,rawResponse:t},"searchContent")},"searchContent")}async getAssetMetadata(e){return p(async()=>{const t=`${e}.json`,n=await this.fetch.get(t),s=n["jcr:content"]?.metadata||{};return i({assetPath:e,metadata:s,fullData:n},"getAssetMetadata")},"getAssetMetadata")}async createPage(e){return p(async()=>{const{parentPath:t,title:n,template:s,name:a,properties:o}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const r=a||n.replace(/\s+/g,"-").toLowerCase(),c=`${t}/${r}`;return await this.fetch.post(c,{"jcr:primaryType":"cq:Page","jcr:title":n,"cq:template":s,...o}),i({success:!0,pagePath:c,title:n,template:s,properties:o,timestamp:new Date().toISOString()},"createPage")},"createPage")}async deletePage(e){return p(async()=>{const{pagePath:t}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{try{await this.fetch.post("/bin/wcmcommand",{cmd:"deletePage",path:t,force:e.force?"true":"false"}),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(o){throw o}}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(e){return p(async()=>{const{pagePath:t,componentPath:n,componentType:s,resourceType:a,properties:o={},name:r}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const c=r||`${s}_${Date.now()}`,l=n||`${t}/jcr:content/${c}`;return await this.fetch.post(l,{"jcr:primaryType":"nt:unstructured","sling:resourceType":a,...o,":operation":"import",":contentType":"json",":replace":"true"}),i({success:!0,componentPath:l,componentType:s,resourceType:a,properties:o,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async deleteComponent(e){return p(async()=>{const{componentPath:t}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});let n=!1;try{await this.fetch.delete(t),n=!0}catch{try{await this.fetch.post(t,{":operation":"delete"}),n=!0}catch(a){throw a}}return i({success:n,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(e){return p(async()=>{const{contentPaths:t,unpublishTree:n=!1}=e;if(!t||Array.isArray(t)&&t.length===0)throw d(h.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const s=[];for(const a of Array.isArray(t)?t:[t])try{const o=new URLSearchParams;o.append("cmd","Deactivate"),o.append("path",a),o.append("ignoredeactivated","false"),o.append("onlymodified","false"),n&&o.append("deep","true");const r=await this.fetch.post("/bin/replicate.json",o);s.push({path:a,success:!0,response:r})}catch(o){s.push({path:a,success:!1,error:o.message})}return i({success:s.every(a=>a.success),results:s,unpublishedPaths:t,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(e){return p(async()=>{const{pagePath:t,activateTree:n=!1}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Activate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,activatedPath:t,activateTree:n,response:a,timestamp:new Date().toISOString()},"activatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,activatedPath:t,activateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw y(s,"activatePage")}}},"activatePage")}async deactivatePage(e){return p(async()=>{const{pagePath:t,deactivateTree:n=!1}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});try{const s=new URLSearchParams;s.append("cmd","Deactivate"),s.append("path",t),s.append("ignoredeactivated","false"),s.append("onlymodified","false"),n&&s.append("deep","true");const a=await this.fetch.post("/bin/replicate.json",s);return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,timestamp:new Date().toISOString()},"deactivatePage")}catch(s){try{const a=await this.fetch.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return i({success:!0,deactivatedPath:t,deactivateTree:n,response:a,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw y(s,"deactivatePage")}}},"deactivatePage")}async uploadAsset(e){return p(async()=>{const{parentPath:t,fileName:n,fileContent:s,mimeType:a,metadata:o={}}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const r=`${t}/${n}`;try{const c=new URLSearchParams;typeof s=="string"?c.append("file",s):c.append("file",s.toString()),c.append("fileName",n),c.append(":operation","import"),c.append(":contentType","json"),c.append(":replace","true"),c.append("jcr:primaryType","dam:Asset"),a&&c.append("jcr:content/jcr:mimeType",a),Object.entries(o).forEach(([f,j])=>{c.append(`jcr:content/metadata/${f}`,String(j))});const l=await this.fetch.post(r,c),m=await this.fetch.get(`${r}.json`);return i({success:!0,assetPath:r,fileName:n,mimeType:a,metadata:o,uploadResponse:l,assetData:m,timestamp:new Date().toISOString()},"uploadAsset")}catch(c){try{const l=await this.fetch.post("/api/assets"+t,{fileName:n,fileContent:s,mimeType:a,metadata:o});return i({success:!0,assetPath:r,fileName:n,mimeType:a,metadata:o,uploadResponse:l,fallbackUsed:"DAM API",timestamp:new Date().toISOString()},"uploadAsset")}catch{throw y(c,"uploadAsset")}}},"uploadAsset")}async updateAsset(e){return p(async()=>{const{assetPath:t,metadata:n,fileContent:s,mimeType:a}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const o=new URLSearchParams;s&&(o.append("file",s),a&&o.append("jcr:content/jcr:mimeType",a)),n&&typeof n=="object"&&Object.entries(n).forEach(([r,c])=>{o.append(`jcr:content/metadata/${r}`,String(c))});try{const r=await this.fetch.post(t,o),c=await this.fetch.get(`${t}.json`);return i({success:!0,assetPath:t,updatedMetadata:n,updateResponse:r,assetData:c,timestamp:new Date().toISOString()},"updateAsset")}catch(r){throw y(r,"updateAsset")}},"updateAsset")}async deleteAsset(e){return p(async()=>{const{assetPath:t,force:n=!1}=e;if(!u(t,this.aemConfig))throw d(h.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.fetch.delete(t),i({success:!0,deletedPath:t,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}async getTemplateStructure(e){return p(async()=>{try{const t=await this.fetch.get(`${e}.infinity.json`),n={path:e,properties:t["jcr:content"]||{},policies:t["jcr:content"]?.policies||{},structure:t["jcr:content"]?.structure||{},initialContent:t["jcr:content"]?.initial||{},allowedComponents:[],allowedPaths:t["jcr:content"]?.allowedPaths||[]},s=(a,o="")=>{if(!(!a||typeof a!="object")){if(a.components){const r=Object.keys(a.components);n.allowedComponents.push(...r)}Object.entries(a).forEach(([r,c])=>{typeof c=="object"&&c!==null&&!r.startsWith("jcr:")&&s(c,o?`${o}/${r}`:r)})}};return s(n.policies),n.allowedComponents=[...new Set(n.allowedComponents)],i({templatePath:e,structure:n,fullData:t},"getTemplateStructure")}catch(t){throw y(t,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(e){return p(async()=>{const{updates:t,validateFirst:n=!0,continueOnError:s=!1}=e;if(!Array.isArray(t)||t.length===0)throw d(h.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const a=[];if(n)for(const r of t)try{await this.fetch.get(`${r.componentPath}.json`)}catch(c){if(c.response?.status===404&&(a.push({componentPath:r.componentPath,success:!1,error:`Component not found: ${r.componentPath}`,phase:"validation"}),!s))return i({success:!1,message:"Bulk update failed during validation phase",results:a,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let o=0;for(const r of t)try{const c=await this.updateComponent({componentPath:r.componentPath,properties:r.properties});a.push({componentPath:r.componentPath,success:!0,result:c,phase:"update"}),o++}catch(c){if(a.push({componentPath:r.componentPath,success:!1,error:c.message,phase:"update"}),!s)break}return i({success:o===t.length,message:`Bulk update completed: ${o}/${t.length} successful`,results:a,totalUpdates:t.length,successfulUpdates:o,failedUpdates:t.length-o},"bulkUpdateComponents")},"bulkUpdateComponents")}async getNodeContent(e,t=1){return p(async()=>{const n=`${e}.json`,s=await this.fetch.get(n,{":depth":t.toString()});return{path:e,depth:t,content:s,timestamp:new Date().toISOString()}},"getNodeContent")}}
@@ -1 +1 @@
1
- "use strict";export class AEMOperationError extends Error{code;details;recoverable;retryAfter;constructor(t){super(t.message),this.name="AEMOperationError",this.code=t.code,this.details=t.details,this.recoverable=t.recoverable,this.retryAfter=t.retryAfter}}export const AEM_ERROR_CODES={CONNECTION_FAILED:"CONNECTION_FAILED",TIMEOUT:"TIMEOUT",AUTHENTICATION_FAILED:"AUTHENTICATION_FAILED",UNAUTHORIZED:"UNAUTHORIZED",INVALID_PATH:"INVALID_PATH",INVALID_COMPONENT_TYPE:"INVALID_COMPONENT_TYPE",INVALID_LOCALE:"INVALID_LOCALE",INVALID_PARAMETERS:"INVALID_PARAMETERS",RESOURCE_NOT_FOUND:"RESOURCE_NOT_FOUND",COMPONENT_NOT_FOUND:"COMPONENT_NOT_FOUND",PAGE_NOT_FOUND:"PAGE_NOT_FOUND",UPDATE_FAILED:"UPDATE_FAILED",VALIDATION_FAILED:"VALIDATION_FAILED",REPLICATION_FAILED:"REPLICATION_FAILED",QUERY_FAILED:"QUERY_FAILED",INSUFFICIENT_PERMISSIONS:"INSUFFICIENT_PERMISSIONS",SYSTEM_ERROR:"SYSTEM_ERROR",RATE_LIMITED:"RATE_LIMITED"};export function createAEMError(e,t,n,r=!1,s){return new AEMOperationError({code:e,message:t,details:n,recoverable:r,retryAfter:s})}export function handleAEMHttpError(e,t){if(e.response){const n=e.response.status,r=e.response.data;switch(n){case 401:return createAEMError(AEM_ERROR_CODES.AUTHENTICATION_FAILED,"Authentication failed. Check AEM credentials.",{status:n,data:r});case 403:return createAEMError(AEM_ERROR_CODES.INSUFFICIENT_PERMISSIONS,"Insufficient permissions for this operation.",{status:n,data:r,operation:t});case 404:return createAEMError(AEM_ERROR_CODES.RESOURCE_NOT_FOUND,"Resource not found in AEM.",{status:n,data:r,operation:t});case 429:const s=e.response.headers["retry-after"];return createAEMError(AEM_ERROR_CODES.RATE_LIMITED,"Rate limit exceeded. Please try again later.",{status:n,data:r},!0,s?parseInt(s)*1e3:6e4);case 500:case 502:case 503:return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,"AEM system error. Please try again later.",{status:n,data:r},!0,3e4);default:return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`HTTP ${n}: ${r?.message||"Unknown error"}`,{status:n,data:r,operation:t})}}else return e.code==="ECONNREFUSED"||e.code==="ENOTFOUND"?createAEMError(AEM_ERROR_CODES.CONNECTION_FAILED,"Cannot connect to AEM instance. Check host and network.",{originalError:e.message},!0,5e3):e.code==="ETIMEDOUT"?createAEMError(AEM_ERROR_CODES.TIMEOUT,"Request to AEM timed out.",{originalError:e.message},!0,1e4):createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`Unexpected error during ${t}: ${e.message}`,{originalError:e.message})}export async function safeExecute(e,t,n=3){let r;for(let s=1;s<=n;s++)try{return await e()}catch(E){if(r=E instanceof AEMOperationError?E:handleAEMHttpError(E,t),!r.recoverable||s===n)break;const a=r.retryAfter||Math.pow(2,s)*1e3;console.warn(`[${t}] Attempt ${s} failed, retrying in ${a}ms:`,r.message),await new Promise(i=>setTimeout(i,a))}throw r}export function validateComponentOperation(e,t,n,r){const s=[];if((!e||typeof e!="string")&&s.push("Locale is required and must be a string"),!t||typeof t!="string"?s.push("Page path is required and must be a string"):t.startsWith("/content")||s.push("Page path must start with /content"),(!n||typeof n!="string")&&s.push("Component type is required and must be a string"),(!r||typeof r!="object")&&s.push("Component properties are required and must be an object"),s.length>0)throw createAEMError(AEM_ERROR_CODES.INVALID_PARAMETERS,"Invalid component operation parameters",{errors:s})}export function createSuccessResponse(e,t){return{success:!0,operation:t,timestamp:new Date().toISOString(),data:e}}export function createErrorResponse(e,t){return{success:!1,operation:t,timestamp:new Date().toISOString(),error:{code:e.code,message:e.message,details:e.details,recoverable:e.recoverable,retryAfter:e.retryAfter}}}
1
+ "use strict";import{LOGGER as o}from"../utils/logger.js";export class AEMOperationError extends Error{code;details;recoverable;retryAfter;constructor(t){super(t.message),this.name="AEMOperationError",this.code=t.code,this.details=t.details,this.recoverable=t.recoverable,this.retryAfter=t.retryAfter}}export const AEM_ERROR_CODES={CONNECTION_FAILED:"CONNECTION_FAILED",TIMEOUT:"TIMEOUT",AUTHENTICATION_FAILED:"AUTHENTICATION_FAILED",UNAUTHORIZED:"UNAUTHORIZED",INVALID_PATH:"INVALID_PATH",INVALID_COMPONENT_TYPE:"INVALID_COMPONENT_TYPE",INVALID_LOCALE:"INVALID_LOCALE",INVALID_PARAMETERS:"INVALID_PARAMETERS",RESOURCE_NOT_FOUND:"RESOURCE_NOT_FOUND",COMPONENT_NOT_FOUND:"COMPONENT_NOT_FOUND",PAGE_NOT_FOUND:"PAGE_NOT_FOUND",UPDATE_FAILED:"UPDATE_FAILED",VALIDATION_FAILED:"VALIDATION_FAILED",REPLICATION_FAILED:"REPLICATION_FAILED",QUERY_FAILED:"QUERY_FAILED",INSUFFICIENT_PERMISSIONS:"INSUFFICIENT_PERMISSIONS",SYSTEM_ERROR:"SYSTEM_ERROR",RATE_LIMITED:"RATE_LIMITED"};export function createAEMError(e,t,n,r=!1,s){return new AEMOperationError({code:e,message:t,details:n,recoverable:r,retryAfter:s})}export function handleAEMHttpError(e,t){if(e.response){const n=e.response.status,r=e.response.data;switch(n){case 401:return createAEMError(AEM_ERROR_CODES.AUTHENTICATION_FAILED,"Authentication failed. Check AEM credentials.",{status:n,data:r});case 403:return createAEMError(AEM_ERROR_CODES.INSUFFICIENT_PERMISSIONS,"Insufficient permissions for this operation.",{status:n,data:r,operation:t});case 404:return createAEMError(AEM_ERROR_CODES.RESOURCE_NOT_FOUND,"Resource not found in AEM.",{status:n,data:r,operation:t});case 429:const s=e.response.headers["retry-after"];return createAEMError(AEM_ERROR_CODES.RATE_LIMITED,"Rate limit exceeded. Please try again later.",{status:n,data:r},!0,s?parseInt(s)*1e3:6e4);case 500:case 502:case 503:return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,"AEM system error. Please try again later.",{status:n,data:r},!0,3e4);default:return createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`HTTP ${n}: ${r?.message||"Unknown error"}`,{status:n,data:r,operation:t})}}else return e.code==="ECONNREFUSED"||e.code==="ENOTFOUND"?createAEMError(AEM_ERROR_CODES.CONNECTION_FAILED,"Cannot connect to AEM instance. Check host and network.",{originalError:e.message},!0,5e3):e.code==="ETIMEDOUT"?createAEMError(AEM_ERROR_CODES.TIMEOUT,"Request to AEM timed out.",{originalError:e.message},!0,1e4):createAEMError(AEM_ERROR_CODES.SYSTEM_ERROR,`Unexpected error during ${t}: ${e.message}`,{originalError:e.message})}export async function safeExecute(e,t,n=3){let r;for(let s=1;s<=n;s++)try{return await e()}catch(E){if(r=E instanceof AEMOperationError?E:handleAEMHttpError(E,t),!r.recoverable||s===n)break;const a=r.retryAfter||Math.pow(2,s)*1e3;o.warn(`[${t}] Attempt ${s} failed, retrying in ${a}ms:`,r.message),await new Promise(i=>setTimeout(i,a))}throw r}export function validateComponentOperation(e,t,n,r){const s=[];if((!e||typeof e!="string")&&s.push("Locale is required and must be a string"),!t||typeof t!="string"?s.push("Page path is required and must be a string"):t.startsWith("/content")||s.push("Page path must start with /content"),(!n||typeof n!="string")&&s.push("Component type is required and must be a string"),(!r||typeof r!="object")&&s.push("Component properties are required and must be an object"),s.length>0)throw createAEMError(AEM_ERROR_CODES.INVALID_PARAMETERS,"Invalid component operation parameters",{errors:s})}export function createSuccessResponse(e,t){return{success:!0,operation:t,timestamp:new Date().toISOString(),data:e}}export function createErrorResponse(e,t){return{success:!1,operation:t,timestamp:new Date().toISOString(),error:{code:e.code,message:e.message,details:e.details,recoverable:e.recoverable,retryAfter:e.retryAfter}}}
@@ -1 +1 @@
1
- "use strict";import{getAccessToken as c}from"./aem.auth.js";export class AEMFetch{fetch;config;constructor(t){this.config=t,this.fetch=null}async init(){const t=await this.getAuthToken(this.config);this.fetch=this.getFetchInstance(t)}getFetchInstance(t){return(e,s={})=>{const n=new Headers(s.headers||{});return n.set("Authorization",`Basic ${t}`),n.set("Accept","application/json"),n.has("Content-Type")||n.set("Content-Type","application/json"),fetch(e,{...s,headers:n})}}async getAuthToken(t){if(t.clientId&&t.clientSecret)return c(t.clientId,t.clientSecret,t.scope);if(t.username&&t.password)return Buffer.from(`${t.username}:${t.password}`).toString("base64");throw new Error("No authentication credentials provided")}getTimeoutOptions(t){let e,s,n;const i=t||this.config.timeout;return i&&(e=new AbortController,n=e.signal,s=setTimeout(()=>e.abort(),i)),{signal:n,timeoutId:s}}buildUrlWithParams(t,e){const s=this.config.host.endsWith("/")?this.config.host.slice(0,-1):this.config.host,n=t.startsWith("/")?t:`/${t}`,i=`${s}${n}`;if(!e||Object.keys(e).length===0)return i;const r=new URL(i);return Object.entries(e).forEach(([o,a])=>{a!=null&&r.searchParams.append(o,String(a))}),r.toString()}async request(t,e={},s){if(!this.fetch)throw new Error("AEMFetch not initialized. Call await init(config) before making requests.");const{timeoutId:n,signal:i}=this.getTimeoutOptions(s);s&&(e.signal=i);let r;try{if(r=await this.fetch(t,e),r.status===401){console.warn(`AEM request to ${t} returned 401 Unauthorized. Attempting to refresh token...`);const o=await this.getAuthToken(this.config);this.fetch=this.getFetchInstance(o),r=await this.fetch(t,e)}if(!r.ok)throw new Error(`AEM ${e.method||"GET"} failed: ${r.status}`);return r.json()}finally{n&&clearTimeout(n)}}async get(t,e,s={},n){const i=this.buildUrlWithParams(t,e);return this.request(i,s,n)}async post(t,e,s={},n){let i,r=new Headers(s.headers||{});e instanceof URLSearchParams?(i=e,r.set("Content-Type","application/x-www-form-urlencoded")):(i=JSON.stringify(e),r.set("Content-Type","application/json"));const o=this.buildUrlWithParams(t);return this.request(o,{...s,method:"POST",body:i,headers:r},n)}async delete(t,e={},s){const n=this.buildUrlWithParams(t);return this.request(n,{...e,method:"DELETE"},s)}}
1
+ "use strict";import{getAccessToken as h}from"./aem.auth.js";import{LOGGER as c}from"../utils/logger.js";export class AEMFetch{fetch;config;token;tokenExpiry;constructor(t){this.config=t,this.fetch=null,this.token="",this.tokenExpiry=0}async init(){this.token=await this.getAuthToken(this.config.auth),this.fetch=this.getFetchInstance()}getFetchInstance(){return(t,e={})=>{const n=new Headers(e.headers||{});return n.set("Authorization",`Basic ${this.token}`),n.set("Accept","application/json"),n.has("Content-Type")||n.set("Content-Type","application/json"),fetch(t,{...e,headers:n})}}async getAuthToken(t){if(t.clientId&&t.clientSecret){const e=Date.now();if(this.token&&e<this.tokenExpiry)return this.token;const n=await h(t.clientId,t.clientSecret,t.scope);return this.token=n.access_token,this.tokenExpiry=e+(n.expires_in-60)*1e3,this.token}if(t.username&&t.password)return Buffer.from(`${t.username}:${t.password}`).toString("base64");throw new Error("No authentication credentials provided")}async refreshAuthToken(){this.token="",this.tokenExpiry=0,this.token=await this.getAuthToken(this.config.auth)}getTimeoutOptions(t){let e,n,i;const s=t||this.config.timeout;return s&&(e=new AbortController,i=e.signal,n=setTimeout(()=>e.abort(),s)),{signal:i,timeoutId:n}}buildUrlWithParams(t,e){const n=this.config.host.endsWith("/")?this.config.host.slice(0,-1):this.config.host,i=t.startsWith("/")?t:`/${t}`,s=`${n}${i}`;if(!e||Object.keys(e).length===0)return s;const r=new URL(s);return Object.entries(e).forEach(([o,a])=>{a!=null&&r.searchParams.append(o,String(a))}),r.toString()}async request(t,e={},n){if(!this.fetch)throw new Error("AEMFetch not initialized. Call await init(config) before making requests.");const{timeoutId:i,signal:s}=this.getTimeoutOptions(n);n&&(e.signal=s);let r;try{if(r=await this.fetch(t,e),r.status===401&&(c.warn(`AEM request to ${t} returned 401 Unauthorized. Attempting to refresh token...`),await this.refreshAuthToken(),r=await this.fetch(t,e)),!r.ok)throw new Error(`AEM ${e.method||"GET"} failed: ${r.status}`);return r.json()}finally{i&&clearTimeout(i)}}async get(t,e,n={},i){const s=this.buildUrlWithParams(t,e);return this.request(s,n,i)}async post(t,e,n={},i){let s,r=new Headers(n.headers||{});e instanceof URLSearchParams?(s=e,r.set("Content-Type","application/x-www-form-urlencoded")):(s=JSON.stringify(e),r.set("Content-Type","application/json"));const o=this.buildUrlWithParams(t);return this.request(o,{...n,method:"POST",body:s,headers:r},i)}async delete(t,e={},n){const i=this.buildUrlWithParams(t);return this.request(i,{...e,method:"DELETE"},n)}}
package/dist/cli.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";import r from"yargs";import{hideBin as a}from"yargs/helpers";import{startServer as t}from"./index.js";const s=r(a(process.argv)).options({host:{type:"string",default:"http://localhost:4502",alias:"H"},user:{type:"string",default:"admin",alias:"u"},pass:{type:"string",default:"admin",alias:"p"},mcpPort:{type:"number",default:3e3,alias:"m"}}).help().alias("h","help").parseSync();s.help&&process.exit(0);const{host:e,user:i,pass:o,mcpPort:p}=s;t({host:e,user:i,pass:o,mcpPort:p});
2
+ "use strict";import r from"yargs";import{hideBin as a}from"yargs/helpers";import{startServer as t}from"./index.js";const s=r(a(process.argv)).options({host:{type:"string",default:"http://localhost:4502",alias:"H"},user:{type:"string",default:"admin",alias:"u"},pass:{type:"string",default:"admin",alias:"p"},mcpPort:{type:"number",default:8502,alias:"m"}}).help().alias("h","help").parseSync();s.help&&process.exit(0);const{host:e,user:i,pass:o,mcpPort:p}=s;t({host:e,user:i,pass:o,mcpPort:p});
@@ -1 +1 @@
1
- "use strict";import{AEMConnector as a}from"../aem/aem.connector.js";import{handleAEMHttpError as o}from"../aem/aem.errors.js";export class MCPRequestHandler{aemConnector;config;constructor(t){this.config=t,this.aemConnector=new a(t)}async init(){this.aemConnector.isInitialized?console.log("AEM Connector already initialized."):(await this.aemConnector.init(),console.log("AEM Connector initialized."))}async handleRequest(t,e){try{await this.init()}catch(n){throw console.error("ERROR initializing MCP Server",n.message),o(n,"MCP Server Initialization")}try{switch(t){case"validateComponent":return await this.aemConnector.validateComponent(e);case"updateComponent":return await this.aemConnector.updateComponent(e);case"undoChanges":return await this.aemConnector.undoChanges(e);case"scanPageComponents":return await this.aemConnector.scanPageComponents(e.pagePath);case"fetchSites":return await this.aemConnector.fetchSites();case"fetchLanguageMasters":return await this.aemConnector.fetchLanguageMasters(e.site);case"fetchAvailableLocales":return await this.aemConnector.fetchAvailableLocales(e.site,e.languageMasterPath);case"replicateAndPublish":return await this.aemConnector.replicateAndPublish(e.selectedLocales,e.componentData,e.localizedOverrides);case"getAllTextContent":return await this.aemConnector.getAllTextContent(e.pagePath);case"getPageTextContent":return await this.aemConnector.getPageTextContent(e.pagePath);case"getPageImages":return await this.aemConnector.getPageImages(e.pagePath);case"updateImagePath":return await this.aemConnector.updateImagePath(e.componentPath,e.newImagePath);case"getPageContent":return await this.aemConnector.getPageContent(e.pagePath);case"listPages":return await this.aemConnector.listPages(e.siteRoot||e.path||"/content",e.depth||1,e.limit||20);case"getNodeContent":return await this.aemConnector.getNodeContent(e.path,e.depth||1);case"listChildren":return await this.aemConnector.listChildren(e.path);case"getPageProperties":return await this.aemConnector.getPageProperties(e.pagePath);case"searchContent":return await this.aemConnector.searchContent(e);case"executeJCRQuery":return await this.aemConnector.executeJCRQuery(e.query,e.limit);case"getAssetMetadata":return await this.aemConnector.getAssetMetadata(e.assetPath);case"getStatus":return this.getWorkflowStatus(e.workflowId);case"enhancedPageSearch":return await this.aemConnector.searchContent({fulltext:e.searchTerm,path:e.basePath,type:"cq:Page",limit:20});case"createPage":return await this.aemConnector.createPage(e);case"deletePage":return await this.aemConnector.deletePage(e);case"createComponent":return await this.aemConnector.createComponent(e);case"deleteComponent":return await this.aemConnector.deleteComponent(e);case"unpublishContent":return await this.aemConnector.unpublishContent(e);case"activatePage":return await this.aemConnector.activatePage(e);case"deactivatePage":return await this.aemConnector.deactivatePage(e);case"uploadAsset":return await this.aemConnector.uploadAsset(e);case"updateAsset":return await this.aemConnector.updateAsset(e);case"deleteAsset":return await this.aemConnector.deleteAsset(e);case"getTemplates":case"getTemplateStructure":return await this.aemConnector.getTemplateStructure(e.templatePath);case"bulkUpdateComponents":return await this.aemConnector.bulkUpdateComponents(e);default:throw new Error(`Unknown method: ${t}`)}}catch(n){return{error:n.message,method:t,params:e}}}getWorkflowStatus(t){return{success:!0,workflowId:t,status:"completed",message:"Mock workflow status - always returns completed",timestamp:new Date().toISOString()}}}
1
+ "use strict";import{AEMConnector as o}from"../aem/aem.connector.js";import{handleAEMHttpError as r}from"../aem/aem.errors.js";import{LOGGER as a}from"../utils/logger.js";export class MCPRequestHandler{aemConnector;config;constructor(t){this.config=t,this.aemConnector=new o(t)}async init(){this.aemConnector.isInitialized?a.log("AEM Connector already initialized."):(await this.aemConnector.init(),a.log("AEM Connector initialized."))}async handleRequest(t,e){try{await this.init()}catch(n){throw a.error("ERROR initializing MCP Server",n.message),r(n,"MCP Server Initialization")}try{switch(t){case"validateComponent":return await this.aemConnector.validateComponent(e);case"updateComponent":return await this.aemConnector.updateComponent(e);case"undoChanges":return await this.aemConnector.undoChanges(e);case"scanPageComponents":return await this.aemConnector.scanPageComponents(e.pagePath);case"fetchSites":return await this.aemConnector.fetchSites();case"fetchLanguageMasters":return await this.aemConnector.fetchLanguageMasters(e.site);case"fetchAvailableLocales":return await this.aemConnector.fetchAvailableLocales(e.site,e.languageMasterPath);case"replicateAndPublish":return await this.aemConnector.replicateAndPublish(e.selectedLocales,e.componentData,e.localizedOverrides);case"getAllTextContent":return await this.aemConnector.getAllTextContent(e.pagePath);case"getPageTextContent":return await this.aemConnector.getPageTextContent(e.pagePath);case"getPageImages":return await this.aemConnector.getPageImages(e.pagePath);case"updateImagePath":return await this.aemConnector.updateImagePath(e.componentPath,e.newImagePath);case"getPageContent":return await this.aemConnector.getPageContent(e.pagePath);case"listPages":return await this.aemConnector.listPages(e.siteRoot||e.path||"/content",e.depth||1,e.limit||20);case"getNodeContent":return await this.aemConnector.getNodeContent(e.path,e.depth||1);case"listChildren":return await this.aemConnector.listChildren(e.path);case"getPageProperties":return await this.aemConnector.getPageProperties(e.pagePath);case"searchContent":return await this.aemConnector.searchContent(e);case"executeJCRQuery":return await this.aemConnector.executeJCRQuery(e.query,e.limit);case"getAssetMetadata":return await this.aemConnector.getAssetMetadata(e.assetPath);case"getStatus":return this.getWorkflowStatus(e.workflowId);case"enhancedPageSearch":return await this.aemConnector.searchContent({fulltext:e.searchTerm,path:e.basePath,type:"cq:Page",limit:20});case"createPage":return await this.aemConnector.createPage(e);case"deletePage":return await this.aemConnector.deletePage(e);case"createComponent":return await this.aemConnector.createComponent(e);case"deleteComponent":return await this.aemConnector.deleteComponent(e);case"unpublishContent":return await this.aemConnector.unpublishContent(e);case"activatePage":return await this.aemConnector.activatePage(e);case"deactivatePage":return await this.aemConnector.deactivatePage(e);case"uploadAsset":return await this.aemConnector.uploadAsset(e);case"updateAsset":return await this.aemConnector.updateAsset(e);case"deleteAsset":return await this.aemConnector.deleteAsset(e);case"getTemplates":case"getTemplateStructure":return await this.aemConnector.getTemplateStructure(e.templatePath);case"bulkUpdateComponents":return await this.aemConnector.bulkUpdateComponents(e);default:throw new Error(`Unknown method: ${t}`)}}catch(n){return{error:n.message,method:t,params:e}}}getWorkflowStatus(t){return{success:!0,workflowId:t,status:"completed",message:"Mock workflow status - always returns completed",timestamp:new Date().toISOString()}}}
@@ -1 +1 @@
1
- "use strict";import{randomUUID as c}from"node:crypto";import{StreamableHTTPServerTransport as m}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as u}from"@modelcontextprotocol/sdk/types.js";import{transports as n}from"./mcp.transports.js";import{createMCPServer as p}from"./mcp.server.js";export const handleRequest=async(e,s,t)=>{console.log("1.Received MCP request:",e.body);const{jsonrpc:a,id:l,method:d,params:v}=e.body;if(a!=="2.0"||!d){s.status(400).json({jsonrpc:"2.0",id:l||null,error:{code:-32600,message:"Invalid Request",data:"Must be valid JSON-RPC 2.0"}});return}try{const o=e.headers["mcp-session-id"];let r;if(o&&n[o])console.log(`Session exists: ${o}`),r=n[o];else if(!o&&u(e.body)){r=new m({sessionIdGenerator:()=>c(),enableJsonResponse:!0,onsessioninitialized:i=>{console.log(`Session initialized with ID: ${i}`),n[i]=r}}),console.log("Connecting to MCP server with CLI params:",t),await p(t).connect(r),await r.handleRequest(e,s,e.body);return}else{console.log("Invalid request - no session ID or not initialization request"),s.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await r.handleRequest(e,s,e.body)}catch(o){console.error("Error handling MCP request:",o),s.headersSent||s.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}};
1
+ "use strict";import{randomUUID as p}from"node:crypto";import{StreamableHTTPServerTransport as u}from"@modelcontextprotocol/sdk/server/streamableHttp.js";import{isInitializeRequest as c}from"@modelcontextprotocol/sdk/types.js";import{transports as n}from"./mcp.transports.js";import{createMCPServer as R}from"./mcp.server.js";import{LOGGER as t}from"../utils/logger.js";export const handleRequest=async(e,s,i)=>{t.log("1.Received MCP request:",e.body);const{jsonrpc:d,id:l,method:m,params:f}=e.body;if(d!=="2.0"||!m){s.status(400).json({jsonrpc:"2.0",id:l||null,error:{code:-32600,message:"Invalid Request",data:"Must be valid JSON-RPC 2.0"}});return}try{const r=e.headers["mcp-session-id"];let o;if(r&&n[r])t.log(`Session exists: ${r}`),o=n[r];else if(!r&&c(e.body)){o=new u({sessionIdGenerator:()=>p(),enableJsonResponse:!0,onsessioninitialized:a=>{t.log(`Session initialized with ID: ${a}`),n[a]=o}}),t.log("Connecting to MCP server with CLI params:",i),await R(i).connect(o),await o.handleRequest(e,s,e.body);return}else{t.log("Invalid request - no session ID or not initialization request"),s.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await o.handleRequest(e,s,e.body)}catch(r){t.error("Error handling MCP request:",r),s.headersSent||s.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}};
@@ -1 +1 @@
1
- "use strict";import{CallToolRequestSchema as i,ListToolsRequestSchema as u}from"@modelcontextprotocol/sdk/types.js";import{Server as p}from"@modelcontextprotocol/sdk/server/index.js";import{tools as s}from"./mcp.tools.js";import{MCPRequestHandler as R}from"./mcp.aem-handler.js";export const createMCPServer=a=>{const n=new R(a),l={name:"aem-mcp-server",version:"1.0.0"},c={capabilities:{resources:{},tools:{}}},r=new p(l,c);return r.setRequestHandler(u,async()=>(console.log("2. Received ListToolsRequest",s),{tools:s})),r.setRequestHandler(i,async t=>{const{name:m,arguments:o}=t.params;if(console.log("Received CallToolRequestSchema",t.params),!o)return{content:[{type:"text",text:"Error: No arguments provided"}],isError:!0};try{const e=await n.handleRequest(m,o);return{content:[{type:"text",text:JSON.stringify(e,null,2)}]}}catch(e){return console.error("ERROR CallToolRequestSchema",e.message),{content:[{type:"text",text:`Error: ${e.message}`}],isError:!0}}}),r};
1
+ "use strict";import{CallToolRequestSchema as u,ListToolsRequestSchema as R,InitializeRequestSchema as p,LATEST_PROTOCOL_VERSION as d,SUPPORTED_PROTOCOL_VERSIONS as v}from"@modelcontextprotocol/sdk/types.js";import{Server as S}from"@modelcontextprotocol/sdk/server/index.js";import{tools as c}from"./mcp.tools.js";import{MCPRequestHandler as E}from"./mcp.aem-handler.js";import{LOGGER as n}from"../utils/logger.js";export const createMCPServer=l=>{const m=new E(l),a={name:"aem-mcp-server",version:"1.0.0"},i={capabilities:{resources:{},tools:{}},instructions:"This is an AEM MCP server that provides tools for managing AEM components and content."},t=new S(a,i);return t.setRequestHandler(p,e=>{const o=e.params.protocolVersion,r=v.includes(o)?o:d;return n.log("1. Received InitializeRequest",e,"response:",{protocolVersion:r}),{protocolVersion:r,...i,serverInfo:a}}),t.setRequestHandler(R,async()=>(n.log("2. Received ListToolsRequest",c),{tools:c})),t.setRequestHandler(u,async e=>{const{name:o,arguments:r}=e.params;if(n.log("3. Received CallToolRequestSchema",e.params),!r)return{content:[{type:"text",text:"Error: No arguments provided"}],isError:!0};try{const s=await m.handleRequest(o,r);return{content:[{type:"text",text:JSON.stringify(s,null,2)}]}}catch(s){return n.error("ERROR CallToolRequestSchema",s.message),{content:[{type:"text",text:`Error: ${s.message}`}],isError:!0}}}),t};
@@ -1 +1 @@
1
- "use strict";import n from"express";import p from"cors";import{handleRequest as l}from"../mcp/mcp.server-handler.js";import{AEMConnector as m}from"../aem/aem.connector.js";import{config as c}from"../config.js";const d=(s={})=>{const e=n();e.use(p({origin:"*",exposedHeaders:["Mcp-Session-Id"]})),e.use(n.json()),e.use(n.json({limit:"10mb"})),e.use(n.urlencoded({extended:!0}));const r=new m(s);return e.get("/health",async(o,t)=>{try{await r.init();const i={status:"healthy",aem:await r.testConnection()?"connected":"disconnected",mcp:"ready",timestamp:new Date().toISOString(),version:c.APP_VERSION||"1.0.0"};t.json(i)}catch(a){t.status(500).json({status:"unhealthy",error:a.message,timestamp:new Date().toISOString()})}}),e.post("/mcp",async(o,t)=>{await l(o,t,s)}),e.get("/mcp",async(o,t)=>{t.status(405).set("Allow","POST").send("Method Not Allowed")}),e.delete("/mcp",async(o,t)=>{console.log("Received DELETE MCP request"),t.writeHead(405).end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}))}),e.get("/",(o,t)=>{t.json({name:"AEM MCP Gateway Server",description:"A Model Context Protocol server for Adobe Experience Manager",version:c.APP_VERSION||"1.0.0",endpoints:{health:{method:"GET",path:"/health",description:"Health check for all services"},mcp:{method:"POST",path:"/mcp",description:"JSON-RPC endpoint for MCP calls"},mcpMethods:{method:"GET",path:"/mcp/methods",description:"List all available MCP methods"}},architecture:"MCP integration",timestamp:new Date().toISOString()})}),e};export const startServer=(s={})=>{const{mcpPort:e=3e3}=s||{};d(s).listen(e,o=>{o&&(console.error("Failed to start server:",o),process.exit(1)),console.log(`AEM MCP Server listening on port ${e}`)})};process.on("SIGINT",async()=>{console.log("Shutting down server..."),process.exit(0)});
1
+ "use strict";import r from"express";import d from"cors";import{handleRequest as l}from"../mcp/mcp.server-handler.js";import{AEMConnector as h}from"../aem/aem.connector.js";import{config as c}from"../config.js";import{LOGGER as n}from"../utils/logger.js";const u=(s={})=>{const e=r();e.use(d({origin:"*",exposedHeaders:["Mcp-Session-Id"]})),e.use(r.json()),e.use(r.json({limit:"10mb"})),e.use(r.urlencoded({extended:!0}));const i=new h(s);return e.get("/health",async(o,t)=>{try{const{aem:a,auth:p}=await i.testConnection(),m={status:"healthy",aem:a?"connected":"disconnected",auth:p?"authorized":"not authorized",mcp:"ready",timestamp:new Date().toISOString(),version:c.APP_VERSION||"1.0.0"};t.json(m)}catch(a){t.status(500).json({status:"unhealthy",error:a.message,timestamp:new Date().toISOString()})}}),e.post("/mcp",async(o,t)=>{await l(o,t,s)}),e.get("/mcp",async(o,t)=>{t.status(405).set("Allow","POST").send("Method Not Allowed")}),e.delete("/mcp",async(o,t)=>{n.log("Received DELETE MCP request"),t.writeHead(405).end(JSON.stringify({jsonrpc:"2.0",error:{code:-32e3,message:"Method not allowed."},id:null}))}),e.get("/",(o,t)=>{t.json({name:"AEM MCP Gateway Server",description:"A Model Context Protocol server for Adobe Experience Manager",version:c.APP_VERSION||"1.0.0",endpoints:{health:{method:"GET",path:"/health",description:"Health check for all services"},mcp:{method:"POST",path:"/mcp",description:"JSON-RPC endpoint for MCP calls"},mcpMethods:{method:"GET",path:"/mcp/methods",description:"List all available MCP methods"}},architecture:"MCP integration",timestamp:new Date().toISOString()})}),e};export const startServer=(s={})=>{const{mcpPort:e=8502}=s||{};u(s).listen(e,o=>{o&&(n.error("Failed to start server:",o),process.exit(1)),n.log(`0. AEM MCP Server listening on port ${e}`)})};process.on("SIGINT",async()=>{n.log("Shutting down server..."),process.exit(0)});
@@ -0,0 +1,2 @@
1
+ "use strict";function o(){const c=(new Error().stack?.split(`
2
+ `)||[])[3]||"",e=c.match(/\(([^)]+)\)/);return e?e[1]:c.trim()}const r=process.env.ENABLE_LOGGER==="true";export const LOGGER={log:(...n)=>{r&&console.log(`[${o()}]`,...n)},info:(...n)=>{r&&console.info(`[${o()}]`,...n)},warn:(...n)=>{r&&console.warn(`[${o()}]`,...n)},error:(...n)=>{r&&console.error(`[${o()}]`,...n)}};
package/docs/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.3.0](https://github.com/easingthemes/aem-mcp-server/compare/v1.2.0...v1.3.0) (2025-08-10)
2
+
3
+
4
+ ### Features
5
+
6
+ * add logger, change port ([f4ee48b](https://github.com/easingthemes/aem-mcp-server/commit/f4ee48b32fde2034b53f29fb2efa3aef9ba20f39))
7
+
1
8
  # [1.2.0](https://github.com/easingthemes/aem-mcp-server/compare/v1.1.5...v1.2.0) (2025-08-10)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aem-mcp-server",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "AEM Model Context Protocol (MCP) server",
5
5
  "private": false,
6
6
  "publishConfig": {