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

31 KiB

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

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:

# 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:

{
  "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 - 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 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

# 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):

# 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

# 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:

// 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:

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:

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:

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:

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:

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:

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:

{
  "bin": "src/index.js",
  "pkg": {
    "targets": ["node18-win-x64"],
    "outputPath": "dist"
  }
}

Build:

npm install -g pkg
pkg . --output dist/mcp-server.exe

SSH Configuration

On Windows Server VM:

# 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:

# 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:

{
  "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:

# 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>_SAAStrue/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

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

  • Project initialized with dependencies (axios, @modelcontextprotocol/sdk, dotenv)
  • Directory structure created
  • API service implemented (OAuth + HTTP client)
  • Workflow service implemented (lazy loading + cache)
  • WMS query service implemented (LINQ helper)
  • Log service implemented

Phase 3-4: MCP Core

  • All 6 resources implemented (wms-entities, entity-schemas, query-examples, workflows, apis, logs)
  • 23 tools implemented (wms-query, workflow, ad, api, metadata, config, profile, log tools)
  • Main server with request routing
  • Error handling wrapper for all tools

Phase 5-6: Deployment

  • 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)
  • .env configured with production credentials

Phase 7: Integration

  • Claude Desktop config updated
  • Basic functionality tests passed
  • Workflow lazy loading validated
  • API token refresh tested
  • Performance validated (query limits, timeouts)

Critical Fixes Applied:

  • OAuth authentication: Added missing tenant_code parameter
  • Workflow API: Extract response.entities instead of treating response as array
  • Property handling: Support both lowercase and uppercase property names (name/Name, id/Id)
  • dotenv stdout: Redirect dotenv output to stderr to comply with MCP protocol
  • .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:

// 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:

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:

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:

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:

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:

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 for detailed results.

Test command:

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.

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