Troubleshooting
The server doesn't appear after setup
- Restart the agent. MCP servers load at startup — Claude Code, Cursor, and Codex all need a restart (or reload) after you add the config.
- Wrong config key or format. Cursor/Claude use
mcpServers; VS Code/Copilot usesservers; Codex uses TOML, not JSON. A mismatch is ignored silently. See Connect your agent. - Run the command yourself. Copy the
command+argsfrom your config and runnpx -y @stepskit/mcpin a terminal — any error (network, Node version) prints directly instead of being swallowed by the client.
"STEPSKIT_TOKEN is not set" or 401 errors
- Make sure
STEPSKIT_TOKENis in the server'senvand is a fullsk_live_…token. - The reference expanded to nothing. If your config says
"STEPSKIT_TOKEN": "${STEPSKIT_TOKEN}"(or Codex'senv_vars), the value comes from your shell — and if you never exported it, the server receives an empty string and reports the variable as unset. Export it in your shell profile (export STEPSKIT_TOKEN=sk_live_...), then restart the agent so it picks up the new environment. A GUI app like Claude Desktop may not see a variable exported only in a terminal session. - A 401 means the token is invalid or revoked. Generate a fresh one in the dashboard (a project's AI Agents page, or Account settings for an account-wide token) and update your config — tokens are shown only once.
- A 401 can also mean the selected project no longer exists on the account —
it was deleted, or
STEPSKIT_PROJECTpoints at something that's gone. Runlist_projectsto see what the token can actually reach.
"No project selected"
An account-scoped token can reach every project you own, so when you own more than one and nothing has said which, tools refuse to guess. The error names your projects and how to choose:
No StepsKit project selected, and this account has 3 of them.
Pick one with the `select_project` tool, pass `project: "<slug>"` on the call,
or set STEPSKIT_PROJECT=<slug> in the server's env in your MCP client config.Ask the agent to run list_projects and then select_project for the one you
want — that lasts the session. To stop choosing every time, pin a default with
--project <slug> in the server's args or STEPSKIT_PROJECT in its env.
See Pick a project.
A related error — No StepsKit project matching "…" — means the slug or id you
pinned doesn't exist on this account (often a renamed project, since slugs
follow the name). list_projects prints both the current slugs and the ids,
which never change.
A tour was created but doesn't show up on my site
A created tour is an unpublished draft and only renders when:
- The embed is installed and live. Ask the agent to run
verify_integration, or check the dashboard. If it's not live, load any page of your running app once so the embed pings StepsKit. - The domain is allowed. Production domains must be whitelisted
(
add_allowed_domain); localhost is always allowed. - You published it. The agent never publishes — review and publish the tour from the StepsKit dashboard.
- It landed in the project you're looking at. With an account-scoped token
the tour goes to whichever project was selected at the time, which may not be
the one open in your dashboard. Ask the agent to run
list_projects(it marks the selected one) andlist_toursto see where the tour actually is.
The tour replays every page load, or my targeting never matches
Almost always the same cause: the visitor is never identified.
The embed works and verify_integration reports green, because that check only
proves a page loaded. It says nothing about identity. With no id, show-once
frequency capping falls back to a per-tab check that dies when the tab closes —
so a returning visitor sees the tour again — and no audience-targeting rule
that tests a value can match. (A bare notExists rule is the one exception: it
matches precisely the anonymous visitors you probably didn't mean to target.)
Confirm it in the browser console on a logged-in page:
window.stepskit.validateEnvironment();If visitorId is null or userAttributes is empty, that's the bug. The
toursFiltered table in the same output tells you which experiences were held
back and why.
The fix is to call identify wherever your app resolves the authenticated user
— once, when auth state settles:
window.stepskit?.identify({ id: user.id, email: user.email, plan: user.plan });See Visitor identification for the details, or hand your agent the install prompt, which wires this for you.
Steps point at the wrong element
When the agent infers a selector from source and isn't sure it matches the live
DOM, it marks the step selector_confidence: "low" — these show a "Review
selector" badge in the canvas. Open the tour, confirm or fix the target, and
clear the badge. Prefer stable selectors (data-testid, id) in your markup to
make this rare.
The agent claims it can do something it can't
The agent can edit an existing tour — its name, behavior settings, visibility
(targeting / URL pattern / screen width / frequency / trigger), theme (the
project-wide default for each experience type — tours, tooltips, announcements,
surveys — plus per-item overrides), and steps. It can also create and edit
tooltips, surveys, and announcements, and — with an account-scoped token — list
and switch between the projects you own. What it cannot do via MCP is
per-step visual styling, upload media/images, publish anything, read analytics,
or create a new project (that's a dashboard action). It
also can't wire custom event tracking — window.stepskit.track() is a no-op
today, so any track() calls an agent adds are dead code. If it offers one of those, point it at
get_capabilities (or see
Capabilities & limits) — those aren't supported
via MCP yet.