# 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 `_HOST`, `_USERNAME`, `_PASSWORD`, `_TENANT`. 3. The full URLs are built as `https://` 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): - `_HOST` — hostname or IP (e.g. `10.255.255.2`, `p4swms.mss.mecalux.com`) - `_USERNAME`, `_PASSWORD`, `_TENANT` - `_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://` — 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** (`_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