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.tsxrenderssrc/App.tsx, which rendersChatLayout. - Dev port:
9333fromvite.config.ts. - Main UI flow:
src/store/useChatStore.tsowns 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. CampaignListfetches campaign data fromhttp://localhost:9332/api/v1/agent/campaignsand campaign rules fromhttp://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 AIChatClient, 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/topicand/queue.config/AgentConfig.java: OAuth2-enabledRestClientfor the loyalty core API.controller/ConversationController.java: simple in-memory conversation/message REST endpoints.
- AI configuration:
- Uses Ollama at
http://localhost:11434. - Default model in
application.yml:qwen3.5:2b. - MCP client connects to
${MCP_SERVER_URL:http://localhost:9331}.
- Uses Ollama at
- 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
- SSE endpoint:
- 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 byconversationId, 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_SECRETKEYCLOAK_TOKEN_URILOYALTY_CORE_BASE_URLMCP_SERVER_URLVITE_AGENT_HTTP_URLVITE_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:
- Loads
.envif present. - Kills processes on ports
9331,9332, and9333. - Starts
loyalty-mcp-serverand waits for port9331. - Starts a
nodemoncompile watcher for the MCP server. - Starts
loyalty-agentand waits for port9332. - Starts a
nodemoncompile watcher for the agent. - Starts the frontend with
pnpm devon port9333. - 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.javaloyalty-agent/src/test/java/dev/sonpx/loyalty/agent/domain/AgentEventTest.javaloyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/McpOrchestratorTest.javaloyalty-mcp-server/src/test/java/dev/sonpx/loyalty/mcp/tool/reward/CampaignRuleMapperTest.javaloyalty-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-agentowns frontend-facing chat orchestration and REST proxy endpoints.loyalty-mcp-serverowns 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.
- Client publishes to
- Preserve
conversationIdpropagation. MCP draft tools depend on it to keep draft campaign/pool/rule state inDraftSessionManager. - Tool responses may contain
presentation.content; the agent system prompt requires displaying that content exactly. - Reuse existing UI primitives in
src/components/uiand 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 is9332, frontend is9333.