npm humansurvey-mcpstdioMITupdated 1mo ago
Attribution for the channels that have no referrer.
What can you do with human survey?
HumanSurvey
Website: humansurvey.co ยท Docs: humansurvey.co/docs ยท FAQ: humansurvey.co/faq
Attribution for the channels that have no referrer.
HumanSurvey asks one question โ how did you hear about us โ inside the host's own signup or payment flow, at a granularity that is actually actionable: the platform first, then which creator, podcast, event or store.
Agent configures a form โ platforms from the catalog, creators supplied by the caller
Host embeds /s/{id} โ in its signup flow, its payment flow, or both
Respondent answers โ picks a platform; that pick expands the follow-up in place
Host pushes conversions โ POST /api/attribution/events, keyed on its own user id
Agent reads back โ rollup, raw response stream, free text awaiting a mapping
What is this?
An API and MCP server for self-reported attribution. TikTok in-app, Instagram, podcasts, communities, word of mouth, AI assistants: the exposure happens where tracking cannot reach, and asking a human is the only always-on signal that survives every referrer leak.
Two placements answer different questions. In the payment flow, the respondent is already a paying customer, so the answer joins to revenue with no conversion ingest at all. In the signup flow, it is the only way to see the people a channel sends who never pay. Divide a channel's share of the paying population by its share of the signup population. Above 1 it converts better than your average, below 1 worse. Multiply that ratio by your overall signup-to-paid rate to get the channel's own rate.
It is designed for:
- hosts embedding a form in their own onboarding or checkout
- agents that keep the candidate list current and read the results back
It is not designed for:
- general-purpose surveys โ arbitrary question types, Markdown authoring and conditional logic were removed in the attribution pivot
- a human-facing analytics dashboard: the aggregates are an API resource, and the agent is the dashboard
- reaching your audience for you โ HumanSurvey never contacts respondents; the transports
it offers (the
/s/{id}URL and the iframe embed) are ones you control
Features
- Progressive disclosure, not pagination โ POST the platform answer, PATCH the follow-up. The first answer is durable before the second is asked, and a respondent who abandons the follow-up is still real data.
- Rotation by default โ the orderable candidates are permuted per respondent, seeded
by a client-minted
render_id, so the raw share is unbiased by construction.fixedorder exists for callers who want it and does not hide its bias. - Retroactive remapping โ free text is stored verbatim and resolved against the remap table on every read, so one mapping fixes months of history with no backfill.
- Immutable config snapshots โ a response is joined to the version it was rendered against, so reconfiguring cannot rewrite what history says was shown.
- One join key, both directions โ
external_idbrings revenue in and carries per-user attribution back out to your own user table. - Cursor reads โ a response becomes visible once it is complete, is emitted exactly once, and is final when emitted. Nothing downstream has to upsert.
Product Principles
- AI-first I/O: agents configure the form and consume the results; humans are in the middle.
- Everything is an API: creator functionality must be available over authenticated HTTP and MCP.
- Narrow scope wins: one question, asked well. A feature that mainly serves a human survey operator probably does not belong here.
- No confident percentages: every number ships beside the denominator it was computed over, and a number we cannot compute honestly is null rather than smoothed.
Quick Start
Get an API key
curl -X POST https://www.humansurvey.co/api/auth/code \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com" }'
curl -X POST https://www.humansurvey.co/api/auth/verify \
-H "Content-Type: application/json" \
-d '{ "email": "you@example.com", "code": "481920", "grant": "api_key" }'
Anonymous key creation is gone. Every key belongs to an account from birth, which is what gives a lost key a recovery path and makes rotation free.
Create a form, then configure it
curl -X POST https://www.humansurvey.co/api/attribution/forms \
-H "Authorization: Bearer hs_sk_..." \
-H "Content-Type: application/json" \
-d '{
"name": "Checkout โ how did you hear about us",
"allowed_origins": ["https://app.example.com"]
}'
{
"id": "abc123efgh45",
"form_url": "https://www.humansurvey.co/s/abc123efgh45",
"warnings": ["this form has no config yet; PUT /api/attribution/forms/abc123efgh45 with {nodes} before embedding it"]
}
A form renders nothing until it has a config. PUT stores one as an immutable snapshot:
curl -X PUT https://www.humansurvey.co/api/attribution/forms/abc123efgh45 \
-H "Authorization: Bearer hs_sk_..." \
-H "Content-Type: application/json" \
-d '{
"nodes": [
{
"id": "channel",
"prompt": "Where did you first hear about us?",
"candidates": [
{ "id": "tiktok", "catalog_slug": "tiktok", "expands": "creator" },
{ "id": "reddit", "catalog_slug": "reddit" },
{ "id": "friend", "label": "A friend or colleague" },
{ "id": "dunno", "label": "I don'\''t remember", "pinned": "end", "dont_remember": true }
]
},
{
"id": "creator",
"prompt": "Which account was it?",
"candidates": [
{ "id": "oecuid_8812", "label": "Jade", "handle": "@jade.work0" }
]
}
]
}'
Platform labels, marks and aliases come from GET /api/attribution/catalog and are copied
into the snapshot. Creator candidates are yours: the product renders a candidate set and
returns the id that was chosen, and matching a vague description against a creator
database is upstream work.
Read the results
curl "https://www.humansurvey.co/api/attribution/rollup?form_id=abc123efgh45&by=candidate&from=2026-07-01&to=2026-08-01" \
-H "Authorization: Bearer hs_sk_..."
Also on the read side: GET /api/attribution/forms/{id}/responses (cursor stream, or one
identity via ?external_id=), .../unresolved for free text awaiting a mapping, and
POST .../remaps to resolve it retroactively. Full request and response shapes are in
the OpenAPI document.
Use with Claude Code
{
"mcpServers": {
"survey": {
"command": "npx",
"args": ["-y", "humansurvey-mcp"],
"env": {
"HUMANSURVEY_API_KEY": "hs_sk_your_key_here"
}
}
}
}
The server name stays survey and the package stays humansurvey-mcp โ both sit inside
every existing user's config. Its ten tools now speak the attribution API โ see
packages/mcp-server/README.md. npm publishes separately
from this repo, so the version on npm can lag what is here.
Public Surface
- Docs page:
https://www.humansurvey.co/docs - OpenAPI:
https://www.humansurvey.co/api/openapi.json - AI index:
https://www.humansurvey.co/llms.txt
Tech Stack
| Component | Technology |
|---|---|
| Framework | Next.js (App Router) |
| Database | Neon (serverless Postgres) |
| Frontend | React + Tailwind CSS |
| MCP Server | @modelcontextprotocol/sdk |
| Deployment | Vercel |
Project Structure
โโโ apps/web/ # Next.js app (API + respondent page + site)
โ โโโ lib/attribution/ # config, responses, reads, rollup, remap
โ โโโ supabase/migrations/ # applied through scripts/migrate.sh, with a ledger
โโโ packages/mcp-server/ # MCP server for Claude Code
โโโ docs/ # architecture, roadmap, design docs
Contributing
Read CONTRIBUTING.md before opening a PR. The most important rule is scope discipline: new UI variants, analytics dashboards, and human-operator features are usually out of scope.
Development
pnpm install
pnpm dev # Start Next.js dev server
pnpm test # node --test over apps/web/lib/**/*.test.ts
pnpm build # Build all packages
License
MIT
Install
Add human survey to your client. Pick the one you use.
claude mcp add humansurvey-mcp -- npx -y humansurvey-mcpcodex mcp add humansurvey-mcp -- npx -y humansurvey-mcpamp mcp add humansurvey-mcp -- npx -y humansurvey-mcp{
"mcpServers": {
"humansurvey-mcp": {
"command": "npx",
"args": [
"-y",
"humansurvey-mcp"
]
}
}
}Add to `claude_desktop_config.json`, then restart Claude Desktop.
{
"mcpServers": {
"humansurvey-mcp": {
"command": "npx",
"args": [
"-y",
"humansurvey-mcp"
]
}
}
}Add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` for a single project.
code --add-mcp '{"name":"humansurvey-mcp","command":"npx","args":["-y","humansurvey-mcp"]}'Or add the block manually to `.vscode/mcp.json` under `servers`.
{
"mcpServers": {
"humansurvey-mcp": {
"command": "npx",
"args": [
"-y",
"humansurvey-mcp"
]
}
}
}Add to `~/.codeium/windsurf/mcp_config.json`.
{
"mcpServers": {
"humansurvey-mcp": {
"command": "npx",
"args": [
"-y",
"humansurvey-mcp"
]
}
}
}Add to `cline_mcp_settings.json` via the MCP Servers panel.
{
"mcpServers": {
"humansurvey-mcp": {
"command": "npx",
"args": [
"-y",
"humansurvey-mcp"
]
}
}
}Add to `~/.gemini/settings.json`.
{
"mcpServers": {
"humansurvey-mcp": {
"type": "local",
"command": "npx",
"args": [
"-y",
"humansurvey-mcp"
],
"tools": [
"*"
]
}
}
}Add to `~/.copilot/mcp-config.json`, or run `/mcp add` inside the CLI.
{
"context_servers": {
"humansurvey-mcp": {
"command": {
"path": "npx",
"args": [
"-y",
"humansurvey-mcp"
]
}
}
}
}Add to your Zed `settings.json`.
npx -y humansurvey-mcpRun `goose configure`, choose **Add Extension โ Command-line Extension**, and paste this command.
Score
39 / 100
Incomplete
- Documentation25/25
- Maintenance22/25
- Trust13/20
- Capability0/15
- Install experience12/15
- Documents what it does and how to connect
- Has a resolvable package or endpoint
- Exposes at least one tool, prompt or resource
- README has substantive content
- Includes a code example
- Documents its configuration
- Mentions credentials or security posture
- Last commit 31 days ago
- Has a release history
- Repository is not archived
- Licensed MIT
- Namespace verified in the official MCP registry
- Claimed by its owner
- Published under an organisation
- 0 tool(s) documented
- Provides prompt templates
- Provides resources
- 12 documented install method(s)
- Published to a package registry
- Offers a hosted endpoint โ no local install
Version history
| Versions | Published |
|---|---|
| 1.2.0Latest | Aug 1, 2026 |
| 1.1.1 | Jul 31, 2026 |
| 1.1.0 | Jul 31, 2026 |
| 0.6.0 | May 22, 2026 |
| 0.1.0 | Apr 7, 2026 |