skulayer
Preview-first feed optimization for retailers and agencies: audit, fix and serve product feeds.
Should I use this
Quality & Safety
Findings (4)
- HIGH
- MEDIUMin preview_rule
- MEDIUMin get_run_status
- MEDIUMin get_activity
Based on automated analysis of tool definitions and protocol compliance.
Context Cost
This is the approximate number of tokens consumed each time the server's tools are loaded into a model's context. Higher counts reduce the attention available for other tasks.
Install
One-Click Install
Add this to your `claude_desktop_config.json` file:
{
"mcpServers": {
"skulayer": {
"url": "https://skulayer.com/mcp"
}
}
}Remote endpoints
https://skulayer.com/mcpstreamable-httpWhat it can do
Tool inventory
Tools (19)
🟢list_feeds
List every live feed in the workspace: source format, schedule, status, channels, item count, and the latest run summary (including audit issue count). Start here to find feed ids. Archived feeds are not listed: they do not run and do not serve, and only a person can restore one, in the app.
Input Schema
{
"type": "object",
"properties": {},
"additionalProperties": false
}🟢get_feed(feedId)
Full detail for one feed: configuration, latest complete run summary, and the size of its rule stack. Works on an archived feed too, and says so, so an id from an earlier conversation resolves to an explanation rather than a not-found.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟢list_rules(feedId)
The feed's rule stack in execution order: names, enabled state, a plain-English summary, and the full JSON definition of each rule.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟢get_audit_findings(feedId)
Audit findings for the latest complete run, grouped by check with severity, counts, sample item ids, and plain-English guidance on why each finding costs money and how to fix it. The audit measures the DELIVERED feed (after rules). Also returns `opportunities`: attributes left empty whose value was found in another field the merchant DID send (e.g. the brand sitting in the title, matched against brands the same feed already uses), each with exact fill and decline counts, the evidence, and a ready rule definition to pass straight to preview_rule. Opportunities are not defects: they sit outside totalIssues and the readiness score.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟢get_field_inventory(feedId)
What the merchant's source feed ACTUALLY contained, and what SKULayer did with each field: every field as their platform sends it, how many items carry a value for it, whether it was recognised as a shopping attribute, kept as a custom attribute, or arrived empty on every row, plus per-attribute coverage before and after the rule stack. Completes the triad with get_audit_findings (what SKULayer thinks) and get_merchant_issues (what Google thinks): this is what the merchant sent. Use it BEFORE proposing rules. It is the only way to tell an attribute the merchant never sends from one they send blank from one they send with a value we cannot use, all three of which look identical in the audit.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟢get_merchant_issues(feedId)
Google's OWN verdict on a feed, from its Merchant Center connection (the app's What Google sees panel): per-issue-code cases with severity, affected-item counts, sample ids, Google's description and documentation link, and an honest lifecycle state — open, fix_shipped (a rule shipped, Google has not re-read yet), or cleared (Google stopped reporting it). Includes account-level issues and the latest pull's scan summary. Complements get_audit_findings: the audit is what SKULayer thinks, this is what Google thinks. Requires the feed to be connected in the app; fixes are built like any rule — preview_rule then create_rule.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟢get_merchant_link
Read this workspace's Merchant Center link. status is linked, pending, expired, error, or none (nothing started). accounts lists Merchant Center account ids and the connection each one belongs to. connections lists stored rows, including revoked, and which feeds are attached. When status is pending and accounts has more than one entry, the owner has not chosen yet: call select_merchant_account with connectionId, accountId and feedId. Does not return tokens or the consent URL.
Input Schema
{
"type": "object",
"properties": {},
"additionalProperties": false
}🟢search_items(feedId, query, view, limit, offset)
Search or page through the items of the latest complete run. Returns delivered items (what channels receive) by default, with base (pre-rules) available via view. query matches id, title, brand, and product type, case-insensitive.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
},
"query": {
"type": "string",
"description": "Substring match on id, title, brand, or product type. Omit to page through everything."
},
"view": {
"type": "string",
"enum": [
"delivered",
"base"
],
"description": "delivered = after rules (default); base = as ingested."
},
"limit": {
"type": "number",
"description": "Items to return, max 25 (default 10)."
},
"offset": {
"type": "number",
"description": "Scan offset into the run for paging (default 0)."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟢preview_rule(feedId, definition)
Preview an UNSAVED rule against a sample of the latest run: match/change/exclusion counts, per-field change tallies, danger warnings (required fields emptied, identifiers stripped, heavy exclusion), audit deltas, and example before/after diffs. Nothing is written anywhere. Rule definition format: {"when":{"all":[conditions],"any":[conditions],"anyOf":[{"all":[conditions]}]},"then":[actions]} — anyOf is an OR of condition groups, AND-ed with the top-level conditions. Actions: set/copy/replace/append/prepend/lookup/extract/shorten/calculate/round/concat/normalize/strip_html/remove_duplicates (dedupe a separator-delimited list value)/clear/fallback/from_parent/exclude/exclude_group. Condition ops: eq, ne, contains, not_contains, starts_with, not_starts_with, ends_with, not_ends_with, regex, not_regex, gt, lt, gte, lte, between, not_between (take "values": [min,max]), longer_than, shorter_than, in_list, not_in_list (take "values": string[]), in_list_regex, not_in_list_regex (take "values" of regex patterns; match when any pattern matches), date_before, date_after ("today", "today+/-N" or "YYYY-MM-DD"), date_range_active, date_range_inactive (no value; test the ISO range in the field against now), empty, not_empty. Conditions may use "valueField" to compare against another field instead of a literal. Variant groups (items sharing custom:item_group_id): exclude_group {"match":"any"|"all", default "any"} removes the whole family when any/every member matches the conditions and must be the rule's only action; from_parent {"field"} copies the group parent's value onto matching variants (parent = the member whose id equals the group id, else the first member); the condition pseudo-field "group:size" is the family's member count (numeric ops only). Group exclusions are reported as whole groups in the preview warnings.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
},
"definition": {
"type": "object",
"description": "The rule definition JSON to preview."
}
},
"required": [
"feedId",
"definition"
],
"additionalProperties": false
}🟢search_taxonomy(query, limit)
Search Google's product taxonomy — the set of valid googleProductCategory values. Returns ranked category paths with their numeric ids, plus whether the query itself is already a valid category (full path or id). Use it to find the right category for a product type, to fix unmatched entries from map_categories, or to check a value before writing it into a rule.
Input Schema
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Free text (\"sofa bed\"), a full path, or a numeric category id."
},
"limit": {
"type": "number",
"description": "Results to return, max 25 (default 10)."
}
},
"required": [
"query"
],
"additionalProperties": false
}🟢get_run_status(feedId, runId)
One run's progress and outcome: status (queued/fetching/auditing/transforming/compiling/complete/failed), item counts, audit issue count, timing, and the error message when a run failed. Use after trigger_run to poll to completion.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
},
"runId": {
"type": "string",
"description": "Run id from trigger_run or list_feeds."
}
},
"required": [
"feedId",
"runId"
],
"additionalProperties": false
}🟢get_activity(limit, actorTypes)
The workspace's recent activity: who did what and when, across the app and every agent connection. Each event has an actor (a human user, an agent via a token or sign-in, or the system scheduler), the action (rule created/updated/enabled/disabled/deleted, run triggered, Merchant Center link started/completed, Merchant Center account chosen), the feed, and a short summary. Use it to check what changed lately, or what you and others have already done, before acting again. Note that counts in a summary are preview SAMPLE counts, not catalog totals: compare them against the sampled figure alongside them. On a workspace with hourly feeds, scheduled runs dominate the newest events, so pass actorTypes to see human and agent changes.
Input Schema
{
"type": "object",
"properties": {
"limit": {
"type": "number",
"description": "How many events to return, newest first (max 100, default 50)."
},
"actorTypes": {
"type": "array",
"items": {
"type": "string",
"enum": [
"user",
"agent",
"system"
]
},
"description": "Narrow to these actor types. Omit for everything. Pass [\"user\",\"agent\"] to skip scheduled runs, which are high volume on hourly feeds and can fill the window on their own."
}
},
"additionalProperties": false
}🟡map_categories(feedId, useAi, onlyMissing, defaultCategory)
Propose a googleProductCategory mapping for a feed: groups the latest run's delivered items by distinct productType, matches each onto Google's taxonomy (exact-path and unique-leaf matches first, then candidate-constrained AI when available and useAi is not false), and returns the mapping table WITH a ready-to-save rule definition (one lookup keyed on productType). NOTHING is saved: show the human the mapping, resolve unmatched product types with search_taxonomy and edit the rule's table, then save with create_rule — which runs the standard preview and returns the impact report. Options: onlyMissing=true scopes the rule to items with no category yet; defaultCategory (a valid taxonomy path) is applied to items whose productType is not in the table.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
},
"useAi": {
"type": "boolean",
"description": "Use AI for product types with no deterministic taxonomy match (default true). AI mapping is rate-limited. When it is not available, call again with useAi=false for deterministic matches."
},
"onlyMissing": {
"type": "boolean",
"description": "Scope the suggested rule to items with an empty googleProductCategory (default false: the reviewed mapping also fixes wrong existing values)."
},
"defaultCategory": {
"type": "string",
"description": "Optional valid taxonomy path applied when an item's productType is not in the table."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟡create_rule(feedId, name, definition)
Create a rule on a feed. The definition is validated, the standard preview runs server-side, and the report comes back WITH the created rule: match/change/exclusion counts, per-field tallies, warnings, and audit deltas. Show the human this report; danger-level warnings mean the rule can empty required fields or exclude heavily. The rule lands enabled, appended after existing rules, and applies on the NEXT run (never to the live delivered feed mid-flight). Same definition format as preview_rule.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
},
"name": {
"type": "string",
"description": "Human-readable rule name, 2 to 120 characters."
},
"definition": {
"type": "object",
"description": "The rule definition JSON (same format as preview_rule)."
}
},
"required": [
"feedId",
"name",
"definition"
],
"additionalProperties": false
}🟡set_rule_enabled(feedId, ruleId, enabled)
Enable or disable one rule by id. Disabling takes a rule out of the stack from the next run onward; nothing else about it changes. Use list_rules to find rule ids.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
},
"ruleId": {
"type": "string",
"description": "Rule id from list_rules."
},
"enabled": {
"type": "boolean",
"description": "true = in the stack, false = skipped."
}
},
"required": [
"feedId",
"ruleId",
"enabled"
],
"additionalProperties": false
}🔴delete_rule(feedId, ruleId)
Delete one rule by id. This is permanent; prefer set_rule_enabled with enabled=false when the human might want the rule back. Takes effect on the next run.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
},
"ruleId": {
"type": "string",
"description": "Rule id from list_rules."
}
},
"required": [
"feedId",
"ruleId"
],
"additionalProperties": false
}🔴trigger_run(feedId)
Start a feed run: fetch the source, apply rules, audit the delivered output, and compile channel files. This tool needs a write-scoped connection. The workspace SKU limit, the manual-run cap, and one run at a time per feed also apply. Returns a runId. Poll get_run_status until it completes. An in-flight run never affects the live delivered feed.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed id from list_feeds."
}
},
"required": [
"feedId"
],
"additionalProperties": false
}🟡start_merchant_link(feedId)
Start connecting Google Merchant Center for this workspace. Returns a short-lived, single-use SKULayer URL the workspace owner must open while signed in as the same person. That page sends them to Google consent and, when Google shows one account, links it. You never receive Google tokens. Optional feedId attaches that feed when there is exactly one account. If there are several, poll get_merchant_link and call select_merchant_account. Owner only. At most 5 starts per workspace per hour, on top of the normal MCP rate limit. Does not add a data source and does not write to Merchant Center.
Input Schema
{
"type": "object",
"properties": {
"feedId": {
"type": "string",
"description": "Feed to attach when Google shows exactly one Merchant Center account. Omit to connect the account without attaching a feed yet."
}
},
"additionalProperties": false
}🟡select_merchant_account(connectionId, accountId, feedId)
Choose which Merchant Center account a feed uses. Required when get_merchant_link lists more than one account. Pass connectionId and accountId from that result, plus feedId. The account must be one Google actually showed for this connection. Owner only. Does not write a feed or a data source to Google.
Input Schema
{
"type": "object",
"properties": {
"connectionId": {
"type": "string",
"description": "connectionId from get_merchant_link."
},
"accountId": {
"type": "string",
"description": "Merchant Center account id from get_merchant_link."
},
"feedId": {
"type": "string",
"description": "Feed that should read this Merchant Center account."
}
},
"required": [
"connectionId",
"accountId",
"feedId"
],
"additionalProperties": false
}Community
Evidence