Model Context Protocol (MCP) server for U2 UniData/UniVerse databases
Project description
UOFast MCP Server
Enterprise Model Context Protocol (MCP) server for U2 UniData/UniVerse databases, with JWT authentication, role-based access control (RBAC), audit logging, and a web-based security admin UI.
What's New in v2
| Feature | v1 (old) | v2 (current) |
|---|---|---|
| Transport | stdio (subprocess) | HTTP/SSE (network service) |
| Authentication | None | JWT · API keys · HTTP Basic Auth |
| Authorization | None | RBAC — per-tool permissions |
| Audit logging | None | SQLite audit log of every tool call |
| Admin UI | None | Web UI at /admin |
| User self-service | None | Login page at /auth/login — get connection config instantly |
Quick Start (Local Dev)
1. Install
pip install uofast-mcp
# or from source:
pip install -e .
2. Set required environment variables
# Windows
set JWT_SECRET_KEY=your-long-random-secret-here
set INITIAL_ADMIN_PASSWORD=YourStrongPassword123!
# macOS/Linux
export JWT_SECRET_KEY=your-long-random-secret-here
export INITIAL_ADMIN_PASSWORD=YourStrongPassword123!
Generate a secure secret key:
python -c "import secrets; print(secrets.token_hex(32))"
3. Start the server
# via console script (after pip install):
uofast-mcp
# or directly:
uvicorn uofast_mcp.app:app --reload --port 8000
On first startup the server will:
- Create
data/security.db(SQLite database) - Seed default roles, permissions, and the admin user
4. Verify it's running
GET http://localhost:8000/health
→ {"status": "ok", "service": "uofast-mcp"}
Open the admin UI: http://localhost:8000/admin
Login with admin / <INITIAL_ADMIN_PASSWORD>
Connecting Clients
- Log in at http://localhost:8000/auth/login with your username and password
- Copy the
claude mcp addcommand shown on the page - Run it in your terminal — done
claude mcp add --transport sse unidata http://localhost:8000/sse --header "Authorization: Basic <your-basic-token>"
One command, works on Windows/macOS/Linux. No config file editing needed.
Manual config: The login page also has a collapsible JSON config block you can paste into
claude_desktop_config.jsonor VSCode MCP settings if you prefer.
User Setup
Quick path — Admin provisions the user (one step)
The admin can create a user and get their ready-to-use CLI command in a single API call:
curl -X POST http://localhost:8000/auth/provision \
-u admin:YourAdminPassword \
-H "Content-Type: application/json" \
-d '{"username": "alice", "password": "SecurePass123!", "role_name": "developer"}'
Response:
{
"username": "alice",
"role": "developer",
"claude_command": "claude mcp add --transport sse unidata http://localhost:8000/sse --header \"Authorization: Basic YWxpY2U6U2VjdXJlUGFzczEyMyE=\""
}
Send the claude_command value to the user — they run it and they're connected.
Alternative — Admin UI + self-service login
- Open http://localhost:8000/admin → Users → Create → fill in username, email, role
- Tell the user to visit http://localhost:8000/auth/login and log in with their credentials
- The login page shows the
claude mcp addcommand — user copies and runs it
(Optional) API Keys for service accounts
For CI pipelines or long-lived integrations, generate an API key via the Admin UI (Users → Generate API Token) or REST API. API keys never expire unless you set a date.
Roles & Permissions
Pre-built Roles
| Role | Permissions |
|---|---|
admin |
All permissions + admin UI access |
developer |
Connections, files, records (read/write), DICT (read/write/delete), BP programs (read/write/compile), commands |
analyst |
Connections (read), files (read), records (read), DICT (read), commands |
readonly |
Connections (read), files (read), records (read), DICT (read) |
service_account |
Same as readonly — customise as needed |
Tool → Permission Mapping
| Tool | Required Permission |
|---|---|
list_connections |
unidata.connection.read |
add_connection, close_connection |
unidata.connection.manage |
list_files |
unidata.files.read |
select_records, read_record, query_file, read_record_with_fields, get_dict_items, query_with_dict_fields |
unidata.record.read / unidata.dict.read |
write_record_with_fields |
unidata.record.write |
execute_command |
unidata.command.execute |
read_dict_item |
unidata.dict.read |
write_dict_item, update_dict_item |
unidata.dict.write |
delete_dict_item |
unidata.dict.delete |
read_bp_program |
unidata.bp.read |
write_bp_program |
unidata.bp.compile |
compile_bp_program |
unidata.bp.compile |
Customise Role Permissions
Via Admin UI: Roles → select role → add/remove permissions
Via REST API:
# Add a permission to a role
curl -X POST http://localhost:8000/admin/api/roles/<role-id>/permissions \
-H "Authorization: Bearer <admin-token>" \
-H "Content-Type: application/json" \
-d '{"permission_key": "unidata.record.write"}'
# Remove a permission
curl -X DELETE http://localhost:8000/admin/api/roles/<role-id>/permissions/<permission-id> \
-H "Authorization: Bearer <admin-token>"
Admin Web UI
Browse to http://localhost:8000/admin — login with admin credentials.
| Section | What you can do |
|---|---|
| Users | Create, search, edit (role/status), deactivate users |
| Roles | Create roles, view assigned permissions |
| Permissions | View all tool permissions |
| Role Permissions | Assign/remove permissions from roles |
| API Tokens | View active tokens, deactivate |
| Audit Logs | Browse all tool calls, filter by user/tool/status; read-only |
Audit Logs
Every tool call is logged with: user, tool name, parameters (sanitised — passwords never stored), result status, timestamp, IP address.
View in Admin UI: Audit Logs section
Via REST API:
# Recent logs
curl http://localhost:8000/admin/api/audit-logs \
-H "Authorization: Bearer <admin-token>"
# Filter by tool and status
curl "http://localhost:8000/admin/api/audit-logs?tool_name=write_record_with_fields&result_status=denied" \
-H "Authorization: Bearer <admin-token>"
# Export as CSV
curl http://localhost:8000/admin/api/audit-logs/export \
-H "Authorization: Bearer <admin-token>" \
-o audit_logs.csv
U2 UniData Connection Configuration
Connection settings are loaded in this order:
unidata_config.ini(recommended — supports multiple named connections)- Environment variables (single connection fallback)
add_connectiontool (on-demand from Claude)
INI config file
[server]
min_connections = 1
max_connections = 10
log_level = INFO
default_connection = production
[connection:production]
host = your-unidata-host
port = 31438
username = your-username
password = your-password
account = C:\U2\UD83\DEMO
service = udcs
auto_connect = true
[connection:test]
host = your-test-host
port = 31438
username = your-username
password = your-password
account = C:\U2\UD83\TEST
service = udcs
auto_connect = false
Environment variables (single connection)
| Variable | Required | Default |
|---|---|---|
UNIDATA_HOST |
Yes | — |
UNIDATA_USERNAME |
Yes | — |
UNIDATA_PASSWORD |
Yes | — |
UNIDATA_ACCOUNT |
Yes | — |
UNIDATA_PORT |
No | 31438 |
UNIDATA_SERVICE |
No | udcs |
UNIDATA_MIN_CONNECTIONS |
No | 0 (no minimum) |
UNIDATA_MAX_CONNECTIONS |
No | 0 (unlimited) |
Environment Variables Reference
| Variable | Required | Description |
|---|---|---|
JWT_SECRET_KEY |
Yes | JWT signing secret — keep long and random |
INITIAL_ADMIN_PASSWORD |
Yes (first run) | Password for the seeded admin account |
DATABASE_URL |
No | Defaults to sqlite+aiosqlite:///./data/security.db |
JWT_ALGORITHM |
No | HS256 (default) or RS256 (production) |
JWT_EXPIRE_MINUTES |
No | 60 (default) |
ENABLE_DOCS |
No | true (default) — set false to hide /docs in production |
Copy .env.example to .env for a full template.
API Endpoints
| Endpoint | Method | Auth | Description |
|---|---|---|---|
/health |
GET | None | Health check |
/sse |
GET | JWT / API Key / Basic Auth | MCP SSE connection |
/messages |
POST | JWT / API Key / Basic Auth | MCP message handler |
/auth/login |
GET/POST | None | User login page — returns ready-to-use connection configs |
/auth/token |
POST | None | Get JWT token (?username=&password=) |
/admin |
GET | Admin session | Web admin UI |
/admin/api/users |
GET/POST | Admin JWT | List / create users |
/admin/api/users/{id} |
GET/PUT/DELETE | Admin JWT | Get / update / deactivate user |
/admin/api/roles |
GET/POST | Admin JWT | List / create roles |
/admin/api/roles/{id}/permissions |
POST/DELETE | Admin JWT | Assign / remove permissions |
/admin/api/permissions |
GET | Admin JWT | List all permissions |
/admin/api/users/{id}/api-tokens |
GET/POST | Admin JWT | List / create API tokens |
/admin/api/api-tokens/{id} |
DELETE | Admin JWT | Revoke API token |
/admin/api/audit-logs |
GET | Admin JWT | Query audit log |
/admin/api/audit-logs/export |
GET | Admin JWT | Export audit log as CSV |
/docs |
GET | None | Interactive API docs (Swagger UI) |
Project Structure
UOFastMCP/
├── src/uofast_mcp/
│ ├── app.py # FastAPI factory — entry point
│ ├── server.py # MCP tool definitions (24 tools)
│ ├── security/
│ │ ├── models.py # SQLAlchemy ORM models
│ │ ├── database.py # DB engine + seeder (create_all, no migrations)
│ │ ├── auth.py # JWT + API key + password helpers
│ │ ├── rbac.py # RBACEngine (permission checks)
│ │ ├── audit.py # Audit logger
│ │ ├── permissions.py # Tool → permission mapping
│ │ └── middleware.py # FastAPI auth middleware
│ ├── admin/
│ │ ├── router.py # Admin REST API
│ │ ├── schemas.py # Pydantic request/response schemas
│ │ └── ui.py # SQLAdmin web UI views
│ ├── setup/
│ │ ├── router.py # Setup wizard routes
│ │ └── templates/ # Jinja2 HTML templates (Bootstrap 5)
│ ├── core/
│ │ ├── connection_manager.py # U2 connection pooling
│ │ └── uopy_operations.py # U2 database operations
│ └── utils/
│ └── config_loader.py # INI config loader
├── data/ # SQLite DB (gitignored)
├── security_config.yaml # Security settings template
├── unidata_config.ini # U2 connection config (gitignored)
├── .env.example # Environment variable template
├── pyproject.toml
└── requirements.txt
Troubleshooting
401 Unauthorized
- Token missing or expired — get a new token at
/auth/loginorPOST /auth/token - Check header format:
Bearer <jwt>·ApiKey <key>·Basic <base64(user:pass)> - Basic auth: re-generate your token at
/auth/loginif you changed your password
403 Forbidden
- User's role doesn't have the required permission
- Check Audit Logs for
result_status=deniedentries - Admin UI → Roles → verify permissions assigned to user's role
Server won't start
JWT_SECRET_KEYnot set — required at startup- Port 8000 in use — use
--port 8001 - Missing
data/directory — the server creates it automatically
Database errors
- Delete
data/security.dband restart to reset (loses all users/audit logs)
U2 connection fails
- Verify UniData server is reachable:
ping <host> - Check credentials in
unidata_config.inior environment variables - Use
add_connectiontool from Claude to test manually
License
MIT License — © 2025 RokiPark. All rights reserved.
Developed by RokiPark.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uofast_mcp-1.0.0.tar.gz.
File metadata
- Download URL: uofast_mcp-1.0.0.tar.gz
- Upload date:
- Size: 62.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6337cca95c23c45ac02b37cf2abece026fa394dcd10def80bb2047d2d2f8990d
|
|
| MD5 |
9aed66ce803128a9e51ab49a6a4d6d49
|
|
| BLAKE2b-256 |
33a692cd205ecf60149dfa6d71918433bb8d4e815104e1f239ab7023be85914b
|
File details
Details for the file uofast_mcp-1.0.0-py3-none-any.whl.
File metadata
- Download URL: uofast_mcp-1.0.0-py3-none-any.whl
- Upload date:
- Size: 68.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5649bb05115ffdba087e6f987ff91762be2092752a8f0abe5a6bac66a2fe6312
|
|
| MD5 |
3bc273d71be6081cabe9400c1c528016
|
|
| BLAKE2b-256 |
4bee3accec165cfbb2d4572f351b28a1578dbecf0aa34f087a801366ef1d5dcd
|