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

.FAF Context

mcp:wolfe-jam/faf-mcp

.FAF Context — persistent project context for Cursor, VS Code and every MCP-compatible IDE.

Verdict
Suspicious

Risky behavior the analysis could not rule out.

Analyzed Oct 26, 2026, 13:37 UTC at 0e5e64d

Dispute security verdict

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

1
findings
critical
highest severity
1
threat classes
166
files scanned
1,205/1,205
checks satisfied
0
package advisories

Why this verdict

mcp-server . Suspicious
  • suspicious: unverified finding mcp-server:.:src/server.ts:101:TOOLPOISON
skill skills/faf-ide No issues found
  • benign: 15 checks satisfied, no findings for skill:skills/faf-ide

Security findings 1

Tool poisoning

criticalUnverifiedlow confidence

The server's resources/list response appends resource metadata computed at runtime from the skill catalog, so those resource names and descriptions reach the agent without static review.

resources: [...base.resources, ...this.skills.listResourcesMeta()],
conceal:remotevector:metadata
Refuted findings 13

Candidates the review stage examined and rejected, kept for transparency.

Persistence

criticalRefutedlow confidence

When no usable cwd exists, the engine adapter silently creates ~/Projects under the user's home directory and uses it as the working directory; this is a benign, documented fallback with no data source or sink chain.

fs.mkdirSync(projectsDir, { recursive: true });
Why it was refuted

Creating ~/Projects is a commented fallback that applies only when no usable cwd exists. It creates an empty directory and has no data source or sink chain, so it is not persistence.

Code execution

highRefutedlow confidence

The npm `debug` script starts the Node inspector bound to 0.0.0.0:9229, which would expose a remote debugger to the network if a developer runs it.

node --inspect-brk=0.0.0.0:9229 -r ts-node/register src/cli.ts
Why it was refuted

This is an openly named, opt-in developer 'debug' script. It is not a lifecycle hook, is never invoked by the server, and does what its name says, so there is no concealed harm or agent chain.

Code execution

highRefutedlow confidence

The undeclared manual 'debug' npm script starts the Node inspector bound to all interfaces (0.0.0.0:9229), which would expose a remote debugger if a developer runs it; it is a dev-only script and not an install-time hook.

node --inspect-brk=0.0.0.0:9229 -r ts-node/register src/cli.ts
Why it was refuted

This is a manual, openly named 'debug' dev script, not an install-time hook, and nothing in the artifact invokes it. There is no concealment and no chain to an agent.

Supply chain

highRefutedlow confidence

The Smithery start command runs `npx -y faf-mcp@latest`, so whatever is currently published under the mutable `latest` tag is fetched and executed without a pinned version.

args: ["-y", "faf-mcp@latest"]
Why it was refuted

The Smithery launch command runs the server's own package, faf-mcp, which is the declared launch mechanism, not a third-party dependency. The unpinned tag is a hygiene point, not concealed or undeclared behavior.

Supply chain

highRefutedmedium confidence

The Smithery start command runs the server via `npx -y faf-mcp@latest`, fetching and executing the latest unpinned registry package without confirmation on every launch.

args: ["-y", "faf-mcp@latest"]
Why it was refuted

This is the same launch command as the previous finding. It launches the artifact's own published package as the declared stdio start command, so it is not undeclared or harmful.

Persistence

highRefutedlow confidence

The agents export command writes project.faf-derived content into AGENTS.md, an agent rules/memory file that later agent sessions load; this is the tool's declared export purpose and is gated by an overwrite check, so it is a low-risk capability.

const outputPath = path.join(projectPath, 'AGENTS.md');
Why it was refuted

Writing AGENTS.md is the declared export function. It is gated by an overwrite check and force flag, and it only writes the user's own project.faf data in a managed block, so it is the core-capability carve-out.

Persistence

highRefutedlow confidence

The cursor export command writes project.faf-derived content into .cursorrules, an agent rules file that Cursor loads; this is the declared export feature and is gated by an overwrite check, so it is a low-risk capability.

const outputPath = path.join(projectPath, '.cursorrules');
Why it was refuted

Writing .cursorrules is the declared cursor export feature. It is gated by an overwrite check and force flag, and it only writes the user's own project.faf content into a managed block.

Data exfiltration

highRefutedlow confidence

The GitHub extractor makes outbound HTTPS calls to api.github.com (sending the owner/repo identifiers) even though the README describes the server as running locally over stdio; the network use is not declared as a tool dependency, though it appears to serve the `faf git` feature and is plausibly honest.

const response = await fetch(apiUrl, {
Why it was refuted

The fetch goes to api.github.com for the declared `faf git` feature, and the only data sent is the user-supplied owner/repo. The README's 'runs locally over stdio' describes the transport, so this is not exfiltration.

Credential access

highRefutedlow confidence

GITHUB_TOKEN/GH_TOKEN from the environment is attached as an Authorization header to every GitHub API fetch, including one to a URL (data.languages_url) taken from an API response; the destination is GitHub's own API, so this is likely honest but the token is sent to a response-derived URL without a host check.

fetch(data.languages_url, {
Why it was refuted

The GitHub token is the wrapped service's own env credential, sent only to GitHub endpoints, and the error message tells users to set GITHUB_TOKEN. The languages_url comes from api.github.com's own response, so this is the env-API-key carve-out with no attacker-controlled sink.

Credential access

highRefutedlow confidence

Undeclared read of GITHUB_TOKEN/GH_TOKEN from the environment, attached as an Authorization header to GitHub API requests including to a response-derived `languages_url`; this is consistent with the GitHub-wrapping purpose, but the destination for that URL is not pinned to api.github.com.

const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
Why it was refuted

Reading GITHUB_TOKEN/GH_TOKEN to authenticate GitHub API calls is the env-key-for-wrapped-service carve-out. It is user-facing in the error text, and the token only goes to GitHub-served URLs.

Persistence

highRefutedlow confidence

faf_auto writes CLAUDE.md (an agent memory/rules file) in the project directory from the faf-cli render of project.faf; this is the declared core function and the content is derived from the project's own data, with no external source.

writeClaudeMd(cwd, renderClaudeMd(readFaf(fafPath)));
Why it was refuted

faf_auto's documented step writes CLAUDE.md as a faf-managed block, rendered from the project's own project.faf. This is the declared core function, with no external content source and no concealment.

Supply chain

mediumRefutedlow confidence

package.json defines a standard npm 'prepare' lifecycle script that runs the local build (tsc) on install from source; this is a routine, low-risk install-time hook with no remote fetch or secret access.

"prepare": "npm run build"
Why it was refuted

The prepare hook only runs the local tsc build, with no remote fetch, secret access or concealment. This is a routine npm lifecycle script, so there is no harm.

Reconnaissance

lowRefutedlow confidence

faf_list accepts a caller-supplied path (including ~ and absolute paths) with no confinement and enumerates directories and project.faf presence up to a caller-chosen depth, returning the listing to the agent; this is a bounded directory-enumeration capability that matches the tool's declared purpose.

const targetPath = args?.path || this.engineAdapter.getWorkingDirectory();
Why it was refuted

faf_list is a directory lister whose purpose is finding project.faf projects, and it returns only names and the project.faf flag. This is its declared function, with no hidden sink or exfiltration chain.