Skip to content

Platform overview

Plugins are the way to extend Agentful without patching core files. A plugin is installed from a validated repository, staged under .shop/plugins, and activated only after its manifest and runtime contributions match.

Storefront slots

Render plugin UI at named product and layout slots declared in the manifest.

Plugin pages

Add full pages under /plugins/:pluginId/... without modifying the core router.

MCP tools

Expose new AI-admin tools only when the manifest declares the tool and permission.

Events

Subscribe to durable commerce events with at-least-once delivery and idempotent handlers.

API routes

Add protected plugin routes under the plugin namespace.

Data

Create plugin-owned tables using guarded migrations and reserved prefixes.

Core MCP/API tools handle lifecycle:

  • plugins.list
  • plugins.get
  • plugins.health
  • plugins.install
  • plugins.upgrade
  • plugins.enable
  • plugins.disable
  • plugins.uninstall
  • plugins.purge

Uninstall disables runtime behavior and removes staged files while preserving plugin data by default. Destructive data removal requires explicit confirmation.

Agentful blocks common plugin hazards:

  • Non-HTTPS repository sources.
  • Secret-like files in packages.
  • Package install hooks.
  • Local, SSH, or HTTP dependency sources.
  • Manifest paths escaping the plugin package.
  • Runtime contributions not declared in the manifest.
  • Protected core table alteration from plugin migrations.
  • Production build commands unless a controlled build image is configured.

Plugin authors import from @shop/plugin-sdk while the internal package scope is still being renamed.

import { defineShopPlugin } from '@shop/plugin-sdk';
export default defineShopPlugin({
register(context) {
context.mcpTool({
name: 'loyalty.points_preview',
title: 'Preview loyalty points',
description: 'Estimate points for an order.',
permission: 'users:read',
async handler(args) {
return { points: Math.floor(Number(args.priceInCents ?? 0) / 100) };
},
});
context.onEvent('orders.paid', async (event, helpers) => {
// Use helpers.eventId or helpers.deliveryId for idempotency.
console.log(event.type, helpers.pluginId);
});
},
});