Files
loyalty-agent-service/context.md

6.4 KiB

trigger
trigger
always_on

Project Context: Loyalty Agent Service

This repository is a local development workspace for a loyalty AI assistant. It has a React chat UI at the repository root and two Spring Boot backend modules managed by a parent Maven project.

Architecture

Frontend: root directory

  • Stack: React 19, Vite, TypeScript, Tailwind CSS, shadcn-style UI primitives, Zustand, @stomp/stompjs, react-markdown, remark-gfm.
  • Entry point: src/main.tsx renders src/App.tsx, which renders ChatLayout.
  • Dev port: 9333 from vite.config.ts.
  • Main UI flow:
    • src/store/useChatStore.ts owns chat state and STOMP connection.
    • WebSocket URL is currently hard-coded as ws://localhost:9332/ws.
    • User messages are published to /app/chat.
    • Agent events are received from /user/queue/chat-events.
    • CampaignList fetches campaign data from http://localhost:9332/api/v1/agent/campaigns and campaign rules from http://localhost:9332/api/v1/agent/campaigns/{campaignId}/rules.

loyalty-agent

  • Spring Boot application: dev.sonpx.loyalty.agent.LoyaltyAgentApplication.
  • Runtime port: 9332.
  • Role: BFF and AI orchestration layer for the frontend.
  • Key files:
    • controller/AgentController.java: STOMP chat endpoint and REST campaign proxy endpoints.
    • service/LoyaltyAgentService.java: builds a Spring AI ChatClient, streams LLM output, wraps tool calls, and emits tool status events.
    • config/WebSocketConfig.java: STOMP endpoint /ws, application prefix /app, user destination prefix /user, broker prefixes /topic and /queue.
    • config/AgentConfig.java: OAuth2-enabled RestClient for the loyalty core API.
    • controller/ConversationController.java: simple in-memory conversation/message REST endpoints.
  • AI configuration:
    • Uses Ollama at http://192.168.99.10:11434.
    • Default model in application.yml: qwen3.5:4b.
    • MCP client connects to ${MCP_SERVER_URL:http://localhost:9331}.
  • Agent event types sent to the UI: TOKEN, TOOL_STATUS, DONE, ERROR.

loyalty-mcp-server

  • Spring Boot application: dev.sonpx.loyalty.mcp.McpServerApplication.
  • Runtime port: 9331.
  • Role: MCP tool server for loyalty operations. The agent calls this service through Spring AI MCP SSE.
  • MCP endpoints from application.yml:
    • SSE endpoint: /sse
    • Message endpoint: /message
  • Key files:
    • config/McpServerConfig.java: registers MCP tool objects.
    • tool/LoyaltyAgentTools.java: member tiers, point pools, campaign creation, static attributes.
    • tool/reward/CampaignTools.java: campaign listing and campaign draft lifecycle.
    • tool/reward/CampaignRuleTools.java: campaign rule listing, creation, editing, and draft rule attachment.
    • tool/pool/PointPoolTools.java: pool and SOP draft tools.
    • draft/DraftSessionManager.java: in-memory draft sessions keyed by conversationId, with a 1-hour TTL.
    • draft/DependencyValidator.java: validates draft dependencies before tools mutate or submit drafts.
  • API clients call the loyalty core service with OAuth2 client credentials.

External Services And Environment

Create .env from .env.example for local development.

Important variables:

  • KEYCLOAK_CLIENT_SECRET
  • KEYCLOAK_TOKEN_URI
  • LOYALTY_CORE_BASE_URL
  • MCP_SERVER_URL
  • VITE_AGENT_HTTP_URL
  • VITE_AGENT_WS_URL

Current defaults:

  • Keycloak token URI: http://192.168.99.235/auth/realms/ols-cn-sit/protocol/openid-connect/token
  • Loyalty core base URL: http://192.168.99.242:8081
  • MCP server URL: http://localhost:9331
  • Agent HTTP URL: http://localhost:9332
  • Agent WS URL: ws://localhost:9332

Note: some frontend code still uses hard-coded localhost URLs instead of the VITE_* variables.

Local Startup

Use the root script for the full local stack:

./start-all.sh

What the script does:

  1. Loads .env if present.
  2. Kills processes on ports 9331, 9332, and 9333.
  3. Starts loyalty-mcp-server and waits for port 9331.
  4. Starts a nodemon compile watcher for the MCP server.
  5. Starts loyalty-agent and waits for port 9332.
  6. Starts a nodemon compile watcher for the agent.
  7. Starts the frontend with pnpm dev on port 9333.
  8. Writes logs to log/.

Manual startup:

./mvnw spring-boot:run -pl loyalty-mcp-server
./mvnw spring-boot:run -pl loyalty-agent
pnpm dev

The script currently invokes -Pgen, but the checked-in POM files do not define a gen Maven profile. Do not assume OpenAPI generation is wired unless you verify or add that profile.

Build, Lint, And Tests

Frontend:

pnpm install
pnpm lint
pnpm build

Backend:

./mvnw test
./mvnw test -pl loyalty-agent
./mvnw test -pl loyalty-mcp-server

Relevant tests:

  • loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/controller/AgentControllerTest.java
  • loyalty-agent/src/test/java/dev/sonpx/loyalty/agent/domain/AgentEventTest.java
  • loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.java
  • loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.java
  • loyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/LoyaltyAgentToolsRealApiTest.java

LoyaltyAgentToolsRealApiTest may depend on real external API availability and credentials.

Development Guidelines For Agents

  • Keep the boundary clear:
    • loyalty-agent owns frontend-facing chat orchestration and REST proxy endpoints.
    • loyalty-mcp-server owns tool implementations and loyalty core API access.
    • The frontend owns STOMP state, message rendering, chat layout, and campaign context panels.
  • Preserve the STOMP contract unless intentionally changing both sides:
    • Client publishes to /app/chat.
    • Server handles @MessageMapping("/chat").
    • Server sends events to /user/queue/chat-events.
  • Preserve conversationId propagation. MCP draft tools depend on it to keep draft campaign/pool/rule state in DraftSessionManager.
  • Tool responses may contain presentation.content; the agent system prompt requires displaying that content exactly.
  • Reuse existing UI primitives in src/components/ui and existing Tailwind conventions.
  • Do not introduce persistent storage assumptions. Conversation repositories and draft sessions are currently in memory.
  • Do not hard-code new service URLs when a matching environment variable already exists.
  • Be careful with ports: MCP is 9331, agent is 9332, frontend is 9333.