Skip to content

Connect an MCP client

Every property has its own server URL:

https://api.snipform.io/mcp/{propertyId}

Open the property in the dashboard and go to API (https://app.snipform.io/property/{property}/api). The page shows this URL with the id filled in, a ready-made server name, and copyable snippets for each flow below. Copy from there rather than typing ids by hand.

The server name the snippets use is derived from the property name: snipform- plus the slug of the name with dots treated as word breaks, capped at 32 characters. A property called example.com becomes snipform-example-com. It carries the property so that two connected properties do not both show up as “snipform” in your client. Any name works; this is just the default.

No token. The client is refused on its first request, follows the WWW-Authenticate pointer to SnipForm’s OAuth discovery metadata, registers itself as a client, and sends you to the normal consent screen in your browser. Approve once and the client holds its own credentials from then on.

Run this in your terminal. A browser opens to approve the connection.

Terminal window
claude mcp add --transport http snipform-example-com https://api.snipform.io/mcp/PROPERTY_ID

The approval page is served from app.snipform.io and shows:

  • The client asking for access. Clients that registered themselves through discovery (which is what every MCP client does) belong to no account, so no owner is shown for them.
  • The property the client is pointed at, taken from the OAuth resource parameter (the server URL discovery handed the client), and whether the account you are signed in as can reach it. If it cannot, the screen says so before you approve rather than after the first tool call fails.
  • The scopes being granted.

If you are signed out, the consent screen shows a sign-in form in place rather than bouncing you to the login page, so the OAuth request survives the sign-in.

An OAuth connection belongs to your user, not to a property. The URL selects the property, and your account must own it; a client connected this way can only ever reach properties on the account you signed in with.

Because clients register themselves, the redirect URL is the one thing standing between SnipForm and a client that would send the authorization code somewhere else. The allow-list is fixed:

KindAllowed
Loopback (RFC 8252)http://localhost, http://127.0.0.1 on any port
Webhttps://claude.ai
Custom schemesclaude://, cursor://, vscode://

Claude Code, Cursor and VS Code all call back on loopback or one of those schemes. A client that needs a different https callback cannot complete the OAuth flow today; use a token instead.

Discovery endpoints, for clients that want them explicitly, live on the API domain: /.well-known/oauth-protected-resource/mcp/{propertyId}, /.well-known/oauth-authorization-server, and dynamic registration at /oauth/register. The authorize screen itself is on the app domain and is pointed at by absolute URL in the metadata.

For clients that cannot sign in, and for checking the server by hand.

  1. On the property’s API page, create a token. Choose type MCP, give it a name, and pick an expiry (7 days, 30 days, 90 days, 1 year, or never).
  2. Copy the token. It is shown once and never again.
  3. Paste it in place of YOUR_TOKEN below.

An MCP token is granted exactly one scope, mcp:use, and is bound to the property it was created on. It cannot call the REST API, and an API token cannot reach the MCP server. See Authentication and tokens for how the two kinds differ.

Terminal window
claude mcp add --transport http snipform-example-com https://api.snipform.io/mcp/PROPERTY_ID \
--header "Authorization: Bearer YOUR_TOKEN"

Useful when writing your own client. Transport is Streamable HTTP: POST JSON-RPC to the server URL with both application/json and text/event-stream in Accept.

Terminal window
curl -s -X POST https://api.snipform.io/mcp/PROPERTY_ID \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "metrics",
"arguments": { "window": "last_7", "metrics": ["sessions", "bounce_rate"] }
}
}'

The tool’s result arrives as a single text content block containing JSON:

{
"ok": true,
"data": {
"property_id": "PROPERTY_ID",
"window": { "from_ts": 1755129600, "to_ts": 1755734399, "label": "last_7" },
"audience": "user",
"metrics": { "sessions": 4180, "bounce_rate": 0.41 }
}
}

There is no property_id argument. The server strips it from every tool’s schema and injects the token’s property; anything a client sends under that name is overwritten.

SymptomCauseFix
401 on every requestNo bearer token, or the token expired or was revokedCreate a new MCP token, or reconnect with sign-in
403 Missing scope: mcp:useAn API token was usedMCP needs an MCP-type token; API tokens never carry mcp:use
403 This token is bound to a different property.The URL names a property other than the one the token was created onUse the URL from that property’s API page, or a token from this one
403 This property is not accessible to the current user.OAuth connection, and the signed-in account does not own the property in the URLSign in with the account that owns it
404 Property not found.Typo in the property idCopy the URL from the API page
429More than 60 requests in a minuteWait; ask the assistant to make fewer, broader calls
Browser shows “redirect not allowed” during sign-inThe client’s callback URL is not on the allow-list aboveUse a token for that client
Consent screen says you cannot access the propertyYou are signed in to a different SnipForm accountSign out and back in with the owning account
tools/list works with curl but the assistant sees no toolsClient config, not the serverCheck the URL and header in the client’s config file
A tool returns {"ok": false, ...}The arguments were rejected (unknown window, bad field, too many filters)The error.message names the accepted values; see Tools