What access comes with an AI tool? Lugano Labs | Open research · Static source study Editor: Brian D. Evans, Lugano Labs’ technical co-founder and research editor. On 4 November 2026 he was 27th of 395 contributors by commits to Nous Research’s open-source Hermes Agent, and his company BDE Ventures ranked No. 172 on Inc.’s 2015 list of America’s fastest-growing private companies. (https://lugano.ai/method/#editor) First prepared 20 October 2026; revised 4 November 2026 Current theses and questions may change with new evidence. We welcome corrections and outside review. For 50 MCP servers, the add-ons that give AI apps tools, we set the code you install beside the README you read. Where the two disagree is the first place to look before you connect. In short After hand review, 7 of the 50 MCP packages we read without running them have code that names a third-party host no README mentions. These are source observations, not proof of requests, behavior, intent or a vulnerability. Our own audit found the sample’s download ranking recorded zero for most candidates, and a corrected sample with checked counts and recorded exclusions could change these counts. What this is A static study we did ourselves on 50 MCP server packages from npm. The scanner and its data are on GitHub, and the appendix gives the method and our audit of the sample. WHAT THE README LEAVES OUT The README for nx-mcp shows a setting called disableTelemetry. The package’s code says more. It names Google Analytics’ Measurement Protocol, with fields including user_id, editor and operating system. The README names neither that host nor those fields. [1] An MCP server like nx-mcp is an add-on that gives an AI app tools. Before you connect one, you usually have its README and its published package to go on. We don’t think a README is a security document. We do think it’s often the only document people read before they connect a server. What matters to us is the gap between what a tool needs and what its user is told. So for 50 MCP server packages on npm, we set the code beside the README. Our scanner listed the hosts each package’s code names, and we checked each one against the README and package.json. After hand review, seven packages name a third-party host that no README mentions. One of the seven ships without a README. Three name analytics hosts, as nx-mcp does. [1] [4] These 50 are a defined sample. Every count here describes them and no wider group. We searched npm with four queries and ranked the results by last month’s downloads. We set aside some packages by hand, such as clients and SDKs, and kept 50 of the highest-ranked that remained. [2] That ranking recorded zero downloads for most candidates, so these aren’t the 50 most-downloaded MCP servers. [1] We read their code and never ran it. You can run the same check yourself. Open a server’s published package and list the hosts it names and the secrets it reads. Then compare that list with the README. Where the code names a host the README leaves out, you’ve found the gap. Read that line before you decide. CODE BESIDE README, PACKAGE BY PACKAGE The table holds the seven, plus two packages whose READMEs document their telemetry. Five of the 50 carry analytics or telemetry markers after hand review, and all five are here. Every row survived that review. [1] [4] What each package’s code names, beside what its README says | Package | What its code names | What its README says | Host not in README | Analytics or telemetry marker | Hosts other than analytics | @upstash/context7-mcp | Microsoft Entra signing keys, for checking tokens | Doesn’t mention Entra | Yes | No | hostinger-api-mcp | api.ipify.org, in a bundled skill | Doesn’t mention the host | Yes | No | @sentry/mcp-server | Default endpoints from a bundled AI SDK | No README. We checked its package.json | Yes | No | comfyui-mcp | Model providers | Doesn’t mention those providers | Yes | No | Analytics hosts (three of the seven) | nx-mcp | Google Analytics’ Measurement Protocol | Only a disableTelemetry setting | Yes | Yes | figma-developer-mcp | A PostHog host and project key | Says nothing about it | Yes | Yes | exa-mcp-server | An Agnost analytics SDK call | Says nothing about it | Yes | Yes | Telemetry the README documents | n8n-mcp | A first-party telemetry endpoint | Documents it | No | Yes | @alisaitteke/photoshop-mcp | A Rybbit analytics host | Documents it, with an opt-out | No | Yes In hostinger-api-mcp, the undocumented host sits in a bundled skill, a set of instructions for the AI app. That skill includes the command curl -s https://api.ipify.org, which returns the machine’s public IP. [1] The comfyui-mcp package also defines a tool, panel_add_mcp. Its code includes logic to add new MCP-server entries to the user’s ~/.claude.json. That tool falls outside our host check. We checked READMEs for hosts, not tools, so we can’t say whether its README mentions this one. [1] IN THESE 50, BROAD REACH IS COMMON AND OFTEN ORDINARY Ask what access comes with these tools, and the scanner’s answer is broad. Code in 24 of the 50 packages can read a secret-named environment variable, one whose name contains a string such as KEY, TOKEN or SECRET. In 20, it can start processes. In 16, it can find the user’s home directory. A package can fall in more than one group. Those three are raw scanner counts, while the seven and the five survived hand review. [7] Many of these markers reflect ordinary engineering choices. A server that drives a local build tool may need to start processes. One that calls a hosted API usually reads its key from an environment variable. [1] So we’d start with the gap. In these 50 it’s rarer than broad reach, and each case comes with a file and line to read. A MARKER TELLS YOU WHERE TO READ Two rows in the table invite the worst reading. The READMEs of figma-developer-mcp and exa-mcp-server say nothing about their analytics. Yet in both, the cited code is more careful with data than the bare marker suggests. In figma-developer-mcp, the code has two opt-outs, FRAMELINK_TELEMETRY=off and DO_NOT_TRACK. It also has redaction logic. [4] In exa-mcp-server, the Agnost analytics SDK call switches off capture of tool inputs, outputs and errors. [4] So we treat a marker as a reason to read the line, never as a finding about the package. A static marker doesn’t prove a runtime request, a behavior, intent or a vulnerability. We can’t say, for instance, whether the PostHog code in figma-developer-mcp runs in ordinary use. [1] SCOPE AND LIMITS What we did We read the source in 50 npm tarballs without running it, including dependencies compiled into their bundles. We read every host-flagged package by hand at the cited line. The appendix gives the method. What we didn’t do We ran no package code, npm install or lifecycle script. We didn’t read transitive dependencies, and none of the 50 tarballs carried its own node_modules. We haven’t published the ranking step. What this can’t tell you It can’t show whether flagged code runs in ordinary use, or whether the user is told at runtime when it does. We checked only READMEs and package.json, and only for hosts. A vendor may document an endpoint elsewhere. Confidence High that each cited line holds what we report. Moderate in the counts, since the scanner can flag code that never runs and the hand review remains internal. Anyone can inspect the cited files and lines. Low for MCP servers beyond these 50, since the sample rests on one month of downloads, a ranking of mostly zeros and unrecorded exclusions. What would change our view We’d change our view if a corrected sample, drawn from verified download counts with every exclusion recorded, moved the counts, or if an independent check overturned the hand review. A runtime study could also change it, by driving each package through its documented tools in an instrumented sandbox to show which flagged paths run and what they send. Corrections We log every correction, with its date, on How we work (https://lugano.ai/method/#corrections). APPENDIX WHAT OUR AUDIT FOUND IN THE SAMPLE The study report calls these 50 the most-downloaded servers among our candidates. [1] Together they had about 19.6 million downloads in the month before the scan. [7] Our own audit on 3 November found that the description doesn’t hold. It also found two gaps in the record behind the sample. Most candidates had a recorded download count of zero, and many of those zeros were failed lookups. [1] One of those zeros belonged to chrome-devtools-mcp. That MCP server had about 8 million downloads in the month before the scan. [8] Of the packages we sampled, only @storybook/mcp had more, with nearly half the sample’s downloads. [1] Our exclusion rule covered clients, SDKs, frameworks, adapters, transports and platform binaries. We removed more packages by hand than that rule called for, and didn’t record why for each one. [1] Several of them call themselves MCP servers on npm, such as @akutishevsky/lunchmoney-mcp and @ankimcp/anki-mcp-server. [8] Two of our own counts also disagree. The report finds 703 unique packages in the search results, but the ranking file published with it, ranked.json, lists 571. Nothing in our published materials explains the difference. [1] HOW WE READ THE CODE We pulled the tarballs from npm on 20 October 2026. Each went into its own isolated directory, and we neither installed nor ran it. [1] The scanner reads the JavaScript and TypeScript files outside test, example and documentation directories. It skips node_modules, .d.ts and .map files. It reads only the first 20,000 characters of each line. [3] A host counted as documented if its registrable domain appeared in the README or in five package.json fields. Those fields are homepage, description, author, repository and bugs. A host also counted if it contained the package’s scope. For an unscoped package, the scope is the first hyphen-separated part of its name. [3] Sentry’s package ships without a README, and so does Supabase’s. For Sentry’s, package.json was the only baseline we checked. [4] The scanner recorded eight kinds of marker. Five feed this essay, and the table defines them. [3] The other three, eval, network calls and file writes, are in the published per-package results. [5] The five markers this essay uses | Marker | What counts | URL | A URL outside comments | Analytics or telemetry host | A known analytics or telemetry host | Process start | A child_process import, or an execSync, spawnSync or execFileSync call | Home-directory lookup | Code that looks up the user’s home directory | Secret-named variable | Code reads it as process.env.NAME, and the name contains KEY, TOKEN, SECRET, PASSWORD, PASSWD or CREDENTIAL Pattern matching can flag code that never runs, so we read every host-flagged package by hand, at the cited file and line. It can also miss a request that code builds from pieces the scanner can’t join. [3] That review dropped six cases and gave a reason for each. Among them were OAuth callback allowlists in two packages, a funding link, and a dashboard link in tool output that the scanner took for telemetry. A general rule covers the rest, such as documentation links, test fixtures, example domains and a vendor’s own hosts in its own package. [4] The review cut both host counts hard. Packages with undocumented hosts fell from 13 to 7. Those with analytics or telemetry hosts fell from 7 to 5. [7] The review is internal, and its record is published for anyone to check. [4] We published the scanner, the sample list, the ranking file, the hand review and the per-package results with the report. They’re also on GitHub, at Lugano-AI/mcp-source-study, under the Apache 2.0 license. Read the report’s main table with care. Its Undocumented and Telemetry columns still list hosts for packages the hand review cleared. [1] Our evidence records give the file and line behind every observation. [6] To rerun the scan, put the extracted packages in one directory and run the command below. Then run report.py to rebuild the report tables. We don’t bundle the tarballs. A rerun on fresh downloads may not match exactly, since versions and download counts keep changing. [1] python3 -I scan.py packages.txt ranked.json . THE OPEN QUESTION Which of these code paths run in ordinary use, and is the user told when they do? A static read can’t answer that. Our own audit of the sample found a download ranking with many failed lookups. The next step is a new sample with checked counts and recorded exclusions. A runtime study could then drive each package through its documented tools in an instrumented sandbox. That study is a proposal. We haven’t designed it in detail or run it. SOURCES [1] Static source observations from 50 sampled MCP server packages Lugano Labs | 2026 Local evidence: materials/research/mcp-scan/REPORT.md The study report: sample, method, headline counts, per-package observations with cited files and lines, and limits. Scope: Static code markers in a defined sample. It does not establish runtime behavior, intent, prevalence across MCP packages, or vulnerabilities. Its description of the sample as the 50 highest-ranked candidates rests on a ranking that recorded zero downloads for most candidates. [2] The 50-package sample (packages.txt) Lugano Labs | 2026 Local evidence: materials/research/mcp-scan/packages.txt The sampled packages, in download-rank order. Scope: A list drawn from a one-month download ranking that recorded zero for most candidates; the reason for each exclusion is not recorded. [3] The static scanner (scan.py) Lugano Labs | 2026 Local evidence: materials/research/mcp-scan/scan.py Records URLs, analytics hosts, process, home-directory and environment-variable markers in package source. Scope: Pattern matching. It can miss behavior assembled at runtime and flag code that never runs. [4] Manual review record (review.json) Lugano Labs | 2026 Local evidence: materials/research/mcp-scan/review.json The findings that survived a hand reading at each cited location, and the false positives dropped with reasons. Scope: An internal review; it has not been independently checked. [5] Per-package results (results.json) Lugano Labs | 2026 Local evidence: materials/research/mcp-scan/results.json The markers recorded for each sampled package. Scope: Scanner output; host and telemetry entries include cases that manual review later dropped. [6] Cited source locations (evidence.jsonl) Lugano Labs | 2026 Local evidence: materials/research/mcp-scan/evidence.jsonl One record per observation, with the package, file, line and source text. Scope: Records of source text only; not runtime observations. [7] Scan summary and reviewed headline counts (summary.json, headline.json) Lugano Labs | 2026 Local evidence: materials/research/mcp-scan/headline.json Scanner totals before review (summary.json) and the reviewed headline counts, including total monthly downloads (headline.json). Scope: Aggregate counts for this sample only. [8] npm downloads API and registry metadata, checked 3 November 2026 npm, Inc. | 2026 https://api.npmjs.org/downloads/point/2026-09-20:2026-10-19/chrome-devtools-mcp Point download counts for 20 September to 19 October 2026 and package descriptions, queried during a Lugano audit of this study. chrome-devtools-mcp, recorded at zero in ranked.json, shows 8,032,928 downloads, and its registry entry calls it an MCP server. Hand-excluded candidates such as @akutishevsky/lunchmoney-mcp and @ankimcp/anki-mcp-server describe themselves as MCP servers. Scope: Counts as served on 3 November 2026, for a window close to but not certainly identical to the study’s last month. The API returned no count for some candidates.