Files
mcp-wms-api/CLAUDE.md
T
2026-05-20 09:38:07 +02:00

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