891 lines
31 KiB
Markdown
891 lines
31 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
**Goal:** Create an MCP (Model Context Protocol) server that allows Claude to interact with a WMS (Warehouse Management System) for debugging and analysis purposes.
|
|
|
|
**Current Status:** ✅ Implementation complete and functional - API-only architecture
|
|
|
|
**Implementation Progress:**
|
|
- ✅ Project planning completed
|
|
- ✅ CLAUDE.md documentation created
|
|
- ✅ Node.js and npm installed on system
|
|
- ✅ Architecture finalized (100% API, no direct Oracle access)
|
|
- ✅ Services and tools implemented
|
|
- ✅ API connectivity tested and validated
|
|
- ✅ OAuth authentication working (with tenant_code fix)
|
|
- ✅ Workflow fetching operational (entities extraction fix)
|
|
- ✅ Application Dictionary API implemented (20 element types, 38,765 elements)
|
|
- ✅ AD API endpoints validated with curl (17/19 working, 2 removed)
|
|
- ✅ Claude Desktop integration successful
|
|
|
|
**Required Information:**
|
|
- 🌐 **WMS API Configuration:**
|
|
- Server IP address: `10.255.255.2`
|
|
- Tenant code (configurable in .env)
|
|
- Username and password for API access
|
|
- 📁 **File Paths:**
|
|
- Log files directory path (e.g., `C:\WMS\Logs`)
|
|
- Deployment directory on VM (e.g., `C:\WMS\mcp`)
|
|
|
|
**Target Architecture:**
|
|
- **MCP Server:** Node.js executable running on Windows Server VM
|
|
- **Data Access:** 100% via WMS REST APIs (no direct Oracle connection)
|
|
- **Query API:** `/ApplicationService/api/QueryExecute` (LINQ queries)
|
|
- **Command API:** `/ApplicationService/api/CommandExecute` (WMS commands)
|
|
- **Workflow API:** `/AD/api/Workflow/GetByApplication` (workflows with pagination)
|
|
- **Application Dictionary API:** `/AD/api/{ElementType}/GetByApplication` (20 element types: Commands, Queries, Dialogs, Views, Entities, Events, etc.)
|
|
- **Authentication:** OAuth 2.0 with automatic token refresh
|
|
- **Connection:** Claude Desktop (PC) → Local or SSH → MCP Server
|
|
|
|
## Quick Start
|
|
|
|
### 1. Install Dependencies
|
|
|
|
```bash
|
|
npm install
|
|
```
|
|
|
|
### 2. Configure Environment
|
|
|
|
Copy `.env.example` to `.env` and configure. The server supports **multiple WMS profiles** (AD, LIMAGRAIN, etc.) and Claude switches between them at runtime:
|
|
|
|
```env
|
|
# Shared settings (same for all profiles)
|
|
WMS_API_AUTH=Basic R05BOklFNGU3aXFoZHQ=
|
|
WMS_APPLICATION=EasyWMS
|
|
WMS_API_PATH=/ApplicationService/api
|
|
WMS_TOKEN_PATH=/EasySTS/OAuth/Token
|
|
WORKFLOW_API_PATH=/AD/api
|
|
|
|
# Profile registry
|
|
WMS_PROFILES=AD,LIMAGRAIN
|
|
DEFAULT_WMS_PROFILE=AD
|
|
|
|
# Profile: AD (on-premise — logs accessible)
|
|
AD_HOST=10.255.255.2
|
|
AD_USERNAME=your-ad-username
|
|
AD_PASSWORD=your-ad-password
|
|
AD_TENANT=AD
|
|
AD_SAAS=false
|
|
|
|
# Profile: LIMAGRAIN (on-premise — logs accessible)
|
|
LIMAGRAIN_HOST=10.255.255.2
|
|
LIMAGRAIN_USERNAME=your-limagrain-username
|
|
LIMAGRAIN_PASSWORD=your-limagrain-password
|
|
LIMAGRAIN_TENANT=LIMAGRAI2512
|
|
LIMAGRAIN_SAAS=false
|
|
|
|
# Logs — {host} is substituted with the active profile's HOST
|
|
LOGS_PATH=\\{host}\inetpub\logs\LogFiles\Mecalux;\\{host}\ProgramData\Mecalux\ETLLogs
|
|
WORKFLOW_PAGE_SIZE=5000
|
|
```
|
|
|
|
**Adding a new profile:**
|
|
1. Add its name to `WMS_PROFILES` (comma-separated).
|
|
2. Define `<NAME>_HOST`, `<NAME>_USERNAME`, `<NAME>_PASSWORD`, `<NAME>_TENANT`.
|
|
3. The full URLs are built as `https://<HOST><WMS_API_PATH>` etc. — no need to repeat URLs per profile.
|
|
|
|
**Switching profiles at runtime (in Claude Desktop):**
|
|
- "Quels WMS sont configurés ?" → calls `list_wms_profiles`
|
|
- "Connecte-toi au WMS LIMAGRAIN" → calls `switch_wms_profile` with `profile: "LIMAGRAIN"`
|
|
- The OAuth token is reset and workflow/AD caches cleared automatically on switch.
|
|
|
|
### 3. Configure Claude Desktop
|
|
|
|
Edit `%APPDATA%\Claude\claude_desktop_config.json`:
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"wms": {
|
|
"command": "node",
|
|
"args": [
|
|
"c:\\path\\to\\wms-mcp-server\\src\\index.js"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Restart Claude Desktop
|
|
|
|
Close and reopen Claude Desktop completely. The MCP server will start automatically.
|
|
|
|
### 5. Test
|
|
|
|
In Claude Desktop, try:
|
|
- **Workflows:** "Search for workflows containing 'Order'"
|
|
- **WMS Queries:** "Query the last 10 products"
|
|
- **AD Elements:** "Get all Query elements" or "Search Commands containing 'Product'"
|
|
- **Logs:** "Show me recent log entries"
|
|
- **Summary:** "Get application summary" (shows cached AD element counts)
|
|
|
|
## Existing Files
|
|
|
|
- [CLAUDE.md](CLAUDE.md) - This file (project guidance and implementation plan)
|
|
- [queries api.php](queries api.php) - Reference PHP file showing WMS API interaction patterns
|
|
|
|
### Understanding the WMS API from queries api.php
|
|
|
|
The PHP file demonstrates the EasyWMS API structure:
|
|
|
|
**Authentication:**
|
|
- OAuth 2.0 token-based authentication
|
|
- Endpoint: `https://{ip}/EasySTS/OAuth/Token`
|
|
- Supports both password and refresh_token grant types
|
|
- Token refresh required after ~1000 seconds (hardcoded in PHP)
|
|
|
|
**Query API:**
|
|
- Endpoint: `https://{ip}/ApplicationService/api/QueryExecute`
|
|
- Uses LINQ-like expression syntax: `Context.{Type}.Select(z => z.Id)`
|
|
- Supported entity types: Containers, Stocks, ProductLocations, Tasks, Products, Accounts, Suppliers, Kits, Aliases, InboundOrders, Receptions, OutboundOrders
|
|
|
|
**Command API:**
|
|
- Endpoint: `https://{ip}/ApplicationService/api/CommandExecute`
|
|
- Command structure includes fully qualified .NET class names
|
|
- Example: `Mecalux.ITSW.EasyWMS.Modules.MasterData.Contracts.Commands.ProductRemoveCommand`
|
|
|
|
**Workflow API:**
|
|
- Endpoint: `POST https://{ip}/AD/api/Workflow/GetByApplication`
|
|
- Request body: `["EasyWMS", "AD", 5000, 0]` (application, tenant, pageSize, offset)
|
|
- Returns workflow definitions with pagination
|
|
- Total workflows: ~3712 (can be fetched with higher page size to reduce API calls)
|
|
- **Lazy loading:** Workflows are only fetched when MCP receives a workflow-related request
|
|
|
|
**Application Dictionary API:**
|
|
- Endpoint pattern: `POST https://{ip}/AD/api/{ElementType}/GetByApplication`
|
|
- Request body: `["EasyWMS", "AD", pageSize, offset]`
|
|
- Response structure: `{entities: [...]}`
|
|
- **Supported element types (20):** Command, Query, Dialog, View, Entity, Event, FieldType, Hook, List, Record, Relationship, Report, Resource, Subscription, TimelineTemplate, Toggle, Validator, ViewGroup, Workflow, Dashboard
|
|
- **Total elements:** ~38,765 across all types
|
|
- **Validated with curl:** 17/19 types working (WorkflowAction and WritingModel return 404)
|
|
- **Lazy loading + caching:** Each element type loaded on first request, cached for 1 hour
|
|
- **Test results:** See [AD_API_TEST_RESULTS.md](AD_API_TEST_RESULTS.md) for detailed validation
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
wms-mcp-server/
|
|
├── src/
|
|
│ ├── index.js # Main MCP server entry point
|
|
│ ├── resources/ # MCP resources (read-only data)
|
|
│ │ ├── wms-entities.js # WMS entities catalog
|
|
│ │ ├── entity-schemas.js # Entity schemas details
|
|
│ │ ├── query-examples.js # LINQ query examples + diagnostic recipes
|
|
│ │ ├── workflows.js # Workflow catalog overview
|
|
│ │ ├── apis.js # API documentation
|
|
│ │ └── logs.js # Log file guide
|
|
│ ├── tools/ # MCP tools (actions)
|
|
│ │ ├── wms-query-tools.js # query_wms_entities, count_wms_entities, get_entity_schema, search_wms_data
|
|
│ │ ├── workflow-tools.js # search_workflows, get_workflow_details, list_workflow_categories
|
|
│ │ ├── ad-tools.js # get_ad_elements, search_ad_elements, get_ad_element_details (5 tools)
|
|
│ │ ├── api-tools.js # call_query_api, execute_command
|
|
│ │ ├── metadata-tools.js # get_entity_metadata, generic_search
|
|
│ │ ├── config-tools.js # get_system_parameters (Parameter + ParamValue merge)
|
|
│ │ ├── profile-tools.js # list_wms_profiles, get_current_wms_profile, switch_wms_profile
|
|
│ │ └── log-tools.js # read_recent_logs, list_log_files, search_logs
|
|
│ ├── services/ # Business logic
|
|
│ │ ├── api-service.js # OAuth + HTTP client (ApplicationService + AD APIs)
|
|
│ │ ├── workflow-service.js # Workflow fetching with cache (lazy loading)
|
|
│ │ ├── ad-service.js # Application Dictionary elements (20 types, lazy loading + cache)
|
|
│ │ ├── wms-query-service.js # LINQ query builder helper
|
|
│ │ └── log-service.js # Log file reading & searching
|
|
│ └── config/
|
|
│ ├── constants.js # Constants and entity type definitions
|
|
│ └── profile-manager.js # Multi-profile registry (AD, LIMAGRAIN, ...) + runtime switching
|
|
├── .env.example
|
|
├── package.json
|
|
└── README.md
|
|
```
|
|
|
|
## Development Setup
|
|
|
|
### Initial Setup
|
|
|
|
```bash
|
|
# Initialize project
|
|
npm init -y
|
|
|
|
# Install dependencies
|
|
npm install @modelcontextprotocol/sdk dotenv axios
|
|
npm install --save-dev @types/node typescript
|
|
```
|
|
|
|
### Environment Configuration
|
|
|
|
Create `.env` file (use `.env.example` as template):
|
|
|
|
```env
|
|
# Logs Configuration
|
|
LOGS_PATH=C:\WMS\Logs
|
|
LOG_FILE_PATTERN=application*.log
|
|
|
|
# WMS API Configuration
|
|
WMS_API_BASE_URL=https://10.255.255.2/ApplicationService/api
|
|
WMS_API_TOKEN_URL=https://10.255.255.2/EasySTS/OAuth/Token
|
|
WMS_API_AUTH=Basic R05BOklFNGU3aXFoZHQ=
|
|
WMS_API_USERNAME=your_username
|
|
WMS_API_PASSWORD=your_password
|
|
WMS_API_TENANT=AD
|
|
|
|
# Workflow API Configuration
|
|
WORKFLOW_API_BASE=https://10.255.255.2/AD/api
|
|
WORKFLOW_APPLICATION=EasyWMS
|
|
WORKFLOW_PAGE_SIZE=5000
|
|
|
|
# Cache Configuration
|
|
WORKFLOW_CACHE_TTL=3600
|
|
```
|
|
|
|
### Common Commands
|
|
|
|
```bash
|
|
# Run MCP server locally
|
|
npm start
|
|
|
|
# Test API connection
|
|
node test-api.js
|
|
|
|
# Build Windows executable
|
|
npm run build
|
|
# or
|
|
pkg . --targets node18-win-x64 --output dist/mcp-server.exe
|
|
|
|
# Run tests (once implemented)
|
|
npm test
|
|
```
|
|
|
|
## Implementation Guide
|
|
|
|
### Phase 1: API Service (src/services/api-service.js)
|
|
|
|
**Required Methods:**
|
|
- `authenticate()` - Get OAuth token with username/password
|
|
- `refreshToken()` - Refresh OAuth token before expiration
|
|
- `post(endpoint, data)` - Generic POST request with automatic token management
|
|
- `get(endpoint, params)` - Generic GET request with automatic token management
|
|
|
|
**Token Management Strategy:**
|
|
- Check token age before each request
|
|
- Auto-refresh if age > 1000 seconds (token lifetime ~1200s)
|
|
- Support both password and refresh_token grant types
|
|
|
|
**Test Script:**
|
|
```javascript
|
|
// test-api.js
|
|
const apiService = require('./src/services/api-service');
|
|
|
|
async function test() {
|
|
await apiService.authenticate();
|
|
const result = await apiService.post('/QueryExecute', {
|
|
Application: "EasyWMS",
|
|
QueryType: 1,
|
|
Expression: "Context.Products.Select(z => new { z.Id }).Take(1)"
|
|
});
|
|
console.log('API connection OK:', result);
|
|
}
|
|
|
|
test();
|
|
```
|
|
|
|
### Phase 2: MCP Resources (Read-Only Context for Claude)
|
|
|
|
Resources provide Claude with background knowledge without explicit queries:
|
|
|
|
1. **`wms://entities`** - List of WMS entities available via Query API (Containers, Stocks, Tasks, etc.)
|
|
2. **`wms://entity-schemas`** - Detailed schemas for top 15 critical entities
|
|
3. **`wms://query-examples`** - LINQ query examples for common use cases
|
|
4. **`workflows://overview`** - Workflow statistics, categories, and top workflows
|
|
5. **`workflows://categories`** - Complete list of workflow categories
|
|
6. **`api://catalog`** - List of available WMS APIs with parameters
|
|
7. **`logs://guide`** - Log file format, locations, common error patterns
|
|
|
|
All resources should return markdown-formatted content.
|
|
|
|
### Phase 3: MCP Tools (Executable Actions)
|
|
|
|
**WMS Query Tools:**
|
|
- `query_wms_entities(entity_type, select_expression, filter, limit)` - Query WMS entities via LINQ (max 1000 rows)
|
|
- `count_wms_entities(entity_type, filter)` - Count entities via QueryScalarExecute (preferred for "how many")
|
|
- `get_entity_schema(entity_type)` - Get available fields for an entity
|
|
- `search_wms_data(keyword, entity_types, limit)` - Search across multiple entities
|
|
|
|
**Workflow Tools:**
|
|
- `search_workflows(query, category, limit)` - Search workflows (lazy loaded with cache)
|
|
- `get_workflow_details(workflow_id)` - Retrieve full workflow JSON
|
|
- `list_workflow_categories()` - List all workflow categories
|
|
|
|
**API Tools:**
|
|
- `call_query_api(entity_type, expression, filter, limit)` - Execute custom LINQ queries
|
|
- `execute_command(command_name, properties)` - Execute WMS commands
|
|
|
|
**Metadata Tools:**
|
|
- `get_entity_metadata(entity_name)` - List queryable entities + their field names/types
|
|
- `generic_search(query, categories, limit)` - Full-text search across indexed WMS documents
|
|
|
|
**Config Tools:**
|
|
- `get_system_parameters(warehouse, param_class, search, only_overridden)` - WMS configuration parameters with per-warehouse effective values (merges `Parameter` + `ParamValue` Reading entities)
|
|
|
|
**Log Tools:**
|
|
- `read_recent_logs(count, log_file)` - Tail recent log entries
|
|
- `list_log_files()` - List available log files
|
|
- `search_logs(keyword, max_results)` - Search logs with context
|
|
|
|
### Phase 4: Main Server (src/index.js)
|
|
|
|
Use the MCP SDK to create the server:
|
|
|
|
```javascript
|
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
|
|
const server = new Server({
|
|
name: 'wms-mcp-server',
|
|
version: '1.0.0',
|
|
}, {
|
|
capabilities: {
|
|
resources: {},
|
|
tools: {},
|
|
},
|
|
});
|
|
|
|
// Implement handlers for:
|
|
// - resources/list
|
|
// - resources/read
|
|
// - tools/list
|
|
// - tools/call
|
|
|
|
async function main() {
|
|
const transport = new StdioServerTransport();
|
|
await server.connect(transport);
|
|
console.error('WMS MCP Server running'); // Use stderr, not stdout
|
|
}
|
|
|
|
main();
|
|
```
|
|
|
|
**Critical:** Use `console.error()` for all logging. Stdout is reserved for MCP protocol communication.
|
|
|
|
### Phase 5: Workflow Service (src/services/workflow-service.js)
|
|
|
|
**Lazy Loading Strategy:**
|
|
```javascript
|
|
let workflowCache = null;
|
|
let cacheTimestamp = null;
|
|
const CACHE_TTL = process.env.WORKFLOW_CACHE_TTL || 3600000; // 1 hour
|
|
|
|
async function fetchAllWorkflows() {
|
|
// Check cache validity
|
|
const now = Date.now();
|
|
if (workflowCache && (now - cacheTimestamp) < CACHE_TTL) {
|
|
console.error('[Workflow] Using cached data');
|
|
return workflowCache;
|
|
}
|
|
|
|
// Fetch from API with high page size to minimize calls
|
|
console.error('[Workflow] Fetching from API...');
|
|
const apiService = require('./api-service');
|
|
|
|
let allWorkflows = [];
|
|
let offset = 0;
|
|
const pageSize = parseInt(process.env.WORKFLOW_PAGE_SIZE) || 5000;
|
|
|
|
while (true) {
|
|
const endpoint = '/AD/api/Workflow/GetByApplication';
|
|
const body = [
|
|
process.env.WORKFLOW_APPLICATION || "EasyWMS",
|
|
process.env.WMS_API_TENANT || "AD",
|
|
pageSize,
|
|
offset
|
|
];
|
|
|
|
const response = await apiService.post(endpoint, body, true); // true = use AD API base
|
|
|
|
if (!response || response.length === 0) break;
|
|
|
|
allWorkflows = allWorkflows.concat(response);
|
|
console.error(`[Workflow] Fetched ${response.length} workflows (total: ${allWorkflows.length})`);
|
|
|
|
if (response.length < pageSize) break; // Last page
|
|
offset += pageSize;
|
|
}
|
|
|
|
// Cache results
|
|
workflowCache = allWorkflows;
|
|
cacheTimestamp = now;
|
|
|
|
console.error(`[Workflow] Cached ${allWorkflows.length} workflows`);
|
|
return allWorkflows;
|
|
}
|
|
```
|
|
|
|
**Key Features:**
|
|
- Only fetches workflows when needed (first workflow-related request)
|
|
- Uses high page size (5000) to reduce API calls
|
|
- Caches results for 1 hour (configurable)
|
|
- Automatic pagination if needed
|
|
|
|
### Phase 6: WMS API Client Details (src/services/api-service.js)
|
|
|
|
Based on the PHP reference file:
|
|
|
|
**Token Management:**
|
|
```javascript
|
|
async function refreshToken() {
|
|
// Check if token age > 1000 seconds
|
|
// If < 1190s: use refresh_token grant
|
|
// If >= 1190s: use password grant
|
|
// Store new token and refresh_token
|
|
}
|
|
```
|
|
|
|
**Query Execution:**
|
|
```javascript
|
|
async function executeQuery(entityType, expression) {
|
|
await ensureTokenValid();
|
|
|
|
const query = {
|
|
Application: "EasyWMS",
|
|
QueryType: 1,
|
|
Expression: `Context.${entityType}.Select(${expression})`
|
|
};
|
|
|
|
// POST to /api/QueryExecute with Bearer token
|
|
}
|
|
```
|
|
|
|
**Command Execution:**
|
|
```javascript
|
|
async function executeCommand(commandName, properties) {
|
|
await ensureTokenValid();
|
|
|
|
const command = [{
|
|
Name: `${commandName}, Mecalux.ITSW.EasyWMS.Modules.Contracts`,
|
|
Properties: properties
|
|
}];
|
|
|
|
// POST to /api/CommandExecute with Bearer token
|
|
}
|
|
```
|
|
|
|
### Phase 7: Error Handling
|
|
|
|
**Wrapper for All Tools:**
|
|
```javascript
|
|
async function safeToolCall(toolFn, args) {
|
|
try {
|
|
const result = await toolFn(args);
|
|
return {
|
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
|
|
};
|
|
} catch (error) {
|
|
console.error('Tool error:', error);
|
|
return {
|
|
content: [{ type: 'text', text: `Error: ${error.message}` }],
|
|
isError: true
|
|
};
|
|
}
|
|
}
|
|
```
|
|
|
|
## Deployment
|
|
|
|
### Build Windows Executable
|
|
|
|
Add to package.json:
|
|
```json
|
|
{
|
|
"bin": "src/index.js",
|
|
"pkg": {
|
|
"targets": ["node18-win-x64"],
|
|
"outputPath": "dist"
|
|
}
|
|
}
|
|
```
|
|
|
|
Build:
|
|
```bash
|
|
npm install -g pkg
|
|
pkg . --output dist/mcp-server.exe
|
|
```
|
|
|
|
### SSH Configuration
|
|
|
|
**On Windows Server VM:**
|
|
```powershell
|
|
# Install OpenSSH Server
|
|
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
|
|
Start-Service sshd
|
|
Set-Service -Name sshd -StartupType 'Automatic'
|
|
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server' `
|
|
-Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
|
|
|
|
# Configure authorized_keys
|
|
mkdir C:\Users\YourUser\.ssh
|
|
# Copy public key to C:\Users\YourUser\.ssh\authorized_keys
|
|
icacls C:\Users\YourUser\.ssh\authorized_keys /inheritance:r
|
|
icacls C:\Users\YourUser\.ssh\authorized_keys /grant:r "YourUser:F"
|
|
```
|
|
|
|
**On Development PC:**
|
|
```bash
|
|
# Generate SSH key
|
|
ssh-keygen -t ed25519 -C "claude-mcp"
|
|
|
|
# Add to ~/.ssh/config
|
|
Host vm-wms
|
|
HostName 192.168.x.x
|
|
User your-username
|
|
IdentityFile ~/.ssh/id_ed25519
|
|
ServerAliveInterval 60
|
|
ServerAliveCountMax 3
|
|
|
|
# Test connection
|
|
ssh vm-wms echo "Connection OK"
|
|
```
|
|
|
|
### Deploy to VM
|
|
|
|
1. Copy `mcp-server.exe` to `C:\WMS\mcp\`
|
|
2. Create `C:\WMS\mcp\.env` with production credentials
|
|
3. Test: `ssh vm-wms "C:\WMS\mcp\mcp-server.exe"`
|
|
|
|
### Claude Desktop Configuration
|
|
|
|
Edit `%APPDATA%\Claude\claude_desktop_config.json`:
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"wms": {
|
|
"command": "ssh",
|
|
"args": ["vm-wms", "C:\\WMS\\mcp\\mcp-server.exe"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Restart Claude Desktop after configuration changes.
|
|
|
|
## Testing
|
|
|
|
### Manual Tests (After Implementation)
|
|
|
|
1. List resources: "Show me available resources"
|
|
2. Read WMS entities: "What entities are available in the WMS?"
|
|
3. Search workflows: "Find workflows related to orders"
|
|
4. Query WMS data: "Query the last 10 products"
|
|
5. Read logs: "Show me the last 50 log lines"
|
|
6. Check workflow cache: "Search for workflows containing 'picking'"
|
|
|
|
### Debugging
|
|
|
|
**MCP Server Logs:**
|
|
- All `console.error()` output appears in Claude Desktop logs
|
|
- Location: `%APPDATA%\Claude\logs\`
|
|
|
|
**Manual MCP Testing:**
|
|
```bash
|
|
# Test server directly via SSH
|
|
ssh vm-wms "C:\WMS\mcp\mcp-server.exe" < test-request.json
|
|
|
|
# Test API connectivity
|
|
curl -X POST "https://10.255.255.2/EasySTS/OAuth/Token" \
|
|
-H "Authorization: Basic R05BOklFNGU3aXFoZHQ=" \
|
|
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
-d "grant_type=password&username=XXX&password=XXX"
|
|
```
|
|
|
|
## Multi-Profile Architecture
|
|
|
|
The server connects to different WMS backends (tenants/customers) via **profiles** defined in `.env`. Each profile has its own host, credentials, and tenant. Claude selects which profile to use at runtime.
|
|
|
|
### .env layout
|
|
|
|
**Shared settings** (same across all profiles):
|
|
- `WMS_API_AUTH` — OAuth client Basic auth header
|
|
- `WMS_APPLICATION` — Application name (e.g. `EasyWMS`)
|
|
- `WMS_API_PATH`, `WMS_TOKEN_PATH`, `WORKFLOW_API_PATH` — URL path components
|
|
|
|
**Profile registry:**
|
|
- `WMS_PROFILES` — comma-separated profile names (e.g. `AD,LIMAGRAIN`)
|
|
- `DEFAULT_WMS_PROFILE` — profile active at startup (optional)
|
|
|
|
**Per profile** (prefixed by profile name):
|
|
- `<NAME>_HOST` — hostname or IP (e.g. `10.255.255.2`, `p4swms.mss.mecalux.com`)
|
|
- `<NAME>_USERNAME`, `<NAME>_PASSWORD`, `<NAME>_TENANT`
|
|
- `<NAME>_SAAS` — `true`/`false` (default `false`). When `true`, the WMS is cloud-hosted and log filesystem tools are disabled (they return an error pointing to the API tools instead).
|
|
|
|
URLs are assembled as `https://<HOST><PATH>` — only the host varies per profile.
|
|
|
|
**Logs path template:** `LOGS_PATH` supports the `{host}` placeholder, substituted with the active profile's `HOST` at each call. Example:
|
|
```
|
|
LOGS_PATH=\\{host}\inetpub\logs\LogFiles\Mecalux;\\{host}\ProgramData\Mecalux\ETLLogs
|
|
```
|
|
|
|
### Runtime behavior
|
|
|
|
- At startup, `profile-manager.loadProfiles()` parses `.env` and activates `DEFAULT_WMS_PROFILE` if valid.
|
|
- If no default is set, any WMS API call throws a structured error that tells Claude to call `switch_wms_profile` first — Claude Desktop reads this and asks the user which WMS to use.
|
|
- On `switch_wms_profile`:
|
|
- `api-service` resets its OAuth token
|
|
- `workflow-service` clears its workflow cache
|
|
- `ad-service` invalidates all element-type caches
|
|
- Next API call re-authenticates against the new host with the new tenant.
|
|
- **SaaS profiles** (`<NAME>_SAAS=true`): log tools (`read_recent_logs`, `search_logs`, `list_log_files`) throw an error because the cloud filesystem is unreachable. Only WMS APIs (query/command/workflow/AD) work. On-premise profiles (`SAAS=false`, default) scan `LOGS_PATH` with `{host}` substituted by the profile's host.
|
|
|
|
### Profile tools
|
|
|
|
- `list_wms_profiles` — list all configured profiles + currently active one
|
|
- `get_current_wms_profile` — return active profile details (host, tenant, URLs)
|
|
- `switch_wms_profile({ profile })` — change active profile
|
|
|
|
### Files
|
|
|
|
- [src/config/profile-manager.js](src/config/profile-manager.js) — profile registry, switching, listener API
|
|
- [src/tools/profile-tools.js](src/tools/profile-tools.js) — the 3 MCP tools above
|
|
|
|
Services (`api-service`, `workflow-service`, `ad-service`) register `onSwitch` listeners so cache/token invalidation is automatic — never call these invalidations manually from other code.
|
|
|
|
## Key Technical Constraints
|
|
|
|
1. **API Access Only:** No direct Oracle access. All data retrieval via WMS REST APIs.
|
|
2. **Performance:** Limit result sets to 1000 rows maximum, implement query timeouts (30s).
|
|
3. **Error Handling:** All tools must return structured errors, never crash the server.
|
|
4. **Logging:** Use `console.error()` for logs (stdout reserved for MCP protocol).
|
|
5. **Windows:** File paths use backslashes, executable must be .exe format.
|
|
6. **Token Management:** Implement automatic token refresh before expiration (~1200s lifetime).
|
|
7. **Lazy Loading:** Workflows are only fetched when needed (not at startup).
|
|
8. **Caching:** Cache workflows for 1 hour to minimize API calls.
|
|
9. **Pagination:** Use high page sizes (5000) to reduce workflow API calls.
|
|
|
|
## Implementation Checklist
|
|
|
|
**Phase 1-2: Foundation**
|
|
- [x] Project initialized with dependencies (axios, @modelcontextprotocol/sdk, dotenv)
|
|
- [x] Directory structure created
|
|
- [x] API service implemented (OAuth + HTTP client)
|
|
- [x] Workflow service implemented (lazy loading + cache)
|
|
- [x] WMS query service implemented (LINQ helper)
|
|
- [x] Log service implemented
|
|
|
|
**Phase 3-4: MCP Core**
|
|
- [x] All 6 resources implemented (wms-entities, entity-schemas, query-examples, workflows, apis, logs)
|
|
- [x] 23 tools implemented (wms-query, workflow, ad, api, metadata, config, profile, log tools)
|
|
- [x] Main server with request routing
|
|
- [x] Error handling wrapper for all tools
|
|
|
|
**Phase 5-6: Deployment**
|
|
- [x] Build to .exe validated (pkg configuration ready)
|
|
- [ ] SSH configured on VM (pending deployment)
|
|
- [ ] Key-based auth working (pending deployment)
|
|
- [ ] Server deployed to C:\WMS\mcp\ (pending deployment)
|
|
- [x] .env configured with production credentials
|
|
|
|
**Phase 7: Integration**
|
|
- [x] Claude Desktop config updated
|
|
- [x] Basic functionality tests passed
|
|
- [x] Workflow lazy loading validated
|
|
- [x] API token refresh tested
|
|
- [x] Performance validated (query limits, timeouts)
|
|
|
|
**Critical Fixes Applied:**
|
|
- [x] OAuth authentication: Added missing `tenant_code` parameter
|
|
- [x] Workflow API: Extract `response.entities` instead of treating response as array
|
|
- [x] Property handling: Support both lowercase and uppercase property names (name/Name, id/Id)
|
|
- [x] dotenv stdout: Redirect dotenv output to stderr to comply with MCP protocol
|
|
- [x] .env path: Use absolute path to ensure .env is loaded from project root
|
|
|
|
## WMS Entity Types Reference
|
|
|
|
Based on [queries api.php](queries api.php), these entity types are supported:
|
|
|
|
**Master Data:** Containers, Products, Accounts, Suppliers, Kits, Aliases
|
|
**Operations:** Tasks, Stocks, ProductLocations
|
|
**Inbound:** InboundOrders, Receptions
|
|
**Outbound:** OutboundOrders
|
|
|
|
Each has corresponding Command classes for operations (focus on Query API for debugging).
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues and Solutions
|
|
|
|
#### 1. "Unexpected token 'd', "[dotenv@17."... is not valid JSON"
|
|
|
|
**Problem:** dotenv writes version info to stdout, but MCP requires stdout to be reserved for JSON protocol only.
|
|
|
|
**Solution:**
|
|
```javascript
|
|
// Redirect stdout to stderr during dotenv loading
|
|
const originalStdoutWrite = process.stdout.write;
|
|
process.stdout.write = process.stderr.write.bind(process.stderr);
|
|
require('dotenv').config({ path: path.join(__dirname, '..', '.env') });
|
|
process.stdout.write = originalStdoutWrite;
|
|
```
|
|
|
|
#### 2. "Authentication failed: Request failed with status code 400"
|
|
|
|
**Problem:** Missing `tenant_code` parameter in OAuth request.
|
|
|
|
**Solution:** Add `tenant_code` to authentication request:
|
|
```javascript
|
|
new URLSearchParams({
|
|
grant_type: 'password',
|
|
tenant_code: process.env.WMS_API_TENANT, // ← This was missing
|
|
username: process.env.WMS_API_USERNAME,
|
|
password: process.env.WMS_API_PASSWORD
|
|
})
|
|
```
|
|
|
|
#### 3. "Successfully cached 0 workflows"
|
|
|
|
**Problem:** Workflow API returns `{entities: [...]}` but code expects direct array.
|
|
|
|
**Solution:** Extract entities from response:
|
|
```javascript
|
|
const response = await apiService.post('/Workflow/GetByApplication', body, true);
|
|
const workflows = response?.entities || []; // ← Extract entities property
|
|
```
|
|
|
|
#### 4. "Workflow not found" despite existing
|
|
|
|
**Problem:** API returns properties in lowercase (`name`, `id`) but code looks for uppercase (`Name`, `Id`).
|
|
|
|
**Solution:** Support both cases:
|
|
```javascript
|
|
const name = (w.name || w.Name || '').toLowerCase();
|
|
const id = w.id || w.Id;
|
|
```
|
|
|
|
#### 5. "Missing environment variables"
|
|
|
|
**Problem:** dotenv looks for `.env` in current working directory, not project root.
|
|
|
|
**Solution:** Specify absolute path:
|
|
```javascript
|
|
require('dotenv').config({
|
|
path: path.join(__dirname, '..', '.env')
|
|
});
|
|
```
|
|
|
|
## Application Dictionary Implementation
|
|
|
|
The MCP server supports the Application Dictionary (AD) API, providing access to 20 element types containing application definitions (commands, queries, dialogs, views, etc.).
|
|
|
|
### Supported Element Types (20)
|
|
|
|
**Working Types (17 validated with curl):**
|
|
- Command (1,872 elements)
|
|
- Query (2,016 elements)
|
|
- Dialog (734 elements)
|
|
- View (373 elements)
|
|
- Entity (331 elements)
|
|
- Event (1,980 elements)
|
|
- FieldType (320 elements)
|
|
- Hook (60 elements)
|
|
- List (243 elements)
|
|
- Record (337 elements)
|
|
- Relationship (49 elements)
|
|
- Report (67 elements)
|
|
- Resource (29,374 elements - largest type)
|
|
- Subscription (500 elements)
|
|
- Validator (17 elements)
|
|
- ViewGroup (180 elements)
|
|
- Workflow (3,712 elements)
|
|
|
|
**Empty Types (0 elements, but endpoints exist):**
|
|
- Dashboard
|
|
- TimelineTemplate
|
|
- Toggle
|
|
|
|
**Total Elements:** 38,765
|
|
|
|
### Architecture
|
|
|
|
The AD implementation follows the same pattern as workflow-service.js:
|
|
|
|
1. **Lazy Loading:** Elements fetched only on first request (not at startup)
|
|
2. **Caching:** 1-hour TTL per element type (configurable via `WORKFLOW_CACHE_TTL`)
|
|
3. **Pagination:** Different page sizes per type (heavy types: 5000, light types: 100000)
|
|
4. **Property Flexibility:** Supports both lowercase (`name`, `id`) and uppercase (`Name`, `Id`)
|
|
|
|
**Files:**
|
|
- [src/services/ad-service.js](src/services/ad-service.js) - Generic service for all 20 element types
|
|
- [src/tools/ad-tools.js](src/tools/ad-tools.js) - 5 MCP tools for AD interaction
|
|
|
|
### Available AD Tools
|
|
|
|
1. **get_application_summary** - Shows cached element counts per type
|
|
2. **get_ad_elements** - Get all elements of a specific type (with limit)
|
|
3. **search_ad_elements** - Search elements by name/description/code
|
|
4. **get_ad_element_details** - Get full details of specific element
|
|
5. **list_ad_types** - List all 20 available element types
|
|
|
|
### curl Testing
|
|
|
|
All 17 working element types were validated with curl. See [AD_API_TEST_RESULTS.md](AD_API_TEST_RESULTS.md) for detailed results.
|
|
|
|
**Test command:**
|
|
```powershell
|
|
powershell -ExecutionPolicy Bypass -File test-ad-api.ps1
|
|
```
|
|
|
|
### Notes
|
|
|
|
- **WorkflowAction** and **WritingModel** removed (404 Not Found on API)
|
|
- Page sizes optimized per type (Resource: 15000, Workflow: 5000, View: 200, others: 100000)
|
|
- Same cache management as workflows (1-hour TTL)
|
|
|
|
## System Parameters (config-tools.js)
|
|
|
|
The `get_system_parameters` tool exposes WMS configuration parameters.
|
|
|
|
- The Reading model has **no `CommandParameterData` entity** — the correct entities
|
|
are `Parameter` (definition + `DefaultValue`) and `ParamValue` (per-warehouse
|
|
overrides, linked by `ParameterId`).
|
|
- The tool fetches both entities fully (small datasets — ~200 / ~50 rows), merges
|
|
them client-side, and reports the **effective value** per warehouse (override if
|
|
present, otherwise default). No LINQ string injection — all filters
|
|
(`warehouse`, `param_class`, `search`, `only_overridden`) are applied in JS.
|
|
- Files: [src/tools/config-tools.js](src/tools/config-tools.js).
|
|
|
|
## Shipment Templates — scope note
|
|
|
|
Requests for **shipment template execution history** are only partially served:
|
|
|
|
- The `ShipmentTemplate` Reading entity (queryable via `query_wms_entities`) exposes
|
|
only the **last** execution (`LastExecuteDate`) plus `Status` / `IsEnabled`.
|
|
- The **full execution history** lives exclusively in server-side
|
|
`ApplyShipmentTemplates` text logs. Those logs are **not present on the reachable
|
|
host** (`10.255.255.2`) — they sit on customer production / ETL servers — so no
|
|
log-parsing tool was built. See the `wms://query-examples` resource for the
|
|
API-only recipes.
|
|
|
|
## LINQ / QueryExecute gotchas (verified against live WMS)
|
|
|
|
- **Relative dates fail:** `DateTime.Now`, `DateTime.Today`, `AddDays()` are NOT
|
|
translatable by the query engine. Use a literal `new DateTime(year, month, day)`.
|
|
- **`select_expression` is unreliable:** LINQ projections passed via the `Select`
|
|
API parameter raise compile errors. Prefer querying full rows. (Open issue.)
|
|
- The `ApplicationService.log` line format is:
|
|
`YYYY-MM-DD HH:MM:SS.ffff [thread] [Level] [Component] [message]`.
|
|
|
|
## Removed code
|
|
|
|
The project is **100% API-based**. Legacy direct-Oracle files
|
|
(`src/services/oracle-service.js`, `src/resources/database.js`,
|
|
`src/tools/database-tools.js`) were dead code (unwired, `oracledb` not even a
|
|
dependency) and have been **removed**. Do not reintroduce direct database access.
|
|
|
|
## Future Enhancements
|
|
|
|
1. **Caching:** Cache frequently accessed resources (workflow categories)
|
|
2. **Analytics:** Tool to analyze error patterns across logs + workflows
|
|
3. **Suggestions:** Tool to suggest fixes based on error analysis
|
|
4. **Metrics:** Performance monitoring and query statistics
|
|
5. **`select_expression` fix:** Investigate the `Select` API parameter compile errors
|