Skip to main content
Glama
SmartSurvey MCP server

SmartSurvey MCP server

by dragosh29

SmartSurvey MCP server

An MCP server that lets Claude, ChatGPT and other MCP clients work with a SmartSurvey account: surveys and their designs, responses, exports and survey folders, and (when enabled) opening or closing a survey and sending an invitation to one person. It is built from SmartSurvey's public API documentation and the OpenAPI definitions it publishes, one per reference page, on docs.smartsurvey.io.

Once it's connected, someone on the account can ask things like:

  • "Which of our surveys are open, and how many responses does each have?"

  • "Show me the questions in the customer satisfaction survey."

  • "What did people write in the comments box since 1 September? Anything about waiting times?"

  • "Pull response 301 with the respondent's details so I can follow up."

  • "Is last night's raw-data export ready?"

  • With writes enabled: "Close the summer party survey." / "Send the CSAT invitation to Sam Evans at sam.evans@example.com."

Tools

Tool

What it does

API calls

whoami

The account user the API key belongs to (name, email, type, survey count) and the base URL in use.

GET /account-user

list_surveys

Surveys with title, nickname, status, response count and dates, in whole API pages up to max_results, continued with page, optional sort_by.

GET /surveys

get_survey

One survey with page/question counts, theme and settings. With detail=true: variables, translations and every page with its questions, answer choices (with score values) and logic flags.

GET /surveys/{id}, or GET /surveys/{id}/detailed

list_responses

Responses to a survey with every page, question and answer, in whole API pages up to max_results (25 by default). Filters since, until, completed_only, filter_id, tracking_link_id, unique_id, include_labels and translation_id are passed to the API as documented; since/until accept ISO 8601 or a Unix timestamp in seconds.

GET /surveys/{id}/responses

get_response

One response in full, including Organisation Hierarchy entity fields when present.

GET /surveys/{id}/responses/{responseId}

list_exports

Exports (reports) of a survey: name, type, status, size, dates and the API download URL. Metadata only; files are never downloaded.

GET /surveys/{id}/exports

list_survey_folders

Survey folders on the account.

GET /survey-folders

open_survey

Opens a survey (the API opens only the default tracking link). Writes only.

PATCH /surveys/{id}/open

close_survey

Closes a survey and, per the API, all of its tracking links. Writes only, marked destructive.

PATCH /surveys/{id}/close

send_invitation_to_one

Emails or texts an existing invitation to one named recipient. The spec calls this a premium endpoint. Writes only.

POST /surveys/{id}/invitations/{invitationId}/sendone

Not covered on purpose: contact lists and contacts, API keys, account users, permissions, tracking links, page and question writes, survey creation, copying and deletion, response inserts and deletes, export downloads and deletes, file library, themes, webhooks, and the multi-recipient invitation send.

Related MCP server: WebAsk MCP

Setup

Requires Node 18 or later.

npm install
npm run build

You need an API key from SmartSurvey (My Account > API Keys > Add New API Key). A key has an API token and a token secret. The API authenticates with HTTP Basic: the token is the username and the secret the password, as the Getting Started guide states ("Username" is the API Token, "Password" is the Token Secret; not the SmartSurvey login).

Use the region you sign in to: app.smartsurvey.co.uk or app.smartsurvey.com is the default (api.smartsurvey.io), app-eu.smartsurvey.com is eu (api-eu.smartsurvey.io), app-us.smartsurvey.com is us (api-us.smartsurvey.io).

Claude Desktop: add this to claude_desktop_config.json:

{
  "mcpServers": {
    "smartsurvey": {
      "command": "node",
      "args": ["/absolute/path/to/smartsurvey-mcp/dist/index.js"],
      "env": { "SMARTSURVEY_API_TOKEN": "your-token", "SMARTSURVEY_API_SECRET": "your-secret" }
    }
  }
}

Claude Code:

claude mcp add smartsurvey -e SMARTSURVEY_API_TOKEN=your-token -e SMARTSURVEY_API_SECRET=your-secret -- node /absolute/path/to/smartsurvey-mcp/dist/index.js

Variable

Required

Meaning

SMARTSURVEY_API_TOKEN

yes

The API token, sent as the HTTP Basic username.

SMARTSURVEY_API_SECRET

yes

The token secret, sent as the HTTP Basic password.

SMARTSURVEY_REGION

no

default (UK, api.smartsurvey.io), uk (the same), eu or us. Anything else stops the server at start-up, whether or not SMARTSURVEY_BASE_URL is set.

SMARTSURVEY_ALLOW_WRITES

no

true to register open_survey, close_survey and send_invitation_to_one. Off by default.

SMARTSURVEY_BASE_URL

no

Overrides the region's host, e.g. https://api-eu.smartsurvey.io/v2. Used by the tests.

Safety defaults

  • Read-only unless SMARTSURVEY_ALLOW_WRITES=true. Read tools carry the MCP readOnlyHint annotation; close_survey is marked destructive because the API closes every tracking link and open_survey afterwards reopens only the default one.

  • Survey responses are third-party data. By default a response's contact_name, contact_email, unique_id, ip_address, user_agent, saved_name, saved_email, and the two links that open the respondent's answers for editing (edit_url, saved_continue_url) are not returned; the value of any survey variable, contact-list column or entity field whose name looks like contact data (email, phone, mobile, name, address, postcode, date of birth, IP, NHS or passport number and the like, whether written as Email Address, E-mail, home_address, customerName, dateOfBirth or nhsnumber) is replaced by a placeholder; and in every other free-text field (answers, choice/row/column labels, question and page titles, variable values, entry_url, referer_url, entity_name) email addresses are replaced with [email redacted] and phone-number-like sequences with [phone redacted]. include_contact_details=true on list_responses or get_response returns all of it as stored. The phone match is a heuristic: it covers international numbers written with + or 00 (including the +44 (0)7700 … form), UK numbers with a bracketed area code such as (020) 7946 0958, and UK-style 0… numbers of 9 to 11 digits with spaces, dots or hyphens between groups. Other digit strings that happen to start with 0 (an order number, say) are redacted too, and so is a hyphenated reference that starts with 0 and holds 9 to 11 digits (0-123-45678-9); numeric IDs, timestamps, page paths such as 9001,9002 and references that start with another digit or letter, such as PO-0001-000123, are left alone. The same redaction is applied to SmartSurvey's own error messages before they are passed on.

  • Survey titles and nicknames, folder titles and the survey design (get_survey: page titles and descriptions, question and choice text, variable labels) are the account holder's own content, but they get the same email and phone redaction by default, so that a question such as "Email us at …" reads the same in get_survey and in list_responses. include_contact_details=true on list_surveys, get_survey or list_survey_folders returns them as stored. Variable names (URL parameter names such as email) and survey URLs are never altered. whoami returns the key owner's own email.

  • List tools return whole API pages, never part of one. The page size sent is min(100, max_results), and a call stops before a page that could take it past max_results, so count can be below max_results (the API may also return fewer rows per page than asked; the docs say visibility rules can filter items out). The result carries page_size, next_page and a note saying to call again with that page and the same max_results: page numbers only line up for one page size, so changing max_results on a continuation would renumber the pages. Cutting a page in the middle and pointing at the page after it would silently skip the rest of that page; this server does not do that.

  • Export download links are the API's own /download endpoints, which need the same credentials; the server returns them as metadata and never fetches a file.

  • IDs are checked before any call is made: every ID in the spec is a positive int32, so anything else (abc, 0, -5, 1.5, 2147483648, ../account-user) is refused locally. since/until take a Unix timestamp in seconds between 2000-01-01 and 2100-01-01, or an ISO 8601 date or date-time (2026-09-01, 2026-09-01T00:00:00, 2026-09-01T00:00:00+01:00; a missing zone means UTC, never the machine's local time). Anything else is refused locally with a message saying what is accepted, rather than guessed: 2026 is not sent as the Unix timestamp 2026 (33 minutes past the epoch), a millisecond timestamp such as 1756000000000 is not sent as seconds, and 12/08/2026 is not read as either 8 December or 12 August.

  • send_invitation_to_one refuses locally when neither an email nor a mobile number is given, or when both entity_id and entity_unique_id are given (the spec says to supply one or the other).

  • SmartSurvey does not document a rate limit anywhere in its Getting Started guide or reference pages (the only "429" in the spec is a generic HTTP status enum). Requests are spaced 250 ms apart (about four per second). A 429 is retried at most twice for any method, including POST …/sendone, on the assumption that a rate-limited request was not processed (see Status). The retry waits for Retry-After (whole or fractional seconds, or an HTTP-date; 2 s then 4 s when the header is absent or unreadable). Each wait is capped at 10 seconds, so one request waits at most 20 seconds in all; a tool call that makes several requests (pages, lookups) can still run past the MCP client's default 60-second request timeout. Past the cap: if SmartSurvey asks for a longer wait the call gives up at once and the message says how long to wait.

  • 502, 503 and 504 are retried the same way for GET only; when all three attempts fail the error says the service may be unavailable and to try again in a few minutes, without the gateway's HTML. A POST …/sendone or a PATCH …/open|close is never retried after a gateway error, because the request may already have been processed and a retry could email someone twice; the error says what to check before repeating it.

  • A 200 whose body is not JSON (a proxy or a login page in the way) is reported as an error naming SMARTSURVEY_BASE_URL / SMARTSURVEY_REGION, never as an empty list or an empty survey.

  • Rejected credentials (401) produce a message that says which variables to fix and how the two halves of the key are used; a 403 explains the two documented causes (no permission for that survey, or an endpoint not included in the plan); a 402 on an invitation send says the email balance is insufficient; a 400 passes on SmartSurvey's validation errors field by field.

Tests

npm test

The test suite:

  1. Validates every fixture record against the component schemas in SmartSurvey's published OpenAPI definitions (AccountUserResponse, SurveyResponse, SurveySingleResponse, SurveyDetailedResponse, Response, DetailedResponse, SurveyExportResponse, SurveyFolderResponse). SmartSurvey publishes one OpenAPI document per reference page; test/assemble-spec.mjs fetches the page index (docs.smartsurvey.io/llms.txt) and every reference page's Markdown version, merges the 89 operations and 115 schemas into spec.json on the first run, and refuses to continue if two pages define the same operation or schema differently.

  2. Starts a local mock of the API under /v2 that serves those fixtures with the documented page/page_size pagination (the PaginatedList* body fields and the four X-SS-Pagination-* headers, X-SS-Pagination-PageSize being the number of rows requested as Getting Started defines it; surveys are capped at 10 per page and responses at 3 whatever is asked, so that lists span several pages and pages are shorter than requested, while folders and exports honour the requested size), Basic-auth 401s, and 402, 403, 404 and 400 answers in the documented ProblemDetails / ValidationProblemDetails shapes, and answers the first GET /survey-folders with a 429. The mock's list, detail, action and error responses are validated against the response schemas the spec names for each operation and status, and the documented keys of each list response are asserted explicitly (the PaginatedList* schemas mark nothing as required).

  3. Starts the built server and drives it over stdio with the official MCP client: 27 checks (29 in the whole suite) covering every tool, tool annotations, page-based pagination stopping at the documented page count (from the body and, when the body lacks the paging fields, at X-SS-Pagination-Total) and continuing across pages shorter than requested, max_results sent as the page size and honoured with whole pages only (following the tool's own continuation notes from a first page, and from a later start page, yields every fixture record exactly once, both when the mock caps the page size below max_results and when it honours it), sort_by sent as a repeated query parameter, translation_id passed through on the detailed survey endpoint and on get_response, every documented list_responses filter passed through exactly (since/until converted from ISO 8601 with and without a zone to Unix timestamps or passed as given, and refused for a bare year, a millisecond timestamp, a fractional number, 12/08/2026, 2026-08 and an impossible date, completed 1 or 0, filter_id, tracking_link_id, unique_id, include_labels, translation_id), redaction of respondent identifiers, edit links, contact-like variables and columns (including E-mail, fullName, homeAddress, dateOfBirth, phoneNumber and nhsnumber), of emails and phone numbers in answers and URLs, in survey titles, folder titles and the survey design by default (the +44 (0)…, bracketed, 00-prefixed, extra-spaced and dot-separated phone forms), and their return on request, entity fields on a detailed response, exports without any download call, the 429 retry waiting for Retry-After in the seconds, fractional-seconds and HTTP-date forms, giving up after three attempts on a persistent 429 and at once on a Retry-After above the cap, a 429 on POST …/sendone retried once, a 502 retried for GET and never for POST …/sendone or PATCH …/close, a GET failing three times with 503 reported with advice and without the gateway HTML, a 200 with a non-JSON body reported as an error, the write gate with the variable unset and set to false, the POST …/sendone body validated against the spec's InvitationRequestContact schema for an email and an SMS recipient, the local refusals, the 402/403/404 messages, SmartSurvey's own error text passed on with contact details redacted and a 400's validation errors listed field by field, ID validation before any call, the 401 message, a bad SMARTSURVEY_REGION (with or without SMARTSURVEY_BASE_URL) or a missing secret stopping the server at start-up, and that every request used Basic base64(token:secret) and a documented method and path.

Status

This is a working prototype. It has not yet been run against the live API, because it was built without a SmartSurvey account. The API itself is a paid-plan feature: SmartSurvey's public pricing page (checked September 2026) lists "API & Webhooks" on the Growth plan, Scale includes everything in Growth, and the Basic and Advanced plans do not list it. Everything below is taken from the published documentation and should be confirmed on a real account:

  • Authentication: that the API token is the Basic username and the token secret the password (Getting Started says so), and the body of a 401 (not documented; the mock answers with ProblemDetails).

  • Pagination: that page_index in list bodies is 1-based like the page parameter (the server keeps its own page counter and stops when it reaches pages, when it has collected total records from page 1, or on an empty page; X-SS-Pagination-Total stands in for total when the body lacks it, and no page count is derived from X-SS-Pagination-PageSize, which Getting Started defines as the number of rows requested, not returned); that pages, total and page_size are present on every list; that the API honours page_size up to 100 (the mock deliberately serves fewer for surveys and responses; the budget check uses the body's page_size when present, else the size requested); and what a page past the end returns (the mock answers 200 with an empty list). A page shorter than page_size is not treated as the end, because the docs say visibility rules may filter items out.

  • sort_by: the accepted property names are not documented, and neither is the serialisation of the array; the server sends the key once per value (sort_by=title&sort_by=date_created), the OpenAPI default for query arrays.

  • completed: the parameter is documented as "Whether to only include completed responses", integer, default 1. The server sends completed=0 for completed_only=false and expects partial and disqualified responses back; the exact effect of 0 is not documented.

  • since/until: documented as Unix timestamps filtering "after"/"before" a date/time; which response timestamp they compare against (date_started, date_ended or date_modified) and whether the bounds are inclusive is not documented. The mock compares since with date_started and until with date_ended, inclusive.

  • include_labels: Getting Started says labels are not included by default and include_labels=true adds them; the list endpoint's own parameter description says the opposite about size. The server passes the value through explicitly on both endpoints (defaulting to true so answers carry question and choice text) and does not know which fields disappear without labels.

  • translation_id: defaults to 0 on the survey endpoints and 1 on the response endpoints in the spec; the server only sends it when given.

  • The values of Response.status (completed, partial, disqualified per the Data Types page), SurveyResponse.status ("e.g. open or closed") and SurveyExportResponse.status (queued, completed or errored), and the integer enums presentation_mode, completion_action.action, terminal.response_status and randomisation, whose names are not documented and are passed through as numbers.

  • Which respondent fields the live API actually fills (unique_id, contact_name/contact_email for invitation responses, ip_address, user_agent, edit_url, saved_*, entity_*) and whether edit_url and saved_continue_url really grant access to the answers, as their names suggest; they are withheld by default either way.

  • PATCH …/open and PATCH …/close: sent with no body and no Content-Type, as the spec defines no request body; the ApiBasicResponse content and whether a 200 is returned when nothing changed.

  • POST …/sendone: whether the invitation's type (email or SMS) decides which of email/mobile is required, whether partial failures come back as a 200 with failed_contacts (as the schema suggests) or as a 4xx, and the exact 402 and 403 behaviour (the spec documents 402 for an insufficient email balance and describes the endpoint as premium and "not available on all plans").

  • A 429 on POST …/sendone is retried on the assumption that a rate-limited request was not processed. SmartSurvey documents no 429 and no rate limit at all, so the 250 ms spacing here is a guess on the polite side.

  • The wording of SmartSurvey's error messages and whether any of them echo request data such as a recipient's email address; the texts here are placeholders, and the server redacts contact details from them regardless.

  • Whether GET /surveys includes surveys owned by sub-users of a master account, and whether GET /account-user reports the master account or the sub-user the key was created under.

  • Timestamps: the docs say all date-times are UTC ISO 8601 (YYYY-MM-DDTHH:MM:SSZ); the server passes them through unchanged.

Going to production

This version runs locally over stdio, with the account holder's own API key. For customers to connect from claude.ai or ChatGPT without handling keys, the next step is a remote server (Streamable HTTP) behind OAuth, hosted by SmartSurvey, and then a listing in the Claude and ChatGPT connector directories.

Licence

MIT. Built by Alexandru Dragoș (alexandru.dragos96@gmail.com) with an AI agent (Claude) working under his direction.

Available Tools

7 tools
get_responseGet one responseA
Read-only

One response with every page, question and answer, plus the Organisation Hierarchy entity fields when the account uses that feature. Respondent identifiers are withheld and emails/phone numbers in answers redacted unless include_contact_details is set.

Inputs: survey_id (required), response_id (required), include_labels, translation_id, +1 more.

get_surveyGet a surveyA
Read-only

One survey with page and question counts, theme and settings. With detail=true the full design is returned: variables, translations, and every page with its questions, answer choices and logic flags (this is the survey design, not respondents' answers). Emails and phone numbers in the title and in page, question and choice text are redacted unless include_contact_details is set.

Inputs: survey_id (required), detail, translation_id, include_contact_details.

list_exportsList exports of a surveyA
Read-only

Exports (reports) generated for one survey: name, type, status (queued, completed or errored), file size and type, dates and the API download URL. Metadata only; this server never downloads the files.

Inputs: survey_id (required), page, sort_by, max_results.

list_responsesList responses to a surveyA
Read-only

Responses to one survey with every page, question and answer. Whole API pages of min(100, max_results) responses are returned, so the count may be below max_results; the note says how to continue. Filters (since, until, completed_only, filter_id, tracking_link_id, unique_id) are passed to the API as documented. Respondent identifiers are withheld and emails/phone numbers in answers redacted unless include_contact_details is set.

Inputs: survey_id (required), page, since, until, +9 more.

list_survey_foldersList survey foldersA
Read-only

The survey folders on the API key's account (id, type, title). Emails and phone numbers in titles are redacted unless include_contact_details is set.

Inputs: page, sort_by, max_results, include_contact_details.

list_surveysList surveysA
Read-only

Surveys on this account with title, nickname, status (open or closed), response count and dates, in the order the API returns them. Whole API pages of min(100, max_results) surveys are returned, so the count may be below max_results; the note says how to continue. Emails and phone numbers in titles are redacted unless include_contact_details is set.

Inputs: page, sort_by, max_results, include_contact_details.

whoamiWho owns this API keyA
Read-only

The SmartSurvey account user the API key belongs to (name, email, user type, number of surveys). Use it to confirm which account and region the server is talking to.

Inputs: no named parameters.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 7 tools

    Disambiguation5/5

    Each tool targets a distinct resource and action: whoami for account, list_surveys/get_survey for surveys, list_responses/get_response for responses, list_exports for exports, and list_survey_folders for folders. No overlapping purposes exist across the set.

    Naming Consistency4/5

    Most tools follow a clear snake_case verb_noun pattern (list_surveys, get_survey, list_responses, get_response, list_exports, list_survey_folders). The single-word 'whoami' deviates from this pattern but remains a common, readable exception.

    Tool Count5/5

    Seven tools is well-scoped for a survey data retrieval server, with each tool earning its place by covering a distinct entity or action. There is no bloat or obvious missing central tool.

    Completeness3/5

    The read-only surface covers account info, surveys, responses, exports, and folders, which is useful for data extraction. However, it lacks any create, update, or delete operations (e.g., creating surveys or generating exports) and omits tracking links despite filters referencing them, leaving notable gaps for full lifecycle management.

    Maintenance

    ActivitySlowing
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      Enables Claude, ChatGPT and other MCP clients to read an Amiqus ID account—clients, onboarding records and steps, check results, templates, case status counts and webhooks—and, when writes are enabled, create records.
      9
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI assistants to create and validate surveys, distribute them, read and tag responses, and generate reports or exports through a hosted Streamable HTTP MCP endpoint with API-key authentication.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables Claude, ChatGPT and other MCP clients to read NewZapp account details, campaign reports, open heatmaps, contact groups and contact counts, and to search contacts with personal data withheld by default; when writes are enabled, it can also create and update contacts.
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables MCP clients such as Claude and ChatGPT to browse an Engaging Networks account: campaign pages, supporter records, transaction and recurring-gift history, supporter fields and questions, and marketing automation statistics. When writes are explicitly enabled, it can also set a supporter's opt-ins, with contact details, payment data and free-text personal data redacted by default.
      10
      MIT