Well-curated SurveyMonkey MCP server with 19 focused tools. Strong naming conventions, typed parameters, and contextual descriptions. Most tools include trigger phrases and USE THIS WHEN guidance. However, some descriptions are truncated (cut off mid-sentence) and a few tools use overly generic "additionalProperties: true" for complex objects. Tool count is ideal (19 is well within the 3-40 sweet spot). No evidence of auto-generation - this appears hand-crafted for survey workflows.
Add a new page to a survey. The page is inserted at the specified position. A page cannot be added to a survey that has responses. **USE THIS WHEN:** User wants to add, create, or insert a new page in a survey. **NOTE:** Newly created surveys already have a default first page. Call `get_pages` first to check existing pages before adding new ones. Only use this tool when you need ADDITIONAL pages beyond the default. **TRIGGER PHRASES:** - "add a page..." - "create a new page..." - "insert a page..." - "add a section called..." - "add ... at the beginning/end/position X" **IMPORTANT:** Call this tool directly - do not stop after calling get_surveys or get_pages. If the user says "add a page at the beginning", use position=1. For "at the end", use position=0. After gathering any needed info (like survey_id), you MUST call add_page to complete the task. Never stop at just reading information - always follow through with the add_page call. Args: survey_id: The ID of the survey position: Position to insert the page (1-based). Use 0 to add at the end. page: Dictionary describing the new page. Fields: - title: Page title (string, required) - description: Optional page description (string) Returns: Dictionary containing the created page with its assigned ID Example: add_page(survey_id="123456", position=0, page={"title": "Demographics"})
Add a question to a survey page. A question cannot be added if the survey has responses. **USE THIS WHEN:** User wants to add, create, or insert a question in a survey. **TRIGGER PHRASES:** - "add a question..." - "add a multiple choice question..." / "add a single choice question..." - "add a star rating question..." / "add a rating question..." - "add an email question..." / "add a contact info question..." - "add an NPS question..." / "add a Net Promoter Score question..." - "add a text question..." / "add an open-ended question..." / "add a comment box..." - "add a matrix question..." / "add a grid question..." - "create a question asking..." - "insert a question at the end/beginning..." **QUESTION TYPE REFERENCE:** - Star rating: family="matrix", subtype="rating", display_options={"display_type":"emoji","display_subtype":"star"} - Email: family="demographic", subtype="email" - NPS (Net Promoter Score): Use question bank format with question_bank_question_id="669": question={"question_bank": {"question_bank_question_id": "669", "modifier_options": {"36628": null}}} - Multiple choice: family="multiple_choice", subtype="vertical" - Single choice: family="single_choice", subtype="vertical" - Open text: family="open_ended", subtype="essay" (comment box) or "single" (short answer) Call get_question_types for the full list of available types. **WORKFLOW:** 1. If you need the page_id, call get_pages first to find the target page 2. Then call add_question to create the question - do NOT stop after reading **POSITION:** Use position=0 to add at end of page, position=1 for first, etc. Args: survey_id: The ID of the survey page_id: The ID of the page to add the question to position: Position on page for question (0 = end of page) question: Dictionary describing the question. Required fields: - family: Question family (e.g., "single_choice", "multiple_choice", "open_ended") - subtype: Question subtype (e.g., "vertical", "menu", "essay") - headings: List with at least one heading object: [{"heading": "Question text"}] - answers: Answer choices object (for choice questions) Returns: Dictionary containing the created question with its assigned ID Example (single choice): add_question( survey_id="123456", page_id="789012", position=0, question={ "family": "single_choice", "subtype": "vertical", "headings": [{"heading": "How satisfied are you?"}], "answers": { "choices": [ {"text": "Very satisfied"}, {"text": "Satisfied"}, {"text": "Neutral"}, {"text": "Dissatisfied"} ] } } )
Description text is truncated/cut off mid-sentence for multiple tools (generate_survey_plan, create_survey, add_page, add_question). This prevents agents from fully understanding tool purpose and usage.
Several tools use 'additionalProperties: true' for complex object parameters (page, question, patch, answers, validation) instead of fully typed schemas. This reduces clarity about what properties are expected.
create_survey description states 'Docstring set dynamically below based on USE_BWAI_SURVEY_CREATE' - appears to be a placeholder that was never filled in properly.
edit_question schema is incomplete - the input definition appears cut off after 'target_page_id' parameter.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-04-14 | C | 64 | 2025-11-25 | v1 |
Docstring set dynamically below based on USE_BWAI_SURVEY_CREATE.
Create a weblink collector for a survey. A weblink collector generates a shareable URL that allows respondents to take your survey. Use this to create a link you can share via email, social media, or embed on a website. The new weblink has a status of open and is ready to collect responses immediately. Args: survey_id: The ID of the survey (string) name: Name for the weblink collector (string) Returns: Dictionary containing: - id (str): Collector ID - name (str): Collector name - href (str): API URL for the collector - url (str): Public survey URL to share with respondents - status (str): Status of the collector (e.g. "open", "closed") Example: { "id": "987654321", "name": "My Weblink", "href": "https://api.surveymonkey.com/v3/collectors/987654321", "url": "https://www.surveymonkey.com/r/ABC123", "status": "open" }
Delete a question from a survey. A question cannot be deleted if the survey has responses. **USE THIS WHEN:** User wants to delete, remove, or get rid of a question. **TRIGGER PHRASES:** - "delete question..." - "remove the question..." - "delete question X from..." - "remove question X..." - "get rid of question..." **IMPORTANT:** Call this tool directly to delete the question. If you need to find the question_id or page_id first: 1. Call get_questions to find the question details 2. Then immediately call delete_question to remove it Do not stop after reading - always follow through with the delete_question call. **PREREQUISITE:** You need survey_id, page_id, and question_id. Use get_questions first if needed. Args: survey_id: The ID of the survey containing the question page_id: The ID of the page containing the question question_id: The ID of the question to delete Returns: Dictionary with deletion status: {"status": "deleted"} Example: delete_question(survey_id="123456", page_id="789012", question_id="345678")
Edit, modify, update, move, reword, or reposition a question. Cannot edit if survey has responses. This is the ONLY tool for changing a single question. Use this for: - Changing question text/wording (text parameter) - reword, rephrase, rewrite, make less leading/biased - Making required/optional (required parameter) - Moving to position 1, 2, 3, etc. (position parameter) - Moving to another page (target_page_id parameter) **TRIGGER PHRASES - use this tool when you see:** - "reword the question..." / "rephrase..." / "rewrite..." / "change the wording..." - "make the question less leading/biased/confusing" - "edit/modify/update question..." - "move question to position..." / "put question first/last" - "change question text to..." **WORKFLOW for editing by description (e.g., "reword the satisfaction question"):** 1. First call get_questions to find the question matching the description 2. Then call THIS tool (edit_question) with the question_id and new text **KEY DISTINCTION:** "reorder question 123" = use THIS tool. "reorder ALL questions" = use reorder_questions. **NPS/QUESTION BANK RESTRICTIONS:** NPS questions (question_bank_question_id="669") have restricted modifications: - CANNOT change: answer choices/scale (standardized for benchmarking), question family/subtype - CAN change: position, required status, page location **IMPORTANT:** After calling get_questions to find a question, you MUST call edit_question to complete the task. Never stop after just reading - always follow through with the edit action. Args: survey_id: The ID of the survey containing the question page_id: The ID of the page currently containing the question question_id: The ID of the question to edit text: New question text/heading (e.g., "What is your favorite color?") required: Whether the question requires an answer (True/False) answers: Update answer choices - format: {"choices": [{"text": "Option 1"}, {"text": "Option 2"}]} validation: Update validation rules (e.g., {"min": 1, "max": 100} for numeric questions) position: New position on the page (1-based). Use this to reorder within a page. target_page_id: Move question to this page. If different from page_id, performs cross-page move. Returns: Dictionary containing the edit result including new question ID if moved Example (change question text): edit_question(survey_id="123", page_id="456", question_id="789", text="What is your favorite color?") Example (make question required): edit_question(survey_id="123", page_id="456", question_id="789", required=True) Example (move to position 1 on same page): edit_question(survey_id="123", page_id="456", question_id="789", position=1) Example (move to different page): edit_question(survey_id="123", page_id="456", question_id="789", target_page_id="012", position=1) Example (combined - change text and position): edit_question(survey_id="123", page_id="456", question_id="789", text="New question text", position=2)
Generate an AI survey plan from a natural language DESCRIPTION of survey goals/content. Returns a JSON plan with suggested title and questions. The plan is NOT persisted — after receiving the plan, use create_survey + add_question to build the actual survey. **USE THIS WHEN:** User describes survey TOPIC, PURPOSE, or CONTENT (not an exact title). **TRIGGER PHRASES - Use this tool when you see:** - "create a survey **about** [topic]" - "generate a survey **for** [purpose]" - "build a survey **to** [goal]" - "survey measuring [metric]" - "feedback survey" (without explicit title) - "survey with questions about [topics]" **DO NOT USE FOR:** - "Survey **called** [name]" → Use create_survey instead - "Survey **named** [name]" → Use create_survey instead - "Survey **titled** [name]" → Use create_survey instead **KEY DISTINCTION:** - "Survey about employee satisfaction" → THIS TOOL (describes content) - "Survey called Employee Satisfaction Survey" → create_survey (exact title) **WORKFLOW:** 1. Call this tool with a description to get a survey plan 2. Call create_survey(title=plan["survey_title"]) to create the empty survey 3. Call get_pages(survey_id) to get the default page 4. For each question in the plan, call add_question(...) mapping the question type to the correct PAPI family/subtype (use get_question_types for reference) Args: description: Natural language describing survey purpose, topics, or content (e.g., "customer satisfaction survey for a coffee shop with questions about service quality, product quality, and likelihood to recommend") Returns: Dictionary containing AI-generated survey plan: { "status": "success", "status_code": 200, "predictions": [{ "survey": { "status": "succeed", "survey_title": "Coffee Shop Customer Satisfaction Survey", "detected_language": "en", "questions": [ { "type": "single_choice", "title": "How satisfied are you with our service?", "answer_choices": ["Very satisfied", "Satisfied", ...] } ] } }] } Error Handling: - Invalid Input (400): Validation errors or improper request format - Model Disabled (403): Model temporarily unavailable - Model Not Found (404): Model version not available - Rate Limiting (429): Too many requests - User Temporarily Blocked (430): User-level rate limiting
Get details about a specific page. **PREREQUISITES:** - survey_id: Get this from `search_surveys` - page_id: Get this from `get_pages(survey_id)` Args: survey_id: The ID of the survey containing the page (get from search_surveys) page_id: The ID of the page to retrieve (get from get_pages) Returns: Dictionary containing page details
Get pages for a survey. **PREREQUISITE:** You need a survey_id. To get survey IDs, use `search_surveys` first. **USE THIS WHEN:** User needs page IDs from a survey. The returned `id` field for each page is what `get_questions`, `get_page`, and other page-related tools need. **ID HIERARCHY:** - `search_surveys` → survey_id - `get_pages(survey_id)` → page_id (this tool) - `get_questions(survey_id, page_id)` → question_id Args: survey_id: The ID of the survey (get this from search_surveys) page: Page number for pagination (default: 1) per_page: Number of pages per page (default: 50, max: 100) Returns: Dictionary containing pages list and pagination info
Get details about a specific question. **PREREQUISITES:** - survey_id: Get this from `search_surveys` - page_id: Get this from `get_pages(survey_id)` - question_id: Get this from `get_questions(survey_id, page_id)` Args: survey_id: The ID of the survey containing the question (get from search_surveys) page_id: The ID of the page containing the question (get from get_pages) question_id: The ID of the question to retrieve (get from get_questions) Returns: Dictionary containing question details
Get available question types and their schemas. The list includes all question types regardless of the user's plan. **NOTE:** This returns types for MANUAL question creation only. NPS questions use the question bank format (question_bank_question_id="669"). See add_question documentation for NPS question creation. Returns: Dictionary containing question types list
Get questions for a specific page in a survey. **PREREQUISITES:** - survey_id: Get this from `search_surveys` - page_id: Get this from `get_pages(survey_id)` - REQUIRED **IMPORTANT:** page_id is REQUIRED. Questions are anchored under pages in the SurveyMonkey Public API. To get all questions in a survey, first call `get_pages()` then call this for each page. **ID HIERARCHY:** - `search_surveys` → survey_id - `get_pages(survey_id)` → page_id - `get_questions(survey_id, page_id)` → question_id (this tool) Args: survey_id: The ID of the survey (get from search_surveys) page_id: The ID of the page (get from get_pages - REQUIRED) page: Page number for pagination (default: 1) per_page: Number of questions per page (default: 50, max: 100) Returns: Dictionary containing questions list and pagination info
Get the number of responses received for a survey. Use this to check how many people have submitted responses to a survey. This is useful for determining if a survey has collected any data before attempting operations like analysis or determining if the survey can be modified. Args: survey_id: The ID of the survey (string) Returns: Dictionary containing: - response_count (int): Number of responses submitted Example: {"response_count": 42}
Get survey responses with full answer data. Retrieves responses for a survey including all answer details. Supports pagination for surveys with many responses. Automatically enriches responses with question headings and answer choice text. **Required OAuth scopes:** responses_read, responses_read_detail **Plan limitations:** Basic plan accounts are limited to the first 25 responses. Attempting to retrieve beyond this limit will return an UpgradeRequired error. To access all responses, upgrade to a paid plan. **Pagination:** Check if `total > len(data)` to determine if more pages exist. Use the `page` parameter to retrieve additional pages. Args: survey_id: The ID of the survey (string or int) page: Page number for pagination (default: 1, minimum: 1) per_page: Number of responses per page (default: 10, max: 100) Returns: Dictionary containing: - data (list): List of response objects with enriched answer data - page (int): Current page number - per_page (int): Number of items per page - total (int): Total number of responses Example success response: { "data": [ { "id": "12345", "survey_id": "67890", "pages": [ { "id": "page_1", "questions": [ { "id": "q1", "heading": "How satisfied are you?", "family": "single_choice", "answers": [ { "choice_id": "123", "choice_text": "Very Satisfied" } ] } ] } ] } ], "page": 1, "per_page": 10, "total": 42 }
Returns information about the MCP server. Returns: Dictionary containing server information
Get details about a specific survey. **PREREQUISITE:** You need a survey_id. To get survey IDs, use `search_surveys` first which returns a list of surveys with their `id` fields. **USE THIS WHEN:** User already has a survey ID and wants its details. **DO NOT USE WHEN:** User doesn't know their survey ID - use `search_surveys` first. **IF search_surveys FAILS:** Tell the user there's a temporary issue and to try again later. Do NOT suggest looking at URLs, web UI, or other methods to find the survey ID. **RETURNS:** Survey metadata including: id, title, href, preview URL, is_owner, date_modified, date_created, language, nickname, folder_id, and question_count. Owner user ID is not returned. Args: survey_id: The numeric ID of the survey (get this from search_surveys) Returns: Dictionary containing survey details (preview, is_owner, dates, language, nickname, folder_id, question_count, and core id/title/href).
Bulk operation: reorder ALL questions on a page. Requires a list of ALL question IDs. **USE THIS WHEN:** - User provides a COMPLETE LIST of question IDs in a new order - User wants to move a GROUP of questions (e.g., "move demographic questions to end") - User wants to reorganize the entire page order **WORKFLOW for "move X questions to end" requests:** 1. Call get_questions to get ALL questions on the page 2. Identify which questions match the user's criteria (e.g., demographic type) 3. Reorder the list with matching questions at the end 4. Call this tool with the complete reordered list **NEVER use for single-question operations.** If user mentions just ONE question (like "move question 123 to first"), use edit_question instead. **KEY RULE:** This tool requires ALL question IDs on the page. You must: 1. First call get_questions to get the current list 2. Reorder the IDs as needed 3. Pass the complete reordered list to this tool Args: survey_id: The ID of the survey page_id: The ID of the page to update question_order: List of ALL question IDs on the page in desired order (must include every question) Returns: Dictionary with updated questions list for the page, or partial failure info
Get a list of surveys for the authenticated user. **DISCOVERY NOTE:** This is the PRIMARY tool for discovering survey IDs. When a user asks "how do I get a survey ID" or "what's my survey ID", use this tool first. The returned `id` field is what other tools need. **USE THIS WHEN:** User wants to see, view, list, or browse their surveys, OR when user needs to find a survey ID. **TRIGGER PHRASES:** - "show my surveys" - "list my surveys" - "what surveys do I have?" - "view my questionnaires" - "how many surveys do I have?" - "get all my surveys" - "how do I get a survey ID?" - "what's my survey ID?" - "find my survey called..." **IF THIS TOOL FAILS:** Tell the user there's a temporary issue and to try again later. Do NOT suggest looking at URLs, web UI, or other methods to find survey IDs. **AUTHENTICATION:** Requires a valid bearer token in the Authorization header. The token is automatically extracted from the request context. **PAGINATION HANDLING - MANDATORY:** This tool returns PAGINATED results (default 10 per page). The response includes: - `data`: Array of surveys returned in this page - `page`: Current page number - `per_page`: Maximum surveys per page (default: 10, max: 100) - `total`: Total number of surveys across ALL pages **YOU MUST ALWAYS:** 1. Check if `total` > `len(data)` - this means results are PARTIAL 2. If partial, ALWAYS tell the user: "You have {total} surveys. Showing {len(data)} of {total}." 3. NEVER say "Here are your surveys" or "Here are all X surveys" when showing partial results 4. For "how many" questions, ALWAYS report the `total` field value, NOT len(data) **REQUIRED RESPONSE FORMAT when total > len(data):** ✓ "You have 35 surveys total. Here are the first 10:" ✓ "Showing 10 of 35 surveys:" ✗ "Here are your surveys:" (WRONG - doesn't indicate partial results) ✗ "Here are your 10 surveys:" (WRONG - implies this is all of them) **QUERY PARAMETER - WHEN TO USE:** The `query` parameter filters surveys by title/nickname matching. **USE query parameter WHEN user says:** find, search, look for, locate, + specific topic - "find surveys about cats" → query="cats" - "search for customer feedback" → query="customer feedback" - "look for NPS surveys" → query="NPS" - "find my satisfaction surveys" → query="satisfaction" **DO NOT use query parameter WHEN user says:** list, show, view, display, get all - "list my surveys" → NO query parameter - "show all my surveys" → NO query parameter - "what surveys do I have" → NO query parameter **DECISION RULE:** If the user mentions a TOPIC to search for, use query. If they just want to see their surveys, don't use query. Args: query: Optional search term that will match survey titles and nicknames with fuzzy matching page: Page number for pagination (default: 1, minimum: 1) per_page: Number of surveys per page (default: 10, max: 100) filters: Optional filters. Valid keys: - start_date: ISO 8601 date-time string (e.g. 2024-01-01T00:00:00Z) - end_date: ISO 8601 date-time string (e.g. 2024-12-31T23:59:59Z) - folder_id: integer folder ID - survey_state: one of "DRAFT", "OPEN", "CLOSED" sort_by: Optional sort field choose from: [ date_modified, title, nickname, survey_state, response_count ] sort_order: Optional sort order (asc/desc) Returns: Dictionary containing surveys search results and pagination info Example response: { "data": [ { "id": "123456789", "title": "Survey title 1234", "nickname": "", "survey_state": "OPEN", "folder_id": 0, "response_count": 0, "date_modified": "2026-02-12T12:00:00Z", "href": "https://api.surveymonkey.com/v3/surveys/125166945" } ], "page": 1, "per_page": 10, "total": 50 } Note: In this example, `total` (50) > `len(data)` (1), indicating 49 more surveys on subsequent pages. Error responses: - {"error": "missing_auth", "message": "Missing Authorization header"} - {"error": "invalid_auth", "message": "Invalid Authorization header format..."} - {"error": "unauthorized", "message": "Invalid or expired access token"} - HTTP 401 - {"error": "forbidden", "message": "Token lacks required scopes"} - HTTP 403 - {"error": "api_error", "message": "...", "status_code": N} - Other HTTP errors - {"error": "timeout", "message": "Request to PAPI timed out"} - {"error": "service_error", "message": "Failed to connect to PAPI service"}
Update survey properties like title or nickname. A survey with responses cannot be updated. **USE THIS WHEN:** User wants to rename a survey, change the survey title, or update survey properties. **TRIGGER PHRASES:** - "rename the survey to..." - "change the survey title to..." - "update the survey name..." - "call this survey..." - "set the survey nickname..." **IMPORTANT:** Call this tool directly to make the change. Do not just read information first - take action immediately when the user requests a rename or title change. Args: survey_id: The ID of the survey to update patch: Dictionary with fields to update. Common fields: - title: New survey title (string) - nickname: Optional survey nickname (string) Returns: Dictionary containing the updated survey details Example: update_survey(survey_id="123456", patch={"title": "Customer Feedback Q1 2026"})