Grades are a hygiene check, not a security guarantee.

MCP, MCP server

Substack Mcp

A Model Context Protocol (MCP) Server for Substack enabling LLM clients to interact with Substack's API for automations like creating posts, managing drafts, and more.

Compatibility: Not stated.

Grade C

One Medium finding. Read the findings before installing.

Graded by PlugVetRubric v2

Not a guarantee. A hygiene check of the source at the commit shown, not a security audit.

Rescanned at commit c148a3b17699, unchanged since the check below.

SECRETS_PATH_CREDENTIAL

Why this grade

  • SECRETS_PATH_CREDENTIAL Medium

    Mentions a credential store path (~/.ssh, id_rsa, .npmrc, .aws/credentials, credentials.json, .netrc).

    ops/publish-npm.sh, line 14 # Create .npmrc file with auth token

Any High, or two or more Medium findings, give D. One Medium gives C. Low findings or a security Note give B. Each code names a finding behind the letter.

Grades are a hygiene check of the source at the commit shown, on the date checked, not a security audit or a guarantee. The free directory grades are automated. Before release, a PlugVet engineer reviews every paid report and any grade we hold back, including all F grades, against the source code and the evidence we quote.

What was checked

Plain notes from the hygiene checks.

  • Permissions

    Concern

    can delete files.

    PERMISSION_DELETE_FILES

    src/api/substack/image.spec.js, line 63

    await fs.rm(dir, {recursive: true, force: true});

    src/tools/upload_image.spec.js, line 71

    after(async () => fs.rm(dir, {recursive: true, force: true}));

  • Install and update

    Pass

    No install-time script found, and dependencies are pinned or absent.

  • Network

    Concern

    can reach a host that is not written as a fixed address. can reach not-mocked.example.com. The description does not name that host.

    NETWORK_NO_FIXED_HOST NETWORK_UNNAMED_HOST

    src/api/substack/SubstackApi.js, line 135

    response = await fetch(url, {

    src/tools/update_draft.spec.js, line 28

    http.get(SOURCE, () => new HttpResponse(body, {status: 200, headers: {'Content-Type': type}}));

    src/tools/update_draft.spec.js, line 273

    http.get(SOURCE, () => new HttpResponse('nope', {status: 500, headers: {'Content-Type': 'text/plain'}}))

    Show more

    src/tools/upload_image.spec.js, line 31

    return http.get(SOURCE, () => new HttpResponse(body, {status: 200, headers: {'Content-Type': type}}));

    src/tools/upload_image.spec.js, line 188

    http.get(REDIRECTOR, () => new HttpResponse(null, {status: 302, headers: {Location: INTERNAL}}))

    docs/superpowers/plans/2026-08-06-test-suite.md, line 304

    const res = await axios.post(DRAFTS_URL, {draft_title: 'Hello'}, {

    docs/superpowers/plans/2026-08-06-test-suite.md, line 316

    await assert.rejects(() => axios.get('https://not-mocked.example.com/leak'));

    docs/superpowers/plans/2026-08-06-test-suite.md, line 322

    const err = await axios.post(DRAFTS_URL, {}).catch((e) => e);

    docs/superpowers/plans/2026-08-06-test-suite.md, line 1456

    await axios.get('https://substack.com/api/v1/whoami');

  • Secrets

    Concern

    reads a credential or environment file.

    SECRETS_ENV_READ SECRETS_PATH_CREDENTIAL

    ops/publish-npm.sh, line 14

    # Create .npmrc file with auth token

    ops/publish-npm.sh, line 15

    echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc

    src/index.spec.js, line 44

    ...env,

    Show more

    src/tools/add_tag_to_post.js, line 43

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    src/tools/comment_on_post.js, line 36

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    src/tools/create_draft_post.js, line 94

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    src/tools/delete_draft.js, line 31

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    src/tools/export_subscribers.js, line 119

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    src/tools/get_analytics.js, line 215

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    src/tools/get_comment_thread.js, line 37

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    src/tools/get_draft.js, line 36

    auth_token: process.env.SUBSTACK_SESSION_TOKEN,

    .dockerignore, line 8

    .vscode

    .gitignore, line 10

    /npm-debug.log*

    docs/superpowers/plans/2026-08-06-test-suite.md, line 117

    .vscode

    docs/superpowers/specs/2026-08-06-test-suite-design.md, line 250

    .vscode

  • Agent instructions

    Pass

    No instruction to ignore earlier instructions or to hide a step.

  • Obfuscation

    Pass

    No encoded or minified payload found.

  • Licence

    Pass

    A licence file is present (LICENSE).

  • Maintainer activity

    Pass

    The latest commit is within the last 365 days.

Note

Hygiene check of commit c148a3b17699. can delete files. can reach a host that is not written as a fixed address. can reach not-mocked.example.com. The description does not name that host. reads a credential or environment file.

Date and source

Date checked:

View source

github.com

Commit graded: c148a3b176997077d2796530ae98660ef2f96d5a

This link goes to the original host. PlugVet does not host or check what you download from there, and installing it is your own decision and risk.

Read how this letter is chosen