mcp-servers/OPENUI_SCHEMA_FIX.md

164 lines
5.5 KiB
Markdown

# OpenUI Tool Discovery Schema Fix
## Problem Analysis
OpenUI fails to load any tools from the MCP Gateway with errors like:
```
"Email Digest string not found"
"DocType string not found"
```
### Root Causes
1. **Missing OpenAI-compatible endpoints** — Gateway only exposes MCP protocol (`/mcp`), not OpenAI API endpoints (`/v1/tools`, `/v1/models`, `/v1/chat/completions`)
2. **Complex JSON schemas break OpenUI parsing** — MCP tool schemas use features OpenAI API doesn't support:
- `type` as array: `["string", "null"]` instead of single type
- `anyOf` / `oneOf` with mixed content
- Tuple validation with `items` as array
- Tool names getting truncated due to improper escaping
3. **No schema normalization** — While `gateway_proxy.py` has `_normalize_schema()` for MCP protocol, the OpenAI adapter doesn't simplify schemas properly for OpenUI
4. **Missing `/v1/tools` endpoint** — OpenUI expects a dedicated tools discovery endpoint, not just embedded in `/v1/chat/completions`
## Solution: Schema Conversion & New Endpoints
### Step 1: Use Fixed OpenAI Routes Handler
Replace the basic `openai_routes.py` with `openai_routes_fixed.py` which includes:
- **`_simplify_schema()`** — Recursively converts complex schemas to OpenAI-compatible format
- **`convert_mcp_tool_to_openai()`** — Properly maps MCP tool definitions to OpenAI function schema
- **`/v1/tools` endpoint** — Dedicated tools discovery (required by OpenUI)
- **Error handling** — Gracefully handles malformed tool definitions
### Step 2: Update gateway_proxy.py
Add the following import after existing imports (around line 27):
```python
from .openai_routes_fixed import convert_mcp_tool_to_openai, list_models, tools, chat_completions
```
Add these routes to the routes list (before the closing bracket, around line 820):
```python
# OpenAI-compatible API endpoints (for Open-UI and other OpenAI clients)
Route("/v1/models", list_models, methods=["GET"]),
Route("/v1/tools", lambda r: tools(r, TOOL_DEFINITIONS), methods=["GET"]),
Route("/v1/chat/completions", lambda r: chat_completions(r, TOOL_DEFINITIONS), methods=["POST"]),
```
## Schema Conversion Examples
### Before (MCP format - causes errors)
```json
{
"name": "erpnext_get_document",
"description": "Retrieve a single document",
"inputSchema": {
"type": "object",
"properties": {
"doctype": {
"type": ["string", "null"],
"description": "DocType name"
},
"name": {
"type": ["string", "null"],
"description": "Document name"
}
},
"required": ["doctype", "name"]
}
}
```
### After (OpenAI format - works in OpenUI)
```json
{
"type": "function",
"function": {
"name": "erpnext_get_document",
"description": "Retrieve a single document",
"parameters": {
"type": "object",
"properties": {
"doctype": {
"type": "string",
"description": "DocType name"
},
"name": {
"type": "string",
"description": "Document name"
}
},
"required": ["doctype", "name"]
}
}
}
```
## Implementation Checklist
### Phase 1: Apply Fixes
- [ ] Deploy `openai_routes_fixed.py` to `gateway-proxy/`
- [ ] Update `gateway_proxy.py` with import and routes (see Step 2 above)
- [ ] Restart gateway container: `docker-compose restart gateway-proxy`
### Phase 2: Verify
- [ ] Test `/v1/models` endpoint returns valid OpenAI model list
- [ ] Test `/v1/tools` endpoint returns tools with proper OpenAI schema
- [ ] Verify no truncated tool names in response
- [ ] Check gateway logs for errors: `docker logs mcp-gateway`
### Phase 3: Test in OpenUI
- [ ] Add gateway URL to OpenUI: `http://mcp.wilddragon.net:8000`
- [ ] Verify tools appear in model selector
- [ ] Try searching for tools by name
- [ ] Call a tool to verify end-to-end functionality
## Files Involved
| File | Purpose | Status |
|------|---------|--------|
| `gateway-proxy/openai_routes_fixed.py` | Fixed OpenAI schema conversion | ✅ Created |
| `gateway-proxy/gateway_proxy.py` | Main gateway with route definitions | ⏳ Needs updates |
| `openai_adapter.py` | (Can be removed - superseded by openai_routes_fixed.py) | Optional |
## Testing Commands
```bash
# Test tool discovery
curl http://localhost:8000/v1/tools
# Test models endpoint
curl http://localhost:8000/v1/models
# Check specific tool schema (example)
curl http://localhost:8000/v1/tools | jq '.data[] | select(.function.name == "erpnext_get_document")'
```
## Troubleshooting
### Tools still not loading
1. Check that `openai_routes_fixed.py` is in the correct directory
2. Verify import statement was added to `gateway_proxy.py`
3. Check for import errors in gateway logs
4. Ensure routes were added before the closing `]` bracket
### Tools loading but parameters wrong
1. Check that `_simplify_schema()` is being called
2. Verify property names aren't being corrupted
3. Test individual tool schema with `jq` to inspect conversion
### OpenUI still showing errors
1. Verify `/v1/tools` returns valid JSON (use `jq` to validate)
2. Check browser console for actual error messages
3. Ensure no truncation of tool names in the response
## Additional Notes
- The `lambda r: tools(r, TOOL_DEFINITIONS)` syntax is required because `TOOL_DEFINITIONS` is a module-level variable that gets populated during initialization
- Schema conversion happens at request time, so adding new backends will automatically make their tools available via OpenAI API
- Error handling is intentionally lenient (malformed tools are skipped rather than failing the whole request)