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>
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-serverv1.0.0 - Runtime: Node.js 25+ (ESM)
- Language: TypeScript 5.7+
- Transport: stdio (local Claude Code integration)
- SDK:
@modelcontextprotocol/sdk1.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:
- Extracts structured error messages from backend
ApiResponseformat (error.message,error,Message,title) - Maps HTTP status codes to user-friendly messages (401 = token expired, 403 = insufficient permissions, 404 = not found, 400 = validation errors)
- Handles network errors (ECONNREFUSED, ETIMEDOUT) with actionable guidance
- Strips
Authorizationheaders from error objects to prevent token leakage - Returns MCP-compliant
{ content: [{ type: "text", text }], isError: true }format
Security
- Bearer token injected via Axios request interceptor from
API_TOKENenv var - Authorization header stripped from error responses to prevent token leakage in MCP tool output
- Startup warning printed to stderr if
API_TOKENis 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)
popular_items
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.