Resolution pipeline
On every chat start, the server callsskill_manager.get_user_skills(email). The result resolves through three fallback levels:
- Memory cache — 60-second TTL. Same
(email)hit twice in a minute = dict lookup. - Settings Wrapper API — if
MCP_TOKENS_URLis set, fetches the user’s enabled skills. - Disk cache — if the API is down but we cached last known good, reuse it.
- Hardcoded defaults — all 13 public skills.
System prompt injection
The skill list becomes an<available_skills> XML block injected into the system prompt:
SKILL.md files on demand.
User skill mounts
User-uploaded skills (ZIPs from the Settings Wrapper) are:- Downloaded and extracted atomically: unzip to temp, rename to final.
- Cached under
/data/skills-cache/{name}/on the host. - Tracked in
.manifest.json(name → SHA-256 + timestamp). - Bind-mounted into the sandbox at
/mnt/skills/user/{name}/— read-only.
SKILLS_CACHE_HOST_PATH.
Core functions (skill_manager.py)
| Function | Purpose |
|---|---|
get_user_skills(email) | Async — full resolution with all fallbacks |
get_user_skills_sync(email) | Sync — reads cache only (called from the Docker-creation thread) |
ensure_skill_cached(skill) | Download + extract user-uploaded ZIP |
build_available_skills_xml(skills) | <available_skills> for the system prompt |
build_sub_agent_skills_text(skills) | Skill list for the sub-agent prompt |
get_skill_mounts(skills) | Dict of bind mounts for user skills |
HTTP endpoints
| Endpoint | Returns |
|---|---|
GET /system-prompt?user_email=X&chat_id=Y | Rendered per-user prompt |
GET /skill-mounts?user_email=X | Bind-mount descriptors for user skills |
GET /skill-list?user_email=X | Formatted skill list for sub-agent prompts |
Configuration
| Var | Default | Purpose |
|---|---|---|
MCP_TOKENS_URL | (empty) | Settings Wrapper URL |
MCP_TOKENS_API_KEY | (empty) | Internal API key |
SKILLS_CACHE_DIR | /data/skills-cache | In-container cache path |
SKILLS_CACHE_HOST_PATH | /tmp/skills-cache | Host path for Docker mounts |
