Back to skill

Security audit

Predict.fun MCP

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but its optional HTTP/SSE mode can expose your Graph API quota and broad query tools to anyone who can reach the server.

Install only if you trust the publisher and understand that all tool calls use your GRAPH_API_KEY. Prefer stdio/local use, pin the npm package version, and do not expose --http or --http-only to untrusted networks unless you add authentication, firewall restrictions, rate limits, and monitoring for Graph API usage.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Error
Location
src/index.ts:1719
Finding
Unauthenticated Network-Accessible MCP Transport Allows Unauthorized API Quota Use## Vulnerability Details **File Location**: `src/index.ts`, lines 1719-1749 **Vulnerability Type**: Unauthenticated remote service and insufficient access control **Risk Level**: High ### Vulnerable Code ```ts function startHttpTransport(port: number) { const app = express(); const sessions = new Map<string, SSEServerTransport>(); app.get("/sse", async (req, res) => { const transport = new SSEServerTransport("/messages", res); sessions.set(transport.sessionId, transport); res.on("close", () => { sessions.delete(transport.sessionId); }); await server.connect(transport); }); app.post("/messages", async (req, res) => { const sessionId = req.query.sessionId as string; const transport = sessions.get(sessionId); if (!transport) { res.status(400).json({ error: "Invalid or expired session" }); return; } await transport.handlePostMessage(req, res); }); app.get("/health", (_req, res) => { res.json({ status: "ok", server: "predictfun-mcp" }); }); app.listen(port, () => { console.error(`SSE transport listening on http://localhost:${port}/sse`); }); } ``` ### Technical Analysis The HTTP/SSE transport does not authenticate or authorize clients. Any party able to reach the listening port can create an SSE session through `/sse` and submit MCP messages through `/messages`. Calling `app.listen(port)` without specifying a host normally binds the service to all available interfaces, even though the log message claims that it is listening on `localhost`. The project documentation also presents this transport as suitable for remote deployments. Consequently, the service may become accessible from other hosts when the port is exposed by a host firewall, container configuration, cloud security group, or reverse proxy. Session IDs prevent posting to a nonexistent session but do not constitute authe ...[truncated 2241 chars]
Remediation
## Remediation Suggestions 1. **Bind locally by default** - Replace `app.listen(port)` with `app.listen(port, "127.0.0.1")`. - Require an explicit, security-conscious configuration option before binding to non-loopback interfaces. - Ensure log messages report the actual bind address. 2. **Require authentication for remote access** - Protect both `/sse` and `/messages` with a strong bearer token, mutual TLS, or an authenticated reverse proxy. - Validate authentication before allocating a session. - Use constant-time token comparison where application-managed static tokens are supported. - Never treat possession of an MCP session ID as proof of client identity. 3. **Add authorization controls** - Restrict sensitive or costly tools, especially `query_subgraph`, to explicitly authorized clients. - Consider disabling arbitrary GraphQL queries in remotely exposed deployments. - Apply per-client quotas and allowlists for supported tools and subgraphs. 4. **Add abuse protections** - Enforce per-client and global rate limits. - Limit concurrent SSE sessions and requests. - Apply request-body size limits and request timeouts. - Bound GraphQL query length, depth, alias count, and complexity. - Add upstream fetch timeouts and cancellation. - Close idle sessions and cap session lifetimes. 5. **Secure deployment guidance** - Document that remote mode must not be directly exposed to untrusted networks. - Require TLS for remote traffic through a properly configured reverse proxy or native HTTPS listener. - Provide firewall, container port-publication, and cloud security-group guidance. - Remove or revise documentation that implies unauthenticated SSE mode is safe for remote deployment. 6. **Protect the upstream credential** - Configure spending and usage limits for `GRAPH_API_KEY` where supported. - Monitor abnormal query volume and rotate the key after su ...[truncated 161 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (23)

Agent Config Directory Access

High
Category
Agent Snooping
Content
### Claude Desktop / Manual Config

Add to your MCP config (`~/.claude/settings.json` or Claude Desktop settings):

```json
{
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
Yes, this is a mismatch. The description promises a substantial data-access and analytics skill for Predict.fun on BNB Chain via The Graph, but the provided code chunk contains no operational logic at all. There are no functions, imports, API calls, blockchain interactions, Graph queries, or analysis utilities. While the code does not exhibit undeclared harmful behavior, its actual behavior is materially different from the declared primary purpose because it does nothing.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
The README instructs users to execute the package via `npx predictfun-mcp` without pinning a specific version. That causes clients to fetch and run whatever package version is current at install/runtime, increasing supply-chain risk if a malicious or compromised release is published later. In the MCP context, this is more sensitive because the server is intended to be run by AI tooling and may receive local context or credentials.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README promotes HTTP/SSE modes for remote deployments but does not warn that exposing an MCP endpoint over the network can broaden access to tools and data if left unauthenticated or broadly bound. Even if this server mainly fronts Predict.fun data, it still uses an API key and may be integrated into agent environments where network exposure creates unauthorized usage, abuse, or pivot opportunities.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
This invocation also uses unpinned `npx predictfun-mcp`, so users may run an unexpected future package version. If the npm package is hijacked or a bad release is pushed, the command could execute attacker-controlled code on the host. Because this mode enables HTTP/SSE exposure, a compromised package could additionally open network-facing behavior.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
The SSE-only example runs the package from npm without a version pin, which is a classic supply-chain exposure. A malicious update could be pulled automatically and run in remote deployment scenarios, where impact may include credential theft or unauthorized network service behavior.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
Using `MCP_HTTP_PORT=4000 npx predictfun-mcp --http` without version pinning leaves execution dependent on the latest npm state. That creates avoidable supply-chain risk, especially for a service intended to expose an HTTP endpoint and handle API-key-backed queries.

Rp1

Medium
Category
MCP Rug Pull
Confidence
83% confidence
Finding
The Docker example runs `predictfun-mcp` without an image tag or digest, so users may execute whichever local or registry image is currently tagged, including a changed or poisoned image. Mutable image references reduce reproducibility and make supply-chain compromise harder to detect.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The transport mode table documents `npx predictfun-mcp` variants without pinning versions, reinforcing insecure deployment guidance across multiple usage paths. Repetition in documentation increases the likelihood that users adopt unsafe defaults and continuously run unreviewed future releases.

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill declares it needs environment variables and network access but does not constrain tool scope with explicit permissions or allowed-tools. In an agent environment, that increases the blast radius by making external requests and secret-bearing execution less governed, which can lead to unintended API key use or broader capability exposure than users expect.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
Using `npx predictfun-mcp` without a pinned version creates a supply-chain risk because execution will resolve to the latest published package at runtime. If the package is compromised, typosquatted, or updated maliciously, users may execute attacker-controlled code with network access and access to GRAPH_API_KEY.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The `query_subgraph` tool accepts an arbitrary user-supplied GraphQL string and forwards it directly to The Graph endpoint using the server's API key. Even though this is 'read-only' data access, it materially expands the capability from curated analytics to unrestricted enumeration and extraction, which can be abused to harvest large datasets, discover undocumented schema objects, and consume the operator's paid query budget.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The custom query path performs network requests using untrusted user input without any user-facing warning, guardrails, or approval step. While the endpoint is fixed, the effective operation still lets users drive arbitrary remote queries through the server's credentials, creating hidden cost, data-extraction, and abuse risks that a user may not realize they are invoking.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The skill goes beyond simple market-data access and includes trader profiling, persona classification, whale detection, leaderboarding, and scans for behavioral archetypes such as 'resolution_sniper' and 'arbitrageur'. This creates a surveillance-style analytics surface centered on individual wallet behavior, which is more sensitive and potentially harmful than the manifest's high-level market-data description, especially because it operationalizes deanonymized behavioral inference at scale.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
Multiple tools send user-provided wallet addresses and derived participant data to The Graph endpoints without any explicit notice, consent, or minimization, and the endpoint URLs embed the operator's API key. In a skill that profiles traders and scans personas, this creates a meaningful privacy and data-governance risk because users may not realize their queried addresses and behavioral lookups are being transmitted to a third-party service.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The `query_subgraph` tool accepts arbitrary user-supplied GraphQL and forwards it directly to paid external subgraph endpoints using the server's `GRAPH_API_KEY`. This bypasses the narrower analytics interface exposed by the rest of the skill and enables unrestricted data extraction and potentially expensive or abusive queries at the operator's expense, even if it does not directly enable code execution.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dev": "tsc && node dist/index.js"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "express": "^4.21.0"
  },
  "devDependencies": {
Confidence
96% confidence
Finding
The dependency uses a caret range instead of an exact version, which makes builds non-reproducible and can silently pull in newer transitive code over time. In an MCP server context, this increases supply-chain uncertainty because the server may run in agent or automation environments where dependency drift can introduce vulnerable or incompatible behavior.

Unverifiable Dependency: @modelcontextprotocol/sdk has 3 known advisory(ies) (CVE-2026-25536 (@modelcontextprotocol/sdk has cross-client data leak via shared server/transport); CVE-2026-0621 (Anthropic's MCP TypeScript SDK has a ReDoS vulnerability); CVE-2025-66414 (Model Context Protocol (MCP) TypeScript SDK does not enable DNS rebinding protec)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
The manifest does not pin @modelcontextprotocol/sdk even though advisories exist for this package family, so it is impossible to verify from this file whether the installed version is safe. This is more concerning in an MCP server because SDK flaws can affect cross-client isolation, request handling, or network trust boundaries in agent-facing deployments.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.1",
    "express": "^4.21.0"
  },
  "devDependencies": {
    "@types/express": "^5.0.0",
Confidence
96% confidence
Finding
The express dependency is specified with a caret range, allowing automatic minor/patch updates that may change the installed codebase between environments. For a network-facing server, even small dependency drift matters because Express processes untrusted requests and could inherit newly introduced bugs or pull vulnerable transitive packages.

Unverifiable Dependency: express has 5 known advisory(ies) (CVE-2024-10491 (Express ressource injection); CVE-2014-6393 (No Charset in Content-Type Header in express); CVE-2024-9266 (Express Open Redirect vulnerability) +2 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
91% confidence
Finding
Express has known advisories, and because the dependency is not pinned, the manifest alone cannot prove the deployed version is outside affected ranges. Since this package is used by an HTTP-capable MCP server, any unresolved Express issue could directly affect externally reachable request handling, making the uncertainty more dangerous than in a purely local tool.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"express": "^4.21.0"
  },
  "devDependencies": {
    "@types/express": "^5.0.0",
    "@types/node": "^22.0.0",
    "typescript": "^5.7.0"
  }
Confidence
88% confidence
Finding
The @types/express package is also unpinned, which reduces build reproducibility. This is less severe than a runtime dependency because it is development-only, but it can still affect CI integrity, type-checking behavior, and potentially the safety of generated or compiled artifacts.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "devDependencies": {
    "@types/express": "^5.0.0",
    "@types/node": "^22.0.0",
    "typescript": "^5.7.0"
  }
}
Confidence
88% confidence
Finding
The @types/node development dependency is unpinned, creating nondeterministic build inputs. While not a direct runtime exposure, drifting Node type definitions can alter compilation and validation outcomes, which weakens supply-chain control over the produced server artifact.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"devDependencies": {
    "@types/express": "^5.0.0",
    "@types/node": "^22.0.0",
    "typescript": "^5.7.0"
  }
}
Confidence
90% confidence
Finding
The typescript compiler dependency is unpinned, so different installs may compile the project with materially different compiler behavior. In security-sensitive automation or agent-serving code, inconsistent builds can hide or introduce issues and make incident response harder because artifacts are not reproducible.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
dist/index.js:9

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
src/index.ts:12