MCP server
depcurfew mcp runs the same check as a Model Context Protocol server over stdio, so that an AI agent in Claude Desktop, VS Code or any other client that starts a local stdio server can ask before it installs a package. It offers two read-only tools:
check_packagestakespackages, a list of 1 to 50 specs asdepcurfew checktakes them (zod@4,pypi:requests,composer:monolog/monolog), and an optionaldirectory. It returns the document ofcheck --json(schemaVersion,policy,packageswith their findings,notices,errors) as structured content and as JSON text. A package that breaks the policy is a successful check with error findings; the call itself is an error (isError) only when nothing could be checked, such as adepcurfew.jsonthat cannot be used, a directory that does not exist or a wrong argument, with a message that says what to do. Even then an exact version that the local OSV.dev data lists is inpackagesasknown-malicious; with more than 50 specs the result is then that document, with the warningtoo-many-packagesinnotices, instead of the message (see Known malicious versions).explain_policytakes an optionaldirectoryand returns whichdepcurfew.jsonapplies (nulland the default policy ofdepcurfew initwhen there is none), the release-age delay and its exceptions, the install-script lists, the allowed sources, and the registry and indexcheck_packagesasks, with the scopes.npmrcassigns to other registries as a list ofscopeandregistry,composerRepository, the Packagist URL it asks about Composer packages, andosv, the OSV.dev endpoint it asks with--osv. It reads local files only.
The policies are those the hook applies, with directory as the working directory: the depcurfew.json of the project directory (or the nearest one above it up to the root of the repository), and a different one nearer directory too, the stricter answer counting, so that a depcurfew.json the user did not write, such as one in a package in .venv/…/site-packages, a vendored clone or a file an agent wrote, can tighten the check but not relax it; a notice names it. Without a depcurfew.json in the project directory, the nearest one from directory applies. Paths are shown relative to the project directory, and a directory must exist inside it. The registries come from .npmrc as for check: an .npmrc that npm reads in directory but that does not belong to the policy makes the packages it applies to unverified, and so does a root composer.json there that declares other repositories for Composer packages.
What the agent gets is the same as in the hook: install scripts are named without their content, text from a registry or the repository (keys too) is shown as visible text and cut to 400 characters, with control characters, bidirectional controls, line and paragraph separators and invisible characters (zero-width characters, Unicode tag characters, variation selectors, Hangul fillers and the blank braille pattern) written as escapes, and the publisher's text, such as a deprecation message, in quotes; the tool descriptions say that it is data, not instructions. A fix that would change depcurfew.json says to ask the user instead, because only the user changes the policy, a fix about the registry or index (one that cannot be reached, plain HTTP, a registry setting depcurfew does not take) says to tell the user, because the agent cannot choose another one, and a fix that sets something in .npmrc says to ask the user to set it; without a depcurfew.json, a notice says that the default policy applies and to tell the user if the project should have its own. The registry and the index are options of the server (--registry, --index, and .npmrc as for check), never arguments of a call, so an agent cannot point the check at a registry that vouches for anything. The project directory is --dir, else CLAUDE_PROJECT_DIR, else the directory the client starts the server in. The server ends when the client closes its stdin or sends SIGINT or SIGTERM, and writes nothing but protocol messages to stdout.
Install depcurfew as a development dependency of the project, then add the server. Each configuration starts node itself with the path of the installed script, which works without a shell on Windows too:
The server speaks the initialize handshake of the protocol revisions 2025-11-25, 2025-06-18, 2025-03-26, 2024-11-05 and the stateless revision 2026-07-28, without the handshake, for clients that use it.
Claude Code. Add the server to .mcp.json of the project, which everyone working on it shares (Claude Code asks before it starts a server from that file):
claude mcp add --scope project --transport stdio depcurfew -- node '${CLAUDE_PROJECT_DIR:-.}/node_modules/depcurfew/dist/cli.js' mcp
or write .mcp.json yourself:
{
"mcpServers": {
"depcurfew": {
"command": "node",
"args": [
"${CLAUDE_PROJECT_DIR:-.}/node_modules/depcurfew/dist/cli.js",
"mcp"
]
}
}
}
Claude Code sets CLAUDE_PROJECT_DIR to the project root for the server, and depcurfew mcp takes it as the project directory. Claude Code itself does not have the variable, so in args it needs the default ${CLAUDE_PROJECT_DIR:-.} (in cmd.exe, leave out the single quotes).
Claude Desktop. In claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json, Windows: %APPDATA%\Claude\claude_desktop_config.json, Linux: ~/.config/Claude/claude_desktop_config.json), with absolute paths and --dir, because Claude Desktop starts servers in no particular directory:
{
"mcpServers": {
"depcurfew": {
"command": "node",
"args": [
"/path/to/project/node_modules/depcurfew/dist/cli.js",
"mcp",
"--dir",
"/path/to/project"
]
}
}
}
VS Code. In .vscode/mcp.json of the workspace:
{
"servers": {
"depcurfew": {
"type": "stdio",
"command": "node",
"args": [
"${workspaceFolder}/node_modules/depcurfew/dist/cli.js",
"mcp"
],
"cwd": "${workspaceFolder}"
}
}
}
code --add-mcp '{"name":"depcurfew","type":"stdio","command":"node","args":["/path/to/project/node_modules/depcurfew/dist/cli.js","mcp","--dir","/path/to/project"]}' adds the server for one project to your user profile instead.
Other clients that start a stdio server take the same command and arguments; give them absolute paths and --dir when they do not start the server in the project directory.
With --osv in the server's arguments ("args": [".../cli.js", "mcp", "--osv"]), check_packages also looks the versions up in OSV.dev, as check --osv does, and explain_policy names the endpoint in lookups.osv (null without it). An agent cannot turn it on or off: it is an option of the server, not an argument of a call. Without --osv, check_packages looks the versions up in the local OSV.dev data of depcurfew osv update, read afresh for each call, and its notices say when that data is old.
The server is advice to an agent that asks, not a guard: an agent can install packages without calling it. The package manager configuration from depcurfew apply is what enforces the policy; in Claude Code the hook above also checks every install command.