aem-mcp-server 1.0.11 → 1.1.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 +0 -3
- package/dist/aem/aem.config.js +1 -1
- package/dist/aem/aem.connector.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/mcp/mcp.aem-handler.js +1 -1
- package/dist/mcp/mcp.server-handler.js +1 -1
- package/dist/mcp/mcp.tools.js +1 -1
- package/dist/server/app.server.js +1 -1
- package/docs/CHANGELOG.md +14 -0
- package/package.json +5 -15
- package/dist/explorer/api.explorer.js +0 -1
- package/dist/explorer/api.spec.js +0 -1
package/README.md
CHANGED
|
@@ -104,9 +104,6 @@ Sample for AI-based code editors or custom clients:
|
|
|
104
104
|
## API Documentation
|
|
105
105
|
|
|
106
106
|
For detailed API documentation, please refer to the [API Docs](docs/API.md).
|
|
107
|
-
- Enable the API explorer with the `-e` flag to access Swagger UI:
|
|
108
|
-
```aem-mcp -e```
|
|
109
|
-
- Open http://localhost:3000/api-docs to explore the API endpoints.
|
|
110
107
|
|
|
111
108
|
## Contribution
|
|
112
109
|
Contributions are welcome! Please open issues or pull requests for bug fixes, features, or documentation improvements.
|
package/dist/aem/aem.config.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export function getAEMConfig(t){return{contentPaths:{sitesRoot:"/content",assetsRoot:"/content/dam",templatesRoot:"/conf",experienceFragmentsRoot:"/content/experience-fragments"},replication:{publisherUrls:t.AEM_PUBLISHER_URLS?.split(",")||["http://localhost:4503"],defaultReplicationAgent:"publish"},components:{
|
|
1
|
+
"use strict";export function getAEMConfig(t){return{contentPaths:{sitesRoot:"/content",assetsRoot:"/content/dam",templatesRoot:"/conf",experienceFragmentsRoot:"/content/experience-fragments"},replication:{publisherUrls:t.AEM_PUBLISHER_URLS?.split(",")||["http://localhost:4503"],defaultReplicationAgent:"publish"},components:{defaultProperties:{"jcr:primaryType":"nt:unstructured","sling:resourceType":"foundation/components/text"}},queries:{maxLimit:parseInt(t.AEM_QUERY_MAX_LIMIT||"100"),defaultLimit:parseInt(t.AEM_QUERY_DEFAULT_LIMIT||"20"),timeoutMs:parseInt(t.AEM_QUERY_TIMEOUT||"30000")},validation:{maxDepth:parseInt(t.AEM_MAX_DEPTH||"5"),allowedLocales:["en"]}}}export function isValidContentPath(t,n){return Object.values(n.contentPaths).some(e=>t.startsWith(e))}export function isValidLocale(t,n){if(!t)return!1;const o=t.toLowerCase();return n.validation.allowedLocales.some(e=>e.toLowerCase()===o||o==="en"&&e.toLowerCase().startsWith("en"))}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import y from"axios";import{getAEMConfig as j,isValidContentPath as h,isValidComponentType as u,isValidLocale as P}from"./aem.config.js";import{createAEMError as l,handleAEMHttpError as A,safeExecute as c,validateComponentOperation as b,createSuccessResponse as p,AEM_ERROR_CODES as m}from"./aem.errors.js";export class AEMConnector{config;auth;aemConfig;constructor(t){this.config=this.loadConfig(t),this.aemConfig=j({}),this.auth={username:this.config.aem.serviceUser.username,password:this.config.aem.serviceUser.password}}loadConfig(t={}){return{aem:{host:t.host||"http://localhost:4502",author:t.host||"http://localhost:4502",publish:"http://localhost:4503",serviceUser:{username:t.user||"admin",password:t.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"}}}createAxiosInstance(){return y.create({baseURL:this.config.aem.host,auth:this.auth,timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"}})}async testConnection(){try{console.log("Testing AEM connection to:",this.config.aem.host);const e=await this.createAxiosInstance().get("/libs/granite/core/content/login.html",{timeout:5e3,validateStatus:n=>n<500});return console.log("\u2705 AEM connection successful! Status:",e.status),!0}catch(t){return console.error("\u274C AEM connection failed:",t.message),t.response&&(console.error(" Status:",t.response.status),console.error(" URL:",t.config?.url)),!1}}async validateComponent(t){return c(async()=>{const e=t.locale,n=t.pagePath||t.page_path,s=t.component,o=t.props;if(b(e,n,s,o),!P(e,this.aemConfig))throw l(m.INVALID_LOCALE,`Locale '${e}' is not supported`,{locale:e,allowedLocales:this.aemConfig.validation.allowedLocales});if(!h(n,this.aemConfig))throw l(m.INVALID_PATH,`Path '${n}' is not within allowed content roots`,{path:n,allowedRoots:Object.values(this.aemConfig.contentPaths)});if(!u(s,this.aemConfig))throw l(m.INVALID_COMPONENT_TYPE,`Component type '${s}' is not allowed`,{component:s,allowedTypes:this.aemConfig.components.allowedTypes});const r=await this.createAxiosInstance().get(`${n}.json`,{params:{":depth":"2"},timeout:this.aemConfig.queries.timeoutMs}),i=this.validateComponentProps(r.data,s,o);return p({message:"Component validation completed successfully",pageData:r.data,component:s,locale:e,validation:i,configUsed:{allowedLocales:this.aemConfig.validation.allowedLocales,allowedComponents:this.aemConfig.components.allowedTypes}},"validateComponent")},"validateComponent")}validateComponentProps(t,e,n){const s=[],o=[];return e==="text"&&!n.text&&!n.richText&&s.push("Text component should have text or richText property"),e==="image"&&!n.fileReference&&!n.src&&o.push("Image component requires fileReference or src property"),{valid:o.length===0,errors:o,warnings:s,componentType:e,propsValidated:Object.keys(n).length}}async updateComponent(t){return c(async()=>{if(!t.componentPath||typeof t.componentPath!="string")throw l(m.INVALID_PARAMETERS,"Component path is required and must be a string");if(!t.properties||typeof t.properties!="object")throw l(m.INVALID_PARAMETERS,"Properties are required and must be an object");if(!h(t.componentPath,this.aemConfig))throw l(m.INVALID_PATH,`Component path '${t.componentPath}' is not within allowed content roots`,{path:t.componentPath,allowedRoots:Object.values(this.aemConfig.contentPaths)});const e=this.createAxiosInstance();try{await e.get(`${t.componentPath}.json`)}catch(a){throw a.response?.status===404?l(m.COMPONENT_NOT_FOUND,`Component not found at path: ${t.componentPath}`,{componentPath:t.componentPath}):A(a,"updateComponent")}const n=new URLSearchParams;Object.entries(t.properties).forEach(([a,r])=>{r==null?n.append(`${a}@Delete`,""):Array.isArray(r)?r.forEach(i=>{n.append(`${a}`,i.toString())}):typeof r=="object"?n.append(a,JSON.stringify(r)):n.append(a,r.toString())});const s=await e.post(t.componentPath,n,{headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},timeout:this.aemConfig.queries.timeoutMs}),o=await e.get(`${t.componentPath}.json`);return p({message:"Component updated successfully",path:t.componentPath,properties:t.properties,updatedProperties:o.data,response:s.data,verification:{success:!0,propertiesChanged:Object.keys(t.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async undoChanges(t){return p({message:"undoChanges is not implemented. Please use AEM version history for undo/rollback.",request:t,timestamp:new Date().toISOString()},"undoChanges")}async scanPageComponents(t){return c(async()=>{const n=await this.createAxiosInstance().get(`${t}.infinity.json`),s=[],o=(a,r)=>{!a||typeof a!="object"||(a["sling:resourceType"]&&s.push({path:r,resourceType:a["sling:resourceType"],properties:{...a}}),Object.entries(a).forEach(([i,g])=>{if(typeof g=="object"&&g!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const d=r?`${r}/${i}`:i;o(g,d)}}))};return n.data["jcr:content"]?o(n.data["jcr:content"],"jcr:content"):o(n.data,t),p({pagePath:t,components:s,totalComponents:s.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return c(async()=>{const e=await this.createAxiosInstance().get("/content.json",{params:{":depth":"2"}}),n=[];return Object.entries(e.data).forEach(([s,o])=>{s.startsWith("jcr:")||s.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&n.push({name:s,path:`/content/${s}`,title:o["jcr:content"]["jcr:title"]||s,template:o["jcr:content"]["cq:template"],lastModified:o["jcr:content"]["cq:lastModified"]})}),p({sites:n,totalCount:n.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(t){return c(async()=>{const n=await this.createAxiosInstance().get(`/content/${t}.json`,{params:{":depth":"3"}}),s=[];return Object.entries(n.data).forEach(([o,a])=>{o.startsWith("jcr:")||o.startsWith("sling:")||a&&typeof a=="object"&&a["jcr:content"]&&s.push({name:o,path:`/content/${o}`,title:a["jcr:content"]["jcr:title"]||o,language:a["jcr:content"]["jcr:language"]||"en"})}),p({site:t,languageMasters:s},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(t,e){return c(async()=>{const s=await this.createAxiosInstance().get(`${e}.json`,{params:{":depth":"2"}}),o=[];return Object.entries(s.data).forEach(([a,r])=>{a.startsWith("jcr:")||a.startsWith("sling:")||r&&typeof r=="object"&&o.push({name:a,title:r["jcr:content"]?.["jcr:title"]||a,language:r["jcr:content"]?.["jcr:language"]||a})}),p({site:t,languageMasterPath:e,availableLocales:o},"fetchAvailableLocales")},"fetchAvailableLocales")}async replicateAndPublish(t,e,n){return c(async()=>p({message:"Replication simulated",selectedLocales:t,componentData:e,localizedOverrides:n},"replicateAndPublish"),"replicateAndPublish")}async getAllTextContent(t){return c(async()=>{const n=await this.createAxiosInstance().get(`${t}.infinity.json`),s=[],o=(a,r)=>{!a||typeof a!="object"||((a.text||a["jcr:title"]||a["jcr:description"])&&s.push({path:r,title:a["jcr:title"],text:a.text,description:a["jcr:description"]}),Object.entries(a).forEach(([i,g])=>{if(typeof g=="object"&&g!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const d=r?`${r}/${i}`:i;o(g,d)}}))};return n.data["jcr:content"]?o(n.data["jcr:content"],"jcr:content"):o(n.data,t),p({pagePath:t,textContent:s},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(t){return c(async()=>this.getAllTextContent(t),"getPageTextContent")}async getPageImages(t){return c(async()=>{const n=await this.createAxiosInstance().get(`${t}.infinity.json`),s=[],o=(a,r)=>{!a||typeof a!="object"||((a.fileReference||a.src)&&s.push({path:r,fileReference:a.fileReference,src:a.src,alt:a.alt||a.altText,title:a["jcr:title"]||a.title}),Object.entries(a).forEach(([i,g])=>{if(typeof g=="object"&&g!==null&&!i.startsWith("rep:")&&!i.startsWith("oak:")){const d=r?`${r}/${i}`:i;o(g,d)}}))};return n.data["jcr:content"]?o(n.data["jcr:content"],"jcr:content"):o(n.data,t),p({pagePath:t,images:s},"getPageImages")},"getPageImages")}async updateImagePath(t,e){return c(async()=>this.updateComponent({componentPath:t,properties:{fileReference:e}}),"updateImagePath")}async getPageContent(t){return c(async()=>{const n=await this.createAxiosInstance().get(`${t}.infinity.json`);return p({pagePath:t,content:n.data},"getPageContent")},"getPageContent")}async listChildren(t,e=1){return c(async()=>((await this.createAxiosInstance().get(this.config.aem.endpoints.query,{params:{path:t,type:"cq:Page","p.nodedepth":1,"p.limit":1e3}})).data.hits||[]).map(a=>({name:a.name,path:a.path,primaryType:"cq:Page",title:a.title||a.name})),"listChildren")}async listPages(t,e=1,n=20){return c(async()=>{const o=await this.createAxiosInstance().get(this.config.aem.endpoints.query,{params:{path:t,type:"cq:Page","p.nodedepth":e,"p.limit":n}}),a=(o.data.hits||[]).map(r=>({name:r.name,path:r.path,primaryType:"cq:Page",title:r.title||r.name,type:"page"}));return{success:!0,siteRoot:t,pages:a,pageCount:a.length,totalChildrenScanned:o.data.hits?o.data.hits.length:0}},"listPages")}async executeJCRQuery(t,e=20){return c(async()=>{if(!t||typeof t!="string"||t.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=t.toLowerCase();if(/drop|delete|update|insert|exec|script|\.|<script/i.test(n)||t.length>1e3)throw new Error("Query contains potentially unsafe patterns or is too long");const o=await this.createAxiosInstance().get("/bin/querybuilder.json",{params:{path:"/content",type:"cq:Page",fulltext:t,"p.limit":e}});return{query:t,results:o.data.hits||[],total:o.data.total||0,limit:e}},"executeJCRQuery")}async getPageProperties(t){return c(async()=>{const s=(await this.createAxiosInstance().get(`${t}/jcr:content.json`)).data;return{title:s["jcr:title"],description:s["jcr:description"],template:s["cq:template"],lastModified:s["cq:lastModified"],lastModifiedBy:s["cq:lastModifiedBy"],created:s["jcr:created"],createdBy:s["jcr:createdBy"],primaryType:s["jcr:primaryType"],resourceType:s["sling:resourceType"],tags:s["cq:tags"]||[],properties:s}},"getPageProperties")}async searchContent(t){return c(async()=>{const n=await this.createAxiosInstance().get(this.config.aem.endpoints.query,{params:t});return p({params:t,results:n.data.hits||[],total:n.data.total||0,rawResponse:n.data},"searchContent")},"searchContent")}async getAssetMetadata(t){return c(async()=>{const n=await this.createAxiosInstance().get(`${t}.json`),s=n.data["jcr:content"]?.metadata||{};return p({assetPath:t,metadata:s,fullData:n.data},"getAssetMetadata")},"getAssetMetadata")}async createPage(t){return c(async()=>{const{parentPath:e,title:n,template:s,name:o,properties:a}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid parent path: ${String(e)}`,{parentPath:e});const r=o||n.replace(/\s+/g,"-").toLowerCase(),i=`${e}/${r}`;return await this.createAxiosInstance().post(i,{"jcr:primaryType":"cq:Page","jcr:title":n,"cq:template":s,...a}),p({success:!0,pagePath:i,title:n,template:s,properties:a,timestamp:new Date().toISOString()},"createPage")},"createPage")}async deletePage(t){return c(async()=>{const{pagePath:e,force:n=!1}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid page path: ${String(e)}`,{pagePath:e});const s=this.createAxiosInstance();let o=!1;try{await s.delete(e),o=!0}catch(a){if(a.response&&a.response.status===405)try{await s.post("/bin/wcmcommand",{cmd:"deletePage",path:e,force:n.toString()}),o=!0}catch{try{await s.post(e,{":operation":"delete"}),o=!0}catch(i){throw console.error("Sling POST delete failed:",i.response?.status,i.response?.data),i}}else throw console.error("DELETE failed:",a.response?.status,a.response?.data),a}return p({success:o,deletedPath:e,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(t){return c(async()=>{const{pagePath:e,componentType:n,resourceType:s,properties:o={},name:a}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid page path: ${String(e)}`,{pagePath:e});if(!u(n,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid component type: ${n}`,{componentType:n});const r=a||`${n}_${Date.now()}`,i=`${e}/jcr:content/${r}`;return await this.createAxiosInstance().post(i,{"jcr:primaryType":"nt:unstructured","sling:resourceType":s,...o,":operation":"import",":contentType":"json",":replace":"true"}),p({success:!0,componentPath:i,componentType:n,resourceType:s,properties:o,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async deleteComponent(t){return c(async()=>{const{componentPath:e,force:n=!1}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid component path: ${String(e)}`,{componentPath:e});const s=this.createAxiosInstance();let o=!1;try{await s.delete(e),o=!0}catch(a){if(a.response&&a.response.status===405)try{await s.post(e,{":operation":"delete"}),o=!0}catch(r){throw console.error("Sling POST delete failed:",r.response?.status,r.response?.data),r}else throw console.error("DELETE failed:",a.response?.status,a.response?.data),a}return p({success:o,deletedPath:e,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(t){return c(async()=>{const{contentPaths:e,unpublishTree:n=!1}=t;if(!e||Array.isArray(e)&&e.length===0)throw l(m.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:e});return await this.createAxiosInstance().post("/etc/replication/agents.author/publish/jcr:content.queue.json",{cmd:"Deactivate",path:e,ignoredeactivated:!1,onlymodified:!1}),p({success:!0,unpublishedPaths:e,unpublishTree:n,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(t){return c(async()=>{const{pagePath:e,activateTree:n=!1}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid page path: ${String(e)}`,{pagePath:e});return await this.createAxiosInstance().post("/etc/replication/agents.author/publish/jcr:content.queue.json",{cmd:"Activate",path:e,ignoredeactivated:!1,onlymodified:!1}),p({success:!0,activatedPath:e,activateTree:n,timestamp:new Date().toISOString()},"activatePage")},"activatePage")}async deactivatePage(t){return c(async()=>{const{pagePath:e,deactivateTree:n=!1}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid page path: ${String(e)}`,{pagePath:e});return await this.createAxiosInstance().post("/etc/replication/agents.author/publish/jcr:content.queue.json",{cmd:"Deactivate",path:e,ignoredeactivated:!1,onlymodified:!1}),p({success:!0,deactivatedPath:e,deactivateTree:n,timestamp:new Date().toISOString()},"deactivatePage")},"deactivatePage")}async uploadAsset(t){return c(async()=>{const{parentPath:e,fileName:n,fileContent:s,mimeType:o,metadata:a={}}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid parent path: ${String(e)}`,{parentPath:e});const r=`${e}/${n}`,i=new URLSearchParams;return typeof s=="string"?i.append("file",s):i.append("file",s.toString()),i.append("fileName",n),o&&i.append("mimeType",o),Object.entries(a).forEach(([d,f])=>{i.append(`./jcr:content/metadata/${d}`,String(f))}),await this.createAxiosInstance().post(r,i,{headers:{"Content-Type":"multipart/form-data"}}),p({success:!0,assetPath:r,fileName:n,mimeType:o,metadata:a,timestamp:new Date().toISOString()},"uploadAsset")},"uploadAsset")}async updateAsset(t){return c(async()=>{const{assetPath:e,metadata:n,fileContent:s,mimeType:o}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid asset path: ${String(e)}`,{assetPath:e});const a={};return s&&(a.file=s,o&&(a.mimeType=o)),n&&Object.entries(n).forEach(([i,g])=>{a[`./jcr:content/metadata/${i}`]=g}),await this.createAxiosInstance().post(e,a),p({success:!0,assetPath:e,updatedMetadata:n,timestamp:new Date().toISOString()},"updateAsset")},"updateAsset")}async deleteAsset(t){return c(async()=>{const{assetPath:e,force:n=!1}=t;if(!h(e,this.aemConfig))throw l(m.INVALID_PARAMETERS,`Invalid asset path: ${String(e)}`,{assetPath:e});return await this.createAxiosInstance().delete(e),p({success:!0,deletedPath:e,force:n,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}async getTemplates(t){return c(async()=>{const n=await this.createAxiosInstance().get(`${t}.json`);return p({sitePath:t,templates:n.data},"getTemplates")},"getTemplates")}async getTemplateStructure(t){return c(async()=>{const n=await this.createAxiosInstance().get(`${t}.json`);return p({templatePath:t,structure:n.data},"getTemplateStructure")},"getTemplateStructure")}async getNodeContent(t,e=1){return c(async()=>{const s=await this.createAxiosInstance().get(`${t}.json`,{params:{depth:e}});return{path:t,depth:e,content:s.data}},"getNodeContent")}}
|
|
1
|
+
"use strict";import P from"axios";import{getAEMConfig as b,isValidContentPath as u,isValidLocale as w}from"./aem.config.js";import{createAEMError as d,handleAEMHttpError as y,safeExecute as l,validateComponentOperation as A,createSuccessResponse as p,AEM_ERROR_CODES as m}from"./aem.errors.js";export class AEMConnector{config;auth;aemConfig;constructor(n){this.config=this.loadConfig(n),this.aemConfig=b({}),this.auth={username:this.config.aem.serviceUser.username,password:this.config.aem.serviceUser.password}}loadConfig(n={}){return{aem:{host:n.host||"http://localhost:4502",author:n.host||"http://localhost:4502",publish:"http://localhost:4503",serviceUser:{username:n.user||"admin",password:n.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"}}}createAxiosInstance(){return P.create({baseURL:this.config.aem.host,auth:this.auth,timeout:3e4,headers:{"Content-Type":"application/json",Accept:"application/json"}})}async testConnection(){try{console.log("Testing AEM connection to:",this.config.aem.host);const t=await this.createAxiosInstance().get("/libs/granite/core/content/login.html",{timeout:5e3,validateStatus:s=>s<500});return console.log("\u2705 AEM connection successful! Status:",t.status),!0}catch(n){return console.error("\u274C AEM connection failed:",n.message),n.response&&(console.error(" Status:",n.response.status),console.error(" URL:",n.config?.url)),!1}}async validateComponent(n){return l(async()=>{const t=n.locale,s=n.pagePath||n.page_path,r=n.component,a=n.props;if(A(t,s,r,a),!w(t,this.aemConfig))throw d(m.INVALID_LOCALE,`Locale '${t}' is not supported`,{locale:t,allowedLocales:this.aemConfig.validation.allowedLocales});if(!u(s,this.aemConfig))throw d(m.INVALID_PATH,`Path '${s}' is not within allowed content roots`,{path:s,allowedRoots:Object.values(this.aemConfig.contentPaths)});const o=await this.createAxiosInstance().get(`${s}.json`,{params:{":depth":"2"},timeout:this.aemConfig.queries.timeoutMs}),c=this.validateComponentProps(o.data,r,a);return p({message:"Component validation completed successfully",pageData:o.data,component:r,locale:t,validation:c,configUsed:{allowedLocales:this.aemConfig.validation.allowedLocales}},"validateComponent")},"validateComponent")}validateComponentProps(n,t,s){const r=[],a=[];return t==="text"&&!s.text&&!s.richText&&r.push("Text component should have text or richText property"),t==="image"&&!s.fileReference&&!s.src&&a.push("Image component requires fileReference or src property"),{valid:a.length===0,errors:a,warnings:r,componentType:t,propsValidated:Object.keys(s).length}}async updateComponent(n){return l(async()=>{if(!n.componentPath||typeof n.componentPath!="string")throw d(m.INVALID_PARAMETERS,"Component path is required and must be a string");if(!n.properties||typeof n.properties!="object")throw d(m.INVALID_PARAMETERS,"Properties are required and must be an object");if(!u(n.componentPath,this.aemConfig))throw d(m.INVALID_PATH,`Component path '${n.componentPath}' is not within allowed content roots`,{path:n.componentPath,allowedRoots:Object.values(this.aemConfig.contentPaths)});const t=this.createAxiosInstance();try{await t.get(`${n.componentPath}.json`)}catch(e){throw e.response?.status===404?d(m.COMPONENT_NOT_FOUND,`Component not found at path: ${n.componentPath}`,{componentPath:n.componentPath}):y(e,"updateComponent")}const s=new URLSearchParams;Object.entries(n.properties).forEach(([e,o])=>{o==null?s.append(`${e}@Delete`,""):Array.isArray(o)?o.forEach(c=>{s.append(`${e}`,c.toString())}):typeof o=="object"?s.append(e,JSON.stringify(o)):s.append(e,o.toString())});const r=await t.post(n.componentPath,s,{headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},timeout:this.aemConfig.queries.timeoutMs}),a=await t.get(`${n.componentPath}.json`);return p({message:"Component updated successfully",path:n.componentPath,properties:n.properties,updatedProperties:a.data,response:r.data,verification:{success:!0,propertiesChanged:Object.keys(n.properties).length,timestamp:new Date().toISOString()}},"updateComponent")},"updateComponent")}async undoChanges(n){return p({message:"undoChanges is not implemented. Please use AEM version history for undo/rollback.",request:n,timestamp:new Date().toISOString()},"undoChanges")}async scanPageComponents(n){return l(async()=>{const s=await this.createAxiosInstance().get(`${n}.infinity.json`),r=[],a=(e,o)=>{!e||typeof e!="object"||(e["sling:resourceType"]&&r.push({path:o,resourceType:e["sling:resourceType"],properties:{...e}}),Object.entries(e).forEach(([c,i])=>{if(typeof i=="object"&&i!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const h=o?`${o}/${c}`:c;a(i,h)}}))};return s.data["jcr:content"]?a(s.data["jcr:content"],"jcr:content"):a(s.data,n),p({pagePath:n,components:r,totalComponents:r.length},"scanPageComponents")},"scanPageComponents")}async fetchSites(){return l(async()=>{const t=await this.createAxiosInstance().get("/content.json",{params:{":depth":"2"}}),s=[];return Object.entries(t.data).forEach(([r,a])=>{r.startsWith("jcr:")||r.startsWith("sling:")||a&&typeof a=="object"&&a["jcr:content"]&&s.push({name:r,path:`/content/${r}`,title:a["jcr:content"]["jcr:title"]||r,template:a["jcr:content"]["cq:template"],lastModified:a["jcr:content"]["cq:lastModified"]})}),p({sites:s,totalCount:s.length},"fetchSites")},"fetchSites")}async fetchLanguageMasters(n){return l(async()=>{const s=await this.createAxiosInstance().get(`/content/${n}.json`,{params:{":depth":"3"}}),r=[];return Object.entries(s.data).forEach(([a,e])=>{a.startsWith("jcr:")||a.startsWith("sling:")||e&&typeof e=="object"&&e["jcr:content"]&&r.push({name:a,path:`/content/${a}`,title:e["jcr:content"]["jcr:title"]||a,language:e["jcr:content"]["jcr:language"]||"en"})}),p({site:n,languageMasters:r},"fetchLanguageMasters")},"fetchLanguageMasters")}async fetchAvailableLocales(n,t){return l(async()=>{const r=await this.createAxiosInstance().get(`${t}.json`,{params:{":depth":"2"}}),a=[];return Object.entries(r.data).forEach(([e,o])=>{e.startsWith("jcr:")||e.startsWith("sling:")||o&&typeof o=="object"&&a.push({name:e,title:o["jcr:content"]?.["jcr:title"]||e,language:o["jcr:content"]?.["jcr:language"]||e})}),p({site:n,languageMasterPath:t,availableLocales:a},"fetchAvailableLocales")},"fetchAvailableLocales")}async replicateAndPublish(n,t,s){return l(async()=>p({message:"Replication simulated",selectedLocales:n,componentData:t,localizedOverrides:s},"replicateAndPublish"),"replicateAndPublish")}async getAllTextContent(n){return l(async()=>{const s=await this.createAxiosInstance().get(`${n}.infinity.json`),r=[],a=(e,o)=>{!e||typeof e!="object"||((e.text||e["jcr:title"]||e["jcr:description"])&&r.push({path:o,title:e["jcr:title"],text:e.text,description:e["jcr:description"]}),Object.entries(e).forEach(([c,i])=>{if(typeof i=="object"&&i!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const h=o?`${o}/${c}`:c;a(i,h)}}))};return s.data["jcr:content"]?a(s.data["jcr:content"],"jcr:content"):a(s.data,n),p({pagePath:n,textContent:r},"getAllTextContent")},"getAllTextContent")}async getPageTextContent(n){return l(async()=>this.getAllTextContent(n),"getPageTextContent")}async getPageImages(n){return l(async()=>{const s=await this.createAxiosInstance().get(`${n}.infinity.json`),r=[],a=(e,o)=>{!e||typeof e!="object"||((e.fileReference||e.src)&&r.push({path:o,fileReference:e.fileReference,src:e.src,alt:e.alt||e.altText,title:e["jcr:title"]||e.title}),Object.entries(e).forEach(([c,i])=>{if(typeof i=="object"&&i!==null&&!c.startsWith("rep:")&&!c.startsWith("oak:")){const h=o?`${o}/${c}`:c;a(i,h)}}))};return s.data["jcr:content"]?a(s.data["jcr:content"],"jcr:content"):a(s.data,n),p({pagePath:n,images:r},"getPageImages")},"getPageImages")}async updateImagePath(n,t){return l(async()=>this.updateComponent({componentPath:n,properties:{fileReference:t}}),"updateImagePath")}async getPageContent(n){return l(async()=>{const s=await this.createAxiosInstance().get(`${n}.infinity.json`);return p({pagePath:n,content:s.data},"getPageContent")},"getPageContent")}async listChildren(n,t=1){return l(async()=>{const s=this.createAxiosInstance();try{const r=await s.get(`${n}.${t}.json`),a=[];return r.data&&typeof r.data=="object"&&Object.entries(r.data).forEach(([e,o])=>{if(!(e.startsWith("jcr:")||e.startsWith("sling:")||e.startsWith("cq:")||e.startsWith("rep:")||e.startsWith("oak:")||e==="jcr:content")&&o&&typeof o=="object"){const c=`${n}/${e}`;a.push({name:e,path:c,primaryType:o["jcr:primaryType"]||"nt:unstructured",title:o["jcr:content"]?.["jcr:title"]||o["jcr:title"]||e,lastModified:o["jcr:content"]?.["cq:lastModified"]||o["cq:lastModified"],resourceType:o["jcr:content"]?.["sling:resourceType"]||o["sling:resourceType"]})}}),a}catch(r){if(r.response?.status===404||r.response?.status===403)return((await s.get("/bin/querybuilder.json",{params:{path:n,type:"cq:Page","p.nodedepth":"1","p.limit":"1000","p.hits":"full"}})).data.hits||[]).map(o=>({name:o.name||o.path?.split("/").pop(),path:o.path,primaryType:o["jcr:primaryType"]||"cq:Page",title:o["jcr:content/jcr:title"]||o.title||o.name,lastModified:o["jcr:content/cq:lastModified"],resourceType:o["jcr:content/sling:resourceType"]}));throw r}},"listChildren")}async listPages(n,t=1,s=20){return l(async()=>{const r=this.createAxiosInstance();try{const a=await r.get(`${n}.${t}.json`),e=[],o=(c,i,h)=>{h>t||e.length>=s||Object.entries(c).forEach(([f,g])=>{if(!(e.length>=s)&&!(f.startsWith("jcr:")||f.startsWith("sling:")||f.startsWith("cq:")||f.startsWith("rep:")||f.startsWith("oak:"))&&g&&typeof g=="object"){const j=`${i}/${f}`;g["jcr:primaryType"]==="cq:Page"&&e.push({name:f,path:j,primaryType:"cq:Page",title:g["jcr:content"]?.["jcr:title"]||f,template:g["jcr:content"]?.["cq:template"],lastModified:g["jcr:content"]?.["cq:lastModified"],lastModifiedBy:g["jcr:content"]?.["cq:lastModifiedBy"],resourceType:g["jcr:content"]?.["sling:resourceType"],type:"page"}),h<t&&o(g,j,h+1)}})};return a.data&&typeof a.data=="object"&&o(a.data,n,0),p({siteRoot:n,pages:e,pageCount:e.length,depth:t,limit:s,totalChildrenScanned:e.length},"listPages")}catch(a){if(a.response?.status===404||a.response?.status===403){const e=await r.get("/bin/querybuilder.json",{params:{path:n,type:"cq:Page","p.nodedepth":t.toString(),"p.limit":s.toString(),"p.hits":"full"}}),o=(e.data.hits||[]).map(c=>({name:c.name||c.path?.split("/").pop(),path:c.path,primaryType:"cq:Page",title:c["jcr:content/jcr:title"]||c.title||c.name,template:c["jcr:content/cq:template"],lastModified:c["jcr:content/cq:lastModified"],lastModifiedBy:c["jcr:content/cq:lastModifiedBy"],resourceType:c["jcr:content/sling:resourceType"],type:"page"}));return p({siteRoot:n,pages:o,pageCount:o.length,depth:t,limit:s,totalChildrenScanned:e.data.total||o.length,fallbackUsed:"QueryBuilder"},"listPages")}throw a}},"listPages")}async executeJCRQuery(n,t=20){return l(async()=>{if(!n||typeof n!="string"||n.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 s=n.toLowerCase();if(/drop|delete|update|insert|exec|script|\.|<script/i.test(s)||n.length>1e3)throw new Error("Query contains potentially unsafe patterns or is too long");const a=await this.createAxiosInstance().get("/bin/querybuilder.json",{params:{path:"/content",type:"cq:Page",fulltext:n,"p.limit":t}});return{query:n,results:a.data.hits||[],total:a.data.total||0,limit:t}},"executeJCRQuery")}async getPageProperties(n){return l(async()=>{const r=(await this.createAxiosInstance().get(`${n}/jcr:content.json`)).data,a={title:r["jcr:title"],description:r["jcr:description"],template:r["cq:template"],lastModified:r["cq:lastModified"],lastModifiedBy:r["cq:lastModifiedBy"],created:r["jcr:created"],createdBy:r["jcr:createdBy"],primaryType:r["jcr:primaryType"],resourceType:r["sling:resourceType"],tags:r["cq:tags"]||[],properties:r};return p({pagePath:n,properties:a},"getPageProperties")},"getPageProperties")}async searchContent(n){return l(async()=>{const s=await this.createAxiosInstance().get(this.config.aem.endpoints.query,{params:n});return p({params:n,results:s.data.hits||[],total:s.data.total||0,rawResponse:s.data},"searchContent")},"searchContent")}async getAssetMetadata(n){return l(async()=>{const s=await this.createAxiosInstance().get(`${n}.json`),r=s.data["jcr:content"]?.metadata||{};return p({assetPath:n,metadata:r,fullData:s.data},"getAssetMetadata")},"getAssetMetadata")}async createPage(n){return l(async()=>{const{parentPath:t,title:s,template:r,name:a,properties:e}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const o=a||s.replace(/\s+/g,"-").toLowerCase(),c=`${t}/${o}`;return await this.createAxiosInstance().post(c,{"jcr:primaryType":"cq:Page","jcr:title":s,"cq:template":r,...e}),p({success:!0,pagePath:c,title:s,template:r,properties:e,timestamp:new Date().toISOString()},"createPage")},"createPage")}async deletePage(n){return l(async()=>{const{pagePath:t,force:s=!1}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const r=this.createAxiosInstance();let a=!1;try{await r.delete(t),a=!0}catch(e){if(e.response&&e.response.status===405)try{await r.post("/bin/wcmcommand",{cmd:"deletePage",path:t,force:s.toString()}),a=!0}catch{try{await r.post(t,{":operation":"delete"}),a=!0}catch(c){throw console.error("Sling POST delete failed:",c.response?.status,c.response?.data),c}}else throw console.error("DELETE failed:",e.response?.status,e.response?.data),e}return p({success:a,deletedPath:t,timestamp:new Date().toISOString()},"deletePage")},"deletePage")}async createComponent(n){return l(async()=>{const{pagePath:t,componentPath:s,componentType:r,resourceType:a,properties:e={},name:o}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const c=o||`${r}_${Date.now()}`,i=s||`${t}/jcr:content/${c}`;return await this.createAxiosInstance().post(i,{"jcr:primaryType":"nt:unstructured","sling:resourceType":a,...e,":operation":"import",":contentType":"json",":replace":"true"}),p({success:!0,componentPath:s,componentType:r,resourceType:a,properties:e,timestamp:new Date().toISOString()},"createComponent")},"createComponent")}async deleteComponent(n){return l(async()=>{const{componentPath:t,force:s=!1}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid component path: ${String(t)}`,{componentPath:t});const r=this.createAxiosInstance();let a=!1;try{await r.delete(t),a=!0}catch(e){if(e.response&&e.response.status===405)try{await r.post(t,{":operation":"delete"}),a=!0}catch(o){throw console.error("Sling POST delete failed:",o.response?.status,o.response?.data),o}else throw console.error("DELETE failed:",e.response?.status,e.response?.data),e}return p({success:a,deletedPath:t,timestamp:new Date().toISOString()},"deleteComponent")},"deleteComponent")}async unpublishContent(n){return l(async()=>{const{contentPaths:t,unpublishTree:s=!1}=n;if(!t||Array.isArray(t)&&t.length===0)throw d(m.INVALID_PARAMETERS,"Content paths array is required and cannot be empty",{contentPaths:t});const r=this.createAxiosInstance(),a=[];for(const e of Array.isArray(t)?t:[t])try{const o=new URLSearchParams;o.append("cmd","Deactivate"),o.append("path",e),o.append("ignoredeactivated","false"),o.append("onlymodified","false"),s&&o.append("deep","true");const c=await r.post("/bin/replicate.json",o,{headers:{"Content-Type":"application/x-www-form-urlencoded"}});a.push({path:e,success:!0,response:c.data})}catch(o){a.push({path:e,success:!1,error:o.response?.data||o.message})}return p({success:a.every(e=>e.success),results:a,unpublishedPaths:t,unpublishTree:s,timestamp:new Date().toISOString()},"unpublishContent")},"unpublishContent")}async activatePage(n){return l(async()=>{const{pagePath:t,activateTree:s=!1}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const r=this.createAxiosInstance();try{const a=new URLSearchParams;a.append("cmd","Activate"),a.append("path",t),a.append("ignoredeactivated","false"),a.append("onlymodified","false"),s&&a.append("deep","true");const e=await r.post("/bin/replicate.json",a,{headers:{"Content-Type":"application/x-www-form-urlencoded"}});return p({success:!0,activatedPath:t,activateTree:s,response:e.data,timestamp:new Date().toISOString()},"activatePage")}catch(a){try{const e=await r.post("/bin/wcmcommand",{cmd:"activate",path:t,ignoredeactivated:!1,onlymodified:!1});return p({success:!0,activatedPath:t,activateTree:s,response:e.data,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"activatePage")}catch{throw y(a,"activatePage")}}},"activatePage")}async deactivatePage(n){return l(async()=>{const{pagePath:t,deactivateTree:s=!1}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid page path: ${String(t)}`,{pagePath:t});const r=this.createAxiosInstance();try{const a=new URLSearchParams;a.append("cmd","Deactivate"),a.append("path",t),a.append("ignoredeactivated","false"),a.append("onlymodified","false"),s&&a.append("deep","true");const e=await r.post("/bin/replicate.json",a,{headers:{"Content-Type":"application/x-www-form-urlencoded"}});return p({success:!0,deactivatedPath:t,deactivateTree:s,response:e.data,timestamp:new Date().toISOString()},"deactivatePage")}catch(a){try{const e=await r.post("/bin/wcmcommand",{cmd:"deactivate",path:t,ignoredeactivated:!1,onlymodified:!1});return p({success:!0,deactivatedPath:t,deactivateTree:s,response:e.data,fallbackUsed:"WCM Command",timestamp:new Date().toISOString()},"deactivatePage")}catch{throw y(a,"deactivatePage")}}},"deactivatePage")}async uploadAsset(n){return l(async()=>{const{parentPath:t,fileName:s,fileContent:r,mimeType:a,metadata:e={}}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid parent path: ${String(t)}`,{parentPath:t});const o=this.createAxiosInstance(),c=`${t}/${s}`;try{const i=new URLSearchParams;typeof r=="string"?i.append("file",r):i.append("file",r.toString()),i.append("fileName",s),i.append(":operation","import"),i.append(":contentType","json"),i.append(":replace","true"),i.append("jcr:primaryType","dam:Asset"),a&&i.append("jcr:content/jcr:mimeType",a),Object.entries(e).forEach(([g,j])=>{i.append(`jcr:content/metadata/${g}`,String(j))});const h=await o.post(c,i,{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),f=await o.get(`${c}.json`);return p({success:!0,assetPath:c,fileName:s,mimeType:a,metadata:e,uploadResponse:h.data,assetData:f.data,timestamp:new Date().toISOString()},"uploadAsset")}catch(i){try{const h=await o.post("/api/assets"+t,{fileName:s,fileContent:r,mimeType:a,metadata:e});return p({success:!0,assetPath:c,fileName:s,mimeType:a,metadata:e,uploadResponse:h.data,fallbackUsed:"DAM API",timestamp:new Date().toISOString()},"uploadAsset")}catch{throw y(i,"uploadAsset")}}},"uploadAsset")}async updateAsset(n){return l(async()=>{const{assetPath:t,metadata:s,fileContent:r,mimeType:a}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});const e=this.createAxiosInstance(),o=new URLSearchParams;r&&(o.append("file",r),a&&o.append("jcr:content/jcr:mimeType",a)),s&&typeof s=="object"&&Object.entries(s).forEach(([c,i])=>{o.append(`jcr:content/metadata/${c}`,String(i))});try{const c=await e.post(t,o,{headers:{"Content-Type":"application/x-www-form-urlencoded"}}),i=await e.get(`${t}.json`);return p({success:!0,assetPath:t,updatedMetadata:s,updateResponse:c.data,assetData:i.data,timestamp:new Date().toISOString()},"updateAsset")}catch(c){throw y(c,"updateAsset")}},"updateAsset")}async deleteAsset(n){return l(async()=>{const{assetPath:t,force:s=!1}=n;if(!u(t,this.aemConfig))throw d(m.INVALID_PARAMETERS,`Invalid asset path: ${String(t)}`,{assetPath:t});return await this.createAxiosInstance().delete(t),p({success:!0,deletedPath:t,force:s,timestamp:new Date().toISOString()},"deleteAsset")},"deleteAsset")}async getTemplates(n){return l(async()=>{const t=this.createAxiosInstance();if(n)try{const s=`/conf${n.replace("/content","")}/settings/wcm/templates`,r=await t.get(`${s}.json`,{params:{":depth":"2"}}),a=[];return r.data&&typeof r.data=="object"&&Object.entries(r.data).forEach(([e,o])=>{e.startsWith("jcr:")||e.startsWith("sling:")||o&&typeof o=="object"&&o["jcr:content"]&&a.push({name:e,path:`${s}/${e}`,title:o["jcr:content"]["jcr:title"]||e,description:o["jcr:content"]["jcr:description"],allowedPaths:o["jcr:content"].allowedPaths,ranking:o["jcr:content"].ranking||0})}),p({sitePath:n,templates:a,totalCount:a.length,source:"site-specific"},"getTemplates")}catch{}try{const s=["/apps/wcm/core/content/sites/templates","/libs/wcm/core/content/sites/templates"],r=[];for(const a of s)try{const e=await t.get(`${a}.json`,{params:{":depth":"2"}});e.data&&typeof e.data=="object"&&Object.entries(e.data).forEach(([o,c])=>{o.startsWith("jcr:")||o.startsWith("sling:")||c&&typeof c=="object"&&r.push({name:o,path:`${a}/${o}`,title:c["jcr:content"]?.["jcr:title"]||o,description:c["jcr:content"]?.["jcr:description"],allowedPaths:c["jcr:content"]?.allowedPaths,ranking:c["jcr:content"]?.ranking||0,source:a.includes("/apps/")?"apps":"libs"})})}catch{}return p({sitePath:n||"global",templates:r,totalCount:r.length,source:"global"},"getTemplates")}catch(s){throw y(s,"getTemplates")}},"getTemplates")}async getTemplateStructure(n){return l(async()=>{const t=this.createAxiosInstance();try{const s=await t.get(`${n}.infinity.json`),r={path:n,properties:s.data["jcr:content"]||{},policies:s.data["jcr:content"]?.policies||{},structure:s.data["jcr:content"]?.structure||{},initialContent:s.data["jcr:content"]?.initial||{},allowedComponents:[],allowedPaths:s.data["jcr:content"]?.allowedPaths||[]},a=(e,o="")=>{if(!(!e||typeof e!="object")){if(e.components){const c=Object.keys(e.components);r.allowedComponents.push(...c)}Object.entries(e).forEach(([c,i])=>{typeof i=="object"&&i!==null&&!c.startsWith("jcr:")&&a(i,o?`${o}/${c}`:c)})}};return a(r.policies),r.allowedComponents=[...new Set(r.allowedComponents)],p({templatePath:n,structure:r,fullData:s.data},"getTemplateStructure")}catch(s){throw y(s,"getTemplateStructure")}},"getTemplateStructure")}async bulkUpdateComponents(n){return l(async()=>{const{updates:t,validateFirst:s=!0,continueOnError:r=!1}=n;if(!Array.isArray(t)||t.length===0)throw d(m.INVALID_PARAMETERS,"Updates array is required and cannot be empty");const a=[],e=this.createAxiosInstance();if(s)for(const c of t)try{await e.get(`${c.componentPath}.json`)}catch(i){if(i.response?.status===404&&(a.push({componentPath:c.componentPath,success:!1,error:`Component not found: ${c.componentPath}`,phase:"validation"}),!r))return p({success:!1,message:"Bulk update failed during validation phase",results:a,totalUpdates:t.length,successfulUpdates:0},"bulkUpdateComponents")}let o=0;for(const c of t)try{const i=await this.updateComponent({componentPath:c.componentPath,properties:c.properties});a.push({componentPath:c.componentPath,success:!0,result:i,phase:"update"}),o++}catch(i){if(a.push({componentPath:c.componentPath,success:!1,error:i.message,phase:"update"}),!r)break}return p({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(n,t=1){return l(async()=>{const r=await this.createAxiosInstance().get(`${n}.json`,{params:{":depth":t.toString()}});return{path:n,depth:t,content:r.data,timestamp:new Date().toISOString()}},"getNodeContent")}}
|
package/dist/cli.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";import
|
|
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});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{config as
|
|
1
|
+
"use strict";import{config as a}from"../config.js";export class MCPRequestHandler{aemConnector;config;constructor(t,e){this.config=t,this.aemConnector=e}async handleRequest(t,e){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":return await this.aemConnector.getTemplates(e.sitePath);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()}}async handleHealthCheck(){return{status:"healthy",aem:await this.aemConnector.testConnection()?"connected":"disconnected",mcp:"ready",timestamp:new Date().toISOString(),version:a.APP_VERSION||"1.0.0",port:this.config.mcpPort}}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import{randomUUID as
|
|
1
|
+
"use strict";import{randomUUID as p}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 t}from"./mcp.transports.js";import{createMCPServer as c}from"./mcp.server.js";export const handleRequest=async(e,o,a)=>{console.log("Received MCP request:",e.body),console.log("Route path:",e.route?.path),console.log("Full path:",e.baseUrl+e.path);const{jsonrpc:i,id:l,method:d,params:R}=e.body;if(i!=="2.0"||!d){o.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 s=e.headers["mcp-session-id"];let r;if(s&&t[s])r=t[s];else if(!s&&u(e.body)){r=new m({sessionIdGenerator:()=>p(),enableJsonResponse:!0,onsessioninitialized:n=>{console.log(`Session initialized with ID: ${n}`),t[n]=r}}),await c(a).connect(r),await r.handleRequest(e,o,e.body);return}else{o.status(400).json({jsonrpc:"2.0",error:{code:-32e3,message:"Bad Request: No valid session ID provided"},id:null});return}await r.handleRequest(e,o,e.body)}catch(s){console.error("Error handling MCP request:",s),o.headersSent||o.status(500).json({jsonrpc:"2.0",error:{code:-32603,message:"Internal server error"},id:null})}};
|
package/dist/mcp/mcp.tools.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export const tools=[{name:"validateComponent",description:"Validate component changes before applying them",inputSchema:{type:"object",properties:{locale:{type:"string"},pagePath:{type:"string"},component:{type:"string"},props:{type:"object"}},required:["locale","pagePath","component","props"]}},{name:"updateComponent",description:"Update component properties in AEM",inputSchema:{type:"object",properties:{componentPath:{type:"string"},properties:{type:"object"}},required:["componentPath","properties"]}},{name:"undoChanges",description:"Undo the last component changes",inputSchema:{type:"object",properties:{jobId:{type:"string"}},required:["jobId"]}},{name:"scanPageComponents",description:"Scan a page to discover all components and their properties",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"fetchSites",description:"Get all available sites in AEM",inputSchema:{type:"object",properties:{}}},{name:"fetchLanguageMasters",description:"Get language masters for a specific site",inputSchema:{type:"object",properties:{site:{type:"string"}},required:["site"]}},{name:"fetchAvailableLocales",description:"Get available locales for a site and language master",inputSchema:{type:"object",properties:{site:{type:"string"},languageMasterPath:{type:"string"}},required:["site","languageMasterPath"]}},{name:"replicateAndPublish",description:"Replicate and publish content to selected locales",inputSchema:{type:"object",properties:{selectedLocales:{type:"array",items:{type:"string"}},componentData:{type:"object"},localizedOverrides:{type:"object"}},required:["selectedLocales","componentData"]}},{name:"getAllTextContent",description:"Get all text content from a page including titles, text components, and descriptions",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"getPageTextContent",description:"Get text content from a specific page",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"getPageImages",description:"Get all images from a page, including those within Experience Fragments",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"updateImagePath",description:"Update the image path for an image component and verify the update",inputSchema:{type:"object",properties:{componentPath:{type:"string"},newImagePath:{type:"string"}},required:["componentPath","newImagePath"]}},{name:"getPageContent",description:"Get all content from a page including Experience Fragments and Content Fragments",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"listPages",description:"List all pages under a site root",inputSchema:{type:"object",properties:{siteRoot:{type:"string"},depth:{type:"number"},limit:{type:"number"}}}},{name:"getNodeContent",description:"Legacy: Get JCR node content",inputSchema:{type:"object",properties:{path:{type:"string"},depth:{type:"number"}},required:["path"]}},{name:"listChildren",description:"Legacy: List child nodes",inputSchema:{type:"object",properties:{path:{type:"string"}},required:["path"]}},{name:"getPageProperties",description:"Get page properties",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"searchContent",description:"Search content using Query Builder",inputSchema:{type:"object",properties:{type:{type:"string"},fulltext:{type:"string"},path:{type:"string"},limit:{type:"number"}}}},{name:"executeJCRQuery",description:"Execute JCR query",inputSchema:{type:"object",properties:{query:{type:"string"},limit:{type:"number"}},required:["query"]}},{name:"getAssetMetadata",description:"Get asset metadata",inputSchema:{type:"object",properties:{assetPath:{type:"string"}},required:["assetPath"]}},{name:"getStatus",description:"Get workflow status by ID",inputSchema:{type:"object",properties:{workflowId:{type:"string"}},required:["workflowId"]}},{name:"
|
|
1
|
+
"use strict";export const tools=[{name:"validateComponent",description:"Validate component changes before applying them",inputSchema:{type:"object",properties:{locale:{type:"string"},pagePath:{type:"string"},component:{type:"string"},props:{type:"object"}},required:["locale","pagePath","component","props"]}},{name:"updateComponent",description:"Update component properties in AEM",inputSchema:{type:"object",properties:{componentPath:{type:"string"},properties:{type:"object"}},required:["componentPath","properties"]}},{name:"undoChanges",description:"Undo the last component changes",inputSchema:{type:"object",properties:{jobId:{type:"string"}},required:["jobId"]}},{name:"scanPageComponents",description:"Scan a page to discover all components and their properties",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"fetchSites",description:"Get all available sites in AEM",inputSchema:{type:"object",properties:{}}},{name:"fetchLanguageMasters",description:"Get language masters for a specific site",inputSchema:{type:"object",properties:{site:{type:"string"}},required:["site"]}},{name:"fetchAvailableLocales",description:"Get available locales for a site and language master",inputSchema:{type:"object",properties:{site:{type:"string"},languageMasterPath:{type:"string"}},required:["site","languageMasterPath"]}},{name:"replicateAndPublish",description:"Replicate and publish content to selected locales",inputSchema:{type:"object",properties:{selectedLocales:{type:"array",items:{type:"string"}},componentData:{type:"object"},localizedOverrides:{type:"object"}},required:["selectedLocales","componentData"]}},{name:"getAllTextContent",description:"Get all text content from a page including titles, text components, and descriptions",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"getPageTextContent",description:"Get text content from a specific page",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"getPageImages",description:"Get all images from a page, including those within Experience Fragments",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"updateImagePath",description:"Update the image path for an image component and verify the update",inputSchema:{type:"object",properties:{componentPath:{type:"string"},newImagePath:{type:"string"}},required:["componentPath","newImagePath"]}},{name:"getPageContent",description:"Get all content from a page including Experience Fragments and Content Fragments",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"listPages",description:"List all pages under a site root",inputSchema:{type:"object",properties:{siteRoot:{type:"string"},depth:{type:"number"},limit:{type:"number"}}}},{name:"getNodeContent",description:"Legacy: Get JCR node content",inputSchema:{type:"object",properties:{path:{type:"string"},depth:{type:"number"}},required:["path"]}},{name:"listChildren",description:"Legacy: List child nodes",inputSchema:{type:"object",properties:{path:{type:"string"}},required:["path"]}},{name:"getPageProperties",description:"Get page properties",inputSchema:{type:"object",properties:{pagePath:{type:"string"}},required:["pagePath"]}},{name:"searchContent",description:"Search content using Query Builder",inputSchema:{type:"object",properties:{type:{type:"string"},fulltext:{type:"string"},path:{type:"string"},limit:{type:"number"}}}},{name:"executeJCRQuery",description:"Execute JCR query",inputSchema:{type:"object",properties:{query:{type:"string"},limit:{type:"number"}},required:["query"]}},{name:"getAssetMetadata",description:"Get asset metadata",inputSchema:{type:"object",properties:{assetPath:{type:"string"}},required:["assetPath"]}},{name:"getStatus",description:"Get workflow status by ID",inputSchema:{type:"object",properties:{workflowId:{type:"string"}},required:["workflowId"]}},{name:"enhancedPageSearch",description:"Intelligent page search with comprehensive fallback strategies and cross-section search",inputSchema:{type:"object",properties:{searchTerm:{type:"string"},basePath:{type:"string"},includeAlternateLocales:{type:"boolean"}},required:["searchTerm","basePath"]}},{name:"createPage",description:"Create a new page in AEM",inputSchema:{type:"object",properties:{parentPath:{type:"string"},title:{type:"string"},template:{type:"string"},name:{type:"string"},properties:{type:"object"}},required:["parentPath","title","template"]}},{name:"deletePage",description:"Delete a page from AEM",inputSchema:{type:"object",properties:{pagePath:{type:"string"},force:{type:"boolean"}},required:["pagePath"]}},{name:"createComponent",description:"Create a new component on a page",inputSchema:{type:"object",properties:{pagePath:{type:"string"},componentType:{type:"string"},resourceType:{type:"string"},properties:{type:"object"},name:{type:"string"}},required:["pagePath","componentType","resourceType"]}},{name:"deleteComponent",description:"Delete a component from AEM",inputSchema:{type:"object",properties:{componentPath:{type:"string"},force:{type:"boolean"}},required:["componentPath"]}},{name:"unpublishContent",description:"Unpublish content from the publish environment",inputSchema:{type:"object",properties:{contentPaths:{type:"array",items:{type:"string"}},unpublishTree:{type:"boolean"}},required:["contentPaths"]}},{name:"activatePage",description:"Activate (publish) a single page",inputSchema:{type:"object",properties:{pagePath:{type:"string"},activateTree:{type:"boolean"}},required:["pagePath"]}},{name:"deactivatePage",description:"Deactivate (unpublish) a single page",inputSchema:{type:"object",properties:{pagePath:{type:"string"},deactivateTree:{type:"boolean"}},required:["pagePath"]}},{name:"uploadAsset",description:"Upload a new asset to AEM DAM",inputSchema:{type:"object",properties:{parentPath:{type:"string"},fileName:{type:"string"},fileContent:{type:"string"},mimeType:{type:"string"},metadata:{type:"object"}},required:["parentPath","fileName","fileContent"]}},{name:"updateAsset",description:"Update an existing asset in AEM DAM",inputSchema:{type:"object",properties:{assetPath:{type:"string"},metadata:{type:"object"},fileContent:{type:"string"},mimeType:{type:"string"}},required:["assetPath"]}},{name:"deleteAsset",description:"Delete an asset from AEM DAM",inputSchema:{type:"object",properties:{assetPath:{type:"string"},force:{type:"boolean"}},required:["assetPath"]}},{name:"getTemplates",description:"Get available page templates",inputSchema:{type:"object",properties:{sitePath:{type:"string"}}}},{name:"getTemplateStructure",description:"Get detailed structure of a specific template",inputSchema:{type:"object",properties:{templatePath:{type:"string"}},required:["templatePath"]}},{name:"bulkUpdateComponents",description:"Update multiple components in a single operation with validation and rollback support",inputSchema:{type:"object",properties:{updates:{type:"array",items:{type:"object",properties:{componentPath:{type:"string"},properties:{type:"object"}},required:["componentPath","properties"]}},validateFirst:{type:"boolean"},continueOnError:{type:"boolean"}},required:["updates"]}}];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";import
|
|
1
|
+
"use strict";import n from"express";import i from"cors";import{handleRequest as l}from"../mcp/mcp.server-handler.js";import{AEMConnector as p}from"../aem/aem.connector.js";import{MCPRequestHandler as d}from"../mcp/mcp.aem-handler.js";import{config as m}from"../config.js";const h=(o={})=>{const e=n();e.use(i({origin:"*",exposedHeaders:["Mcp-Session-Id"]})),e.use(n.json()),e.use(n.json({limit:"10mb"})),e.use(n.urlencoded({extended:!0}));const c=new p(o),r=new d(o,c);return e.get("/health",async(s,t)=>{try{const a=await r.handleHealthCheck();t.json(a)}catch(a){t.status(500).json({status:"unhealthy",error:a.message,timestamp:new Date().toISOString()})}}),e.post("/mcp",async(s,t)=>{console.log("Received MCP request:",s.body),await l(s,t,o)}),e.get("/mcp",async(s,t)=>{t.status(405).set("Allow","POST").send("Method Not Allowed")}),e.delete("/mcp",async(s,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("/",(s,t)=>{t.json({name:"AEM MCP Gateway Server",description:"A Model Context Protocol server for Adobe Experience Manager",version:m.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=(o={})=>{const{mcpPort:e=3e3}=o||{};h(o).listen(e,r=>{r&&(console.error("Failed to start server:",r),process.exit(1)),console.log(`AEM MCP Server listening on port ${e}`)})};process.on("SIGINT",async()=>{console.log("Shutting down server..."),process.exit(0)});
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.1.0](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.12...v1.1.0) (2025-07-19)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* [deps] remove swagger ([6cfa01a](https://github.com/easingthemes/aem-mcp-server/commit/6cfa01ac1d8336fb2a5de300cedcc5581726512f))
|
|
7
|
+
|
|
8
|
+
## [1.0.12](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.11...v1.0.12) (2025-07-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* [aem] remove component type check ([6dbf89c](https://github.com/easingthemes/aem-mcp-server/commit/6dbf89c39f78b24e33b3e34992f886c761e6f572))
|
|
14
|
+
|
|
1
15
|
## [1.0.11](https://github.com/easingthemes/aem-mcp-server/compare/v1.0.10...v1.0.11) (2025-07-18)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aem-mcp-server",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "AEM Model Context Protocol (MCP) server",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,32 +33,22 @@
|
|
|
33
33
|
"build": "esbuild src/**/*.ts src/*.ts --outdir=dist --platform=node --minify",
|
|
34
34
|
"build:cjs": "esbuild src/**/*.ts src/*.ts --outdir=dist --platform=node --format=cjs",
|
|
35
35
|
"start": "node dist/index.js",
|
|
36
|
-
"dev": "
|
|
37
|
-
"test:
|
|
36
|
+
"test:dev": "node ./dist/cli.js",
|
|
37
|
+
"test:npm": "npm pack && npm install -g ./aem-mcp-server-$npm_package_version.tgz && aem-mcp -e -H=http://localhost:5502"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.16.0",
|
|
41
41
|
"axios": "^1.10.0",
|
|
42
42
|
"cors": "^2.8.5",
|
|
43
|
-
"dotenv": "^17.2.0",
|
|
44
43
|
"express": "^5.1.0",
|
|
45
|
-
"
|
|
46
|
-
"swagger-ui-express": "^5.0.1",
|
|
47
|
-
"tsx": "^4.20.3",
|
|
48
|
-
"yargs": "^18.0.0",
|
|
49
|
-
"zod": "^4.0.5"
|
|
44
|
+
"yargs": "^18.0.0"
|
|
50
45
|
},
|
|
51
46
|
"devDependencies": {
|
|
52
47
|
"@types/cors": "^2.8.19",
|
|
53
48
|
"@types/express": "^5.0.3",
|
|
54
49
|
"@types/node": "^24.0.14",
|
|
55
|
-
"@types/swagger-jsdoc": "^6.0.4",
|
|
56
|
-
"@types/swagger-ui-express": "^4.1.8",
|
|
57
50
|
"@types/yargs": "^17.0.33",
|
|
58
|
-
"esbuild": "^0.25.6"
|
|
59
|
-
"ts-node-dev": "^2.0.0",
|
|
60
|
-
"tsx": "^4.20.3",
|
|
61
|
-
"typescript": "^5.8.3"
|
|
51
|
+
"esbuild": "^0.25.6"
|
|
62
52
|
},
|
|
63
53
|
"type": "module"
|
|
64
54
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";import t from"swagger-ui-express";import p from"swagger-jsdoc";import{apiPaths as n}from"./api.spec.js";const a=e=>{const o={swaggerDefinition:{openapi:"3.0.0",info:{title:"AEM MCP Gateway API",version:"1.0.0",description:"API documentation for the AEM MCP Gateway Server"},servers:[{url:`http://localhost:${e.mcpPort}`}]},apis:[]},s=p(o);return s.paths=n,s};export const useExplorer=(e,r)=>{const o=a(r);e.use("/api-docs",t.serve,t.setup(o)),e.get("/openapi.json",(s,i)=>{i.json(o)})};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";export const apiPaths={"/mcp":{post:{summary:"JSON-RPC endpoint for MCP calls",description:"Call MCP methods using JSON-RPC 2.0. The method and params must be provided in the request body.",requestBody:{required:!0,content:{"application/json":{schema:{type:"object",properties:{jsonrpc:{type:"string",example:"2.0"},id:{type:"integer",example:1},method:{type:"string",example:"listMethods"},params:{type:"object"}},required:["jsonrpc","id","method"]}}}},responses:{200:{description:"JSON-RPC response",content:{"application/json":{schema:{type:"object",properties:{jsonrpc:{type:"string",example:"2.0"},id:{type:"integer",example:1},result:{type:"object"},error:{type:"object"}}}}}}}}},"/mcp/methods":{get:{summary:"List all available MCP methods",description:"Returns a list of all available MCP methods and their parameters.",responses:{200:{description:"A list of MCP methods",content:{"application/json":{schema:{type:"object",properties:{methods:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:{type:"string"},parameters:{type:"array",items:{type:"string"}}}}},total:{type:"integer"},timestamp:{type:"string",format:"date-time"}}}}}}}}}};
|