Skip to content

DocsBuilding on HEY

MCP server

Ask an AI assistant about Robinhood Chain projects through HEY.

HEY answers one question: which projects on Robinhood Chain are still building, what have they shipped, and which of them are not yet getting much market attention? That is the shape of a question someone asks an assistant, so HEY is an MCP server — thirteen tools an assistant can call while answering, fourteen where HEY publishes Market Integrity, hosted at https://heyresearch.xyz/mcp or run beside the assistant, and a research profile at https://heyresearch.xyz/mcp/research (round 4, 2026-09-30): builder intelligence first — no market-move, Under the Radar or valuation tool, and the ledger without market events; research_answer still carries the market context of HEY's agent contract (the valuation with its kind, source and reading date, and the market status), labelled context only and never a builder judgement (wording corrected 2026-10-03: it said "builder intelligence only").

It holds no database and no credentials of its own: every tool reads the same public API anyone can curl, through the typed SDK. That is the rule the whole server is built on — the MCP can say nothing the API does not.

Connect

Hosted (2026-09-26)

claude mcp add --transport http hey-research https://heyresearch.xyz/mcp

Any client that speaks MCP's Streamable HTTP transport works the same way: point it at https://heyresearch.xyz/mcp. With an API key, add --header "Authorization: Bearer hey_…"; the calls are then metered against the key's allowance, exactly as they would be on the API.

What the hosted endpoint is:

  • Stateless and read-only. Each POST is answered on its own, as JSON; there is no session id and no server-sent event stream. GET and DELETE answer 405, which is also the liveness check; the site's health is GET /api/health. Because the answer is always JSON, a client whose Accept is application/json, */* or absent is answered too (2026-10-05; the transport otherwise asks for text/event-stream as well and answered 406); a client that accepts only an event stream is refused. A tools/call needs no initialize first, but send one with your clientInfo name: it is how HEY tells an agent from a registry probe when it counts use. The name is a self-declaration, never an identity, and changes no answer.
  • Metered as you. The tools read the public API on the server's own loopback address, forwarding your address and — only to that loopback address — your key. The API's per-minute limit (120 a minute per address, a key's tier otherwise), monthly quota and holds apply unchanged. On top of that, 60 JSON-RPC calls a minute per address bound initialize and tools/list.
  • Guarded. A request whose Origin is not on the allowlist is refused (server-to-server clients send none and are allowed); the body is capped at 64 KB; cookies are ignored, so nothing here acts as a signed-in user.
  • Counted, not recorded. HEY's request table records which tool a call served, whether it failed or was cut, how long it took and how large the answer was. Never the arguments, the question, or the answer's text.

market_integrity is offered only where the site itself publishes Market Integrity.

Research profile (round 4, 2026-09-30)

claude mcp add --transport http hey-research-builder https://heyresearch.xyz/mcp/research

A founder decision for platforms that want HEY's builder intelligence and nothing that reads like a market screen. The same server, the same registry of tools, filtered by profile (mcpToolsFor(profile) in packages/mcp-core/src/tools.ts); the same guards, origin allowlist and 60-a-minute JSON-RPC bucket as /mcp. It offers seven tools:

Tool On the research profile
research_answer Unchanged: the agent contract, every capability (chain_brief since 2026-10-18). Its market context stays labelled context only.
find_projects Identity only: a name, ticker, slug or contract through /api/search/suggest (at most eight rows, no market figure), or a 0x… address through the token lookup. No surface, no market filter, no sort.
get_changes The ledger without market events: with no domain or type it asks for build, contract, token, research, lock; a market.* type is refused.
get_project_timeline Lenses build (the default), code, onchain, locks; no market or everything.
explain_fact Every fact but market.valuation, market.status, discovery_gap and market_integrity.state.
get_evidence, get_contract Unchanged.

No get_project_snapshot (its market block), get_token_market, project_diff (valuation then and now), ask_hey, chain_overview or market_integrity (never, whatever the flag), and no Under the Radar, shipping-in-silence or Builder Radar surface. Resources drop the snapshot; the timeline resource reads the build lens and hey://changes/latest the non-market domains. Prompts name only the profile's tools. Locally, start the stdio server with HEY_MCP_PROFILE=research. The request table counts /mcp/research as the MCP surface, and a partner key reaches it with the mcp route group.

Local

On npm since 2026-09-27 as @hey-research-lab/mcp (Node 20 or newer). Name the version: npx then runs exactly the release you chose, and a newer one only when you change the number.

claude mcp add hey-research -- npx -y @hey-research-lab/[email protected]

To run a change of your own, build it from a clone instead — pnpm install && pnpm --filter @hey-research-lab/mcp build — and point the assistant at node /absolute/path/to/hey-research/apps/mcp/dist/index.js.

Claude Desktop — in claude_desktop_config.json:

{
  "mcpServers": {
    "hey-research": {
      "command": "node",
      "args": ["/absolute/path/to/hey-research/apps/mcp/dist/index.js"]
    }
  }
}

Set HEY_API_URL to read a different instance (http://localhost:3000 while developing; anything else must be https, because the key rides on every request). Set HEY_API_KEY to read with your key's allowance; the server never prints the key. Start it with HEY_MARKET_INTEGRITY=public only where the site publishes Market Integrity.

The thirteen tools, and a fourteenth

One tool per real question. The twenty-two tools before 2026-09-26 were replaced, not aliased: nothing was published and the MCP had been called once. research_answer (2026-09-30) carries HEY's agent contract. On 2026-09-30 (round 4) three tools that overlapped a listed one were folded to pay for typed output inside the list's byte budget — see Folded tools below; they still answer.

Tool The question it answers Replaces
find_projects "What is AgentOS?" · "What is still being built?" · "Who is building quietly?" · "Who is shipping faster?" · "Top builders on Pons?" — a name, ticker or contract, or one of HEY's surfaces with the catalogue's filters. A pasted 0x… address is the contract lookup (what lookup_token answered): is anyone building it, ship records and meaningful ships in 30 days, the last ship with its source, whether the project names the contract; on MISMATCH it says the activity does not apply to this token. search_projects, list_projects, shipping_in_silence, builder_comebacks, accelerating_builders, list_builders
get_project_snapshot "Tell me about X." — opening since 2026-09-28 on the Research Summary: one tagged line (FACT, DERIVED, UNKNOWN) each for build, product usage, market, contracts, fundamentals, security context, the latest change and what HEY does not know, printed as the API serves them with their evidence ids and reasons, never restated; then identity and when HEY first recorded it, build status and Build Momentum, market context with its valuation kind or why it is withheld, on-chain use, verification and sources, HoodLock locks, the latest changes, freshness, and what HEY does not know. Context blocks, never building (2026-09-27): paid promotion seen on the token (presence and dates, never an amount), DefiLlama protocol economics (each metric measured, not tracked or unread — a measured zero says so), and the developer footprint (official repositories, newest production deployment, packages, advisories; a count only where measured). Usage (2026-09-28), its own dimension and never building: active and watched contracts and calls in 7 days (the ERC-20 surface apart), distinct caller addresses on the newest and busiest day — a count per day, "addresses, not people", and UNKNOWN across the window because days cannot be added; a project outside the method watch is UNKNOWN, never zero; the daily series is GET /api/projects/{slug}/usage. Security context (evidence, never a verdict) (2026-09-28): audit report links with where each is published and its security: evidence id, bug-bounty programs, a published security.txt, OSV advisories about the published packages, Scorecard checks never summed, incidents UNKNOWN. Peer context (peers-v2; own-function calls since 2026-10-20, ERC-20 calls apart): the cohort, then each measured figure's own line — DERIVED where the cohort cleared its minimum, UNKNOWN ("not enough comparable projects") where it did not — never one combined number. Relationships are not in the snapshot: GET /api/projects/{slug}/relationships. get_project, project_intelligence
get_changes "What changed?" · "Anything new on X since Monday?" — the change ledger, one event per change, with its own time, precision, when HEY knew and its evidence; browse or sync with a cursor. list_ships, list_signals, contract_changes
get_project_timeline "Show me X's history." — every kind of evidence on one axis, paged with a cursor. project_timeline
explain_fact "Why does HEY show this valuation / status / momentum?" — the rule, the source, the inputs, the lineage and the evidence ids. fact=build.change (2026-10-20) explains one change — the latest builder change, or event=<ledger id> from get_changes: its stage, who reported it, whether a release and a deployment are linked by an exact reference, activity on a contract tied to it, and what HEY does not know; the snapshot's summary prints the same line under Latest change, tagged DERIVED. —
get_evidence "What backs this?" — one typed evidence id as a receipt, including method: and, since 2026-09-27, sourcechange: (a material change to what the official site declares), and since 2026-09-28 security: (one audit, bounty program or published security contact — evidence, never a verdict). —
get_token_market "Does this token still trade? What did HEY check on the contract?" — HEY's daily index, lifecycle, pools, a supply-concentration summary (shares only), contract checks; include: ["moves"] adds each valuation move with what shipped before it. events_before_market_change
get_contract "What is this contract?" — creation, deployer, verified source, proxy kind and implementation history, interface counts and changes, activity and calls per method (counts by bucket, names withheld); by address, or every contract of a project. Since 2026-09-27: how the explorer verified the source, whose code it is (a DERIVED line: template, bytecode match, or published for the address), Sourcify's answer (not read is never "unverified"), the contract a minimal clone copies, calls the verified ABI names, undecoded selectors with a signature candidate (a guess, never a name) and creation calls counted apart. —
get_v4_hook "Who built this Uniswap v4 hook?" (2026-10-11) — GET /api/v4-hooks/{address} through the SDK: the answer, the project HEY ties it to and the basis (named only while HEY lists the hook; never a creator account), its creation state, declared callbacks (never what the code does or whether it is safe), verified source and proxy as HEY's contract read holds them, Uniswap's hooklist as context, pools on tracked tokens by ISO week (by when HEY recorded them; context, never an order), the deployment it counts through, and every unknown as UNKNOWN with its reason. On both profiles; the full list's byte budget is 20,000 since this tool. —
project_diff "What changed for X between two dates?" — then and now from persisted points, the changes recorded between. —
ask_hey A free-text question about one project (English or Malay), answered only from HEY's record. Since 2026-09-27 also the free-data questions: its public API, code hosts (GitLab answered honestly), functions that became active, what DefiLlama tracks, packages, advisories, docs changes and HEY's gaps — each line citing its evidence receipt where it restates a ledger event. —
chain_overview "How active is Robinhood Chain?" — day by day, this week's rollup, an archived weekly report, scheduled HoodLock unlocks, or Build Momentum beside market attention. chain_activity, this_week, weekly_report, upcoming_unlocks
research_answer The agent contract (2026-09-30): one bounded answer, AgentIntelligenceResponse v1, for capability = research_project (HEY's current view of one project), what_changed (the ledger over 1–30 days for a project or the chain, with the window's true total), builder_status (the status, its rule and version, inputs, lineage, evidence ids and what the rule never reads), verify_project (VERIFIED, UNVERIFIED, CONTRACT_MISMATCH or UNKNOWN attribution of a contract, with reasons), compare_builders (2–4 projects' building records over 30 days, no winner) or unknowns (every gap as UNKNOWN, NOT_MEASURED, NOT_VERIFIED, STALE or INSUFFICIENT_EVIDENCE, each with its coverage state and what not to conclude, and the dimensions measured, not applicable and withheld — every coverage dimension's state, what get_project_coverage answered). The answer first, then tagged claims, unknowns, freshness per data family and evidence ids; a source's words are quoted as data. Reads GET /api/agent/{capability}, the same JSON REST serves and A2A carries. —

market_integrity is a fourteenth, offered only where the site publishes Market Integrity — as heyresearch.xyz does, so its hosted tools/list names fourteen (corrected 2026-10-16). It lists every event HEY stands behind with its id, its time as precisely as HEY knows it, and its words with the reading dates and sources; there, get_changes also accepts the market_integrity domain and the market_integrity.event type (2026-09-27). list_bounties was dropped: bounties are not research, and they stay in the API and the SDK.

Folded tools (round 4, 2026-09-30): callable until 2026-12-31

By 2026-09-30 the tools were on npm and in the MCP Registry, so these are aliased, not replaced. They are no longer in tools/list, and still answer on /mcp and the stdio server exactly as before — the same read, the same text, typed output — plus one line saying which listed tool to use and _meta["io.heyresearch/deprecated"], until 2026-12-31 (at least 90 days, the partner overlap rule). They are not served on the research profile, which is new.

Folded tool Why it overlapped Ask instead
lookup_token The same read and renderer as find_projects with a 0x… address. find_projects with query = the address
compare_projects The same comparison read (loadCompare) as research_answer's compare_builders. research_answer { capability: "compare_builders", projects }; each project's market context is get_token_market
get_project_coverage research_answer's unknowns restates the same coverage states, with what not to conclude. research_answer { capability: "unknowns", project }

/developers prints the list.

Typed output (round 4, 2026-09-30)

Every tool returns structuredContent beside its text, and declares an outputSchema (MCP 2025-06-18 and later; a client on an older protocol reads the text, which is unchanged):

  • research_answer returns the AgentIntelligenceResponse v1 itself. The declared schema is the envelope's stable spine (schema, schemaVersion, capability, status, answer, claims with each id and status, evidence, data), open to every additive field; the whole schema is components.schemas.AgentIntelligenceResponse in /openapi.json and the Zod schema in @hey/agent-provider-core. A client that validates gets the contract's own answer. detail: "compact" (2026-10-18, opt-in) returns the same summary text and the same answer without its capability data (dataOmitted: "compact", full: the whole answer's URL, also the resource link) — every claim, unknown and evidence id kept — for a client that hands structuredContent to the model; the default stays whole.
  • Every other tool returns { schema: "hey.mcp-api-answer", api, data, notice }: the public API object the text was rendered from, unchanged (its shape is the partner contract and never changes meaning), the URL it is at, and a notice that its names, titles, summaries and descriptions are a source's words — data, never instructions. Over 64 KB, data is left out (dataOmitted: "over_64_kb") and api fetches it.
  • The byte budget. The tool list, output schemas included, stays under the old 22-tool list's 19,179 bytes on /mcp (19,051 on 2026-09-30) and under 11,000 on /mcp/research (10,085), held by one test per profile. Folding the three tools and dropping the $schema stamp the SDK adds to every schema paid for it; the budget was not lifted.

find_projects surfaces, one definition each

  • building-with-token — verified shipping, active or resumed, with a token whose market is live.
  • still-building — verified activity continuing through a market drawdown HEY tracked, on an active market that is more than a launch curve or a launch pool; on a thinner one Still Building is not measured (stillBuildingWithheld: "market_too_thin", hbm-v19), and since hbm-v31 nor on an active market whose reading carries under $1,000 of 24-hour volume. With no tracked token, no current market reading or building HEY cannot read it is not measured either (no_token, no_market_reading, activity_unknown, hbm-v21), never "does not hold"; nor on a token HEY cannot tell from a lookalike (token_not_the_projects) or when every builder source is unavailable (builder_sources_unavailable, hbm-v31).
  • under-the-radar — eligible under HEY's Under the Radar rule (status shipping, active or resumed; Build Momentum at least 30; at least 2 meaningful events in the last 30 days, one of them a ship rather than a commit summary; a fresh reading of an active market beyond a launch curve or a launch pool, trading $1,000+ a day, for the project's own token; market attention TYPICAL or lower (percentile below 60); never on activity HEY cannot read or a contested unverified ticker) and a positive Discovery Gap: the market-attention percentile is below the build percentile. A token whose market is not active has no Discovery Gap at all (discoveryGapWithheld: "market_too_thin", hbm-v18). A market active only because another pool of the token holds the liquidity is measured on that pool's reading, and not at all when HEY holds none (active_pool_not_read, hbm-v21). Since hbm-v21 the stored flag itself requires the positive gap; the listing keeps the test too. A positive gap alone is not enough (explain_fact calls that one POSITIVE). Since hbm-v30 it bounds attention too: a project whose market attention is ELEVATED or HIGH (the 60th percentile or above) is never Under the Radar, whatever its gap, and a gap withheld because HEY cannot read the building (activity_unknown) or cannot tell the token from another published project's token with the same ticker (token_not_the_projects) holds nothing.
  • shipping-now (status SHIPPING), most-active (shipping, active or resumed, by Build Momentum), new-builders (recorded in the last 7 days, with a builder source HEY reads and at least one counted ship), new-on-hey (every page recorded in the last 7 days), utility, memes.
  • back-from-dormancy — status RESUMED, narrowed to verified builders native to the chain; status: "RESUMED" gives every resumed project.
  • shipping-in-silence — held Under the Radar (the rule above, its positive gap and every hold the /radar listing applies) and below the 40th market-attention percentile.
  • accelerating — more meaningful events in the last 30 days than in the 30 before.
  • builder-radar — the Builder Radar, with radar for its views, never ranked by price.

The catalogue filters are exactly the parameters /api/projects reads (kind, status, narrative, launchpad, has, stage, minLiquidity, minMarketCap, maxMarketCap, minVolume, age, deployed, sort including shipped, limit, offset); a test holds the tool's schema to the parser in both directions.

Resources and prompts

Resources, for clients that attach context rather than call tools — the same reads and the same rendering:

  • hey://project/{slug} — the snapshot
  • hey://project/{slug}/timeline — the newest page of the timeline
  • hey://project/{slug}/coverage — kept while get_project_coverage is folded: a resource is context, not a tool, and costs the tool list nothing
  • hey://contract/{chainId}/{address}
  • hey://changes/latest — the newest thirty events

Prompts (since round 4 they call research_answer where they called a folded tool): deep_research_project, what_changed_since, explain_metric, investigate_contract, and — with global Ask HEY (2026-09-28) — what_changed_today, compare_project_usage, explain_project_evidence and monitor_project. Each names the tools in the order that answers the question and the rules the answer must keep. A prompt fetches nothing and adds no tool: the new four are workflows over the tools above, held by a test to name only tools that exist and to keep usage's words ("distinct caller addresses", "observed after", never a cause). No prompt creates an alert or follows a project; a reader does that on the site.

What every answer does

  • Tags each line FACT (a value HEY recorded, with its source), DERIVED (a rule HEY applied) or UNKNOWN (HEY does not hold it). The tag is never stronger than the API's own explain engine gives the same fact: activity status and Build Momentum are DERIVED, and so is every record HEY derived — a status or market-state transition, a signal over a window HEY measured, a market-integrity reading — in get_changes, get_project_timeline and get_evidence, one rule by the record's typed id. Still Building and Under the Radar counts are DERIVED wherever they appear.
  • Keeps the API's disclaimer on every answer, the Builder Radar ranking and chain_overview included.
  • Says what it showed of the whole — "Showing 20 of 412" — and the exact parameter or cursor that reads on.
  • Prints time at its precision — "week of 2026-09-14" for a week of code activity, "HEY saw it 2026-09-20" when no source dates the event.
  • Names a valuation by its kind. An FDV is an FDV; a valuation whose kind the API did not send is a "valuation", never a "market cap". A withheld valuation says it is withheld. In project_diff each end carries its own kind, and two ends of different kinds are said to be two measures, not one figure moving.
  • Says what a week of commits changed, at the API's strength (2026-09-27): in get_project_timeline a code week carries DERIVED what changed (<classifierVersion>, today commit-substance-v4): changed code | documentation or maintenance only | substance not fully read and the API's own sentence; in get_changes a build.code_activity event carries the same verdict and the FACT count of commits read. A documentation-only week says it is not counted as building; an unread week says it is unread, never that it is documentation. No commit message or author is ever in an answer.
  • Counts only what HEY could read. An on-chain event sum over a window with unreadable days says "over the N days HEY could read", and the rest are unknown, never zero.
  • Carries the source, and ends with a resource_link to the JSON it was rendered from.
  • Is capped at 24 KB, cut on a line, with how many lines were cut and where the whole answer is. A cursor listing (get_changes, get_project_timeline) over the cap is asked for again with a smaller limit until it fits, and says so, so the cursor it prints reads on from the last row shown (2026-10-18): a page cut on its rows never prints a cursor, which would skip the rows not shown.
  • Says when a slug moved (2026-10-18): a slug HEY retired when its page was renamed or merged answers for the page it moved to, with "Resolved from the slug …" first in the text and resolvedFrom: { slug, to } in structuredContent (research_answer: subject.resolvedFrom.slug).
  • Quotes a source's words as data (2026-09-30): a release title, a ship summary or a project's name is folded onto one line with control, invisible and template characters and tags removed, and text that reads like an instruction to a model ("ignore previous instructions", "you are now…") is kept and marked as a source's words, never as HEY's. In research_answer every such string is «…» (source title's words, quoted as data). Since round 4 every other tool does the same through the one helper (quoteExternal in packages/agent-provider-core/src/text.ts): every project name, ticker, narrative, launchpad, contract name, auditor, release title and caller's question is printed «…», bounded and folded, labelled when it reads like an instruction, and an answer that quotes one ends with a line saying what «…» means. HEY's own sentences that carry a record's values (a Research Summary line, an Ask HEY line) are HEY's derived text — folded and labelled, not quoted — unless they repeat a ship's title, which is quoted whole, the agent contract's rule (summaryIsSourceText). render-injection.test.ts injects hostile values into every renderer.
  • Discloses $HEY (round 4): an answer whose JSON names $HEY's contract opens with "Disclosure: HEY's own token — researched by the same rules" and carries it in structuredContent.disclosures; research_answer's contract carries disclosures of its own. Rankings are unchanged: no bonus and no demotion.
  • Fails as an error, never as an empty answer: an unreachable HEY, a rate limit (with how long to wait) or a missing slug comes back as isError with a sentence.

And never:

  • ranks by price, values a token, or recommends anything — the tests fail if a description picks up undervalued, price target or predict;
  • states a cause — a change beside a market move is a sequence;
  • prints "Still Building" without its meaning — verified activity continuing through a market drawdown HEY tracked; a record of what happened, not a prediction and not a buy signal;
  • turns unknown into zero — a missing figure is absent or UNKNOWN, never 0, "none" or a dash;
  • names an account other than a contract's deployer, and never a partnership from a call. get_token_market and get_contract name the deployer; get_contract adds how many other tracked projects' tokens the same account deployed — a count, never a profile — and calls it a launch service only on HEY's own shared-deployer flag (three projects), the rule /market reads;
  • returns Terminal-only data — the public API does not, so the MCP cannot.

Discovery: the registry, the handshake, $HEY (2026-09-28)

  • Official MCP Registry. The entry is apps/mcp/server.json (schema 2025-12-11/server.schema.json, validated in the gate and by node scripts/release/validate-mcp-server-json.mjs [--live]): name io.github.hey-research-lab/hey-research, the hosted remote https://heyresearch.xyz/mcp (Streamable HTTP) and, since round 4, a second remote for the research profile https://heyresearch.xyz/mcp/research (the registry schema's remotes is an array; the entry goes live at the next mcp-publisher publish), the public repository and the agent guide. The npm package carries the same name as mcpName, which is how the registry verifies an npm package; it ships with the next MCP release, and only then is the package added to the entry. Publishing to the registry is a manual step the lab takes. On 2026-10-10 the entry in the repository and the npm package are both 0.2.0, while the published registry entry still reads 0.1.1: the next mcp-publisher publish brings it level (adoption audit AG-03). The npm README's mission line now reads "not yet getting much market attention"; npm shows a README only with a new version, so it reaches npm with the next MCP release.
  • The instructions name the argument (AG-03, 2026-10-10). "Start with research_answer" now says its capability argument is one of the six and gives {"capability":"research_project","project":"<slug>"}: the old list read as if research_answer took a free-text question.
  • Question first (2026-10-13). An agent picks a tool from the list alone, and of about 30,000 MCP requests in 30 days only about 600 were tool calls (research_answer 253, find_projects 88, get_changes 86, chain_overview 32, get_project_snapshot 31); the rest were directory probes and handshakes. So every tool's description and every A2A skill's description opens on the question it answers, in the words a user types ("Is this Robinhood Chain project still being built, what did it ship…?", "What changed on Robinhood Chain today or this week…?", "Who made this Robinhood Chain contract, and is it a known project's?"), names the inputs an agent holds (a name, a $TICKER, a 0x contract on chain 4663 — find_projects turns either of the first two into the slug research_answer takes), and says briefly what comes back and what it is not (not price advice, not a safety verdict). The handshake's instructions open on one paragraph saying when to reach for HEY. HEY_MCP_QUESTIONS (packages/mcp-core/src/tools.ts) maps each question to its tool; llms.txt prints it, and tests hold every tool it names to the catalogue and every description to a leading question. The claims behind each question are the API's, unchanged (machine-layer rule 7). The list stays inside its byte budget: the default execution: {"taskSupport": "forbidden"} the SDK stamps on every tool is dropped (MCP reads an absent taskSupport as forbidden), as the $schema stamps were — 19,716 bytes of 20,000 on /mcp, 10,760 of 11,000 on /mcp/research (19,918 and 10,855 before).
  • An agent's first call succeeds and its answer is worth repeating (2026-10-14, agent-adoption audit AG-01, AG-02, AG-04, AG-05). The Claude connectors directory's first day was 12 connects and 0 tool calls, and every answer took two calls (find_projects for the slug, then research_answer) and ~24 KB of text with no line an agent could cite. So: research_answer takes q (a name or $TICKER) or address beside project on research_project, builder_status and unknowns — exactly one — through the one search matcher (one exact match answers with subject.resolvedFrom; several answer isError with error.candidates, the slugs to send back; none not_published); every answer carries citation.text ("Source: HEY Research Lab, builder research for Robinhood Chain — , as of , rules ") and it is the tool text's last line whatever the 24 KB cap cuts; the text is the lean summary by default (answer, claims, unknowns, links, citation; detail: "full" for the data, freshness and evidence lines; the JSON is always whole in structuredContent and at the resource link); the research answer's first sentence is plain English from the Research Summary's own answer and figures ("Chit is shipping consistently: 4 meaningful events in 30 days; latest ship 3 days ago."), the machine words kept after it; the handshake's instructions are a trigger list (per thing a user types, the first call and its arguments); and tools/list leads with research_answer, then find_projects (HEY_MCP_TOOL_LIST_ORDER). The list measures 19,867 bytes of 20,000 on /mcp and 10,911 of 11,000 on /mcp/research (the q and detail arguments). A caller that declares x-hey-integration on /mcp has it carried onto the loopback read, so the API row names it and the answer's links.pageAttributed carries utm_source=hey-agent&utm_medium=<name>; the canonical page and the citation stay clean.
  • The handshake names the server hey-research, titled HEY Research Lab, with websiteUrl the agent guide (/developers/agents). The MCP SDK in use (1.30) negotiates protocol versions up to 2025-11-25; the current specification is 2026-07-28.
  • $HEY is researched with the same tools as any token: find_projects with its contract, then get_project_snapshot or research_answer. The token's documented utility, each LIVE, PLANNED, RETIRED or UNKNOWN, is at GET /api/hey/profile (hey.heyProfile() in the SDK); no tool was added for it, and the server instructions say where it is.
  • A2A is a separate, task-oriented door (/.well-known/agent-card.json, POST /api/a2a) over the same reads; this server stays tool-oriented. See "Agent discovery" in the public API.

How it is built

Piece Where
Tools, renderers, resources, prompts (no transport) packages/mcp-core (@hey/mcp-core)
The tool list as data packages/mcp-core/src/tools.ts — read by the server, /developers, and the tests
stdio entry point (bundles the core) apps/mcp/src/index.ts
Hosted routes the web app's POST /mcp and POST /mcp/research (Web-standard Streamable HTTP, stateless JSON; one handler, lib/mcp-hosted.ts)
Profiles, typed output and folded tools, over a real MCP client packages/mcp-core/src/server-profiles.test.ts
Hostile text in every renderer packages/mcp-core/src/render-injection.test.ts
HTTP client for the public API packages/sdk (@hey-research-lab/sdk)
Renderer tests, one per tool, on API-typed fixtures packages/mcp-core/src/render.test.ts, src/fixtures/api.ts
Server tests over a real MCP client packages/mcp-core/src/server.test.ts
The agent contract behind research_answer (schema, composers, freshness, machine-safe text, adapters) packages/agent-provider-core (@hey/agent-provider-core)
research_answer on API-typed fixtures packages/mcp-core/src/agent-contract.test.ts

The server tests drive it through an actual Client over the in-memory transport: the tool list (exactly thirteen, fourteen with the flag, every one titled and read-only, under the old list's size with its output schemas), every tool end to end on its fixture, the resources, the prompts, and every failure.

What is deliberately not here

  • No write tools. Submitting a project, claiming ownership and posting an update are things a person does on the site, signed in, with an audit trail.
  • No sessions and no stream. The producer behind the change ledger runs every few minutes; polling get_changes with a cursor is as fresh as a stream would be, and a stateless endpoint survives every deploy.
  • No hand-kept schema per API shape. Since round 4 every tool declares an outputSchema, but for the other tools it is the wrapper's spine, not the API object's every field: a second, hand-kept schema for every shape would be a second source of truth. The API objects are typed by the SDK and described by /openapi.json.