Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions .cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "1password",
"version": "1.1.0",
"description": "1Password plugin for Cursor — securely manage development secrets.",
"displayName": "1Password",
"version": "1.2.0",
"description": "1Password Developer Environments for Cursor: MCP tools to create, import, and manage project secrets; an agent skill with the full import-and-mount workflow; and a hook that validates local .env mounts before shell commands run. Requires 1Password desktop with Labs MCP Server enabled. Local .env mounts: macOS/Linux only.",
"author": {
"name": "1Password"
},
"homepage": "https://1password.com",
"homepage": "https://www.1password.dev/",
"repository": "https://github.com/1Password/cursor-plugin",
"license": "MIT",
"keywords": [
Expand All @@ -16,8 +17,12 @@
"env",
"dotenv",
"hooks",
"validation"
"validation",
"mcp",
"developer-environments"
],
"logo": "assets/logo.svg",
"hooks": "hooks/hooks.json"
"logo": "assets/icon.svg",
"hooks": "./hooks/hooks.json",
"skills": "./skills/",
"mcpServers": "./mcp.json"
}
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.DS_Store

# Environment files (keep templates committable)
.env
.env.*
!.env.example
!.env.sample
!.env.template
!.env.dist

# Python
__pycache__/
*.py[cod]

# Local dev hook wiring (use when team policy blocks local plugin imports)
.cursor/hooks.json
83 changes: 74 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
# 1Password Plugin for Cursor

The official [1Password](https://1password.com) plugin for [Cursor](https://cursor.com). It brings 1Password's secret management capabilities directly into your editor, helping you develop securely without leaving your workflow.
The official [1Password](https://1password.com) plugin for [Cursor](https://cursor.com). It ships three pieces that work together: **hooks** that validate locally mounted `.env` files, an **agent skill** with the complete Developer Environment workflow, and **MCP configuration** for the 1Password desktop app server. Secret values stay in 1Password — the agent sees variable names and mount paths, not secret contents.

Install the **plugin** (not a hand-configured MCP entry alone). The bundled `1password-environments` skill is the authoritative agent workflow; the MCP server's built-in documentation resources cover tool basics only and omit import-and-mount steps.

For more on 1Password's developer tools, see the [1Password Developer Documentation](https://developer.1password.com).

## Requirements

- [1Password](https://1password.com) subscription
- [1Password for Mac or Linux](https://1password.com/downloads)
- [1Password for Mac](https://1password.com/downloads) (this plugin targets **macOS**)
- [Cursor](https://cursor.com)
- [sqlite3](https://www.sqlite.org/) installed and available in your `PATH` (pre-installed on macOS; install via your package manager on Linux)

Additional requirements by feature:

- **Hooks** — [sqlite3](https://www.sqlite.org/) installed and available in your `PATH` (pre-installed on macOS; install via your package manager on Linux)
- **MCP** — 1Password Labs **MCP Server** experiment enabled in the desktop app (`onepassword://settings/labs`). If the setting is missing, your account may not have the `ai-local-mcp-server` feature flag.

> **Note:** 1Password Environments local `.env` mounts only apply on **macOS and Linux**. **`hooks.json`** invokes **`./scripts/validate-mounted-env-files`** with no extension. On **macOS / Linux**, that runs the **Bash** script. On **Windows** the shell looks for a real file by trying suffixes from **`PATHEXT`** until one matches on disk. That yields **`validate-mounted-env-files.cmd`**, which returns **`allow`** and skips validation so agent shells are not blocked.

Expand All @@ -24,14 +30,32 @@ Before using this plugin, you'll need to configure your secrets in 1Password:

### Step 2: Install the plugin

Install from the [Cursor Marketplace](https://cursor.com/marketplace):
Install from the [Cursor Marketplace](https://cursor.com/marketplace). This registers hooks, the `1password-environments` agent skill, and `mcp.json` together. Do not add the 1Password MCP server manually in user settings instead of installing the plugin — agents will get MCP tools without the skill workflow.

1. Open **Cursor Settings** > **Plugins**.
2. Search for **1password**.
3. Click **Install**.

Or use the command palette: `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS) > **Plugins: Install Plugin** > search for `1password`.

Or install directly:

```
/add-plugin 1password
```

### Step 3: Enable MCP in 1Password (required for Environment management)

Enable the **MCP Server** experiment in the 1Password desktop app: open **Settings → Labs** (or use `onepassword://settings/labs`) and turn on **MCP Server**. The plugin's `mcp.json` connects Cursor to that server after this step.

The MCP server binary on macOS:

```text
/Applications/1Password.app/Contents/MacOS/onepassword-mcp
```

On Linux, see the [1Password MCP server documentation](https://www.1password.dev/environments/mcp-server) for the binary path on your platform.

Comment thread
scottisloud marked this conversation as resolved.
## Features

### Hooks
Expand All @@ -40,7 +64,7 @@ Or use the command palette: `Ctrl+Shift+P` (or `Cmd+Shift+P` on macOS) > **Plugi

Validates locally mounted `.env` files from [1Password Environments](https://developer.1password.com/docs/environments) before any shell command executes. When required environment files are missing, disabled, or misconfigured, the hook blocks execution and surfaces actionable error messages so the Cursor Agent can guide you to a fix.

This hook was originally developed in the [1Password Cursor Hooks](https://github.com/1Password/cursor-hooks) repository. For the full setup guide, see [Validate local `.env` files with Cursor Agent](https://developer.1password.com/docs/environments/cursor-hook-validate/).
This hook was originally developed in the [1Password Agent Hooks](https://github.com/1Password/agent-hooks) repository. For the full setup guide, see [Validate local `.env` files with Cursor Agent](https://developer.1password.com/docs/environments/cursor-hook-validate/).

**How it works:**

Expand Down Expand Up @@ -122,26 +146,66 @@ DEBUG=1 echo '{"command": "echo test", "workspace_roots": ["/path/to/your/projec

When not running in debug mode, the hook writes logs to `/tmp/1password-cursor-hooks.log`. Log entries include timestamps and details about 1Password queries, validation results, and permission decisions.

### MCP and agent skill

The plugin connects Cursor to the local 1Password MCP server and bundles the **`1password-environments`** skill (`skills/1password-environments/SKILL.md`). Agents should read that skill before calling MCP tools — it defines the complete workflow for importing a plain `.env` file, appending variables, and mounting at the source path. The MCP server's built-in docs cover tool basics but omit those import-and-mount steps.

See `skills/1password-environments/reference.md` for setup, mount conflicts, and shell validation details.

#### Example prompts

- "List my 1Password Environments"
- "Mount my staging Environment as `.env` in this repo"
- "What variables are in my production Environment?"
- "Create a new Environment called `my-app-dev`"
- "Create an Environment from my project `.env` file"
- "Import `.env` into 1Password and mount it here"
- "Add a placeholder for my OpenAI API key"

#### MCP tools

| Tool | Description |
|------|-------------|
| `authenticate` | Authenticate with the 1Password desktop app; returns `accountId` |
| `list_environments` | List Developer Environments for an account |
| `create_environment` | Create a new Developer Environment |
| `rename_environment` | Rename an existing Developer Environment |
| `list_variables` | List variable names in an Environment (no values) |
| `append_variables` | Add or update Environment variables |
| `create_local_env_file` | Mount an Environment as a local `.env` file |
| `list_local_env_files` | List existing local `.env` mounts for an Environment |

Confirm the MCP server is connected in **Cursor Settings → MCP** after installing the plugin and enabling the Labs experiment in 1Password.

## Plugin Structure

```
1password/
cursor-plugin/
├── .cursor-plugin/
│ └── plugin.json # Plugin manifest
├── hooks/
│ └── hooks.json # Hook event configuration
│ └── hooks.json # beforeShellExecution mount validation
├── skills/
│ └── 1password-environments/
│ ├── SKILL.md # Agent skill for MCP workflows
│ └── reference.md # Setup, mount conflict, and troubleshooting
├── mcp.json # MCP server configuration
├── assets/
│ └── logo.svg # Plugin logo
│ ├── logo.svg # Plugin logo
│ └── icon.svg
├── scripts/
│ ├── lib/
│ │ └── telemetry.sh # Opt-in telemetry helpers for the validation hook
│ ├── validate-mounted-env-files # Bash hook (macOS / Linux)
│ └── validate-mounted-env-files.cmd # Windows cmd wrapper returns allow (validation skipped)
├── LICENSE
└── README.md
```


## Telemetry

The plugin emits **opt-in** telemetry so 1Password can understand plugin adoption and the prevalence of common failure modes (missing files, disabled mounts). Two event types are emitted:
The validation hook emits **opt-in** telemetry so 1Password can understand plugin adoption and the prevalence of common failure modes (missing files, disabled mounts). Two event types are emitted:

- `agent_hook_execution` — fired once per hook invocation; carries the hook name, plugin version, client (`cursor`), bucketed duration, decision (`allow`/`deny`), reason for deny, validation mode (`default`/`configured`), and a count of mounts checked.
- `agent_hook_install` — fired once per `(hook_name, plugin_version)` on the first hook run after installation or upgrade; `install_method` is `plugin_marketplace`.
Expand All @@ -159,6 +223,7 @@ The plugin emits **opt-in** telemetry so 1Password can understand plugin adoptio
## Resources

- [Validate local `.env` files with Cursor Agent](https://developer.1password.com/docs/environments/cursor-hook-validate/) — full setup guide on the 1Password Developer site
- [1Password MCP server documentation](https://www.1password.dev/environments/mcp-server)
- [1Password Agent Hooks](https://github.com/1Password/agent-hooks) — the original hooks repository this plugin is based on
- [1Password Environments](https://developer.1password.com/docs/environments) — documentation for 1Password's environment and secrets management
- [1Password Local `.env` Files](https://developer.1password.com/docs/environments/local-env-file) — how local `.env` file mounting works
Expand Down
23 changes: 23 additions & 0 deletions assets/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/logo-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"1password": {
"command": "/Applications/1Password.app/Contents/MacOS/onepassword-mcp",
"args": []
}
}
}
22 changes: 3 additions & 19 deletions scripts/lib/telemetry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,24 +59,14 @@ get_telemetry_dir() {
echo "${HOME}/.config/1Password/data/hook-events"
}

# Emit the `,"ts":<epoch_ms>` JSON fragment stamping when the event was
# produced, or an empty string if the clock is unreadable.
ts_json_fragment() {
local ts
ts=$(current_time_ms)
if [[ "$ts" =~ ^[0-9]+$ ]]; then
printf ',"ts":%s' "$ts"
fi
}

# Check whether the 1Password app has signaled that telemetry is enabled.
# Returns 0 (true) if the signal file exists, 1 (false) otherwise.
telemetry_consent_enabled() {
[[ -f "${HOME}/.config/1Password/telemetry-enabled" ]]
}

# Append a single JSON line to the events.jsonl file.
# Checks consent and enforces a 1MB file size cap (drop-newest).
# Checks consent and enforces a 1MB file size cap.
write_telemetry_event() {
local json_line="$1"
local event_dir
Expand Down Expand Up @@ -152,11 +142,8 @@ write_execution_event() {
local duration_bucket
duration_bucket=$(bucket_duration_ms "$duration_ms")

local ts_field
ts_field=$(ts_json_fragment)

local json_line
json_line="{\"schema\":\"agent_hook_execution\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"client\":\"${escaped_client}\",\"event_type\":\"${escaped_event_type}\",\"decision\":\"${decision}\",\"deny_reason\":${deny_reason_json},\"duration_bucket\":\"${duration_bucket}\",\"mode\":${mode_json},\"mount_count\":${mount_count_json}${ts_field}}"
json_line="{\"schema\":\"agent_hook_execution\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"client\":\"${escaped_client}\",\"event_type\":\"${escaped_event_type}\",\"decision\":\"${decision}\",\"deny_reason\":${deny_reason_json},\"duration_bucket\":\"${duration_bucket}\",\"mode\":${mode_json},\"mount_count\":${mount_count_json}}"

write_telemetry_event "$json_line"
}
Expand All @@ -173,11 +160,8 @@ write_install_event() {
escaped_hook_name=$(escape_json_string "$hook_name")
escaped_hook_version=$(escape_json_string "$hook_version")

local ts_field
ts_field=$(ts_json_fragment)

local json_line
json_line="{\"schema\":\"agent_hook_install\",\"client\":\"${escaped_client}\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"install_method\":\"${install_method}\"${ts_field}}"
json_line="{\"schema\":\"agent_hook_install\",\"client\":\"${escaped_client}\",\"hook_name\":\"${escaped_hook_name}\",\"hook_version\":\"${escaped_hook_version}\",\"install_method\":\"${install_method}\"}"

write_telemetry_event "$json_line"
}
Expand Down
Loading