Content
# UAI Project
UAI is a service collection for Agent/LLM, including MCP tool management, session memory, SSE streaming output, and web-side debugging capabilities.
## Module Description
- `uai-mcp-server`: MCP Server, responsible for tool registration, tool invocation, and dialogue orchestration.
- `uai-mcp-base`: MCP basic capabilities and database structure (DDL).
- `uai-web-boot`: Spring Boot web container capabilities (including WebSocket/SSE integration).
- Other modules (`uai-common`, `uai-util`, `uai-rag`, `uai-vec`, `uai-graph`) provide public capabilities and extension support.
## Quick Start
### 1. Initialize Database
```bash
mysql -u root -p < uai-mcp-base/src/main/resources/sql/mcp_ddl.sql
```
If the database already exists and writing emoji reports an error, execute `uai-mcp-base/src/main/resources/sql/mcp_utf8mb4_fix.sql` again.
### 2. Configure LLM
Blocking calls and SSE streaming output **share the same LLM configuration** (see `ChatModelFactory`). Ensure that both point to the same provider to avoid Chinese garbled characters during streaming.
#### Configuration Item Description
| Environment Variable | JVM / YAML Property | Description | Default Value |
|---------|----------------|------|--------|
| `UAI_LLM_API_KEY` | `uai.llm.api-key` | API Key (required, except for demo) | None |
| `UAI_LLM_BASE_URL` | `uai.llm.base-url` | OpenAI compatible interface address | `https://api.deepseek.com` |
| `UAI_LLM_MODEL_NAME` | `uai.llm.model-name` | Model name | `deepseek-chat` |
#### Configuration Priority (High → Low)
1. **OS Environment Variable** (`export UAI_LLM_API_KEY=...`)
2. **JVM Parameter** (`-Duai.llm.api-key=...`)
3. **`.env` File** (project root directory, automatically loaded as System Property by `EnvFileLoader` at startup; **does not override** existing values in 1 and 2)
4. **`application-{profile}.yml`** `uai.llm.*`
5. **Code Default Value**
> Note: Java processes **do not** automatically read `.env` like Node.js. This project calls `EnvFileLoader.loadIfPresent()` before `ChatModelFactory` initialization to map `UAI_LLM_*` in `.env` to `uai.llm.*` system properties for subsequent configuration parsing.
#### Method A: Using `.env` (Recommended for Local Development)
```bash
cp .env.example .env
# Edit .env, variable name must be UAI_LLM_API_KEY, cannot be written as api-key
```
`.env` format:
```bash
UAI_LLM_API_KEY=sk-your-real-api-key
UAI_LLM_BASE_URL=https://api.deepseek.com
UAI_LLM_MODEL_NAME=deepseek-chat
```
After successful startup, the log should appear:
```
Loaded env file: /path/to/uai/.env
```
`.env` will search up to 6 parent directories from the current working directory, generally placed in the **project root directory**.
#### Method B: Export Environment Variable
```bash
export UAI_LLM_API_KEY="your_api_key"
export UAI_LLM_BASE_URL="https://api.deepseek.com"
export UAI_LLM_MODEL_NAME="deepseek-chat"
```
#### Method C: YAML Configuration
Configure in `uai-mcp-server/src/main/resources/application-local.yml` (local private, do not submit key):
```yaml
uai:
llm:
base-url: https://api.deepseek.com
model-name: deepseek-chat
api-key: your_api_key
```
When using the `langchain4j.dev` demo, `UAI_LLM_API_KEY` can be omitted, and the API key is automatically set to `demo`.
### 3. Configure Database Connection
Edit the configuration file for the corresponding profile (e.g., `uai-mcp-server/src/main/resources/application-local.yml`) and modify the address, username, and password of `spring.datasource` and `mysql.datasource`.
### 4. Start Service
```bash
mvn clean install jetty:run -Dspring.profiles.active=local
```
Configuration reading depends on `spring.profiles.active`. Ensure that the data source and LLM configuration for the corresponding profile are correct.
### 5. Verify Dialogue
Open the streaming dialogue page in the browser:
```
http://localhost:8080/static/mcp/chat/stream/ssebox
```
Or use `curl` to call the SSE interface:
```bash
curl 'http://localhost:8080/commonsse/api/query_ai' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json' \
--data '{"sessionId":"test-session-001","clientName":"test-client","ucid":"0","message":"What tools do you have, table display"}'
```
It is recommended to explicitly pass `sessionId` and `ucid` in the request for multi-turn memory and log tracking.
## MCP Core Design
MCP achieves Agent role-based orchestration through "tools + tags + tag-tool relationships":
- `t_tool`: Tool definition (name, description, parameter schema, return type, data source configuration, etc.).
- `t_label`: Tag definition (can be used as a role corresponding to `clientName`, `description` is often used for system prompt).
- `t_label_tool`: Tag and tool binding relationship (which tools a role can use).
Typical process:
1. Develop and register atomic capabilities to `t_tool`.
2. Define roles in `t_label` (system prompt).
3. Associate roles with tools in `t_label_tool`.
4. Select roles through `clientName` when calling, driving the tool call chain.
## Database Initialization
Refer to `uai-mcp-base/src/main/resources/sql/mcp_ddl.sql`. The default database name is `mcp`, and core tables include:
- `t_chat_message`: Session message history (`session_id`, `single_id`, message type, and content).
- `t_server`: Tool service definition (`local`/`http` data source).
- `t_tool`: Tool definition and execution configuration.
- `t_prompt`: Prompt template definition and parameter schema.
- `t_label`, `t_label_tool`: Role tags and tool binding.
It is recommended to execute DDL first, then import initial tool and tag data.
```bash
mysql -u root -p < uai-mcp-base/src/main/resources/sql/mcp_ddl.sql
```
### Character Set (utf8mb4)
Session messages may contain emoji and other 4-byte UTF-8 characters. `t_chat_message.content` must use `utf8mb4`. If encountering the following error:
```
Incorrect string value: '\xF0\x9F...' for column 'content'
```
Please execute the repair script:
```bash
mysql -u root -p mcp < uai-mcp-base/src/main/resources/sql/mcp_utf8mb4_fix.sql
```
JDBC connection also needs to use utf8mb4 (the project `application-*.yml` has been configured by default):
```
characterEncoding=UTF-8&connectionCollation=utf8mb4_unicode_ci
```
## clientName Routing Rules
`clientName` is used to select role tags. The priority is from high to low:
1. User input prefix `@clientName`
2. URL parameter `clientName`
3. MCP Client default parameter `clientName`
The system built-in tag `_mcp` can be used for system tool capabilities (e.g., natural language query MCP metadata).
## Session and Memory
- Supports `sessionId` transparent transmission and persists historical messages to `t_chat_message`.
- Can perform multi-turn memory dialogue (context continuity under the same `sessionId`).
- It is recommended to explicitly pass `sessionId` and `ucid` in the request for tracking and authentication integration.
## SSE Streaming Process (UAI Side)
The key link of SSE is:
1. Servlet initializes SSE response header and starts asynchronous context (`AsyncContext`).
2. Asynchronous thread processes business and continuously `send` data through context.
3. Business completion or exception calls `complete()` to end the connection.
4. When docking LangChain4j streaming callback, it is necessary to explicitly pass `AsyncContext` to avoid ThreadLocal loss due to asynchronous nesting.
`DynamicServletListener` registration needs to enable asynchronous support:
- Path: `/commonsse/*`
- Configuration: `setAsyncSupported(true)`
## MCP + SSE Call Characteristics
When the request contains MCP tool calls, the streaming process usually consists of two stages:
1. **Blocking Judgment Stage**: First, call the model to determine if a tool is needed (this stage often appears `AiMessage.text == null`, not streaming output to the front end).
2. **Streaming Answer Stage**: After tool execution is completed, perform a second model call and stream the final answer.
This strategy has been merged into the unified MCP Client base class process.
## Debugging and Testing Entrance
Common pages and interfaces:
- `http://localhost:8080/pages/mcp/chat/box`
- `http://localhost:8080/static/mcp/chat/lighthtml`
- `http://localhost:8080/static/mcp/chat/lightmarkdown`
- `http://localhost:8080/static/mcp/chat/stream/ssebox`
- `http://localhost:8080/static/mcp/chat/stream/uploadssebox`
- `http://localhost:8080/static/mcp/chat/websocketbox`
SSE `curl` example:
```bash
curl 'http://localhost:8080/commonsse/api/query_ai' \
-H 'Connection: keep-alive' \
-H 'Content-Type: application/json' \
--data '{"sessionId":"mhn9kcp3hj9woc4k","clientName":"test-client","ucid":"0","message":"Beijing weather and traffic conditions"}'
```
## Sensitive Information Configuration
LLM-related configurations are uniformly created by `ChatModelFactory` to block models and streaming models. Secrets are not hardcoded in the code.
Implementation points:
- `EnvFileLoader`: Load project root directory `.env` as System Property at startup
- `ChatModelFactory.resolveConfig()`: Merge environment variables, JVM parameters, YAML, and default values by priority
- Blocking model (`getDefaultChatModel`) and streaming model (`getDefaultStreamingChatModel`) must use the same `baseUrl` / `modelName` / `apiKey`
Required configuration (choose one source):
- Environment variable: `UAI_LLM_API_KEY`
- JVM parameter: `-Duai.llm.api-key=...`
- `.env` file: `UAI_LLM_API_KEY=...` (automatically loaded)
- YAML: `uai.llm.api-key`
Optional configuration (with default values):
- `UAI_LLM_BASE_URL` / `uai.llm.base-url` (default `https://api.deepseek.com`)
- `UAI_LLM_MODEL_NAME` / `uai.llm.model-name` (default `deepseek-chat`)
Startup example (`.env` method):
```bash
cp .env.example .env # Edit and fill in the real key
mvn clean install jetty:run -Dspring.profiles.active=local
```
Startup example (export method):
```bash
export UAI_LLM_API_KEY="your_api_key"
export UAI_LLM_BASE_URL="https://api.deepseek.com"
export UAI_LLM_MODEL_NAME="deepseek-chat"
mvn clean install jetty:run -Dspring.profiles.active=local
```
If the API Key is not configured, an `IllegalStateException` will be thrown during the static initialization phase of `ChatModelFactory` when calling LLM for the first time.
### Pre-commit Prevention of Disclosure (Recommended)
It is recommended to install `pre-commit` and `gitleaks` locally to automatically scan secrets before each submission:
```bash
brew install pre-commit gitleaks
```
Create `.pre-commit-config.yaml` in the project root directory:
```yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaks
```
Enable hook:
```bash
pre-commit install
pre-commit run --all-files
```
If the scan hits a suspected secret, handle it before submitting.
## Frequently Asked Questions
### Missing LLM API key (`.env` Not Effective)
**Phenomenon**: Report an error `Missing LLM API key. Please set environment variable UAI_LLM_API_KEY or JVM property uai.llm.api-key` when requesting.
**Common Causes**:
1. Variable name written incorrectly in `.env` (e.g., written as `api-key=` instead of `UAI_LLM_API_KEY=`)
2. `.env` placed in the wrong directory (should be in the project root directory or ensure startup from the root directory)
3. After modifying `.env`, did not recompile and start ( `ChatModelFactory` initializes at class loading, need to restart the process)
4. The value in `.env` is still the placeholder `replace_with_your_real_api_key`
**Handling**:
1. Check variable names and format against `.env.example`
2. Restart and confirm the log appears `Loaded env file: ...`; if this line does not appear, it means `.env` was not found
3. Or use `export UAI_LLM_API_KEY=...` / YAML / JVM parameter
### Writing Session History Error (MySQL 1366)
**Phenomenon**: Log appears `Incorrect string value: '\xF0\x9F...' for column 'content'`.
**Cause**: Database connection or table character set does not support emoji and other 4-byte characters.
**Handling**:
1. Execute `uai-mcp-base/src/main/resources/sql/mcp_utf8mb4_fix.sql`
2. Confirm JDBC URL contains `connectionCollation=utf8mb4_unicode_ci`
3. Restart service
### SSE Streaming Response Chinese Garbled Characters
**Phenomenon**: The first LLM call log is normal, but the front-end or `onCompleteResponse` output is `????`.
**Cause**: Blocking model and streaming model use different LLM providers. For example, blocking goes to DeepSeek, and streaming still goes to `langchain4j.dev` demo (SSE response `charset=iso-8859-1`), leading to Chinese parsing errors.
**Handling**: Unify `UAI_LLM_BASE_URL`, `UAI_LLM_MODEL_NAME`, `UAI_LLM_API_KEY`, make blocking and streaming use the same provider, then restart the service.
### LogPushTask Not Started
**Phenomenon**: Log repeatedly prints `LogPushTask not started, ubag.log.timer.task.push.enable = false`.
**Description**: This is a prompt that the ubag log push switch is not enabled. It does not affect MCP dialogue and `t_chat_message` writing. If you need to enable it, set `ubag.log.timer.task.push.enable = true` in `ubag-conf.properties`.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available