Skip to content

AI agents ​

An MCP server for Claude, Cursor and other agents: they read and write the site's content with the rights of the user whose key they hold.

kitloom/ai · admins: kitloom/ai-filament, kitloom/ai-nova

Video lessons ​

Recorded in the Filament admin; the Nova admin works the same way.

Clear the site with an AI agent
Fill the site with sample content
Orders with an AI agent
Shop sales with an AI agent
SEO and redirects with an AI agent
Site health with an AI agent

AI agents on a kitloom site — Claude, Cursor or any client of the Model Context Protocol: an MCP server at /mcp whose tools read and write the site's posts, pages, terms, media and settings, find out what lives at an address and show a page as the site renders it. The agent works with the rights of the user whose key it holds. Built on laravel/mcp; the admin section is kitloom/ai-filament or kitloom/ai-nova.

This is your agent, on your subscription, working on the site from the outside. For the site itself to ask a model — a module's "with AI" button, with an API key saved on the site and a monthly budget — install kitloom/ai-writer; the two do not depend on each other.

Turning it on ​

  1. AI agents → Settings: turn on AI agents. Until then /mcp answers 404.
  2. The permission Let AI agents work with my application passwords (use_ai_agents) — administrators have it; give it to another role on the roles screen only if that role should have agents.
  3. A key: AI agents → Connect an agent makes an application password for you and shows it once, with the settings to paste into the agent. Any application password of the REST API works the same way.

A key that writes lets the agent create and change records; a read-only key gets the reading tools only. Revoke a key on the Application Passwords screen and the agent stops at once.

Connecting an agent ​

The address is https://your-site/mcp; the key goes in the Authorization header as Bearer <id>|<secret>.

Claude Code:

bash
claude mcp add --transport http kitloom https://your-site/mcp --header "Authorization: Bearer 12|…"

Cursor (.cursor/mcp.json) and other clients that take a URL with headers:

json
{
    "mcpServers": {
        "kitloom": {
            "url": "https://your-site/mcp",
            "headers": { "Authorization": "Bearer 12|…" }
        }
    }
}

Claude Desktop, through mcp-remote (claude_desktop_config.json):

json
{
    "mcpServers": {
        "kitloom": {
            "command": "npx",
            "args": ["mcp-remote", "https://your-site/mcp", "--header", "Authorization: Bearer 12|…"]
        }
    }
}

claude.ai (on the web, on phones and as Claude Desktop's own connectors): Settings → Connectors → Add custom connector — any name, the URL https://your-site/mcp, Authentication No sign-in, then under Request headers Add header: Authorization, with the value Bearer 12|…. Connect an agent shows the URL and the value ready made with a new key.

A client that sends no headers at all can take the key inside the address instead — the bar of the key written as a dot:

https://your-site/mcp/k/12.…

Such an address is a password: it lands in the web server's logs, in browser histories and in error reports that keep the URL, so use the header wherever the client has one. Make a key of its own for it, with an expiry, and revoke it when it is no longer used — a revoked or expired address answers 404, as one that never was. kitloom-ai.mcp.secret_address false turns the addresses off; signing in with OAuth is not offered yet.

On the dashboard ​

While agents are on, Connect an AI agent sits at the top of the dashboard for a user who may use them and has no key yet, with a button that makes one. A key made ahead with

bash
php artisan ai:prepare-key admin --name="AI agent"

turns it into the settings themselves, ready to copy: claude.ai's URL and header, the Claude Code command, mcp.json and the address for a client without headers, plus a few things to ask the agent first. Add to Cursor and Add to VS Code open the editor with the same server through its install link (cursor://anysphere.cursor-deeplink/mcp/install, vscode:mcp/install); the key travels inside the link, so it is as much a password as the header is. The key is an application password like any other; only this one is kept readable, encrypted with the application key, for its owner's dashboard alone, and it leaves the dashboard when it is revoked. Running the command again revokes the key it made before. A demo does this for every sandbox (see kitloom/sandbox); on a real site, make the key in the admin, where it is shown once.

In Filament the widget comes with the package. Nova has no hook into the host's dashboards, so the host lists the card in its Main:

php
public function cards(): array
{
    return [
        new \Kitloom\Ai\Nova\Cards\AgentCard,
        // …
    ];
}

kitloom-ai.filament.dashboard / kitloom-ai.nova.dashboard false hides it.

What the agent can do ​

ToolWhat
describe_siteThe site, its post types and taxonomies with what they support, the statuses, what the key may do with each type — the agent's first call
list_contentRecords of a post type, every status but the trash: search, status, terms, parent, author, dates, pages
get_contentOne record in full, with its stored HTML
create_contentA new record — a draft unless the agent gives a status — with terms, a featured image and, for types with comments, whether they are open
update_contentChanges to a record: only the fields sent
trash_content, restore_contentInto the trash and back, with the status it had
content_revisions, restore_revisionA record's saved versions — when, by whom, whether an agent made it, what changed — and one brought back; the restore is a version too. A revision an agent's change left is marked _kitloom_agent
list_termsTerms of a taxonomy: search, parent, the terms of a record; or those in the trash
create_term, update_termA new term, changes to one
trash_term, restore_termA term into the trash and back — never the default category
list_mediaThe media library with the address of each file; images without alt text (missing_alt), files nothing seems to use (unused — no post, no featured image, no content names them)
upload_mediaA file into the library — from a public web address, or sent as base64 with its name — with title, alt text, caption
update_mediaTitle, alt text or caption of a file
list_usersUsers by name, for author ids — without emails; without list_users, only authors of published posts, without their logins
list_settingsThe declared settings by group, with values for those who may change them, and whether the agent may change each
update_settingOne setting, checked against its declaration and the user's permission
resolve_urlWhat lives at an address: the kind of page, the record or term, the records of an archive, the templates the theme tries and the one it has
render_pageA page as the site shows it — text a visitor reads, HTML, or the page's parts as JSON (an object, page); a draft as its preview
get_page_schemaThe JSON Schema of that page, or of one part: the parts this site has installed, the theme's own among them

Installed modules add tools of their own, shown only to agents whose user has the module's permission — the menus, the widgets, the head meta, the redirects and the 404 log, the custom fields, the forms and their messages, the comments, the shop, the sliders, the bookings, the backups, the activity log, Site Health, the translations, the broken links, the post types made in the admin, the search index, image compression, imports:

ModuleTools
kitloom/navigationmenus_list, menu_get, menu_save (edit_theme_options)
kitloom/seoseo_get, seo_update — title, description, robots, canonical, Facebook card, primary category of a record or a term
kitloom/widgetswidgets_list, widgets_save (edit_theme_options; a widget left out goes to the inactive ones)
kitloom/redirectsredirects_list, redirects_create, redirects_update, redirects_trash, redirects_settings_get, redirects_settings_update (manage_redirections), not_found_list (manage_404_log), redirect_from_404
kitloom/custom-fieldsfields_get, fields_set — checked as the edit form checks them
kitloom/formsforms_list, submissions_list (read_contact_messages), form_get, form_update, submission_spam (manage_contact_forms)
kitloom/commentscomments_list (edit_posts), comments_moderate, comments_reply (moderate_comments) — comments and product reviews
kitloom/shopproducts_list, product_get, product_save, attributes_list (edit_products; a new product is a draft), product_category_image (edit_product_terms), attribute_save (manage_product_terms), orders_list, order_get, order_set_status, order_add_note, sales_report, top_products, customers_list (edit_shop_orders; a completed or refunded order is not reopened, refunds stay in the admin, notes are internal), coupons_list, coupon_save (edit_shop_coupons)
kitloom/slidersliders_list, slider_get, slider_save (manage_sliders)
kitloom/bookingsbookings_catalog, bookings_slots (read), bookings_list, bookings_create, bookings_decide, bookings_clients, bookings_client, bookings_client_note (manage_bookings; a booking ends declined or cancelled, never deleted; notes are only added)
kitloom/backupbackup_status, backup_run (manage_options; archives are downloaded and deleted in the admin)
kitloom/activity-logactivity_list (view_activity_log)
kitloom/healthhealth_report, health_run (manage_options)
kitloom/languageslanguages_list, languages_get_translations, languages_save_translation, languages_translate (edit_posts; a machine translation is saved as a draft)
kitloom/link-checkslink_checks_list, link_checks_recheck, link_checks_dismiss, link_checks_replace (manage_link_checks)
kitloom/post-typespost_types_list, post_types_get, post_types_create, post_types_update, post_types_trash, post_types_restore (manage_post_types; a definition goes to the trash, its records stay)
kitloom/searchsearch_status, search_reindex (manage_options; the settings are options of update_setting)
kitloom/import-exportimports_list, import_get, import_start (import; a WXR file from a public address, through the media library's guard; no export)
kitloom/media-optimizermedia_optimizer_status (manage_options; the tools are programs on the server, nothing to change)

describe_site names each module's tools and what it tells about the site: which field groups show where, the menu places, what each SEO name means. A module offers a tool by implementing Kitloom\WpSchema\Contracts\AgentTool and tagging it; each module turns its own off with kitloom-<name>.ai.register.

Nothing is deleted for good: permanent deletion is left to a person in the admin. upload_media fetches only from public hosts — redirects included — and, from an address or as base64, takes only file types the library takes and up to its size limit (kitloom-media.max_size, kitloom-media.remote). Answers use plain names (title, status, published_at, terms with their names), not the WordPress REST envelope; dates are site time.

Who sees and writes what ​

An agent with a key sees what its user may see: every record, whatever its status, with edit_others_posts (an editor, an administrator); what is published and the user's own records otherwise (an author). Of the post types, it sees the public ones and those its user may edit: a type without pages of its own, such as the shop's coupons, is for whoever holds its capabilities, as its admin list is. A password-protected record's text is shown only to a user who may edit it, as in the REST API, and a search finds such a record only for them. It writes as the key's user, through the same writers the admins use (PostWriter, TermWriter): the slug is made unique, the dates filled, and whatever the user may not do — publish, edit another author's record, manage terms — is refused with a message the agent reads. The activity log shows an agent's changes under the key's user.

Settings agents leave alone. Protected settings — the site address, the roles, secrets — are never shown to an agent. A few more are locked: the agent sees them without their values and may not change them, because text it reads could try to talk it into it — who gets in (registration, the default role, the admin email, two-factor, social login), the doors agents and apps come through (AI agents, REST API), and the code on every page and the data that leaves the site (Header & Footer, Tag Manager, Analytics, Ad Pixels, Sentry, Backups, the mail server), and SEO's instant indexing, which holds a Google service account's private key. A setting's value is shown only to a user who may change it. A person changes those in the admin; kitloom-ai.settings.locked holds the list.

The address has no session and no CSRF; every request carries its key. Requests are limited per user: kitloom-ai.mcp.rate_limit a minute (120; 0 — no limit).

The local server ​

php artisan mcp:start kitloom serves the same tools over stdio, for an agent on the machine the site runs on — Claude Code in the project:

bash
claude mcp add kitloom -e KITLOOM_AI_USER=admin -- php artisan mcp:start kitloom

It acts as the user KITLOOM_AI_USER names (login or email), who needs use_ai_agents too; with nobody named every tool refuses. It does not depend on the settings switch: whoever runs it has the console anyway.

Configuration ​

php artisan vendor:publish --tag=kitloom-ai-config for kitloom-ai.php: mcp.register, mcp.path (mcp), mcp.rate_limit, mcp.secret_address, local.user, the default and the largest render_page length (render.max_chars, render.max_chars_limit), the settings agents leave alone (settings.locked, * matches any end), and the admin sections (filament.register, nova.register).

Tests ​

From the application that installs the package: php vendor/bin/phpunit --testsuite Ai.

kitloom — modules and themes for Laravel.
Pricing · Services · License · Refunds · Privacy · Community · [email protected]