Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Mcp Laravel Package

unopim/mcp

UnoPim MCP bridges AI assistants like Claude, Copilot, and Cursor to your UnoPim catalog. Manage products, categories, attributes, channels, and locales via MCP, with dev tools, dynamic skills, and hardened security.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify in under two minutes.

composer require unopim/mcp:^1.0.5
php artisan mcp:install        # Passport scaffolding (if present), publishes config/mcp.php, clears caches
php artisan mcp:inspector unopim-dev

The inspector is the first thing to run — it opens a web UI listing every registered tool, letting you fire search_products, get_database_schema, or read_logs manually before wiring any AI agent to it. If a tool fails here, it will fail in Claude/Copilot too, so this isolates package problems from editor-config problems.

Pin ^1.0.5 and check your version before debugging anything else. The package supports Laravel 11–13 and UnoPim 1.x–3.x; Laravel 13 / UnoPim 3 support landed in 1.0.2 specifically, and 1.0.5 is the first release where the server instructions (the text clients read to decide what to call) are accurate about tool availability and catalog discovery. On older patches the agent may be told to start with get_catalog_schema when it should be reading search_settings / search_attributes — a silent quality regression, not an error. Confirm with composer show unopim/mcp and php artisan --version.

What changed in 1.0.5 that affects setup:

  1. Instructions describing dev_tools, diagnostics, and dynamic skills are now sent only over stdio, or over HTTP when MCP_REMOTE_DEVELOPER_TOOLS is enabled. If you expose the HTTP endpoint and expected the agent to see create_file / execute_* skills, it won't.
  2. Instructions now point at search_settings (channels/locales) and search_attributes (attribute definitions) for catalog structure instead of get_catalog_schema.
  3. search_jobs now returns a latest_execution object per job, removing the separate lookup step.
  4. Instructions are emitted without leading indentation, so clients stop rendering them as a Markdown code block.

Where to look first, in order:

  1. config/mcp.php — this is the file you will actually edit. Auth, rate limits, allowed_paths, audit logging, and skills caching all live here. Published by mcp:install via the mcp-config tag.
  2. docs/tool-reference.md — the authoritative parameter list per tool. The README's tool table is a summary; the reference has defaults and required fields.
  3. packages/Webkul/MCP/src/Registry/ToolRegistry.php — the static map of all core tools and their ACL bindings. Read this whenever bouncer() denies something.
  4. docs/troubleshooting.md — connection/auth failures are the common first-day blocker, followed by "the agent can't see the dev tools" (now almost always MCP_REMOTE_DEVELOPER_TOOLS).

Register the server where you work. For a local coding agent, stdio is the right transport and it exposes the full tool surface by design:

// .vscode/mcp.json (Copilot) — or ~/.windsurf/mcp.json
{
  "servers": {
    "unopim-dev": {
      "command": "php",
      "args": ["artisan", "mcp:start", "unopim-dev"],
      "cwd": "/path/to/your/unopim"
    }
  }
}

For Claude Code it is a single line: claude mcp add unopim-dev -- php artisan mcp:start unopim-dev.

First genuinely useful use case: point the agent at your codebase instead of pasting files. Ask it to "find the most recent import job and tell me why it failed" — on 1.0.5 that routes through search_jobs, which now hands back the latest_execution.id, state, and timestamps in the same response, then get_job_execution for the error detail. No custom tooling required.


Implementation Patterns

Pick the transport deliberately, one per workload — the tool surface now differs per transport.

Workload Transport Why
Editor/coding agent (Copilot, Cursor, Claude Code) stdio — php artisan mcp:start unopim-dev Runs as a child process in the repo; no auth, no network, ACL bypassed for local dev; dev tools, diagnostics, and dynamic skills always advertised
Remote assistant / hosted ChatGPT / PIM operator workflow HTTP — POST /api/mcp/unopim (SSE) Bearer-token auth via auth:api, rate limited, ACL enforced per user; dev tools/skills only advertised with MCP_REMOTE_DEVELOPER_TOOLS=true

Don't mix them. A stdio server embedded in an editor has no user context, so ACL checks are skipped — great for scaffolding plugins, wrong for exposing to a business user's remote assistant. And don't assume a workflow that works in Cursor will behave the same over HTTP: on 1.0.5 the remote client is not even told that dev_tools or execute_* skills exist unless you opt in.

On HTTP, long-lived SSE sessions are the fragile part — 1.0.2 fixed the reconnect bug, but design for drops anyway. A multi-step agent workflow (search_products → get_product → upsert_products) can lose its place and re-issue writes. Make every tool call independently safe to retry: idempotent reads, and upserts keyed by SKU rather than "append what I did last turn."

Discover catalog structure with the right tool — the mapping changed in 1.0.5. Agents that guess field names (price vs. price_amount, status vs. enabled) get empty result sets, so seed every prompt or skill with a discovery step, but use the correct one:

  • search_settings for channels and locales — filter status = 1, because the unfiltered list returns every locale UnoPim ships, not the active ones.
  • search_attributes for attribute definitions (codes, types, options) before writing attribute values.
  • get_catalog_schema for per-entity filterable fields, valid operators, and pagination shape before the first search_products.

A reliable opening sequence:

1. search_settings (type: locales) + (type: channels), status = 1
2. search_attributes (entity: products) — only if you'll write attributes
3. get_catalog_schema (entity: products)
4. search_products (filters from 3, pagination: {limit: 100})
5. get_product (by id or sku) — only for the rows you actually need
6. upsert_products (batches of ≤ 50)

Paginate with cursors, not offsets. Search tools return a cursor; loop until it's null. Max limit is 100 per page. For a 30k-SKU catalog, a naive "fetch everything" prompt will burn tool calls and time out — have the agent report counts, then page only the filtered subset.

Batch upserts are all-or-nothing — plan the batch boundary. upsert_products accepts up to 50 items in one atomic transaction. This is a feature: a partial write to a catalog is worse than a retry. Two consequences for day-to-day use:

  • Validate payloads client-side (or via a dry-run skill) before the call; one bad row rolls back 49 good ones.
  • Chunk 500-row imports into ten 50-row calls rather than one mega-prompt — you get resumability and a clear failure boundary.

Use filter operators instead of pulling data into the LLM. The IN, NOT IN, CONTAINS, STARTS WITH, ENDS WITH, >, < operators push work into the DB. {"field":"sku","operator":"STARTS WITH","value":"PRD"} is one round trip versus the agent asking for everything and filtering in context.

Settings use a type discriminator. search_settings and upsert_settings are generic — pass type: channels or type: locales. Forget the discriminator and you get an ambiguous error. Currencies have their own dedicated pair (search_currencies / upsert_currencies), which is a mild inconsistency in the surface API.

Job monitoring is now a one-hop loop. search_jobs (filter by code, type, entity_type, action) returns each job's latest_execution — id, state, started_at, completed_at, or null if it never ran — loaded in one query per page. So the chain collapses to:

search_jobs → read latest_execution.state / .id from the same response
            → get_job_execution(id: latest_execution.id) for counts and errors

Two limits worth coding around: latest_execution is only returned to admins holding the data_transfer.job_tracker permission, and it is null for scheduled-but-never-run jobs — handle that explicitly rather than letting the agent invent an execution. get_job_execution accepts ids 1..2147483647 only.

dev_tools is one tool with six actions — over stdio, or over HTTP with the flag on. create_file, read_file, update_file, run_command, generate_plugin, generate_test are driven by an action parameter. Useful pattern: pre-generate the plugin skeleton yourself with the Artisan command, then let the agent fill it in with update_file:

php artisan mcp:make plugin Acme/BulkSkuSync --type=connector
php artisan mcp:make test Webkul/BulkSkuSync ProductImporter

The scaffold command and the MCP tool take different namespaces (Acme/BulkSkuSync vs. Webkul/X) — the Artisan form registers into UnoPim's package layout; check the generated path before you let the agent write into it.

Dynamic skills are the extension point worth learning. Drop a SKILL.md with YAML frontmatter into .ai/skills/<name>/SKILL.md and it registers as execute_<name> — no PHP, no service provider, no redeploy. Good candidates: "weekly catalog completeness report," "bulk reassign products to a family," "find SKUs missing translations in a locale." Keep skills narrowly scoped and require explicit parameters (required: true in frontmatter) so the agent can't run them with guessed inputs. On the HTTP transport, skills are only advertised when MCP_REMOTE_DEVELOPER_TOOLS=true — a skill that "disappeared" after moving from stdio to HTTP is a config change, not a broken skill.

Investigate with the read-only DB tools, not logs alone. get_database_schema with no table lists tables; with a table returns columns and relationships. run_database_query executes read-only SQL. Together they let the agent build a correct join the first time — pair them with get_app_info when the question is "which Laravel/PHP version is this running" instead of inferring from composer.json. Diagnostics belong to the same gated group as dev tools, so they follow the same stdio / MCP_REMOTE_DEVELOPER_TOOLS rule.


Gotchas and Tips

MCP_REMOTE_DEVELOPER_TOOLS is the new flag, and it is the first thing to check when an HTTP agent "can't" do something. As of 1.0.5, a remote client is only told about dev_tools, diagnostics, and dynamic skills when this is enabled. Nothing errors — the tools simply aren't in the instruction set, so the model never thinks to call them. Enable it only for trusted operator assistants; a remote workflow that can create_file and run_command is a much larger blast radius than one that can only read the catalog. Because the key is new, an already-published config/mcp.php will not contain it — set the env var rather than hunting for a key that isn't in your file.

api_auth defaults to true and will bite on first HTTP setup. The HTTP endpoint requires a Laravel Passport bearer token. If you haven't installed Passport, mcp:install will skip scaffolding it and every HTTP call returns 401. Either install Passport and issue a token, or set MCP_API_AUTH=false for a local-only endpoint — never both at once in production. APP_URL must be correct or the SSE endpoint self-references the wrong host.

Error messages over HTTP are now localised to the authenticated admin's UI locale. 1.0.5 makes remote requests use the admin's panel locale, falling back to the app default. Great for real users, confusing in support: two admins hitting the same failing tool can paste two different strings, and your log-grep patterns for English messages won't match. Don't assert on message text in tests or alerts — assert on error codes/status, and reproduce in mcp:inspector (which runs with your CLI locale) when comparing reports. get_job_execution returning a translated "no such execution" is the first place this shows up.

get_job_execution validates its id now — stop passing whatever the model guessed. 1.0.5 rejects ids outside 1..2147483647 with a validation error (previously a generic failure on PostgreSQL) and advertises the range in the input schema. The correct value is search_jobs → latest_execution.id; do not pass a job code, a UUID, or 0. If latest_execution is null, the job has never run — that is not an error, and the agent should say so rather than retrying with an invented id.

Filter locales by status = 1 or you'll get noise. The instructions now say so explicitly because the unfiltered search_settings locale list includes every locale UnoPim ships, active or not. An agent building a "translate missing values" skill against the unfiltered list will happily write for locales you never enabled.

SSE disconnects were a real bug, fixed back in 1.0.2 — don't chase them on old patches. If a hosted assistant disconnects after a few minutes of thinking, or a tool result silently never arrives, check the installed version first. As a defensive habit, keep the client's reconnect logic and keep per-call timeouts short so a dropped stream surfaces as a retryable error rather than a hung prompt.

CLI bypasses ACL — don't discover permission bugs in production. Stdio runs as the CLI user with bouncer() skipped. A tool that works perfectly in Cursor may be denied for a restricted PIM admin over HTTP. This now has a second dimension: latest_execution on search_jobs is only returned for admins with data_transfer.job_tracker. Test both the tool set and the enriched fields against a non-admin via the HTTP transport before shipping a workflow.

Rate limiting is per tool per caller, default 60/min. For an agent doing broad catalog exploration this is tighter than it looks — 60 search_products calls is a lot only if you're paginating well. Batch aggressively and prefer one filtered call over several narrow ones. Raise MCP_RATE_LIMIT for HTTP in a trusted network only; the limit is one of the four real security layers. A reconnect loop on a flaky SSE session burns this budget fast.

Path jail is literal and normalized. allowed_paths defaults to base_path() and sys_get_temp_dir(). Anything outside is rejected, and ./.. segments are resolved before comparison. If a skill needs to read from a symlinked directory or a sibling repo, add the resolved absolute path to config — adding the symlink path won't work.

Command whitelisting blocks shell operators, including innocent ones. Only php artisan and composer base commands are allowed, with ;, &, |, `, $(), <, > all rejected. So php artisan migrate && php artisan db:seed fails as one call — split it. Same for composer dump-autoload | grep. Also note that run_command cannot be used to cd somewhere; use the cwd in your editor config or an absolute path inside the whitelisted command. Over HTTP this tool is only reachable with MCP_REMOTE_DEVELOPER_TOOLS=true — keep it that way.

Skills are cached — edit and nothing happens. With MCP_ENABLE_CACHE=true (default) and MCP_CACHE_TTL=3600, a modified SKILL.md won't appear until the cache key mcp.skills is cleared. During active skill development, either set MCP_ENABLE_CACHE=false or make php artisan cache:forget mcp.skills part of your edit loop.

Skill tool names are derived from directory names, not frontmatter name. A directory my-cool-skill registers as execute_my_cool_skill. Rename the directory and every prompt referencing the old tool name breaks silently — the tool simply isn't there. Keep directory names stable and treat them as public API. This compounds with the 1.0.5 gating change: on HTTP, both "wrong name" and "not enabled" present as an absent tool, so check MCP_REMOTE_DEVELOPER_TOOLS before renaming anything.

Config drift after upgrade — 1.0.5 adds a key, so this is a live issue, not a hypothetical. mcp:install publishes via the mcp-config tag; if you already published, new keys added in a release will not appear in your file. Compare against packages/Webkul/MCP/src/Config/mcp.php after every composer update, paying particular attention to MCP_REMOTE_DEVELOPER_TOOLS (new in 1.0.5) and the media block (reserved for upcoming media tools). When a release note describes behaviour that seems absent, suspect an unpublished config key before filing a bug.

Audit logging is on by default and logs every destructive call with user ID, IP, tool name, and arguments. That's exactly what you want for a production catalog — but it means upsert_products arguments (including full product payloads) land in your logs. If that's a GDPR or data-residency problem, disable MCP_AUDIT_LOGGING in that environment and log tool names yourself instead. Don't silently disable it everywhere.

Error semantics come from BaseMcpTool. Every tool extends the abstract base, so failures return a structured error rather than throwing through the transport. When the agent reports a vague failure, reproduce it in mcp:inspector — you'll see the actual message, whereas the agent may be paraphrasing (and, over HTTP, may be paraphrasing a translation).

Requirement floor is Laravel 11–13 / PHP 8.2 / UnoPim 1.x–3.x. On UnoPim 0.x or Laravel 10, the service provider's route and command registration will fail before you get a useful error. A framework upgrade without a package bump is the most likely way to hit this. Confirm with get_app_info (or composer show unopim/mcp plus php artisan --version) rather than assuming.

Version pinning matters here — small package, no downstream consumers to surface regressions. Pin the minor version in composer.json and read CHANGELOG.md before bumping; the tool surface is large enough that a breaking rename in dev_tools actions or skill naming will be non-obvious until an agent workflow silently stops working. The reassuring part: 1.0.5 is a correctness release — instruction accuracy, job-tool ergonomics, locale handling — with no announced breaking changes and no changes to tool signatures beyond the advertised id range. Moving 1.0.2 → 1.0.5 is the safe direction; the behaviour change to watch is the stricter advertisement of dev tools and skills over HTTP, which is a config action, not a code action.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource