npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@layers/mcp-server

v1.3.2

Published

MCP server exposing the Layers API as tools

Readme

Layers MCP Server

An MCP (Model Context Protocol) server that exposes the Layers API as tools, so AI agents can manage projects, generate short-form social content, and publish it — straight from any MCP client.

Tool coverage tracks the Layers API reference.

One-paste onboarding

Paste this one sentence to your coding agent, from the product repository:

Onboard this repository into Layers: run npx --yes @layers/[email protected] onboard from the repo root as a background process and follow the agent_instructions it prints; each input_required event advertises the exact command to send, except approve_consent, where you print the consent_proposal verbatim and stop until the human explicitly approves.

That is the whole paste. The consent clause is not decoration: approve_consent advertises its approval command like every other turn, so a paste that says only "send the advertised command" reads as licence to send that one too. The exception has to be stated where the human pastes it, not only inside the launcher output the agent may summarize.

The rest of the rules for driving the launcher used to live in the paste; they now arrive from the launcher itself, as the FIRST line it writes:

{"type":"agent_instructions","protocolVersion":1,"instructions":"…","commands":{…}}

instructions is the full operating protocol in plain ASCII — how to poll the process, what to print verbatim, when to stop and wait for a human, and what to do with the SDK fact after the claim. commands lists the command shapes the protocol uses; every turn still advertises its own exact command, and that advertised string is the one to send. The event is emitted once, before the launcher does anything else, so an agent that started the process has the protocol before it has to drive anything.

The phrase "as a background process" is load-bearing: Claude Code's permission classifier approves a long-running npx only when it is told the task runs in the background. Keep it.

The paste pins an exact version, on purpose. The server's capability manifest is the source of truth for which launcher it will accept: it advertises minimumMcpServerVersion and acceptedCollectorVersions, and a launcher outside that window is refused at preflight before anything is reserved. Pinning the paste to the version the server currently advertises means the paste and the server agree by construction. @latest does not: it can outrun a cutover and hand somebody a launcher the server has not been told about yet, which fails with "requires an update" for a version that is newer than the one being demanded. So the paste stays pinned and moves when the server's advertised version moves.

The same pin applies to the Claude Code recipe below, which is the paste's own command with a FIFO wired to its stdin — a recipe that drifted to @latest would quietly contradict the sentence it implements. The legacy public-URL form further down stays on @latest: it predates the capability manifest and is not part of the pinned flow.

The command checks server compatibility before reading the workspace, runs the checksum-verified native collector locally, shows the exact bounded source-data proposal, and waits for explicit approval before sending evidence. While the preview builds it asks the same setup questions the Layers web onboarding asks, one at a time, each carrying its exact title, its offered options, and the exact command that answers it. It then surfaces the preview and attempt-bound browser claim URL and returns the safe post-claim workspace projection to the same process when the browser claim finishes within its bounded window. Reservation, transport, PKCE verifier, post-claim capability, full evidence-envelope, and transient excerpt values never enter terminal output; the bounded consent projection does.

Setup questions and the claim link

Once evidence is approved and on its way, the command reads the outstanding setup questions and emits input_required: answer_intake one question at a time. Each turn carries the canonical question (title, optional subtitle, whether it takes one option or several, whether it takes free text, and the offered options as value/label pairs) and the commands that answer it:

answer <field> <value>                 pick one offered option
answer <field> <value>,<value>         pick several (multi-select questions)
answer <field>                         pick none (multi-select questions)
answer <field> other <your own words>  questions whose allowsFreeText is true

Which questions take free text is the server's call, carried per question on the wire and reported on the turn as allowsFreeText. A question the walk refuses an answer to is re-asked with the server's reason on refusal, rather than reported as recorded.

Send exactly one advertised command per turn; the next question arrives once the answer is recorded. A line that names an option the question does not offer re-asks the same question rather than guessing.

The claim link waits for both the preview and the questions. Whichever finishes second releases it, so the browser claim is never offered while setup questions are still outstanding. Progress is reported on intake events, whose complete flag is the explicit signal that the walk is done — the same way previewReady reports the preview. state is one of asking, complete, not_required (there were no questions), or skipped. skipped means the gate failed open: the question service was unreachable, refused repeatedly, or the questions were left unanswered past their bounded window. A broken question service costs a person their questions, never their workspace. The terminal complete event carries the same summary on its intake field.

Waiting for the browser claim

The claim handoff waits for the human, not for a timer. A browser claim link is attempt-bound and the server caps each attempt at 15 minutes, so an unopened link is re-minted rather than treated as the end of the session: the command emits status with stage claim_link_refreshed, and the fresh URL arrives on the next progress event. Give the human that one; the earlier link is dead.

While it waits it emits status with stage awaiting_claim every few minutes, so an agent polling a deliberately idle process has something true to report. The overall wait is 24 hours, intersected with the reservation.

If it does stop before the claim, it says so explicitly on status with stage claim_still_open before the terminal complete: the workspace is still built and still claimable until the reservation expires, and nothing was cancelled.

1.3.0 ended the wait at the 15-minute transport-attempt expiry and exited 0 with state: awaiting_claim, which read as "the claim expired" when the only thing that had expired was the transport leg. That is fixed in 1.3.1.

Claude Code process control

The command stays alive across scope review, human approval, preview creation, and claim. Each local source-inspection generation has a fixed 15-minute privacy lifetime. If that lifetime ends during scope review or consent, the command clears the expired local artifact and emits input_required: resume_inspection; resume performs a fresh inspection with fresh opaque IDs and requires a fresh proposal and approval. An expired proposal can never authorize an upload. In Claude Code, run the command as a native background Bash task so later tool calls can read its JSONL and answer its stdin. Do not put &, nohup, or a foreground wait around the command; those do not give Claude a controllable task handle.

  1. In a short foreground Bash call, create the private input pipe and retain the absolute directory printed by the last line:

    layers_session_dir="$(mktemp -d "${TMPDIR:-/tmp}/layers-onboard.XXXXXX")"
    chmod 700 "$layers_session_dir"
    mkfifo "$layers_session_dir/input"
    chmod 600 "$layers_session_dir/input"
    printf '%s\n' "$layers_session_dir"
  2. In a second Bash tool call, substitute that absolute path and run exactly this shell with the Bash tool's run_in_background parameter set to true:

    exec 3<>"/absolute/layers-onboard.ABC123/input"
    npx --yes @layers/[email protected] onboard <&3

    The shell command itself stays in the foreground of the background task. Claude Code returns a task ID instead of blocking the conversation.

  3. Use TaskOutput on that task ID with block: true and a timeout no longer than 15 seconds to read new JSONL. Send each advertised response from a separate short Bash call, for example:

    printf '%s\n' 'prepare' > '/absolute/layers-onboard.ABC123/input'

    Read TaskOutput again after every response. Print the complete consent_proposal.canonicalProjection verbatim, along with its display ID, display time, projection hash, and exact advertised approval command. End the turn and wait for the human's explicit approval before writing that command. If resume_inspection appears, send only its advertised resume or cancel command; a resumed inspection requires a new proposal and new approval. Keep the task alive until a terminal complete or error event.

  4. At input_required: answer_intake, relay one question per turn while the launcher keeps building in the background: show its exact title, its subtitle when present, and its offered options, take an offered option or the human's own words, and send the exact advertised answer command before asking the next. Never show a setup question while a consent proposal is displayed or awaiting approval. Withhold the claim link until the launcher reports the questions complete and the preview ready.

For ordinary public callers, a server that has not opened source admission stops the command before local inspection. An operator-only environment token can authorize a closed internal probe. The older public-URL compatibility form remains available:

npx --yes @layers/mcp-server@latest onboard https://example.com

Install as an MCP server

To expose the persistent Layers MCP tools without an existing API key:

claude mcp add layers -- npx -y @layers/mcp-server@latest

The server enters keyless onboarding mode when neither --api-key nor LAYERS_API_KEY is set. It can create and claim a workspace without an existing Layers account or API key.

To connect an existing Layers workspace with an API key:

claude mcp add layers -- npx -y @layers/mcp-server@latest --api-key lp_YOUR_KEY

Or configure API-key mode in any MCP client's JSON config:

{
  "mcpServers": {
    "layers": {
      "command": "npx",
      "args": ["-y", "@layers/mcp-server@latest", "--api-key", "lp_YOUR_KEY"]
    }
  }
}

Pin a reviewed version (@layers/mcp-server@<version>) in production setups instead of @latest if you want releases to be deliberate on your side.

Configuration

Flags take precedence; environment variables are the fallback.

| Flag | Env var | Default | Description | |---|---|---|---| | --api-key | LAYERS_API_KEY | unset | Layers API key (lp_...). When neither form is set, the server starts in keyless onboarding mode. | | --base-url | LAYERS_BASE_URL | https://api.layers.com | API host. Paths are versioned under /v1. | | --read-only | LAYERS_READ_ONLY=1 | off | In API-key mode, register only the 25 read tools. In keyless mode, this limits the workspace API tools; the five onboarding tools remain available. | | --organization | LAYERS_ORGANIZATION | unset | Act on behalf of a child org (org_...), sent as the X-Layers-Organization header on every request. Requires an org:admin parent key. |

Security note: flags end up in your client's config file and in the process argv (visible via ps). Prefer the env var for the key where your client supports it:

{
  "mcpServers": {
    "layers": {
      "command": "npx",
      "args": ["-y", "@layers/mcp-server@latest"],
      "env": { "LAYERS_API_KEY": "lp_YOUR_KEY" }
    }
  }
}

Sandbox keys (lp_test_...) skip real platform calls during development — content, OAuth, and publish return fixture-backed results.

Tools

The tool surface depends on how the server starts:

  • API-key mode: 52 workspace tools, one per API route.
  • Keyless onboarding mode: five onboarding tools plus the same 52 workspace tools. The workspace tools are registered up front so the MCP client can see them, but they refuse calls until the onboarding session claims a workspace.

--read-only hides workspace write tools (marked W below). The five onboarding tools remain available in keyless mode.

Keyless onboarding tools

onboard_start · get_onboarding_status · onboard_claim_begin · onboard_claim_verify · ask_elle

onboard_start without a URL remains a reservation-only MCP tool. Its result is honestly limited to awaiting_evidence, and its opaque capability stays inside the server process. The full local inspection, consent, evidence, preview, browser claim, and same-process return path belongs to the one-shot layers-mcp-server onboard command above. Passing an explicit public product URL to the MCP tool keeps the existing preview and claim flow working.

Workspace API tools (52)

Core

whoami · list_projects · get_project · get_credits · list_credit_events · create_project (W) · update_project (W) · archive_project (W)

Creative

list_influencers · get_influencer · get_keywords · list_content · get_content · get_content_progress · get_content_asset · get_hooks · get_source_recommendations · get_content_review_policy · create_influencer (W) · clone_influencer (W) · update_influencer (W) · delete_influencer (W) · refresh_keywords (W) · generate_slideshow (W) · generate_ugc_remix (W) · generate_video_remix (W) · generate_slideshow_remix (W) · create_content_upload (W) · upload_content_from_url (W) · finalize_content_upload (W) · update_content_caption (W) · approve_content (W) · reject_content (W) · update_content_review_policy (W)

Distribution

list_social_accounts · get_scheduled_post · list_scheduled_posts · list_tiktok_music · get_engagement_config · publish_content (W) · schedule_content (W) · reschedule_post (W) · cancel_scheduled_post (W) · notify_device (W) · update_engagement_config (W)

Measurement

get_metrics · get_top_performers · list_ads_content · list_recommendations · update_ads_content (W) · update_recommendation (W)

Framework

list_audit_log

Conventions

  • Async jobs. Generation, influencer creation/cloning, and keyword refresh return a 202 job envelope (jobId, containerIds/influencerId). Poll the matching read tool (get_content_progress, get_influencer, get_keywords) until the resource is terminal.
  • Uploading your own media. Two transports, both producing an uploaded content container you can then schedule/publish. For already-hosted files, upload_content_from_url is one synchronous call. For large/private files, create_content_upload returns presigned PUT URLs — your client uploads the bytes directly to storage (outside this server, within ~15 min), then calls finalize_content_upload per container. Fix a caption afterward with update_content_caption (uploaded content only).
  • Pagination. List tools accept cursor + limit and return { items, nextCursor }; pass nextCursor back verbatim.
  • Idempotency. The server stamps a fresh UUID Idempotency-Key on every mutating POST/PATCH automatically.
  • Errors expose only the public status, stable error code, safe message, and validated requestId. Backend details and non-contract response bodies are not copied into the agent transcript. Include the requestId in support tickets.
  • Timestamps are ISO 8601 UTC with a Z suffix everywhere (offset forms are rejected by the API). scheduledFor is a literal UTC instant — convert from local time before calling.

Development

Requires Node 20+.

npm install
npm run build                 # tsc -> dist/

# wire the local build into Claude Code:
claude mcp add layers -- node $(pwd)/dist/index.js --api-key lp_YOUR_KEY

# exercise the same-session command from a product workspace:
(cd /path/to/product && node /path/to/mcp-server/dist/index.js onboard)

# or explore interactively with the MCP inspector:
npx @modelcontextprotocol/inspector node dist/index.js --api-key lp_test_dummy

In MCP-server mode, stdout is the JSON-RPC channel and all logging goes to stderr. The one-shot onboarding CLI emits its documented JSONL events on stdout; the legacy URL form emits its compatibility output there.

Testing

npm test            # hermetic suite — no API key, no network
npm run smoke       # opt-in live smoke; needs LAYERS_TEST_KEY=lp_test_...

npm test builds, then runs the contract suite with Node's built-in test runner against a localhost mock — it verifies tool registration, --read-only gating, annotation hints, stdout protocol discipline, and the request contract (auth, idempotency, query encoding, per-tool routing, error rendering). No credentials or outbound network required; this is what CI runs. See test/README.md for the full breakdown and the sandbox smoke script.

License

Apache-2.0 — see LICENSE and NOTICE. Copyright 2026 Layers AI, Inc.

Report vulnerabilities privately as described in SECURITY.md.