Files
pos-system/services/goodgo-mcp-server/SERVICE_DOCS.md
Ho Ngoc Hai d0211e5a3c docs: full documentation audit — update 7 files, create MCP SERVICE_DOCS
Project-level docs:
- README.md: rewrite with correct tech stack (.NET 10/Blazor, not Node.js/Flutter)
- ROADMAP.md: add MCP server, shop lifecycle, onboarding redesign, POS nav fix
- CLAUDE.md: add goodgo-mcp-server to project structure
- CTO_REPORT_SHOP_DELETE.md: status OPEN → RESOLVED (implemented in 6263eeb)

MCP Server docs:
- SERVICE_DOCS.md: new file — 12 tools reference, architecture, setup guide

Frontend docs:
- web-client-tpos-net README: fix wrong paths (web-client-base-net → web-client-tpos-net)
- web-client-tpos-net docs/en/README: same path fix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:46:15 +07:00

15 KiB

GoodGo MCP Server - Service Documentation

Auto-generated from source code audit on 2026-03-20.

Overview

GoodGo MCP Server is a TypeScript Model Context Protocol (MCP) server that enables AI assistants (Claude Code, Claude Desktop) to perform F&B shop operations -- catalog management, inventory tracking, recipe handling, and sales analytics -- by proxying requests through the Traefik API gateway to backend .NET microservices.

  • Package: @goodgo/mcp-server v1.0.0
  • Runtime: Node.js 25+ (ESM)
  • Language: TypeScript 5.7+
  • Transport: stdio (local Claude Code integration)
  • SDK: @modelcontextprotocol/sdk 1.12.1
  • Validation: Zod 3.24
  • HTTP Client: Axios 1.7
  • Default Shop: Cobic Coffee (e1f392af-fe95-4c7f-8656-5b74ad5fd0a9)
  • Auth: JWT Bearer token (from IAM IdentityServer OAuth2 password grant)

Configuration

Environment variables (.env file in service root):

Variable Required Default Description
API_GATEWAY_URL No http://localhost/api/v1 Traefik gateway base URL. Docker local uses port 80. Staging: https://api.staging.goodgo.vn/api/v1
DEFAULT_SHOP_ID No e1f392af-fe95-4c7f-8656-5b74ad5fd0a9 Default shop UUID (Cobic Coffee). Used when tools omit shopId.
API_TOKEN Yes (empty) JWT Bearer token from IAM login. All API calls fail 401 without this.

Architecture

Source Structure

goodgo-mcp-server/
  src/
    index.ts                      # Entry point: McpServer + StdioServerTransport, registers all tool groups
    services/
      api-client.ts               # Axios gateway client (single instance, Bearer interceptor, token-leak guard)
      error-handler.ts            # Shared errorResponse() + textResponse() helpers
    tools/
      catalog-tools.ts            # 4 tools: list_products, create_product, update_product, delete_product
      inventory-tools.ts          # 4 tools: check_inventory, record_intake, record_usage, low_stock_alerts
      recipe-tools.ts             # 2 tools: list_recipes, create_recipe
      analytics-tools.ts          # 2 tools: popular_items, cost_analysis
    types/                        # (reserved, currently empty)
  dist/                           # Compiled JS output (ESM)
  .env                            # Local config (git-ignored)
  .env.example                    # Template
  package.json
  tsconfig.json

Gateway Routing

All tools call a single Axios client pointed at API_GATEWAY_URL (default http://localhost/api/v1). Traefik routes by path prefix to the appropriate backend microservice:

Path Prefix Backend Service Tools Using It
/products catalog-service-net (port 5016) list_products, create_product, update_product, delete_product
/inventory, /inventory/stock-in, /inventory/stock-out, /inventory/low-stock inventory-service-net (port 5020) check_inventory, record_intake, record_usage, low_stock_alerts
/kitchen/recipes fnb-engine-net (port 5018) list_recipes, create_recipe
/orders/dashboard order-service-net (port 5014) popular_items, cost_analysis

Error Handling

error-handler.ts provides a centralized errorResponse() function that:

  1. Extracts structured error messages from backend ApiResponse format (error.message, error, Message, title)
  2. Maps HTTP status codes to user-friendly messages (401 = token expired, 403 = insufficient permissions, 404 = not found, 400 = validation errors)
  3. Handles network errors (ECONNREFUSED, ETIMEDOUT) with actionable guidance
  4. Strips Authorization headers from error objects to prevent token leakage
  5. Returns MCP-compliant { content: [{ type: "text", text }], isError: true } format

Security

  • Bearer token injected via Axios request interceptor from API_TOKEN env var
  • Authorization header stripped from error responses to prevent token leakage in MCP tool output
  • Startup warning printed to stderr if API_TOKEN is not set

Tools (12 total)

Catalog Tools (4)

list_products

List menu items/products for a shop. Returns name, price, category, stock status.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
categoryId string (UUID) No - Filter by category
isActive boolean No - Filter by active status (omit to show all)
page number No 1 Page number
pageSize number No 20 Items per page (max 200)

Backend: GET /products?shopId=&categoryId=&isActive=&page=&pageSize= Output: Formatted table with Name, Price (VND), Category, Active columns.


create_product

Create a new product/menu item in the shop catalog.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
name string Yes - Product name (1-200 chars)
description string No - Description (max 1000 chars)
price number Yes - Price in VND (positive)
type string No PreparedFood Product type: PreparedFood, Beverage, RetailItem
categoryId string (UUID) No - Category ID
sku string No - Stock-keeping unit code
imageUrl string (URL) No - Image URL

Backend: POST /products Output: Created product ID, name, and formatted price.


update_product

Update an existing product's name, price, description, or category. Fetches current values first to avoid overwriting unchanged fields (merge-patch semantics).

Parameter Type Required Default Description
productId string (UUID) Yes - Product ID to update
name string No - New name (1-200 chars)
description string No - New description (max 1000 chars)
price number No - New price in VND
categoryId string (UUID) No - New category ID
imageUrl string (URL) No - New image URL

Backend: GET /products/{id} (fetch current) then PUT /products/{id} (full update with merged fields) Output: Updated fields summary.


delete_product

Deactivate a product from the catalog (soft delete).

Parameter Type Required Default Description
productId string (UUID) Yes - Product ID to deactivate

Backend: DELETE /products/{id} Output: Confirmation message.


Inventory Tools (4)

check_inventory

Check current inventory/stock levels for a shop. Shows item name, quantity, unit, reorder level, and low stock warnings.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
skip number No 0 Records to skip
take number No 50 Records to take (max 200)

Backend: GET /inventory?shopId=&skip=&take= Output: Table with Name, Qty, Unit, Reorder Level, Status (OK/Low).


record_intake

Record stock intake (nhap kho) -- when goods are received from suppliers.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
productId string (UUID) Yes - Product ID to stock in
amount number (int) Yes - Quantity to add (positive integer)
notes string No - Notes for this intake
unitCost number No - Cost per unit

Backend: POST /inventory/stock-in Output: Intake confirmation with product, amount, cost, and notes.


record_usage

Record stock usage/outflow (xuat kho) -- when items are consumed, wasted, or adjusted.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
productId string (UUID) Yes - Product ID to stock out
amount number (int) Yes - Quantity to remove (positive integer)
notes string No - Reason for usage

Backend: POST /inventory/stock-out Output: Usage confirmation with product, amount, and notes.


low_stock_alerts

Get items that are at or below their reorder level. Critical for preventing stockouts.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
skip number No 0 Records to skip
take number No 50 Records to take (max 200)

Backend: GET /inventory/low-stock?shopId=&skip=&take= Output: List of low-stock items with current quantity, reorder level, and deficit.


Recipe Tools (2)

list_recipes

List all recipes for a shop. Shows recipe name, linked product, prep time, and ingredients.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID

Backend: GET /kitchen/recipes?shopId= Output: Numbered list with recipe details and ingredient breakdown (name, quantity, unit, cost).


create_recipe

Create a new recipe with ingredients list. Links to a product in the catalog.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
productId string (UUID) Yes - Catalog product this recipe is for
name string Yes - Recipe name (1-200 chars)
instructions string No - Preparation instructions (max 2000 chars)
prepTimeMinutes number (int) No 5 Prep time in minutes
ingredients array Yes - Ingredients list (min 1 item)

Ingredient object schema:

Field Type Required Default Description
ingredientName string Yes - Ingredient name
quantity number Yes - Quantity required
unit string Yes - Unit of measurement (g, ml, pcs, etc.)
costPerUnit number No 0 Cost per unit
inventoryItemId string (UUID) No - Linked inventory item ID
quantityPerServing number No 0 Quantity per serving for inventory deduction

Backend: POST /kitchen/recipes Output: Created recipe ID, name, product link, prep time, and ingredient summary.


Analytics Tools (2)

Get top selling products by analyzing order data. Shows product name, quantity sold, and revenue.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID
period enum No 7d Time period: today, 7d, 30d

Backend: GET /orders/dashboard?shopId=&period= Output: Rankings with total revenue, order count, items sold, avg order value, and per-product breakdown.


cost_analysis

Analyze cost structure by comparing inventory costs with revenue. Shows inventory value and revenue breakdown.

Parameter Type Required Default Description
shopId string (UUID) No DEFAULT_SHOP_ID Shop ID

Backend: GET /inventory?shopId=&take=200 + GET /orders/dashboard?shopId=&period=30d (parallel via Promise.allSettled) Output: Cost analysis report with 30-day revenue, inventory value, order stats, and top cost items ranked by inventory value.


API Endpoints Consumed

Summary of all backend API routes called by MCP tools:

Method Route Backend Service Tool(s)
GET /api/v1/products catalog-service list_products
GET /api/v1/products/{id} catalog-service update_product (fetch current)
POST /api/v1/products catalog-service create_product
PUT /api/v1/products/{id} catalog-service update_product
DELETE /api/v1/products/{id} catalog-service delete_product
GET /api/v1/inventory inventory-service check_inventory, cost_analysis
POST /api/v1/inventory/stock-in inventory-service record_intake
POST /api/v1/inventory/stock-out inventory-service record_usage
GET /api/v1/inventory/low-stock inventory-service low_stock_alerts
GET /api/v1/kitchen/recipes fnb-engine list_recipes
POST /api/v1/kitchen/recipes fnb-engine create_recipe
GET /api/v1/orders/dashboard order-service popular_items, cost_analysis

Setup & Build

Prerequisites

  • Node.js 25+ with ESM support
  • Docker stack running (Traefik + backend services)
  • Valid JWT token from IAM service

Install & Build

cd services/goodgo-mcp-server
npm install
npm run build          # tsc -> dist/

Run (development)

npm run dev            # tsx src/index.ts (hot reload)

Run (production)

npm start              # node dist/index.js

Claude Code Integration

Register as MCP server

claude mcp add --transport stdio goodgo -- node /absolute/path/to/services/goodgo-mcp-server/dist/index.js

Or with environment variables inline:

claude mcp add --transport stdio goodgo -- \
  env API_TOKEN=eyJ... DEFAULT_SHOP_ID=e1f392af-... \
  node /absolute/path/to/services/goodgo-mcp-server/dist/index.js

Verify registration

claude mcp list

Token Acquisition

The MCP server requires a valid JWT token from the IAM service (Duende IdentityServer password grant).

Get token via curl

curl -s http://localhost/connect/token \
  -d "grant_type=password" \
  -d "username=hongochai10@icloud.com" \
  -d "password=Velik@2026" \
  -d "client_id=password-client" \
  -d "client_secret=password-client-secret" \
  | jq -r '.access_token'

Set token in .env

# Copy template
cp .env.example .env

# Paste the access_token value
echo "API_TOKEN=eyJhbGciOiJSUzI1NiIs..." >> .env

Token refresh

Tokens expire (typically 1 hour). Re-run the curl command above and update API_TOKEN in .env. The MCP server reads .env at startup via dotenv, so restart the server after updating.


Currency & Locale

All prices are in VND (Vietnamese Dong). The server formats prices using Intl.NumberFormat("vi-VN") (e.g., 45.000 VND). When interacting with the AI assistant, prices display as X.000d format.