Aaron Steele

Remote MCP server, MIT licensed

Doorknock

Everything a salesperson could learn about a company before they knock, read live off that company's own website and DNS, scored against your rules, and written into HubSpot.

MCP
https://doorknock-eight.vercel.app/mcp
OpenAPI
/openapi.json
Health
/health, calls every upstream
Source
github.com/Aa-ronJS/doorknock

Checked from your browser: not checked yet not checked yet

01

Connect it while you read this.

Streamable HTTP, stateless, and no authentication at all on the research tools.

claude mcp add --transport http doorknock https://doorknock-eight.vercel.app/mcp

The HubSpot tools need a private app token with crm.objects.companies.read and .write. It goes on the connection as a header, never as a tool argument.

claude mcp add --transport http doorknock https://doorknock-eight.vercel.app/mcp \
  --header "X-HubSpot-Token: pat-na1-..."

Anything that does not speak MCP gets the identical operations at /v1/<operation>, described by an OpenAPI document generated from the same constants the router uses, so the two cannot drift. That is what a custom GPT Action or an automation platform points at.

02

Seven tools.

Each one states what it will not do beside what it does, because the refusals are the part that keeps a model honest.

01
research_company

Reads the company's own home page and its public DNS. Returns the marketing and CRM tools loaded on the page, which standard pages exist, who handles their email, and whether their outbound email is protected.

Will not tell you headcount, revenue, ownership or contact names. None of it is knowable from a domain, and it would rather say nothing than guess.
02
qualify_lead

Runs the research, then scores it against the profile you pass in, returning a tier with the reasoning attached to every rule: what was expected, what was observed, and the source behind it.

Will not treat a signal it could not determine as a miss. That comes back as indeterminate and blocks the tier, because "we could not tell" and "no" send a lead to different people.
03
check_email_posture

MX, SPF and DMARC read in plain words. It keeps two questions apart on purpose: whether your mail will reach them is about their MX records, and whether their own mail lands is about SPF and DMARC.

Will not check DKIM. The selector cannot be discovered from DNS, so checking it means guessing, and a guess that misses looks identical to a domain with no DKIM at all.
04
list_signals

The vocabulary that profile rules are written against: 54 named facts, what each one means, and what its absence does and does not prove. No network calls.

Will not let a rule quietly reference a signal that does not exist. That comes back labelled as an unknown signal.
05
hubspot_check_connection

Calls HubSpot with the token on this connection and reports the portal and scopes. Worth running before the first write of a session: a token with read but not write scope fails at the moment of writing, which is the worst time to find out.

06
hubspot_find_company

Finds a company by exact domain, so an enrichment does not create a second record for a company already there under a different name.

07
hubspot_write_company

Creates or updates the company and optionally adds a timeline note. Properties the portal does not have are skipped and listed back rather than failing the whole write, and an update that would change nothing reports "unchanged" instead of touching the record.

Will not write a property you did not name, and will not decide on its own that a lifecycle stage should move.
03

Three decisions worth arguing about.

04

What broke.

Six defects so far. Three came from the adversarial suite on its first run against the deployment, one from the independent verification script, and one was in a test rather than in the server.

05

Check it without trusting this page.

My own tests passing proves very little to someone who did not write them.

# Anthropic's own inspector, not my code
npx @modelcontextprotocol/inspector --cli \
  https://doorknock-eight.vercel.app/mcp --transport http --method tools/list

# raw JSON-RPC, no client, no session, because it is stateless
curl -s -X POST https://doorknock-eight.vercel.app/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# in the repository: re-fetches every fact from a DIFFERENT resolver
# than the server used, and the page straight from the company
npm run prove -- https://doorknock-eight.vercel.app

Or the version that needs only a browser. Open both of these and compare the DMARC line.

06

Limits, stated plainly.