jfb-action-messages

Surfaces user-facing custom messages from a JetFormBuilder custom Form Action — both the idiomatic path (register message types via 'jet-form-builder/form-messages/register' so they appear in the form's Messages panel and can be overridden globally per form) and the action-local path (custom message fields inside the action editor, dispatched via Action_Exception for errors or via context + 'jet-form-builder/form-handler/after-send' + Messages_Manager::dynamic_success() for success messages). Use when a custom JFB action needs configurable messages for cases like "already subscribed", "duplicate row skipped", "API rate limited", or per-action success copy. Triggers on mentions of "JFB messages", "Action_Exception", "Base_Action_Messages", "jet-form-builder/form-messages/register", "_jf_messages", "dynamic_success", "add_context_once" with a message, "after-send" hook, or "custom action message".

Best use case

jfb-action-messages is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Surfaces user-facing custom messages from a JetFormBuilder custom Form Action — both the idiomatic path (register message types via 'jet-form-builder/form-messages/register' so they appear in the form's Messages panel and can be overridden globally per form) and the action-local path (custom message fields inside the action editor, dispatched via Action_Exception for errors or via context + 'jet-form-builder/form-handler/after-send' + Messages_Manager::dynamic_success() for success messages). Use when a custom JFB action needs configurable messages for cases like "already subscribed", "duplicate row skipped", "API rate limited", or per-action success copy. Triggers on mentions of "JFB messages", "Action_Exception", "Base_Action_Messages", "jet-form-builder/form-messages/register", "_jf_messages", "dynamic_success", "add_context_once" with a message, "after-send" hook, or "custom action message".

Teams using jfb-action-messages should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/jfb-action-messages/SKILL.md --create-dirs "https://raw.githubusercontent.com/nvdigitalsolutions/mcp-ai-wpoos/main/addons/pro/includes/bundled-skills/jfb-action-messages/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/jfb-action-messages/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How jfb-action-messages Compares

Feature / Agentjfb-action-messagesStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Surfaces user-facing custom messages from a JetFormBuilder custom Form Action — both the idiomatic path (register message types via 'jet-form-builder/form-messages/register' so they appear in the form's Messages panel and can be overridden globally per form) and the action-local path (custom message fields inside the action editor, dispatched via Action_Exception for errors or via context + 'jet-form-builder/form-handler/after-send' + Messages_Manager::dynamic_success() for success messages). Use when a custom JFB action needs configurable messages for cases like "already subscribed", "duplicate row skipped", "API rate limited", or per-action success copy. Triggers on mentions of "JFB messages", "Action_Exception", "Base_Action_Messages", "jet-form-builder/form-messages/register", "_jf_messages", "dynamic_success", "add_context_once" with a message, "after-send" hook, or "custom action message".

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# JetFormBuilder: custom messages from a Form Action

JFB has a centralized form-message system (the `_jf_messages` post meta + `Manager` class). Every form ships with a Messages panel where the admin can override the canonical message keys (`success`, `failed`, `validation_failed`, `invalid_email`, `empty_field`, `internal_error`, etc.). A custom action can hook into this system in two distinct ways, and the right choice depends on **where the admin should configure the text**.

This skill documents both mechanisms, explains when to use which, and shows the exact PHP+JS plumbing for each. Read it together with the **`jfb-form-action`** skill — that one covers the action class itself; this one is the message layer on top.

## API stability note

The form-messages `Manager`, the `'jet-form-builder/form-messages/register'` filter, the `Base_Action_Messages` abstract class, the `Action_Exception` lookup behavior, the `Messages_Manager::dynamic_success()` prefix convention, and the `'jet-form-builder/form-handler/after-send'` hook were all observed in JFB 3.5.x and are stable across the 3.x line. The `plugin-version-tested` value records last end-to-end verification only.

## When to use this skill

- A custom action needs to surface a user-facing message that the admin can configure (success, "already exists", "duplicate skipped", API-error fallback, etc.).
- The diff/files contain `Action_Exception`, `Base_Action_Messages`, `'jet-form-builder/form-messages/register'`, `add_context_once` with a message-like value, or `Messages_Manager::dynamic_success`.
- The user mentions "custom message" together with a JFB action.

## Two mechanisms — pick one per message

| | A. Form-level (registered) | B. Action-local |
|---|---|---|
| **Where the admin configures the text** | Form's "Messages" panel (one place, all forms see the same keys via JFB Messages UI) | The action's own settings inside the action editor |
| **Per-form override** | Yes (built-in via `_jf_messages`) | Yes (each action instance has its own setting) |
| **Per-action-instance override** | No (one form = one set of keys) | Yes |
| **Globalizable across plugins** | Yes (a translation plugin / migration tool can target a known key) | No (text is opaque to outsiders) |
| **Best for** | Errors that match a stable taxonomy (`email_exists`, `username_exists`, `not_authorized`) | Success copy, "duplicate skipped" copy, anything where each instance of the action wants its own wording |
| **Used by** | JFB core `Register_User_Action` (`email_exists`, `username_exists`) | `google-sheet-for-jetformbuilder`, `fluent-subscriptions-for-jetformbuilder` |

You can mix both in one action — register stable error keys via Mechanism A, expose freeform success copy via Mechanism B.

## Architecture in one paragraph

The JFB `Manager` (in `includes/form-messages/manager.php`) holds an associative array of message keys → resolved text, built from two sources at request time: the form's `_jf_messages` post meta (admin-configured per form) and the registered action message types (via the `'jet-form-builder/form-messages/register'` filter). When an action throws `Action_Exception( $code, $message )`, the form handler catches it and asks the `Manager` to resolve the user-visible text. If `$code` is a key the manager knows, the resolved text wins; if `$message` was passed, that string is used as-is (or as a fallback when the key isn't registered). For success cases, an action stores text via `$handler->add_context_once()` and a separate plugin-level handler hooks `'jet-form-builder/form-handler/after-send'` to overwrite the response with `Messages_Manager::dynamic_success( $message )`, which prefixes with `dsuccess|` so the rendering pipeline treats it as inline-supplied content and runs macro / placeholder expansion on it.

## Mechanism A — Register message types (idiomatic)

Use this when your action has a stable, named set of failure modes that admins should be able to translate or rephrase per form.

### Step A.1 — PHP: define a Base_Action_Messages subclass

```php
<?php
namespace MyPlugin\Actions\Messages;

use Jet_Form_Builder\Form_Messages\Actions\Base_Action_Messages;
use Jet_Form_Builder\Actions\Types\Base;

class SubscribeMessages extends Base_Action_Messages {

    public function is_supported( Base $action ): bool {
        return 'myplugin_subscribe' === $action->get_id();
    }

    protected function messages(): array {
        return array(
            'already_subscribed' => array(
                'label' => __( 'Already subscribed', 'myplugin' ),
                'value' => __( 'This email address is already subscribed.', 'myplugin' ),
            ),
            'crm_unreachable' => array(
                'label' => __( 'CRM unreachable', 'myplugin' ),
                'value' => __( 'CRM is temporarily unreachable. Please try again later.', 'myplugin' ),
            ),
            'rate_limited' => array(
                'label' => __( 'Rate limited', 'myplugin' ),
                'value' => __( 'Too many submissions. Please wait a minute.', 'myplugin' ),
            ),
        );
    }
}
```

The `label` is what the admin sees in the form's Messages panel. The `value` is the default text. Each key registered here becomes overridable per form in the JFB UI without any extra work.

### Step A.2 — PHP: register on the filter

```php
add_filter(
    'jet-form-builder/form-messages/register',
    function ( array $registered ): array {
        $registered[] = new \MyPlugin\Actions\Messages\SubscribeMessages();
        return $registered;
    }
);
```

### Step A.3 — PHP: throw `Action_Exception` with the key

```php
public function do_action( array $request, Action_Handler $handler ) {
    $email = $this->get_mapped_value( 'email', $request );

    if ( $this->already_subscribed( $email ) ) {
        // First arg is the registered key; second arg is optional fallback.
        throw new Action_Exception( 'already_subscribed' );
    }

    if ( $this->is_rate_limited() ) {
        throw new Action_Exception( 'rate_limited' );
    }

    try {
        $this->call_remote_api( $email );
    } catch ( \Throwable $e ) {
        // For unknown API errors, register a key OR fall back to a free-form
        // message string. The string path is fine for genuinely opaque cases.
        throw new Action_Exception(
            'crm_unreachable',
            sprintf( __( 'CRM error: %s', 'myplugin' ), wp_strip_all_tags( $e->getMessage() ) )
        );
    }
}
```

When the manager resolves the message, it looks up the first argument. If the admin overrode it in the form's Messages panel, the override wins. If not, your registered default wins. The second argument is the fallback text used only when neither resolution path yields anything.

### Step A.4 — UI behavior

Nothing to do on the JS side. The form editor's Messages panel reads from `_jf_messages` and from the registered action message types — your keys appear automatically in the panel under the action's section.

## Mechanism B — Action-local custom messages

Use this when the message text is genuinely per-instance (each action on the form gets its own copy), and especially when the admin wants the message UI **right next to the rest of the action's settings** rather than in a separate Messages panel.

This is the pattern used by `google-sheet-for-jetformbuilder` (success / duplicate-skip) and `fluent-subscriptions-for-jetformbuilder` (already-subscribed / existing-contact).

### Step B.1 — PHP: declare the message setting in `action_attributes()`

```php
public function action_attributes() {
    return array(
        // ... other settings ...
        'success_message'           => array( 'default' => '' ),
        'already_subscribed_message' => array( 'default' => '' ),
    );
}
```

### Step B.2 — JS: render plain text controls in the action editor

```js
// Inside your action editor component
el( TextareaControl, {
    label:    __( 'Success message (optional)', 'myplugin' ),
    help:     __( 'Leave empty to use the form default. Supports JFB macros.', 'myplugin' ),
    value:    settings.success_message || '',
    onChange: ( v ) => onChangeSetting( v, 'success_message' ),
} );

el( TextControl, {
    label:    __( 'Already-subscribed message (optional)', 'myplugin' ),
    value:    settings.already_subscribed_message || '',
    onChange: ( v ) => onChangeSetting( v, 'already_subscribed_message' ),
} );
```

Plain `TextControl` / `TextareaControl` is the convention — no JFB-shipped "message picker" component is required.

### Step B.3 — PHP: dispatch the message

**For error cases**, throw `Action_Exception` with the user's text directly:

```php
$message = trim( (string) ( $this->settings['already_subscribed_message'] ?? '' ) );
if ( '' === $message ) {
    $message = __( 'This email address is already subscribed.', 'myplugin' );
}
throw new Action_Exception( $message );
```

The user-typed text becomes the response message verbatim (no manager lookup — there's no key).

**For success cases**, use the context + after-send hook pattern:

```php
// In do_action() — store the message in the action context.
$message = trim( (string) ( $this->settings['success_message'] ?? '' ) );
if ( '' !== $message ) {
    $handler->add_context_once(
        $this->get_id(),
        array( 'myplugin_success_message' => $message )
    );
}
// ... action's actual work ...
```

Then, in your plugin's bootstrap (NOT inside the action class — this fires once globally), hook the after-send action:

```php
use Jet_Form_Builder\Form_Messages\Manager as Messages_Manager;

add_action(
    'jet-form-builder/form-handler/after-send',
    function ( $form_handler, bool $is_success ) {
        if ( ! $is_success || empty( $form_handler->action_handler ) ) {
            return;
        }

        $message = $form_handler->action_handler->get_context(
            'myplugin_subscribe',
            'myplugin_success_message'
        );
        if ( ! $message ) {
            return;
        }

        $form_handler->set_response_args(
            array(
                'status'  => Messages_Manager::dynamic_success( $message ),
                'message' => $message,
            )
        );
    },
    10,
    2
);
```

`Messages_Manager::dynamic_success( $msg )` returns the string `'dsuccess|' . $msg`. The form rendering pipeline recognizes this prefix, strips it, and uses the rest as the resolved message — bypassing the lookup that normal `success` would go through. This is how the action wins over the form's default success copy without permanently mutating the form-level configuration.

If your action needs to set a **failure** message via context (rare — usually you'd just throw), there's a parallel `Messages_Manager::dynamic_failed()` helper.

## The `dsuccess|` prefix — what it means

JFB's message resolution distinguishes three forms in `Manager::get_message_by_info()`:

1. **Plain key** (`'success'`, `'failed'`, custom registered keys) → look up in `_types`, return resolved text.
2. **Dynamic prefix** (`'dsuccess|...'`, `'dfailed|...'`) → strip prefix, treat the rest as the message verbatim.
3. **Anything else** → use as-is.

You don't need to construct the prefix manually; use the helper functions. But know it's there because:
- It's what your action's success message looks like in the response payload.
- If you log responses or write tests, you'll see the prefix in the wire data.

## Macros / placeholders inside messages

JFB's macros parser runs at render time on whatever message text the manager returns — both for registered messages and for dynamically-supplied ones. Common macros: `%form_id%`, `%user_id%`, `%post_id%`, `%field_id%` (any form field's submitted value), and many others depending on installed modules.

This means **admin-typed text in a custom message setting CAN include macros**, and they'll be expanded. Document this in the help text of your message field so admins know:

```js
el( TextareaControl, {
    label: __( 'Success message', 'myplugin' ),
    help:  __( 'Supports JFB macros, e.g. %email% or %form_id%.', 'myplugin' ),
    // ...
} );
```

Default text from your code (the `__()` fallback) typically doesn't include macros — keep them admin-driven.

## i18n — what's translatable

| Source | Translatable | Notes |
|---|---|---|
| `Base_Action_Messages` defaults (Mechanism A `value`) | Yes — wrap with `__()` | Standard gettext flow. |
| `Base_Action_Messages` labels (Mechanism A `label`) | Yes — wrap with `__()` | Shown in the form editor. |
| Admin-typed override in form Messages panel | No — stored as user content | If multilingual, use a translation plugin that targets `_jf_messages`. |
| Admin-typed text in action setting (Mechanism B) | No — stored as user content | Same caveat. |
| `__()`-wrapped fallbacks in `do_action()` | Yes | Standard gettext flow. |
| Third-party API error strings | No (typically English) | Wrap with `__()` for the prefix only: `sprintf( __( 'API error: %s' ), $api_msg )`. |

## Critical rules

- **Mechanism A keys are public identifiers** — once admins start overriding `'already_subscribed'` in production forms, renaming it strands their text. Treat keys like API contracts.
- **`is_supported()` MUST be tight** — return true only for your own action's `get_id()`. A loose check (e.g. matching a substring) registers your messages on every action and pollutes the Messages panel.
- **Don't register the same key twice** across multiple `Base_Action_Messages` classes. The last one wins, but the order is implementation-defined and brittle.
- **`Action_Exception` second argument is a fallback, not an override** — if you pass both a known key AND a message, the form-level configured text still wins for that key. Don't rely on the second arg to "force" a specific text.
- **For Mechanism B success messages, hook `after-send` ONCE at plugin bootstrap**, not inside `do_action()`. Hooking inside the action class accumulates duplicate listeners across requests.
- **Don't mutate `_jf_messages` directly from PHP at runtime** to "set a message" — that writes to post meta and persists. Use the dynamic prefix or registered keys.
- **Always trim and length-check user-typed message text** before storing in context — a 50KB message via copy-paste should not blow up the response payload.
- **Both mechanisms support macros**; if the admin has access to fill in PII via macros (e.g. `%email%`), make sure the message context is HTML-escaped at render. JFB's pipeline does this; don't disable it.

## Common pitfalls (failure modes inferred from the API contract)

- **Registered key works in dev, ignored in prod**: the `is_supported()` returns false because `get_id()` was changed. Cross-check the action ID exactly.
- **Custom success message never appears**: `$is_success` is false (the action threw), OR `add_context_once` was called with the wrong action ID, OR the after-send hook isn't registered (registered inside the action class instead of plugin bootstrap).
- **Message panel shows the action's keys but admin's overrides don't take effect**: form is saved against a stale meta cache; check `_jf_messages` post meta directly to verify storage.
- **Macros not expanded**: admin used `{email}` syntax instead of `%email%`. Document the correct delimiters in the help text.
- **`Action_Exception( $message )` (only one arg) treated as a key**: it is — JFB doesn't know which arg is which. If the string isn't a registered key, it's used as the fallback text and rendered verbatim. This is fine, but cluttering: prefer `Action_Exception( 'failed', $message )` for clarity when the text is meant to be the final copy.
- **Two plugins both register `'rate_limited'`**: the second registration wins. Namespace your keys (`'myplugin_rate_limited'`) when there's any risk of overlap.

## Cross-references

- Run **`jfb-form-action`** first — this skill assumes you already have a working action class with `do_action`, `Action_Handler`, and `action_attributes`.
- Run **`wp-i18n-audit`** on the action's PHP + JS message strings — message defaults are the most common spot for translation mistakes (text-domain mismatches, missing `__()`, ambiguous strings used in two contexts).

## What this skill does NOT cover

- The form-level Messages panel UI (built into JFB; not configurable from a plugin).
- Custom message rendering templates (`common/messages.php`) — touching that is theme-territory, out of scope.
- Inline field validation messages (those flow through a different subsystem driven by field block validation rules).
- Multilingual / WPML / Polylang integration of `_jf_messages` content — a translation plugin handles that against the storage; this skill is about producing the keys/text in the first place.
- Browser-side toast / banner styling — entirely controlled by the form's frontend renderer / theme.

## References

- Manager (resolution): `wp-content/plugins/jetformbuilder/includes/form-messages/manager.php`
- Action message manager (registration): `wp-content/plugins/jetformbuilder/includes/form-messages/action-messages-manager.php`
- Base action-messages class: `wp-content/plugins/jetformbuilder/includes/form-messages/actions/base-action-messages.php`
- `_jf_messages` post meta + canonical keys: `wp-content/plugins/jetformbuilder/modules/post-type/meta/messages-meta.php`
- Mechanism A reference (built-in): `wp-content/plugins/jetformbuilder/modules/actions-v2/register-user/messages/register-user-messages.php`
- Mechanism B reference (success path): `wp-content/plugins/google-sheet-for-jetformbuilder/includes/Plugin.php` (`maybe_adjust_response_message`)
- Mechanism B reference (error + success mix): `wp-content/plugins/fluent-subscriptions-for-jetformbuilder-main/src/Actions/FluentCrmSubscribeAction.php`

Related Skills

jfb-form-action

5
from nvdigitalsolutions/mcp-ai-wpoos

Registers a custom JetFormBuilder Form Action — a server-side handler that runs after submit (send to API, subscribe to CRM, write to a sheet, etc.). Covers extending Base action class, declaring settings via action_attributes(), implementing do_action() with Action_Exception error reporting, building the action-editor React panel via window.JetFBActions.addAction(), the two field-mapping patterns (dynamic "Add row" like Google Sheets vs fixed-key pattern like Fluent CRM with predefined target fields), looking up the form's current fields via the useFields() hook, multi-select with FormLabeledTokenField, plus the category/docHref convention. Use when scaffolding a JFB integration plugin (Mailchimp, Slack, custom API, payment processor, CRM subscribe). Triggers on mentions of "JFB action", "jet-form-builder/actions/register", "Base_Action", "JetFBActions.addAction", "Action_Exception", "field map", or "fields_map".

jfb-action-item-decorator

5
from nvdigitalsolutions/mcp-ai-wpoos

Wraps every action item in the JetFormBuilder action editor with custom UI via the 'jet.fb.action.item' wp.hooks filter — the wrapper renders on top of (or alongside) the original action editor for each action and can read/write that action's settings and events array. Use to add quick toggles or panels to every action without modifying each action's own editor — e.g. a TRUE/FALSE/Always button group that drives which custom event the action responds to, a "run once per session" toggle, an inline label override, any visual shortcut over the action's persisted state. Triggers on mentions of "jet.fb.action.item", "useLoopedAction", "useActionsEdit", "useActions", "ActionItemWrapper", "ActionItemBody", "decorate every action", "per-action toggle", or "visual control over action events".

jfb-action-external-api

5
from nvdigitalsolutions/mcp-ai-wpoos

How to read submitted JetFormBuilder form data from a custom action, transform it (including %macro% replacement of admin-typed templates), call an external HTTP API, write the response back into the form context for downstream actions to use, and dispatch JFB events based on the API outcome. Use when building a custom action that integrates with a third-party service (LLM call, webhook, CRM, payment processor, geolocation lookup) and needs the action to behave as a node in a data-flow graph rather than a one-shot leaf. Triggers on mentions of "jet_fb_context", "update_request", "has_field", "get_value", "wp_remote_post" with form data, "macro replacement", or "API call from action".

jfb-action-events

5
from nvdigitalsolutions/mcp-ai-wpoos

Configures the JetFormBuilder action events system — the named events (DEFAULT.PROCESS, DEFAULT.REQUIRED, GATEWAY.SUCCESS, GATEWAY.FAILED, BAD.REQUEST, ON.DYNAMIC_STATE, never) that decide WHEN a Form Action runs. Covers declaring supported / unsupported / required events on a custom action, the validation precedence inside Base_Event::is_valid_action(), the multi-event subscription model the action editor exposes, and how to register a brand-new event type via the 'jet-form-builder/event-types' filter using Base_Event, Base_Action_Event, or Base_Gateway_Event. Use when an action needs to run only on payment callbacks, only on validation failure, only on a specific conditional state, after another action's hidden flow, or when the plugin needs to define its own event class (e.g. for inbound webhooks). Triggers on mentions of "JFB events", "action events", "GATEWAY.SUCCESS", "DEFAULT.PROCESS", "supported_events", "unsupported_events", "get_required_events", "Base_Event", "Base_Gatewa...

wp-action-scheduler

5
from nvdigitalsolutions/mcp-ai-wpoos

Design and review Action Scheduler jobs in WordPress plugins using Action Scheduler 3.9.x public APIs - async, single, recurring, and cron-expression actions; action_scheduler_init load timing; hook/args/group naming; unique and priority parameters; idempotent callbacks; chunked workloads; activation/deactivation cleanup; WooCommerce-bundled or standalone dependency usage; admin and WP-CLI debugging; queue runner limits; and safe operational troubleshooting. Use when a plugin schedules background jobs with as_enqueue_async_action, as_schedule_single_action, as_schedule_recurring_action, as_schedule_cron_action, as_get_scheduled_actions, or integrates with WooCommerce background queues.

webapp-testing

5
from nvdigitalsolutions/mcp-ai-wpoos

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

web-artifacts-builder

5
from nvdigitalsolutions/mcp-ai-wpoos

Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.

valyu

5
from nvdigitalsolutions/mcp-ai-wpoos

Search the live web and 36+ specialised data sources including SEC filings, PubMed, ChEMBL, clinical trials, FRED economic indicators, and patent databases. Use when current, authoritative, or paywalled data is required.

ui-ux-pro-max

5
from nvdigitalsolutions/mcp-ai-wpoos

AI-powered design intelligence with 67 UI styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 15+ tech stacks. Generates complete design systems for any product type with industry-specific reasoning rules.

theme-factory

5
from nvdigitalsolutions/mcp-ai-wpoos

Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.

slack-gif-creator

5
from nvdigitalsolutions/mcp-ai-wpoos

Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."

skill-creator

5
from nvdigitalsolutions/mcp-ai-wpoos

Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.