Skip to main content
Your server runs on your machine over stdio, and you want its score before anyone installs it. mcpscore launches the server itself, audits it, and stops it when the audit ends.
That is the command you end up with. The steps below build it one piece at a time, and each piece fixes a failure you would otherwise hit.

Step 1: Audit a Python file

Start with a small server. Save it as server.py:
server.py
Pass the file to mcpscore the same way you pass a URL in the quick start:
That’s it. Your server has a score. A .py file runs with mcpscore’s own Python, and a .js file runs with node. mcpscore’s Python has mcpscore’s own dependencies installed, mcp among them, and none of your project’s. Add one import from your own project and the server cannot start:
server.py
The exit code is 2: mcpscore could not connect. Lines that start with server stderr: are your server’s own output, relayed so you can see why it stopped.

Step 2: Run it in its own environment

--stdio takes the command you already use to start your server. Put it where the file name was:
uv run starts the server inside your project’s environment, so every dependency in your pyproject.toml is there. The score is the same as in Step 1, because the server is the same. --stdio works for any language. It runs the command directly, with no shell in between:
--stdio consumes the rest of the command line, including your server’s own flags. Every mcpscore option goes before it.

Step 3: Pass the server its secrets

Most servers read an API key at startup. Add that to server.py:
server.py
Export the key and run the Step 2 command:
The key is in your shell, and the server still cannot see it. mcpscore starts the server with a minimal environment: HOME, LOGNAME, PATH, SHELL, TERM, and USER on macOS and Linux. Your shell’s secrets stay out of a process you have not vouched for. Hand the key over by name with --env:
--env NAME copies the value from mcpscore’s own environment, so it never appears on the command line, in shell history, or in the report. Repeat the flag for each variable. --env NAME=VALUE sets a value inline, which is fine for non-secret settings like --env LOG_LEVEL=debug.

Step 4: Fail when the score drops

Add --fail-under with the lowest percentage you accept:
The exit code is 3. 105 of 126 rounds to 83%, below the 90% you asked for. Lower the bar to where the server is today:
Now a change that costs points fails the command, in your terminal or in CI, before the server reaches anyone else. Raise the number as you fix findings.
  • A .py target runs with the interpreter mcpscore itself runs on (sys.executable). A .js target runs with node from PATH.
  • --stdio runs the first word as the executable and passes the rest as arguments. There is no shell, so quoting, pipes, and $VARS in the server command are not interpreted. A script name on its own is not an executable: --stdio server.py fails with a hint to use --stdio python server.py.
  • The server gets the MCP Python SDK’s default environment (HOME, LOGNAME, PATH, SHELL, TERM, USER on POSIX) plus each --env. --env values are never logged and never written to the report.
  • The command line, as typed after --stdio, becomes the report’s target field and is visible in the process list. That is why secrets go in with --env NAME and never as arguments.
  • Your server’s stderr is relayed line by line with a server stderr: prefix. Its stdout carries the MCP protocol and is never printed.
  • mcpscore opens more than one connection during an audit, for example to check that the tool catalog is the same on a second connection, so your server starts more than once.

When it fails

More failures and their fixes are in Troubleshooting.

Recap

  • Audit a Python or JavaScript file with mcpscore server.py.
  • Run the server in its own environment with --stdio uv run server.py, or any command in any language.
  • Pass secrets with --env NAME, before --stdio.
  • Fail the run below a score with --fail-under 80.

What’s next