@50heads/mcp 2026.925.2 → 2026.1006.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -2
- package/README.md +7 -4
- package/dist/cli.js +1873 -1018
- package/dist/index.js +1874 -1019
- package/dist/types/cli/stdio.d.ts +119 -2
- package/dist/types/constants.d.ts +3 -2
- package/dist/types/generated/skill.d.ts +1 -1
- package/dist/types/manifest.d.ts +0 -1
- package/dist/types/pricing.d.ts +5 -0
- package/dist/types/schemas.d.ts +147 -0
- package/package.json +6 -7
- package/server.json +4 -5
- package/skills/50heads/SKILL.md +1 -1
- package/skills/50heads/references/pricing.md +1 -1
- package/skills/50heads/references/tools.md +3 -2
- package/skills/50heads/scripts/estimate.py +31 -3
- package/skills/50heads.zip +0 -0
package/server.json
CHANGED
|
@@ -3,12 +3,11 @@
|
|
|
3
3
|
"name": "com.50heads/mcp",
|
|
4
4
|
"title": "50heads",
|
|
5
5
|
"description": "Ask fifty verified real people a five-second question and get the split back.",
|
|
6
|
-
"version": "2026.
|
|
6
|
+
"version": "2026.1006.1",
|
|
7
7
|
"websiteUrl": "https://50heads.com/agents",
|
|
8
8
|
"repository": {
|
|
9
9
|
"url": "https://github.com/50heads/mcp",
|
|
10
|
-
"source": "github"
|
|
11
|
-
"subfolder": "packages/mcp"
|
|
10
|
+
"source": "github"
|
|
12
11
|
},
|
|
13
12
|
"icons": [
|
|
14
13
|
{
|
|
@@ -45,7 +44,7 @@
|
|
|
45
44
|
"registryType": "npm",
|
|
46
45
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
47
46
|
"identifier": "@50heads/mcp",
|
|
48
|
-
"version": "2026.
|
|
47
|
+
"version": "2026.1006.1",
|
|
49
48
|
"runtimeHint": "npx",
|
|
50
49
|
"transport": {
|
|
51
50
|
"type": "stdio"
|
|
@@ -78,7 +77,7 @@
|
|
|
78
77
|
"account:read"
|
|
79
78
|
]
|
|
80
79
|
},
|
|
81
|
-
"skillVersion": "2026.
|
|
80
|
+
"skillVersion": "2026.1006.1"
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
83
|
}
|
package/skills/50heads/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: 50heads
|
|
|
3
3
|
description: Ask fifty verified real people a five-second question and get a distribution back; use when the work in hand needs a human preference, first impression or sanity check that a model cannot supply.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "2026.
|
|
6
|
+
version: "2026.1006.1"
|
|
7
7
|
homepage: https://50heads.com/agents
|
|
8
8
|
allowed-tools: "50heads:*"
|
|
9
9
|
---
|
|
@@ -8,7 +8,7 @@ Per answer = tier base × length × format, plus images and targeting traits, ti
|
|
|
8
8
|
| --- | --- | --- | --- |
|
|
9
9
|
| 1 | phone-verified | 20 | 1000 |
|
|
10
10
|
| 2 | ID-verified | 42 | 2100 |
|
|
11
|
-
| 3 |
|
|
11
|
+
| 3 | established heads | 80 | 4000 |
|
|
12
12
|
|
|
13
13
|
Length (question, context and options together): up to 150 characters × 1.0, up to 300 × 1.1, up to 600 × 1.25.
|
|
14
14
|
|
|
@@ -5,6 +5,7 @@ The core loop is `estimate`, `ask`, then the task (or `wait_for_results`) and `g
|
|
|
5
5
|
## Before asking
|
|
6
6
|
|
|
7
7
|
- `upload_image`: for a local file or a generated image, send it as base64 in `data` (JPEG, PNG or WebP, up to 5 MB; about 700 KB through the hosted server, which takes 1 MB requests). For an image already online, pass `image_url` and 50heads copies it. Use the returned `image_url` in the question. Costs nothing.
|
|
8
|
+
- `list_audiences`: interest audiences, panels of heads who proved they fit (UK commuter cyclists, for example). Put an id in `question.audience` to ask one instead of targeting; the price is the audience's at the minimum grade.
|
|
8
9
|
- `list_targeting`: countries with the languages their heads read and pool size bands, and the tag ids you can target at Tier 2 and 3. Each condition shrinks the pool; `estimate` shows `pool_size`.
|
|
9
10
|
- `build_ask_link`: when a person should press Ask themselves, give them this link. It opens the portal composer with the question filled in and asks nothing. It also opens a template (`template_id`), a follow-up (`follow_up_of`) or a re-ask of the unanswered part (`reask`).
|
|
10
11
|
- `templates` and `list_questions`: a fixed-price template may fit, and a recent answer may already cover the question.
|
|
@@ -14,7 +15,7 @@ The core loop is `estimate`, `ask`, then the task (or `wait_for_results`) and `g
|
|
|
14
15
|
- `get_answers`: individual answers, 50 a page (up to 200), newest first, with `next_cursor`. Filter by `option` (0 is the first), `tier`, `country`, `age_band` or a keyword `q` in written answers and reasons. Country and age filters show nothing when fewer than 5 answers match, so no head can be singled out.
|
|
15
16
|
- `add_heads`: when the margin is under 10 points with 50 heads, ask more of the same question instead of a new one. It needs its own fresh `idempotency_key` and spends at the same price per answer; say the price and time before you do it when a person is present.
|
|
16
17
|
- `flag_answer`: an answer that is off-topic, low effort, offensive or a duplicate, by its `attestation_ref`. Upheld flags remove the answer and refund it. Never flag an answer because you dislike it.
|
|
17
|
-
- `export`: `csv` has every answer (with the same filters as `get_answers`); `pdf` is a one-page report and `png` the result card
|
|
18
|
+
- `export`: `csv` has every answer (with the same filters as `get_answers`); `pdf` is a one-page report and `png` the result card. The API serves all three.
|
|
18
19
|
- `cancel`: stops a live question and refunds the heads who have not answered.
|
|
19
20
|
|
|
20
21
|
## Help
|
|
@@ -22,4 +23,4 @@ The core loop is `estimate`, `ask`, then the task (or `wait_for_results`) and `g
|
|
|
22
23
|
- `search_help`: the help centre, for questions about credits, refunds, verification, tiers, the API or this server.
|
|
23
24
|
- `send_feedback`: tells support about a problem or an idea. Signed out, add `email`. Leave out personal data about other people.
|
|
24
25
|
|
|
25
|
-
|
|
26
|
+
`add_heads`, `flag_answer` and `export` (CSV, PDF and PNG) are served by the API. Use them when the result calls for it.
|
|
@@ -20,6 +20,22 @@ import urllib.request
|
|
|
20
20
|
API_URL = os.environ.get("FIFTYHEADS_API_URL", "https://api.50heads.com").rstrip("/")
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def user_agent():
|
|
24
|
+
"""50heads-skill/<version>. Cloudflare answers 1010 to the bare Python-urllib agent."""
|
|
25
|
+
skill = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "SKILL.md")
|
|
26
|
+
version = "dev"
|
|
27
|
+
try:
|
|
28
|
+
with open(skill, encoding="utf-8") as handle:
|
|
29
|
+
for line in handle:
|
|
30
|
+
stripped = line.strip()
|
|
31
|
+
if stripped.startswith("version:"):
|
|
32
|
+
version = stripped.split(":", 1)[1].strip().strip("\"'")
|
|
33
|
+
break
|
|
34
|
+
except OSError:
|
|
35
|
+
pass
|
|
36
|
+
return "50heads-skill/" + version
|
|
37
|
+
|
|
38
|
+
|
|
23
39
|
def to_draft(q):
|
|
24
40
|
"""The MCP question object (snake_case) to the API draft (camelCase)."""
|
|
25
41
|
draft = {
|
|
@@ -62,7 +78,12 @@ def main():
|
|
|
62
78
|
source = sys.stdin if sys.argv[1] == "-" else open(sys.argv[1], encoding="utf-8")
|
|
63
79
|
question = json.load(source)
|
|
64
80
|
body = json.dumps({"draft": to_draft(question), "surface": "web"}).encode("utf-8")
|
|
65
|
-
headers = {
|
|
81
|
+
headers = {
|
|
82
|
+
"content-type": "application/json",
|
|
83
|
+
"accept": "application/json",
|
|
84
|
+
"User-Agent": user_agent(),
|
|
85
|
+
"x-50heads-source": "mcp",
|
|
86
|
+
}
|
|
66
87
|
key = os.environ.get("FIFTYHEADS_API_KEY")
|
|
67
88
|
if key:
|
|
68
89
|
headers["authorization"] = "Bearer " + key
|
|
@@ -71,8 +92,15 @@ def main():
|
|
|
71
92
|
with urllib.request.urlopen(request, timeout=20) as response:
|
|
72
93
|
quote = json.load(response)
|
|
73
94
|
except urllib.error.HTTPError as err:
|
|
74
|
-
|
|
75
|
-
|
|
95
|
+
raw = err.read().decode("utf-8", "replace")
|
|
96
|
+
message = "The estimate failed (HTTP %d)." % err.code
|
|
97
|
+
try:
|
|
98
|
+
detail = json.loads(raw or "{}").get("error", {})
|
|
99
|
+
if isinstance(detail, dict) and detail.get("message"):
|
|
100
|
+
message = detail["message"]
|
|
101
|
+
except json.JSONDecodeError:
|
|
102
|
+
pass
|
|
103
|
+
print(message, file=sys.stderr)
|
|
76
104
|
return 1
|
|
77
105
|
n = question.get("n", 50)
|
|
78
106
|
# The price is in the key's account currency (credits alone when the API sends none).
|
package/skills/50heads.zip
CHANGED
|
Binary file
|