Layers Growth MCP
One MCP server that onboards your product, installs Layers measurement, and connects Meta to launch ads, from your repository. Setting it up is one paste into your coding agent, which installs the Layers CLI and runs setup for you. Prices, resources, and skills are on this page.
Layers Growth MCP is one MCP server that a coding agent uses to run paid growth for
your product on the Layers platform. First run is the ten-step paste below.
Onboarding saves your product's brief and binds the repository to one Layers
organization and project. Layers measurement is the Layers SDK in your app:
install_growth_measurement returns the dependency, the app id and the init
snippet, and its verify action reports when the first real event arrives. Then
the person states a daily ad cap, which set_spend_cap records, and opens the
signed Meta connect link on layers://workspace. The agent launches the first
Meta campaign with launch_ads, and it calls one named outcome per job.
launch_ads launches in one call. It checks the app's daily spend cap first,
runs Meta's validate-only check on every object, creates the campaign, ad sets
and ads paused, reads each ad's live text, link and Page back from Meta, and
turns them on only when every read-back matches. An app with a store listing
gets an App Promotion campaign for installs, a product sold on its website gets
a Sales campaign for purchases, and a waitlist or sign-up site gets a Leads
campaign on its lead event, such as Lead or CompleteRegistration. halt with
the project or organization scope pauses every Meta campaign a launch built,
and the kill switch stops every turn-on. Trial cancellation is a
preview-then-confirm call.
An Instagram account connects, satisfies onboarding, syncs, and is measured,
and its own posts can be remixed. Discovery, delivery, and managed accounts run
on TikTok today. No paid lane is certified yet: plan_paid_campaign refuses
every platform until the Paid Growth lane ledger records a live-certified row
for that lane.
Install
Set up Layers for this product: onboard it, install Layers measurement, then connect Meta and launch ads.
1. Install the CLI: run `npm install -g @layers/cli`.
2. Check that the README opens with a plain sentence saying what this product is, and write one if it does not. Setup sends that single line as the product brief, and it stops with exit code 1 when it cannot find one.
3. Set it up: run `layers setup` from this repository. It prints one line, "Send this to the person: <link> (code <code>)". Send me that link and code; I tap the link, check the code matches, and tap Allow. It also opens a browser on this machine when there is one, and either way finishes the sign-in. The link works for 15 minutes, and running `layers setup` again after a restart resumes the same link. It writes the `.mcp.json` entry and the Layers skill into the repo and stores the session in the OS credential store, so nothing secret lands in the repo. On a machine with no OS credential store, such as a Linux cloud computer, run `layers setup --allow-file-credentials` instead, which keeps the session in a private 0600 file. If it asks for the product's public URL, which is its website, its App Store page, or its Google Play page, pass that question to me. Setup also onboards the product: Layers saves the README line and the public URL as this product's brief.
4. Check the organization: when setup creates the project, its summary names the organization the project is in and says how to put the project in another one. Send me that organization line. If I name another organization, follow those instructions.
5. Read the skill at `.agents/skills/layers/SKILL.md`. If this session cannot load the Layers MCP server, as in a headless run, call a tool with `layers call <tool> '<json>'` and read a resource with `layers call layers://<resource>`; both reach the same tools and resources.
6. Install Layers measurement: call install_growth_measurement with action spec. For an Expo, React Native or Flutter app, send platforms with that framework, such as ["expo"]; the response lists the platforms the spec covers. When the product has a website to promote, send webDomain set to its host name, such as example.com. It returns the SDK dependency, the app id and the init snippet for this app. Add them to the app, run the app once on a simulator, a device or in a browser, then call install_growth_measurement with action verify until it answers reporting.
7. Set the daily cap before any launch: ask me "What is the most you want to spend on ads per day, in US dollars?" and set my answer with set_spend_cap. The cap is a 7-day average, so one day over it is fine.
8. Connect Meta: read layers://workspace. The meta-ads row of paidReadiness.providers carries a signed connect link. Send me that link; I open it and choose the ad account, the Facebook Page the ads run as and, for an app with a store listing, its Meta app. Every Meta ad runs as a Facebook Page. If I have none, the page links to where I create one; when I tell you the Page is ready, send me a new connect link. Wait until that row reads configured.
9. Make the ads: write the primary text and headline and make the images for each market, in that market's language and sized for Meta placements. Save each image inside this repository: launch_ads takes it as media with source local_file and the file's path, and the layers CLI uploads it. An image already at an https address goes as source image_url with that url. Show me the copy and the images before you launch.
10. Launch: call launch_ads with action launch, a dailyBudget within the cap in the ad account's currency, currency, markets and the creatives. An app with a live App Store or Google Play listing gets an App Promotion campaign for installs. A website product, such as a web app, a SaaS, a store or an info product, gets a campaign whose ads link to the website and optimize the conversion its Meta Pixel received in the last 28 days: a Sales campaign for Purchase, else a Leads campaign for a lead event such as Lead or CompleteRegistration from a waitlist or sign-up form. An app with no store listing yet launches only through its website, or waits for the listing. launch_ads returns each blocker with its fix; do the fix and call it again. While Meta reviews the ads, the launch answers status running; call launch_ads with action status and its jobId until it finishes.
Let me know when it is ready.Paste that into Claude Code, Codex, Cursor, or any coding agent working in your repository: the agent runs the commands and the tool calls. A person taps the sign-in link, answers the daily ad cap question, and opens the Meta connect link.
Setup prints a sign-in link and code for the person to open, and opens a browser
when the machine has one. It stores the session in the operating system
credential store, or with --allow-file-credentials in a private 0600 file on
the machine, so no token, key, or header is written into the repository or into
your agent's configuration. layers mcp and
layers call are the credential's only readers.
MCP clients read .mcp.json when they start, so the entry setup writes reaches
the client on its next start or after /mcp. In the session where setup just
ran, layers call <tool> calls the same tools over the same sign-in. Claude
Code holds a new repository server pending until you approve that entry once;
setup reports project_config_pending_approval and names the exact entry to
approve.
Manual setup
If you would rather run the commands in a shell:
npm install -g @layers/cli
layers setupWhat setup writes
| Path | Contents | Secret |
|---|---|---|
.layers/project.json | public project ID, public organization ID, environment, issuer, setup version | No |
.mcp.json | { "command": "layers", "args": ["mcp"] } merged under server name layers | No |
.agents/skills/layers/SKILL.md | thin pointer to the server-owned skill index | No |
.claude/skills/layers | relative symlink to the canonical skill where symlinks are supported | No |
Every file above is safe to commit. Setup is idempotent: running it again
repairs Layers-owned entries, leaves every other MCP server entry byte for byte
as it was, and creates no second project. For a client that does not read the
repository .mcp.json, setup registers the server through that client's own
command, such as codex mcp add layers -- layers mcp.
The tools
Twenty-seven outcomes make up the catalog. Prices are in credits and every
charge is quoted before the work runs. The Credits column lists each charged
action; anything not listed there is free. tools/list in your session is the
authority on what your agent can call: a tool is there because it ships, and a
tool Layers withdraws is deleted from the catalog by a release rather than
switched off under you.
Three profiles, and a project has to have a profile enabled before its tools admit a call. Growth is the default.
Growth
| Tool | What it does | Credits |
|---|---|---|
select_project | Lists your usable projects and current selection, or binds this sign-in to a project so later requests need no selector headers. A person with one project is bound to it on the first request, and tools/list lists every tool before a project is bound. Available to signed-in people | 0 |
create_app | Creates a Layers app from a name and platform: ios needs iosBundleId, android needs androidPackage, and both needs both identifiers. Returns its App ID and project. Existing apps sharing an identifier must match every requested identifier; exact matches return the oldest app with created: false, and missing or different identifiers are refused. Needs a signed-in organization owner or admin. Works before a project is bound; pass organizationId for an organization where you are an owner or admin | 0 |
list_apps | Lists your organization's live native Layers apps with their App IDs, iOS bundle IDs, Android packages, last event times and ingest (accepted, or revoked after revoke_app_key), one page of 50 at a time. Defaults to the bound organization, or the organization joined first before binding. An MMA API key needs mma:read and sees only apps in its organization and project scope. Works before a project is bound | 0 |
get_app | Reads one app by exactly one of appId or projectId: its identities, store links, store listings, ingest (accepted, or revoked after revoke_app_key) and SDK issuance and revocation state. A signed-in person can read any app they can access. An MMA API key needs mma:read and can read only apps in its organization and project scope. Works before a project is bound | 0 |
revoke_app_key | Revokes an app's public App ID, chosen by exactly one of appId or projectId, so Layers stops accepting its SDK events. Event capture refuses the App ID within about two minutes, for builds already shipped too. Events it admitted before that reach every store. Tracking-link clicks, SKAN postbacks and revenue webhooks are separate inputs that a revoke does not stop. Stored events and the app record are kept. The app's TikTok and Google conversion destinations are disabled. Turning the SDK off in the app directory keeps the App ID accepted. This tool refuses it at ingest. A repeat call returns the first revocation time with alreadyRevoked: true. Needs a signed-in organization owner or admin with a project of the app's organization bound. An MMA API key cannot call it | 0 |
onboard_product | Binds the product, collects the facts no source can supply, writes its brief and store identity, and answers ready on the same call. It needs no social account and starts no background jobs | 0 |
manage_growth_landscape | Pins, removes, or rescopes the tracked accounts, and requests an explicit refresh | 6 forced incremental refresh; 6 full rebuild |
find_growth_opportunities | Returns the strongest current tests with source evidence, effort, and prior use | 0 read; 2 per creative preview produced |
plan_content_experiment | Turns one opportunity into an editable plan with one controlled variable, a target account, a measurement window, and a success gate | 6 new plan or structural revision |
generate_influencer | Creates one persona for the project from a gender, an age range, and an optional one-line look, and returns its id and a link to the portrait slate straight away while the portrait is drawn | 0 |
add_brand_asset | Puts one of the project's own product assets into Layers from a public https URL, so a render has something of yours to composite | 0 |
render_content | Renders reviewable content from an approved plan revision or straight from a brief with no plan. One output per call | 25 static image; 85 slideshow; 200 video; 400 extended video |
deliver_content_experiment | Prepares a rights-cleared artifact, takes artifact-level approval, and delivers it to a TikTok draft inbox or posts it | 5 per delivered target |
track_growth_experiment | Registers a post you published yourself as an experiment with its hypothesis and source lineage | 0 |
review_growth_results | Reads the evidence, records the verdict, and names the next controlled test | 1 forced provider read |
install_growth_measurement | Puts the Layers SDK in this product's own repository and confirms it reports, so installs and revenue resolve back to the post that produced them | 0 |
generate_tracking_link | Mints a named tracking link as a stored row: a label and tags for your own reports, the five UTM fields for the ad network, a payload the app receives on first launch, and a destination that is either the app store or a web address with the click id appended | 0 |
archive_tracking_link | Takes one link out of the working inventory, and puts it back. The URL keeps resolving and the clicks keep recording either way | 0 |
configure_growth_measurement | Sets the App Store URL, the Play Store URL, the fallback URL and the clipboard switch on the product's measurement app, which is the fix for the tracking link's only refusal | 0 |
manage_bio_page | Publishes a link-in-bio page at <slug>.layers.click made of tracking links, states the buttons on it, edits the five tokens it renders with, or takes it down | 0 |
configure_conversions_forwarding | Points one ad platform's conversions destination at the product, says which conversion it counts and which traffic reaches it. It takes no access key: when the destination has none on file the result hands back a signed page a person opens to paste one | 0 |
decline_next_step | Records the person's answer to an offer layers://next made: declined, or not before a date they name. One row per offer, and a later answer overwrites it | 0 |
research_tiktok_creator | Reads one TikTok creator by handle: followers, posts, bio, bio link, verified and private flags, avatar, profile URL, and the date the account was created | 1 per call |
research_tiktok_creator_posts | Reads one creator's recent posts, newest first, with views, likes, comments, shares, saves, length, cover and sound | 1 per call |
research_tiktok_search | Searches TikTok for public posts on a topic, with the same numbers plus the poster's handle and follower count | 1 per call |
research_tiktok_hashtag | Reads one hashtag: post count, total views, average views per post, its momentum against its own history, and recent posts | 1 per call |
research_tiktok_trending_sounds | Reads the TikTok sound chart as it stands: title, artist, length, post count, whether anybody sings on it, a preview URL, and cover art | 1 per call |
halt | Stops queued and running work in scope and returns a receipt per target | 0 |
cancel_trial_auto_renewal | Previews and confirms cancellation of the trial renewal | 0 |
send_feedback | Sends a message to the Layers founders, who read every one. It works for a workspace that has run out of credits or has billing paused | 0 |
A creative edit changes the script, opening, caption, or tags, and it is free however many times you make it. A structural revision changes the source, hypothesis, controlled variable, format, target account, or schedule, and it costs the same 6 credits as a new plan because the server recomputes the plan. Every revision is recorded and supersedes the previous one.
One render_content call starts one generation and returns one job, so it
charges one rate. Rendering more is calling again. An unusable output is
refunded. Reading stored results costs nothing, and a forced refresh that
causes no provider read creates no debit.
Paid media
Ad spend is your own money at the provider. It never consumes credits and never hides inside a credit quote, and every campaign runs under a spend authority a human granted.
The daily spend cap is the number you state for your app, set through your
agent with set_spend_cap. It is a 7-day average: one day over is fine.
create_approved_campaign_draft, launch_approved_campaign and
apply_approved_campaign_change are off tools/list and still callable by name.
| Tool | What it does | Credits |
|---|---|---|
set_spend_cap | Sets or changes the bound app's daily spend cap in USD and returns it with the 7-day-average rule and the app's current 7-day average. Needs a signed-in organization owner or admin with the app's project bound; an MMA API key cannot call it | 0 |
plan_paid_campaign | Produces a mutation-free campaign plan from a proven source or a bounded demand brief, echoing back the caps and stop rules you stated | 0 |
start_experiment | Starts an experiment from the post the Content Planner already picked: you send the daily budget, Layers supplies the post, the network and the schedule, and it answers with the same plan | 0 |
resume_paid_setup | Continues the plan a setup handoff belongs to: send the handle the handoff carried, and Layers re-reads the provider and returns the same plan | 0 |
create_approved_campaign_draft | Builds the non-delivering TikTok campaign drafts one exact plan describes | 10 per campaign that reads back complete |
launch_ads | Launches Meta ads for the bound app or website in one call: one App Promotion campaign optimizing for installs for an app with a store listing, or, for a website product, one campaign optimizing the event the project's Meta dataset received in the last 28 days: a Sales campaign for Purchase, else a Leads campaign for Lead, CompleteRegistration, SubmitApplication or Schedule. conversionEvent picks another event the dataset received. Each gets one ad set per market with the budget split equally, and one ad per creative in every market whose language it carries. The app's daily spend cap is checked first, every object passes Meta's validate-only check and is created paused, each ad's live copy is read back and must match, then everything turns on. Blockers (no Meta ad account, no Page, no store link, no Meta dataset for a website, a dataset that received none of those events, the cap) come back with their fix. A long launch returns running and a status call with its jobId. A signed-in person only | 0; Meta bills the ad spend to your own ad account |
launch_approved_campaign | Previews or confirms delivery for one campaign, and is also how a paused campaign starts delivering again | 0; the provider bills the ad spend to your own ad account |
apply_approved_campaign_change | Previews or confirms one bounded change to one campaign: move the daily total, or delete the campaign at the provider | 5 on confirm |
create_campaign | Creates one campaign on the project's connected Meta ad account, PAUSED, with an optional campaign budget. The engine checks the app's daily spend cap, runs Meta's validate-only check, creates it paused and reads it back. Refused until granular intents ship: under the current Meta safety rules a new campaign launches through launch_ads | 0 |
create_ad_set | Creates one ad set, PAUSED, under a campaign the project holds: its targeting, budget, optimization goal, bid, schedule and promoted app or pixel. Same engine steps as create_campaign. Refused until granular intents ship: a new campaign launches through launch_ads | 0 |
upload_creative | Puts one image or video from an https URL into the ad account's media library and returns Meta's imageHash or videoId, recorded in the project's creative library | 0 |
create_ad | Creates one ad, PAUSED, from an uploaded image or video with your primary text, headline, description and link, on the project's bound Facebook Page and its Instagram account. Meta's live copy, link and Page are read back and must match | 0 |
change_ad_object | Changes one campaign, ad set or ad: status, budget, schedule, bid, name or targeting. Status ACTIVE is the only change that starts delivery, after the kill switch, a halt, the read-back block for an MMA API key and the app's daily spend cap are checked, and Meta's state is read back | 0; Meta bills the ad spend to your own ad account |
read_ad_objects | Reads the stored campaigns, ad sets, ads and creatives with each one's read-back state: matches, mismatch with the fields that disagreed, or not read | 0 |
The six granular tools take a network field, meta today, with each
network's own fields as its arm of the input. A create is always paused. A
project with no connected Meta ad account answers PROVIDER_NOT_READY with a
connect link, and a write that outlives the call answers pending; the same
call with the same idempotencyKey reads the outcome.
set_daily_budget moves one campaign's daily total. delete_campaign removes
the campaign at the provider, including one that is delivering right now, and a
deleted campaign cannot be resumed. Both preview for free, both cost 5 credits
on confirm, and both report success only after the provider reads the change
back.
Managed distribution
A Layers-operated fleet is billed in dollars from the organization's cash wallet, at $150 per account per month. That is separate money from credits.
| Tool | What it does | Credits |
|---|---|---|
plan_managed_distribution | Quotes a fleet of Layers-operated TikTok accounts. Quoting reserves nothing and holds nothing | 0 |
provision_approved_managed_accounts | Orders the exact fleet one plan quoted. This is the call the cash wallet is charged on, at the monthly rate above | 0 |
release_managed_accounts | Previews or confirms giving managed accounts back, with what money comes back and what the current period keeps | 0 |
MMA (Mobile Measurement Agent)
A studio that measures its app in MMA gets the same
numbers in its coding agent. Run npm i -g @layers/cli && layers setup in the
app's repository. Setup binds the repository to one organization and one
project; in a studio with several apps, layers setup --project prj_<id> names
the app. The agent then reads the app's MMA screens as resources (listed under
Free resources) and records the product experiments it runs,
which show on MMA's Experiments page. Every read and write uses your own
sign-in and MMA's own rule: studio membership and access to the app.
Approving a recommendation stays a click in MMA.
An agent that runs unattended (a scheduled Claude Code or Codex run, a GitHub
Action) signs in with an MMA API key instead. Create one on MMA's API Keys page
with Read MMA over MCP and the app it reads, then send it as
Authorization: Bearer <key> together with the x-layers-organization-id
(org_<uuid>) and x-layers-project-id (prj_<uuid>) headers. For that app
the key reads the layers://mma resources, list_mma_experiments,
get_mma_experiment, list_mma_reviews, get_mma_ratings,
get_mma_cohorts, get_mma_retention, get_mma_insights and
get_mma_pockets, and marks its own inbox read with mark_mma_inbox_read.
get_mma_studio_brief and run_events_sql also need only Read MMA over
MCP (mma:read). get_mma_studio_brief takes no input. An app-scoped key
receives only its bound app's brief, without studio-wide decisions or alerts;
an org-wide key or a signed-in person receives every app in the studio. The result states scope as app
or studio, and mmaUrl opens the studio Brief page.
Each write tool below needs one more permission on the key: Manage
experiments (experiments:write) for register_mma_experiment and
update_mma_experiment, Request decisions (decisions:write) for
file_mma_decision and withdraw_mma_decision, and Write Shipyard records
(shipyard:write) for the Shipyard tools. Prototype gates are set by a person;
a key proposes a change with file_mma_decision.
Each wake-up starts at layers://mma/inbox: what changed for the app since the
agent last marked it read (experiment reads due, answers to its asks, spend
against the daily cap, broken signals, next steps). Reading it marks nothing;
mark_mma_inbox_read with the read's readAt does. The layers-status prompt
runs the whole check-in, and hosts that do not show MCP prompts use the
layers-status skill.
| Tool | What it does | Credits |
|---|---|---|
record_mma_shipyard | Records one Shipyard record: a playtest (vendor, study, testers planned and done, cost, status, build, scores, "play tomorrow" counts), a survey (questions, shown and answered counts, answer counts by option), a release (version, platform, track, rollout, store review state) or a dated change on an experiment. An optional externalKey makes a repeat update the same record (upserted: true); a release updated this way also dates each rollout or track change. An MMA API key needs shipyard:write | 0 |
update_mma_shipyard | Changes a playtest, a survey or a logged experiment change by id; a release is append-only, so record a new one instead. An MMA API key needs shipyard:write | 0 |
create_mma_attachment_upload | Requests an upload for one screenshot, short video or PDF on an experiment or Shipyard record, and returns a signed PUT URL with the exact headers to send. The agent PUTs the bytes itself, then finalizes. Images up to 30 MB, video 200 MB, PDF 10 MB. An optional externalKey makes a repeat return the same attachment. An MMA API key needs shipyard:write | 0 |
finalize_mma_attachment | Checks the uploaded file's size and type against the request and publishes it with a short-lived signed read URL. A key finalizes only its own uploads. An MMA API key needs shipyard:write | 0 |
exclude_mma_install | Takes one QA or test install out of the app's numbers: send the installId the SDK sent from the test device and a reason. MMA records who excluded it and why, marks the install's stored and future events in MMA as test data, and stops counting it in installs, sessions, cohorts, retention and SDK revenue. It has no undo. A repeat returns the stored record with alreadyExcluded: true; an install MMA has never counted, or whose first day is more than 35 days old, is refused. An MMA API key needs shipyard:write | 0 |
set_mma_prototype_gates | Replaces the app's prototype gates: each gate's criteria on playtest scores, read against the newest done playtest recorded for it. A signed-in person only: an MMA API key is refused and files an MMA decision with file_mma_decision to propose a change | 0 |
list_mma_experiments | Lists the app's experiments in MMA, newest first, filtered by status, with a cursor to page | 0 |
get_mma_experiment | Reads one experiment by its mexp_ id or by the key it was registered under, with MMA's per-arm read: installs exposed per arm, conversions on the primary metric with a 95% interval, spend and cost per conversion, network-claimed installs and the proof rate (proven installs ÷ installs the network reports), the leading arm and whether its interval separates it, and readDue once the read date passes | 0 |
register_mma_experiment | Records a new experiment: key, title, hypothesis, arms with traffic shares and the mapping MMA reads them by (SDK event property values or landing pages, and the campaigns or ad sets whose spend counts), primary metric, decision rule, guardrails, planned read date, a link, and status. A key already on file returns that experiment unchanged | 0 |
update_mma_experiment | Moves an experiment's status (planned, running, paused, reading, decided, stopped) and records its result: winner, effect, confidence interval, units per arm, and a note | 0 |
list_mma_reviews | Lists the bound app's deduped store reviews, newest first. Filter by store, inclusive minRating and maxRating (1 to 5), version and since (ISO date-time with offset or UTC day). limit is 1 to 50, default 25; send nextCursor back as cursor to page. App Store reviews come from RSS and App Store Connect; Google Play reviews come from the Play API. A review in another language also carries titleEn and bodyEn (its English translation), detectedLanguage (ISO 639-1) and translationModel | 0 |
get_mma_ratings | Reads daily displayed ratings and rating counts aggregated over storefronts for each store, with per-version statistics, release markers and model definitions. days is 7 to 365, default 30. App Store version statistics count ratings received while the version was current; Google Play version statistics count written reviews on the version | 0 |
get_mma_studio_brief | Reads the morning brief: yesterday and trailing seven-day spend against caps, proven installs, revenue by source, ratings, alerts, experiment reads due, open decisions, releases and day-over-day changes. Takes no input. People and org-wide keys read the studio; app-scoped keys read only the bound app. Every figure carries its n or source, with low-n marked below 30 observations. mmaUrl opens the Brief page | 0 |
run_events_sql | Runs one read-only SELECT or WITH statement over the studio's apps in ClickHouse. Read layers://events/sql-guide first. Use events.seat_events, events.seat_events_final for exact counts and events.seat_events_by_install for per-install timelines; these views drop identifier and contact property paths. A query has a 60-second limit and a 100,000-row server cap. maxRows is 1 to 10,000, default 1,000, and truncated is true when more rows exist. The result carries column names and types, rows, the query id, elapsed time, and rows and bytes read. organizationId defaults to the bound organization, and only studio organizations can run it. An MMA API key needs mma:read | 0 |
get_mma_cohorts | Reads install-day cohorts for the bound app: installs per install day and in total, proven installs with their network and attribution method, organic installs, the installs the ad networks claim, and returners on day 1, 3, 7, 14 and 30. from and to are install days (at most 92) in the app reporting timezone. Optional filters: campaignId, adSetId, network, country, platform and appVersion. Emulators, store test devices and excluded installs are absent, as on every MMA page. A day N that has not ended yet reports mature: false with null counts. Totals carry each rate's 95% Wilson interval and say whether every install day has reached day N. basis names what the installs hold (proven, proven_and_organic or organic); claimed installs stay a separate figure. Send nextCursor back as cursor for the following install days | 0 |
get_mma_retention | Reads the bound app's daily retention baseline, discovered dimensions and selected slices, with D1, D3, D7, D14 and D30 returners, heavy-player share, D7 revenue intervals and attributable cost. dimension defaults to builtin:all; use builtin:<key>, property:<key>, day0_event:<key> or cross:<key> to select slices. computedOn selects a snapshot date and defaults to latest; sdkAppId selects among the returned sdkAppIds. Each metric uses 28 fully aged local install days. Rates carry 95% Wilson intervals; D1 and D7 differences use Newcombe against the rest of the app, with Holm correction across the app's slice tests that day. lowN flags fewer than 100 eligible D7 installs (D1 when the app has no eligible D7 installs), or a missing D1 interval or one wider than 10 percentage points. Low-n slices produce no insights. Cost is present only where the spend report carries the slice's keys; unavailable cost or currencies that cannot be combined give null | 0 |
get_mma_insights | Reads stored pocket findings, slice outliers, trend shifts and experiment observations, newest day first, then score, with sample sizes, values and intervals. scope defaults to app; studio includes only authorized apps. Optional sdkAppId and computedOn filter the feed; limit is 1 to 30, default 20. Send nextCursor back as cursor with the same filters. Slice findings use 95% Wilson rates, Newcombe differences against the rest of the app in the same 28 fully aged local install days, and Holm-corrected D1 and D7 tests. Low-n slices produce no findings. Pocket cost appears only where the spend report carries its keys. The findings describe associations | 0 |
get_mma_pockets | Reads healthy acquisition pockets, weak cut candidates and behavioral product pockets from each authorized app's latest daily snapshot. scope defaults to app; studio includes only authorized apps. Optional filters: sdkAppId, computedOn and verdict (healthy_pocket, weak_pocket or product_pocket). limit is 1 to 30, default 20; send nextCursor back as cursor with the same filters. Rows carry retention with 95% Wilson intervals, D1 and D7 Newcombe differences against the rest of the app and Holm-corrected tests, heavy-player share and D7 revenue with a seeded bootstrap interval. Low-n slices receive no pocket verdict. Pockets rank by retained players per day, then cost per D7 player within each source currency. Spend, CPI and cost per D7 player are present only where the spend report carries the pocket's keys; unavailable cost or currencies that cannot be combined give null, not zero. Pockets overlap and describe associations, so do not sum them or infer causation | 0 |
file_mma_decision | Asks a studio member a question on MMA's Decisions page: title, ask, recommendation, options (default approve and deny), cost in US dollars and a due date. It opens pending; the answer arrives on layers://mma/decisions and in the inbox, with chosenOption verbatim. The same title again while your first ask is pending returns that ask with created: false. An ask filed with an MMA API key carries the key's name. An MMA API key needs decisions:write | 0 |
withdraw_mma_decision | Takes a pending decision back by its mdec_ id or bare uuid. An answered decision keeps its answer and is refused; a repeat returns it with alreadyWithdrawn: true. You withdraw only the asks you filed. An MMA API key needs decisions:write | 0 |
mark_mma_inbox_read | Marks the agent's MMA inbox read up to the readAt of the read it acted on; the marker is the agent's own and only moves forward | 0 |
What onboarding sends
Your agent reads the repository on your machine and submits a short product
brief: name, one-line description, category, target users, the problem, the
conversion goal, seed keywords, known social handles, and store identities.
Layers never receives your source code. No file, path, diff, or excerpt is sent.
The request carries the brief fields, one digest, and a closed list of evidence
classes such as package_manifest and readme.
Onboarding writes the brief and the store identity and finishes on the same
call. It needs no social account and starts no background jobs. The calls after
it are create_app, which registers the app's iOS bundle id or Android package
and issues its App ID on the bound project, and install_growth_measurement,
which puts the Layers SDK in the app and confirms it reports.
A project onboarded from an App Store or Google Play listing also gets its mobile app layer, its store listing record and its bundle id at the same moment, which is what lights the App Store Optimization surface in the app.
It creates no schedule, campaign, managed account, standing automation, provider mutation, or charge.
Free resources
Resources read stored state. They are free, they never trigger a provider read or a model call, and each one reports when its data was observed and which tool refreshes it.
| Resource | What it holds |
|---|---|
layers://next | the one move this project is at and the rows that follow it: a tool to call now with complete arguments and its price, a link to hand a person, a purchase that funds the move already chosen, or a wait with the time to come back. Beside it, the presence tier the decision table branched on and the inputs that decided it, every row of the table with what happened to it, and the answers already given to an offer. The same step rides every tool result in the envelope's next field, without the rows that follow |
layers://account | identity, plan or trial, credit balances, reserved credits, profile availability, active offers, and a human billing link. layers://account/credit-events pages every balance change |
layers://workspace | product brief, growth goal, connected accounts, measurement readiness, and posting policy |
layers://landscape | ranked tracked accounts, pins, exclusions, scope, freshness, and source evidence |
layers://opportunities | the standing ranked queue, with full source evidence per opportunity |
layers://experiments | experiment summaries, the current projection per experiment, and the append-only event ledger |
layers://influencers | the project's personas, each with the look it was generated from, its portrait, and whether it is still being drawn, plus slateUrl, a page you can hand to a person who wants to see the faces |
layers://brand-assets | the project's own product media a render composes: app screenshots, demo videos and end cards, each with a stable id render_content accepts |
layers://music | the curated tracks a render can be scored with: title, artist, length, whether anybody sings on it, and a preview URL to play a person |
layers://measurement | what the project spent beside what Layers recognized over one window: ad spend per platform with how much of it is settled, recognized revenue per channel, the frozen D7, D30 and D90 cohort figures, and installs as observed, proven and claimed, with the arbitration between them. It names every figure that is unknown rather than zero |
layers://jobs | active and recent jobs with state, receipts, errors, and recovery steps |
layers://artifacts/<id> | one rendered artifact by the cnt_ id its job named: whether it is ready, whether it is video, slideshow or image, the addresses to look at, the measured bytes with dimensions and duration, and the job and experiment it came out of |
layers://tracking-links | every link this project has minted, newest first, with its label, its tags, its UTM fields and what it did over the last 30 days: clicks, store taps, installs attributed to one of its clicks, and the revenue that followed. Filter by tag and by the surface that minted it; read one at layers://tracking-links/<id> |
layers://clicks/<click id> | one tap: when it happened, the platform it routed to, the link it came from, its UTM fields, the install it produced with the match method and confidence tier, and the revenue that followed |
layers://events | the product events the SDK reported over a window: every event name with its count, its distinct people, when it was first and last seen, and a country rollup. Never a single event row |
layers://revenue | what the product earned from its own billing: a daily series per source, the monthly and annual run rate, subscribers, trials, churn and lifetime value, the trial forecast, and each source stated as ready, degraded or unavailable |
layers://attribution | which installs Layers could tie to a click and by what evidence: installs by method and rung, the resolve funnel with its five outcomes, the SKAdNetwork postbacks received, decoded and relayed on, and the residual with its causes |
layers://sdk-health | whether the SDK in the field is reporting at all: the versions in use with the installs behind each, the newest beacon and event, the events accepted, delivered and dropped with reasons, and whether the build is behind the current release |
layers://conversions | where the product's conversions are forwarded, per ad platform: whether it is on, which destination is bound and what class it is, what it counts, the delivery counts over 30 days (including conversions unsent because the access token was refused), whether it needs a reconnect, and the circuit breaker. Never an access key |
layers://bio-page | the hosted page at <slug>.layers.click: whether it is published, the icon, name and tagline it leads with, the five tokens it renders with, its buttons in order with the tracking link behind each one and what that link did, and how many people and how many crawlers opened the page each day |
layers://mma/overview | the app's MMA Overview figures over a window (?from=YYYY-MM-DD&to=YYYY-MM-DD, default the last 30 days): spend, installs, paid installs, CPI, D1 and D7 retention, trials, new payers, gross and net revenue with the split by store (App Store, Google Play, web), MRR, cohort ROAS from D7 to D180, payback day, and a daily series, each beside the previous window. Installs are first recorded SDK production installs on the UTC install day; D1 and D7 retention are the share of each install day's installs with any production event exactly 1 or 7 UTC days later. Ad revenue is not ingested and the body says so |
layers://mma/retention | the install-day retention matrix from MMA's Cohorts screen (up to 31 install days): each cohort's size and, for day 0 through 30, day 60 and day 90, the share active on exactly that UTC day and the share active on it or later. A null cell is a day that has not ended |
layers://mma/spend | spend by campaign per ad network over a window: spend, impressions, clicks, the network's claimed installs, the installs Layers proved to the campaign, trials, payers, revenue from those installs, CPI, and revenue over spend, with organic installs beside them |
layers://mma/cohorts | the Cohorts screen's read over install days you choose (?from&to, default 30 days): installs per install day, revenue and conversions at each age, spend and projection ratios, from which ROAS, LTV per install and payback follow |
layers://mma/funnels | the saved funnels, newest first, up to 10, each computed over a window (?from&to, default 7 days, at most 31) |
layers://mma/events | the event taxonomy over a window: each production event name with its counts, where it is forwarded, and whether it is live, quiet or missing |
layers://mma/realtime | the Realtime screen: active devices, events per minute, installs and revenue today, and the last 30 minutes |
layers://mma/deliveries | conversion deliveries per ad network destination over a window, with health and the delivery log (?cursor pages) |
layers://mma/matches | installs matched to an ad click, store referrer or URL parameter, newest first, with the match rate (?cursor pages) |
layers://mma/activity | the change log: every change to the app's ad accounts and campaigns and who made it (?cursor pages) |
layers://mma/decisions | the app's decisions from MMA's Decisions list (?cursor pages) |
layers://mma/shipyard | the app's Shipyard: the newest playtests, surveys, releases and changes logged on its experiments, each prototype gate with its verdict and the first gate not yet passed, and playtest spend by vendor |
layers://mma/data-notes | dated notes on changes in how MMA derives its numbers (?cursor pages) |
layers://mma/portfolio | the app's Portfolio row: today, 7-day and 30-day figures and the alerts |
layers://mma/ladder | the app's row of the viability ladder, as the Ladder section on its MMA Overview shows it: the stage, every playbook gate with its value, benchmark, the app's own target and a verdict, and the keep, iterate or kill suggestion with the rule behind it |
layers://mma/recommendations | what MMA is asking a person to act on: the ad optimizer's open approval queue with each proposed change, its rationale and projected outcome, and the measurement-health problems (an ad sign-in in error or expiring, spend with no installs, installs from a network whose spend is not connected, figures that stopped refreshing). Each row carries a link to the MMA screen where a person approves, dismisses or fixes it |
layers://skills/v1 | the skill index and each skill body |
A client without MCP resource support gets one extra read-only tool,
read_layers_state, which returns the same data and makes the same
authorization decision. It reaches every resource above except
layers://campaigns, so a paid-media projection needs a resource-capable
client.
Skills
The server owns the skill bodies and setup installs one thin pointer at
.agents/skills/layers/SKILL.md. The pointer names the server, the project
binding rule, and the pinned major version. Skills carry judgment about when to
use an outcome and how to read the result. They hold no credentials, no prices,
and no tool of their own.
| Skill | Profile | Job |
|---|---|---|
niche-map | growth | Build and curate the standing map of direct, adjacent, and inspirational accounts |
what-to-post | growth | Take one opportunity through a plan, an optional render, approval, and delivery |
growth-review | growth | Read early or final evidence, state the uncertainty, and pick the next controlled test |
outcome-log | growth | Register work you posted yourself and keep the hypothesis, result, and next decision together |
paid-growth | paid media | Promote an owned post under exact spend authority |
managed-distribution | managed distribution | Plan, approve, monitor, and release a disclosed managed fleet |
Pricing
Credits pay for the work the agent asks for. One organization holds one credit balance, and the same operation costs the same number of credits on every plan.
| Plan | Price | Credits |
|---|---|---|
| Pay as you go | $0 per month | none included |
| Pro monthly | $49 per month | 5,000 per month |
| Pro annual | $468 per year, $39 per month | 5,000 each month |
| Ultra monthly | $199 per month | 25,000 per month |
| Ultra annual | $1,908 per year, $159 per month | 25,000 each month |
Pay as you go costs nothing a month and includes no credits. It needs a card, and credits are bought in packs at 2 cents each. It is the way in for a workspace that wants to pay for exactly the work it asks for.
The trial is the first week: 7 days, $9 today, a card required, and 1,000 credits. The first landscape build is included, so the trial can reach opportunities without spending them. The trial converts to Pro monthly at $49 unless it is cancelled first, with the $9 credited toward the first month.
Subscriptions auto-renew until a human cancels them. Your agent can cancel the
trial renewal for you with cancel_trial_auto_renewal: the first call previews
the trial end, the effective date, what you keep, and what you lose, and returns
a link. Cancellation itself takes your click on that Layers page. No MCP tool
can complete a checkout, start a plan, or change a renewal on its own.
One-time packs add credits to the same balance and do not expire. There are two sets of prices, and a workspace buys from the one its plan puts it on.
| Pack | Credits | On a plan | On pay as you go |
|---|---|---|---|
| Crawl | 1,000 | $9 | $20 |
| Walk | 4,000 | $29 | $80 |
| Run | 10,000 | $69 | $200 |
| Scale | 20,000 | $129 | $400 |
layers://account reports which set applies to you, at
creditPacks.eligibility.observed.packAudience, and every pack price it lists is
the price your card is charged. A checkout link for the other set is refused.
Ad spend and managed-account fees are separate money. They never consume credits and never hide inside a credit quote.
Available today
Verified against the server source on 2026-09-02. tools/list in your session
is the authority: every tool in the tables above is in the catalog, and a bound
project can call it.
Every profile is on for a bound project, so paid media and managed distribution need no separate enablement, and neither adds a network-prefixed or provider-prefixed tool.
A few of the growth outcomes carry detail the table has no room for.
manage_growth_landscapecharges only for a refresh. A full rebuild costs the same six credits as a forced incremental one and runs the same discovery crawl.find_growth_opportunitiesreads the stored queue. The default read starts no vendor call, no model call, and no charge.includeCreativePreview: "generate"makes the creative analysis for the returned cards that have none yet, at two credits for each preview actually produced and nothing for a card it could not analyze; each one is cached, so a later read of the same card is free.quoteOnlyreturns the same cards and that price without producing anything.generate_influenceris free.layers://influencersis where the persona is read back once its portrait lands, andslateUrlon both surfaces opens a page of the project's portraits for a person to look at.render_contentrenders one output a call. A remix reads video or slideshow off the post it adapts and charges the matching rate;layers://brand-assetsandlayers://musicare where the screens and the track it can be directed with are read.- The five
research_tiktok_*reads each cost one credit a call whatever the row count, charged on the call.research_tiktok_trending_soundsandlayers://musicanswer different questions: the chart is what TikTok is playing, and the resource is what a render may legally be scored with. review_growth_resultsreads stored evidence for nothing and pays one credit only for a forced provider sync.haltcovers one job, one experiment, one social account, one paid campaign, one managed account, or the whole project. On a campaign it pauses provider delivery and withdraws the approvals that campaign holds.
Two earlier surfaces were retired on 2026-08-30 with no bridge and no
compatibility shim: the npm package @layers/mcp-server, including the keyless
onboarding mode it started in without a key, and the growth MCP that served the
protocol at growth.layers.com. Do not install or call either one. That
hostname now serves the landing site for this server and no protocol. The
Growth page covers the retired growth MCP. The product contract
for this server, including where the outcome of every retired tool went, is
docs/specs/layers-mcp-product.md in the Layers repository: section 15.2 for
the 26 growth tools and section 15.3 for the 52 API-backed tools and the five
keyless tools. The REST routes those tools mirrored are unchanged.
Support
Ask in the in-app chat at app.layers.com, or email support@layers.com. For a security report, email security@layers.com.
Layers also has a public REST API for building against the platform directly. See API.
Legacy growth MCP endpoint
The legacy endpoint at growth.layers.com/mcp was retired on 2026-08-30. Layers Growth is live at growth.layers.com and connects to https://mcp.layers.com/mcp.
Layers API integration
Programmatic access to generate on-brand short-form content for your customers and (optionally) publish it to their connected social accounts.