← Documentation  /  Developer Guide

Build a plugin

The full SDK reference for plugin developers — sandboxed Python, shipped as a zip, distributed through the marketplace.

These docs are publicly viewable. To publish a plugin you'll need a free MMO Maid account — sign in to access the Dev Portal.

SDK v0.5.1 · Plugins are sandboxed Python processes. Write a handler, declare your capabilities, ship a zip — we run it in a locked-down Docker container and stream Discord events to it over JSON-RPC.

Reference — ctx attributes

AttributeType
ctx.server_idstr — Discord server (guild) ID
ctx.plugin_idstr — your plugin's id
ctx.versionstr — installed version string
ctx.capabilitiesset[str] — capabilities the user approved
ctx.discordDiscord REST surface
ctx.interactionInteraction response surface
ctx.kvPer-server KV store
ctx.sqlSandboxed SQL (capability)
ctx.ephemeralRedis-backed counters / cooldowns / flags
ctx.httpOutbound HTTP via proxy
ctx.metricsTime-series metrics
ctx.log(message, *, level, tags, **extra)Structured plugin log
ctx.has_capability(name)bool — capability check

ctx.discord

MethodCapability
send_message(*, channel_id, content="", embeds=None, components=None, files=None)discord:send_message
edit_message(*, channel_id, message_id, content=None, embeds=None)discord:edit_message
delete_message(*, channel_id, message_id)discord:delete_message
bulk_delete_messages(*, channel_id, message_ids)discord:delete_message
add_reaction(*, channel_id, message_id, emoji)discord:add_reaction
pin_message(*, channel_id, message_id)discord:edit_message
unpin_message(*, channel_id, message_id)discord:edit_message
get_messages(*, channel_id, limit=50, before=None, after=None)discord:read
get_guild()discord:read
get_channel(*, channel_id)discord:read
list_channels()discord:read
get_member(*, user_id)discord:read
list_members(*, role_id=None, limit=100, after=None)discord:read
search_members(query, *, limit=25)discord:read
list_roles()discord:read
create_channel(*, name, channel_type=0, category_id=None, topic=None, user_limit=None)discord:manage_channels
edit_channel(*, channel_id, name=None, topic=None, user_limit=None)discord:manage_channels
delete_channel(*, channel_id)discord:manage_channels
set_channel_permissions(*, channel_id, target_id, allow="0", deny="0", target_type=0)discord:manage_channels
delete_channel_permission(*, channel_id, target_id)discord:manage_channels
create_thread(*, channel_id, name, thread_type=11, auto_archive_duration=1440)discord:manage_channels
edit_thread(*, thread_id, archived=None, locked=None, name=None, auto_archive_duration=None)discord:manage_channels
timeout_member(*, user_id, duration_seconds, reason="")discord:moderate_members
set_nickname(*, user_id, nickname=None)discord:moderate_members
kick_member(*, user_id, reason="")discord:kick_members
ban_member(*, user_id, reason="", delete_message_seconds=0)discord:ban_members
unban_member(*, user_id)discord:ban_members
add_role(*, user_id, role_id, reason="")discord:manage_roles
remove_role(*, user_id, role_id, reason="")discord:manage_roles
add_role_bulk / remove_role_bulk / timeout_bulk / kick_bulk(matches single-action capability)
create_webhook(*, channel_id, name)discord:manage_webhooks
execute_webhook(*, webhook_id, webhook_token, content="", embeds=None, username=None, avatar_url=None)discord:send_message
delete_webhook(*, webhook_id)discord:manage_webhooks

ctx.interaction

MethodUse
respond(*, content="", embeds=None, components=None, ephemeral=False, allowed_mentions=None)First reply (within 3s of receiving the interaction). allowed_mentions mirrors Discord's API field — e.g. {"parse": []} suppresses all pings.
defer(*, ephemeral=False)"I'm working on it" — buys you up to 15 minutes for a followup
followup(*, content="", embeds=None, components=None, ephemeral=False, allowed_mentions=None)Reply after a defer (or send additional messages)
send_modal(*, title, custom_id, fields=None)Open a modal — use as the FIRST response, not after a defer

ctx.kv / ctx.sql / ctx.ephemeral / ctx.http / ctx.metrics

Full method signatures are in the dedicated sections above (KV, SQL, Ephemeral, HTTP, Metrics).

Plugin decorators

DecoratorHandler signature
@plugin.on_install(ctx)
@plugin.on_enable(ctx)
@plugin.on_ready(ctx)
@plugin.on_disable(ctx)
@plugin.on_uninstall(ctx)
@plugin.on_event(event_type)(ctx, event)
@plugin.on_slash_command(name)(ctx, event)
@plugin.on_component(custom_id)(ctx, event)
@plugin.on_modal_submit(custom_id)(ctx, event)
@plugin.schedule(seconds)(ctx)
@plugin.cron(spec)(ctx) — 5-field UTC cron string
@plugin.on_dashboard(method_name)(ctx, params) → dict

Event payload reference

Complete shape with default values produced by mmo_maid_sdk.testing.make_event:

make_event("message_create", content="hi")
# {message_id, channel_id, guild_id, author_id, author_username, author_bot,
#  content, timestamp}

make_event("interaction_create", command_name="ban", custom_id="")
# {interaction_id, interaction_type, guild_id, channel_id, user_id,
#  command_name, custom_id, modal_values}

make_event("reaction_add", emoji="🎉")
# {message_id, channel_id, user_id, emoji, guild_id}

Exceptions reference

See the full table under Error handling.

SDK changelog

v0.5.1

  • Added allowed_mentions parameter on ctx.interaction.respond() and ctx.interaction.followup() — controls which mentions actually ping (e.g. {"parse": []} suppresses all pings).

v0.5.0

  • Added ctx.metrics, ctx.sql, ctx.ephemeral sub-APIs.
  • Added mmo_maid_sdk.testing module with MockContext and make_event.
  • Added lifecycle decorators: on_install, on_enable, on_disable, on_uninstall.
  • Added @plugin.cron(spec) for wall-clock UTC scheduling (5-field cron).
  • Added bulk Discord operations: add_role_bulk, remove_role_bulk, kick_bulk, timeout_bulk.
  • Added typed exception classes (10) for actionable error handling.
  • Security 7 critical + 1 high SDK / sandbox findings fixed (audit, April 2026).

SDK v0.5.1 · Last updated April 2026 · Back to Dev Portal