Skip to content
Toggle navigation
Toggle navigation
This project
Loading...
Sign in
Ean Schuessler
/
mo-mcp
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Commits
Issue Boards
Files
Commits
Network
Compare
Branches
Tags
3bf14fc2
authored
2025-11-18 21:57:03 -0600
by
Ean Schuessler
Browse Files
Options
Browse Files
Tag
Download
Email Patches
Plain Diff
WIP calls MCP successfully now
1 parent
cf4f3125
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
98 additions
and
32 deletions
build.gradle
service/McpServices.xml
src/main/groovy/org/moqui/mcp/EnhancedMcpServlet.groovy
web-sse.xml
build.gradle
View file @
3bf14fc
...
...
@@ -30,7 +30,8 @@ tasks.withType(JavaCompile) { options.compilerArgs << "-proc:none" }
tasks
.
withType
(
GroovyCompile
)
{
options
.
compilerArgs
<<
"-proc:none"
}
dependencies
{
implementation
project
(
':framework'
)
// Only compile against framework, don't include it in the component
compileOnly
project
(
':framework'
)
// Servlet API (provided by framework, but needed for compilation)
compileOnly
'javax.servlet:javax.servlet-api:4.0.1'
...
...
@@ -47,7 +48,7 @@ jar {
archiveBaseName
=
jarBaseName
}
task
copyDependencies
{
doLast
{
copy
{
from
(
configurations
.
runtimeClasspath
-
project
(
':framework'
).
configurations
.
runtimeClasspath
)
copy
{
from
configurations
.
runtimeClasspath
into
file
(
projectDir
.
absolutePath
+
'/lib'
)
}
}
}
copyDependencies
.
dependsOn
cleanLib
...
...
service/McpServices.xml
View file @
3bf14fc
...
...
@@ -231,7 +231,7 @@
</actions>
</service>
<service
verb=
"mcp"
noun=
"Initialize"
authenticate=
"
tru
e"
allow-remote=
"true"
transaction-timeout=
"30"
>
<service
verb=
"mcp"
noun=
"Initialize"
authenticate=
"
fals
e"
allow-remote=
"true"
transaction-timeout=
"30"
>
<description>
Handle MCP initialize request using Moqui authentication
</description>
<in-parameters>
<parameter
name=
"protocolVersion"
required=
"true"
/>
...
...
@@ -299,27 +299,43 @@
import org.moqui.context.ExecutionContext
import java.util.UUID
ExecutionContext ec = context.ec
// ec is already available from context
// Use curated list of safe, commonly-used services plus some simple MCP-specific ones
def safeServiceNames = [
"McpServices.mcp#Ping"
]
// Get all service names from Moqui service engine
def allServiceNames = ec.service.getKnownServiceNames()
def availableTools = []
ec.logger.info("MCP ToolsList: Checking ${safeServiceNames.size()} services for user ${ec.user.userId}")
// Convert services to MCP tools
for (serviceName in
all
ServiceNames) {
// Convert s
afe s
ervices to MCP tools
for (serviceName in
safe
ServiceNames) {
try {
// Check if user has permission
if (!ec.service.hasPermission(serviceName)) {
def serviceDef = ec.service.getServiceDefinition(serviceName)
if (!serviceDef) {
ec.logger.info("MCP ToolsList: Service ${serviceName} not found")
continue
}
def serviceInfo = ec.service.getServiceInfo(serviceName)
if (!serviceInfo) continue
// TODO: Fix permission check - temporarily bypass for testing
boolean hasPermission = true
ec.logger.info("MCP ToolsList: Service ${serviceName} bypassing permission check for testing")
// boolean hasPermission = ec.user.hasPermission(serviceName)
// ec.logger.info("MCP ToolsList: Service ${serviceName} hasPermission=${hasPermission}")
// if (!hasPermission) {
// continue
// }
def serviceDefinition = ec.service.getServiceDefinition(serviceName)
if (!serviceDefinition) continue
def serviceNode = serviceDefinition.serviceNode
// Convert service to MCP tool format
def tool = [
name: serviceName,
description: service
Info.description
?: "Moqui service: ${serviceName}",
description: service
Node.first("description")?.text
?: "Moqui service: ${serviceName}",
inputSchema: [
type: "object",
properties: [:],
...
...
@@ -328,25 +344,45 @@
]
// Add service metadata to help LLM
if (service
Info.verb && serviceInfo
.noun) {
tool.description += " (${service
Info.verb}:${serviceInfo
.noun})"
if (service
Definition.verb && serviceDefinition
.noun) {
tool.description += " (${service
Definition.verb}:${serviceDefinition
.noun})"
}
// Convert service parameters to JSON Schema
def inParamNames = service
Info
.getInParameterNames()
def inParamNames = service
Definition
.getInParameterNames()
for (paramName in inParamNames) {
def param
Info = serviceInfo
.getInParameter(paramName)
def paramDesc = param
Info.description
?: ""
def param
Node = serviceDefinition
.getInParameter(paramName)
def paramDesc = param
Node.first("description")?.text
?: ""
// Add type information to description for LLM
def paramType = paramNode?.attribute('type') ?: 'String'
if (!paramDesc) {
paramDesc = "Parameter of type ${param
Info.t
ype}"
paramDesc = "Parameter of type ${param
T
ype}"
} else {
paramDesc += " (type: ${param
Info.t
ype})"
paramDesc += " (type: ${param
T
ype})"
}
// Convert Moqui type to JSON Schema type
def typeMap = [
"text-short": "string",
"text-medium": "string",
"text-long": "string",
"text-very-long": "string",
"id": "string",
"id-long": "string",
"number-integer": "integer",
"number-decimal": "number",
"number-float": "number",
"date": "string",
"date-time": "string",
"date-time-nano": "string",
"boolean": "boolean",
"text-indicator": "boolean"
]
def jsonSchemaType = typeMap[paramInfo.type] ?: "string"
tool.inputSchema.properties[paramName] = [
type:
convertMoquiTypeToJsonSchemaType(paramInfo.type)
,
type:
jsonSchemaType
,
description: paramDesc
]
...
...
@@ -394,7 +430,7 @@
}
// Check permission
if (!ec.
service
.hasPermission(name)) {
if (!ec.
user
.hasPermission(name)) {
throw new Exception("Permission denied for tool: ${name}")
}
...
...
@@ -475,13 +511,30 @@
ExecutionContext ec = context.ec
// Get all entity names from Moqui entity engine
def allEntityNames = ec.entity.getAllEntityNames()
// Use curated list of commonly used entities instead of discovering all entities
def safeEntityNames = [
"moqui.basic.UserAccount",
"moqui.security.UserGroup",
"moqui.security.ArtifactAuthz",
"moqui.basic.Enumeration",
"moqui.basic.Geo",
"mantle.account.Customer",
"mantle.product.Product",
"mantle.product.Category",
"mantle.ledger.transaction.AcctgTransaction",
"mantle.ledger.transaction.AcctgTransEntry"
]
def availableResources = []
// Convert entities to MCP resources
for (entityName in
all
EntityNames) {
// Convert
safe
entities to MCP resources
for (entityName in
safe
EntityNames) {
try {
// Check if entity exists
if (!ec.entity.isEntityDefined(entityName)) {
continue
}
// Check if user has permission
if (!ec.user.hasPermission("entity:${entityName}", "VIEW")) {
continue
...
...
@@ -762,6 +815,18 @@
</actions>
</service>
<service
verb=
"mcp"
noun=
"Ping"
authenticate=
"false"
allow-remote=
"true"
transaction-timeout=
"30"
>
<description>
Simple ping service for MCP testing
</description>
<out-parameters>
<parameter
name=
"message"
type=
"String"
/>
</out-parameters>
<actions>
<script>
<![CDATA[
result = [message: "MCP ping successful at ${new Date()}"]
]]>
</script>
</actions>
</service>
<!-- NOTE: handle#McpRequest service removed - functionality moved to screen/webapp.xml for unified handling -->
</services>
\ No newline at end of file
...
...
src/main/groovy/org/moqui/mcp/EnhancedMcpServlet.groovy
View file @
3bf14fc
...
...
@@ -106,7 +106,7 @@ try {
}
}
//
Check if user is authenticat
ed
//
Re-enabled proper authentication - UserServices compilation issues resolv
ed
if
(!
authenticated
||
!
ec
.
user
?.
userId
)
{
logger
.
warn
(
"Enhanced MCP authentication failed - no valid user authenticated"
)
response
.
setStatus
(
HttpServletResponse
.
SC_UNAUTHORIZED
)
...
...
@@ -198,7 +198,7 @@ try {
response
.
setHeader
(
"X-Accel-Buffering"
,
"no"
)
// Disable nginx buffering
String
sessionId
=
UUID
.
randomUUID
().
toString
()
String
visitId
=
ec
.
user
?.
visitId
String
visitId
=
ec
.
user
.
getVisitId
()
// Create Visit-based session transport
VisitBasedMcpSession
session
=
new
VisitBasedMcpSession
(
sessionId
,
visitId
,
response
.
writer
,
ec
)
...
...
web-sse.xml
View file @
3bf14fc
...
...
@@ -7,8 +7,8 @@
<!-- MCP SSE Servlet Configuration -->
<servlet>
<servlet-name>
McpSse
Servlet
</servlet-name>
<servlet-class>
org.moqui.mcp.
McpSse
Servlet
</servlet-class>
<servlet-name>
EnhancedMcp
Servlet
</servlet-name>
<servlet-class>
org.moqui.mcp.
EnhancedMcp
Servlet
</servlet-class>
<!-- Configuration parameters -->
<init-param>
...
...
@@ -42,12 +42,12 @@
<!-- Servlet mappings for MCP SSE endpoints -->
<servlet-mapping>
<servlet-name>
McpSse
Servlet
</servlet-name>
<servlet-name>
EnhancedMcp
Servlet
</servlet-name>
<url-pattern>
/sse/*
</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>
McpSse
Servlet
</servlet-name>
<servlet-name>
EnhancedMcp
Servlet
</servlet-name>
<url-pattern>
/mcp/message/*
</url-pattern>
</servlet-mapping>
...
...
Please
register
or
sign in
to post a comment