Committing WIP
Showing
12 changed files
with
1222 additions
and
0 deletions
AGENTS.md
0 → 100644
| 1 | # Moqui MCP v2 Server Agent Guide | ||
| 2 | |||
| 3 | This guide explains how to interact with the Moqui MCP v2 server component for AI-Moqui integration using the Model Context Protocol (MCP). | ||
| 4 | |||
| 5 | ## Architecture Overview | ||
| 6 | |||
| 7 | ``` | ||
| 8 | moqui-mcp-2/ | ||
| 9 | ├── AGENTS.md # This file - MCP server agent guide | ||
| 10 | ├── component.xml # Component configuration | ||
| 11 | ├── entity/ # MCP entity definitions | ||
| 12 | │ └── McpCoreEntities.xml # Core MCP entities | ||
| 13 | ├── service/ # MCP service implementations | ||
| 14 | │ ├── McpDiscoveryServices.xml # Tool discovery services | ||
| 15 | │ ├── McpExecutionServices.xml # Tool execution services | ||
| 16 | │ ├── McpSecurityServices.xml # Session/security services | ||
| 17 | │ └── mcp.rest.xml # REST API endpoints | ||
| 18 | ├── screen/ # Web interface screens | ||
| 19 | │ └── webapp.xml # MCP web interface | ||
| 20 | └── template/ # UI templates | ||
| 21 | └── McpInfo.html.ftl # MCP info page | ||
| 22 | ``` | ||
| 23 | |||
| 24 | ## MCP Server Capabilities | ||
| 25 | |||
| 26 | ### Core Features | ||
| 27 | - **Session Management**: Secure session creation, validation, and termination | ||
| 28 | - **Tool Discovery**: Dynamic discovery of available Moqui services and entities | ||
| 29 | - **Tool Execution**: Secure execution of services with proper authorization | ||
| 30 | - **Entity Operations**: Direct entity query capabilities with permission checks | ||
| 31 | - **Health Monitoring**: Server health and status monitoring | ||
| 32 | |||
| 33 | ### Security Model | ||
| 34 | - **Session-based Authentication**: Uses Moqui's built-in user authentication | ||
| 35 | - **Context Tokens**: Secure token-based session validation | ||
| 36 | - **Permission Validation**: All operations checked against Moqui security framework | ||
| 37 | - **Audit Logging**: Complete audit trail of all MCP operations | ||
| 38 | |||
| 39 | ## REST API Endpoints | ||
| 40 | |||
| 41 | ### Session Management | ||
| 42 | ``` | ||
| 43 | POST /mcp-2/session | ||
| 44 | - Create new MCP session | ||
| 45 | - Parameters: username, password, clientInfo, ipAddress, userAgent | ||
| 46 | - Returns: sessionId, contextToken, expiration | ||
| 47 | |||
| 48 | POST /mcp-2/session/{visitId}/validate | ||
| 49 | - Validate existing session | ||
| 50 | - Parameters: visitId (from path), contextToken | ||
| 51 | - Returns: session validity, user info | ||
| 52 | |||
| 53 | POST /mcp-2/session/{visitId}/terminate | ||
| 54 | - Terminate active session | ||
| 55 | - Parameters: visitId (from path), contextToken | ||
| 56 | - Returns: termination confirmation | ||
| 57 | ``` | ||
| 58 | |||
| 59 | ### Tool Discovery | ||
| 60 | ``` | ||
| 61 | POST /mcp-2/tools/discover | ||
| 62 | - Discover available MCP tools | ||
| 63 | - Parameters: userAccountId, visitId, servicePattern, packageName, verb, noun | ||
| 64 | - Returns: List of available tools with metadata | ||
| 65 | |||
| 66 | POST /mcp-2/tools/validate | ||
| 67 | - Validate access to specific tools | ||
| 68 | - Parameters: userAccountId, serviceNames, visitId | ||
| 69 | - Returns: Access validation results | ||
| 70 | ``` | ||
| 71 | |||
| 72 | ### Tool Execution | ||
| 73 | ``` | ||
| 74 | POST /mcp-2/tools/execute | ||
| 75 | - Execute service as MCP tool | ||
| 76 | - Parameters: sessionId, contextToken, serviceName, parameters, toolCallId | ||
| 77 | - Returns: Service execution results | ||
| 78 | |||
| 79 | POST /mcp-2/tools/entity/query | ||
| 80 | - Execute entity query as MCP tool | ||
| 81 | - Parameters: sessionId, contextToken, serviceName, parameters, toolCallId | ||
| 82 | - Returns: Entity query results | ||
| 83 | ``` | ||
| 84 | |||
| 85 | ### Health Check | ||
| 86 | ``` | ||
| 87 | GET /mcp-2/health | ||
| 88 | - Server health status | ||
| 89 | - Returns: Server status, version, available endpoints | ||
| 90 | ``` | ||
| 91 | |||
| 92 | ## Agent Integration Patterns | ||
| 93 | |||
| 94 | ### 1. Session Initialization | ||
| 95 | ```bash | ||
| 96 | # Create session | ||
| 97 | curl -X POST http://localhost:8080/mcp-2/session \ | ||
| 98 | -H "Content-Type: application/json" \ | ||
| 99 | -d '{ | ||
| 100 | "username": "admin", | ||
| 101 | "password": "admin", | ||
| 102 | "clientInfo": "AI-Agent-v1.0" | ||
| 103 | }' | ||
| 104 | |||
| 105 | # Response: { "sessionId": "12345", "contextToken": "abc123", "expires": "2025-01-01T00:00:00Z" } | ||
| 106 | ``` | ||
| 107 | |||
| 108 | ### 2. Tool Discovery Workflow | ||
| 109 | ```bash | ||
| 110 | # Discover available tools | ||
| 111 | curl -X POST http://localhost:8080/mcp-2/tools/discover \ | ||
| 112 | -H "Content-Type: application/json" \ | ||
| 113 | -H "Authorization: Bearer abc123" \ | ||
| 114 | -d '{ | ||
| 115 | "userAccountId": "ADMIN", | ||
| 116 | "visitId": "12345", | ||
| 117 | "servicePattern": "org.moqui.*" | ||
| 118 | }' | ||
| 119 | |||
| 120 | # Response: { "tools": [ { "name": "create#Order", "description": "...", "parameters": [...] } ] } | ||
| 121 | ``` | ||
| 122 | |||
| 123 | ### 3. Tool Execution Pattern | ||
| 124 | ```bash | ||
| 125 | # Execute a service | ||
| 126 | curl -X POST http://localhost:8080/mcp-2/tools/execute \ | ||
| 127 | -H "Content-Type: application/json" \ | ||
| 128 | -H "Authorization: Bearer abc123" \ | ||
| 129 | -d '{ | ||
| 130 | "sessionId": "12345", | ||
| 131 | "contextToken": "abc123", | ||
| 132 | "serviceName": "org.moqui.example.Services.create#Example", | ||
| 133 | "parameters": { "field1": "value1", "field2": "value2" }, | ||
| 134 | "toolCallId": "call_123" | ||
| 135 | }' | ||
| 136 | |||
| 137 | # Response: { "result": { "exampleId": "EX123" }, "success": true } | ||
| 138 | ``` | ||
| 139 | |||
| 140 | ## Security Considerations | ||
| 141 | |||
| 142 | ### Authentication Requirements | ||
| 143 | - All MCP operations require valid session authentication | ||
| 144 | - Context tokens must be included in all requests after session creation | ||
| 145 | - Sessions expire based on configured timeout settings | ||
| 146 | |||
| 147 | ### Permission Validation | ||
| 148 | - Tool access validated against Moqui user permissions | ||
| 149 | - Entity operations respect Moqui entity access controls | ||
| 150 | - Service execution requires appropriate service permissions | ||
| 151 | |||
| 152 | ### Audit and Logging | ||
| 153 | - All MCP operations logged to Moqui audit system | ||
| 154 | - Session creation, tool discovery, and execution tracked | ||
| 155 | - Failed authentication attempts monitored | ||
| 156 | |||
| 157 | ## Error Handling | ||
| 158 | |||
| 159 | ### Common Error Responses | ||
| 160 | ```json | ||
| 161 | { | ||
| 162 | "error": "INVALID_SESSION", | ||
| 163 | "message": "Session expired or invalid", | ||
| 164 | "code": 401 | ||
| 165 | } | ||
| 166 | |||
| 167 | { | ||
| 168 | "error": "PERMISSION_DENIED", | ||
| 169 | "message": "User lacks permission for tool", | ||
| 170 | "code": 403 | ||
| 171 | } | ||
| 172 | |||
| 173 | { | ||
| 174 | "error": "TOOL_NOT_FOUND", | ||
| 175 | "message": "Requested tool not available", | ||
| 176 | "code": 404 | ||
| 177 | } | ||
| 178 | ``` | ||
| 179 | |||
| 180 | ### Error Recovery Strategies | ||
| 181 | - **Session Expiration**: Re-authenticate and create new session | ||
| 182 | - **Permission Issues**: Request additional permissions or use alternative tools | ||
| 183 | - **Tool Not Found**: Re-discover available tools and update tool registry | ||
| 184 | |||
| 185 | ## Performance Optimization | ||
| 186 | |||
| 187 | ### Caching Strategies | ||
| 188 | - Tool discovery results cached for session duration | ||
| 189 | - Permission validation results cached per user | ||
| 190 | - Entity query results cached based on Moqui cache settings | ||
| 191 | |||
| 192 | ### Connection Management | ||
| 193 | - Use persistent HTTP connections where possible | ||
| 194 | - Implement session pooling for high-frequency operations | ||
| 195 | - Monitor session lifecycle and cleanup expired sessions | ||
| 196 | |||
| 197 | ## Integration Examples | ||
| 198 | |||
| 199 | ### Python Client Example | ||
| 200 | ```python | ||
| 201 | import requests | ||
| 202 | import json | ||
| 203 | |||
| 204 | class MoquiMCPClient: | ||
| 205 | def __init__(self, base_url, username, password): | ||
| 206 | self.base_url = base_url | ||
| 207 | self.session_id = None | ||
| 208 | self.context_token = None | ||
| 209 | self.authenticate(username, password) | ||
| 210 | |||
| 211 | def authenticate(self, username, password): | ||
| 212 | response = requests.post(f"{self.base_url}/mcp-2/session", json={ | ||
| 213 | "username": username, | ||
| 214 | "password": password | ||
| 215 | }) | ||
| 216 | data = response.json() | ||
| 217 | self.session_id = data["sessionId"] | ||
| 218 | self.context_token = data["contextToken"] | ||
| 219 | |||
| 220 | def discover_tools(self, pattern="*"): | ||
| 221 | response = requests.post(f"{self.base_url}/mcp-2/tools/discover", | ||
| 222 | headers={"Authorization": f"Bearer {self.context_token}"}, | ||
| 223 | json={"servicePattern": pattern} | ||
| 224 | ) | ||
| 225 | return response.json() | ||
| 226 | |||
| 227 | def execute_tool(self, service_name, parameters): | ||
| 228 | response = requests.post(f"{self.base_url}/mcp-2/tools/execute", | ||
| 229 | headers={"Authorization": f"Bearer {self.context_token}"}, | ||
| 230 | json={ | ||
| 231 | "sessionId": self.session_id, | ||
| 232 | "contextToken": self.context_token, | ||
| 233 | "serviceName": service_name, | ||
| 234 | "parameters": parameters | ||
| 235 | } | ||
| 236 | ) | ||
| 237 | return response.json() | ||
| 238 | ``` | ||
| 239 | |||
| 240 | ### JavaScript Client Example | ||
| 241 | ```javascript | ||
| 242 | class MoquiMCPClient { | ||
| 243 | constructor(baseUrl, username, password) { | ||
| 244 | this.baseUrl = baseUrl; | ||
| 245 | this.authenticate(username, password); | ||
| 246 | } | ||
| 247 | |||
| 248 | async authenticate(username, password) { | ||
| 249 | const response = await fetch(`${this.baseUrl}/mcp-2/session`, { | ||
| 250 | method: 'POST', | ||
| 251 | headers: { 'Content-Type': 'application/json' }, | ||
| 252 | body: JSON.stringify({ username, password }) | ||
| 253 | }); | ||
| 254 | const data = await response.json(); | ||
| 255 | this.sessionId = data.sessionId; | ||
| 256 | this.contextToken = data.contextToken; | ||
| 257 | } | ||
| 258 | |||
| 259 | async discoverTools(pattern = '*') { | ||
| 260 | const response = await fetch(`${this.baseUrl}/mcp-2/tools/discover`, { | ||
| 261 | method: 'POST', | ||
| 262 | headers: { | ||
| 263 | 'Content-Type': 'application/json', | ||
| 264 | 'Authorization': `Bearer ${this.contextToken}` | ||
| 265 | }, | ||
| 266 | body: JSON.stringify({ servicePattern: pattern }) | ||
| 267 | }); | ||
| 268 | return response.json(); | ||
| 269 | } | ||
| 270 | |||
| 271 | async executeTool(serviceName, parameters) { | ||
| 272 | const response = await fetch(`${this.baseUrl}/mcp-2/tools/execute`, { | ||
| 273 | method: 'POST', | ||
| 274 | headers: { | ||
| 275 | 'Content-Type': 'application/json', | ||
| 276 | 'Authorization': `Bearer ${this.contextToken}` | ||
| 277 | }, | ||
| 278 | body: JSON.stringify({ | ||
| 279 | sessionId: this.sessionId, | ||
| 280 | contextToken: this.contextToken, | ||
| 281 | serviceName, | ||
| 282 | parameters | ||
| 283 | }) | ||
| 284 | }); | ||
| 285 | return response.json(); | ||
| 286 | } | ||
| 287 | } | ||
| 288 | ``` | ||
| 289 | |||
| 290 | ## Monitoring and Debugging | ||
| 291 | |||
| 292 | ### Health Monitoring | ||
| 293 | - Monitor `/mcp-2/health` endpoint for server status | ||
| 294 | - Track session creation and termination rates | ||
| 295 | - Monitor tool execution success/failure rates | ||
| 296 | |||
| 297 | ### Debug Information | ||
| 298 | - Check Moqui logs for MCP-related errors | ||
| 299 | - Use Moqui's built-in monitoring tools | ||
| 300 | - Monitor REST API access patterns | ||
| 301 | |||
| 302 | ### Performance Metrics | ||
| 303 | - Session creation time | ||
| 304 | - Tool discovery response time | ||
| 305 | - Tool execution duration | ||
| 306 | - Concurrent session limits | ||
| 307 | |||
| 308 | ## Configuration | ||
| 309 | |||
| 310 | ### Component Configuration | ||
| 311 | - Session timeout settings in `component.xml` | ||
| 312 | - Security permission mappings | ||
| 313 | - Cache configuration for performance | ||
| 314 | |||
| 315 | ### Runtime Configuration | ||
| 316 | - Database connection settings | ||
| 317 | - REST API endpoint configuration | ||
| 318 | - Security context configuration | ||
| 319 | |||
| 320 | --- | ||
| 321 | |||
| 322 | **This guide focuses on MCP server interaction patterns and agent integration.** | ||
| 323 | For Moqui framework integration details, see the main project AGENTS.md files. | ||
| 324 | For component-specific implementation details, refer to individual service files. | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
README.md
0 → 100644
| 1 | # Moqui MCP Server v0.2.0 | ||
| 2 | |||
| 3 | ## Overview | ||
| 4 | Simplified MCP server that uses Moqui's native service engine directly, eliminating need for manual tool registry management. | ||
| 5 | |||
| 6 | ## Evolution from v0.1.0 (moqui-mcp) to v0.2.0 (moqui-mcp-2) | ||
| 7 | |||
| 8 | ### Architectural Problems Identified in v0.1.0 | ||
| 9 | |||
| 10 | The original moqui-mcp implementation had significant architectural duplications with Moqui's native systems: | ||
| 11 | |||
| 12 | 1. **McpToolRegistry vs Service Engine** | ||
| 13 | - Manual tool registry duplicated Moqui's service discovery | ||
| 14 | - Required manual maintenance of tool definitions | ||
| 15 | - Added unnecessary database queries and complexity | ||
| 16 | |||
| 17 | 2. **McpAuditLog vs ArtifactHit** | ||
| 18 | - Custom audit logging duplicated Moqui's proven ArtifactHit system | ||
| 19 | - Missed built-in performance monitoring and aggregation | ||
| 20 | - Separate audit trail instead of unified system-wide analytics | ||
| 21 | |||
| 22 | 3. **McpSession vs Visit Entity** | ||
| 23 | - Custom session management duplicated Moqui's Visit entity | ||
| 24 | - Lost geo-location, referrer tracking, and other built-in features | ||
| 25 | - Separate session lifecycle instead of leveraging existing patterns | ||
| 26 | |||
| 27 | 4. **McpToolCall vs ArtifactHit** | ||
| 28 | - Custom tool call tracking duplicated ArtifactHit functionality | ||
| 29 | - Manual performance tracking instead of automatic aggregation | ||
| 30 | - Separate error handling and logging | ||
| 31 | |||
| 32 | ### v0.2.0 Architectural Solutions | ||
| 33 | |||
| 34 | **Complete Elimination of Duplications**: | ||
| 35 | - ❌ McpToolRegistry → ✅ Service Engine (`ec.service.getServiceNames()`) | ||
| 36 | - ❌ McpAuditLog → ✅ ArtifactHit (native audit & performance) | ||
| 37 | - ❌ McpSession → ✅ Visit Entity (extended with MCP fields) | ||
| 38 | - ❌ McpToolCall → ✅ ArtifactHit (with `artifactType="MCP"`) | ||
| 39 | |||
| 40 | **Key Architectural Changes**: | ||
| 41 | |||
| 42 | 1. **Native Service Discovery** | ||
| 43 | ```groovy | ||
| 44 | // Before: Query McpToolRegistry | ||
| 45 | def tools = ec.entity.find("McpToolRegistry").list() | ||
| 46 | |||
| 47 | // After: Direct service engine access | ||
| 48 | def allServiceNames = ec.service.getServiceNames() | ||
| 49 | def hasPermission = ec.service.hasPermission(serviceName) | ||
| 50 | ``` | ||
| 51 | |||
| 52 | 2. **Unified Session Management** | ||
| 53 | ```xml | ||
| 54 | <!-- Before: Custom McpSession entity --> | ||
| 55 | <entity entity-name="McpSession" package="org.moqui.mcp"> | ||
| 56 | |||
| 57 | <!-- After: Extend native Visit entity --> | ||
| 58 | <extend-entity entity-name="Visit" package="moqui.server"> | ||
| 59 | <field name="mcpContextToken" type="text-medium"/> | ||
| 60 | <field name="mcpStatusId" type="id"/> | ||
| 61 | <field name="mcpExpiresDate" type="date-time"/> | ||
| 62 | </extend-entity> | ||
| 63 | ``` | ||
| 64 | |||
| 65 | 3. **System-Wide Analytics** | ||
| 66 | ```sql | ||
| 67 | -- Before: Separate MCP tracking | ||
| 68 | SELECT * FROM McpToolCall ORDER BY startTime DESC; | ||
| 69 | SELECT * FROM McpAuditLog WHERE severity = 'McpSeverityWarning'; | ||
| 70 | |||
| 71 | -- After: Unified system tracking | ||
| 72 | SELECT * FROM moqui.server.ArtifactHit | ||
| 73 | WHERE artifactType = 'MCP' ORDER BY startDateTime DESC; | ||
| 74 | SELECT * FROM moqui.server.ArtifactHitReport | ||
| 75 | WHERE artifactType = 'MCP'; | ||
| 76 | ``` | ||
| 77 | |||
| 78 | ### Benefits Achieved | ||
| 79 | |||
| 80 | **Zero Maintenance**: | ||
| 81 | - No manual tool registry updates | ||
| 82 | - Automatic reflection of actual Moqui services | ||
| 83 | - No custom audit infrastructure to maintain | ||
| 84 | |||
| 85 | **Maximum Performance**: | ||
| 86 | - Direct service engine access (no extra lookups) | ||
| 87 | - Native caching and optimization | ||
| 88 | - Built-in performance monitoring | ||
| 89 | |||
| 90 | **Unified Analytics**: | ||
| 91 | - MCP operations visible in system-wide reports | ||
| 92 | - Automatic aggregation via ArtifactHitBin | ||
| 93 | - Standard reporting tools and dashboards | ||
| 94 | |||
| 95 | **Architectural Purity**: | ||
| 96 | - Follows Moqui's established patterns | ||
| 97 | - Leverages proven native systems | ||
| 98 | - REST service pattern (MCP as service invocation layer) | ||
| 99 | |||
| 100 | ### Technical Implementation | ||
| 101 | |||
| 102 | **MCP Operation Flow**: | ||
| 103 | 1. Session creation → `Visit` record with MCP extensions | ||
| 104 | 2. Tool discovery → `ec.service.getServiceNames()` + permission check | ||
| 105 | 3. Tool execution → Direct service call + `ArtifactHit` creation | ||
| 106 | 4. Analytics → Native `ArtifactHitReport` aggregation | ||
| 107 | |||
| 108 | **Data Model**: | ||
| 109 | - Sessions: `moqui.server.Visit` (extended) | ||
| 110 | - Operations: `moqui.server.ArtifactHit` (filtered) | ||
| 111 | - Performance: `moqui.server.ArtifactHitBin` (automatic) | ||
| 112 | - Security: `moqui.security.UserGroupPermission` (native) | ||
| 113 | |||
| 114 | This evolution represents a complete architectural alignment with Moqui's core design principles while maintaining full MCP functionality. | ||
| 115 | |||
| 116 | ## Quick Start | ||
| 117 | |||
| 118 | ### 1. Add Component to Moqui | ||
| 119 | Add to your `MoquiConf.xml`: | ||
| 120 | ```xml | ||
| 121 | <component name="moqui-mcp-2" location="moqui-mcp-2/"/> | ||
| 122 | ``` | ||
| 123 | |||
| 124 | ### 2. Start Moqui | ||
| 125 | ```bash | ||
| 126 | ./gradlew run | ||
| 127 | ``` | ||
| 128 | |||
| 129 | ### 3. Test MCP Server | ||
| 130 | ```bash | ||
| 131 | # Health check | ||
| 132 | curl http://localhost:8080/mcp-2/health | ||
| 133 | |||
| 134 | # Create session | ||
| 135 | curl -X POST http://localhost:8080/mcp-2/session \ | ||
| 136 | -H "Content-Type: application/json" \ | ||
| 137 | -d '{"username": "admin", "password": "admin"}' | ||
| 138 | ``` | ||
| 139 | |||
| 140 | ## Integration with Opencode | ||
| 141 | |||
| 142 | ### MCP Skill Configuration | ||
| 143 | Add to your Opencode configuration: | ||
| 144 | |||
| 145 | ```json | ||
| 146 | { | ||
| 147 | "skills": [ | ||
| 148 | { | ||
| 149 | "name": "moqui-mcp", | ||
| 150 | "type": "mcp", | ||
| 151 | "endpoint": "http://localhost:8080/mcp-2", | ||
| 152 | "auth": { | ||
| 153 | "type": "session", | ||
| 154 | "username": "admin", | ||
| 155 | "password": "admin" | ||
| 156 | }, | ||
| 157 | "description": "Query business data from Moqui ERP system" | ||
| 158 | } | ||
| 159 | ] | ||
| 160 | } | ||
| 161 | ``` | ||
| 162 | |||
| 163 | ### Example Business Questions | ||
| 164 | Once configured, you can ask: | ||
| 165 | |||
| 166 | - "Show me all active customers" | ||
| 167 | - "What are recent orders for ACME Corp?" | ||
| 168 | - "List all products in inventory" | ||
| 169 | - "Find unpaid invoices" | ||
| 170 | - "Show user accounts with admin permissions" | ||
| 171 | |||
| 172 | ## API Endpoints | ||
| 173 | |||
| 174 | ### Session Management | ||
| 175 | - `POST /session` - Create session | ||
| 176 | - `POST /session/{visitId}/validate` - Validate session | ||
| 177 | - `POST /session/{visitId}/terminate` - Terminate session | ||
| 178 | |||
| 179 | ### Tool Operations | ||
| 180 | - `POST /tools/discover` - Discover available tools | ||
| 181 | - `POST /tools/validate` - Validate tool access | ||
| 182 | - `POST /tools/execute` - Execute service tool | ||
| 183 | - `POST /tools/entity/query` - Execute entity query | ||
| 184 | |||
| 185 | ### System | ||
| 186 | - `GET /health` - Server health check | ||
| 187 | |||
| 188 | ## Security Model | ||
| 189 | |||
| 190 | ### Permission-Based Access | ||
| 191 | Tools are discovered based on user's `UserGroupPermission` settings: | ||
| 192 | - Services: `ec.service.hasPermission(serviceName)` | ||
| 193 | - Entities: `ec.user.hasPermission("entity:EntityName", "VIEW")` | ||
| 194 | |||
| 195 | ### Audit Trail | ||
| 196 | All operations are tracked via Moqui's native `ArtifactHit` system: | ||
| 197 | - Automatic performance monitoring | ||
| 198 | - Built-in security event tracking | ||
| 199 | - Configurable persistence (database/ElasticSearch) | ||
| 200 | - Standard reporting via `ArtifactHitReport` | ||
| 201 | |||
| 202 | ## Monitoring | ||
| 203 | |||
| 204 | ### Check Logs | ||
| 205 | ```bash | ||
| 206 | # MCP operations | ||
| 207 | tail -f moqui.log | grep "MCP" | ||
| 208 | |||
| 209 | # Session activity | ||
| 210 | tail -f moqui.log | grep "Visit" | ||
| 211 | ``` | ||
| 212 | |||
| 213 | ### Database Queries | ||
| 214 | ```sql | ||
| 215 | -- MCP operations via ArtifactHit | ||
| 216 | SELECT * FROM moqui.server.ArtifactHit | ||
| 217 | WHERE artifactType = 'MCP' | ||
| 218 | ORDER BY startDateTime DESC LIMIT 10; | ||
| 219 | |||
| 220 | -- MCP sessions (using Visit entity) | ||
| 221 | SELECT * FROM moqui.server.Visit | ||
| 222 | WHERE mcpStatusId = 'McsActive' AND webappName = 'mcp-2'; | ||
| 223 | |||
| 224 | -- MCP service operations | ||
| 225 | SELECT * FROM moqui.server.ArtifactHit | ||
| 226 | WHERE artifactType = 'MCP' AND artifactSubType = 'Service' | ||
| 227 | ORDER BY startDateTime DESC LIMIT 10; | ||
| 228 | |||
| 229 | -- MCP entity operations | ||
| 230 | SELECT * FROM moqui.server.ArtifactHit | ||
| 231 | WHERE artifactType = 'MCP' AND artifactSubType = 'Entity' | ||
| 232 | ORDER BY startDateTime DESC LIMIT 10; | ||
| 233 | |||
| 234 | -- Performance analytics | ||
| 235 | SELECT * FROM moqui.server.ArtifactHitReport | ||
| 236 | WHERE artifactType = 'MCP'; | ||
| 237 | ``` | ||
| 238 | |||
| 239 | ## Next Steps | ||
| 240 | |||
| 241 | 1. **Test Basic Functionality**: Verify session creation and tool discovery | ||
| 242 | 2. **Configure Opencode Skill**: Add MCP skill to your Opencode instance | ||
| 243 | 3. **Test Business Queries**: Try natural language business questions | ||
| 244 | 4. **Monitor Performance**: Check logs and audit tables | ||
| 245 | 5. **Extend as Needed**: Add custom services/entities for specific business logic | ||
| 246 | |||
| 247 | ## Architecture Benefits | ||
| 248 | |||
| 249 | - **Zero Maintenance**: No manual tool registry updates | ||
| 250 | - **Always Current**: Reflects actual Moqui services in real-time | ||
| 251 | - **Secure**: Uses Moqui's proven permission system | ||
| 252 | - **Performant**: Direct service engine access, no extra lookups | ||
| 253 | - **Unified Analytics**: All MCP operations tracked via native ArtifactHit | ||
| 254 | - **Built-in Reporting**: Uses ArtifactHitReport for standard analytics | ||
| 255 | - **Native Session Management**: Uses Moqui's Visit entity for session tracking | ||
| 256 | - **REST Service Pattern**: MCP is fundamentally a REST service invocation layer | ||
| 257 | - **Zero Custom Tracking**: Eliminates McpToolCall duplication with ArtifactHit | ||
| 258 | |||
| 259 | This MVP provides core functionality needed to integrate Moqui with Opencode as an MCP skill for business intelligence queries. | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
build.gradle
0 → 100644
component.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- This software is in the public domain under CC0 1.0 Universal plus a | ||
| 3 | Grant of Patent License. | ||
| 4 | |||
| 5 | To the extent possible under law, the author(s) have dedicated all | ||
| 6 | copyright and related and neighboring rights to this software to the | ||
| 7 | public domain worldwide. This software is distributed without any warranty. | ||
| 8 | |||
| 9 | You should have received a copy of the CC0 Public Domain Dedication | ||
| 10 | along with this software (see the LICENSE.md file). If not, see | ||
| 11 | <https://creativecommons.org/publicdomain/zero/1.0/>. --> | ||
| 12 | |||
| 13 | <component xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 14 | xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/component-definition-3.xsd"> | ||
| 15 | |||
| 16 | <!-- No dependencies - uses only core framework --> | ||
| 17 | |||
| 18 | <entity-factory load-path="entity/" /> | ||
| 19 | <service-factory load-path="service/" /> | ||
| 20 | <screen-factory load-path="screen/" /> | ||
| 21 | |||
| 22 | <!-- Load seed data --> | ||
| 23 | <entity-factory load-data="data/McpSecuritySeedData.xml" /> | ||
| 24 | |||
| 25 | |||
| 26 | <webapp name="mcp-2" | ||
| 27 | title="MCP Server v2" | ||
| 28 | server="default" | ||
| 29 | location="webapp" | ||
| 30 | https-enabled="false" | ||
| 31 | require-session-token="false"> | ||
| 32 | <root-screen host=".*" screen-path="mcp-2"/> | ||
| 33 | </webapp> | ||
| 34 | |||
| 35 | |||
| 36 | |||
| 37 | </component> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
data/McpSecuritySeedData.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- This software is in the public domain under CC0 1.0 Universal plus a | ||
| 3 | Grant of Patent License. | ||
| 4 | |||
| 5 | To the extent possible under law, the author(s) have dedicated all | ||
| 6 | copyright and related and neighboring rights to this software to the | ||
| 7 | public domain worldwide. This software is distributed without any warranty. | ||
| 8 | |||
| 9 | You should have received a copy of the CC0 Public Domain Dedication | ||
| 10 | along with this software (see the LICENSE.md file). If not, see | ||
| 11 | <https://creativecommons.org/publicdomain/zero/1.0/>. --> | ||
| 12 | |||
| 13 | <entity-facade-xml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 14 | xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/entity-facade-3.xsd"> | ||
| 15 | |||
| 16 | <!-- MCP User Group --> | ||
| 17 | <moqui.security.UserGroup userGroupId="McpUser" description="MCP Server Users"/> | ||
| 18 | |||
| 19 | <!-- MCP Artifact Groups --> | ||
| 20 | <moqui.security.ArtifactGroup artifactGroupId="McpRestPaths" description="MCP REST Paths"/> | ||
| 21 | <moqui.security.ArtifactGroup artifactGroupId="McpServices" description="MCP Services"/> | ||
| 22 | |||
| 23 | <!-- MCP Artifact Group Members --> | ||
| 24 | <moqui.security.ArtifactGroupMember artifactGroupId="McpRestPaths" artifactName="/rest/s1/mcp-2" artifactTypeEnumId="AT_REST_PATH"/> | ||
| 25 | <moqui.security.ArtifactGroupMember artifactGroupId="McpRestPaths" artifactName="/rest/s1/mcp-2/*" artifactTypeEnumId="AT_REST_PATH"/> | ||
| 26 | <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="org.moqui.mcp.*" artifactTypeEnumId="AT_SERVICE"/> | ||
| 27 | |||
| 28 | <!-- MCP Artifact Authz --> | ||
| 29 | <moqui.security.ArtifactAuthz userGroupId="McpUser" artifactGroupId="McpRestPaths" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | ||
| 30 | <moqui.security.ArtifactAuthz userGroupId="McpUser" artifactGroupId="McpServices" authzTypeEnumId="AUTHZT_ALLOW" authzActionEnumId="AUTHZA_ALL"/> | ||
| 31 | |||
| 32 | <!-- Add john.doe to MCP user group --> | ||
| 33 | <moqui.security.UserGroupMember userGroupId="McpUser" userId="JOHN_DOE" fromDate="2025-01-01 00:00:00.000"/> | ||
| 34 | |||
| 35 | <!-- Add MCP user to MCP user group --> | ||
| 36 | <moqui.security.UserGroupMember userGroupId="McpUser" userId="MCP_USER" fromDate="2025-01-01 00:00:00.000"/> | ||
| 37 | |||
| 38 | <!-- Create minimal MCP user for testing --> | ||
| 39 | <moqui.security.UserAccount userId="MCP_USER" username="mcp-user" disabled="N" requirePasswordChange="N" | ||
| 40 | currentPassword="16ac58bbfa332c1c55bd98b53e60720bfa90d394" passwordHashType="SHA" /> | ||
| 41 | |||
| 42 | </entity-facade-xml> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
entity/McpCoreEntities.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- This software is in the public domain under CC0 1.0 Universal plus a | ||
| 3 | Grant of Patent License. | ||
| 4 | |||
| 5 | To the extent possible under law, the author(s) have dedicated all | ||
| 6 | copyright and related and neighboring rights to this software to the | ||
| 7 | public domain worldwide. This software is distributed without any warranty. | ||
| 8 | |||
| 9 | You should have received a copy of the CC0 Public Domain Dedication | ||
| 10 | along with this software (see the LICENSE.md file). If not, see | ||
| 11 | <https://creativecommons.org/publicdomain/zero/1.0/>. --> | ||
| 12 | |||
| 13 | <entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 14 | xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/entity-definition-3.xsd"> | ||
| 15 | |||
| 16 | <!-- Extend Visit entity for MCP-specific session tracking --> | ||
| 17 | <extend-entity entity-name="Visit" package="moqui.server"> | ||
| 18 | <field name="mcpContextToken" type="text-medium"/> | ||
| 19 | <field name="mcpStatusId" type="id"/> | ||
| 20 | <field name="mcpExpiresDate" type="date-time"/> | ||
| 21 | <field name="mcpClientInfo" type="text-long"/> | ||
| 22 | <field name="mcpSessionData" type="text-very-long"/> | ||
| 23 | |||
| 24 | <relationship type="one" related-entity-name="moqui.basic.StatusItem" short-alias="mcpStatus"> | ||
| 25 | <key-map field-name="mcpStatusId"/> | ||
| 26 | </relationship> | ||
| 27 | |||
| 28 | <index name="VISIT_MCP_TOKEN" unique="true"> | ||
| 29 | <index-field name="mcpContextToken"/> | ||
| 30 | </index> | ||
| 31 | <index name="VISIT_MCP_USER_STATUS"> | ||
| 32 | <index-field name="userId"/> | ||
| 33 | <index-field name="mcpStatusId"/> | ||
| 34 | </index> | ||
| 35 | <index name="VISIT_MCP_EXPIRE"> | ||
| 36 | <index-field name="mcpExpiresDate"/> | ||
| 37 | </index> | ||
| 38 | </extend-entity> | ||
| 39 | |||
| 40 | |||
| 41 | |||
| 42 | |||
| 43 | |||
| 44 | </entities> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
screen/webapp.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- This software is in the public domain under CC0 1.0 Universal plus a | ||
| 3 | Grant of Patent License. | ||
| 4 | |||
| 5 | To the extent possible under law, the author(s) have dedicated all | ||
| 6 | copyright and related and neighboring rights to this software to the | ||
| 7 | public domain worldwide. This software is distributed without any warranty. | ||
| 8 | |||
| 9 | You should have received a copy of the CC0 Public Domain Dedication | ||
| 10 | along with this software (see the LICENSE.md file). If not, see | ||
| 11 | <https://creativecommons.org/publicdomain/zero/1.0/>. --> | ||
| 12 | |||
| 13 | <screen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 14 | xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/screen-definition-3.xsd"> | ||
| 15 | |||
| 16 | <parameter name="sessionId"/> | ||
| 17 | <parameter name="contextToken"/> | ||
| 18 | |||
| 19 | <widgets> | ||
| 20 | <render-mode> | ||
| 21 | <text type="html" location="component://moqui-mcp-2/template/McpInfo.html.ftl"/> | ||
| 22 | </render-mode> | ||
| 23 | </widgets> | ||
| 24 | </screen> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
service/McpDiscoveryServices.xml
0 → 100644
This diff is collapsed.
Click to expand it.
service/McpExecutionServices.xml
0 → 100644
This diff is collapsed.
Click to expand it.
service/McpSecurityServices.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- This software is in the public domain under CC0 1.0 Universal plus a | ||
| 3 | Grant of Patent License. | ||
| 4 | |||
| 5 | To the extent possible under law, the author(s) have dedicated all | ||
| 6 | copyright and related and neighboring rights to this software to the | ||
| 7 | public domain worldwide. This software is distributed without any warranty. | ||
| 8 | |||
| 9 | You should have received a copy of the CC0 Public Domain Dedication | ||
| 10 | along with this software (see the LICENSE.md file). If not, see | ||
| 11 | <https://creativecommons.org/publicdomain/zero/1.0/>. --> | ||
| 12 | |||
| 13 | <services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 14 | xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-definition-3.xsd"> | ||
| 15 | |||
| 16 | <!-- MCP Unified Security and Audit Services --> | ||
| 17 | |||
| 18 | <service verb="create" noun="McpSession" authenticate="false" transaction-timeout="30"> | ||
| 19 | <description>Create a new MCP session using Moqui's Visit entity</description> | ||
| 20 | <in-parameters> | ||
| 21 | <parameter name="username" type="text-medium" required="true"/> | ||
| 22 | <parameter name="password" type="text-medium" required="true"/> | ||
| 23 | <parameter name="clientInfo" type="text-long"/> | ||
| 24 | <parameter name="ipAddress" type="text-short"/> | ||
| 25 | <parameter name="userAgent" type="text-medium"/> | ||
| 26 | </in-parameters> | ||
| 27 | <out-parameters> | ||
| 28 | <parameter name="visitId" type="id"/> | ||
| 29 | <parameter name="contextToken" type="text-medium"/> | ||
| 30 | <parameter name="userAccountId" type="id"/> | ||
| 31 | <parameter name="success" type="text-indicator"/> | ||
| 32 | <parameter name="errorMessage" type="text-long"/> | ||
| 33 | </out-parameters> | ||
| 34 | <actions> | ||
| 35 | <script><![CDATA[ | ||
| 36 | import org.moqui.context.ExecutionContext | ||
| 37 | import java.util.UUID | ||
| 38 | |||
| 39 | ExecutionContext ec = context.ec | ||
| 40 | |||
| 41 | // Authenticate user | ||
| 42 | def userAccount = ec.entity.find("moqui.security.UserAccount") | ||
| 43 | .condition("username", username) | ||
| 44 | .one() | ||
| 45 | |||
| 46 | if (!userAccount) { | ||
| 47 | success = "N" | ||
| 48 | errorMessage = "Invalid username or password" | ||
| 49 | return | ||
| 50 | } | ||
| 51 | |||
| 52 | // Verify password using Moqui's password service | ||
| 53 | try { | ||
| 54 | def authResult = ec.service.sync("org.moqui.security.UserServices.authenticate#User", [ | ||
| 55 | username: username, | ||
| 56 | password: password | ||
| 57 | ]) | ||
| 58 | |||
| 59 | if (!authResult.authenticated) { | ||
| 60 | success = "N" | ||
| 61 | errorMessage = "Invalid username or password" | ||
| 62 | return | ||
| 63 | } | ||
| 64 | } catch (Exception e) { | ||
| 65 | success = "N" | ||
| 66 | errorMessage = "Authentication error: ${e.message}" | ||
| 67 | return | ||
| 68 | } | ||
| 69 | |||
| 70 | // Create Visit record (Moqui's native session tracking) | ||
| 71 | def visit = ec.entity.makeValue("moqui.server.Visit") | ||
| 72 | visit.setSequencedIdPrimary() | ||
| 73 | visit.userId = userAccount.userAccountId | ||
| 74 | visit.userCreated = "N" | ||
| 75 | visit.sessionId = UUID.randomUUID().toString() | ||
| 76 | visit.webappName = "mcp-2" | ||
| 77 | visit.initialLocale = ec.user.locale.toString() | ||
| 78 | visit.initialRequest = "MCP Session Creation" | ||
| 79 | visit.initialUserAgent = userAgent | ||
| 80 | visit.clientIpAddress = ipAddress | ||
| 81 | visit.fromDate = ec.user.now | ||
| 82 | |||
| 83 | // Add MCP-specific fields | ||
| 84 | visit.mcpContextToken = UUID.randomUUID().toString() | ||
| 85 | visit.mcpStatusId = "McsActive" | ||
| 86 | visit.mcpExpiresDate = new Date(ec.user.now.time + (24 * 60 * 60 * 1000)) // 24 hours | ||
| 87 | visit.mcpClientInfo = clientInfo | ||
| 88 | |||
| 89 | visit.create() | ||
| 90 | |||
| 91 | visitId = visit.visitId | ||
| 92 | contextToken = visit.mcpContextToken | ||
| 93 | userAccountId = userAccount.userAccountId | ||
| 94 | success = "Y" | ||
| 95 | |||
| 96 | // Log session creation via ArtifactHit | ||
| 97 | ec.message.addMessage("MCP session created for user ${username}", "info") | ||
| 98 | ]]></script> | ||
| 99 | </actions> | ||
| 100 | </service> | ||
| 101 | |||
| 102 | <service verb="validate" noun="McpSession" authenticate="false" transaction-timeout="30"> | ||
| 103 | <description>Validate MCP session using Visit entity</description> | ||
| 104 | <in-parameters> | ||
| 105 | <parameter name="visitId" type="id" required="true"/> | ||
| 106 | <parameter name="contextToken" type="text-medium" required="true"/> | ||
| 107 | </in-parameters> | ||
| 108 | <out-parameters> | ||
| 109 | <parameter name="valid" type="text-indicator"/> | ||
| 110 | <parameter name="userAccountId" type="id"/> | ||
| 111 | <parameter name="errorMessage" type="text-long"/> | ||
| 112 | </out-parameters> | ||
| 113 | <actions> | ||
| 114 | <script><![CDATA[ | ||
| 115 | import org.moqui.context.ExecutionContext | ||
| 116 | |||
| 117 | ExecutionContext ec = context.ec | ||
| 118 | |||
| 119 | def visit = ec.entity.find("moqui.server.Visit") | ||
| 120 | .condition("visitId", visitId) | ||
| 121 | .condition("mcpContextToken", contextToken) | ||
| 122 | .condition("mcpStatusId", "McsActive") | ||
| 123 | .one() | ||
| 124 | |||
| 125 | if (!visit) { | ||
| 126 | valid = "N" | ||
| 127 | errorMessage = "Invalid or expired session" | ||
| 128 | return | ||
| 129 | } | ||
| 130 | |||
| 131 | // Check if session has expired | ||
| 132 | if (visit.mcpExpiresDate && visit.mcpExpiresDate.before(ec.user.now)) { | ||
| 133 | // Mark as expired | ||
| 134 | visit.mcpStatusId = "McsExpired" | ||
| 135 | visit.thruDate = ec.user.now | ||
| 136 | visit.update() | ||
| 137 | |||
| 138 | valid = "N" | ||
| 139 | errorMessage = "Session expired" | ||
| 140 | return | ||
| 141 | } | ||
| 142 | |||
| 143 | // Update Visit thruDate (acts as last accessed) | ||
| 144 | visit.thruDate = ec.user.now | ||
| 145 | visit.update() | ||
| 146 | |||
| 147 | valid = "Y" | ||
| 148 | userAccountId = visit.userId | ||
| 149 | ]]></script> | ||
| 150 | </actions> | ||
| 151 | </service> | ||
| 152 | |||
| 153 | <service verb="terminate" noun="McpSession" authenticate="false" transaction-timeout="30"> | ||
| 154 | <description>Terminate MCP session using Visit entity</description> | ||
| 155 | <in-parameters> | ||
| 156 | <parameter name="visitId" type="id" required="true"/> | ||
| 157 | <parameter name="contextToken" type="text-medium" required="true"/> | ||
| 158 | </in-parameters> | ||
| 159 | <out-parameters> | ||
| 160 | <parameter name="success" type="text-indicator"/> | ||
| 161 | <parameter name="errorMessage" type="text-long"/> | ||
| 162 | </out-parameters> | ||
| 163 | <actions> | ||
| 164 | <script><![CDATA[ | ||
| 165 | import org.moqui.context.ExecutionContext | ||
| 166 | |||
| 167 | ExecutionContext ec = context.ec | ||
| 168 | |||
| 169 | def visit = ec.entity.find("moqui.server.Visit") | ||
| 170 | .condition("visitId", visitId) | ||
| 171 | .condition("mcpContextToken", contextToken) | ||
| 172 | .one() | ||
| 173 | |||
| 174 | if (!visit) { | ||
| 175 | success = "N" | ||
| 176 | errorMessage = "Invalid session" | ||
| 177 | return | ||
| 178 | } | ||
| 179 | |||
| 180 | // Mark as terminated | ||
| 181 | visit.mcpStatusId = "McsTerminated" | ||
| 182 | visit.thruDate = ec.user.now | ||
| 183 | visit.update() | ||
| 184 | |||
| 185 | success = "Y" | ||
| 186 | |||
| 187 | // Log session termination via ArtifactHit | ||
| 188 | ec.message.addMessage("MCP session terminated", "info") | ||
| 189 | ]]></script> | ||
| 190 | </actions> | ||
| 191 | </service> | ||
| 192 | |||
| 193 | |||
| 194 | |||
| 195 | <service verb="cleanup" noun="ExpiredSessions" authenticate="true" transaction-timeout="300"> | ||
| 196 | <description>Cleanup expired MCP sessions</description> | ||
| 197 | <in-parameters> | ||
| 198 | <parameter name="dryRun" type="text-indicator" default="N"/> | ||
| 199 | </in-parameters> | ||
| 200 | <out-parameters> | ||
| 201 | <parameter name="expiredCount" type="number-integer"/> | ||
| 202 | <parameter name="cleanedCount" type="number-integer"/> | ||
| 203 | </out-parameters> | ||
| 204 | <actions> | ||
| 205 | <script><![CDATA[ | ||
| 206 | import org.moqui.context.ExecutionContext | ||
| 207 | |||
| 208 | ExecutionContext ec = context.ec | ||
| 209 | |||
| 210 | // Find expired MCP sessions | ||
| 211 | def expiredVisits = ec.entity.find("moqui.server.Visit") | ||
| 212 | .condition("mcpStatusId", "McsActive") | ||
| 213 | .condition("mcpExpiresDate", ec.user.now, "less-than") | ||
| 214 | .list() | ||
| 215 | |||
| 216 | expiredCount = expiredVisits.size() | ||
| 217 | cleanedCount = 0 | ||
| 218 | |||
| 219 | if (dryRun != "Y") { | ||
| 220 | for (visit in expiredVisits) { | ||
| 221 | visit.mcpStatusId = "McsExpired" | ||
| 222 | visit.thruDate = ec.user.now | ||
| 223 | visit.update() | ||
| 224 | cleanedCount++ | ||
| 225 | |||
| 226 | // Log session expiration via ArtifactHit | ||
| 227 | ec.message.addMessage("MCP session expired during cleanup", "info") | ||
| 228 | } | ||
| 229 | } | ||
| 230 | ]]></script> | ||
| 231 | </actions> | ||
| 232 | </service> | ||
| 233 | |||
| 234 | </services> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
service/mcp.rest.xml
0 → 100644
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <!-- This software is in the public domain under CC0 1.0 Universal plus a | ||
| 3 | Grant of Patent License. | ||
| 4 | |||
| 5 | To the extent possible under law, the author(s) have dedicated all | ||
| 6 | copyright and related and neighboring rights to this software to the | ||
| 7 | public domain worldwide. This software is distributed without any warranty. | ||
| 8 | |||
| 9 | You should have received a copy of the CC0 Public Domain Dedication | ||
| 10 | along with this software (see the LICENSE.md file). If not, see | ||
| 11 | <https://creativecommons.org/publicdomain/zero/1.0/>. --> | ||
| 12 | |||
| 13 | <resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 14 | xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/rest-api-3.xsd" | ||
| 15 | name="mcp-2" displayName="MCP v2 REST API" version="2.0.0" | ||
| 16 | description="Model Context Protocol (MCP) v2 server endpoints for AI-Moqui integration"> | ||
| 17 | |||
| 18 | <!-- MCP Session Management --> | ||
| 19 | |||
| 20 | <resource name="session" description="MCP Session Management"> | ||
| 21 | <method type="post" json-request="true"> | ||
| 22 | <description>Create new MCP session</description> | ||
| 23 | <service name="org.moqui.mcp.McpSecurityServices.create#McpSession"/> | ||
| 24 | <parameter-map> | ||
| 25 | <parameter field-name="username" required="true"/> | ||
| 26 | <parameter field-name="password" required="true"/> | ||
| 27 | <parameter field-name="clientInfo"/> | ||
| 28 | <parameter field-name="ipAddress"/> | ||
| 29 | <parameter field-name="userAgent"/> | ||
| 30 | </parameter-map> | ||
| 31 | </method> | ||
| 32 | |||
| 33 | <id name="visitId"> | ||
| 34 | <method type="post" json-request="true"> | ||
| 35 | <description>Validate MCP session</description> | ||
| 36 | <service name="org.moqui.mcp.McpSecurityServices.validate#McpSession"/> | ||
| 37 | <parameter-map> | ||
| 38 | <parameter field-name="visitId" from="uri-path"/> | ||
| 39 | <parameter field-name="contextToken" required="true"/> | ||
| 40 | </parameter-map> | ||
| 41 | </method> | ||
| 42 | |||
| 43 | <resource name="terminate"> | ||
| 44 | <method type="post" json-request="true"> | ||
| 45 | <description>Terminate MCP session</description> | ||
| 46 | <service name="org.moqui.mcp.McpSecurityServices.terminate#McpSession"/> | ||
| 47 | <parameter-map> | ||
| 48 | <parameter field-name="visitId" from="uri-path"/> | ||
| 49 | <parameter field-name="contextToken" required="true"/> | ||
| 50 | </parameter-map> | ||
| 51 | </method> | ||
| 52 | </resource> | ||
| 53 | </id> | ||
| 54 | </resource> | ||
| 55 | |||
| 56 | <!-- MCP Tool Discovery --> | ||
| 57 | |||
| 58 | <resource name="tools" description="MCP Tool Discovery and Execution"> | ||
| 59 | <resource name="discover"> | ||
| 60 | <method type="post" json-request="true"> | ||
| 61 | <description>Discover available MCP tools</description> | ||
| 62 | <service name="org.moqui.mcp.McpDiscoveryServices.discover#McpTools"/> | ||
| 63 | <parameter-map> | ||
| 64 | <parameter field-name="userAccountId" required="true"/> | ||
| 65 | <parameter field-name="visitId"/> | ||
| 66 | <parameter field-name="servicePattern"/> | ||
| 67 | <parameter field-name="packageName"/> | ||
| 68 | <parameter field-name="verb"/> | ||
| 69 | <parameter field-name="noun"/> | ||
| 70 | <parameter field-name="includeParameters"/> | ||
| 71 | </parameter-map> | ||
| 72 | </method> | ||
| 73 | </resource> | ||
| 74 | |||
| 75 | <resource name="validate"> | ||
| 76 | <method type="post" json-request="true"> | ||
| 77 | <description>Validate access to specific MCP tools</description> | ||
| 78 | <service name="org.moqui.mcp.McpDiscoveryServices.validate#McpToolAccess"/> | ||
| 79 | <parameter-map> | ||
| 80 | <parameter field-name="userAccountId" required="true"/> | ||
| 81 | <parameter field-name="serviceNames" required="true"/> | ||
| 82 | <parameter field-name="visitId"/> | ||
| 83 | </parameter-map> | ||
| 84 | </method> | ||
| 85 | </resource> | ||
| 86 | |||
| 87 | <method type="post" json-request="true"> | ||
| 88 | <description>Execute MCP tool (service)</description> | ||
| 89 | <service name="org.moqui.mcp.McpExecutionServices.execute#McpTool"/> | ||
| 90 | <parameter-map> | ||
| 91 | <parameter field-name="sessionId" required="true"/> | ||
| 92 | <parameter field-name="contextToken" required="true"/> | ||
| 93 | <parameter field-name="serviceName" required="true"/> | ||
| 94 | <parameter field-name="parameters" required="true"/> | ||
| 95 | <parameter field-name="toolCallId"/> | ||
| 96 | </parameter-map> | ||
| 97 | </method> | ||
| 98 | |||
| 99 | <resource name="entity"> | ||
| 100 | <resource name="query"> | ||
| 101 | <method type="post" json-request="true"> | ||
| 102 | <description>Execute entity query as MCP tool</description> | ||
| 103 | <service name="org.moqui.mcp.McpExecutionServices.execute#EntityQuery"/> | ||
| 104 | <parameter-map> | ||
| 105 | <parameter field-name="sessionId" required="true"/> | ||
| 106 | <parameter field-name="contextToken" required="true"/> | ||
| 107 | <parameter field-name="serviceName" required="true"/> | ||
| 108 | <parameter field-name="parameters" required="true"/> | ||
| 109 | <parameter field-name="toolCallId"/> | ||
| 110 | </parameter-map> | ||
| 111 | </method> | ||
| 112 | </resource> | ||
| 113 | </resource> | ||
| 114 | </resource> | ||
| 115 | |||
| 116 | <!-- MCP Health Check --> | ||
| 117 | |||
| 118 | <resource name="health" description="MCP Server Health Check"> | ||
| 119 | <method type="get"> | ||
| 120 | <actions> | ||
| 121 | <script><![CDATA[ | ||
| 122 | import groovy.json.JsonBuilder | ||
| 123 | |||
| 124 | def health = [ | ||
| 125 | status: "healthy", | ||
| 126 | timestamp: ec.user.now, | ||
| 127 | version: "2.0.0-MVP", | ||
| 128 | server: "Moqui MCP Server v2", | ||
| 129 | endpoints: [ | ||
| 130 | "POST /session - Create session", | ||
| 131 | "POST /session/{visitId}/validate - Validate session", | ||
| 132 | "POST /session/{visitId}/terminate - Terminate session", | ||
| 133 | "POST /tools/discover - Discover tools", | ||
| 134 | "POST /tools/validate - Validate tool access", | ||
| 135 | "POST /tools/execute - Execute service tool", | ||
| 136 | "POST /tools/entity/query - Execute entity query", | ||
| 137 | "GET /health - Health check" | ||
| 138 | ] | ||
| 139 | ] | ||
| 140 | |||
| 141 | response = new JsonBuilder(health).toString() | ||
| 142 | ]]></script> | ||
| 143 | </actions> | ||
| 144 | </method> | ||
| 145 | </resource> | ||
| 146 | |||
| 147 | </resource> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
template/McpInfo.html.ftl
0 → 100644
| 1 | <!DOCTYPE html> | ||
| 2 | <html> | ||
| 3 | <head> | ||
| 4 | <title>Moqui MCP Server v2</title> | ||
| 5 | <style> | ||
| 6 | body { font-family: Arial, sans-serif; margin: 40px; } | ||
| 7 | .header { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; } | ||
| 8 | .endpoint { background: #f8f9fa; padding: 10px; margin: 5px 0; border-radius: 5px; } | ||
| 9 | .method { color: #28a745; font-weight: bold; } | ||
| 10 | .path { font-family: monospace; background: #e9ecef; padding: 2px 5px; } | ||
| 11 | </style> | ||
| 12 | </head> | ||
| 13 | <body> | ||
| 14 | <h1 class="header">Moqui MCP Server v2 (MVP)</h1> | ||
| 15 | |||
| 16 | <h2>Server Information</h2> | ||
| 17 | <p><strong>Version:</strong> 2.0.0-MVP</p> | ||
| 18 | <p><strong>Status:</strong> Active</p> | ||
| 19 | <p><strong>Description:</strong> Simplified MCP server using Moqui's native service engine</p> | ||
| 20 | |||
| 21 | <h2>Available Endpoints</h2> | ||
| 22 | |||
| 23 | <div class="endpoint"> | ||
| 24 | <span class="method">POST</span> <span class="path">/session</span><br> | ||
| 25 | <strong>Description:</strong> Create new MCP session<br> | ||
| 26 | <strong>Required:</strong> username, password<br> | ||
| 27 | <strong>Optional:</strong> clientInfo, ipAddress, userAgent | ||
| 28 | </div> | ||
| 29 | |||
| 30 | <div class="endpoint"> | ||
| 31 | <span class="method">POST</span> <span class="path">/session/{sessionId}/validate</span><br> | ||
| 32 | <strong>Description:</strong> Validate MCP session<br> | ||
| 33 | <strong>Required:</strong> contextToken | ||
| 34 | </div> | ||
| 35 | |||
| 36 | <div class="endpoint"> | ||
| 37 | <span class="method">POST</span> <span class="path">/session/{sessionId}/terminate</span><br> | ||
| 38 | <strong>Description:</strong> Terminate MCP session<br> | ||
| 39 | <strong>Required:</strong> contextToken | ||
| 40 | </div> | ||
| 41 | |||
| 42 | <div class="endpoint"> | ||
| 43 | <span class="method">POST</span> <span class="path">/tools/discover</span><br> | ||
| 44 | <strong>Description:</strong> Discover available MCP tools<br> | ||
| 45 | <strong>Required:</strong> userAccountId<br> | ||
| 46 | <strong>Optional:</strong> sessionId, servicePattern, packageName, verb, noun, includeParameters | ||
| 47 | </div> | ||
| 48 | |||
| 49 | <div class="endpoint"> | ||
| 50 | <span class="method">POST</span> <span class="path">/tools/validate</span><br> | ||
| 51 | <strong>Description:</strong> Validate access to specific MCP tools<br> | ||
| 52 | <strong>Required:</strong> userAccountId, serviceNames<br> | ||
| 53 | <strong>Optional:</strong> sessionId | ||
| 54 | </div> | ||
| 55 | |||
| 56 | <div class="endpoint"> | ||
| 57 | <span class="method">POST</span> <span class="path">/tools/execute</span><br> | ||
| 58 | <strong>Description:</strong> Execute MCP tool (service)<br> | ||
| 59 | <strong>Required:</strong> sessionId, serviceName, parameters<br> | ||
| 60 | <strong>Optional:</strong> toolCallId | ||
| 61 | </div> | ||
| 62 | |||
| 63 | <div class="endpoint"> | ||
| 64 | <span class="method">POST</span> <span class="path">/tools/entity/query</span><br> | ||
| 65 | <strong>Description:</strong> Execute entity query as MCP tool<br> | ||
| 66 | <strong>Required:</strong> sessionId, entityName<br> | ||
| 67 | <strong>Optional:</strong> queryType, conditions, orderBy, limit, offset, toolCallId | ||
| 68 | </div> | ||
| 69 | |||
| 70 | <div class="endpoint"> | ||
| 71 | <span class="method">GET</span> <span class="path">/health</span><br> | ||
| 72 | <strong>Description:</strong> Server health check | ||
| 73 | </div> | ||
| 74 | |||
| 75 | <h2>Key Features</h2> | ||
| 76 | <ul> | ||
| 77 | <li><strong>Native Service Engine:</strong> Uses Moqui's service engine directly (no registry)</li> | ||
| 78 | <li><strong>Dynamic Tool Discovery:</strong> Tools = services user has permission to execute</li> | ||
| 79 | <li><strong>Unified Security:</strong> Single permission validation using UserGroupPermission</li> | ||
| 80 | <li><strong>Audit Logging:</strong> Complete audit trail of all operations</li> | ||
| 81 | <li><strong>Session Management:</strong> Secure session handling with expiration</li> | ||
| 82 | </ul> | ||
| 83 | |||
| 84 | <h2>Usage Example</h2> | ||
| 85 | <pre> | ||
| 86 | # 1. Create session | ||
| 87 | curl -X POST http://localhost:8080/mcp-2/session \ | ||
| 88 | -H "Content-Type: application/json" \ | ||
| 89 | -d '{"username": "admin", "password": "admin"}' | ||
| 90 | |||
| 91 | # 2. Discover tools | ||
| 92 | curl -X POST http://localhost:8080/mcp-2/tools/discover \ | ||
| 93 | -H "Content-Type: application/json" \ | ||
| 94 | -d '{"userAccountId": "ADMIN", "sessionId": "YOUR_SESSION_ID"}' | ||
| 95 | |||
| 96 | # 3. Execute tool | ||
| 97 | curl -X POST http://localhost:8080/mcp-2/tools/execute \ | ||
| 98 | -H "Content-Type: application/json" \ | ||
| 99 | -d '{ | ||
| 100 | "sessionId": "YOUR_SESSION_ID", | ||
| 101 | "serviceName": "org.moqui.entity.EntityServices.find#Entity", | ||
| 102 | "parameters": {"entityName": "moqui.security.UserAccount"} | ||
| 103 | }' | ||
| 104 | </pre> | ||
| 105 | </body> | ||
| 106 | </html> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or sign in to post a comment