Skip to main content
All agent tools in GenieHelper are served by a single MCP server: genie-mcp-server (scripts/mcp/genie-mcp-server.mjs). It runs as a stdio process, spawned at boot by AnythingLLM’s bootMCPServers() function. There is no per-tool process overhead, no duplicated auth, and no tool name collisions — the plugin loader enforces uniqueness at startup and exits with an error if duplicates are found.

Architecture

genie-mcp-server (stdio)
├── lib/pluginLoader.mjs   — scans storage/plugins/, validates manifests
├── lib/services.mjs       — shared fetch clients for Directus, Ollama, Stagehand

├── cms.directus    (55 tools)
├── ai.ollama       (3 tools)
├── web.stagehand   (9 tools)
├── media.process   (5 tools)
├── taxonomy.core   (7 tools)
└── memory.recall   (4 tools)
                    ───────────
                    83 tools total

Boot sequence

1

AnythingLLM server starts

PM2 starts the anything-llm process (server/). The boot sequence runs server/utils/boot/index.js.
2

bootMCPServers() is called

bootMCPServers() reads storage/plugins/anythingllm_mcp_servers.json and spawns each registered MCP server as a child process.
3

genie-mcp-server starts

The single genie entry spawns genie-mcp-server.mjs. The plugin loader scans storage/plugins/ for directories containing both plugin.json and handler.mjs.
4

Plugins load and register tools

Each plugin’s createTools(services) function is called. The loader registers all returned ToolDef objects. Duplicate tool names cause process.exit(1).

Boot config

The MCP server registration lives in storage/plugins/anythingllm_mcp_servers.json:
{
  "mcpServers": {
    "genie": {
      "command": "node",
      "args": ["/path/to/scripts/mcp/genie-mcp-server.mjs"],
      "env": {
        "PLUGINS_DIR": "/path/to/storage/plugins",
        "MCP_SERVICE_TOKEN": "...",
        "DIRECTUS_URL": "http://127.0.0.1:8055",
        "OLLAMA_URL": "http://127.0.0.1:11434",
        "STAGEHAND_URL": "http://127.0.0.1:3002"
      }
    }
  }
}

The MCP write constraint

All Directus writes from server code must go through cms.directus MCP tools. No raw fetch() calls to Directus are permitted in business logic. This is a hard architectural rule enforced by code review.The only exceptions are register.js and rbacSync.js — both run pre-auth and require the DIRECTUS_ADMIN_TOKEN directly.
This constraint exists to maintain a single audit path for all data mutations. Every write goes through the MCP layer, which means every write is logged, rate-limited, and consistently authenticated via MCP_SERVICE_TOKEN.

Plugin reference

cms.directus — 55 tools

Full Directus 11 API coverage: items CRUD, users, files, flows, fields, relations, roles, policies, permissions, settings, server info, activity log, revision history, and schema management. Auth: MCP_SERVICE_TOKEN Bearer · Base: http://127.0.0.1:8055
ToolDescription
list-collectionsList all custom Directus collections
get-collection-schemaGet field schema for a collection
read-itemsRead items with filter, sort, pagination, field projection
read-itemRead a single item by UUID
create-itemCreate a new item (tier-gates media_jobs writes)
update-itemPATCH update a single item
delete-itemDELETE a single item
search-itemsFull-text search across a collection
ToolDescription
get-meGet current MCP service account profile
list-usersList users with filter and field projection
get-userGet single user by UUID
create-userCreate a new Directus user
update-userPATCH update a user
delete-userDelete a user
invite-userInvite a user by email
ToolDescription
list-filesList Directus file assets
get-fileGet file metadata and asset URL
delete-fileDelete a file asset
import-fileImport a file from a URL
ToolDescription
list-flowsList all Directus Flows
trigger-flowPOST to a Flow’s webhook trigger by UUID
create-flowCreate a new Flow definition
update-flowUpdate an existing Flow
delete-flowDelete a Flow
list-operationsList all operations within a flow
create-operationCreate a new operation in a flow
ToolDescription
list-fieldsList fields for a collection
create-fieldCreate a new field
update-fieldUpdate field metadata
delete-fieldDelete a field
list-relationsList all relations
create-relationCreate a relation
delete-relationDelete a relation
create-collectionCreate a new collection
delete-collectionDelete a collection
get-schema-snapshotExport full schema JSON
diff-schemaDiff a schema against current state
apply-schemaApply a schema diff
ToolDescription
list-rolesList all roles
get-roleGet role by UUID
create-roleCreate a new role
update-roleUpdate a role
delete-roleDelete a role
list-policiesList all access policies
get-policyGet a policy by UUID
list-permissionsList all permission rules
create-permissionCreate a permission rule
update-permissionUpdate a permission rule
delete-permissionDelete a permission rule
ToolDescription
get-settingsGet global Directus settings
update-settingsUpdate global settings
server-healthServer health check
server-infoServer version and info
list-activityList the audit activity log
list-revisionsList item revision history

ai.ollama — 3 tools

Direct access to local Ollama inference. Base: http://127.0.0.1:11434 · Default model: OLLAMA_MODEL env (fallback: qwen-2.5:latest)
ToolDescription
list-modelsList locally available Ollama models
generateSingle-turn completion — prompt, system, temperature, max_tokens
chatMulti-turn chat — messages JSON array, system prompt, temperature
Use generate for single-shot tasks (caption drafting, JSON extraction). Use chat when you need to maintain a message history across turns (multi-step reasoning, iterative refinement).

web.stagehand — 9 tools

Playwright-based browser automation via the Stagehand server. Base: http://127.0.0.1:3002 · Model: STAGEHAND_MODEL env (fallback: ollama/qwen-2.5)
All tools except start-session require a session_id returned by start-session. Sessions are stateful — navigate, act, and extract all operate on the same browser context.
ToolDescription
start-sessionLaunch a headless browser. Returns session_id.
navigateNavigate to a URL
actNatural language browser action (click, type, scroll, select)
extractExtract structured data from the current page
observeList interactive elements on the current page
screenshotTake a screenshot of the current page
get-cookiesGet all cookies from the session
set-cookiesInject cookies (platform session rehydration)
close-sessionEnd and clean up the browser session
The set-cookies / get-cookies pair is the mechanism for platform session rehydration — creators authenticate once via HITL, cookies are encrypted and stored in platform_sessions, then injected into future headless sessions automatically.

media.process — 5 tools

Async media processing via FFmpeg and ImageMagick. Tools that create jobs write a record to media_jobs in Directus; the media-worker BullMQ consumer processes them. Auth: via MCP_SERVICE_TOKEN (Directus) · Async pattern: create job → poll media.job-status
ToolModeDescription
media.validatesyncRun ffprobe on a Directus file — returns format, duration, resolution, bitrate, optional platform compliance check
media.watermarkasyncOverlay watermark onto media → { job_id }
media.clipasyncFFmpeg clip / transcode video → { job_id }
media.thumbnailasyncResize / convert image → { job_id }
media.job-statussyncPoll media_jobs by job_id — returns status and output file_id
Platform specs supported by media.validate: onlyfans · fansly · instagram_feed · instagram_story · tiktok
Typical async flow:
  media.watermark({ file_id, ... })
    → creates media_jobs record, returns { job_id }
  media-worker (polls every 1500ms)
    → picks up job, executes FFmpeg
    → updates media_jobs status to "done"
  media.job-status({ job_id })
    → returns { status: "done", outputs: [{ file_id }] }

taxonomy.core — 7 tools

The 3,205-node adult content taxonomy graph. Graph source: Nodes/Universe/taxonomy_graph.json · Cached in module scope after first load.
ToolDescription
taxonomy.searchSearch graph by label token — ranked by match quality and usage count. Compound tags are indexed both as full terms and tokens: big-ass is found by "ass" or "big".
taxonomy.tag-contentLLM-assisted tagging: Ollama → validate against graph → primary/secondary/tertiary buckets. Output normalized: "big ass""big-ass".
taxonomy.map-termMap an arbitrary term to its canonical graph node
taxonomy.ingest-sourceIngest a new content source into the taxonomy pipeline
taxonomy.rebuild-graphRebuild the full taxonomy graph from source data
taxonomy.pruneRemove low-weight or orphaned nodes
taxonomy.strengthenReinforce a node’s weight based on an engagement signal
taxonomy.tag-content is the hot path for content classification. It calls Ollama internally to suggest tags, then validates each suggestion against the graph before accepting it. This prevents hallucinated tags that don’t exist in the taxonomy.

memory.recall — 4 tools

The JIT skill hydration interface to the DuckDB skill graph (memory/core/agent_memory.duckdb). 191 skills · 252 nodes · 12,880+ edges.
ToolDescription
activate_skillsRun stimulus propagation from a task description — returns top-N relevant skill keys with scores
memory_recallFull retrieval pipeline returning context chunks ready for LLM prompt injection (BM25 + dense + RRF + synaptic + entropy)
get_skillFetch the full content of a skill by name from DuckDB
find_skillsSearch skills by comma-separated tags
// Typical session start sequence
const skills = await mcp.call('activate_skills', {
  query: 'scrape OnlyFans stats and draft a post',
});

for (const skill of skills.activated_skills) {
  const content = await mcp.call('get_skill', { name: skill.name });
  // Inject skill content into agent system prompt
}
get_correlations exists as a surgical_context.py CLI command (python3 memory/core/surgical_context.py get_correlations <skill-name>) but is not exposed as an MCP tool. Use find_skills for tag-based discovery or activate_skills for task-based retrieval.

Plugin convention

All plugins follow a consistent structure under storage/plugins/:
storage/plugins/{plugin-name}/
├── plugin.json    # Manifest: id, name, version, kind, dependencies, config
└── handler.mjs    # ESM. Exports createTools(services) → Array<ToolDef>
The services object passed to createTools provides pre-configured fetch clients:
// lib/services.mjs — passed to every plugin handler
{
  directus: { fetch, baseUrl },  // authenticated with MCP_SERVICE_TOKEN
  ollama:   { fetch, baseUrl },  // http://127.0.0.1:11434
  stagehand: { fetch, baseUrl }, // http://127.0.0.1:3002
}
Plugin directories that contain only handler.js (CommonJS) are skipped by the loader. MCP plugins must use handler.mjs (ESM). The handler.js convention is reserved for AnythingLLM agent skills, which load via a separate pipeline.

Environment variables

VariableDefaultUsed by
DIRECTUS_URLhttp://127.0.0.1:8055services.mjs
MCP_SERVICE_TOKEN— (required)services.mjs — Directus auth
OLLAMA_URLhttp://127.0.0.1:11434services.mjs
OLLAMA_MODELqwen-2.5:latestai-ollama plugin
STAGEHAND_URLhttp://127.0.0.1:3002services.mjs
STAGEHAND_MODELollama/qwen-2.5web-stagehand plugin
PLUGINS_DIRstorage/pluginspluginLoader.mjs

Build docs developers (and LLMs) love