Plugins overview

How plugins auto-detect, load, and add tools at boot.

Plugins are how agent_api adapts to whatever framework runs on your server. Each plugin is a single TypeScript file that exports a Plugin object. At boot the loader walks ALL_PLUGINS, runs each plugin's detect(), and either installs its tools or logs why it skipped.

What lives in src/server/plugins/

plugins/
  index.ts               # ALL_PLUGINS array — append your plugin here
  loader.ts              # detect + install + status logging
  types.ts               # Plugin interface
  helpers.ts             # isResourceStarted, safeExport, callExport
  dynamic.ts             # csvSet, isAllowed, listCallable, safeSerialize
  esx/index.ts
  oxlib/index.ts
  oxmysql/index.ts

Bundled plugins

PluginDetectsPages
esxes_extendedESX plugin
oxlibox_libox_lib plugin
oxmysqloxmysqloxmysql plugin

Boot-time behaviour

When agent_api starts:

[agent_api] plugin enabled : esx (+8 tools: esx_list_players, esx_get_player, esx_add_money, ...)
[agent_api] plugin enabled : oxlib (+5 tools: oxlib_notify, oxlib_trigger_client_callback, ...)
[agent_api] plugin enabled : oxmysql (+3 tools: oxmysql_query, oxmysql_scalar, oxmysql_execute)

A plugin can be skipped because:

  • Its target resource isn't started — detect() returns ok:false.
  • The operator opted out via agent_api_plugin_<name>_enabled false.
  • install() threw.

You can introspect via the list_plugins MCP tool.

Common patterns

Detection

detect: () => isResourceStarted('target_resource'),

Or more specific:

detect: () => {
  const started = isResourceStarted('target_resource');
  if (!started.ok) return started;
  if (!safeExport('target_resource', 'someExport')) {
    return { ok: false, reason: 'target_resource is up but missing someExport' };
  }
  return { ok: true };
},

Readonly + blocklist gate (for reflective tools)

Use isAllowed from dynamic.ts:

const blocklist = csvSet('agent_api_plugin_yourname_blocked_methods');
const guard = isAllowed(methodName, { readonly: convars.readonly, blocklist });
if (!guard.ok) return err('COMMAND_NOT_ALLOWED', guard.reason);

Safe serialization

Framework objects often contain functions, circular refs, or huge nested data. Use safeSerialize from dynamic.ts before returning anything from a reflective call.

return ok({ method, result: safeSerialize(raw) });