russdanner/craftercms-mcp-server-plugin
If you are the rightful owner of craftercms-mcp-server-plugin and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
MCP Server for CrafterCMS is designed to facilitate seamless communication and integration between different components of the CrafterCMS ecosystem.
Model Context Protocol (MCP) Server Implementation in CrafterCMS
Overview
Installs a baseline MCP server into your CrafterCMS Engine instance.
Presequsits
This project relies on external dependencies. You will need to configure Grape-based dependency downloads and the Groovy sandbox for this functionality to work.
In a test environment, you can disable the sandbox instead of using the whitelist/blacklist features to simplify installation.
In CRAFTER_HOME/bin/apache-tomcat/shared/classes/org/crafter/engine/extension/server-config.properties add the following lines:
crafter.engine.groovy.sandbox.enable=false
crafter.engine.groovy.grapes.download.enabled=true
Installation & Configuration
- Ensure that the prerequisites are met (see above).
- Install this plugin into the project.
- Add configuration for authentication
site-config.xmlfor the project:
<cors>
<enable>true</enable>
<accessControlMaxAge>3600</accessControlMaxAge>
<accessControlAllowOrigin>*</accessControlAllowOrigin>
<accessControlAllowMethods>GET\, POST\, PUT\, DELETE\, OPTIONS</accessControlAllowMethods>
<accessControlAllowHeaders>Content-Type\, Mcp-Session-Id\, mcp-protocol-version</accessControlAllowHeaders>
<accessControlAllowCredentials>true</accessControlAllowCredentials>
</cors>
<crafterMcp>
<allowPublicAccess>false</allowPublicAccess>
<auth>
<oauth>
<mcpServer>
<serverUrlBase>http://BASE_URL</serverUrlBase>
<authorizationEndpoint>${crafterMcp.auth.oauth.mcpServer.serverUrlBase}/authorize</authorizationEndpoint>
<tokenEndpoint>${crafterMcp.auth.oauth.mcpServer.serverUrlBase}/token</tokenEndpoint>
<resourceUrl>${crafterMcp.auth.oauth.mcpServer.serverUrlBase}/api/craftermcp/stream</resourceUrl>
</mcpServer>
<authServer>
<serverUrlBase>https://IPD_SERVER/oauth2</serverUrlBase>
<userInfoUrlBase>https://IPD_SERVER/oauth2</userInfoUrlBase>
<jwksBase>https://IPD_SERVER/JWKS-Base</jwksBase>
<clientId>CLIENT-ID</clientId>
<secret>SECRET</secret>
<authorizationEndpoint>${crafterMcp.auth.oauth.authServer.serverUrlBase}/authorize</authorizationEndpoint>
<tokenEndpoint>${crafterMcp.auth.oauth.authServer.serverUrlBase}/token</tokenEndpoint>
<userinfoEndpoint>${crafterMcp.auth.oauth.authServer.userInfoUrlBase}/userInfo</userinfoEndpoint>
<jwksUri>${crafterMcp.auth.oauth.authServer.jwksBase}/.well-known/jwks.json</jwksUri>
</authServer>
</oauth>
</auth>
</crafterMcp>
- Add the MCP server to your
application-context.xmlfor the project:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" parent="crafter.properties"/>
<!-- MCP Server -->
<bean name="crafterMcpServer" class="plugins.org.craftercms.rd.plugin.mcp.server.CrafterMcpServer">
<property name="previewMode" value="${crafter.security.preview.enabled:false}"/>
<property name="allowPublicAccess" value="${crafterMcp.allowPublicAccess}"/>
<property name="oauthMcpServerUrlBase" value="${crafterMcp.auth.oauth.mcpServer.serverUrlBase}" />
<!-- property name="oauthMcpServerAuthUrlBase" value="${crafterMcp.auth.oauth.mcpServer.serverUrlBase}" /-->
<property name="oauthMcpServerAuthorizationEndpoint" value="${crafterMcp.auth.oauth.mcpServer.authorizationEndpoint}" />
<property name="oauthMcpServerTokenEndpoint" value="${crafterMcp.auth.oauth.mcpServer.tokenEndpoint}" />
<property name="oauthMcpServerResourceUrl" value="${crafterMcp.auth.oauth.mcpServer.resourceUrl}" />
<property name="oauthAuthServerUrlBase" value="${crafterMcp.auth.oauth.authServer.serverUrlBase}" />
<property name="oauthAuthServerAuthorizationEndpoint" value="${crafterMcp.auth.oauth.authServer.authorizationEndpoint}" />
<property name="oauthAuthServerTokenEndpoint" value="${crafterMcp.auth.oauth.authServer.tokenEndpoint}" />
<property name="oauthAuthServerUserinfoEndpoint" value="${crafterMcp.auth.oauth.authServer.userinfoEndpoint}" />
<property name="oauthAuthServerJwksUri" value="${crafterMcp.auth.oauth.authServer.jwksUri}" />
<property name="oauthAuthServerClientId" value="${crafterMcp.auth.oauth.authServer.clientId}" />
<property name="oauthAuthServerSecret" value="${crafterMcp.auth.oauth.authServer.secret}" />
<property name="oauthClientRedirectUrlBase" value="${crafterMcp.auth.oauth.client.redirectUrlBase}" />
<property name="authValidator">
<bean name="jwtAuthenticator" class="plugins.org.craftercms.rd.plugin.mcp.server.auth.validator.SimpleAuthValidator">
</bean>
</property>
</bean>
<bean name="toolSpringBeanScanner" class="plugins.org.craftercms.rd.plugin.mcp.server.tools.ToolSpringBeanScanner" init-method="scan">
<property name="mcpServer" ref="crafterMcpServer" />
</bean>
- Configure URL rewrites for OAuth
Add the following to
urlrewrite.xml
<urlrewrite>
<rule>
<from>^/.well-known/oauth-protected-resource(.*)$</from>
<to type="forward" qsappend="true">/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/protected-resource.json</to>
</rule>
<rule>
<from>^/.well-known/oauth-protected-resource/api/craftermcp/stream(.*)$</from>
<to type="forward" qsappend="true">/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/protected-resource.json</to>
</rule>
<rule>
<from>^/authorize(.*)$</from>
<to type="forward" qsappend="true">/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/authorize</to>
</rule>
<rule>
<from>^/.well-known/openid-configuration(.*)$</from>
<to type="forward" qsappend="true">/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/oauth-config.json</to>
</rule>
<rule>
<from>^/.well-known/openid-configuration/api/craftermcp/stream(.*)$</from>
<to type="forward" qsappend="true">/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/oauth-config.json</to>
</rule>
<rule>
<from>^/api/craftermcp/stream/.well-known/openid-configuration(.*)$</from>
<to type="forward" qsappend="true">/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/oauth-config.json</to>
</rule>
<rule>
<from>^/token(.*)$</from>
<to type="forward" qsappend="true">/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/token</to>
</rule>
</urlrewrite>
Example Apache HTTPD Configuration:
<VirtualHost *:80>
ServerName localhost
# RewriteRule (.*) $1/?crafterSite=mcptest [QSA,PT]
Header unset Access-Control-Allow-Origin
Header unset Access-Control-Allow-Methods
Header unset Access-Control-Allow-Headers
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS, PATCH"
Header always set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With, Accept, Origin, Mcp-Session-Id, mcp-protocol-version"
# "Content-Type, Authorization, X-Requested-With, Accept, Origin, Mcp-Session-Id, mcp-protocol-version"
Header always set Access-Control-Expose-Headers "Mcp-Session-Id, Content-Type, mcp-protocol-version"
Header always set Access-Control-Max-Age "3600"
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule .* - [END]
RewriteRule ^(.*)$ $1?crafterSite=mcptest [QSA,PT]
ProxyPass / http://localhost:9080/
ProxyPassReverse / http://localhost:9080/
</VirtualHost>
Adding Tools
Adding Tools as beans
- Declare the tool as a bean
- Wire the bean into the MCP Server:
<bean name="crafterMcpServer" class="org.craftercms.ai.mcp.server.CrafterMcpServer">
...
<property name="mcpTools">
<list>
<ref bean="toolIsIngredientAvail" />
<ref bean="toolGetTemperature" />
</list>
</property>
...
</bean>
Wiring to bean and service records with Reflections
<!-- My Services-->
<bean name="recipeService" class="foo.RecipeService" />
<!-- Tools -->
<bean name="toolIsIngredientAvail" class="org.craftercms.ai.mcp.server.tools.McpToolReflect">
<property name="serviceObject" ref="recipeService" />
<property name="methodName" value="isIngredientAvailable" />
<property name="toolName" value="isIngredientAvailable" />
<property name="toolDescription" value="returns a response indicating if an ingredient is available or not" />
<property name="returnType" value="string" />
<property name="params">
<list>
<bean name="param1" class="org.craftercms.ai.mcp.server.tools.McpTool.ToolParam">
<property name="name" value="ingrdient" />
<property name="type" value="string" />
<property name="description" value="The name of the ingredient to check" />
<property name="required" value="true" />
</bean>
</list>
</property>
</bean>
Wiring to an existing REST API
<bean name="toolGetTemperature" class="org.craftercms.ai.mcp.server.tools.McpToolRest">
<property name="baseUrl" value="http://localhost:8080" />
<property name="url" value="/api/foo/temperature.json" />
<property name="toolName" value="getTemperature" />
<property name="toolDescription" value="Returns the temperature" />
<property name="returnType" value="string" />
<property name="previewToken" value="${ai.crafterPreviewToken}" />
<property name="params">
<list>
<bean name="param1" class="org.craftercms.ai.mcp.server.tools.McpTool.ToolParam">
<property name="name" value="city" />
<property name="type" value="string" />
<property name="description" value="The name of the city to check" />
<property name="required" value="true" />
</bean>
</list>
</property>
</bean>
Wiring tools via Open API spec
<bean name="toolOpenApiSpecParser" class="org.craftercms.ai.mcp.server.tools.ToolOpenApiSpecParser" init-method="parse">
<property name="baseUrl" value="http://localhost:8080" />
<property name="openApiSpecUrl" value="https://raw.githubusercontent.com/craftercms/engine/refs/tags/v4.4.2/src/main/api/engine-api.yaml"/>
<property name="mcpServer" ref="crafterMcpServer" />
<property name="includeTags">
<list>
<value>content</value>
</list>
</property>
<property name="excludeTags">
<list />
</property>
</bean>
Using Annotations
package org.acme
import plugins.org.craftercms.rd.plugin.mcp.server.tools.DeclareTool
import plugins.org.craftercms.rd.plugin.mcp.server.tools.DeclareToolParam
public class AcmeAirlineServices {
@DeclareTool(toolName="bookFlight", returnType="string", toolDescription="Book a specific seat on a given flight", scopes="custom:Wallet, profile, email" )
@DeclareToolParam (name="flight", type="string", description="The flight the user wants")
@DeclareToolParam (name="seat", type="string", description="The seat the user wants")
public String bookFlight(String flight, String seat) {
return "Booked"
}
}
Note: Don't forget to declare the bean in your applicaiton-context.xml
<bean name="acmeAirlineServices" class="org.acme.AcmeAirlineServices" />
Alternative approach (declare bean via Spring annotations) Currently blocked by: https://github.com/craftercms/craftercms/issues/8375
package org.acme
import org.springframework.stereotype.Component
import org.springframework.context.annotation.Lazy
import plugins.org.craftercms.rd.plugin.mcp.server.tools.DeclareTool
import plugins.org.craftercms.rd.plugin.mcp.server.tools.DeclareToolParam
@Component("acmeAirlineServices")
@Lazy(false)
public class AcmeAirlineServices {
@DeclareTool(toolName="bookFlight", returnType="string", toolDescription="Book a specific seat on a given flight", scopes="custom:Wallet, profile, email" )
@DeclareToolParam (name="flight", type="string", description="The flight the user wants")
@DeclareToolParam (name="seat", type="string", description="The seat the user wants")
public String bookFlight(String flight, String seat) {
return "Booked"
}
}
Note: Don't forget to add the Spring context scanning to your Application Context
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.acme"/>
Adding Resources
More to come on this
Adding Prompts
More to come on this
Connecting with the MCP Inspector
- Start the inspector by executing
npx @modelcontextprotocol/inspector - Configure
Transport Typewith the auth URLStreamable HTTP - Configure
URLwith the auth URLhttp://localhost/api/plugins/org/craftercms/rd/plugin/mcp/server/craftermcp/stream - If you are requiring authentication, configure your OID/OAuth client ID