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.
Architecture fit. The package is shipped as a first-party UnoPim module (packages/Webkul/MCP) rather than an external sidecar, so it registers through a normal Laravel service provider, publishes routes (POST /api/mcp/unopim), and reuses UnoPim's repositories and query builder for catalog reads/writes. That is the lowest-friction shape for a Laravel monorepo: composer require plus one Artisan command, no separate runtime, no Node process, no schema of its own. Its dual transport (SSE over HTTP for remote agents, stdio for local coding agents) maps cleanly to the two real consumers — business users driving ChatGPT/Claude against a PIM, and developers driving Copilot/Cursor against the codebase. v1.0.5 sharpens this split rather than blurring it: the tool surface presented to a client is now conditional on transport and configuration, which is the correct architectural direction.
Integration feasibility. Improved, and materially so for production. composer require unopim/mcp && php artisan mcp:install publishes config/mcp.php, optionally scaffolds Passport, and clears caches; mcp:inspector gives a manual test harness in under an hour. The declared support window from v1.0.2 (Laravel 11–13, UnoPim 1.x–3.x) is unchanged by this release, so the enlarged compatibility matrix remains the maintainer's testing burden and ours to verify. There are no data migrations beyond Passport's, so the change remains additive and reversible by config flag and route removal.
Technical risk. v1.0.5 is a patch release with no breaking changes and — notably — no dependency-major churn, unlike v1.0.2. The most important line in the changelog is not a fix but a fact it reveals: MCP_REMOTE_DEVELOPER_TOOLS exists as a config gate controlling whether developer tools, diagnostics, and dynamic skills are served over HTTP. That was question 1 in the prior assessment and it is now answered in the affirmative, at least at the family level. The same change means the server instructions no longer advertise developer tools to clients that cannot call them, which shrinks the prompt-injection blast radius: a model reasoning over attacker-controlled product descriptions is no longer told that run_command is on the table when it isn't.
Two caveats keep this from being a clean close. First, the gate is described as a single switch over the whole developer-tool family, not a per-tool allowlist — confirm whether individual tools (run_command, run_database_query, dev_tools) can be disabled independently, because a family-level switch forces an all-or-nothing posture. Second, the gate governs serving, and the release notes scope it to HTTP; the stdio path still serves everything by design. That is correct for a laptop and irrelevant to production, but it means the gate must be verified as default-off in the HTTP deployment rather than assumed.
| Risk | Severity | Why |
|---|---|---|
| Command execution surface | High (conditional) | run_command allowlists the base binaries (php artisan, composer) and blocks shell metacharacters, but php artisan tinker and composer run-script remain arbitrary-code paths unless subcommands are denied. Now gated behind MCP_REMOTE_DEVELOPER_TOOLS, but the subcommand policy is still undocumented. |
| stdio bypasses ACL | High | README states CLI bypasses ACL for local development. Any agent or CI job with shell access to the host gets unscoped catalog and DB reach. Unchanged, and correctly so — but it constrains where stdio may run. |
| Dynamic skills as escalation | Medium (reduced) | dev_tools.create_file can write a SKILL.md under .ai/skills/, auto-registered as an executable tool within the 3600s cache TTL. With dev tools off over HTTP the write half of the chain is gone; the risk survives wherever the flag is enabled. |
| Prompt injection via catalog data | High | Product names, descriptions, and attribute labels are attacker-influenced content fed to an agent. v1.0.5 reduces the likelihood of the agent reaching for a dev tool by not describing one, but catalog write tools remain exposed and there is still no documented mitigation. Confused-deputy risk carries forward. |
| Read-only SQL is not low-risk | Medium | A read-only SELECT still reaches users, oauth_clients, pricing, and supplier data; no documented row cap, statement timeout, or table deny-list. Mitigated in production only if the gate is off. |
| Broad-version compatibility claim | Medium | v1.0.5 says nothing about the Laravel 13 / UnoPim 3 claim made in v1.0.2. Confirm the matrix is CI-backed rather than a composer.json constraint bump. |
| Maturity signal | Medium (improving) | Single-vendor, low dependents, MIT. v1.0.5 is a disciplined patch: validation hardening, locale correctness, permission-scoped field addition, N+1 avoidance (latest_execution loaded in one query per page), and instruction hygiene. Three patch releases in roughly a quarter is a healthier cadence than the two-data-point picture we had. |
| Deployment model mismatch | Medium (reduced) | v1.0.2 fixed dropped remote clients; v1.0.5 doesn't touch transport. SSE still fights PHP-FPM/nginx buffering and timeout defaults — proxy_buffering off and raised read timeouts remain mandatory. |
Two smaller points worth recording. get_job_execution now rejects out-of-range ids at validation rather than failing generically on PostgreSQL, and advertises the accepted range in its input schema — that is exactly the kind of schema honesty that stops agents retry-looping on malformed input. And search_jobs.latest_execution is gated on the data_transfer.job_tracker permission, which is a concrete data point supporting the claim that ACL is mapped per tool and per field rather than per route.
Behavior change to absorb: server instructions no longer direct clients to get_catalog_schema first, and now point at search_settings (channels/locales) and search_attributes. Any internal system prompt, runbook, or catalog-manager training material that hardcodes the old discovery sequence must be revised, or agents will follow stale instructions and under-discover. Also note the instruction to filter locales by status = 1 — the unfiltered list includes every shipped locale, which will otherwise produce wrong-locale lookups that look like data bugs.
Key questions to put to the maintainer or the internal owner.
MCP_REMOTE_DEVELOPER_TOOLS gates developer tools, diagnostics, and dynamic skills over HTTP. Remaining: is it family-level or per-tool, is it default-off, and is there an equivalent gate for upsert_*?run_command — is artisan tinker, artisan db:wipe, composer run-script, and composer require explicitly denied, or only the shell operators? Unchanged by this release and still the top unverified item.LIMIT, a statement timeout, and a table deny-list for users/oauth_*/jobs?Stack fit. Composer-native, PHP 8.2+, Laravel 11–13, UnoPim 1.x–3.x, no new infrastructure. It slots into an existing UnoPim deployment as an optional module. The friction points remain operational, not architectural: SSE needs nginx proxy_buffering off, proxy_read_timeout raised well past default, and a load balancer that does not idle-kill streaming responses. v1.0.2 reduced client-drop risk at the application layer; v1.0.5 adds nothing to the edge story, so treat the proxy configuration as unchanged and mandatory.
Migration path. Still additive, not a migration. No data model change to sequence against; the risk is blast radius and gating. The v1.0.5 gate lets us tighten the recommended posture:
mcp:dev, full dev tools. Accept ACL bypass — it is a developer laptop.MCP_REMOTE_DEVELOPER_TOOLS left at its default (confirm default is off), MCP_AUDIT_LOGGING=true, rate limit lowered to force the failure path, and a long-lived session left open for 30+ minutes to prove the keep-alive fix holds through your proxy chain.upsert_* restricted to a named service account, allowed_paths narrowed to a single scratch directory rather than base_path().Compatibility. Pin the package to a minor (~1.0), pin UnoPim and Laravel majors to the ones you actually test, and re-run the inspector suite on every UnoPim minor. Do not adopt Laravel 13 / UnoPim 3 on the strength of a changelog line — v1.0.5 does not reassert that matrix — and keep the previous known-good pairing documented as the fallback. Passport version coupling remains the most likely silent break; test token refresh and 401 behavior on each bump. The good news is that v1.0.5 shows the maintainer can ship a dependency-neutral patch, which makes routine pinning less anxious than the v1.0.2 precedent implied.
Sequencing (with exit criteria).
| Phase | Scope | Exit criteria |
|---|---|---|
| 0. Spike (3–5 days) | Sandbox UnoPim, mcp:install, mcp:inspector, connect one editor via stdio |
All catalog tools callable; MCP_REMOTE_DEVELOPER_TOOLS default and granularity confirmed; dev tools shown absent from the HTTP tool list when disabled; revised instruction set (search_settings/search_attributes discovery) validated with a real agent |
| 1. Read-only (1–2 wks) | HTTP + Passport, expose get_catalog_schema, search_*, get_product, search_jobs, get_job_execution only |
Auth enforced, rate limit verified under load, audit log lands in collector, 30-minute idle session survives the production proxy path, latest_execution correctly suppressed for admins lacking data_transfer.job_tracker |
| 2. Catalog writes (2–3 wks) | upsert_products/categories/attributes behind a service account, with approval gate design |
Rollback runbook for a bad 50-item batch; audit trail reconstructs every write |
| 3. Settings + currency (1 wk) | search_settings, upsert_settings |
Same controls as Phase 2; locale status = 1 filtering verified; channel misconfiguration drill passed |
| 4. Dev tools (non-prod only, or not at all) | dev_tools, run_skill, run_database_query, read_logs |
Subcommand policy verified; tinker/run-script denied; path jail tested against symlinks; flag proven off in the production config artifact |
| 5. Dynamic skills (optional) | .ai/skills/ authoring workflow |
Write access to skills path separated from execute access |
Phase 4 is now genuinely optional rather than a required gate, because the gate exists. It should still not start until Phase 3 sign-off, and the production flag check belongs in Phase 0 and again as a pre-launch assertion in your deployment pipeline — configuration that can silently re-enable a code-execution surface is exactly the kind of thing to verify mechanically rather than by reading a review.
Maintenance. Single-vendor, low-adoption project with a clean MIT license. Budget a named internal owner (platform team, not the PIM team) and roughly 10–15% of one engineer per quarter for dependency bumps and upstream review — the higher end still applies given the three-major Laravel/UnoPim matrix, because each upgrade path multiplies the combinations you must smoke-test. The maintenance signal is now the strongest it has been across the assessments we have: an acknowledged client-disconnect defect fixed promptly in v1.0.2, followed by a disciplined patch at v1.0.5 with no breaking changes, no dependency-major churn, sensible validation hardening, and a permission-scoped field addition that respects existing ACL rather than widening it. The existing Pest suite remains an asset — run it in your own CI against a pinned UnoPim image on every bump. Keep a fork-and-own contingency; at this adoption level, assuming indefinite upstream maintenance is still not a plan, even with improving evidence.
Support. No SLA, no commercial backing indicated. Documentation is above average for a repo at this adoption level, and v1.0.5 improves the self-service surface incidentally: instruction text that no longer renders as a Markdown code block, translated error messages, and an input schema that advertises its accepted id range all reduce the volume of confusing first-contact issues. GitHub issues remain the only escalation path; anything urgent becomes internal engineering work. Continue to pin, and continue to assume a comparable defect could ship in any patch — that assumption is less alarming than it was, not unnecessary.
Scaling. The HTTP path is stateless per request, but SSE holds a connection per active agent for the session duration, and v1.0.2 deliberately extended that lifetime. Size the PHP-FPM pool and nginx worker limits for sustained concurrent sessions rather than bursty ones, and do not put this behind an autoscaler that reaps idle connections. The rate limit is per tool per caller IP (60/min default), so a shared corporate egress IP or a multi-agent orchestrator will contend on a single bucket; plan per-agent tokens or raise the limit deliberately rather than discovering it in production. The 50-item batch cap bounds per-call throughput but also means a 100k-SKU enrichment is 2,000 sequential tool calls — model the wall-clock cost before promising an AI enrichment workflow. Writes are synchronous transactions with no queue, so a chatty agent will hold database connections and can deadlock against concurrent imports. v1.0.5's latest_execution eager-load per page is a small but real efficiency gain for job-monitoring agents, which otherwise poll get_job_execution per job id.
Failure modes to instrument before launch.
.env, an unpropagated config cache, or a deployment that omits the variable silently restores the code-execution surface. Assert on the served tool list at boot, not on the env var's presence.run_command and upsert_* in prod; detect via audit log anomaly alerting. v1.0.5 removes the advertisement of dev tools, not the agent's ability to be manipulated into catalog writes.SKILL.md is invisible for an hour; document this or disable cache in dev.upsert_products means full product payloads in logs. v1.0.5 makes remote HTTP messages respect the authenticated admin's UI locale, so log entries for the same class of failure will now appear in multiple languages; confirm your collector and dashboards normalise rather than fragment. Plan retention and redaction before the first production write.Ramp-up. A Laravel developer reaches a working local demo in 1–2 days; the inspector makes that near-zero-friction. Getting to a production-safe posture — approval gates, egress and proxy config, subcommand policy verification, audit pipeline, sustained-session soak test, and a security review of the dev-tool surface — is realistically 3–4 weeks of engineering plus one week of security review, with the caveat that the developer-tool security review can now be scoped to non-production only. Catalog managers need half a day of prompt-pattern training, and that material needs rewriting for v1.0.5: schema discovery now starts from search_settings (channels, locales filtered by status = 1) and search_attributes, not get_catalog_schema. Developer agents are self-service for anyone already comfortable with Cursor or Claude Code.
Go/no-go. The gating question from the prior assessment — "can we turn off the developer tools in production and prove it" — now has a configuration mechanism behind it, which converts an architectural objection into a verification task. That verification is cheap and belongs in Phase 0, alongside a check of whether the gate is family-level or per-tool. The remaining unresolved items are unchanged and still substantive: run_command subcommand policy, SQL row/table limits, upsert approval, audit redaction, and CI backing for the broad compatibility claim. v1.0.5 does not clear the security bar on its own; it removes the largest single blocker to reaching it.
How can I help you explore Laravel packages today?