QuestionPunk
Server Details
Create, edit and publish surveys and AI interviews, then read responses and reports.
- Status
- Healthy
- Uptime
- 100.0% over the last 24 hours
- OAuth
- Works in Glama
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- ChatGPT
- –
- Claude
- –
TDQS
Scored across 70 tools
Most tools are cleanly separated by resource and action, and descriptions actively redirect (report_get vs report_get_crosstab vs responses_read, skill_prompt vs skill_run, responses_get_counts vs responses_list). A few genuine overlaps remain: fetch vs survey_get both read one survey, search vs survey_list both find surveys, and report_load_more_answers mostly duplicates responses_read paging.
A clear resource-then-verb convention runs through nearly everything (question_create, survey_update_title, display_logic_rule_delete, notion_survey_sync_get, prolific_study_calc_cost). Exceptions are the bare verbs fetch and search, plus singular/plural drift within families (response_get_details vs responses_read, responses_get_counts).
70 tools is far past a comfortable surface, even for a broad platform spanning surveys, display logic, responses, reports, skills, Notion, Prolific, synthetic personas, templates and usability tests. The families are coherent internally, but the overall set is heavy enough that selection and context cost suffer.
Coverage is unusually deep: survey lifecycle, question CRUD, display-logic rule trees, response reading/export/counting, reports, templates, Notion sync, Prolific, synthetic runs and usability tests. Gaps are real but workaroundable: no survey delete/archive, and referenced operations like survey_lang_create and survey_update_props are explicitly absent, deferring the agent to the editor.
Available Tools
70 toolsdisplay_logic_container_createDisplay Logic Container CreateAInspect
Show a question only when a condition holds, e.g. ask a follow-up only to people who rated 6 or lower. Creates the target question's display logic with one group and one rule; add more conditions with display_logic_group_create and display_logic_rule_create. A question has at most one container (a second is rejected): check display_logic_list first. Draft surveys only: a published survey rejects logic edits, so set logic up before publishing. Question ids and option order come from survey_get. Each group has exactly one source: sourceSurveyItemId or sourceUrlParameterId. URL parameter conditions support is/is_not with args.value matching a declared enumValues entry. Conditions by source question type: multiple_choice, dropdown, ranking, constant_sum: is_selected_option with { idx } (0-based index into answer_options, or "none" / "other"). opinion_scale, slider, rating, numeric: is_number_less, is_number_greater, is_number_equal_to, is_number_not_equal_to with { value: number }. date: is_date_on, is_date_not_on, is_date_before, is_date_later with { date: "YYYY-MM-DD" }. matrix: is_matrix_selected with { rowIdx, colIdx } (0-based). short_text, long_text: is, is_not (AI meaning match) with { value }. short_text, long_text, email, phone_number, website: string_contains, string_does_not_contain, is_string_equal_to, is_string_not_equal_to, string_begins_with, string_ends_with with {}, then set the text with display_logic_rule_update_text. URL parameter sources: is, is_not with { value: "" }. A decision question cannot be a source. The target question shows when its logic matches and is hidden otherwise.
Inputs: surveyId (required), displayLogicGroups (required), targetSurveyItemId (required), containerCondition. Returns: result
display_logic_container_deleteDisplay Logic Container DeleteADestructiveInspect
Remove all display logic from a question (its container with every group and rule), so the question always shows. Draft surveys only. displayLogicContainerId comes from display_logic_list. To change one condition, update or delete that rule instead.
Inputs: surveyId (required), displayLogicContainerId (required). Returns: result
display_logic_group_createDisplay Logic Group CreateAInspect
Add another leg of conditions to a question's display logic (draft surveys only); the container's 'or'/'and' combines legs. Each group has exactly one source: sourceSurveyItemId (an answer) or sourceUrlParameterId (an enum URL parameter, with is/is_not and args { value: "" }), and starts with one rule; add rules with display_logic_rule_create. displayLogicContainerId comes from display_logic_list or display_logic_container_create.
Inputs: surveyId (required), groupCondition (required), displayLogicRules (required), displayLogicContainerId (required), +2 more. Returns: result
display_logic_group_deleteDisplay Logic Group DeleteADestructiveInspect
Remove one group (one leg) of a question's display logic with all its rules (draft surveys only). displayLogicGroupId comes from display_logic_list.
Inputs: surveyId (required), displayLogicGroupId (required). Returns: result
display_logic_group_updateDisplay Logic Group UpdateADestructiveInspect
Change a display logic group's source question or URL parameter, or how it combines its rules (draft surveys only). Each group has exactly one source: sourceSurveyItemId or sourceUrlParameterId. displayLogicGroupId comes from display_logic_list.
Inputs: surveyId (required), groupCondition (required), displayLogicGroupId (required), sourceSurveyItemId, +1 more. Returns: result
display_logic_listDisplay Logic ListARead-onlyIdempotentInspect
List a survey's display logic: one container per question that has any (targetSurveyItemId), with its groups and rules and their ids. Read it before adding, changing or deleting logic, and to explain why a question is hidden.
Inputs: surveyId (required). Returns: result
display_logic_rule_createDisplay Logic Rule CreateAInspect
Add one condition to a display logic group (draft surveys only); the group's 'and'/'or' combines its rules. displayLogicGroupId comes from display_logic_list or display_logic_container_create. Conditions by source question type: multiple_choice, dropdown, ranking, constant_sum: is_selected_option with { idx } (0-based index into answer_options, or "none" / "other"). opinion_scale, slider, rating, numeric: is_number_less, is_number_greater, is_number_equal_to, is_number_not_equal_to with { value: number }. date: is_date_on, is_date_not_on, is_date_before, is_date_later with { date: "YYYY-MM-DD" }. matrix: is_matrix_selected with { rowIdx, colIdx } (0-based). short_text, long_text: is, is_not (AI meaning match) with { value }. short_text, long_text, email, phone_number, website: string_contains, string_does_not_contain, is_string_equal_to, is_string_not_equal_to, string_begins_with, string_ends_with with {}, then set the text with display_logic_rule_update_text. URL parameter sources: is, is_not with { value: "" }. A decision question cannot be a source. The target question shows when its logic matches and is hidden otherwise. A URL parameter must be declared type "enum_" and the value must be one of its enumValues.
Inputs: args (required), surveyId (required), ruleCondition (required), displayLogicGroupId (required). Returns: result
display_logic_rule_deleteDisplay Logic Rule DeleteADestructiveInspect
Remove one condition from a display logic group (draft surveys only). displayLogicRuleId comes from display_logic_list.
Inputs: surveyId (required), displayLogicRuleId (required). Returns: result
display_logic_rule_updateDisplay Logic Rule UpdateADestructiveInspect
Change one display logic rule's condition and arguments (draft surveys only). displayLogicRuleId comes from display_logic_list. For the text of string conditions (string_contains and the like) use display_logic_rule_update_text.
Inputs: args (required), surveyId (required), ruleCondition (required), displayLogicRuleId (required). Returns: result
display_logic_rule_update_textDisplay Logic Rule Update TextADestructiveInspect
Set the text a string condition matches (string_contains, string_does_not_contain, is_string_equal_to, is_string_not_equal_to, string_begins_with, string_ends_with), in one of the survey's languages. Draft surveys only. Call it after creating such a rule, which starts without text.
Inputs: lang (required), surveyId (required), textFields (required), ruleCondition (required), +1 more. Returns: result
fetchFetchARead-onlyIdempotentInspect
Read one QuestionPunk study by the id search returned: its title, status, description and every question with its answer type and options, as text. Responses are not included.
Inputs: id (required). Returns: id, url, text, title, +1 more
notion_list_databasesNotion List DatabasesARead-onlyIdempotentInspect
Search the Notion databases a connected workspace shared with QuestionPunk, to send a survey's responses into one that exists. Returns { databases: [{ dataSourceId, databaseId, title, icon, url }] }; pass dataSourceId to notion_survey_link_database. A database the user expects but that is missing was not shared with QuestionPunk in Notion.
Inputs: connectionId (required), query. Returns: result
notion_list_pagesNotion List PagesARead-onlyIdempotentInspect
Search the Notion pages a connected workspace shared with QuestionPunk, to choose where notion_survey_create_database puts a new database. Returns { pages: [{ pageId, title, icon }] }.
Inputs: connectionId (required), query. Returns: result
notion_statusNotion StatusARead-onlyIdempotentInspect
Whether this QuestionPunk account can sync to Notion, and its connected Notion workspaces: id, workspaceName, needsReconnect and surveyCount. Returns { available, connections, connectUrl }. available false means Notion is not switched on for this QuestionPunk server. With no connections, or one that needs reconnecting, the user connects a workspace in the app at connectUrl (Notion's own consent screen, where they pick the pages QuestionPunk may use); give them that link.
Inputs: no named parameters. Returns: result
notion_survey_create_databaseNotion Survey Create DatabaseADestructiveInspect
Create a Notion database built from a survey's questions (typed columns: choices as selects, ratings as numbers, dates, emails and so on) and send every new completed response to it as a page. With backfill (the default) the responses collected so far are sent too, on the server. The database goes inside parentPageId (a page from notion_list_pages; Notion lets QuestionPunk create databases only inside a shared page, so if notion_list_pages returns none, ask the user to share a page with QuestionPunk in Notion). Not idempotent: each call creates another database and points the survey at it, and a survey that already syncs stops sending to its old database, so check notion_survey_sync_get first and confirm with the user before replacing a sync. Sends respondents' answers to Notion. Returns the survey's sync as notion_survey_sync_get does; give the user its databaseUrl.
Inputs: surveyId (required), connectionId (required), parentPageId (required), backfill, +2 more. Returns: result
notion_survey_link_databaseNotion Survey Link DatabaseADestructiveInspect
Send a survey's completed responses to an existing Notion database (dataSourceId from notion_list_databases). Questions are matched to columns by name and type, and columns the database lacks are added. With backfill (the default) the responses collected so far are sent too, on the server. Replaces any database the survey synced to before. Sends respondents' answers to Notion. Returns the survey's sync as notion_survey_sync_get does; give the user its databaseUrl.
Inputs: surveyId (required), connectionId (required), dataSourceId (required), backfill, +2 more. Returns: result
notion_survey_sync_getNotion Survey Sync GetARead-onlyIdempotentInspect
Read a survey's Notion sync: the workspace and database (with its Notion url), whether it is on, what each page includes, counts of responses synced, failed and not yet synced, the last error code, questions with no column, and whether a backfill is running. Returns { isOwner, sync }; sync is null when the survey is not syncing. Only the survey's owner can sync it (isOwner false otherwise).
Inputs: surveyId (required). Returns: result
notion_survey_sync_startNotion Survey Sync StartAIdempotentInspect
Start sending a syncing survey's responses to its Notion database, on the server: mode 'backfill' (default) sends every completed response that has no page yet; 'retry_failed' resends the ones that failed. Each response has one page, so nothing is duplicated, and a second call while a run is going does nothing. Returns the survey's sync; poll notion_survey_sync_get until syncInProgress is false, then report its counts.
Inputs: surveyId (required), mode. Returns: result
prolific_count_participantsProlific Count ParticipantsARead-onlyIdempotentInspect
Count Prolific participants who match a set of screeners, to check a pool is big enough. filters use Prolific's filter ids, such as those on an existing study from prolific_study_get. An invalid combination returns count 0 rather than an error. Returns { count }; creates and spends nothing.
Inputs: filters (required), study_type. Returns: result
prolific_study_calc_costProlific Study Calc CostARead-onlyIdempotentInspect
Quote what a Prolific study would cost, without creating or charging anything. reward is in cents; the result is in currency units: prolificTotalCost, platformFee, prolificTotalCostWithFee, questionpunkCharge (what QuestionPunk credits would pay; 0 when the user's own Prolific account pays), fundingSource ('connected' or 'platform') and currency. Fails if the reward is under $8.00/hour.
Inputs: reward (required), total_available_places (required), estimated_completion_time (required). Returns: result
prolific_study_getProlific Study GetARead-onlyIdempotentInspect
Get one Prolific study's live details by studyId (the id from prolific_study_list): status, submissions, places, screener filters, devices, content warnings, and reward and costs in dollars (not cents). Only the survey's owner can read it. Returns no participant data.
Inputs: studyId (required), surveyId (required). Returns: result
prolific_study_listProlific Study ListARead-onlyIdempotentInspect
List a survey's Prolific recruitment studies, newest first, with live status from Prolific (UNPUBLISHED, ACTIVE, PAUSED, ...), submissions, places taken, and reward and costs in dollars. Pass an item's id as studyId to prolific_study_get. Needs edit access to the survey. Read-only: studies are drafted, published and managed in the QuestionPunk app.
Inputs: surveyId (required). Returns: result
question_createQuestion CreateAInspect
Add one question at a position in a survey. Scales: opinion_scale itemDetails { min_value: 0 or 1 (default 1), max_value: 5 to 10 (default 10), nps: true for a 0 to 10 Net Promoter Score }, end labels in textItemDetails { min_value_label, max_value_label }; rating itemDetails { scale: 5 to 11 (default 5) }, end labels in textItemDetails { min_label, max_label }. Several short answers in one question (ideas, notes, action items): short_text itemDetails.multiple_entries = { min: 0 to 20, max: 1 to 20, min <= max }; the respondent adds one entry per row and the answer is stored as the entries joined by newlines; null turns it off. It can't be combined with unique_answer or share_results. Find a time polls (itemDetails.find_a_time, find_a_time_zone) can't be created or re-zoned here: add them in the QuestionPunk editor. Choice labels belong in item.textItemDetails.answer_options as an array of strings; item.itemDetails holds behavior settings such as multiple_selection. Example single choice: {"text":"How easy was setup?","answer_type":"multiple_choice","required":true,"textItemDetails":{"answer_options":["Easy","Difficult"]}}.
Call survey_get first for versions.current (pass it as surveyVersion) and the question count; to add at the end, surveyItemIndex = that count. The result returns the next surveyVersion for your next edit. To add several questions at the end, use question_create_bulk (one call). You write the question text; this tool generates nothing. On a published survey the new question is live at once.
For AI interviews use answer_type 'ai_moderated_interview': it supersedes ai_interview and ai_audio_interview. Set itemDetails.modality to 'text' (default) or 'voice'; put interviewGoal, greetingInstruction and avoidTopics in item.textItemDetails; its other settings match the two older types below, except context from earlier answers, which follows the modality (see the next paragraph).
Context from earlier answers: itemDetails.referenceSources = { perRespondent: { enabled, mode: 'all' | 'selected', selectedQuestionIds }, thisSurvey: { enabled, mode, selectedQuestionIds }, externalSurveys: [{ surveyId, surveyTitle, mode, selectedQuestionIds }], priorWaves?: { enabled, depth: 'last_wave' | 'all_waves', mode, selectedQuestionIds } }; thisSurvey and externalSurveys are required whenever referenceSources is sent. perRespondent is this respondent's own earlier answers, given to the interviewer as they answered (long answers shortened). thisSurvey and externalSurveys give it a summary or answer counts of other respondents' answers, never quotes, and skip types they can't summarize (email, uploads and similar) and questions with fewer than 3 answers. priorWaves is a diary participant's earlier check-ins. Voice interviews (ai_audio_interview, ai_moderated_interview with modality 'voice', usability_test) read the respondent's own answers only from referenceSources.perRespondent: reference_questions has no effect on them. Text interviews (ai_interview, ai_moderated_interview with modality 'text') read them from reference_questions with referenceQuestionsMode, so set those and referenceSources.perRespondent to the same questions.
For a numbered scale use answer_type 'opinion_scale'; a 0 to 10 scale needs itemDetails { min_value: 0, max_value: 10 }. itemDetails { nps: true } also labels the ends "Not at all likely" and "Extremely likely" in English.
Older AI Interview questions (answer_type: 'ai_interview') take:
itemDetails.length: Number of exchanges (default: 5, used in fixed_count mode)
itemDetails.reference_questions: Array of UUIDs of PREVIOUS questions. The text interviewer sees the respondent's answers to these questions as context (text interviews only; see referenceSources above).
itemDetails.personality: { kind: 'preset', code: 'expert_interviewer' | 'friendly_interviewer' | 'focus_group_moderator' | 'researcher' | 'detective' | 'stubborn_interrogator' | 'comedian' } or { kind: 'custom', id: '' }
itemDetails.thinkingMode: 'fast' (default) or 'slow' for deeper reasoning
itemDetails.questionMode: 'fixed_count' (default) | 'ai_decides' | 'time_based'
itemDetails.aiDecidesConfig: { minQuestions: number, maxQuestions: number, stoppingGuidance?: string } (for ai_decides mode)
itemDetails.durationMinutes: number (for time_based mode, default: 10)
itemDetails.advancedSettings: { temperature?: number, maxTokens?: number, enableFollowUpProbing?: boolean, responseStyle?: 'concise'|'detailed'|'conversational' }
itemDetails.aiModel: { type: 'direct', model: '' } (optional; the app picks a default)
itemDetails.showContinueButton: boolean (show start button before interview, default: false)
itemDetails.aiFailureBehavior: 'complete_survey' (default) | 'pause_survey' | 'skip_to_next' | 'skip_question'
itemDetails.endOnStopRequest: boolean (when the respondent clearly asks to stop, the interviewer asks once whether to end or keep going and a clear yes ends the interview, default: true; false = it runs to its configured length)
Older AI Audio Interview questions (answer_type: 'ai_audio_interview') take:
itemDetails.timeoutMs: Session timeout in ms (default: 300000)
itemDetails.recordingMode: 'none' | 'audio' | 'video'
itemDetails.sessionConfig: { type, sttModel, ttsModel } voice pipeline config
itemDetails.promptMode: 'standard' (default) | 'custom'
itemDetails.showContinueButton: boolean (default: false)
itemDetails.greetingInstruction: string (initial greeting text)
itemDetails.interviewGoal: string (goal for the AI interviewer)
itemDetails.avoidTopics: string (topics to avoid)
For usability tests (answer_type: 'usability_test'), the question starts with one empty website task, screen share required and voice moderation. Describe tasks in itemDetails.guidedTask.tasks[] ({ id, order, scenario, targetType: 'website' | 'prototype' | 'native_app' | 'none', ... }); fetch the question with survey_get and edit it with question_update. A Figma prototype task uses targetType 'prototype', its Figma presentation link (https://www.figma.com/proto/..., kept unchanged with its query parameters) as targetUrl, and an optional figmaPrototype { startNodeId?: 'n:m', goalNodeIds?: ['n:m', ...] (at most 10) } naming the start screen and goal screens by Figma node id; goal screens are observations in recorded sessions, never automatic task completion.
Signature, file_upload, photo_response and consent questions get working defaults; for consent, set item.textItemDetails.statement and agree_label.
Gallery voting (design sprint "art museum"): a design question (answer_type 'design') with itemDetails.gallery { sourceSurveyId, sourceItemId, pickCount?, max? } shows each respondent the images other respondents uploaded to that photo_response or file_upload question, without names. A person's own upload is left out when the source question is in the same survey, or in a diary study's earlier wave (the same participant); with a source in another, non-diary survey a person may see their own. The source question must be in a survey you own and must have itemDetails.share_with_respondents: true (set it on the upload question; uploaders are then told their upload will be shown, without their name, to other people taking part; only uploads made while it is on, through a page that showed that notice, are shown; uploads from the mobile app are not shared yet). pickCount (1-24) lets each person pick that many images (a straw poll, or 1 for a Decider's supervote); max (1-24, default 12) caps the images shown. Leave assetUrls and assetIds empty.
Inputs: item (required), lang (required), surveyId (required), surveyVersion (required), +2 more. Returns: result
question_create_bulkQuestion Create BulkAInspect
Add several questions after the existing ones, in one call; the usual way to fill a new survey. Scales: opinion_scale itemDetails { min_value: 0 or 1 (default 1), max_value: 5 to 10 (default 10), nps: true for a 0 to 10 Net Promoter Score }, end labels in textItemDetails { min_value_label, max_value_label }; rating itemDetails { scale: 5 to 11 (default 5) }, end labels in textItemDetails { min_label, max_label }. Several short answers in one question (ideas, notes, action items): short_text itemDetails.multiple_entries = { min: 0 to 20, max: 1 to 20, min <= max }; the respondent adds one entry per row and the answer is stored as the entries joined by newlines; null turns it off. It can't be combined with unique_answer or share_results. Find a time polls (itemDetails.find_a_time, find_a_time_zone) can't be created or re-zoned here: add them in the QuestionPunk editor. Each choice question requires textItemDetails.answer_options as an array of option strings. itemDetails holds non-translatable behavior settings, as in question_create (see it for AI interview and other types). You write the questions; this tool generates nothing. To insert at a position use question_create. The result returns surveyVersion for your next edit. Questions are written one by one: if one fails, the earlier ones stay saved, so read the survey with survey_get before retrying, or you will create duplicates.
Inputs: lang (required), items (required), surveyId (required), surveyVersion (required), +1 more. Returns: result
question_deleteQuestion DeleteADestructiveInspect
Delete a question, and display logic that depends on its answers. It works on published surveys too, where the question disappears for participants at once: confirm with the user before deleting from a survey that is collecting responses. A restore point is saved first (survey_version_restore, where this connection has it, can bring the question back). Needs surveyVersion = versions.current from survey_get; the result returns the next one.
Inputs: surveyItemId (required), surveyVersion (required). Returns: result
question_reorderQuestion ReorderADestructiveInspect
Move a question to a new position or between groups. Read the survey with survey_get first for positions and versions.current (pass it as surveyVersion); the result returns the next one. Changes a published survey at once.
IMPORTANT: Indices are based on the "formatted structure" where:
Root-level items and groups are sorted by order
Items inside groups are not counted in the root index
When moving FROM root level (no surveyItemGroupIdFrom):
orderIndexFrom = array index in the formatted structure (0, 1, 2...)
Groups count as 1 item at their position
When moving FROM a group (with surveyItemGroupIdFrom):
orderIndexFrom = index within that group's items (0, 1, 2...)
When moving TO root level (no surveyItemGroupIdTo):
orderIndexTo = target position in root level
When moving TO a group (with surveyItemGroupIdTo):
orderIndexTo = target position within that group's items
Example: To move item at root index 2 into a group at position 0:
orderIndexFrom: 2
orderIndexTo: 0
surveyItemGroupIdTo: "group-uuid"
Example: To move item from group position 1 back to root at index 0:
orderIndexFrom: 1
surveyItemGroupIdFrom: "group-uuid"
orderIndexTo: 0
Inputs: surveyId (required), orderIndexTo (required), surveyVersion (required), orderIndexFrom (required), +2 more. Returns: result
question_updateQuestion UpdateADestructiveInspect
Change a question's settings: answer type, required flag, internal name, or itemDetails (scale range, AI interview options). Send only what changes; nested settings are merged with the current values. Wording and answer options are not changed here: use question_update_text for those. Needs surveyVersion = versions.current from survey_get (stale versions are rejected); the result returns the next surveyVersion. On a published survey the change is live at once.
Inputs: item (required), surveyId (required), surveyItemId (required), surveyVersion (required). Returns: result
question_update_textQuestion Update TextADestructiveInspect
Rewrite a question's wording, and its answer options, matrix rows/columns or scale labels, in one language; also how you write a translation. Send the question's current answer_type and the full itemDetails for that language (all answer_options, not only the changed one), both from survey_get. langCode must be one of the survey's languages, and auto-translate must be off for it unless it is the primary language. A missing language is rejected, so add it first (survey_lang_create where this connection has it, otherwise the user adds it in the editor at editorUrl from survey_get). Needs surveyVersion = versions.current; the result returns the next one. On a published survey the new text is live at once.
Inputs: text (required), langCode (required), answer_type (required), surveyItemId (required), +2 more. Returns: result
report_ai_analysis_historyReport Ai Analysis HistoryARead-onlyIdempotentInspect
Read AI analyses already run in the QuestionPunk app for one question (surveyItemId from survey_get). Free: it starts nothing. Empty if none were run; to analyze answers yourself, read them with responses_read.
Inputs: surveyItemId (required). Returns: result
report_getReport GetARead-onlyIdempotentInspect
Read a survey's aggregated results: answer distributions and statistics per question. Use it for "what did people answer" summaries; to cross two numeric questions (rating, opinion_scale, slider or numeric) use report_get_crosstab; to break answers down by a choice question, read them with responses_read and group them; for open-text answers use responses_read. By default it counts every response (real and synthetic, any status); scope it with the filters, e.g. responseType 'real' and suspiciousLevel 'clean', and give the same filters to responses_get_counts so the denominators match.
Inputs: surveyId (required), status, dateEnd, dateStart, +3 more. Returns: result
report_get_crosstabReport Get CrosstabARead-onlyIdempotentInspect
Cross-tabulate two numeric questions of a survey. Both must be rating, opinion_scale, slider or numeric questions (ids from survey_get); any other type, such as a multiple choice question, is rejected: to break a measure down by a choice question, read the answers with responses_read and group them yourself. It takes no filters and covers all responses, real and synthetic. Report each group's base (n) alongside percentages.
Inputs: surveyId (required), colQuestionId (required), rowQuestionId (required). Returns: result
report_load_more_answersReport Load More AnswersARead-onlyIdempotentInspect
Page through one question's open-text answers as the report shows them, starting at page 1. To read many answers at once or several questions together, responses_read with questionIds is usually better. Accepts the same filters as report_get.
Inputs: surveyId (required), surveyItemId (required), page, status, +5 more. Returns: result
report_options_from_resultsReport Options From ResultsARead-onlyIdempotentInspect
Read-only. The top answers to a question, ranked, to use as another question's choices: the most common short-text entries (multiple-entry answers split into entries, case-insensitive duplicates merged), the most picked multiple choice or dropdown choices, a ranking's top items by Borda count, or a wiki survey's top-scored ideas. Several short-text questions of one survey can be pooled. Real responses only, without ballots left out of the count; text entries and ideas only from the responses the plan shows. Returns { options, results: [{ text, score, count }], sourceType, responses }. It changes nothing: to use the choices, write them with question_update. To combine similar entries into themes (a retro's notes), read more entries than you need and group them yourself before writing. Needs edit access to targetSurveyId and read access to sourceSurveyId; answers from a HIPAA team's survey are refused unless the target survey is in the same team.
Inputs: sourceSurveyId (required), targetSurveyId (required), sourceQuestionIds (required), count, +1 more. Returns: result
response_get_detailsResponse Get DetailsARead-onlyIdempotentInspect
Read every answer in one response. userSessionId comes from responses_list. To read many responses at once use responses_read.
Inputs: userSessionId (required). Returns: result
responses_exportResponses ExportARead-onlyIdempotentInspect
Get download links for a survey's responses as a spreadsheet: csvUrl and xlsxUrl (Excel). format 'wide' is one row per response; 'flat' is one row per answer. Give the user the link they need as a clickable absolute link: it downloads in a browser signed in to QuestionPunk (or with the connection's Bearer token), and is not a public share link. To analyze responses yourself, read them with responses_read instead.
Inputs: surveyId (required), format, status, dateEnd, +6 more. Returns: result
responses_get_countsResponses Get CountsARead-onlyIdempotentInspect
Count a survey's responses without fetching any of them: the way to answer "how many responses". Omitted filters count everything, including AI (synthetic) and flagged responses, so say which filters you applied; scope with responseType 'real' and suspiciousLevel 'clean' when the user means real people. Use the same filters with report_get so the numbers describe one population.
Inputs: surveyId (required), status, dateEnd, dateStart, +3 more. Returns: result
responses_listResponses ListARead-onlyIdempotentInspect
List a survey's responses (sessions), oldest first, 35 per page, with optional filters. Use it to find a userSessionId for response_get_details. For a count use responses_get_counts; to read and analyze the answers themselves use responses_read, which returns answers as JSON in larger pages.
Inputs: surveyId (required), page, status, dateEnd, +6 more. Returns: result
responses_readResponses ReadARead-onlyIdempotentInspect
Read survey responses as JSON so YOUR model can analyze, summarize or code them itself (no QuestionPunk AI credits are used). Returns { survey, questions: [{id, index, label, text, answer_type}], conditions?: [{key, label, arms}], responses: [{id, status, startedAt, completedAt, responseType, language, conditions?: {: arm}, answers: {: answer}}], page: {limit, cursor, nextCursor, total} }. conditions appears only when the survey randomly assigns respondents to A/B arms (e.g. two wordings of a question): the top-level list names each condition and its arms, and each response's conditions gives the arm it was in per key (null if none was assigned; a value not in arms is an arm renamed or removed since). To compare arms, group responses by that value. No other URL parameters are returned. Answers are rendered exactly as in the CSV export (choice labels by default; matrix = list of "Row: value"; AI interviews = list of {question, answer} turns); unanswered questions are omitted and answers over 4,000 characters are truncated with a marker. Page through with cursor = the previous page.nextCursor until it is null (up to 200 responses per page, default 50; a page may end early to stay small). To code one open-text question across many responses, pass questionIds: [thatQuestionId] (only responses that answered it are returned). For counts only, use responses_get_counts instead.
Inputs: surveyId (required), limit, cursor, status, +8 more. Returns: result
searchSearchARead-onlyIdempotentInspect
Search the connected account's QuestionPunk studies (surveys and AI interviews) by title or description. Returns up to 10 results, each with an id, title and url. Read a result with fetch.
Inputs: query (required). Returns: results
skill_createSkill CreateAInspect
Save a reusable analysis prompt as a skill. Personal by default; pass teamId (from skill_list's teams) to share it with that team. Runs with skill_run on any survey.
Inputs: name (required), prompt (required), teamId. Returns: result
skill_deleteSkill DeleteADestructiveInspect
Delete a saved skill, for everyone it is shared with. Answers it already produced stay in their chats. Only its author or an owner of its team may.
Inputs: skillId (required). Returns: result
skill_listSkill ListARead-onlyIdempotentInspect
List the analysis skills the user can run: QuestionPunk's built-ins (executive-summary, segment-comparison, key-themes, recommendations) and the skills the user and their teams saved, each with its prompt and whether the user may edit it. With surveyId, also returns segmentQuestions: the questions segment-comparison can group that survey's respondents by. Also returns the user's teams, for sharing a skill. To run a skill, use skill_prompt and analyze the answers yourself (free); skill_run has QuestionPunk's AI run it instead.
Inputs: surveyId. Returns: result
skill_promptSkill PromptARead-onlyIdempotentInspect
Get an analysis skill's prompt, ready for one survey, to run yourself: read the answers with responses_read and answer the prompt from them. Free: it spends no QuestionPunk AI credits. This is the default way to run a skill; use skill_run only when the user asks for QuestionPunk's AI to run it. segment-comparison needs segmentQuestionId from skill_list's segmentQuestions.
Inputs: skillId (required), surveyId (required), segmentQuestionId. Returns: result
skill_runSkill RunAInspect
Have QuestionPunk's AI run an analysis skill on a survey's responses, in the app's analysis chat, and return the answer; every number and quote in it is checked against the responses by QuestionPunk. Spends the user's AI credits like a chat message (a turn that cites no evidence is not charged). Use it only when the user asks for QuestionPunk to run it or for its checked analysis, and say it uses their credits; otherwise run the skill yourself with skill_prompt. Takes up to two minutes; if it returns status 'running', call skill_run_get with the same surveyId and threadId. segment-comparison needs segmentQuestionId from skill_list's segmentQuestions. Pass a threadId from an earlier run to continue that conversation; otherwise a new chat thread is created in the app.
Inputs: skillId (required), surveyId (required), threadId, segmentQuestionId. Returns: result
skill_run_getSkill Run GetARead-onlyIdempotentInspect
Read the answer of a skill run that returned status 'running' (surveyId, threadId and afterMessageId from skill_run). Free: it starts nothing. Returns status 'running' until the answer is saved.
Inputs: surveyId (required), threadId (required), afterMessageId. Returns: result
skill_updateSkill UpdateADestructiveInspect
Rename a saved skill or rewrite its prompt (its author or an owner of its team), or change who it is shared with (teamId, or null for personal; its author only). Built-in skills cannot be changed.
Inputs: skillId (required), name, prompt, teamId. Returns: result
studio_use_edited_versionStudio Use Edited VersionAInspect
Save a Studio board as a new revision of the production candidate or tested concept it was opened from. The old revision and everything tested or published from it stay unchanged; the new one starts unreviewed. An unchanged board returns its latest revision. Spends nothing.
Inputs: designProjectId (required). Returns: result
survey_check_custom_urlSurvey Check Custom UrlARead-onlyIdempotentInspect
Check whether a custom URL slug for the participant link is free. Returns true when it is available. Read-only. The slug is set with survey_update_props where this connection has it, otherwise in the editor.
Inputs: customUrl (required). Returns: result
survey_createSurvey CreateAInspect
Create a new, empty draft survey. Returns it as survey_get does: id, versions.current, shareUrl and editorUrl. Add questions next with question_create_bulk, passing versions.current as surveyVersion. To start from a template use template_create_survey_from; to copy a survey, survey_duplicate. Not idempotent: each call creates another survey, so after a timeout check survey_list before retrying. Give the user editorUrl as a clickable link.
Inputs: title (required), lang, teamId. Returns: result
survey_duplicateSurvey DuplicateAInspect
Copy a survey (questions, logic, settings) into a new survey and return the copy as survey_get does, with its id, shareUrl and editorUrl. Pass title to name the copy in the same call. Not idempotent: each call makes another copy, so after a timeout check survey_list before retrying. Give the user the copy's editorUrl as a clickable link.
Inputs: surveyId (required), title. Returns: result
survey_getSurvey GetARead-onlyIdempotentInspect
Read one survey: its questions (ids, answer types, options), languages, screens, status, and versions.current, the surveyVersion every edit needs. Call it before editing or analyzing a survey to get question ids and the current version. Returns shareUrl, the participant link (it takes responses only once status is published), and editorUrl, the owner's editor; give the user the one they need as a clickable absolute link. surveyId comes from survey_list.
Inputs: surveyId (required). Returns: result
survey_listSurvey ListARead-onlyIdempotentInspect
Find surveys. Use it to turn a survey the user names into a surveyId: pass a word from its title as search. Returns [counts, surveys, labels]: surveys is this page (up to 35; ask for the next page when it is full); labels names the counts ({ counts: { allSurveys, updatedInLast3Days, starred }, page, pageSize }), which cover every survey you can access, not only this search. If several surveys match, ask the user which one.
Inputs: page, method, search, sortBy, +5 more. Returns: result
survey_publishSurvey PublishADestructiveInspect
Publish a survey so its participant link takes responses. Publish only when the user has asked to. Returns the stored status, surveyVersion and shareUrl (also as respondentLink): give the user shareUrl as a clickable absolute link to send to participants; editorUrl is the owner's editor. The status is read back after publishing, so no survey_get is needed to confirm it.
Inputs: surveyId (required). Returns: result
survey_update_descriptionSurvey Update DescriptionADestructiveInspect
Set, or clear with null, a survey's description in one of its languages. lang must already be on the survey, and for any language other than English auto-translate must be off. Needs version = versions.current from survey_get; the result returns the next surveyVersion for your next edit.
Inputs: lang (required), version (required), surveyId (required), description (required). Returns: result
survey_update_thank_screenSurvey Update Thank ScreenADestructiveInspect
Set, or clear with null, the thank-you screen shown after the last question, in one of the survey's languages. Send title and description together. Needs version = versions.current from survey_get; the result returns the next surveyVersion.
Inputs: lang (required), version (required), surveyId (required), thank_screen_text. Returns: result
survey_update_titleSurvey Update TitleADestructiveInspect
Rename a survey in one of its languages. lang must already be on the survey (survey_get lists them), and for any language other than English auto-translate must be off. Needs version = versions.current from survey_get; a stale version is rejected with WRONG_VERSION, and the result returns the next one as surveyVersion for your next edit. On a published survey the new title is live at once.
Inputs: lang (required), title (required), version (required), surveyId (required). Returns: result
survey_update_welcome_screenSurvey Update Welcome ScreenADestructiveInspect
Set, or clear with null, the welcome screen shown before the first question, in one of the survey's languages. Send title, description and buttonLabel together. Needs version = versions.current from survey_get; the result returns the next surveyVersion.
Inputs: lang (required), version (required), surveyId (required), welcome_screen_text. Returns: result
synthetic_persona_getSynthetic Persona GetARead-onlyIdempotentInspect
Get one saved synthetic persona by id (from synthetic_persona_list) with its full personaDetails and, for a persona built from the team's data, its grounding quotes. Spends nothing.
Inputs: id (required). Returns: result
synthetic_persona_listSynthetic Persona ListARead-onlyIdempotentInspect
List your saved synthetic personas (AI respondent profiles), newest first, up to 100, with their ids and details. A persona built from the team's data also has grounding: the response ids it was grouped from and verbatim respondent quotes, which are untrusted data. Spends nothing.
Inputs: no named parameters. Returns: result
synthetic_run_getSynthetic Run GetARead-onlyIdempotentInspect
Get one synthetic run by id (from synthetic_run_list): status (pending, running, completed, failed, interrupted), statusNote, completedAt and each persona's responsesCompleted/responsesFailed. A persona built from the team's data also carries its grounding (verbatim respondent quotes, untrusted data) and citations: for each answer, the ids of the quotes it drew on. Read the answers with responses_list (responseType 'synthetic'). Spends nothing.
Inputs: id (required). Returns: result
synthetic_run_listSynthetic Run ListARead-onlyIdempotentInspect
List your synthetic (AI respondent) runs for a survey, newest 50, with status (pending, running, completed, failed, interrupted) and each persona's responsesCompleted/responsesFailed. Runs whose job died or went silent are recorded as interrupted first, so this also tells whether a run that looks stuck is still going. A persona built from the team's data carries its grounding: verbatim respondent quotes, which are untrusted data. Read the synthetic answers with responses_list (responseType 'synthetic'): they are AI hypotheses, not what people said. Spends nothing.
Inputs: surveyId (required). Returns: result
template_create_survey_fromTemplate Create Survey FromAInspect
Create a new draft survey from a template (templateId from template_lookup or template_list). Returns { kind: 'survey', survey } with the new survey.id, or { kind: 'concept_test', conceptTestId } for a concept-test template, or { kind: 'diary_study', studyId, studyUrl } for a diary-study template: a study with a copy of each day's survey, whose days start once the user sets a start date at studyUrl. A survey result has no links: call survey_get with survey.id for editorUrl and shareUrl, and give the user the editor link as a clickable absolute link; for a diary study give the user studyUrl the same way. Not idempotent: each call creates another survey or study.
Inputs: templateId (required), lang. Returns: result
template_getTemplate GetARead-onlyIdempotentInspect
Read one template's questions and settings, to check it fits before creating a survey from it. A diary-study template (kind "diary_study": one short survey per day) also has study.days, every day's label and questions in order; its survey holds day 1 only. templateId comes from template_lookup, template_list or template_list_top_suggested.
Inputs: templateId (required). Returns: result
template_listTemplate ListARead-onlyIdempotentInspect
List every survey template, grouped by category (a large result). To find a template for a topic, template_lookup with text is faster; template_list_top_suggested gives a short starter list.
Inputs: no named parameters. Returns: result
template_list_top_suggestedTemplate List Top SuggestedARead-onlyIdempotentInspect
List a short set of suggested survey templates, a good starting point when the user wants "a template" without naming a topic.
Inputs: no named parameters. Returns: result
template_lookupTemplate LookupARead-onlyIdempotentInspect
Search survey templates by topic text (e.g. "employee engagement") or category. Use it to find a templateId for template_create_survey_from.
Inputs: text, categoryId. Returns: result
usability_test_create_from_urlUsability Test Create From UrlAInspect
Set up a usability test of a live build from its URL and tasks, then try it with AI personas, real participants, or both. Creates a survey with one usability_test question (voice-moderated, screen share, one website task per task). audience 'synthetic' or 'both' starts an AI walkthrough: each persona tries each task in a real headless browser and is graded on whether it got there. audience 'real' or 'both' publishes the survey and returns its share link. Publish only when the user asked for real participants. Not idempotent: each call creates another survey. Uses QuestionPunk AI credits for the walkthrough.
Inputs: url (required), tasks (required), audience (required), lang, +2 more. Returns: result
usability_test_findings_getUsability Test Findings GetARead-onlyIdempotentInspect
Read a usability test's findings: the AI walkthrough (the latest, or walkthroughId) with its status, per-task success, where personas got stuck, what they said while trying, and every attempt's steps with a link to its last screenshot; and the real participants' task results. Poll while walkthrough.status is queued or running. AI results are hypotheses: never present them as what people did.
Inputs: surveyId (required), walkthroughId. Returns: result
usability_walkthrough_startUsability Walkthrough StartAIdempotentInspect
Run an AI walkthrough of an existing usability test: each persona tries each of its website tasks in a real headless browser, and each attempt is graded. Pass url to try the same tasks on a new deploy (the stored test keeps its own URL). Returns at once; poll usability_test_findings_get. The same clientRequestId returns the same walkthrough. Uses QuestionPunk AI credits.
Inputs: surveyId (required), questionId (required), clientRequestId (required), url, +3 more. Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
Related MCP Connectors
Create, edit and publish surveys, then read results from your AI chat.
NPS, eNPS, CSAT and CES surveys with AI follow-up interviews: create, invite, read results.
AI survey platform: create and publish surveys, collect responses, generate AI analysis reports.
Build, publish and share surveys and forms, then read what people answered.
Related MCP Servers
AlicenseNot gradedqualityBmaintenanceEnables 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- AlicenseNot gradedqualityCmaintenanceEnables creating surveys, validating questions, collecting responses, and generating analysis reports.7 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables users to generate and publish AI surveys, retrieve collected responses, and produce analysis reports with executive summaries, key metrics, and per-question insights by forwarding tool calls to a hosted survey backend.280 npmMIT
- AlicenseNot gradedqualityDmaintenanceMCP server for creating AI-moderated interviews and surveys, deployable with shareable links2MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.