Usercall MCP - AI agents that run real user interviews
MCP serverAI & modelsGive your AI agents the ability to ask real users why.
Available today. Use it from your connected AI after setup.
Needs your own Usercall account. Keys stay in your vault.
Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
Then ask your AI: use Usercall MCP - AI agents that run real user interviews
Install Usercall MCP - AI agents that run real user interviews
The server’s own address, for the clients that take one directly. Or connect ahel once and every client you use reads it from one address, with the account kept on ahel rather than in each client’s config.
Claude Code
claude mcp add --transport http --scope user usercall-mcp-ai-agents-that-run 'https://mcp.usercall.co'Run it once in your project, then open /mcp to approve any sign-in the server asks for.
Claude Desktop
https://mcp.usercall.coAdd a custom connector in Settings, paste this address, and approve the sign-in.
Cursor
cursor://anysphere.cursor-deeplink/mcp/install?name=usercall-mcp-ai-agents-that-run&config=eyJ1cmwiOiJodHRwczovL21jcC51c2VyY2FsbC5jbyJ9Open the link and Cursor adds the server at that address.
ChatGPT
https://mcp.usercall.coIn Settings, enable Developer mode, create an MCP app, and paste this address. Your plan and workspace must allow custom apps.
Codex
codex mcp add usercall-mcp-ai-agents-that-run --url 'https://mcp.usercall.co'Run it once, then sign in with codex mcp login usercall-mcp-ai-agents-that-run if the server asks for an account.
From the project's README
As published by junetic/usercall-mcp in README.md.
AI can build products. But it still doesn't talk to users.
Give your AI agents the ability to ask real users why.
Usercall MCP lets AI agents run user interviews via voice or text and return structured insights with themes and verbatim quotes.
Why this exists
AI agents can now build and ship products extremely quickly.
But most agents still rely on synthetic feedback or assumptions about users.
Usercall MCP lets agents gather real qualitative feedback directly from users.
Choose a connection
Recommended: hosted MCP (Claude, ChatGPT, Cursor, Grok Bot)
Add https://mcp.usercall.co as a remote MCP connector / custom connector.
- OAuth sign-in (no API key, no
npx) - Same tools as this package (studies + Research Triggers)
- Docs: app.usercall.co/docs/mcp
- Cursor Directory / Grok Bot: this repo ships
.mcp.jsonso cursor.directory can install the hosted connector. Grok Bot cannot run the localnpxpackage.
This package: local / API-key / machine-to-machine
Use @usercall/mcp over stdio when you want a Bearer API key (scripts, local clients, M2M).
- Sign in at app.usercall.co → Home → Developer → Create API key
- Run
npx -y @usercall/mcpwithUSERCALL_API_KEY
Example workflow
Agent: "Why are users confused about onboarding?"
→ create_study
→ share interview_link with users
→ get_study_results
The returned interview_link can be shared with participants through email, Slack, Discord, or in-product prompts.
Example result:
{
"themes": [
{
"name": "Onboarding confusion",
"summary": "Users struggled to understand the second step.",
"quotes": [
"I wasn't sure what the app was asking me to do.",
"I didn't know I had to verify my email before continuing."
]
},
{
"name": "Pricing confusion",
"summary": "Free plan limits were not clearly communicated.",
"quotes": ["I wasn't sure if the free plan included analytics."]
}
]
}
How it works
AI Agent
↓
Usercall MCP (hosted OAuth or this stdio package)
↓
Usercall Agent API
↓
Real user interviews
↓
Themes and verbatim quotes returned to the agent
With Research Triggers, the agent can also target users in your product:
Analytics MCP (PostHog, Mixpanel, …) finds a behavior
↓
Usercall MCP creates a study and a paused Research Trigger
↓
You activate it in Usercall
↓
The Usercall SDK invites matching users to an interview right after the behavior
Research Triggers
Analytics tells an agent what users do. Research Triggers let it ask them why.
User: "Look at our PostHog data and find something worth investigating."
Agent (PostHog MCP): users who test a study rarely launch one.
Agent (Usercall MCP):
list_trigger_events() → study_tested, study_launched, …
get_trigger_event_schema("study_tested")
→ properties: source, interview_type
traits: plan ("free", "pro"), account_type
create_study(...) or list_studies()
create_research_trigger({
study_id, event_name: "study_tested",
traits: { plan: "free" }, sampling_percent: 25, max_invites_per_day: 10
}) → status: "paused", summary, activation_url
Agent: "I've prepared a Research Trigger. When: study_tested · Audience: plan = free ·
25% sampled · max 10 invites/day. It's paused. A human activates it here: <activation_url>"
- The Usercall SDK has to be installed. If
list_trigger_eventsreturns nothing, callget_trigger_sdk_setup(with your analytics provider and event names) to get the snippet. Coding agents can install it for you. - Only events Usercall has actually received can be used. Filters are exact matches on event properties or user traits.
get_trigger_event_schemashows which field is which. - Unsupported conditions are rejected, not silently dropped. These include event counts, sequences, absence ("did not do X"), time windows, and not-equals.
get_trigger_capabilitiesreturns the full list.
Local install (API key)
1. Get an API key
Sign in at app.usercall.co → Home → Developer → Create API key
2. Add to your MCP client
Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"usercall": {
"command": "npx",
"args": ["-y", "@usercall/mcp"],
"env": {
"USERCALL_API_KEY": "your_key_here"
}
}
}
}
Cursor (.cursor/mcp.json):
{
"mcpServers": {
"usercall": {
"command": "npx",
"args": ["-y", "@usercall/mcp"],
"env": {
"USERCALL_API_KEY": "your_key_here"
}
}
}
}
For Claude, ChatGPT, or Cursor remote connectors, prefer https://mcp.usercall.co instead of this JSON config.
Restart your MCP client.
3. Ask your agent
Run user interviews to understand why users drop off during onboarding.
Context:
- B2B SaaS product
- 3-step signup flow
Goal:
Identify confusion points and friction.
Target interviews: 5
Language: ko
Interview mode: voice
Show participants this prototype during the interview:
https://www.figma.com/proto/abcd1234/onboarding-flow
The agent will:
- create a study
- return an interview link
- collect responses
- return the summary (themes, insights, and risks)
Structured tool example
Equivalent create_study tool call:
create_study
key_research_goal: "Understand why users drop off during onboarding"
business_context: "B2B SaaS signup flow"
target_interviews: 5
languages: ["en"]
interview_mode: "voice"
study_media:
type: "prototype"
url: "https://www.figma.com/proto/abcd1234/onboarding-flow"
description: "New onboarding flow concept"
Tools
create_study
Create an interview study when you already know what happened and still need to learn why. Returns study_id and interview_link. Do not share the link yet. Call list_studies first and reuse a study that already asks this question. key_research_goal is required. business_context is optional. One active agent study per account. On 402, surface checkout_url to a human. This does not run the interview. Call simulate_interview next.
| Field | Type | Required | Default |
|---|---|---|---|
key_research_goal | string (5–2000) | yes | |
business_context | string (5–2000) | no | |
additional_context_prompt | string | no | |
target_interviews | number (1–200) | no | 1 |
languages | string[] | no | |
duration_minutes | number (5–65) | no | 12 |
interview_mode | voice | text | voice_and_text | no | voice |
voice_gender | female | male | no | |
enable_link_context | boolean | no | |
custom_link_variables | { key, label?, default_value? }[] | no | |
metadata | object | no | |
study_media | object | no |
One locale turns the language picker off; two or more turn it on. Research goal cannot be changed after create.
study_media (optional). Visual stimulus shown during all interview questions:
| Field | Type | Required |
|---|---|---|
type | image | prototype | yes |
url | string (URL) | yes |
description | string (max 500 chars) | no |
image: Direct image URL (.png,.jpg,.gif,.webp)prototype: Figma prototype URL (converted to interactive embed)- Media is only visible to web participants; phone callers won't see it
update_study
Edit an existing study's slots, interview mode, languages, voice, link context, guide text, questions, or media. Use this after review_study or a failed simulation names a guide change, or when the link is disabled and you are about to share. You cannot change key_research_goal. One locale turns the language picker off. Two or more turn it on. Query params on interview_link are ignored until enable_link_context is true. Call simulate_interview again before sharing.
| Field | Type | Required |
|---|---|---|
study_id | uuid string | yes |
target_interviews | number (1–200) | no |
is_link_disabled | boolean | no |
ai_agent_intro_message | string | no |
key_learning_goals | string | no |
workflow_end_message | string | no |
workflow_questions | { text, ... }[] | no |
interview_mode | voice | text | voice_and_text | no |
languages | string[] | no |
voice_gender | female | male | no |
enable_link_context | boolean | no |
custom_link_variables | { key, label?, default_value? }[] | no |
study_media | object or null | no |
Pass study_media: null to clear media. The study_media object follows the same schema as in create_study.
get_study_status
Check whether a study is running, analyzing, or complete. running and analyzing mean wait and call this again. Do not treat those payloads as findings. When status is complete, call get_study_results. This does not return themes.
| Field | Type |
|---|---|
study_id | uuid string |
Status values: running · analyzing · complete
Response includes interview progress fields, including
completed_interviews and target_interviews.
get_study_results
Read findings after get_study_status is complete. Prefer format=summary for themes, insights, and risks. Use format=full only when a verbatim transcript is required. Empty themes while the study is still running are not a finding.
| Field | Type | Required |
|---|---|---|
study_id | uuid string | yes |
format | summary | full | no |
Summary/full responses include study progress fields and analysis output.
simulate_interview
Dry-run the interview after you create or edit a study, and before any real invite. Omit simulation_id to start. Pass that id to read the result. The start returns immediately with running. Cap is 5 simulations per account per UTC day. A simulation is not a completed interview and does not change completed_interviews. On fail, call update_study, then simulate again.
| Field | Type | Required |
|---|---|---|
study_id | uuid string | yes |
simulation_id | uuid string | no |
persona | { name, prompt } | no |
Omit simulation_id to POST /api/v1/agent/studies/{studyId}/simulations. Pass simulation_id to GET that simulation. The tool does not poll.
review_study
Check the interview guide before sharing it. It reads the guide only. It does not read transcripts and it does not apply edits. It costs 1 credit. On 402, surface checkout_url to a human. Write suggested changes with update_study. Stop after one review unless the guide changed. This is not simulate_interview and it is not get_study_results.
| Field | Type | Required |
|---|---|---|
study_id | uuid string | yes |
Sends study_id only. It does not send call_ids.
delete_study
Permanently delete a study when it asks the wrong question or you must free the one active agent study. This cannot be undone. To stop new interviews without deleting evidence, call update_study with is_link_disabled true. This does not delete a research trigger.
| Field | Type | Required |
|---|---|---|
study_id | uuid string | yes |
Research Trigger tools
| Tool | Purpose |
|---|---|
get_trigger_capabilities | Before designing a trigger. One event, exact property or trait, URL rule, page dwell. No counts, sequences, absence, time windows, or not-equals |
get_trigger_sdk_setup | Install snippet when list_trigger_events is empty. No secret keys |
list_trigger_events | Events Usercall has received in the last 30 days. Required before create_research_trigger |
get_trigger_event_schema | Observed properties and traits for one event. Call after the event is listed |
list_studies | List before creating. Reuse study_id and interview_link. trigger_eligible is false when there is no link |
create_research_trigger | Paused invite for an observed event. activation_url is for a human. Agents cannot activate |
list_research_triggers | Status and activation_url. Recover a paused link. Empty after a human activates. Agents cannot activate |
get_research_trigger | One trigger's status or paused activation_url. Not interview evidence |
update_research_trigger | Edit targeting, sampling, or copy. Never status: "active" (409). Editing an active trigger pauses it |
delete_research_trigger | Permanently delete a trigger. Pause instead when you only want to stop it |
create_research_trigger
| Field | Type | Required | Default |
|---|---|---|---|
study_id | uuid string | yes | |
event_name | string (from list_trigger_events) | yes | |
properties | object of exact-match values | no | |
traits | object of exact-match values | no | |
url | { match: equals | contains | starts_with, value } | no | |
dwell_seconds | 1–600 (page-visit triggers only) | no | |
source | page_visit | analytics_event | custom | no | |
sampling_percent | 1–100 | no | 100 |
cooldown_days | 0–365 | no | 30 |
max_invites_per_day | 1–100 | no | 100 |
intercept_title | string (≤120), small label above the prompt | no | default |
intercept_body | string (≤500), prompt text | no | default |
delivery_method | intercept | webhook | no | intercept |
webhook_url | public https URL (required for webhook) | no | |
webhook_secret | string (16–200), HMAC key, write-only | no | |
invite_link_params | { static?, from_traits?, from_properties? } | no | |
name | string (≤100) | no | generated |
For page-visit triggers, use source: "page_visit" and event_name: "$pageview", with url and optionally dwell_seconds.
Delivery.
intercept(default) shows the Usercall widget in your product, and the user takes a voice or text interview in the page. The modes come from the study;list_studiesreturns each study'sinterview_mode.webhookPOSTs each matched user towebhook_url, with their user ID, email if known, traits, event properties and a personal interview link. Ifwebhook_secretis set, requests carry anx-usercall-signatureHMAC header.- Only public
httpsURLs are accepted, and the activation page shows the destination before a person activates the trigger.
Safety
- Agents cannot activate triggers. Triggers are always created paused. Calling
update_research_triggerwithstatus: "active"returns HTTP 409 and theactivation_url. A person has to open that link, review who will be invited, what they will see and the credit cost, and click Activate. - Changes to an active trigger need re-approval. Changing an active trigger's configuration pauses it again.
- Secret keys are never returned. The ingestion secret key never comes back from any tool.
Example workflow
1. create_study
key_research_goal: "Why do users drop off during onboarding?"
business_context: "B2B SaaS, 3-step signup flow"
target_interviews: 5
languages: ["ko"]
interview_mode: "voice"
→ returns { study_id, interview_link }
(`business_context` is optional; `key_research_goal` alone still creates a study)
2. simulate_interview
study_id
→ running, simulation_id
call again with simulation_id
→ pass, fail, or error
3. review_study
study_id
→ guide check only; write changes with update_study
4. Share interview_link with participants
(email, Slack, in-product prompt, etc.)
5. get_study_status
→ "analyzing"
6. get_study_results
→ summary: themes, insights, and risks
use format=full only for a quote
With visual stimulus
1. create_study
key_research_goal: "Get feedback on new dashboard design"
business_context: "Redesigning analytics dashboard for power users"
study_media:
type: "image"
url: "https://example.com/dashboard-mockup.png"
description: "New dashboard design concept"
→ returns { study_id, interview_link }
2. After simulate_interview passes, a human shares interview_link. Participants see the mockup during the interview.
For Figma prototypes, use type: "prototype" with a Figma proto URL.
Requirements
- Node.js 18+
- A valid Usercall API key (local / API-key path only)
Self-hosting / development
pnpm install
pnpm build
USERCALL_API_KEY="your_key_here" pnpm start
Tests and smoke tests:
pnpm test # unit + MCP contract tests
USERCALL_API_KEY="your_key_here" pnpm smoke # creates a real study
USERCALL_API_KEY="your_key_here" SMOKE_STUDY_ID="<uuid>" SMOKE_EVENT_NAME="<observed event>" pnpm smoke:triggers
Official MCP Registry
Usercall is listed on the Official MCP Registry as co.usercall/mcp.
Troubleshooting
| Error | Fix |
|---|---|
Missing USERCALL_API_KEY | Set the env var before starting this stdio package |
401 Unauthorized | Invalid or revoked API key |
402 Insufficient credits | Open the returned checkout_url, or add credits at app.usercall.co |
500 on create | Verify your key has access to Agent API v1 |
event_not_observed | Usercall hasn't received the event. Add it to your SDK allowlist (get_trigger_sdk_setup(events=[...])), trigger it in your app, then retry |
wrong_placement | The field is a trait, not a property (or the reverse). Use the suggested fix in the error |
| Trait filters never match | Call window.usercall.identify({ userId, traits }) when the user is known (see identify_snippet) |
webhook_url_not_allowed | Use a public https URL, without credentials; localhost and private IPs are rejected |
409 activation_required | Expected: agents can't activate. Share activation_url with the user |
429 on simulate_interview | Cap is 5 simulations per account per UTC day. Stop for the day |
| Active trigger never fires | Check the event is still arriving (list_trigger_events), and check the values match exactly (case and type) |
Remote Claude / ChatGPT / Cursor connectors should use https://mcp.usercall.co (OAuth). This package is the API-key stdio path.
License
MIT
Signals
- GitHub stars
- 5
- Forks
- 1
- Last commit
- Sep 2026
Advanced
- Delivery
- Usercall MCP server → your ahel connector (mcp.ahel.ai) → your AI.
- Item type
- mcp-server
- Key
co-usercall-mcp- Source
- github.com/junetic/usercall-mcp
- Hosted endpoint
https://mcp.usercall.co