Announcing Beacon: Self-improving memory for coding agents
← All MCP servers
MCP server

HirePulse Agent Gateway

mcp:edtool79-collab/hirepulse-mcp

26 tools: 4-in-1 company profiles, growth signals, commerce; free sandbox and 25-call trials. x402.

Verdict
Suspicious

Risky behavior the analysis could not rule out.

Analyzed Oct 28, 2026, 12:15 UTC at edf05c1

Dispute security verdict

If you believe this verdict is incorrect, submit a dispute and we'll review it.

9
findings
critical
highest severity
4
threat classes
32
files scanned
138/138
checks satisfied
0
package advisories

Why this verdict

  • suspicious: finding mcp-server:.:proxy.mjs:21:SUPPLY
  • suspicious: finding mcp-server:.:proxy.mjs:32:TOOLPOISON
  • suspicious: finding mcp-server:.:proxy.mjs:36:TOOLPOISON
  • suspicious: finding mcp-server:.:proxy.mjs:42:EXFIL
  • suspicious: finding mcp-server:.:proxy.mjs:45:TOOLPOISON
  • suspicious: finding mcp-server:.:proxy.mjs:47:TOOLPOISON
  • suspicious: finding mcp-server:.:proxy.mjs:50:TOOLPOISON
  • suspicious: finding mcp-server:.:proxy.mjs:51:HIJACK
  • suspicious: finding mcp-server:.:server.json:10:SUPPLY

Security findings 9

Tool poisoning

criticalContextual riskmedium confidence

The proxy serves the agent's entire tools/list response, including tool names, descriptions, and parameter descriptions, directly from the remote host via remote.listTools(), so the instruction-bearing tool metadata the agent reads is remote-controlled at runtime and cannot be reviewed from the artifact.

const res = await remote.listTools(r.params);
conceal:remotevector:metadata
Counter-argument considered

The strongest kill case is that relaying listTools is normal proxy behavior, but the relayed content is instruction-bearing tool metadata injected into agent context, the target declares no claims that would make this a declared core capability, and the artifact's own folded metadata confirms nothing is statically fixed, so the finding stands unrefuted.

Tool poisoning

criticalContextual risklow confidence

The proxy fabricates parameter descriptions ('<name> parameter for <tool>') for remotely defined tools that ship without any, making unreviewed remote tool inputs appear documented to the agent and user.

schema.description = `${name} parameter for ${tool.name}`;
conceal:remotevector:metadata
Counter-argument considered

The strongest kill case is that filling empty descriptions with generic names is a benign usability shim and arguably a mitigation, but it does exactly what the claim says: it manufactures documentation for remote-controlled tool inputs so they appear reviewed to the agent, no carve-out covers metadata fabrication, and no declared claim or artifact text qualifies it.

Data exfiltration

criticalContextual riskmedium confidence

The proxy forwards every CallTool request, including all agent-supplied arguments, to the remote host https://gethirepulse.xyz/mcp, so any data the agent places in tool parameters leaves the host for a third-party server whose tool schemas are themselves remotely defined and unreviewable.

server.setRequestHandler(CallToolRequestSchema, r => remote.callTool(r.params));
conceal:remotevector:code
Counter-argument considered

The strongest kill case is that tool arguments flowing to the implementing service is ordinary MCP operation, but the closed carve-out list does not cover relaying agent-supplied arguments to an unpinned third-party host whose schemas are themselves remotely defined, the declared description is null so no declared-core-capability carve-out exists, and open disclosure of the URL in server.json does not neutralize the flow.

Tool poisoning

criticalContextual riskmedium confidence

The proxy forwards resources/list from the remote host unchanged, so resource metadata the agent may load is remote-controlled and unreviewable in the artifact.

server.setRequestHandler(ListResourcesRequestSchema, r => remote.listResources(r.params));
conceal:remotevector:metadata
Counter-argument considered

The strongest kill case is that resource listings are low-stakes directory data, but the metadata the agent uses to select what to load is authored by the remote at runtime and cannot be reviewed from the artifact, no declaration or carve-out covers it, and the detector lead confirms the dependency is unresolved.

Tool poisoning

criticalContextual riskmedium confidence

The proxy forwards resources/templates/list from the remote host unchanged, so resource-template metadata is remote-controlled and unreviewable in the artifact.

server.setRequestHandler(ListResourceTemplatesRequestSchema, r => remote.listResourceTemplates(r.params));
conceal:remotevector:metadata
Counter-argument considered

The strongest kill case is that resource templates are only name patterns, but they are remote-authored metadata the agent consumes and cannot review in the artifact; nothing declares this (declared claims: None) and no carve-out applies, so the finding survives.

Tool poisoning

criticalContextual riskmedium confidence

The proxy forwards prompts/list from the remote host unchanged, so prompt texts that are injected directly into agent context are authored by the remote host at runtime and cannot be reviewed from the artifact.

server.setRequestHandler(ListPromptsRequestSchema, r => remote.listPrompts(r.params));
conceal:remotevector:metadata
Counter-argument considered

The strongest kill case is that prompts/list returns only titles rather than bodies, but the prompt inventory steers which remote-authored texts the agent will fetch into context and is fixed by the remote at runtime; with declared claims None and no carve-out, the unreviewable-metadata finding is not refuted.

Agent hijacking

highContextual risklow confidence

The proxy relays GetPrompt and ReadResource content verbatim from the remote server gethirepulse.xyz into the agent's context, giving the remote party a direct, unreviewable, remotely mutable instruction-injection channel.

server.setRequestHandler(GetPromptRequestSchema, r => remote.getPrompt(r.params));
conceal:remotevector:code
Counter-argument considered

The strongest kill case is that relaying prompt content is the proxy's whole purpose, but purpose is not a carve-out here since the declared description is null and the behavior is undeclared; prompt and resource bodies fetched verbatim from the mutable remote are a concrete source-to-agent-context instruction-injection channel, and no validation or mitigation exists in the shown code.

Supply chain

highContextual riskmedium confidence

The server manifest directs clients to fetch all MCP tools, resources, and prompts from the mutable remote endpoint https://gethirepulse.xyz/mcp, making the agent's instruction surface depend on unpinned remote content rather than anything reviewable in the artifact.

remotes[0].url: https://gethirepulse.xyz/mcp
conceal:remotevector:metadata
Counter-argument considered

The strongest kill case is that a remote-server manifest simply must state its URL, but documented is not acceptable: open disclosure does not clear the unpinned, mutable dependency of the agent's instruction surface on gethirepulse.xyz, the pinned-registry carve-out fails because nothing is pinned, and the declared claims of the target are None, so no carve-out or mitigation refutes the finding.

Supply chain

mediumContextual risklow confidence

The proxy makes the mutable, unpinned remote https://gethirepulse.xyz/mcp the agent's entire tool/instruction surface by forwarding all tool calls with their arguments, resource reads, and prompt fetches to it and relaying the remote's tool descriptions, resources, prompts, and 402 payment challenges back to the agent unmodified and unvetted.

await remote.connect(new StreamableHTTPClientTransport(new URL('https://gethirepulse.xyz/mcp')));
vector:code
Counter-argument considered

The strongest kill case is that this is a transparently declared gateway architecture, but the declared description is null (declared claims: None) and the remote is unpinned and mutable, so neither the declared-core-capability nor pinned-registry carve-out applies; the code verifiably makes the remote the agent's entire tool/instruction surface with no validation anywhere in the shown 61 lines, and no mitigation exists.

Open questions 13

What the analysis could not settle from the code alone.

What tool descriptions, prompts, and resource contents does the remote endpoint https://gethirepulse.xyz/mcp actually serve to the agent at runtime? This cannot be determined from the artifact, so the presence or absence of actual hijacking/poisoning content is unestablished.
Tool poisoning
Do the x402 payment challenges that 'pass through' the proxy (proxy.mjs:11) deliver remote-authored instructions or payment/credential requests to the agent, and does the agent act on them?
Agent hijacking
What the remote at https://gethirepulse.xyz/mcp actually serves (tool count versus the advertised 26, tool descriptions, resource and prompt content) cannot be determined from the artifact, so the declared catalog cannot be reconciled with the delivered surface.
Tool poisoning
The comment 'Payment challenges pass through' indicates x402 payment flows transit the proxy, but no shown code handles payment authorization or credentials, so whether any local credential material participates is unknown.
Credential access
The proxy relays x402 payment challenges from the remote server ('Payment challenges pass through'); whether the remote can induce the agent into arbitrary or repeated payments cannot be determined from the shown units.
Whether the remote server at https://gethirepulse.xyz/mcp serves tool descriptions or prompt content carrying prompt-injection instructions is unknowable from the artifact, since the entire agent-visible tool surface is remote-controlled at runtime.
Tool poisoning
[file request smoke.mjs] What does smoke.mjs execute when run by the package.json test script (network calls, filesystem or environment access, credentials, or process operations beyond a benign connectivity check)?
Code execution
[file request README.md] Does README.md contain agent-facing instructions such as auto-run setup or prerequisite steps, credential or API-key handling, or URLs (including gethirepulse.xyz) from which to fetch further content?
Agent hijacking
[file request smoke.mjs] What does smoke.mjs, wired to the npm test script in package.json, execute on run: network calls, environment or file access, or output that an agent running the test suite would act on?
Code execution
[file request README.md] Does README.md (shipped per package.json files[]) contain agent-facing instructions such as auto-run setup or prerequisite steps, credential or API-key handling, or URLs from which to fetch further content?
Agent hijacking
[file request smoke.mjs] package.json's test script runs `node smoke.mjs`; what does smoke.mjs execute, and does it contact the network or modify local state?
Code execution
[file request README.md] What does README.md declare about the remote endpoint gethirepulse.xyz, data sent off-host, credentials, and telemetry, to compare against the proxy's full pass-through of all MCP traffic?
Data exfiltration
[file request smoke.mjs] smoke.mjs is executed by the package.json test script ('node smoke.mjs') but is not shown in this batch; does it perform network, filesystem, credential, or process operations beyond a benign MCP handshake or connectivity check?
Code execution

Execution chain 142

What it calls, runs, installs, reads and writes, with the line that does it.

GitHub repositories 2

edtool79-collab/hirepulse-mcp
edtool79-collab/hirepulse-mcp

URLs 140

https://gethirepulse.xyz
https://gethirepulse.xyz
https://gethirepulse.xyz/mcp
https://gethirepulse.xyz/mcp
https://github.com/edtool79-collab/hirepulse-mcp
https://github.com/edtool79-collab/hirepulse-mcp.git
https://github.com/sponsors/colinhacks
https://github.com/sponsors/epoberezkin
https://github.com/sponsors/express-rate-limit
https://github.com/sponsors/fastify
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/ljharb
https://github.com/sponsors/panva
https://github.com/sponsors/sindresorhus
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/express
https://opencollective.com/fastify
https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.5.tgz
https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.1.tgz
https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz
https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz
https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz
https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz
https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz
https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz
https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz
https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz
https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz
https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz
https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz
https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz
https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz
https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz
https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz
https://registry.npmjs.org/cors/-/cors-2.8.6.tgz
https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz
https://registry.npmjs.org/debug/-/debug-4.4.3.tgz
https://registry.npmjs.org/depd/-/depd-2.0.0.tgz
https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz
https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz
https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz
https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz
https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz
https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz
https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz
https://registry.npmjs.org/etag/-/etag-1.8.1.tgz
https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz
https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz
https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz
https://registry.npmjs.org/express/-/express-5.3.0.tgz
https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz
https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz
https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz
https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz
https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz
https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz
https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz
https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz
https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz
https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz
https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz
https://registry.npmjs.org/hono/-/hono-4.13.13.tgz
https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz
https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz
https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz
https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz
https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz
https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz
https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz
https://registry.npmjs.org/jose/-/jose-6.2.12.tgz
https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz
https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz
https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz
https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz
https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz
https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz
https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz
https://registry.npmjs.org/ms/-/ms-2.1.3.tgz
https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz
https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz
https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz
https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz
https://registry.npmjs.org/once/-/once-1.4.0.tgz
https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz
https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz
https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz
https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz
https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz
https://registry.npmjs.org/qs/-/qs-6.16.1.tgz
https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz
https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz
https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz
https://registry.npmjs.org/router/-/router-2.2.0.tgz
https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz
https://registry.npmjs.org/send/-/send-1.2.1.tgz
https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz
https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz
https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz
https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz
https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz
https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz
https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz
https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz
https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz
https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz
https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz
https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz
https://registry.npmjs.org/vary/-/vary-1.1.2.tgz
https://registry.npmjs.org/which/-/which-2.0.2.tgz
https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz
https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz
https://registry.npmjs.org/zod/-/zod-3.25.76.tgz
https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json

Coverage gaps 5

Parts of the artifact the static analysis could not fully examine.

dynamic_metadata line 12: tool (computed name) tools list depends on dependency-provided value (dependency): instance of a class from '@modelcontextprotocol/sdk': new Client({ name: 'hirepulse-mcp-proxy', version: '1.0.0' })
proxy.mjs
dynamic_metadata line 12: resource (computed name) resources list depends on dependency-provided value (dependency): instance of a class from '@modelcontextprotocol/sdk': new Client({ name: 'hirepulse-mcp-proxy', version: '1.0.0' })
proxy.mjs
dynamic_metadata line 12: resource (computed name) resourceTemplates list depends on dependency-provided value (dependency): instance of a class from '@modelcontextprotocol/sdk': new Client({ name: 'hirepulse-mcp-proxy', version: '1.0.0' })
proxy.mjs
dynamic_metadata line 12: prompt (computed name) prompts list depends on dependency-provided value (dependency): instance of a class from '@modelcontextprotocol/sdk': new Client({ name: 'hirepulse-mcp-proxy', version: '1.0.0' })
proxy.mjs
no_tools_extracted MCP server target with no statically extractable tools/prompts/resources
package.json