Libre Agents Spec
Purpose
Implement Alternative E: Master Agent + Programmatic Sync from the Research doc to solve the agent-model binding problem. One master agent serves as the single source of truth; a sync script propagates its configuration to N model-variant agents.
Design Decisions
All open questions resolved. Decisions recorded here for reference.
| # | Decision | Choice |
|---|---|---|
| Q1 | Master agent visibility | Usable — the master agent IS the GLM 5 Turbo variant, users chat with it directly |
| Q2 | MongoDB access | Environment variable MONGO_URI (Railway service env) |
| Q3 | ModelSpecs in v1 | Yes — include ModelSpecs configuration in this project |
| Q4 | Existing duplicate agents | Update in-place by name, preserving _id — never delete, never create with new ID |
| Q5 | Naming convention | Uvilo Agent <Model> (e.g., “Uvilo Agent GLM 5 Turbo”) |
| Q6 | Auto MCP sync | No — MCP servers still need manual addition to the master agent via Agent Builder |
Architecture Overview
Components
1. Master Agent
The single agent edited manually via the Agent Builder UI. It is also a fully functional, usable agent — users pick it from the model picker and chat with it directly.
| Property | Value |
|---|---|
| Name | Uvilo Agent GLM 5 Turbo |
| Agent ID | agent_s5VyS1DBdUsGcsRxaw8x3 |
| Provider | OpenRouter |
| Model | z-ai/glm-5-turbo |
Contains:
- System instructions — the full Uvilo assistant prompt
- MCP server bindings — all configured servers
- Deferred tool configuration — which tools load eagerly vs. on demand
- Capabilities — Code Interpreter, File Search, Actions, Artifacts
- Agent-level settings — temperature, max tokens, etc.
2. Variant Config File
A YAML file defining the master agent and all model variants. Lives in the repo at Forge/Configs/agent-sync.yaml.
Each variant has an id field:
- Master agent: ID is known and hardcoded (
agent_s5VyS1DBdUsGcsRxaw8x3). This entry is a no-op during sync (it’s the source, not a target). - Existing variants: IDs are populated from the first schema discovery run. The script looks up agents by
iddirectly for precise updates.
Why ID-based lookup matters: Using _id directly (instead of finding by name) guarantees the script updates the exact right agent document. Name-only lookup is fragile — if someone renames an agent or creates a new one with the same name, the script could update the wrong document and break conversation links.
Adding a new model variant = add an entry with id: null, run /update-agents, paste the reported ID into the config, then update librechat.yaml ModelSpecs.
3. Sync Script (update-agents.ts)
A Python script that:
- Reads
agent-sync.yamlfor variant definitions - Connects to MongoDB via
MONGODB_URIenvironment variable - Finds the master agent document by name
- For each variant (except the master itself):
- If
idis set: looks up the agent directly by_idfor a precise update - If
idis null: searches for an existing agent by name, or creates a new one - If found: updates the agent document in-place using
$seton_id, preserving all conversation references - If not found: inserts a new agent document (cloned from master with model/provider overridden)
- If
- Outputs a summary: which agents were created, updated, or skipped — and the ID of any newly created agents
- Generates ModelSpecs YAML entries for all variants (writes to stdout)
- If any new agent IDs were reported, prompts the user to update
agent-sync.yamlwith them before the next run
Critical: the script NEVER deletes agents and NEVER creates a new agent when one with that name already exists. Existing agents are always updated in-place to preserve conversation history.
Synced Fields
Copied from master to each variant on every sync:
| Field | Behavior |
|---|---|
name | Set from variant config (not copied from master) |
model | Set from variant config (not copied from master) |
provider | Set from variant config (not copied from master) |
instructions | Copied from master |
tools | Copied from master (flat list of 378 tool ID strings) |
model_parameters | Copied from master (nested dict: temperature, max_tokens, top_p, etc.) |
tool_options | Copied from master (per-tool deferred loading config, 334 entries) |
mcpServerNames | Copied from master (18 MCP server names) |
artifacts | Copied from master ("default") |
description | Copied from master |
category | Copied from master ("general") |
end_after_tools | Copied from master |
hide_sequential_outputs | Copied from master |
is_promoted | Copied from master |
support_contact | Copied from master |
actions | Copied from master (empty array) |
agent_ids | Copied from master (empty array) |
edges | Copied from master (empty array) |
conversation_starters | Copied from master (empty array) |
tool_kwargs | Copied from master (empty array) |
projectIds | Copied from master (empty array) |
Excluded Fields
Never overwritten on existing variants:
| Field | Reason |
|---|---|
_id | Unique identifier (ObjectId) — preserved to maintain conversation links |
id | Agent ID string — preserved to maintain conversation links |
conversations | Conversation history belongs to each variant |
createdAt | Original creation timestamp |
updatedAt | Set automatically by MongoDB |
author | Owner — preserved |
versions | Version history — unique per agent |
__v | Mongoose version key — preserved |
Provider → Endpoint Mapping
The script maps provider names to LibreChat endpoint identifiers:
| Provider | Endpoint |
|---|---|
anthropic | anthropic |
openAI | openAI |
OpenRouter | OpenRouter |
openrouter | openrouter |
google | google |
4. MongoDB Access
- Connection: via
MONGO_URIenvironment variable (set on Railway service env) - Connection string:
mongodb://mongo:<password>@mongodb.railway.internal:27017 - Database:
test - Collection:
agents - Library:
pymongo(installed viauv pip install pymongo) - Operations:
find_one(lookup by name or id),update_onewith$set(in-place update),insert_one(new agent)
5. ModelSpecs Configuration
Added to librechat.yaml to present all variant agents as a clean curated model picker. Each agent gets a ModelSpecs entry with its agent ID.
Agent IDs for non-master variants are populated after the first script run. The script outputs a ready-to-paste ModelSpecs snippet.
6. /update-agents Command
Registered in instructions.md as a command the assistant can execute:
- Trigger: User says
/update-agentsor assistant runs it after editing the master agent - Execution:
run("npx tsx /workspace/erik/uvilo-os/Forge/Skills/Update_Agents/scripts/update-agents.ts") - Output: Summary of what was created/updated/skipped + ModelSpecs snippet if agent IDs changed
- Post-run: If ModelSpecs need updating, the assistant copies the snippet to
librechat.yamland reminds the user to/deploy-config+/redeploy
Data Flow
File Structure
Implementation Steps
Phase 1: Schema Discovery
- 1.1 Connect to MongoDB and inspect the master agent document (
agent_s5VyS1DBdUsGcsRxaw8x3) - 1.2 Document all fields present in the agent schema
- 1.3 Confirm field names for synced fields (instructions, tools, capabilities, etc.)
- 1.4 Confirm provider → endpoint mapping from the master agent’s document
- 1.5 Identify any additional fields not in the current synced/excluded tables
- 1.6 Update this spec’s field tables with confirmed schema
Confirmed schema (Phase 1 results):
- Env var:
MONGO_URI(notMONGODB_URI) - Database:
test(notlibrechat) - Connection:
mongodb.railway.internal:27017(internal Railway network) - Master agent provider:
OpenRouter(notz-ai) - Master agent model:
z-ai/glm-5-turbo - Model parameters nested in
model_parametersdict (not top-level fields) tool_options(notdeferredTools) holds per-tool deferred loading configmcpServerNamestracks MCP server bindings (18 servers)toolsis a flat list of 378 tool ID stringsartifactsfield:"default"versionsis a list of version history entries- Empty arrays:
actions,agent_ids,edges,conversation_starters,tool_kwargs,projectIds - Agent
idfield (string, e.g.agent_s5VyS1...) is separate from_id(ObjectId)
Phase 2: Script Development
- 2.1 Create
agent-sync.yamlwith the 5 variant entries - 2.2 Build
update-agents.ts:- MongoDB connection via
MONGO_URI - Master agent lookup by name (with ID verification)
- Variant find-or-create logic (find by name, update in-place, or insert)
- Field cloning with model/provider/endpoint override
- Provider → endpoint mapping
- ModelSpecs YAML snippet generation (with all agent IDs)
- Result reporting (created/updated/skipped counts)
- MongoDB connection via
- 2.3 Test: run script, verify GLM 5 Turbo variant is a no-op (already is master)
- 2.4 Test: verify one non-master variant is updated in-place (check
_idpreserved) - 2.5 Test: re-run script to verify idempotency (no changes on second run)
- [BLOCKED] 2.6 Test: add a new variant to config, run script, verify it’s created (requires creating a throwaway agent in production DB)
- 2.7 Test: verify ModelSpecs snippet output matches expected format
Phase 3: Integration
- 3.1 Register
/update-agentscommand ininstructions.md - 3.2 Run script to get all agent IDs
- 3.3 Populate ModelSpecs in
librechat.yamlwith actual agent IDs - 3.4 Deploy config and redeploy LibreChat
- 3.5 Verify: all 5 agents appear in the model picker via ModelSpecs
- 3.6 Verify: each agent has the correct model and all tools are available
- 3.7 Verify: existing conversations are still accessible under their agents
Phase 4: Documentation
- 4.1 Update Knowledge index with agent management notes
- 4.2 Add operational lesson: “To add a new model, edit agent-sync.yaml, run /update-agents, update ModelSpecs in librechat.yaml, then /deploy-config + /redeploy”
- 4.3 Mark spec as
status: approved
Error Handling
| Scenario | Behavior |
|---|---|
MONGO_URI not set | Error: “MONGO_URI environment variable not set.” |
| MongoDB connection failure | Error: report connection details, suggest checking env var |
| Master agent not found | Error: “Master agent ‘Uvilo Agent GLM 5 Turbo’ not found in MongoDB.” |
| Master agent ID mismatch | Warning: “Master agent found but ID differs from expected agent_s5VyS1DBdUsGcsRxaw8x3. Using found ID.” |
| Variant config has unknown provider | Warning: skip that variant, continue with others |
| Variant ID set but agent not found | Error: “Agent {id} not found. Was it deleted?” — stops processing that variant |
| Variant ID is null, no agent by name | Info: create new agent, report its ID for config update |
| Variant ID is null, agent found by name | Info: update in-place, report ID for config update |
| Partial failure (some variants fail) | Report which succeeded and which failed; don’t roll back successes |
| Script run with no changes | Info: “All variants are up to date.” |
Safety Properties
- Idempotent: Running the script N times produces the same result as running it once
- Non-destructive: Existing agents are updated in-place;
_idand conversations are never touched - Never deletes: The script has no delete logic — agents are only created or updated
- Atomic per-variant: Each variant update is a single MongoDB
$setoperation - Conversation-preserving: Updating an agent in-place keeps all existing conversations linked
- No LibreChat source changes: External script accessing the database directly
Prerequisites
Before implementation begins:
-
MONGO_URIenvironment variable set on Railway (contains the full MongoDB connection string for the LibreChat database) -
pymongoavailable on the Railway volume (installed viauv pip install pymongo) - Master agent
Uvilo Agent GLM 5 Turboexists and is fully configured with all tools - All existing variant agents exist in MongoDB (will be found by name and updated in-place)