WP Manifestindependent plugin directory

Install

Download gds-assistant.zip From the v0.5.0 release on GitHub, Jun 15, 2026

Upload it under Plugins → Add New Plugin → Upload Plugin, then activate.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/generoi/gds-assistant/releases/download/v0.5.0/gds-assistant.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

AI chat assistant built into the WordPress admin. Talk to it in natural language to manage your site — create pages, edit content, run audits, handle translations, and more.

What it does

  • Chat with AI in your admin — a floating chat widget on every admin page. Ask it to do things like "create a draft campaign page" or "find all pages with broken links" and it actually does them using your site's real data.
  • Multiple AI providers — supports Claude (Anthropic), GPT (OpenAI), Gemini (Google), Mistral, Groq, xAI, and DeepSeek. Pick the model that fits the task — cheap and fast for quick queries, powerful for complex operations. Switch mid-conversation.
  • Skills — save reusable prompts as skills (like macros). Create them through the chat or in WP Admin under Tools > AI Skills. Invoke with /skill-name. Each skill can have a preferred model — e.g. use a cheap model for lookups, a smart one for content creation.
  • Conversation history — past chats are saved and searchable. Pick up where you left off. See how much each conversation cost.
  • Cost tracking — live token count and estimated cost displayed as you chat. Price indicators ($-$$$$) next to each model so you know what you're spending.
  • Works with your content — the assistant can list, create, update, and delete posts, pages, products, media, translations, forms, blocks, and more. It sees your actual site structure and uses real WordPress APIs.

Built on assistant-ui for the chat UI, WordPress 7 AI support and Connectors for availability/credential discovery, and the WordPress Abilities API for tool execution.

Requirements

  • PHP >= 8.3
  • WordPress >= 7.0
  • generoi/gds-mcp (provides WordPress tools)
  • At least one AI provider API key configured

Installation

composer install
npm install
npm run build
wp plugin activate gds-assistant

Configuration

Configure provider credentials through WordPress 7 Connectors where available. Legacy environment variables remain supported for the streaming chat providers.

Environment Variables

Set API keys for the providers you want to use, or configure matching WordPress connector credentials. The chat widget only loads if AI support is enabled and at least one provider is configured.

Provider API Keys

Each provider checks WordPress connector credentials first, then legacy env var names:

Provider Env vars (checked in order) Get a key
Anthropic (Claude) GDS_ASSISTANT_ANTHROPIC_KEY, GDS_ASSISTANT_API_KEY, ANTHROPIC_API_KEY console.anthropic.com
OpenAI GDS_ASSISTANT_OPENAI_KEY, OPENAI_API_KEY platform.openai.com
Google Gemini GDS_ASSISTANT_GEMINI_KEY, GOOGLE_AI_API_KEY aistudio.google.com
Mistral GDS_ASSISTANT_MISTRAL_KEY, MISTRAL_API_KEY console.mistral.ai
Groq GDS_ASSISTANT_GROQ_KEY, GROQ_API_KEY console.groq.com
xAI (Grok) GDS_ASSISTANT_XAI_KEY, XAI_API_KEY console.x.ai
DeepSeek GDS_ASSISTANT_DEEPSEEK_KEY, DEEPSEEK_API_KEY platform.deepseek.com

Other Settings

# Optional — override the default provider (first available is used otherwise)
GDS_ASSISTANT_DEFAULT_PROVIDER=anthropic

# Optional — default max output tokens (default: 4096)
GDS_ASSISTANT_MAX_TOKENS=4096

Example .env

# Anthropic (Claude) — primary provider. Prefer Settings > Connectors on WordPress 7.
GDS_ASSISTANT_ANTHROPIC_KEY=sk-ant-api03-...

# OpenAI — secondary provider
GDS_ASSISTANT_OPENAI_KEY=sk-proj-...

# Gemini — cheap option for quick queries
GDS_ASSISTANT_GEMINI_KEY=AIza...

Available Models

Models are grouped by provider in the chat widget dropdown. Only providers with configured API keys appear.

Provider Model Key Label Notes
Anthropic anthropic:haiku Haiku Fast, cheap
anthropic:sonnet Sonnet Best balance (default)
anthropic:opus Opus Most capable
anthropic:haiku-advisor Haiku+Advisor Haiku executor + Opus advisor
anthropic:advisor Sonnet+Advisor Sonnet executor + Opus advisor
OpenAI openai:gpt-4.1-mini GPT-4.1 Mini Fast, affordable
openai:gpt-4.1 GPT-4.1 Strong tool use
openai:o4-mini o4 Mini Reasoning model
Gemini gemini:gemini-flash Flash 2.5 Very cheap
gemini:gemini-pro Pro 2.5 Near-Opus quality
Mistral mistral:mistral-large Large EU-hosted
Groq groq:llama-scout Llama Scout Ultra-fast inference
groq:llama-maverick Llama Maverick Larger Llama model
xAI xai:grok-3 Grok 3 Full model
xai:grok-3-fast Grok 3 Fast Faster variant
DeepSeek deepseek:deepseek-chat DeepSeek Chat Cheapest option

Filters

Filter Default Description
gds-assistant/capability edit_posts Minimum user capability to access the chat
gds-assistant/retention_days 30 Days before conversations are auto-deleted
gds-assistant/max_iterations 25 Maximum agentic loop iterations per message
gds-assistant/rate_limit ['requests' => 20, 'window' => 300] Per-user rate limit
gds-assistant/system_prompt (auto-generated) Customize the system prompt
gds-assistant/tools (all registered) Filter available tools
gds-assistant/tool_risk_level (name/annotation based) Override a tool's risk classification (safe/moderate/dangerous)
gds-assistant/tool_requires_approval (risk based) Per-invocation override for whether a tool call needs user approval. Used to gate irreversible writes — e.g. a gds/forms-update that rewrites the field structure, or any gds/terms-update (no revisions).
gds-assistant/confirm_on_approval ['gds/forms-update'] Abilities whose data-layer destructive guard accepts a confirm_destructive flag; the flag is injected when the user approves the call
gds-assistant/provider (from registry) Override the LLM provider instance
gds-assistant/register_providers — Action to register custom providers
gds-assistant/register_tools — Action to register custom tool providers

Architecture

LLM Providers

Three provider implementations ship out of the box:

  • AnthropicProvider — Claude models with streaming + advisor tool support
  • OpenAiCompatibleProvider — Covers OpenAI, Mistral, Groq, xAI, and DeepSeek (same API format, different base URLs)
  • GeminiProvider — Google Gemini with function calling

The ProviderRegistry auto-discovers available providers from env vars and exposes them to the frontend model selector. Add custom providers via the gds-assistant/register_providers action.

Tool Bridge

Tools are sourced from WordPress Abilities API via AbilitiesToolProvider. The ToolProviderInterface allows registering additional tool sources. Hook into gds-assistant/register_tools to add providers.

Remote MCP servers

The assistant can pull in tools from external Model Context Protocol servers (e.g. Asana, Figma, in-house MCPs). Each configured server's tools appear in the tool list as mcp_{server}__{tool} and route through McpToolProvider.

Configuration — register servers via the gds-assistant/mcp_servers filter or the GDS_ASSISTANT_MCP_SERVERS env var (JSON, same shape):

add_filter('gds-assistant/mcp_servers', function ($servers) {
    $servers['asana'] = [
        'url'   => 'https://mcp.asana.com/sse',
        'label' => 'Asana',
        'auth'  => ['type' => 'oauth', 'scopes' => ['default']],
    ];

    $servers['internal'] = [
        'url'  => 'https://mcp.internal.example/mcp',
        'auth' => ['type' => 'bearer', 'env' => 'INTERNAL_MCP_TOKEN'],
    ];

    return $servers;
});

Supported auth modes:

Type Config Notes
none ['type' => 'none'] Public/unauthenticated MCP
bearer ['type' => 'bearer', 'token' => '...'] or ['env' => 'NAME'] Static API token
oauth ['type' => 'oauth', 'scopes' => [...], 'client_id' => '...'?, 'client_secret' => '...'?] OAuth 2.1 + PKCE. Uses RFC 7591 dynamic client registration when client_id is omitted and the server supports it

OAuth connect flow:

  1. Configure the server via filter/env
  2. Go to AI Assistant → Settings — configured servers appear in the "MCP Servers" section
  3. Click Connect → the plugin discovers the auth server (RFC 9728 / RFC 8414), registers a client if needed, and redirects you to the provider's authorize page
  4. After you approve, the callback at /wp-json/gds-assistant/v1/mcp/{name}/callback exchanges the code for tokens and stores them. Tokens are refreshed transparently on 401.

Server names must match [a-z0-9_]+ (used in the tool-name namespace and callback URL).

Token scope:

  • OAuth servers — tokens are per-user. Each admin connects their own upstream account (Asana, Figma, etc.) so tool calls act on behalf of whoever is chatting. Stored in user_meta with autoload off.
  • Bearer servers — the token comes from config/env, so it's inherently site-wide. All admins share it.
  • Server metadata (auth endpoints, DCR client_id/client_secret) is site-wide in wp_options — one registration per WP install, reused across users.

Encryption at rest: access_token, refresh_token, and any DCR-issued client_secret are encrypted with AES-256-GCM keyed from wp_salt('auth') (HKDF-SHA256). This is defense in depth, not a boundary — an attacker with DB access typically has wp-config.php too — but it stops casual DB dumps/backups/logs from leaking usable upstream-service tokens.

Streaming

The chat endpoint (POST /wp-json/gds-assistant/v1/chat) streams Server-Sent Events (SSE). The entire agentic loop runs server-side in a single SSE connection. No WebSockets required.

Storage

  • Conversations: {prefix}_gds_assistant_conversations — messages, token usage, per-user
  • Audit log: {prefix}_gds_assistant_audit_log — every tool execution with input/result
  • Cleanup: Daily WP-Cron prunes old records based on gds-assistant/retention_days

Skills

Reusable prompt templates stored as a custom post type (assistant_skill). Invoke via /slug in the chat. Manage in WP Admin under Tools > AI Skills, or ask the assistant to create them.

Development

composer install
npm install
npm run build          # Production build
npm run start          # Watch mode
composer lint          # Check PHP code style
composer lint:fix      # Fix PHP code style
npm run lint           # Check JS/CSS code style
npm run lint:fix       # Fix JS/CSS code style

Testing

npx @wordpress/env start
composer test:wp-env
npx @wordpress/env stop

License

MIT

Releases

24 releases. Each count is every asset in that release; expand a row for the breakdown.

Tag
Published
Assets
Downloads
v0.5.0 latest
Jun 15, 2026 4mo ago
gds-assistant.zip
184
May 29, 2026 4mo ago
gds-assistant.zip
228
May 29, 2026 4mo ago
gds-assistant.zip
0
May 29, 2026 4mo ago
gds-assistant.zip
22
May 28, 2026 4mo ago
gds-assistant.zip
2
May 28, 2026 4mo ago
gds-assistant.zip
3
May 27, 2026 4mo ago
gds-assistant.zip
3
Apr 21, 2026 6mo ago
gds-assistant.zip
71
Apr 17, 2026 6mo ago
gds-assistant.zip
14
Apr 17, 2026 6mo ago
gds-assistant.zip
3
Apr 17, 2026 6mo ago
gds-assistant.zip
2
Apr 17, 2026 6mo ago
gds-assistant.zip
2
Apr 17, 2026 6mo ago
gds-assistant.zip
0
Apr 17, 2026 6mo ago
gds-assistant.zip
1
Apr 16, 2026 6mo ago
gds-assistant.zip
5
Apr 16, 2026 6mo ago
gds-assistant.zip
2
Apr 16, 2026 6mo ago
gds-assistant.zip
0
Apr 14, 2026 6mo ago
gds-assistant.zip
2
Apr 14, 2026 6mo ago
gds-assistant.zip
2
Apr 14, 2026 6mo ago
gds-assistant.zip
4
Apr 13, 2026 6mo ago
gds-assistant.zip
4
Apr 13, 2026 6mo ago
gds-assistant.zip
1
Apr 13, 2026 6mo ago
gds-assistant.zip
6
Apr 13, 2026 6mo ago
tag only
—
D+ grade

Security

GDS Assistant 0.1.0 · audited by WP Registry

High-severity findings.

3 high
Audited release
0.1.0
Findings
3
Worst severity
high
Content hash
c0d75bff53e3b4df349ebf69…

Findings

  • high edit_posts users can overwrite any skill and enable admin-privileged scheduled runs

    missing_capability

    assistant__skills-create/update are classified ToolRestrictor-safe (assistant__* prefix), so MessageLoop executes them with no approval prompt. The provider only checks current_user_can(edit_posts) (the same gate as opening chat). createSkill wp_insert_post()s a published assistant_skill and, if schedule is set, update_post_meta(_assistant_schedule) — Contributors cannot publish via core REST, but this path publishes anyway. updateSkill loads any assistant_skill by id with no current_user_can('edit_post', $id), then writes title/slug/prompt/excerpt and the same schedule meta. Plugin.php registers _assistant_schedule with auth_callback requiring manage_options specifically because 'Scheduled skills run unattended with the author's privileges'; that callback is not consulted by update_post_meta. SkillScheduler::executeSkill then runs due skills as post_author when that user still has manage_options. An Author therefore overwrites an admin-authored (or DefaultSkills-installed-while-admin) skill prompt, sets schedule=hourly, and the next cron tick wp_set_current_user()'s the administrator and runs the agentic tool loop with no human on the request. Non-destructive abilities (e.g. gds/content-create, classified safe) execute silently as that admin. The same tool can rewrite slash-command prompts that admins later invoke interactively. MemoryToolProvider similarly lists/deletes any assistant_memory after only edit_posts; DestructiveGuard requires approval for assistant/memory-save but not for REST POST /wp/v2/assistant-memory, so an Author can also publish memory that SystemPrompt::getMemoryEntries() injects into every future conversation.

    src/Bridge/SkillsToolProvider.php:57-184

    Recommendation

    In SkillsToolProvider require current_user_can('edit_post', $id) on update, current_user_can('publish_posts') (or manage_options) before publishing, and current_user_can('manage_options') before writing _assistant_schedule — do not use update_post_meta to bypass the REST auth_callback. Keep SkillScheduler's author re-check. Gate memory create/delete on manage_options or edit_post, and keep DestructiveGuard in front of every memory write path including core REST.

  • high Private skill and memory CPTs are world-readable via WordPress REST

    information_disclosure

    assistant_skill and assistant_memory are registered public=false, show_ui for wp-admin, and show_in_rest=true (rest_base assistant-skills / assistant-memory) with capability_type=post. The settings DataViews for both are manage_options-only, and memory is documented as persistent site knowledge injected into every system prompt. Core WP_REST_Posts_Controller::check_read_permission returns true for post_status=publish without a capability check whenever show_in_rest is true, so unauthenticated GET /wp-json/wp/v2/assistant-memory and /wp-json/wp/v2/assistant-skills list every published item including post_content. That leaks stored facts (emails, IDs, conventions), full skill/prompt templates, and _assistant_model (show_in_rest meta). Authors can also POST published assistant-memory via the same controller, which bypasses DestructiveGuard's approval requirement for assistant/memory-save and poisons SystemPrompt::build() for all users.

    src/Plugin.php:195-250

    Recommendation

    Do not treat publish as world-readable for these types. Use a custom REST controller whose permission_callback requires the assistant capability (or manage_options), or set show_in_rest false and drive the admin DataViews through plugin routes. Filter rest_assistant_skill_query / rest_assistant_memory_query to empty results for users who cannot edit_posts. Restrict create/update/delete to manage_options if memory/skills are site-wide admin state.

  • high Chat image URLs are fetched with file_get_contents after a bypassable https check

    ssrf

    POST /gds-assistant/v1/chat accepts message content blocks of type image/source.type=url. ChatEndpoint::normalizeMessages comments 'validate it's a real URL on our domain' but actually allows any FILTER_VALIDATE_URL whose host equals the site host or whose string starts with https:// — so https://127.0.0.1/, https://metadata.internal/, and https://attacker.example/ all pass. AnthropicProvider::convertUrlImages() then @file_get_contents($url) (PHP HTTP wrapper follows redirects by default) and base64-inlines whatever body came back into the provider payload; GeminiProvider (and VertexExpressProvider) do the same. An Author with the chat nonce can therefore make the WordPress server request RFC1918/link-local/cloud-metadata endpoints, including via an https origin that 302s to http://169.254.169.254/latest/meta-data/, and recover the response through the model/SSE. ImageGenerationToolProvider::loadRemoteImage correctly uses wp_http_validate_url(); this chat path does not.

    src/Llm/AnthropicProvider.php:291-324

    Recommendation

    Reject image URLs unless wp_http_validate_url() succeeds (blocks private/reserved IPs and odd schemes). Fetch only with wp_remote_get (redirection limited, size capped) and require an image Content-Type before inlining. Do not use file_get_contents on request-controlled URLs.

WP Registry hashes the installable build and reports on that exact bytes-for-bytes copy. Embargoed findings are withheld until they are disclosed, so a clean verdict means nothing public is outstanding. WP Manifest does not audit code itself.