Use 1Password Developer Environments with Cursor
By Flavio Copes
Learn how the 1Password Cursor plugin mounts secrets without plaintext .env files, what its skill and validation hook do, and how to use it safely.
1Password has an official Cursor plugin for managing project secrets.
You keep the values in a 1Password Developer Environment. Your application still reads a normal .env path, but the plaintext contents are not stored on disk.
If the underlying feature is new to you, start with my deep dive into 1Password Developer Environments.
Cursor can also create Environments, import an existing .env file, list variable names, and mount an Environment inside a project.
Let’s see how the plugin works, how to set it up in Cursor, and where the security boundary really is.
What 1Password Developer Environments are
A 1Password Developer Environment is a collection of environment variables for one project or stage.
For example, you might create separate Environments called:
my-app-developmentmy-app-stagingmy-app-production
Each Environment contains key-value pairs such as DATABASE_URL, RESEND_API_KEY, and STRIPE_SECRET_KEY.
The difference from a regular .env file is where the values live.
A normal .env file stores plaintext secrets on disk. It can be copied, indexed, backed up, or accidentally committed.
1Password stores the values in the Environment and exposes them through a local .env path only when a process reads it.
The local .env file is a named pipe
The path looks like a file, but it is a Unix named pipe, also called a FIFO.
The flow looks like this:
application reads .env
↓
1Password asks for authorization
↓
secret values pass through the named pipe
↓
application receives the variables
The plaintext contents are never written to that path on disk.
This means existing tools can keep using .env. Node.js dotenv, Docker Compose, python-dotenv, and other common libraries can read the mounted path without a special 1Password integration.
You can verify that the path is a pipe:
test -p .env && echo "1Password mount is active"
Do not use test -f. A FIFO is not a regular file, so that check fails even when the mount works.
What the Cursor plugin adds
The official 1Password plugin for Cursor bundles three pieces.
The MCP server
The MCP server gives Cursor tools for managing Developer Environments.
The agent can:
- list Environments
- create and rename an Environment
- list variable names
- add or update variables
- create a local
.envmount - list existing mounts
The listing tools return variable names and mount paths, not secret values.
If you want the background on this protocol, read my guide to how MCP works.
The agent skill
The plugin includes a 1password-environments skill.
This is important because the MCP tools alone do not describe the complete workflow. The skill tells the agent what order to use, how to handle duplicate names, and how to finish an import.
For example, importing a .env file is not complete after Cursor creates the Environment and adds the variables. On macOS and Linux, the skill also mounts the Environment at the original .env path and verifies the mount.
The skill also tells Cursor not to reveal values in chat and not to read a mounted FIFO to inspect it. Cursor should use the variable-listing tool instead.
The validation hook
The plugin registers a beforeShellExecution hook.
Before Cursor runs a shell command, the hook checks every relevant 1Password .env mount in the workspace.
It verifies that:
- the path exists
- the path is a FIFO
- the mount is enabled in 1Password
If a required mount is missing or disabled, the hook blocks the command and gives the agent an error it can help you fix.
This avoids a common failure: the agent starts a server or test suite without its required environment and then wastes time debugging unrelated errors.
Requirements
You need:
- a 1Password subscription
- the 1Password desktop app
- Cursor
- macOS or Linux
sqlite3in yourPATHfor the validation hook- the MCP Server experiment enabled in 1Password Labs
macOS already includes sqlite3. On Linux, install it with your package manager if it is missing.
The complete workflow is not supported on Windows. Local .env mounts and the desktop MCP server are unavailable there. The Windows validation hook allows commands without checking mounts.
The plugin skill recommends using op run --environment on Windows instead of mounting a file.
Install the plugin in Cursor
First, open the 1Password desktop app.
Go to Settings → Labs and enable MCP Server. You can also open onepassword://settings/labs on a computer with 1Password installed.
Then open Cursor and go to Settings → Plugins. Search for 1password and install the plugin.
You can also open the command palette and choose Plugins: Install Plugin.
Or ask Cursor directly:
/add-plugin 1password
The plugin installs the MCP configuration, skill, and hook together. Do not add only the MCP command by hand, because you would miss the import workflow and mount validation.
Finally, open Cursor Settings → MCP and confirm that the 1password server is connected.
Under the hood, the configuration is tiny:
{
"mcpServers": {
"1password": {
"command": "1password-mcp"
}
}
}
The 1password-mcp command comes from the 1Password desktop app. Cursor starts it on the same computer where the editor is running.
Create an Environment
You can create the first Environment in the 1Password app.
Open Developer → View Environments, select New environment, give it a name, and save it.
You can then add variables manually or import an existing .env file.
After the plugin is connected, Cursor can do the same work through the agent:
Create a new 1Password Environment called my-app-development
The skill first lists existing Environments. If the name already exists, it stops and asks whether to use it, choose another name, or cancel.
That pause matters. Silently reusing an Environment could update the wrong secrets.
Import an existing .env file
To move an existing project into 1Password, ask Cursor:
Import .env into a new 1Password Environment called my-app-development and mount it here
The workflow is:
- Read the existing
.envfile. - Authenticate with the local 1Password app.
- Check whether the Environment name already exists.
- Create or select the Environment.
- Add the variables.
- Mount the Environment at the original
.envpath. - Verify the mount.
Importing is a migration step. Cursor must read the existing plaintext values to pass them to 1Password, so do this only with a local agent you trust.
If the .env file is tracked by Git, the skill stops before mounting.
Delete the tracked file and commit that deletion first. Otherwise Git still expects a regular file at the same path and operations such as git status become confusing.
The mounted FIFO cannot be staged with its secret contents, but removing the old tracked file is still required.
Mount an existing Environment
You can also mount an Environment that already exists:
Mount my staging Environment as .env in this repo
When the application first reads .env, 1Password shows an authorization prompt.
Approve it, and the application receives the values once through the pipe. Authorization remains active until 1Password locks.
You can have up to ten enabled local .env mounts per device.
Choose which mounts the hook validates
By default, the hook finds every 1Password mount related to the current workspace.
You can make the list explicit with .1password/environments.toml:
mount_paths = [".env", "billing.env"]
Now the hook checks only those two paths.
An empty list disables validation:
mount_paths = []
I would keep the default unless a repository uses several mounts and only some are required for every command.
The security boundary
This setup removes plaintext secrets at rest. It does not make secrets impossible for local processes to read.
Once you authorize the mounted .env file, every process on your computer can read it until 1Password locks or you disable the mount. 1Password does not distinguish between your application, Cursor, and another local process.
Do not ask an agent to run cat .env. Let the application read the file.
The validation hook is also fail open. If 1Password is missing, its database is unavailable, or sqlite3 cannot run, the hook allows the shell command.
That is a sensible choice for development. A broken helper should not block every terminal command. But it means the hook is a setup guard, not a security sandbox.
There are two more practical limitations:
- named pipes are not designed for several simultaneous readers
- tools that watch
.envmay react to pipe activity as if the file changed
1Password specifically warns that Vite can enter a restart loop. If that happens, exclude the mounted path from the watcher:
import { defineConfig } from 'vite'
export default defineConfig({
server: {
watch: {
ignored: ['**/.env'],
},
},
})
How I would use it
I would use this plugin for local development in Cursor.
For a project that needs a database, an email provider, and a payment provider, I would create one Development Environment and mount it as .env.
I would let the application read that path. I would let Cursor list variable names, detect missing mounts, and add a placeholder when the code introduces a new variable.
I would keep staging and production separate. Production secrets would stay in the hosting platform’s secret store, not in a local .env mount.
The strongest part of this plugin is not that it gives an agent more secrets.
It gives the agent a structured way to work around secrets while keeping the plaintext file off disk.
Want me to talk about your product? You can sponsor this site.