MCP Interface¶
Askalot supports the Model Context Protocol (MCP)—an open standard that enables AI assistants to interact directly with the platform. This allows AI agents to create surveys, manage campaigns, execute questionnaire flows, and analyze results through natural language conversations.
What is MCP?¶
MCP is a standardized protocol that lets AI systems like Claude interact with external tools and data sources. With Askalot's MCP interface, you can:
- Automate survey workflows - Create projects, questionnaires, and campaigns through AI conversations
- Execute surveys programmatically - Navigate questionnaire flows and submit responses
- Manage respondents - Import, organize, and assign survey targets to campaigns
- Query audit trail - Track changes and ensure compliance
- Access documentation - AI agents can read platform documentation for context
Connecting to the MCP Interface¶
Endpoint¶
Replace <tenant> with your organization's tenant name.
Authentication¶
The MCP interface authenticates with OAuth 2.1, not a pasted API token. On the first tool call, Portor returns 401 with an OAuth discovery hint and your MCP client (for example, the Askalot plugin for Claude Code) walks you through a browser sign-in against the platform OIDC provider. The client then stores the resulting access + refresh tokens and sends the access token as Authorization: Bearer on every call — you never handle the bearer value yourself.
Configuring the endpoint (URL only):
The only thing you configure is the server URL. The Askalot plugin reads it from the ASKALOT_MCP_URL environment variable; a plain .mcp.json entry carries the same URL and nothing else — no token field:
{
"mcpServers": {
"askalot": {
"type": "http",
"url": "https://portor.<tenant>.askalot.io/mcp"
}
}
}
Signing in (first use):
- Point the client at your tenant (
export ASKALOT_MCP_URL=https://portor.<tenant>.askalot.io/mcp). - Trigger any MCP tool. The client opens your browser to Portor's
/authorizeendpoint, which redirects to the platform OIDC login. - Sign in and approve the consent screen. The client exchanges the code for tokens and retries the original call.
- The browser handshake happens once; the refresh token is exchanged transparently afterward.
REST/CI uses a token instead
The browser OAuth flow is for interactive MCP clients. For non-interactive REST or CI use, generate a classic API token in Roundtable → Profile Settings and send it in the X-Api-Token header on the REST API. That token authenticates REST only — it is not the credential for the MCP transport, which resolves OAuth (or synthetic service) bearer tokens exclusively.
Your Active Organization¶
Your MCP session is always in exactly one organization at a time — its active organization — and every tool call resolves against it. Projects, questionnaires, campaigns, respondents and datasets are all organization-scoped, so this is the single most important thing to know about where a tool is looking.
Where you start. A fresh session lands in your home organization, the one your account belongs to. Nothing asks you to choose during sign-in.
Seeing it. list_organizations returns every organization the session may act in, marks which one is active, and flags any that are read-only. The list is exactly what the Switch Organization menu offers you in the browser — if you belong to one organization, that is the whole list and there is nothing to manage.
Changing it. switch_organization moves the session to another organization from that list. Every later call resolves in the new one, and the change persists: restart your MCP client and you resume where you left off. The tenant is fixed when you sign in and never changes, so switching only ever moves you between organizations inside one tenant.
A not-found tells you where it looked
Because reads are filtered by the active organization, an entity that lives elsewhere is simply absent — which reads exactly like an entity that never existed. Askalot names the organization it searched:
That is the signal to call list_organizations and switch, rather than concluding the project does not exist. The message names only the organization that was searched, never the one that owns the entity.
Sealed Organizations¶
Some organizations are sealed — a curated demo, or a former customer's data kept available for its retention window. list_organizations marks them, so you can tell before you act.
Inside a sealed organization, reading works and anything that would change data is refused with org_read_only. Listing and fetching projects, campaigns, questionnaires, respondents, pools, datasets and users all keep working, as do the documentation, audit and methodology-library tools; creating, updating and deleting are refused. Each tool declares whether it reads or writes, and that declaration is what the refusal is driven by — so the boundary is a property of the tool, not a guess about its name.
list_organizations and switch_organization both keep working there, so a session can always see where else it may go and leave.
Sealing also disables API tokens for that organization: you cannot generate one, and an existing one stops working. The OAuth sign-in above is unaffected.
Available Tools¶
The MCP interface provides tools organized by functionality.
Session and Organizations¶
| Tool | Description |
|---|---|
list_organizations |
List the organizations this session may switch into, marking the active one and flagging read-only ones |
switch_organization |
Move the session's active organization — every later call resolves against it |
Project Management¶
| Tool | Description |
|---|---|
list_projects |
List all your projects |
get_project |
Get project details |
create_project |
Create a new project |
add_project_owners |
Add users as project owners (targets must belong to the project's organization) |
remove_project_owners |
Remove users from project ownership (same-organization rule applies) |
Questionnaire Operations¶
| Tool | Description |
|---|---|
list_questionnaires |
List all questionnaires |
get_questionnaire |
Get questionnaire details |
create_questionnaire |
Create a new questionnaire |
rename_questionnaire |
Rename a questionnaire (address unchanged) |
delete_questionnaire |
Soft-delete a questionnaire |
restore_questionnaire |
Restore a recently soft-deleted questionnaire |
list_qml_files |
List the organization's QML questionnaires |
inspect_qml_file |
Quick summary of a questionnaire's current version |
get_qml_content |
Retrieve a questionnaire's current QML (returns a short-lived download link instead of inline content above 32 KB) |
validate_qml_file |
Run Z3 SMT validation on a questionnaire's current version (reachability, consistency) |
qml_quality_report |
Compute the D2–D8 design-quality scorecard for a questionnaire's current version |
save_qml_file |
Save QML as a questionnaire's new version — never blocks on broken drafts (content over 32 KB is refused; use the upload handle below) |
request_upload_url / finalize_upload |
Two-step handle for saving QML content over save_qml_file's 32 KB limit |
publish_qml_file |
Publish a questionnaire to a campaign — formally validates and pins the exact version (the only gated door) |
move_qml_to_project |
Move a questionnaire to another project (version history preserved) |
unpublish_qml_file |
Soft-delete a questionnaire, retaining its version history (blocked if active campaigns) |
Parameter Reference — parameters, return schemas, QML lifecycle, Z3 output structure
Document Discovery¶
| Tool | Description |
|---|---|
list_indexed_documents |
List all documents indexed for AI semantic search |
get_document_summary |
Get the stored Markdown summary for a specific document |
get_document_chunk |
Retrieve a specific chunk of an indexed document by index |
search_document_chunks_by_keyword |
Search indexed document chunks by keyword |
These two searches cost you nothing and need no setup
get_document_chunk and search_document_chunks_by_keyword match on meaning
alone — no AI model is involved — so they work whether or not you have
configured a provider, and they never appear on your provider bill.
The knowledge-graph searches are different: search_methodology_library
and get_methodology_paper_summary read your question with a model before
they can answer. Those run on your own Direct LLM slot, so their (small)
cost lands on your provider account, and without a configured slot they
return an actionable error. See AI Functions & Model Options.
Research Paper¶
| Tool | Description |
|---|---|
read_paper |
Read a project's Research Paper — the assembled chapters, plus each unit's current edit token |
edit_paper_unit |
Replace an exact passage inside one unit of the paper, leaving everything else byte-for-byte unchanged |
You cannot edit a chapter you have not just read
There is deliberately no "write the whole chapter" tool. read_paper
returns a base_hash per unit, and edit_paper_unit refuses an edit whose
base_hash is missing (you never read it) or stale (someone else changed
the unit since you did). This is what stops an agent from silently
overwriting a researcher's approved text, so read first, every turn — a
hash from earlier context is not a read.
An edit names an old_string to find and a new_string to put in its
place. If the old_string appears more than once the edit is refused with
the match positions rather than guessing which one you meant. On success
you get a fresh new_base_hash, so several edits to the same unit in one
turn need no re-read in between.
Content is HTML restricted to a small allowlist of tags and classes. Anything outside it is refused, and the refusal names each disallowed construct so you can correct it.
Campaign Management¶
| Tool | Description |
|---|---|
list_campaigns |
List campaigns (optionally by project) |
get_campaign |
Get campaign details |
create_campaign |
Create a new campaign |
update_campaign |
Update campaign fields (name, status) |
delete_campaign |
Delete a campaign |
add_interviewers_to_campaign |
Add interviewers (Users with interviewer role) |
remove_interviewers_from_campaign |
Remove interviewers from a campaign |
update_campaign_questionnaire |
Change the questionnaire assigned to a campaign |
assign_pool_to_campaign |
Assign a respondent pool to a campaign |
get_campaign_pool |
Get the respondent pool assigned to a campaign |
send_campaign_invitations |
Send survey invitation emails to respondents |
Respondents are not added to a campaign directly — assign a respondent pool with assign_pool_to_campaign, then bulk_create_surveys creates a survey per pool respondent.
Parameter Reference — parameters, return schemas, batch patterns, invitation eligibility
Respondent Management¶
| Tool | Description |
|---|---|
list_respondents |
List all respondents (survey targets) |
get_respondent |
Get respondent details |
create_respondent |
Create a new respondent |
update_respondent |
Update respondent information |
delete_respondent |
Remove a respondent |
bulk_create_respondents |
Create multiple respondents at once |
bulk_delete_respondents |
Delete respondents by filter (two-step dry_run workflow) |
generate_survey_access_token |
Mint a magic-link token for a respondent's survey |
send_survey_invitation |
Email a respondent a magic-link survey invitation |
Parameter Reference — parameters, return schemas, bulk creation, demographics
Respondent Pools¶
Respondent pools let you organize respondents into reusable groups for campaigns.
| Tool | Description |
|---|---|
list_respondent_pools |
List all respondent pools |
get_respondent_pool |
Get pool details including respondent list |
create_respondent_pool |
Create a new respondent pool |
add_respondents_to_pool |
Add respondents to an existing pool |
remove_respondents_from_pool |
Remove respondents from a pool |
generate_pool_from_strategy |
Create a pool using a sampling strategy |
preview_pool_generation |
Preview pool generation without creating it |
refresh_pool_from_strategy |
Re-run selection algorithm on existing pool |
delete_respondent_pool |
Delete a respondent pool |
Parameter Reference — parameters, return schemas, duplicate handling, strategy generation
Sampling Strategies¶
Sampling strategies define target demographic distributions for creating representative respondent samples.
| Tool | Description |
|---|---|
list_sampling_strategies |
List all sampling strategies |
get_sampling_strategy |
Get strategy details including factors |
create_sampling_strategy |
Create a strategy with custom demographic factors |
create_default_strategy |
Create a strategy with standard gender and age factors |
update_sampling_strategy |
Update an existing strategy |
advance_sampling_strategy |
Clone a strategy and append reality-grounded outcome factors |
delete_sampling_strategy |
Delete a sampling strategy |
Example: Creating a Custom Sampling Strategy
AI Assistant: I'll create a sampling strategy targeting your demographics.
→ create_sampling_strategy(
name="Urban Millennials",
project_id="...",
target_size=200,
factors=[
{"name": "Gender", "attribute_path": "gender",
"target_distribution": {"male": 0.48, "female": 0.50, "other": 0.02}},
{"name": "Age Group", "attribute_path": "age",
"target_distribution": {"25-34": 0.60, "35-44": 0.40}}
]
)
Strategy created! Now generating a representative pool...
→ generate_pool_from_strategy(strategy_id, "Urban Millennials Pool")
Parameter Reference — parameters, return schemas, factor schema, distribution validation
User Management¶
| Tool | Description |
|---|---|
list_users |
List platform users (including interviewers) |
get_user |
Get user details |
Note: Interviewers are Users with the interviewer role. They are managed as Users, not as Respondents.
Interviewer Workload¶
For campaigns with interviewers (Users with interviewer role), manage workload distribution:
| Tool | Description |
|---|---|
assign_respondents_to_interviewer |
Assign respondents to a specific interviewer (User) |
unassign_respondents_from_interviewer |
Remove interviewer assignments |
get_interviewer_workload |
View assigned respondents and completion status |
get_unassigned_respondents |
Find respondents not yet assigned |
Survey Execution¶
| Tool | Description |
|---|---|
list_surveys |
List surveys (filter by campaign or respondent) |
create_survey |
Start a new survey session |
update_survey |
Update a survey's status and flow state (bound entities are immutable) |
bulk_create_surveys |
Create surveys for all respondents in a campaign |
bulk_delete_surveys |
Delete surveys by filter (two-step dry_run workflow) |
get_survey_current_item |
Get the current question with its options folded in |
submit_survey_response |
Submit an answer and advance (next step folded in) |
finish_survey |
Explicitly complete a survey |
mass_fill_surveys |
Enqueue an asynchronous background task to fill surveys with synthetic test responses (returns {task_id, status}) |
get_task_status |
Poll an async mass_fill_surveys run by task_id (dataset coding jobs are polled via get_dataset instead) |
Parameter Reference — parameters, return schemas, state machine, distribution modes, personas
Data Analysis¶
A Bundle binds a Project, Questionnaire, and Campaign subset to one linear Bronze → Silver → Gold dataset chain. Bundle tools manage that binding and carry the Bundle's quality story; dataset tools run each Bundle-scoped stage — extraction, coding + weighting, refinement, and export.
| Tool | Description |
|---|---|
create_bundle |
Create a pipeline Bundle |
list_bundles |
List Bundles, optionally filtered to one project |
get_bundle |
Bundle detail, including its stage recipes (Silver Calibration Targets, Gold operations) |
clone_bundle |
Deep-copy a Bundle's ready datasets + recipes into an independent variant |
delete_bundle |
Delete a Bundle and its dataset chain |
get_bundle_quality |
The Bundle's Representativeness story (selection, fielded, weighted, fielding shift, response) |
compare_bundle_quality |
Bronze vs Silver improvement against the Bundle's current Strategy |
get_bundle_coding |
Open-end coding state — the candidates the Bronze offers, the analyst's question selection, and per question the proposed dimensions, their categories and which are selected |
set_coding_selection |
Set which open-text units the Bundle codes (manager role, identified user required) |
assign_bundle_strategy |
Assign (or clear) the Bundle's current Sampling Strategy |
set_calibration_targets |
Edit the Bundle's Calibration Targets — the Silver weighting spec |
list_datasets |
List datasets scoped to a Bundle's chain |
get_dataset |
Get dataset details including schema and metrics |
create_bronze_dataset |
Extract a Bundle's Bronze dataset |
code_open_ends (alias derive_silver) |
Code open-ended text, then weight against Calibration Targets, producing the Bundle's Silver |
create_gold_dataset |
Refine a Bundle's Silver into its Gold, using the Gold operation catalog |
export_dataset |
Export a ready dataset (CSV, XLSX, SPSS, Parquet) |
delete_dataset |
Delete a dataset |
Bundle Tools Reference — Bundle lifecycle and quality parameters, return schemas
Dataset Tools Reference — stage parameters, return schemas, export formats
Audit & Compliance¶
| Tool | Description |
|---|---|
query_audit_events |
Search audit trail with filters |
get_entity_history |
View complete history of any entity |
get_audit_stats |
Get audit statistics |
Documentation Access¶
| Tool | Description |
|---|---|
list_documentation |
List available documentation |
get_documentation |
Load a specific document |
search_documentation |
Search documentation by keyword |
MCP Resources¶
Resources provide read-only access to platform data:
| Resource URI | Description |
|---|---|
repository://overview |
Platform statistics and counts |
repository://schema |
Entity relationships and structure |
qml://files |
The organization's QML questionnaires |
docs://primer |
Platform overview |
docs://qml-syntax |
QML language reference |
docs://getting-started |
Quick start guide |
Additional documentation resources mirror the knowledge base (docs://guide, docs://glossary, docs://campaign-management, docs://survey-execution, docs://creating-surveys, docs://data-analysis, docs://quality-metrics).
Example Workflows¶
Creating a Survey Campaign with AI¶
AI Assistant: I'll help you set up a customer satisfaction survey.
1. First, let me create a project...
→ create_project("Customer Research 2026", "Annual satisfaction study")
2. Now I'll register your questionnaire...
→ create_questionnaire("CSAT Survey", qml_content, project_id)
3. Creating the campaign...
→ create_campaign("Q1 CSAT", project_id, questionnaire_id)
4. Adding your respondent list...
→ bulk_create_respondents(100, generate_demographics=true)
→ create_respondent_pool("CSAT Sample", respondent_ids=[...])
→ assign_pool_to_campaign(campaign_id, pool_id)
→ bulk_create_surveys(campaign_id)
Your campaign is ready! 100 respondents have surveys waiting.
Creating a Representative Sample¶
AI Assistant: I'll create a demographically balanced sample for your study.
1. Creating a sampling strategy with your target demographics...
→ create_sampling_strategy(
name="National Representative",
project_id,
target_size=500,
factors=[
{"name": "Gender", "attribute_path": "gender",
"target_distribution": {"male": 0.49, "female": 0.51}},
{"name": "Age", "attribute_path": "age",
"target_distribution": {"18-34": 0.30, "35-54": 0.40, "55+": 0.30}}
]
)
2. Generating a pool that matches your targets...
→ generate_pool_from_strategy(strategy_id, "Q1 Sample Pool")
3. Assigning the pool to your campaign...
→ assign_pool_to_campaign(campaign_id, pool_id)
Done! Your campaign now has 500 respondents balanced to match
national demographics.
Executing a Survey Programmatically¶
AI Assistant: I'll walk through the survey and show you each question.
→ create_survey(questionnaire_id, respondent_id)
→ get_survey_current_item(survey_id)
Question 1: "What is your age?"
Type: Integer input (18-100)
User: 35
→ submit_survey_response(survey_id, "q_age", 35)
→ get_survey_current_item(survey_id)
Question 2: "How satisfied are you with our service?"
Type: Scale (1-5)
...
Managing Interviewer Workload¶
AI Assistant: Let me distribute the 50 respondents among your 5 interviewers (Users with interviewer role).
→ get_unassigned_respondents(campaign_id)
Found 50 unassigned respondents
→ assign_respondents_to_interviewer(campaign_id, interviewer_user_1, respondents[0:10])
→ assign_respondents_to_interviewer(campaign_id, interviewer_user_2, respondents[10:20])
...
→ get_interviewer_workload(campaign_id, interviewer_user_1)
Assigned: 10 respondents
Completed: 0
In Progress: 0
Platform Concepts¶
Understanding the entity model helps when working with the MCP interface:
Entity Hierarchy¶
Project
├── Questionnaire (owns a QML version chain)
└── Campaign
├── Respondents (survey targets)
├── Interviewers (Users with interviewer role, optional)
└── Surveys (individual sessions)
Respondents and Interviewers¶
- Respondents: Survey targets with demographic data. Their survey completions determine campaign progress. They access surveys via magic links (no platform authentication required).
- Interviewers (optional): Users with the
interviewerrole who facilitate surveys for respondents. They have platform login and help respondents complete surveys but don't fill in surveys themselves.
Interviewers can be assigned to help respondents in person or by phone. When interviewers are assigned, surveys track which interviewer facilitated each respondent's survey.
Respondents vs Users¶
- Respondents are survey targets with demographic data (age, gender, location). They don't have platform accounts.
- Users are platform accounts for authentication and management. Users with the
interviewerrole can facilitate surveys. - Respondents and Users are separate entities. Interviewers are Users, not Respondents.
Error Handling¶
MCP tools return structured error messages. Two apply across every category:
not_found— the entity is not in the session's active organization. The message names the organization it searched; see Your Active Organization.org_read_only— the active organization is sealed. Reads succeed; this refusal only ever comes from a tool that would have written something.
Detailed error patterns are documented per category:
- Campaign Tools — batch patterns (added/skipped/reasons), invitation eligibility
- Survey Tools — state machine transitions, flow errors
- Dataset Tools — stage validation, format errors
- Pool Tools — duplicate handling, strategy validation
- Sampling Tools — factor schema validation, default strategy protection
- Questionnaire Tools — lifecycle constraints, active campaign checks
- Respondent Tools — bulk creation errors
Next Steps¶
-
REST API
Traditional REST endpoints for integration
-
QML Syntax
Learn the questionnaire markup language
-
Campaigns
Configure and manage survey campaigns