
Cursor Docs
- 28 installs
- Updated August 3, 2026
- mehmetbaykar/cursor-docs-skill
Helps with ai & agent building tasks.
About
cursor-docs is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- cursor-docs
- AI & Agent Building
- AI-coding skill
Cursor Docs by the numbers
- 28 all-time installs (skills.sh)
- Ranked #9,505 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mehmetbaykar/cursor-docs-skill --skill cursor-docsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 28 |
|---|---|
| Last updated | August 3, 2026 |
| Repository | mehmetbaykar/cursor-docs-skill ↗ |
What it does
Helps with ai & agent building tasks.
Files
Cursor Docs
Local mirror of selected Cursor documentation, checked for freshness by scheduled CI. The cleaned Markdown lives in references/; the generated topic list lives in references/INDEX.md; the per-file manifest with upstream URLs lives in references/docs_manifest.json.
Scope
Use this skill for Cursor-specific product and configuration questions about Agent Skills, Rules, Hooks, Model Context Protocol (MCP), Subagents, Plugins, and related Cursor agent-extension features. If the question is about Claude Code, OpenAI Codex, Anthropic API docs, or another non-Cursor coding agent, this skill does not apply.
Workflow
1. If the user supplied a topic, normalize it to a slug:
- lowercase the requested topic
- strip leading
https://cursor.com/docs/,/docs/, and surrounding slashes - join nested path segments with
__(for example,foo/barbecomesfoo__bar)
2. If references/<slug>.md exists, read that file directly. Do not search the whole references/ tree first. 3. If no exact match exists, read references/INDEX.md and choose the closest topic. If multiple topics could match, list the candidates and ask the user to choose. 4. If the user supplied no topic, read references/INDEX.md, present the available mirrored topics, and ask what they want to inspect.
Answer Format
- Lead with a direct answer grounded in the local reference file.
- Quote short commands, config keys, schema snippets, and exact constraints when useful.
- End with
Source: <upstream Cursor docs URL>using the URL from the file frontmatter orreferences/docs_manifest.json.
Freshness
Scheduled CI runs the updater and fails if checked-in references drift from live Cursor docs. If the local content looks stale, contradicted by the user, empty, or incomplete, tell the user to run or inspect the updater workflow rather than guessing from memory.
Examples
| Invocation | Reads |
|---|---|
/cursor-docs hooks | references/hooks.md |
/cursor-docs mcp | references/mcp.md |
/cursor-docs skills | references/skills.md |
/cursor-docs | references/INDEX.md |
interface:
display_name: "Cursor Docs"
short_description: "Answers Cursor docs questions from a local generated mirror."
policy:
allow_implicit_invocation: true
{
"description": "Cursor documentation mirror manifest. Files live beside this manifest in references/.",
"fetch_metadata": {
"failed_pages": [],
"fetch_tool_version": "1.0",
"fixture_mode": false,
"pages_failed": 0,
"pages_fetched_successfully": 6,
"pages_skipped": 0,
"raw_fallback_pages": 0,
"total_pages_discovered": 6
},
"files": {
"hooks.md": {
"hash": "905eb68a6939dba4d63c18334b35b815a84054110f75c98700b43749b60b7003",
"last_updated": "2026-06-20T19:39:55+00:00",
"original_url": "https://cursor.com/docs/hooks",
"path": "/docs/hooks",
"source_url": "https://cursor.com/docs/hooks.md",
"title": "Hooks"
},
"mcp.md": {
"hash": "71e38d99749af8e1de704c10e8c04c4c86fc6b5ee943c226b47f304862bdb038",
"last_updated": "2026-06-23T03:44:59+00:00",
"original_url": "https://cursor.com/docs/mcp",
"path": "/docs/mcp",
"source_url": "https://cursor.com/docs/mcp.md",
"title": "Model Context Protocol (MCP)"
},
"plugins.md": {
"hash": "c2c004874c313779c41b293b6fafec5c35fd4fbb5eb74c68492f2f2c39c75168",
"last_updated": "2026-06-04T22:22:21+00:00",
"original_url": "https://cursor.com/docs/plugins",
"path": "/docs/plugins",
"source_url": "https://cursor.com/docs/plugins.md",
"title": "Plugins"
},
"rules.md": {
"hash": "2225bb8aa81707ab46c8aba45119628fd3752a585d8f188091c6c531f13d1e4a",
"last_updated": "2026-05-29T20:28:45+00:00",
"original_url": "https://cursor.com/docs/rules",
"path": "/docs/rules",
"source_url": "https://cursor.com/docs/rules.md",
"title": "Rules"
},
"skills.md": {
"hash": "92b30d75141294c37e64de081a242b751d49de20f2a7284b3adce04297225329",
"last_updated": "2026-05-18T19:24:13+00:00",
"original_url": "https://cursor.com/docs/skills",
"path": "/docs/skills",
"source_url": "https://cursor.com/docs/skills.md",
"title": "Agent Skills"
},
"subagents.md": {
"hash": "6b87ec3becb18cd7a44f967715d66401078a118169859e1cd6022f271264eaca",
"last_updated": "2026-06-18T04:11:36+00:00",
"original_url": "https://cursor.com/docs/subagents",
"path": "/docs/subagents",
"source_url": "https://cursor.com/docs/subagents.md",
"title": "Subagents"
}
},
"include": [
"https://cursor.com/docs/skills",
"https://cursor.com/docs/rules",
"https://cursor.com/docs/hooks",
"https://cursor.com/docs/mcp",
"https://cursor.com/docs/subagents",
"https://cursor.com/docs/plugins"
],
"last_updated": "2026-06-23T03:44:59+00:00",
"skipped": [],
"source": "https://cursor.com/docs/"
}
Hooks
Hooks let you observe, control, and extend the agent loop using custom scripts. Hooks are spawned processes that communicate over stdio using JSON in both directions. They run before or after defined stages of the agent loop and can observe, block, or modify behavior.
With hooks, you can:
- Run formatters after edits
- Add analytics for events
- Scan for PII or secrets
- Gate risky operations (e.g., SQL writes)
- Control subagent (Task tool) execution
- Inject context at session start
Looking for ready-to-use integrations? See Partner Integrations for security, governance, and secrets management solutions from our ecosystem partners.
Cursor supports loading hooks from third-party tools like Claude Code. See Third Party Hooks for details on compatibility and configuration.
Hook categories
Hooks fall into three categories based on what triggers them:
Agent hooks (Cmd+K/Agent Chat) fire during an agent session:
sessionStart/sessionEnd- Session lifecycle managementpreToolUse/postToolUse/postToolUseFailure- Generic tool use hooks (fires for all tools)subagentStart/subagentStop- Subagent (Task tool) lifecyclebeforeShellExecution/afterShellExecution- Control shell commandsbeforeMCPExecution/afterMCPExecution- Control MCP tool usagebeforeReadFile/afterFileEdit- Control file access and editsbeforeSubmitPrompt- Validate prompts before submissionpreCompact- Observe context window compactionstop- Handle agent completionafterAgentResponse/afterAgentThought- Track agent responses
Tab hooks (inline completions) fire for autonomous Tab operations:
beforeTabFileRead- Control file access for Tab completionsafterTabFileEdit- Post-process Tab edits
App lifecycle hooks fire outside any agent session:
workspaceOpen- Fires when Cursor opens a workspace and on every workspace folder change. Can return additional plugin paths to load for the current workspace.
These separate hook surfaces let you apply different policies to autonomous Tab operations, user-directed Agent operations, and workspace startup.
Cloud agent support
Cloud agents run command-based hooks from your repository. If you have hooks defined in .cursor/hooks.json at the root of your project, cloud agents pick them up and run them during their work.
On Enterprise plans, cloud agents also run team hooks and enterprise-managed hooks configured through the web dashboard.
Supported hooks
The following hooks run in cloud agents:
| Hook | Supported |
|---|---|
beforeShellExecution | Yes |
afterShellExecution | Yes |
beforeReadFile | Yes |
afterFileEdit | Yes |
preToolUse | Yes |
postToolUse | Yes |
postToolUseFailure | Yes |
subagentStart | Yes |
subagentStop | Yes |
preCompact | Yes |
Hooks not available in cloud agents
Some hooks don't apply to cloud agents due to differences in the execution environment:
| Hook | Reason |
|---|---|
sessionStart / sessionEnd | Cloud agent VMs are provisioned after a task is submitted. There is no VM running when the session starts on cursor.com/agents, so these hooks have no equivalent trigger point. |
beforeSubmitPrompt | The prompt is submitted from the web before the VM exists, so this hook can't run in the cloud environment. |
beforeTabFileRead / afterTabFileEdit | Tab completions are an IDE feature and don't run in cloud agents. |
workspaceOpen | This is an IDE lifecycle hook and doesn't apply to cloud agents. |
beforeMCPExecution / afterMCPExecution | Not yet wired for cloud agents. |
afterAgentResponse / afterAgentThought | Not yet wired for cloud agents. |
stop | Not yet wired for cloud agents. |
Configuration sources
Cloud agents load hooks from these sources:
- Project hooks (
.cursor/hooks.jsonin your repo): Loaded and run during cloud agent work. - Team hooks (Enterprise): Distributed from the dashboard and run in cloud agents.
- Enterprise hooks (Enterprise): System-wide managed hooks run in cloud agents.
User-level hooks (~/.cursor/hooks.json) are not available in cloud agents. Cloud agent VMs don't have access to your local home directory configuration.
Execution type limits
Cloud agents run command-based hooks only. Prompt-based hooks require authentication wiring between the hook and the agent loop, which isn't available in the cloud execution environment.
Quickstart
Create a hooks.json file. You can create it at the project level (<project>/.cursor/hooks.json) or in your home directory (~/.cursor/hooks.json). Project-level hooks apply only to that specific project, while home directory hooks apply globally.
User hooks (\~/.cursor/)
For user-level hooks that apply globally, create ~/.cursor/hooks.json:
{
"version": 1,
"hooks": {
"afterFileEdit": [{ "command": "./hooks/format.sh" }]
}
}
Create your hook script at ~/.cursor/hooks/format.sh:
#!/bin/bash
# Read input, do something, exit 0
cat > /dev/null
exit 0
Make it executable:
chmod +x ~/.cursor/hooks/format.sh
Project hooks (.cursor/)
For project-level hooks that apply to a specific repository, create <project>/.cursor/hooks.json:
{
"version": 1,
"hooks": {
"afterFileEdit": [{ "command": ".cursor/hooks/format.sh" }]
}
}
Note: Project hooks run from the project root, so use .cursor/hooks/format.sh (not ./hooks/format.sh).
Create your hook script at <project>/.cursor/hooks/format.sh:
#!/bin/bash
# Read input, do something, exit 0
cat > /dev/null
exit 0
Make it executable:
chmod +x .cursor/hooks/format.sh
Cursor watches hooks config files and reloads them automatically. Your hook runs after every file edit.
Hook Types
Hooks support two execution types: command-based (default) and prompt-based (LLM-evaluated).
Command-Based Hooks
Command hooks execute shell scripts that receive JSON input via stdin and return JSON output via stdout.
{
"hooks": {
"beforeShellExecution": [
{
"command": "./scripts/approve-network.sh",
"timeout": 30,
"matcher": "curl|wget|nc"
}
]
}
}
Exit code behavior:
- Exit code
0- Hook succeeded, use the JSON output - Exit code
2- Block the action (equivalent to returningpermission: "deny") - Other exit codes - Hook failed, action proceeds (fail-open by default)
Prompt-Based Hooks
Prompt hooks use an LLM to evaluate a natural language condition. They're useful for policy enforcement without writing custom scripts.
{
"hooks": {
"beforeShellExecution": [
{
"type": "prompt",
"prompt": "Does this command look safe to execute? Only allow read-only operations.",
"timeout": 10
}
]
}
}
Features:
- Returns structured
{ ok: boolean, reason?: string }response - Uses a fast model for quick evaluation
$ARGUMENTSplaceholder is auto-replaced with hook input JSON- If
$ARGUMENTSis absent, hook input is auto-appended - Optional
modelfield to override the default LLM model
Examples
The examples below use ./hooks/... paths, which work for user hooks (~/.cursor/hooks.json) where scripts run from ~/.cursor/. For project hooks (<project>/.cursor/hooks.json), use .cursor/hooks/... paths instead since scripts run from the project root.
```json title="hooks.json" { "version": 1, "hooks": { "sessionStart": [ { "command": "./hooks/session-init.sh" } ], "sessionEnd": [ { "command": "./hooks/audit.sh" } ], "beforeShellExecution": [ { "command": "./hooks/audit.sh" }, { "command": "./hooks/block-git.sh" } ], "beforeMCPExecution": [ { "command": "./hooks/audit.sh" } ], "afterShellExecution": [ { "command": "./hooks/audit.sh" } ], "afterMCPExecution": [ { "command": "./hooks/audit.sh" } ], "afterFileEdit": [ { "command": "./hooks/audit.sh" } ], "beforeSubmitPrompt": [ { "command": "./hooks/audit.sh" } ], "preCompact": [ { "command": "./hooks/audit.sh" } ], "stop": [ { "command": "./hooks/audit.sh" } ], "beforeTabFileRead": [ { "command": "./hooks/redact-secrets-tab.sh" } ], "afterTabFileEdit": [ { "command": "./hooks/format-tab.sh" } ] } }
#!/bin/bash
audit.sh - Hook script that writes all JSON input to /tmp/agent-audit.log
This script is designed to be called by Cursor's hooks system for auditing purposes
Read JSON input from stdin
json_input=$(cat)
Create timestamp for the log entry
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
Create the log directory if it doesn't exist
mkdir -p "$(dirname /tmp/agent-audit.log)"
Write the timestamped JSON entry to the audit log
echo "[$timestamp] $json_input" >> /tmp/agent-audit.log
Exit successfully
exit 0
#!/bin/bash
Hook to block git commands and redirect to gh tool usage
This hook implements the beforeShellExecution hook from the Cursor Hooks Spec
Initialize debug logging
echo "Hook execution started" >> /tmp/hooks.log
Read JSON input from stdin
input=$(cat) echo "Received input: $input" >> /tmp/hooks.log
Parse the command from the JSON input
command=$(echo "$input" | jq -r '.command // empty') echo "Parsed command: '$command'" >> /tmp/hooks.log
Check if the command contains 'git' or 'gh'
if [[ "$command" =~ git[[:space:]] ]] || [[ "$command" == "git" ]]; then echo "Git command detected - blocking: '$command'" >> /tmp/hooks.log
Block the git command and provide guidance to use gh tool instead
cat << EOF { "continue": true, "permission": "deny", "user_message": "Git command blocked. Please use the GitHub CLI (gh) tool instead.", "agent_message": "The git command '$command' has been blocked by a hook. Instead of using raw git commands, please use the 'gh' tool which provides better integration with GitHub and follows best practices. For example:\n- Instead of 'git clone', use 'gh repo clone'\n- Instead of 'git push', use 'gh repo sync' or the appropriate gh command\n- For other git operations, check if there's an equivalent gh command or use the GitHub web interface\n\nThis helps maintain consistency and leverages GitHub's enhanced tooling." } EOF elif [[ "$command" =~ gh[[:space:]] ]] || [[ "$command" == "gh" ]]; then echo "GitHub CLI command detected - asking for permission: '$command'" >> /tmp/hooks.log
Ask for permission for gh commands
cat << EOF { "continue": true, "permission": "ask", "user_message": "GitHub CLI command requires permission: $command", "agent_message": "The command '$command' uses the GitHub CLI (gh) which can interact with your GitHub repositories and account. Please review and approve this command if you want to proceed." } EOF else echo "Non-git/non-gh command detected - allowing: '$command'" >> /tmp/hooks.log
Allow non-git/non-gh commands
cat << EOF { "continue": true, "permission": "allow" } EOF fi
### TypeScript stop automation hook
Choose TypeScript when you need typed JSON, durable file I/O, and HTTP calls in the same hook. This Bun-powered `stop` hook tracks per-conversation failure counts on disk, forwards structured telemetry to an internal API, and can automatically schedule a retry when the agent fails twice in a row.
{ "version": 1, "hooks": { "stop": [ { "command": "bun run .cursor/hooks/track-stop.ts --stop" } ] } }
import { mkdir, readFile, writeFile } from 'node:fs/promises'; import { stdin } from 'bun';
type StopHookInput = { conversation_id: string; generation_id: string; model: string; model_id?: string; model_params?: Array<{ id: string; value: string }>; status: 'completed' | 'aborted' | 'error'; loop_count: number; };
type StopHookOutput = { followup_message?: string; };
type MetricsEntry = { lastStatus: StopHookInput['status']; errorCount: number; lastUpdatedIso: string; };
type MetricsStore = Record<string, MetricsEntry>;
const STATE_DIR = '.cursor/hooks/state'; const METRICS_PATH = ${STATE_DIR}/agent-metrics.json; const TELEMETRY_URL = Bun.env.AGENT_TELEMETRY_URL;
async function parseHookInput<T>(): Promise<T> { const text = await stdin.text(); return JSON.parse(text) as T; }
async function readMetrics(): Promise<MetricsStore> { try { return JSON.parse(await readFile(METRICS_PATH, 'utf8')) as MetricsStore; } catch { return {}; } }
async function writeMetrics(store: MetricsStore) { await mkdir(STATE_DIR, { recursive: true }); await writeFile(METRICS_PATH, JSON.stringify(store, null, 2), 'utf8'); }
async function sendTelemetry(payload: StopHookInput, entry: MetricsEntry) { if (!TELEMETRY_URL) return; await fetch(TELEMETRY_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ conversationId: payload.conversation_id, generationId: payload.generation_id, model: payload.model, modelId: payload.model_id, modelParams: payload.model_params, status: payload.status, errorCount: entry.errorCount, loopCount: payload.loop_count, timestamp: entry.lastUpdatedIso }) }); }
async function main() { const payload = await parseHookInput<StopHookInput>(); const metrics = await readMetrics(); const entry = metrics[payload.conversation_id] ?? { lastStatus: payload.status, errorCount: 0, lastUpdatedIso: '' };
entry.lastStatus = payload.status; entry.lastUpdatedIso = new Date().toISOString(); entry.errorCount = payload.status === 'error' ? entry.errorCount + 1 : 0;
metrics[payload.conversation_id] = entry; await writeMetrics(metrics); await sendTelemetry(payload, entry);
const response: StopHookOutput = {}; if (entry.errorCount >= 2 && payload.loop_count < 4) { response.followup_message = 'Automated retry triggered after two failures. Double-check credentials before running again.'; }
process.stdout.write(JSON.stringify(response) + '\n'); }
main().catch(error => { console.error('[stop hook] failed', error); process.stdout.write('{}\n'); });
Set `AGENT_TELEMETRY_URL` to the internal endpoint that should receive run summaries.
### Python manifest guard hook
Python shines when you need rich parsing libraries. This hook uses `pyyaml` to inspect Kubernetes manifests before `kubectl apply` runs; Bash would struggle to parse multi-document YAML safely.
{ "version": 1, "hooks": { "beforeShellExecution": [ { "command": "python3 .cursor/hooks/kube_guard.py" } ] } }
#!/usr/bin/env python3 import json import shlex import sys from pathlib import Path
import yaml
SENSITIVE_NAMESPACES = {"prod", "production"}
def main() -> None: payload = json.load(sys.stdin) command = payload.get("command", "") cwd = Path(payload.get("cwd") or ".") response = {"continue": True, "permission": "allow"}
try: args = shlex.split(command) except ValueError: print(json.dumps(response)) return
if len(args) < 2 or args[0] != "kubectl" or args[1] != "apply" or "-f" not in args: print(json.dumps(response)) return
f_index = args.index("-f") if f_index + 1 >= len(args): print(json.dumps(response)) return
manifest_arg = args[f_index + 1] manifest_path = (cwd / manifest_arg).resolve()
if not manifest_path.exists(): print(json.dumps(response)) return
cli_namespace = None for i, arg in enumerate(args): if arg in ("-n", "--namespace") and i + 1 < len(args): cli_namespace = args[i + 1] elif arg.startswith("--namespace="): cli_namespace = arg.split("=", 1)[1] elif arg.startswith("-n="): cli_namespace = arg.split("=", 1)[1]
try: documents = list(yaml.safe_load_all(manifest_path.read_text())) except (OSError, yaml.YAMLError) as exc: sys.stderr.write(f"Failed to read/parse {manifest_path}: {exc}\n") print(json.dumps(response)) return
if cli_namespace in SENSITIVE_NAMESPACES or any( (doc or {}).get("metadata", {}).get("namespace") in SENSITIVE_NAMESPACES for doc in documents ): response.update( { "permission": "ask", "user_message": "kubectl apply to prod requires manual approval.", "agent_message": f"{manifest_path.name} includes protected namespaces; confirm with your team before continuing.", } )
print(json.dumps(response))
if __name__ == "__main__": main()
Install PyYAML (for example, `pip install pyyaml`) wherever your hook scripts run so the parser import succeeds.
## Partner Integrations
We partner with ecosystem vendors who have built hooks support with Cursor. These integrations cover security scanning, governance, secrets management, and more.
### MCP governance and visibility
| Partner | Description |
| --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| [MintMCP](https://www.mintmcp.com/blog/mcp-governance-cursor-hooks) | Build a complete inventory of MCP servers, monitor tool usage patterns, and scan responses for sensitive data before it reaches the AI model. |
| [Oasis Security](https://www.oasis.security/blog/cursor-oasis-governing-agentic-access) | Enforce least-privilege policies on AI agent actions and maintain full audit trails across enterprise systems. |
| [Runlayer](https://www.runlayer.com/blog/cursor-hooks) | Wrap MCP tools and integrate with their MCP broker for centralized control and visibility over agent-to-tool interactions. |
### Code security and best practices
| Partner | Description |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| [Corridor](https://corridor.dev/blog/corridor-cursor-hooks/) | Get real-time feedback on code implementation and security design decisions as code is being written. |
| [Semgrep](https://semgrep.dev/blog/2025/cursor-hooks-mcp-server) | Automatically scan AI-generated code for vulnerabilities with real-time feedback to regenerate code until security issues are resolved. |
### Dependency security
| Partner | Description |
| ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| [Endor Labs](https://www.endorlabs.com/learn/bringing-malware-detection-into-ai-coding-workflows-with-cursor-hooks) | Intercept package installations and scan for malicious dependencies, preventing supply chain attacks before they enter your codebase. |
### Agent security and safety
| Partner | Description |
| ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| [Snyk](https://snyk.io/blog/evo-agent-guard-cursor-integration/) | Review agent actions in real-time with Evo Agent Guard, detecting and preventing issues like prompt injection and dangerous tool calls. |
### Secrets management
| Partner | Description |
| ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [1Password](https://marketplace.1password.com/integration/cursor-hooks) | Validate that environment files from 1Password Environments are properly mounted before shell commands execute, enabling just-in-time secrets access without writing credentials to disk. |
For more details about our hooks partners, see the [Hooks for security and platform teams](/blog/hooks-partners) blog post.
## Configuration
Define hooks in a `hooks.json` file. Configuration can exist at multiple levels. All matching hooks from every source run; when responses conflict, higher-priority sources take precedence during merge:
~/.cursor/ ├── hooks.json └── hooks/ ├── audit.sh └── block-git.sh
- **Enterprise** (MDM-managed, system-wide):
- macOS: `/Library/Application Support/Cursor/hooks.json`
- Linux/WSL: `/etc/cursor/hooks.json`
- Windows: `C:\\ProgramData\\Cursor\\hooks.json`
- **Team** (Cloud-distributed, enterprise only):
- Configured in the [web dashboard](https://cursor.com/dashboard/team-content?section=hooks) and synced to all team members automatically
- **Project** (Project-specific):
- `<project-root>/.cursor/hooks.json`
- Project hooks run in any trusted workspace and are checked into version control with your project
- **User** (User-specific):
- `~/.cursor/hooks.json`
Priority order (highest to lowest): Enterprise → Team → Project → User
The `hooks` object maps hook names to arrays of hook definitions. Each definition currently supports a `command` property that can be a shell string, an absolute path, or a relative path. The working directory depends on the hook source:
- **Project hooks** (`.cursor/hooks.json` in a repository): Run from the **project root**
- **User hooks** (`~/.cursor/hooks.json`): Run from `~/.cursor/`
- **Enterprise hooks** (system-wide config): Run from the enterprise config directory
- **Team hooks** (cloud-distributed): Run from the managed hooks directory
For project hooks, use paths like `.cursor/hooks/script.sh` (relative to project root), not `./hooks/script.sh` (which would look for `<project>/hooks/script.sh`).
### Configuration file
This example shows a user-level hooks file (`~/.cursor/hooks.json`). For project-level hooks, change paths like `./hooks/script.sh` to `.cursor/hooks/script.sh`:
{ "version": 1, "hooks": { "sessionStart": [{ "command": "./session-init.sh" }], "sessionEnd": [{ "command": "./audit.sh" }], "preToolUse": [ { "command": "./hooks/validate-tool.sh", "matcher": "Shell|Read|Write" } ], "postToolUse": [{ "command": "./hooks/audit-tool.sh" }], "subagentStart": [{ "command": "./hooks/validate-subagent.sh" }], "subagentStop": [{ "command": "./hooks/audit-subagent.sh" }], "beforeShellExecution": [{ "command": "./script.sh" }], "afterShellExecution": [{ "command": "./script.sh" }], "afterMCPExecution": [{ "command": "./script.sh" }], "afterFileEdit": [{ "command": "./format.sh" }], "preCompact": [{ "command": "./audit.sh" }], "stop": [{ "command": "./audit.sh", "loop_limit": 10 }], "beforeTabFileRead": [{ "command": "./redact-secrets-tab.sh" }], "afterTabFileEdit": [{ "command": "./format-tab.sh" }], "workspaceOpen": [{ "command": "./register-workspace-plugins.sh" }] } }
The Agent hooks (`sessionStart`, `sessionEnd`, `preToolUse`, `postToolUse`, `postToolUseFailure`, `subagentStart`, `subagentStop`, `beforeShellExecution`, `afterShellExecution`, `beforeMCPExecution`, `afterMCPExecution`, `beforeReadFile`, `afterFileEdit`, `beforeSubmitPrompt`, `preCompact`, `stop`, `afterAgentResponse`, `afterAgentThought`) apply to Cmd+K and Agent Chat operations. The Tab hooks (`beforeTabFileRead`, `afterTabFileEdit`) apply specifically to inline Tab completions. The app lifecycle hook (`workspaceOpen`) fires when a workspace opens and on workspace folder changes, independent of any agent session.
### Global Configuration Options
| Option | Type | Default | Description |
| --------- | ------ | ------- | --------------------- |
| `version` | number | `1` | Config schema version |
### Per-Script Configuration Options
| Option | Type | Default | Description |
| ------------ | ------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `command` | string | required | Script path or command |
| `type` | `"command"` \| `"prompt"` | `"command"` | Hook execution type |
| `timeout` | number | platform default | Execution timeout in seconds |
| `loop_limit` | number \| null | `5` | Per-script loop limit for stop/subagentStop hooks. `null` means no limit. Default is `5` for Cursor hooks, `null` for Claude Code hooks. |
| `failClosed` | boolean | `false` | When `true`, hook failures (crash, timeout, invalid JSON) block the action instead of allowing it through. Useful for security-critical hooks. |
| `matcher` | object | - | Filter criteria for when hook runs |
### Matcher Configuration
Matchers let you filter when a hook runs. Which field the matcher applies to depends on the hook:
{ "hooks": { "preToolUse": [ { "command": "./validate-shell.sh", "matcher": "Shell" } ], "subagentStart": [ { "command": "./validate-explore.sh", "matcher": "explore|shell" } ], "beforeShellExecution": [ { "command": "./approve-network.sh", "matcher": "curl|wget|nc " } ] } }
- **subagentStart**: The matcher runs against the **subagent type** (e.g. `explore`, `shell`, `generalPurpose`). Use it to run hooks only when a specific kind of subagent is started. The example above runs `validate-explore.sh` only for explore or shell subagents.
- **beforeShellExecution**: The matcher runs against the **shell command** string. Use it to run hooks only when the command matches a pattern (e.g. network calls, file deletions). The example above runs `approve-network.sh` only when the command contains `curl`, `wget`, or `nc `.
**Available matchers by hook:**
- **preToolUse / postToolUse / postToolUseFailure**: Filter by tool type. Values include `Shell`, `Read`, `Write`, `Grep`, `Delete`, `Task`, and MCP tools using the `MCP:<tool_name>` format.
- **subagentStart / subagentStop**: Filter by subagent type (`generalPurpose`, `explore`, `shell`, etc.).
- **beforeShellExecution / afterShellExecution**: Filter by the shell command text; the matcher is matched against the full command string.
- **beforeReadFile**: Filter by tool type (`TabRead`, `Read`, etc.).
- **afterFileEdit**: Filter by tool type (`TabWrite`, `Write`, etc.).
- **beforeSubmitPrompt**: Matched against the value `UserPromptSubmit`.
- **stop**: Matched against the value `Stop`.
- **afterAgentResponse**: Matched against the value `AgentResponse`.
- **afterAgentThought**: Matched against the value `AgentThought`.
## Team Distribution
Hooks can be distributed to team members using project hooks (via version control), MDM tools, or Cursor's cloud distribution system.
### Project Hooks (Version Control)
Project hooks are the simplest way to share hooks with your team. Place a `hooks.json` file at `<project-root>/.cursor/hooks.json` and commit it to your repository. When team members open the project in a trusted workspace, Cursor automatically loads and runs the project hooks.
Cloud agents also load these project hooks when they work on your repository in
the cloud.
Project hooks:
- Are stored in version control alongside your code
- Automatically load for all team members in trusted workspaces
- Can be project-specific (e.g., enforce formatting standards for a particular codebase)
- Require the workspace to be trusted to run (for security)
### MDM Distribution
Distribute hooks across your organization using Mobile Device Management (MDM) tools. Place the `hooks.json` file and hook scripts in the target directories on each machine.
**User home directory** (per-user distribution):
- `~/.cursor/hooks.json`
- `~/.cursor/hooks/` (for hook scripts)
**Global directories** (system-wide distribution):
- macOS: `/Library/Application Support/Cursor/hooks.json`
- Linux/WSL: `/etc/cursor/hooks.json`
- Windows: `C:\\ProgramData\\Cursor\\hooks.json`
Note: MDM-based distribution is fully managed by your organization. Cursor does not deploy or manage files through your MDM solution. Ensure your internal IT or security team handles configuration, deployment, and updates in accordance with your organization's policies.
### Cloud Distribution (Enterprise Only)
Enterprise teams can use Cursor's native cloud distribution to automatically sync hooks to all team members. Configure hooks in the [web dashboard](https://cursor.com/dashboard/team-content?section=hooks). Cursor automatically delivers configured hooks to all client machines when team members log in.
Cloud distribution provides:
- Automatic synchronization to all team members (every thirty minutes)
- Operating system targeting for platform-specific hooks
- Centralized management through the dashboard
Enterprise administrators can create, edit, and manage team hooks from the dashboard without requiring access to individual machines.
[Contact sales](https://cursor.com/contact-sales?source=docs-hooks-cloud) to get Enterprise cloud hook distribution.
## Reference
### Common schema
#### Input (all hooks)
All hooks receive a base set of fields in addition to their hook-specific fields:
{ "conversation_id": "string", "generation_id": "string", "model": "string", "model_id": "string", "model_params": [{ "id": "string", "value": "string" }], "hook_event_name": "string", "cursor_version": "string", "workspace_roots": ["<path>"], "user_email": "string | null", "transcript_path": "string | null" }
| Field | Type | Description |
| ----------------- | ----------------- | --------------------------------------------------------------------------------------------------------- |
| `conversation_id` | string | Stable ID of the conversation across many turns |
| `generation_id` | string | The current generation that changes with every user message |
| `model` | string | Legacy model slug configured for the composer that triggered the hook |
| `model_id` | string (optional) | Structured ID for the selected model, when available |
| `model_params` | array (optional) | Selected model parameters, such as thinking, context, or effort. Each item has an `id` and `value`. |
| `hook_event_name` | string | Which hook is being run |
| `cursor_version` | string | Cursor application version (e.g. "1.7.2") |
| `workspace_roots` | string\[] | The list of root folders in the workspace (normally just one, but multiroot workspaces can have multiple) |
| `user_email` | string \| null | Email address of the authenticated user, if available |
| `transcript_path` | string \| null | Path to the main conversation transcript file (null if transcripts disabled) |
App lifecycle hooks (`workspaceOpen`) fire outside any agent session, so the request omits `conversation_id`, `generation_id`, `model`, `session_id`, and `transcript_path`. They still receive `hook_event_name`, `cursor_version`, `workspace_roots`, and `user_email`.
### Hook events
#### preToolUse
Called before any tool execution. This is a generic hook that fires for all tool types (Shell, Read, Write, MCP, Task, etc.). Use matchers to filter by specific tools.
// Input { "tool_name": "Shell", "tool_input": { "command": "npm install", "working_directory": "/project" }, "tool_use_id": "abc123", "cwd": "/project", "model": "claude-opus-4-7-thinking-max", "model_id": "claude-opus-4-7", "model_params": [ { "id": "thinking", "value": "true" }, { "id": "context", "value": "1m" }, { "id": "effort", "value": "max" } ], "agent_message": "Installing dependencies..." }
// Output { "permission": "allow" | "deny", "user_message": "<message shown in client when denied>", "agent_message": "<message sent to agent when denied>", "updated_input": { "command": "npm ci" } }
| Output Field | Type | Description |
| --------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `permission` | string | `"allow"` to proceed, `"deny"` to block. `"ask"` is accepted by the schema but not enforced for `preToolUse` today. |
| `user_message` | string (optional) | Message shown to the user when the action is denied |
| `agent_message` | string (optional) | Message fed back to the agent when the action is denied |
| `updated_input` | object (optional) | Modified tool input to use instead |
#### postToolUse
Called after successful tool execution. Useful for auditing, analytics, and injecting context.
// Input { "tool_name": "Shell", "tool_input": { "command": "npm test" }, "tool_output": "{\"exitCode\":0,\"stdout\":\"All tests passed\"}", "tool_use_id": "abc123", "cwd": "/project", "duration": 5432, "model": "claude-opus-4-7-thinking-max", "model_id": "claude-opus-4-7", "model_params": [ { "id": "thinking", "value": "true" }, { "id": "context", "value": "1m" }, { "id": "effort", "value": "max" } ] }
// Output { "updated_mcp_tool_output": { "modified": "output" }, "additional_context": "Test coverage report attached." }
| Input Field | Type | Description |
| ------------- | ------ | --------------------------------------------------------------------- |
| `duration` | number | Execution time in milliseconds |
| `tool_output` | string | JSON-stringified result payload from the tool (not raw terminal text) |
| Output Field | Type | Description |
| ------------------------- | ----------------- | ------------------------------------------------------------------ |
| `updated_mcp_tool_output` | object (optional) | For MCP tools only: replaces the tool output seen by the model |
| `additional_context` | string (optional) | Extra context injected into the conversation after the tool result |
#### postToolUseFailure
Called when a tool fails, times out, or is denied. Useful for error tracking and recovery logic.
// Input { "tool_name": "Shell", "tool_input": { "command": "npm test" }, "tool_use_id": "abc123", "cwd": "/project", "error_message": "Command timed out after 30s", "failure_type": "timeout" | "error" | "permission_denied", "duration": 5000, "is_interrupt": false }
// Output { // No output fields currently supported }
| Input Field | Type | Description |
| --------------- | ------- | ----------------------------------------------------------------- |
| `error_message` | string | Description of the failure |
| `failure_type` | string | Type of failure: `"error"`, `"timeout"`, or `"permission_denied"` |
| `duration` | number | Time in milliseconds until the failure occurred |
| `is_interrupt` | boolean | Whether this failure was caused by a user interrupt/cancellation |
#### subagentStart
Called before spawning a subagent (Task tool). Can allow or deny subagent creation.
// Input { "subagent_id": "abc-123", "subagent_type": "generalPurpose", "task": "Explore the authentication flow", "parent_conversation_id": "conv-456", "tool_call_id": "tc-789", "subagent_model": "claude-sonnet-4-20250514", "is_parallel_worker": false, "git_branch": "feature/auth" }
// Output { "permission": "allow" | "deny", "user_message": "<message shown when denied>" }
| Input Field | Type | Description |
| ------------------------ | ----------------- | ------------------------------------------------------------ |
| `subagent_id` | string | Unique identifier for this subagent instance |
| `subagent_type` | string | Type of subagent: `generalPurpose`, `explore`, `shell`, etc. |
| `task` | string | The task description given to the subagent |
| `parent_conversation_id` | string | Conversation ID of the parent agent session |
| `tool_call_id` | string | ID of the tool call that triggered the subagent |
| `subagent_model` | string | Model the subagent will use |
| `is_parallel_worker` | boolean | Whether this subagent is running as a parallel worker |
| `git_branch` | string (optional) | Git branch the subagent will operate on, if applicable |
| Output Field | Type | Description |
| -------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------- |
| `permission` | string | `"allow"` to proceed, `"deny"` to block. `"ask"` is not supported for `subagentStart` and is treated as `"deny"`. |
| `user_message` | string (optional) | Message shown to the user when the subagent is denied |
#### subagentStop
Called when a subagent completes, errors, or is aborted. Can trigger follow-up actions.
// Input { "subagent_type": "generalPurpose", "status": "completed" | "error" | "aborted", "task": "Explore the authentication flow", "description": "Exploring auth flow", "summary": "<subagent output summary>", "duration_ms": 45000, "message_count": 12, "tool_call_count": 8, "loop_count": 0, "modified_files": ["src/auth.ts"], "agent_transcript_path": "/path/to/subagent/transcript.txt" }
// Output { "followup_message": "<auto-continue with this message>" }
| Input Field | Type | Description |
| ----------------------- | -------------- | ------------------------------------------------------------------------------------------------ |
| `subagent_type` | string | Type of subagent: `generalPurpose`, `explore`, `shell`, etc. |
| `status` | string | `"completed"`, `"error"`, or `"aborted"` |
| `task` | string | The task description given to the subagent |
| `description` | string | Short description of the subagent's purpose |
| `summary` | string | Output summary from the subagent |
| `duration_ms` | number | Execution time in milliseconds |
| `message_count` | number | Number of messages exchanged during the subagent session |
| `tool_call_count` | number | Number of tool calls the subagent made |
| `loop_count` | number | Number of times a `subagentStop` follow-up has already triggered for this subagent (starts at 0) |
| `modified_files` | string\[] | Files the subagent modified |
| `agent_transcript_path` | string \| null | Path to the subagent's own transcript file (separate from the parent conversation) |
| Output Field | Type | Description |
| ------------------ | ----------------- | ------------------------------------------------------------------------------ |
| `followup_message` | string (optional) | Auto-continue with this message. Only consumed when `status` is `"completed"`. |
The `followup_message` field enables loop-style flows where subagent completion triggers the next iteration. Follow-ups are subject to the same configurable loop limit as the `stop` hook (default 5, configurable via `loop_limit`).
#### beforeShellExecution / beforeMCPExecution
Called before any shell command or MCP tool is executed. Return a permission decision.
By default, hook failures (crash, timeout, invalid JSON) allow the action through (fail-open). Set `failClosed: true` on the hook definition to block the action on failure instead. This is recommended for security-critical `beforeMCPExecution` hooks.
// beforeShellExecution input { "command": "<full terminal command>", "cwd": "<current working directory>", "sandbox": false }
// beforeMCPExecution input { "tool_name": "<tool name>", "tool_input": "<json params>" } // Plus either: { "url": "<server url>" } // Or: { "command": "<command string>" }
// Output { "permission": "allow" | "deny" | "ask", "user_message": "<message shown in client>", "agent_message": "<message sent to agent>" }
#### afterShellExecution
Fires after a shell command executes; useful for auditing or collecting metrics from command output.
// Input { "command": "<full terminal command>", "output": "<full terminal output>", "duration": 1234, "sandbox": false }
| Field | Type | Description |
| ---------- | ------- | ---------------------------------------------------------------------------------------- |
| `command` | string | The full terminal command that was executed |
| `output` | string | Full output captured from the terminal |
| `duration` | number | Duration in milliseconds spent executing the shell command (excludes approval wait time) |
| `sandbox` | boolean | Whether the command ran in a sandboxed environment |
#### afterMCPExecution
Fires after an MCP tool executes; includes the tool's input parameters and full JSON result.
// Input { "tool_name": "<tool name>", "tool_input": "<json params>", "result_json": "<tool result json>", "duration": 1234 }
| Field | Type | Description |
| ------------- | ------ | ----------------------------------------------------------------------------------- |
| `tool_name` | string | Name of the MCP tool that was executed |
| `tool_input` | string | JSON params string passed to the tool |
| `result_json` | string | JSON string of the tool response |
| `duration` | number | Duration in milliseconds spent executing the MCP tool (excludes approval wait time) |
#### afterFileEdit
Fires after the Agent edits a file; useful for formatters or accounting of agent-written code.
// Input { "file_path": "<absolute path>", "edits": [{ "old_string": "<search>", "new_string": "<replace>" }] }
#### beforeReadFile
Called before Agent reads a file. Use for access control to block sensitive files from being sent to the model.
By default, `beforeReadFile` hook failures (crash, timeout, invalid JSON) are logged and the read is allowed through. Set `failClosed: true` on the hook definition to block the read on failure instead.
// Input { "file_path": "<absolute path>", "content": "<file contents>", "attachments": [ { "type": "file" | "rule", "file_path": "<absolute path>" } ] }
// Output { "permission": "allow" | "deny", "user_message": "<message shown when denied>" }
| Input Field | Type | Description |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------- |
| `file_path` | string | Absolute path to the file being read |
| `content` | string | Full contents of the file |
| `attachments` | array | Context attachments associated with the prompt. Each entry has a `type` (`"file"` or `"rule"`) and a `file_path`. |
| Output Field | Type | Description |
| -------------- | ----------------- | --------------------------------------- |
| `permission` | string | `"allow"` to proceed, `"deny"` to block |
| `user_message` | string (optional) | Message shown to user when denied |
#### beforeTabFileRead
Called before Tab (inline completions) reads a file. Enable redaction or access control before Tab accesses file contents.
**Key differences from `beforeReadFile`:**
- Only triggered by Tab, not Agent
- Does not include `attachments` field (Tab doesn't use prompt attachments)
- Useful for applying different policies to autonomous Tab operations
// Input { "file_path": "<absolute path>", "content": "<file contents>" }
// Output { "permission": "allow" | "deny" }
#### afterTabFileEdit
Called after Tab (inline completions) edits a file. Useful for formatters or auditing of Tab-written code.
**Key differences from `afterFileEdit`:**
- Only triggered by Tab, not Agent
- Includes detailed edit information: `range`, `old_line`, and `new_line` for precise edit tracking
- Useful for fine-grained formatting or analysis of Tab edits
// Input { "file_path": "<absolute path>", "edits": [ { "old_string": "<search>", "new_string": "<replace>", "range": { "start_line_number": 10, "start_column": 5, "end_line_number": 10, "end_column": 20 }, "old_line": "<line before edit>", "new_line": "<line after edit>" } ] }
// Output { // No output fields currently supported }
#### beforeSubmitPrompt
Called right after user hits send but before backend request. Can prevent submission.
// Input { "prompt": "<user prompt text>", "attachments": [ { "type": "file" | "rule", "file_path": "<absolute path>" } ] }
// Output { "continue": true | false, "user_message": "<message shown to user when blocked>" }
| Output Field | Type | Description |
| -------------- | ----------------- | ---------------------------------------------------- |
| `continue` | boolean | Whether to allow the prompt submission to proceed |
| `user_message` | string (optional) | Message shown to the user when the prompt is blocked |
#### afterAgentResponse
Called after the agent has completed an assistant message.
// Input { "text": "<assistant final text>" }
#### afterAgentThought
Called after the agent completes a thinking block. Useful for observing the agent's reasoning process.
// Input { "text": "<fully aggregated thinking text>", "duration_ms": 5000 }
// Output { // No output fields currently supported }
| Field | Type | Description |
| ------------- | ----------------- | ------------------------------------------------------ |
| `text` | string | Fully aggregated thinking text for the completed block |
| `duration_ms` | number (optional) | Duration in milliseconds for the thinking block |
#### stop
Called when the agent loop ends. Can optionally auto-submit a follow-up user message to keep iterating.
// Input { "status": "completed" | "aborted" | "error", "loop_count": 0 }
// Output { "followup_message": "<message text>" }
- The optional `followup_message` is a string. When provided and non-empty, Cursor will automatically submit it as the next user message. This enables loop-style flows (e.g., iterate until a goal is met).
- The `loop_count` field indicates how many times the stop hook has already triggered an automatic follow-up for this conversation (starts at 0). The default limit is 5 auto follow-ups per script, configurable via the `loop_limit` option. Set `loop_limit` to `null` to remove the cap. The same limit applies to `subagentStop` follow-ups.
#### sessionStart
Called when a new composer conversation is created. This hook runs as fire-and-forget; the agent loop does not wait for or enforce a blocking response. Use it to set up session-specific environment variables or inject additional context.
// Input { "session_id": "<unique session identifier>", "is_background_agent": true | false, "composer_mode": "agent" | "ask" | "edit" }
// Output { "env": { "<key>": "<value>" }, "additional_context": "<context to add to conversation>" }
| Input Field | Type | Description |
| --------------------- | ----------------- | ------------------------------------------------------------------- |
| `session_id` | string | Unique identifier for this session (same as `conversation_id`) |
| `is_background_agent` | boolean | Whether this is a background agent session vs interactive session |
| `composer_mode` | string (optional) | The mode the composer is starting in (e.g., "agent", "ask", "edit") |
| Output Field | Type | Description |
| -------------------- | ----------------- | ------------------------------------------------------------------------------------------ |
| `env` | object (optional) | Environment variables to set for this session. Available to all subsequent hook executions |
| `additional_context` | string (optional) | Additional context to add to the conversation's initial system context |
The schema also accepts `continue` and `user_message` fields, but current callers do not enforce them. Session creation is not blocked even when `continue` is `false`.
#### sessionEnd
Called when a composer conversation ends. This is a fire-and-forget hook useful for logging, analytics, or cleanup tasks. The response is logged but not used.
// Input { "session_id": "<unique session identifier>", "reason": "completed" | "aborted" | "error" | "window_close" | "user_close", "duration_ms": 45000, "is_background_agent": true | false, "final_status": "<status string>", "error_message": "<error details if reason is 'error'>" }
// Output { // No output fields - fire and forget }
| Input Field | Type | Description |
| --------------------- | ----------------- | ----------------------------------------------------------------------------------------- |
| `session_id` | string | Unique identifier for the session that is ending |
| `reason` | string | How the session ended: "completed", "aborted", "error", "window\_close", or "user\_close" |
| `duration_ms` | number | Total duration of the session in milliseconds |
| `is_background_agent` | boolean | Whether this was a background agent session |
| `final_status` | string | Final status of the session |
| `error_message` | string (optional) | Error message if reason is "error" |
#### preCompact
Called before context window compaction/summarization occurs. This is an observational hook that cannot block or modify the compaction behavior. Useful for logging when compaction happens or notifying users.
// Input { "trigger": "auto" | "manual", "context_usage_percent": 85, "context_tokens": 120000, "context_window_size": 128000, "message_count": 45, "messages_to_compact": 30, "is_first_compaction": true | false }
// Output { "user_message": "<message to show when compaction occurs>" }
| Input Field | Type | Description |
| ----------------------- | ------- | ---------------------------------------------------------- |
| `trigger` | string | What triggered the compaction: "auto" or "manual" |
| `context_usage_percent` | number | Current context window usage as a percentage (0-100) |
| `context_tokens` | number | Current context window token count |
| `context_window_size` | number | Maximum context window size in tokens |
| `message_count` | number | Number of messages in the conversation |
| `messages_to_compact` | number | Number of messages that will be summarized |
| `is_first_compaction` | boolean | Whether this is the first compaction for this conversation |
| Output Field | Type | Description |
| -------------- | ----------------- | -------------------------------------------------- |
| `user_message` | string (optional) | Message to show to the user when compaction occurs |
#### workspaceOpen
Fires once when Cursor opens a workspace and again on every workspace folder change. Skipped when the window has zero workspace folders. Runs in the Cursor desktop app and CLI.
// Input { "hook_event_name": "workspaceOpen", "cursor_version": "string", "workspace_roots": ["<absolute path>"], "user_email": "string | null" }
// Output { "pluginPaths": ["<absolute path>", "..."] }
| Output Field | Type | Description |
| ------------- | -------------------- | ----------------------------------------------------------------------- |
| `pluginPaths` | string\[] (optional) | Absolute paths to plugin directories to load for the current workspace. |
## Environment Variables
Hook scripts receive environment variables when executed:
| Variable | Description | Always Present |
| ------------------------ | ------------------------------------------------------------- | ---------------------- |
| `CURSOR_PROJECT_DIR` | Workspace root directory | Yes |
| `CURSOR_VERSION` | Cursor version string | Yes |
| `CURSOR_USER_EMAIL` | Authenticated user email | If logged in |
| `CURSOR_TRANSCRIPT_PATH` | Path to the conversation transcript file | If transcripts enabled |
| `CURSOR_CODE_REMOTE` | Set to the string `"true"` when running in a remote workspace | For remote workspaces |
| `CLAUDE_PROJECT_DIR` | Alias for project dir (Claude compatibility) | Yes |
Session-scoped environment variables from `sessionStart` hooks are passed to all subsequent hook executions within that session.
## Troubleshooting
**How to confirm hooks are active**
There is a Hooks tab in Cursor Settings to debug configured and executed hooks, as well as a Hooks output channel to see errors.
**If hooks are not working**
- Cursor watches `hooks.json` files and reloads them on save. If hooks still do not load, restart Cursor.
- Check that relative paths are correct for your hook source:
- For **project hooks**, paths are relative to the **project root** (e.g., `.cursor/hooks/script.sh`)
- For **user hooks**, paths are relative to `~/.cursor/` (e.g., `./hooks/script.sh` or `hooks/script.sh`)
**Exit code blocking**
Exit code `2` from command hooks blocks the action (equivalent to returning `permission: "deny"`). This matches Claude Code behavior for compatibility.
### Enterprise hooks and distribution
Cloud distribution and team-wide hook management are available on Enterprise.
---
## Sitemap
[Overview of all docs pages](/llms.txt)
Cursor Docs Index
Local mirror of selected Cursor documentation from https://cursor.com/docs/.
Invoke this skill with a topic, for example /cursor-docs hooks.
Topics
hooks- Hooksmcp- Model Context Protocol (MCP)plugins- Pluginsrules- Rulesskills- Agent Skillssubagents- Subagents
Model Context Protocol (MCP)
What is MCP?
Model Context Protocol (MCP) enables Cursor to connect to external tools and data sources.
Why use MCP?
MCP connects Cursor to external systems and data. Instead of explaining your project structure repeatedly, integrate directly with your tools.
Write MCP servers in any language that can print to stdout or serve an HTTP endpoint - Python, JavaScript, Go, etc.
Browse official plugins in the Cursor Marketplace. For community plugins and MCP servers, browse cursor.directory.
How it works
MCP servers expose capabilities through the protocol, connecting Cursor to external tools or data sources.
Cursor supports three transport methods:
| Transport | Execution environment | Deployment | Users | Input | Auth |
|---|---|---|---|---|---|
| `stdio` | Local | Cursor manages | Single user | shell command | Manual |
| `SSE` | Local/Remote | Deploy as server | Multiple users | URL to an SSE endpoint | OAuth |
| `Streamable HTTP` | Local/Remote | Deploy as server | Multiple users | URL to an HTTP endpoint | OAuth |
Protocol and extension support
Cursor supports these MCP protocol capabilities and extensions:
| Feature | Support | Description |
|---|---|---|
| Tools | Supported | Functions for the AI model to execute |
| Prompts | Supported | Templated messages and workflows for users |
| Resources | Supported | Structured data sources that can be read and referenced |
| Roots | Supported | Server-initiated inquiries into URI or filesystem boundaries |
| Elicitation | Supported | Server-initiated requests for additional information from users |
| Apps (extension) | Supported | Interactive UI views returned by MCP tools |
MCP apps
Cursor supports the MCP Apps extension. MCP tools can return interactive UI along with standard tool output.
MCP Apps follow progressive enhancement. If a host cannot render app UI, the same tool still works through normal MCP responses.
Installing MCP servers
One-click installation
Browse the Cursor Marketplace for official plugins with one-click install. For community plugins and MCP servers, browse cursor.directory. Click "Add to Cursor" on a marketplace entry to install it and authenticate with OAuth.
Using mcp.json
Configure custom MCP servers with a JSON file:
```json title="CLI Server - Node.js" { "mcpServers": { "server-name": { "command": "npx", "args": ["-y", "mcp-server"], "env": { "API_KEY": "value" } } } }
{ "mcpServers": { "server-name": { "command": "python", "args": ["mcp-server.py"], "env": { "API_KEY": "value" } } } }
// MCP server using HTTP or SSE - runs on a server { "mcpServers": { "server-name": { "url": "http://localhost:3000/mcp", "headers": { "API_KEY": "value" } } } }
### Static OAuth for remote servers
For MCP servers that use OAuth, you can provide **static OAuth client credentials** in `mcp.json` instead of dynamic client registration. Use this when:
- The MCP provider gives you a fixed **Client ID** (and optionally **Client Secret**)
- The provider requires **whitelisting a redirect URL** (e.g. Figma, Linear)
- The provider does not support OAuth 2.0 Dynamic Client Registration
Add an `auth` object to remote server entries that use `url`:
{ "mcpServers": { "oauth-server": { "url": "https://api.example.com/mcp", "auth": { "CLIENT_ID": "your-oauth-client-id", "CLIENT_SECRET": "your-client-secret", "scopes": ["read", "write"] } } } }
| Field | Required | Description |
| :----------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------- |
| **CLIENT\_ID** | Yes | OAuth 2.0 Client ID from the MCP provider |
| **CLIENT\_SECRET** | No | OAuth 2.0 Client Secret (if the provider uses confidential clients) |
| **scopes** | No | OAuth scopes to request. If omitted, Cursor will use `/.well-known/oauth-authorization-server` to discover `scopes_supported` |
#### Static redirect URL
Cursor uses a **fixed OAuth redirect URL** for all MCP servers:
https://www.cursor.com/agents/mcp/oauth/callback
When configuring the MCP provider's OAuth app, register this URL as an allowed redirect URI. The server is identified via the OAuth `state` parameter, so one redirect URL works for all MCP servers.
#### Combining with config interpolation
`auth` values support the same interpolation as other fields:
{ "mcpServers": { "oauth-server": { "url": "https://api.example.com/mcp", "auth": { "CLIENT_ID": "${env:MCP_CLIENT_ID}", "CLIENT_SECRET": "${env:MCP_CLIENT_SECRET}" } } } }
Use environment variables for Client ID and Client Secret instead of hardcoding them.
### STDIO server configuration
For STDIO servers (local command-line servers), configure these fields in your `mcp.json`:
| Field | Required | Description | Examples |
| :---------- | :------- | :------------------------------------------------------------------------------------------------------ | :---------------------------------------- |
| **type** | Yes | Server connection type | `"stdio"` |
| **command** | Yes | Command to start the server executable. Must be available on your system path or contain its full path. | `"npx"`, `"node"`, `"python"`, `"docker"` |
| **args** | No | Array of arguments passed to the command | `["server.py", "--port", "3000"]` |
| **env** | No | Environment variables for the server | `{"API_KEY": "${env:api-key}"}` |
| **envFile** | No | Path to an environment file to load more variables | `".env"`, `"${workspaceFolder}/.env"` |
The `envFile` option is only available for STDIO servers. Remote servers (HTTP/SSE) do not support `envFile`. For remote servers, use [config interpolation](mcp.md#config-interpolation) with environment variables set in your shell profile or system environment instead.
### Using the Extension API
For programmatic MCP server registration, Cursor provides an extension API that allows dynamic configuration without modifying `mcp.json` files. This is particularly useful for enterprise environments and automated setup workflows.
### Extension API reference
Register MCP servers programmatically using
`vscode.cursor.mcp.registerServer()`
### Configuration locations
### Project Configuration
Create `.cursor/mcp.json` in your project for project-specific tools.
### Global Configuration
Create `~/.cursor/mcp.json` in your home directory for tools available everywhere.
### Config interpolation
Use variables in `mcp.json` values. Cursor resolves variables in these fields: `command`, `args`, `env`, `url`, and `headers`.
Supported syntax:
- `${env:NAME}` environment variables
- `${userHome}` path to your home folder
- `${workspaceFolder}` project root (the folder that contains `.cursor/mcp.json`)
- `${workspaceFolderBasename}` name of the project root
- `${pathSeparator}` and `${/}` OS path separator
Examples
{ "mcpServers": { "local-server": { "command": "python", "args": ["${workspaceFolder}/tools/mcp_server.py"], "env": { "API_KEY": "${env:API_KEY}" } } } }
{ "mcpServers": { "remote-server": { "url": "https://api.example.com/mcp", "headers": { "Authorization": "Bearer ${env:MY_SERVICE_TOKEN}" } } } }
### Authentication
MCP servers use environment variables for authentication. Pass API keys and tokens through the config.
Cursor supports OAuth for servers that require it.
## Using MCP in chat
Cursor automatically uses MCP tools listed under `Available Tools` when relevant. This includes [Plan Mode](https://cursor.com/docs/agent/plan-mode.md#plan). Ask for a specific tool by name or describe what you need. Enable or disable tools from settings.
### Tool approval
Cursor asks for approval before using MCP tools by default. Click the arrow next to the tool name to see arguments.

#### Run Mode
MCP [follows the same Run Modes as terminal commands](https://cursor.com/docs/agent/security/run-modes.md#run-mode). For example, in **Auto-review** mode, allowlisted MCP tools run immediately and everything else is routed through the classifier.
### Tool response
Cursor shows the response in chat with expandable views of arguments and responses:

### Images as context
MCP servers can return images - screenshots, diagrams, etc. Return them as base64 encoded strings:
const RED_CIRCLE_BASE64 = "/9j/4AAQSkZJRgABAgEASABIAAD/2w..."; // ^ full base64 clipped for readability
server.tool("generate_image", async (params) => { return { content: [ { type: "image", data: RED_CIRCLE_BASE64, mimeType: "image/jpeg", }, ], }; });
See this [example server](https://github.com/msfeldstein/mcp-test-servers/blob/main/src/image-server.js) for implementation details. Cursor attaches returned images to the chat. If the model supports images, it analyzes them.
## Security considerations
When installing MCP servers, consider these security practices:
- **Verify the source**: Only install MCP servers from trusted developers and repositories
- **Review permissions**: Check what data and APIs the server will access
- **Limit API keys**: Use restricted API keys with minimal required permissions
- **Audit code**: For critical integrations, review the server's source code
Remember that MCP servers can access external services and execute code on your behalf. Always understand what a server does before installation.
## Real-world examples
For practical examples of MCP in action:
- **[Xcode integration](https://cursor.com/docs/integrations/xcode.md)** — Connect Cursor to Xcode 26.3+ for builds, tests, SwiftUI previews, and Apple documentation search
- **[Web Development guide](https://cursor.com/for/web-development.md)** — Integrate Linear, Figma, and browser tools into your development workflow
## FAQ
### What's the point of MCP servers?
MCP servers connect Cursor to external tools like Google Drive, Notion, and
other services to bring docs and requirements into your coding workflow.
### How do I debug MCP server issues?
View MCP logs by:
1. Open the Output panel in Cursor (Cmd+Shift+U)
2. Select "MCP Logs" from the dropdown
3. Check for connection errors, authentication issues, or server crashes
The logs show server initialization, tool calls, and error messages.
### Can I temporarily disable an MCP server?
Yes! Toggle servers on/off without removing them:
1. Open Settings (Cmd+Shift+J)
2. Go to Features → Model Context Protocol
3. Click the toggle next to any server to enable/disable
Disabled servers won't load or appear in chat. This is useful for troubleshooting or reducing tool clutter.
### What happens if an MCP server crashes or times out?
If an MCP server fails:
- Cursor shows an error message in chat
- The tool call is marked as failed
- You can retry the operation or check logs for details
- Other MCP servers continue working normally
Cursor isolates server failures to prevent one server from affecting others.
### How do I update an MCP server?
For npm-based servers:
1. Remove the server from settings
2. Clear npm cache: `npm cache clean --force`
3. Re-add the server to get the latest version
For custom servers, update your local files and restart Cursor.
### Can I use MCP servers with sensitive data?
Yes, but follow security best practices:
- Use environment variables for secrets, never hardcode them
- Run sensitive servers locally with `stdio` transport
- Limit API key permissions to minimum required
- Review server code before connecting to sensitive systems
- Consider running servers in isolated environments
---
## Sitemap
[Overview of all docs pages](/llms.txt)
Plugins
Plugins package rules, skills, agents, commands, MCP servers, and hooks into distributable bundles. Browse official plugins in the Cursor Marketplace. For community plugins and MCP servers, browse cursor.directory. You can also build your own to share with other developers.
What plugins contain
A plugin can bundle any combination of these components:
| Component | Description |
|---|---|
| Rules | Persistent AI guidance and coding standards (.mdc files) |
| Skills | Specialized agent capabilities for complex tasks |
| Agents | Custom agent configurations and prompts |
| Commands | Agent-executable command files |
| MCP Servers | Model Context Protocol integrations |
| Hooks | Automation scripts triggered by events |
The marketplace
The Cursor Marketplace is where you discover and install official plugins. Plugins are distributed as Git repositories and submitted through the Cursor team. Every plugin is manually reviewed before it's listed. Browse official plugins at cursor.com/marketplace or search by keyword in the marketplace panel. For community plugins and MCP servers, browse cursor.directory.
Team marketplaces
Team marketplaces are available on Teams and Enterprise plans.
- Teams plan: up to 1 team marketplace
- Enterprise plan: unlimited team marketplaces
Contact sales for unlimited team marketplaces and Enterprise admin controls.
The Team Marketplaces section appears below Plugins in dashboard settings.
On Enterprise plans, only admins can add team marketplaces from Dashboard -> Settings -> Plugins.
Required vs optional plugins
When you assign a plugin to a distribution group, you can set it as required or optional:
- Required: After you click Save, the plugin is installed automatically for everyone in that distribution group.
- Optional: The plugin is available to everyone in that distribution group, and each developer can choose whether to install it.
How do distribution groups work with SCIM?
Distribution groups can be controlled with SCIM-synced directory groups. If your organization uses SCIM, manage group membership in your identity provider, and Cursor will sync those group updates.
Add a team marketplace
Use this flow to import a GitHub repository as a team marketplace:
1. Go to Dashboard -> Settings -> Plugins. 2. In Team Marketplaces, click Add Marketplace. 3. Follow the instructions to create a marketplace from scratch, or use "Import from Repo" if importing from GitHub. 4. Add and review plugins using "Add to Marketplace". 5. Set Team Access groups, optionally enable Auto Refresh, then save.
Example repository to try:
Keep plugins up to date
When importing from GitHub, plugins are indexed when you first import the repository. You can refresh plugins in two ways:
- Automatically: Turn on Enable Auto Refresh to update plugins automatically whenever changes are pushed to the branch the marketplace tracks. This requires the Cursor GitHub App installed on the repository. Cursor re-indexes a marketplace at most once every 10 minutes, batching rapid pushes to the latest commit.
- Manually: Click "Refresh" to manually update.
Auto Refresh updates plugins that are already part of the marketplace. Adding a brand-new plugin from the repository isn't automatic — re-import the repository URL to pick up newly added plugins.
Where developers find team marketplaces
Developers can find team marketplaces in the marketplace panel in Cursor.
- Open the marketplace panel in Cursor.
- Look for plugins from your team marketplace.
- Install optional plugins directly from that panel.
- Required plugins are installed automatically when admins save the required setting for your distribution group.
Installing plugins
Install plugins from the marketplace. Plugins can be scoped to a project or installed at the user level.
MCP Apps deeplinks
Share MCP server configurations using install links:
cursor://anysphere.cursor-deeplink/mcp/install?name=$NAME&config=$BASE64_ENCODED_CONFIG
See MCP install links for details on generating these links.
Managing installed plugins
MCP servers
Toggle MCP servers on or off from Cursor Settings:
1. Open Settings (Cmd+Shift+J) 2. Go to Features > Model Context Protocol 3. Click the toggle next to any server
Disabled servers won't load or appear in chat.
Rules and skills
Manage rules and skills from the Rules section of Cursor Settings. Toggle individual rules between Always, Agent Decides, and Manual modes. Skills appear in the Agent Decides section and can be invoked manually with /skill-name in chat.
Using the workspaceOpen hook
A workspaceOpen hook can return plugin paths to load on workspace open, which is useful when the set of plugins depends on the workspace itself.
Hooks reference
Register plugin paths from a workspaceOpen hook script
Creating plugins
A plugin is a directory with a .cursor-plugin/plugin.json manifest and your components (rules, skills, agents, commands, hooks, or MCP servers). Start from the plugin template repository or create one from scratch:
my-plugin/
├── .cursor-plugin/
│ └── plugin.json
├── rules/
│ └── coding-standards.mdc
├── skills/
│ └── code-reviewer/
│ └── SKILL.md
└── mcp.json
The manifest only requires a name field. Components are discovered automatically from their default directories, or you can specify custom paths in the manifest.
{
"name": "my-plugin",
"description": "Custom development tools",
"version": "1.0.0",
"author": { "name": "Your Name" }
}
Test plugins locally
Before you publish, load your plugin from ~/.cursor/plugins/local:
1. Create a folder for your plugin: ~/.cursor/plugins/local/my-plugin 2. Copy your plugin files into that folder. Make sure .cursor-plugin/plugin.json is at the plugin root. 3. Restart Cursor, or run Developer: Reload Window. 4. Verify your plugin components load in Cursor, such as rules, skills, or MCP servers.
For faster iteration, symlink your plugin repository:
ln -s /path/to/my-plugin ~/.cursor/plugins/local/my-plugin
When your plugin is ready, submit it for review at cursor.com/marketplace/publish. For multi-plugin repositories, add a marketplace manifest at .cursor-plugin/marketplace.json.
See the Plugins reference for the full manifest schema, component formats, and submission checklist.
Team and Enterprise marketplaces
Upgrade for private team marketplaces and organization-wide plugin distribution.
FAQ
Are marketplace plugins reviewed for security?
Yes. Every plugin is manually reviewed before it's listed. All plugins must be open source, and we review each update before publishing. See Marketplace security for details on vetting, update reviews, and how to report issues.
How do I create a plugin?
Create a directory with a .cursor-plugin/plugin.json manifest file, add your rules, skills, agents, commands, or other components, and submit it to the Cursor team. See the Plugins reference for the full guide.
---
Sitemap
Rules
Rules provide system-level instructions to Agent. They bundle prompts, scripts, and more together, making it easy to manage and share workflows across your team.
Cursor supports four types of rules:
Project Rules
Stored in .cursor/rules, version-controlled and scoped to your codebase.
User Rules
Global to your Cursor environment. Used by Agent (Chat).
Team Rules
Team-wide rules managed from the dashboard. Available on Team and Enterprise plans.
AGENTS.md
Agent instructions in markdown format. Simple alternative to .cursor/rules.
How rules work
Large language models don't retain memory between completions. Rules provide persistent, reusable context at the prompt level.
When applied, rule contents are included at the start of the model context. This gives the AI consistent guidance for generating code, interpreting edits, or helping with workflows.
Project rules
Project rules live in .cursor/rules as .mdc files and are version-controlled. They are scoped using path patterns, invoked manually, or included based on relevance.
Use project rules to:
- Encode domain-specific knowledge about your codebase
- Automate project-specific workflows or templates
- Standardize style or architecture decisions
Rule file structure
Each rule is an .mdc file that you can name anything you want. Project rules must use the .mdc extension. A plain .md file in .cursor/rules is ignored by the rules system because it has no frontmatter to specify description, globs, and alwaysApply. If you prefer plain markdown, use AGENTS.md instead.
.cursor/rules/
react-patterns.mdc # Recognized as a project rule
api-guidelines.md # Ignored (wrong extension)
frontend/ # Organize rules in folders
components.mdc
Rule anatomy
Each rule is a markdown file with frontmatter metadata and content. Control how rules are applied from the type dropdown which changes properties description, globs, alwaysApply.
| Rule Type | Description |
|---|---|
Always Apply | Apply to every chat session |
Apply Intelligently | When Agent decides it's relevant based on description |
Apply to Specific Files | When file matches a specified pattern |
Apply Manually | When @-mentioned in chat (e.g., @my-rule) |
Under the hood, the three frontmatter fields interact to determine when a rule is included:
alwaysApply | description | globs | Behavior |
|---|---|---|---|
true | — | — | Always included. Globs and description are ignored. |
false | — | provided | Auto-attached when a matching file is in context. |
false | provided | omitted | Agent reads the description and pulls the rule in when relevant. |
false | omitted | omitted | Included only when you @-mention the rule in chat. |
```md title="Always applied" --- alwaysApply: true ---
- All source files must include the company copyright header
- When you are unsure about implementation details, read the relevant
source files before proposing changes
- Never modify generated files in the
dist/orbuild/directories
--- globs: src/components/*/.tsx alwaysApply: false ---
- Use named exports, not default exports
- Co-locate styles in a module CSS file next to the component
- Keep components under 200 lines. Extract subcomponents into the same
directory when a file grows beyond that
- Prefer composition over prop drilling. Pass children or render props
instead of threading data through multiple layers
--- description: RPC service conventions and patterns for the backend alwaysApply: false ---
- Define each service in its own file under
src/services/ - Always validate inputs at the service boundary before passing data
to internal functions
- Return structured error objects with a
codeandmessagefield,
never throw raw strings
- Add a
@service-template.tsreference file when creating a new
service for the standard boilerplate
--- alwaysApply: false ---
- Every database migration must have both
upanddownfunctions
so it can be fully reversed
- Never alter a column type in-place. Add a new column, backfill,
then drop the old one in a separate migration
- Reference the template for the expected file structure
@migration-template.sql
### Glob pattern examples
Use `globs` to scope a rule to specific files or directories. Separate multiple patterns with commas.
| Pattern | Matches |
| :---------------------------- | :----------------------------------------------------- |
| `*` | Any single file name segment |
| `**` | Any number of directories (recursive) |
| `*.ts` | All `.ts` files in the root |
| `**/*.ts` | All `.ts` files in any directory |
| `src/**` | All files anywhere under `src/` |
| `src/**/*.tsx` | All `.tsx` files anywhere under `src/` |
| `docs/**/*.md, docs/**/*.mdx` | `.md` and `.mdx` files under `docs/` (comma-separated) |
| `tailwind.config.*` | `tailwind.config` with any extension |
### Creating a rule
There are two ways to create rules:
- **`/create-rule` in chat**: Type `/create-rule` in Agent and describe what you want. Agent generates the rule file with proper frontmatter and saves it to `.cursor/rules`.
- **From settings**: Open `Cursor Settings > Rules, Commands` and click `+ Add Rule`. This creates a new rule file in `.cursor/rules`. From settings you can see all rules and their status.
## Best practices
Good rules are focused, actionable, and scoped.
- Keep rules under 500 lines
- Split large rules into multiple, composable rules
- Provide concrete examples or referenced files
- Avoid vague guidance. Write rules like clear internal docs
- Reuse rules when repeating prompts in chat
- Reference files instead of copying their contents—this keeps rules short and prevents them from becoming stale as code changes
### What to avoid in rules
- **Copying entire style guides**: Use a linter instead. Agent already knows common style conventions.
- **Documenting every possible command**: Agent knows common tools like npm, git, and pytest.
- **Adding instructions for edge cases that rarely apply**: Keep rules focused on patterns you use frequently.
- **Duplicating what's already in your codebase**: Point to canonical examples instead of copying code.
Start simple. Add rules only when you notice Agent making the same mistake repeatedly. Don't over-optimize before you understand your patterns.
Check your rules into git so your whole team benefits. When you see Agent make a mistake, update the rule. You can even tag `@cursor` on a GitHub issue or PR to have Agent update the rule for you.
## Rule file format
Each rule is a markdown file with frontmatter metadata and content. The frontmatter metadata is used to control how the rule is applied. The content is the rule itself.
--- description: "This rule provides standards for frontend components and API validation" alwaysApply: false ---
...rest of the rule content
If alwaysApply is true, the rule will be applied to every chat session. Otherwise, the description of the rule will be presented to the Cursor Agent to decide if it should be applied.
## Examples
### Standards for frontend components and API validation
This rule provides standards for frontend components:
When working in components directory:
- Always use Tailwind for styling
- Use Framer Motion for animations
- Follow component naming conventions
This rule enforces validation for API endpoints:
In API directory:
- Use zod for all validation
- Define return types with zod schemas
- Export types generated from schemas
### Templates for Express services and React components
This rule provides a template for Express services:
Use this template when creating Express service:
- Follow RESTful principles
- Include error handling middleware
- Set up proper logging
@express-service-template.ts
This rule defines React component structure:
React components should follow this layout:
- Props interface at top
- Component as named export
- Styles at bottom
@component-template.tsx
### Automating development workflows and documentation generation
This rule automates app analysis:
When asked to analyze the app:
1. Run dev server with `npm run dev`
2. Fetch logs from console
3. Suggest performance improvements
This rule helps generate documentation:
Help draft documentation by:
- Extracting code comments
- Analyzing README.md
- Generating markdown documentation
### Adding a new setting in Cursor
First create a property to toggle in `@reactiveStorageTypes.ts`.
Add default value in `INIT_APPLICATION_USER_PERSISTENT_STORAGE` in `@reactiveStorageService.tsx`.
For beta features, add toggle in `@settingsBetaTab.tsx`, otherwise add in `@settingsGeneralTab.tsx`. Toggles can be added as `<SettingsSubSection>` for general checkboxes. Look at the rest of the file for examples.
<SettingsSubSection label="Your feature name" description="Your feature description" value={ vsContext.reactiveStorageService.applicationUserPersistentStorage .myNewProperty ?? false } onChange={(newVal) => { vsContext.reactiveStorageService.setApplicationUserPersistentStorage( "myNewProperty", newVal, ); }} />
To use in the app, import reactiveStorageService and use the property:
const flagIsEnabled = vsContext.reactiveStorageService.applicationUserPersistentStorage .myNewProperty;
Examples are available from providers and frameworks. Community-contributed rules are found across crowdsourced collections and repositories online.
## Team Rules
Team and [Enterprise](https://cursor.com/docs/enterprise.md) plans can create and enforce rules across their entire organization from the [Cursor dashboard](https://cursor.com/dashboard/team-content). Admins can configure whether or not each rule is required for team members.
Team Rules work alongside other rule types and take precedence to ensure organizational standards are maintained across all projects. They provide a powerful way to ensure consistent coding standards, practices, and workflows across your entire team without requiring individual setup or configuration.
### Managing Team Rules
Team administrators can create and manage rules directly from the Cursor dashboard:

Once team rules are created, they automatically apply to all team members and are visible in the dashboard:

### Activation and enforcement
- **Enable this rule immediately**: When checked, the rule is active as soon as you create it. When unchecked, the rule is saved as a draft and does not apply until you enable it later.
- **Enforce this rule**: When enabled, the rule is required for all team members and cannot be disabled in their Cursor settings. When not enforced, team members can toggle the rule off in `Cursor Settings → Rules` under the Team Rules section.
By default, non‑enforced Team Rules can be disabled by users. Use Enforce this rule to prevent that.
### Format and how Team Rules are applied
- **Content**: Team Rules are free‑form text. They do not use the folder structure of Project Rules.
- **Glob patterns**: Team Rules support glob patterns for file-scoped application. When a glob pattern is set (e.g., `**/*.py`), the rule only applies when matching files are in context. Rules without a glob pattern apply to every conversation.
- **Where they apply**: When a Team Rule is enabled (and not disabled by the user, unless enforced), it is included in the model context for Agent (Chat) across all repositories and projects for that team.
- **Precedence**: Rules are applied in this order: **Team Rules → Project Rules → User Rules**. All applicable rules are merged; earlier sources take precedence when guidance conflicts.
Some teams use enforced rules as part of internal compliance workflows. While this is supported, AI guidance should not be your only security control.
## Importing Rules
You can import rules from external sources to reuse existing configurations or bring in rules from other tools.
### Remote rules (via GitHub)
Import rules directly from any GitHub repository you have access to—public or private.
1. Open **Cursor Settings → Rules, Commands**
2. Click `+ Add Rule` next to `Project Rules`, then select Remote Rule (Github)
3. Paste the GitHub repository URL containing the rules. Cursor will scan for all `.mdc` files in the repo.
4. Cursor will pull and sync the rule(s) into your project
Rules will be placed in `.cursor/rules/imported/<repoName>`. Rules will also keep their relative paths, so `dir/rule.mdc` will be imported as `.cursor/rule/imported/<repoName>/dir/rule.mdc`.
## AGENTS.md
`AGENTS.md` is a simple markdown file for defining agent instructions. Place it in your project root as an alternative to `.cursor/rules` for straightforward use cases.
Unlike Project Rules, `AGENTS.md` is a plain markdown file without metadata or complex configurations. It's perfect for projects that need simple, readable instructions without the overhead of structured rules.
Cursor supports AGENTS.md in the project root and subdirectories.
Project Instructions
Code Style
- Use TypeScript for all new files
- Prefer functional components in React
- Use snake_case for database columns
Architecture
- Follow the repository pattern
- Keep business logic in service layers
### Improvements
### Nested AGENTS.md support
Nested `AGENTS.md` support in subdirectories is now available. You can place `AGENTS.md` files in any subdirectory of your project, and they will be automatically applied when working with files in that directory or its children.
This allows for more granular control of agent instructions based on the area of your codebase you're working in:
project/ AGENTS.md # Global instructions frontend/ AGENTS.md # Frontend-specific instructions components/ AGENTS.md # Component-specific instructions backend/ AGENTS.md # Backend-specific instructions
Instructions from nested `AGENTS.md` files are combined with parent directories, with more specific instructions taking precedence.
## User Rules
User Rules are global preferences defined in **Cursor Settings → Rules** that apply across all projects. They are used by Agent (Chat) and are perfect for setting preferred communication style or coding conventions:
Please reply in a concise style. Avoid unnecessary repetition or filler language.
## FAQ
### Why isn't my rule being applied?
Check the rule type. For `Apply Intelligently`, ensure a description is defined. For `Apply to Specific Files`, ensure the file pattern matches referenced files.
### Can rules reference other rules or files?
Yes. Use `@filename.ts` to include files in your rule's context. You can also @mention rules in chat to apply them manually.
### Can I create a rule from chat?
Yes, you can ask the agent to create a new rule for you.
### Do rules impact Cursor Tab or other AI features?
No. Rules do not impact Cursor Tab or other AI features.
### Do User Rules apply to Inline Edit (Cmd/Ctrl+K)?
No. User Rules are not applied to Inline Edit (Cmd/Ctrl+K). They are only
used by Agent (Chat).
---
## Sitemap
[Overview of all docs pages](/llms.txt)
Agent Skills
Agent Skills is an open standard for extending AI agents with specialized capabilities. Skills package domain-specific knowledge and workflows that agents can use to perform specific tasks.
What are skills?
A skill is a portable, version-controlled package that teaches agents how to perform domain-specific tasks. Skills can include scripts, templates, and references that agents may act on using their tools.
Portable
Skills work across any agent that supports the Agent Skills standard.
Version-controlled
Skills are stored as files and can be tracked in your repository, or installed via GitHub repository links.
Actionable
Skills can include scripts, templates, and references that agents act on using their tools.
Progressive
Skills load resources on demand, keeping context usage efficient.
How skills work
When Cursor starts, it automatically discovers skills from skill directories and makes them available to Agent. The agent is presented with available skills and decides when they are relevant based on context.
Skills can also be manually invoked by typing / in Agent chat and searching for the skill name.
Skill directories
Skills are automatically loaded from these locations:
| Location | Scope |
|---|---|
.agents/skills/ | Project-level |
.cursor/skills/ | Project-level |
~/.agents/skills/ | User-level (global) |
~/.cursor/skills/ | User-level (global) |
For compatibility, Cursor also loads skills from Claude and Codex directories: .claude/skills/, .codex/skills/, ~/.claude/skills/, and ~/.codex/skills/.
Each skill should be a folder containing a SKILL.md file:
.agents/
└── skills/
└── my-skill/
└── SKILL.md
Skills can also include optional directories for scripts, references, and assets:
.agents/
└── skills/
└── deploy-app/
├── SKILL.md
├── scripts/
│ ├── deploy.sh
│ └── validate.py
├── references/
│ └── REFERENCE.md
└── assets/
└── config-template.json
Nested skill directories
Skill directories can be organized into subdirectories. This is useful for grouping related skills by category, team, or domain. Cursor walks the skills root recursively and picks up any SKILL.md it finds:
.cursor/
└── skills/
├── shipping/
│ ├── land-it/
│ │ └── SKILL.md
│ └── careful-merge-conflicts/
│ └── SKILL.md
├── debugging/
│ └── using-datadog-mcp/
│ └── SKILL.md
└── workflow/
└── tdd/
└── SKILL.md
The category folder is purely organizational. The skill's identity comes from the folder containing SKILL.md (here land-it, tdd, etc.), not the parent category.
Cursor also discovers skills inside nested project subdirectories. A .cursor/skills/ (or .agents/skills/) folder anywhere inside your repository is picked up, so monorepos can colocate skills with the package they apply to:
my-monorepo/
├── .cursor/skills/ # repo-wide skills
│ └── land-it/SKILL.md
└── apps/
└── web/
└── .cursor/skills/ # app-specific skills
└── deploy-web/SKILL.md
Skills in nested project directories are automatically scoped to files inside that directory. In the example above, deploy-web is only surfaced when the agent works with files under apps/web/, while skills in the repo-wide .cursor/skills/ are available everywhere. This is similar to the `paths` frontmatter field — you don't need to set paths on a nested skill to scope it to its directory.
SKILL.md file format
Each skill is defined in a SKILL.md file with YAML frontmatter:
---
name: my-skill
description: Short description of what this skill does and when to use it.
---
# My Skill
Detailed instructions for the agent.
## When to Use
- Use this skill when...
- This skill is helpful for...
## Instructions
- Step-by-step guidance for the agent
- Domain-specific conventions
- Best practices and patterns
- Use the ask questions tool if you need to clarify requirements with the user
Frontmatter fields
| Field | Required | Description |
|---|---|---|
name | Yes | Skill identifier. Lowercase letters, numbers, and hyphens only. Must match the parent folder name. |
description | Yes | Describes what the skill does and when to use it. Used by the agent to determine relevance. |
paths | No | Glob patterns that scope the skill to matching files. Accepts a comma-separated string or a list. When set, the skill is only surfaced when the agent works with files that match. |
disable-model-invocation | No | When true, the skill is only included when explicitly invoked via /skill-name. The agent will not automatically apply it based on context. |
metadata | No | Arbitrary key-value mapping for additional metadata. |
Scoping a skill to specific files
Use the paths field to limit a skill to files that match one or more glob patterns. The skill is then only surfaced to the agent when it is reading or editing matching files. This keeps file-specific guidance out of context for unrelated work.
---
name: react-component-patterns
description: Conventions for writing React components in this codebase.
paths:
- "**/*.tsx"
- "packages/ui/**/*.ts"
---
# React component patterns
...
You can also pass a single comma-separated string:
---
name: python-style
description: Style rules for Python files.
paths: "**/*.py, scripts/**/*.py"
---
Patterns follow standard glob syntax. Leave paths unset for a skill that should be available regardless of which files are open.
The legacy globs field is still accepted as a fallback for older skills, but new skills should use paths.
Disabling automatic invocation
By default, skills are automatically applied when the agent determines they are relevant. Set disable-model-invocation: true to make a skill behave like a traditional slash command, where it is only included in context when you explicitly type /skill-name in chat.
Including scripts in skills
Skills can include a scripts/ directory containing executable code that agents can run. Reference scripts in your SKILL.md using relative paths from the skill root.
---
name: deploy-app
description: Deploy the application to staging or production environments. Use when deploying code or when the user mentions deployment, releases, or environments.
---
# Deploy App
Deploy the application using the provided scripts.
## Usage
Run the deployment script: `scripts/deploy.sh <environment>`
Where `<environment>` is either `staging` or `production`.
## Pre-deployment Validation
Before deploying, run the validation script: `python scripts/validate.py`
The agent reads these instructions and executes the referenced scripts when the skill is invoked. Scripts can be written in any language—Bash, Python, JavaScript, or any other executable format supported by the agent implementation.
Scripts should be self-contained, include helpful error messages, and handle edge cases gracefully.
Optional directories
Skills support these optional directories:
| Directory | Purpose |
|---|---|
scripts/ | Executable code that agents can run |
references/ | Additional documentation loaded on demand |
assets/ | Static resources like templates, images, or data files |
Keep your main SKILL.md focused and move detailed reference material to separate files. This keeps context usage efficient since agents load resources progressively—only when needed.
Viewing skills
To view discovered skills:
1. Open Cursor Settings (Cmd+Shift+J on Mac, Ctrl+Shift+J on Windows/Linux) 2. Navigate to Rules 3. Skills appear in the Agent Decides section
Installing skills from GitHub
You can import skills from GitHub repositories:
1. Open Cursor Settings → Rules 2. In the Project Rules section, click Add Rule 3. Select Remote Rule (Github) 4. Enter the GitHub repository URL
Migrating rules and commands to skills
Cursor includes a built-in /migrate-to-skills skill in 2.4 that helps you convert existing dynamic rules and slash commands to skills.
The migration skill converts:
- Dynamic rules: Rules that use the "Apply Intelligently" configuration—rules with
alwaysApply: false(or undefined) and noglobspatterns defined. These are converted to standard skills. - Slash commands: Both user-level and workspace-level commands are converted to skills with
disable-model-invocation: true, preserving their explicit invocation behavior.
To migrate:
1. Type /migrate-to-skills in Agent chat 2. The agent will identify eligible rules and commands and convert them to skills 3. Review the generated skills in .cursor/skills/
Rules with alwaysApply: true or specific globs patterns are not migrated, as they have explicit triggering conditions that differ from skill behavior. User rules are also not migrated since they are not stored on the file system.
Learn more
Agent Skills is an open standard. Learn more at agentskills.io.
---
Sitemap
Subagents
Subagents are specialized AI assistants that Cursor's agent can delegate tasks to. Each subagent operates in its own context window, handles specific types of work, and returns its result to the parent agent. Use subagents to break down complex tasks, do work in parallel, and preserve context in the main conversation.
You can use subagents in the editor, CLI, and Cloud Agents.
Context isolation
Each subagent has its own context window. Long research or exploration tasks don't consume space in your main conversation.
Parallel execution
Launch multiple subagents simultaneously. Work on different parts of your codebase without waiting for sequential completion.
Specialized expertise
Configure subagents with custom prompts, tool access, and models for domain-specific tasks.
Reusability
Define custom subagents and use them across projects.
How subagents work
When Agent encounters a complex task, it can launch a subagent automatically. The subagent receives a prompt with all necessary context, works autonomously, and returns a final message with its results.
Subagents start with a clean context. The parent agent includes relevant information in the prompt since subagents don't have access to prior conversation history.
Foreground vs background
Subagents run in one of two modes:
| Mode | Behavior | Best for |
|---|---|---|
| Foreground | Blocks until the subagent completes. Returns the result immediately. | Sequential tasks where you need the output. |
| Background | Returns immediately. The subagent works independently. | Long-running tasks or parallel workstreams. |
Built-in subagents
Cursor includes three built-in subagents that handle context-heavy operations automatically. These subagents were designed based on analysis of agent conversations where context window limits were hit.
| Subagent | Purpose | Why it's a subagent |
|---|---|---|
| Explore | Searches and analyzes codebases | Codebase exploration generates large intermediate output that would bloat the main context. Uses a faster model to run many parallel searches. |
| Bash | Runs series of shell commands | Command output is often verbose. Isolating it keeps the parent focused on decisions, not logs. |
| Browser | Controls browser via MCP tools | Browser interactions produce noisy DOM snapshots and screenshots. The subagent filters this down to relevant results. |
Why these subagents exist
These three operations share common traits: they generate noisy intermediate output, benefit from specialized prompts and tools, and can consume significant context. Running them as subagents solves several problems:
- Context isolation — Intermediate output stays in the subagent. The parent only sees the final summary.
- Model flexibility — The explore subagent uses a faster model by default. This enables running 10 parallel searches in the time a single main-agent search would take.
- Specialized configuration — Each subagent has prompts and tool access tuned for its specific task.
- Cost efficiency — Faster models cost less. Isolating token-heavy work in subagents with appropriate model choices reduces overall cost.
You don't need to configure these subagents. Agent uses them automatically when appropriate.
When to use subagents
| Use subagents when... | Use skills when... |
|---|---|
| You need context isolation for long research tasks | The task is single-purpose (generate changelog, format) |
| Running multiple workstreams in parallel | You want a quick, repeatable action |
| The task requires specialized expertise across many steps | The task completes in one shot |
| You want an independent verification of work | You don't need a separate context window |
If you find yourself creating a subagent for a simple, single-purpose task like "generate a changelog" or "format imports," consider using a skill instead.
Quick start
Agent automatically uses subagents when appropriate. You can also create a custom subagent by asking Agent:
Create a subagent file at .cursor/agents/verifier.md with YAML frontmatter (name, description) followed by the prompt. The verifier subagent should validate completed work, check that implementations are functional, run tests, and report what passed vs what's incomplete.
For more control, create custom subagents manually in your project or user directory.
Custom subagents
Define custom subagents to encode specialized knowledge, enforce team standards, or automate repetitive workflows.
File locations
| Type | Location | Scope |
|---|---|---|
| Project subagents | .cursor/agents/ | Current project only |
.claude/agents/ | Current project only (Claude compatibility) | |
.codex/agents/ | Current project only (Codex compatibility) | |
| User subagents | ~/.cursor/agents/ | All projects for current user |
~/.claude/agents/ | All projects for current user (Claude compatibility) | |
~/.codex/agents/ | All projects for current user (Codex compatibility) |
Project subagents take precedence when names conflict. When multiple locations contain subagents with the same name, .cursor/ takes precedence over .claude/ or .codex/.
File format
Each subagent is a markdown file with YAML frontmatter:
---
name: security-auditor
description: Security specialist. Use when implementing auth, payments, or handling sensitive data.
model: inherit
readonly: true
---
You are a security expert auditing code for vulnerabilities.
When invoked:
1. Identify security-sensitive code paths
2. Check for common vulnerabilities (injection, XSS, auth bypass)
3. Verify secrets are not hardcoded
4. Review input validation and sanitization
Report findings by severity:
- Critical (must fix before deploy)
- High (fix soon)
- Medium (address when possible)
Configuration fields
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | No | Derived from filename | Display name and identifier. Use lowercase letters and hyphens. |
description | string | No | — | Short description shown in Task tool hints. Agent reads this to decide delegation. |
model | string | No | inherit | Model to use: inherit or a specific model ID. See model configuration. |
readonly | boolean | No | false | If true, the subagent runs with restricted write permissions (no file edits, no state-changing shell commands). |
is_background | boolean | No | false | If true, the subagent runs in the background without blocking the parent. |
Model configuration
The model field controls which model a subagent uses. There are two options:
| Value | Behavior |
|---|---|
inherit | Uses the same model as the parent agent. This is the default. |
| A specific model ID | Uses the exact model you specify, such as composer-2 or gpt-5.5. See the models reference for available IDs. |
Choose inherit when the subagent needs the same reasoning power as the parent. Use a specific model ID when you need a particular model's capabilities regardless of what the parent uses.
When the configured model won't be used
Cursor honors the model field in your subagent frontmatter unless one of these conditions applies:
- Team admin restrictions — Your organization's admin has blocked the specified model.
- Max Mode required — The model requires Max Mode and you don't have it enabled.
- Plan limitations — The model isn't available on your current plan.
In these cases, Cursor falls back to a compatible model. If you're seeing unexpected model behavior, check your plan settings and Max Mode status.
---
name: code-reviewer
description: Reviews code for correctness and style.
model: inherit
---
Review the code changes for bugs, style issues, and edge cases.
---
name: search-agent
description: Searches the codebase for relevant files and symbols.
model: inherit
---
Search the codebase and return relevant file paths and code snippets.
---
name: reasoning-agent
description: Handles complex architectural decisions.
model: gpt-5.5
---
Analyze the architecture and recommend changes with detailed reasoning.
Using subagents
Automatic delegation
Agent proactively delegates tasks based on:
- The task complexity and scope
- Custom subagent descriptions in your project
- Current context and available tools
Include phrases like "use proactively" or "always use for" in your description field to encourage automatic delegation.
Explicit invocation
Request a specific subagent by using the /name syntax in your prompt:
> /verifier confirm the auth flow is complete
> /debugger investigate this error
> /security-auditor review the payment module
You can also invoke subagents by mentioning them naturally:
> Use the verifier subagent to confirm the auth flow is complete
> Have the debugger subagent investigate this error
> Run the security-auditor subagent on the payment module
Parallel execution
Launch multiple subagents concurrently for maximum throughput:
> Review the API changes and update the documentation in parallel
Agent sends multiple Task tool calls in a single message, so subagents run simultaneously.
Cloud subagents
From a local agent session, you can hand off work to a cloud subagent that runs on its own VM and branch. Your local workspace stays clean and responsive while long-running or parallel work happens in the cloud. The parent agent keeps running locally or in the cloud without interruption. Cloud subagents run from the Agents Window in the Cursor desktop app.
Start a cloud subagent with /in-cloud
Type /in-cloud and the next task you submit runs as a cloud subagent. It spins up its own VM and branch to work on the task.
This is useful for isolating long-running or parallel work, such as fixing CI, investigating an issue, or exploring a codebase while you keep working locally.
Babysit a PR with /babysit
Ask a cloud subagent to babysit a pull request with /babysit or by clicking the quick-action pill. The cloud agent iterates remotely to prepare the PR for merge without tying up your local session.
Cloud subagents use the environment configured for your repo and follow the same model and capability rules as other Cloud Agents. Because they run on a cloud VM, their MCP servers come from your team's configuration at cursor.com/agents, not from your local session.
Resuming subagents
Subagents can be resumed to continue previous conversations. This is useful for long-running tasks that span multiple invocations.
Each subagent execution returns an agent ID. Pass this ID to resume the subagent with full context preserved:
> Resume agent abc123 and analyze the remaining test failures
Background subagents write their state as they run. You can resume a subagent after it completes to continue the conversation with preserved context.
Common patterns
Verification agent
A verification agent independently validates whether claimed work was actually completed. This addresses a common issue where AI marks tasks as done but implementations are incomplete or broken.
---
name: verifier
description: Validates completed work. Use after tasks are marked done to confirm implementations are functional.
---
You are a skeptical validator. Your job is to verify that work claimed as complete actually works.
When invoked:
1. Identify what was claimed to be completed
2. Check that the implementation exists and is functional
3. Run relevant tests or verification steps
4. Look for edge cases that may have been missed
Be thorough and skeptical. Report:
- What was verified and passed
- What was claimed but incomplete or broken
- Specific issues that need to be addressed
Do not accept claims at face value. Test everything.
Create a subagent file at .cursor/agents/verifier.md with YAML frontmatter containing name and description. The description should be 'Validates completed work. Use after tasks are marked done to confirm implementations are functional.' The prompt body should instruct it to be skeptical, verify implementations actually work by running tests, and look for edge cases.
This pattern is useful for:
- Validating that features work end-to-end before marking tickets complete
- Catching partially implemented functionality
- Ensuring tests actually pass (not just that test files exist)
Orchestrator pattern
For complex workflows, a parent agent can coordinate multiple specialist subagents in sequence:
1. Planner analyzes requirements and creates a technical plan 2. Implementer builds the feature based on the plan 3. Verifier confirms the implementation matches requirements
Each handoff includes structured output so the next agent has clear context.
Example subagents
Debugger
---
name: debugger
description: Debugging specialist for errors and test failures. Use when encountering issues.
---
You are an expert debugger specializing in root cause analysis.
When invoked:
1. Capture error message and stack trace
2. Identify reproduction steps
3. Isolate the failure location
4. Implement minimal fix
5. Verify solution works
For each issue, provide:
- Root cause explanation
- Evidence supporting the diagnosis
- Specific code fix
- Testing approach
Focus on fixing the underlying issue, not symptoms.
Create a subagent file at .cursor/agents/debugger.md with YAML frontmatter containing name and description. The debugger subagent should specialize in root cause analysis: capture stack traces, identify reproduction steps, isolate failures, implement minimal fixes, and verify solutions.
Test runner
---
name: test-runner
description: Test automation expert. Use proactively to run tests and fix failures.
---
You are a test automation expert.
When you see code changes, proactively run appropriate tests.
If tests fail:
1. Analyze the failure output
2. Identify the root cause
3. Fix the issue while preserving test intent
4. Re-run to verify
Report test results with:
- Number of tests passed/failed
- Summary of any failures
- Changes made to fix issues
Create a subagent file at .cursor/agents/test-runner.md with YAML frontmatter containing name and description (mentioning 'Use proactively'). The test-runner subagent should proactively run tests when it sees code changes, analyze failures, fix issues while preserving test intent, and report results.
Best practices
- Write focused subagents — Each subagent should have a single, clear responsibility. Avoid generic "helper" agents.
- Invest in descriptions — The
descriptionfield determines when Agent delegates to your subagent. Spend time refining it. Test by making prompts and checking if the right subagent gets triggered. - Keep prompts concise — Long, rambling prompts dilute focus. Be specific and direct.
- Add subagents to version control — Check
.cursor/agents/into your repository so the team benefits. - Start with Agent-generated agents — Let Agent help you draft the initial configuration, then customize.
- Use hooks for file output — If you need subagents to produce structured output files, consider using hooks to process and save their results consistently.
Anti-patterns to avoid
Don't create dozens of generic subagents. Having 50+ subagents with vague instructions like "helps with coding" is ineffective. Agent won't know when to use them, and you'll waste time maintaining them.
- Vague descriptions — "Use for general tasks" gives Agent no signal about when to delegate. Be specific: "Use when implementing authentication flows with OAuth providers."
- Overly long prompts — A 2,000-word prompt doesn't make a subagent smarter. It makes it slower and harder to maintain.
- Duplicating slash commands — If a task is single-purpose and doesn't need context isolation, use a slash command instead.
- Too many subagents — Start with 2-3 focused subagents. Add more only when you have clear, distinct use cases.
Managing subagents
Creating subagents
The easiest way to create a subagent is to ask Agent to create one for you:
Create a subagent file at .cursor/agents/security-reviewer.md with YAML frontmatter containing name and description. The security-reviewer subagent should check code for common vulnerabilities like injection, XSS, and hardcoded secrets.
You can also create subagents manually by adding markdown files to .cursor/agents/ (project) or ~/.cursor/agents/ (user).
Viewing subagents
Agent includes all custom subagents in its available tools. You can see which subagents are configured by checking the .cursor/agents/ directory in your project.
Performance and cost
Subagents have trade-offs. Understanding them helps you decide when to use them.
| Benefit | Trade-off |
|---|---|
| Context isolation | Startup overhead (each subagent gathers its own context) |
| Parallel execution | Higher token usage (multiple contexts running simultaneously) |
| Specialized focus | Latency (may be slower than main agent for simple tasks) |
Token and cost considerations
- Subagents consume tokens independently — Each subagent has its own context window and token usage. Running five subagents in parallel uses roughly five times the tokens of a single agent.
- Evaluate the overhead — For quick, simple tasks, the main agent is often faster. Subagents shine for complex, long-running, or parallel work.
- Subagents can be slower — The benefit is context isolation, not speed. A subagent doing a simple task may be slower than the main agent because it starts fresh.
FAQ
What are the built-in subagents?
Cursor includes three built-in subagents: explore for codebase search, bash for running shell commands, and browser for browser automation via MCP. These handle context-heavy operations automatically. You don't need to configure them.
Can subagents launch other subagents?
Yes, within a nesting limit. Since Cursor 2.5, subagents can launch child subagents to create a tree of coordinated work. The main agent and its direct subagents can launch subagents, but a subagent launched by another subagent can't launch further ones. Nested launches also need Task tool access in the current mode, and hooks or tool policies can block spawning.
How do I see what a subagent is doing?
Background subagents write output to ~/.cursor/subagents/. The parent agent can read these files to check progress.
What happens if a subagent fails?
The subagent returns an error status to the parent agent. The parent can retry, resume with additional context, or handle the failure differently.
Can I use MCP tools in subagents?
Yes. Subagents inherit all tools from the parent, including MCP tools from configured servers. Cloud subagents are the exception: they run on a cloud VM and use the MCP servers configured for your team at cursor.com/agents, not the servers from your local session.
How do I debug a misbehaving subagent?
Check the subagent's description and prompt. Ensure the instructions are specific and unambiguous. You can also test the subagent by invoking it explicitly with a simple task.
Why is my subagent using a different model?
Cursor overrides the configured model in three cases: the model is blocked by your team admin, the model requires Max Mode and you don't have it enabled, or the model isn't available on your plan. On legacy request-based plans without Max Mode, subagents run using Composer regardless of any model configuration. If your team admin has blocked Composer, subagents will only be able to run in Max Mode for request-based plans. Usage-based plans and Max Mode will default to the parent model. See model configuration for details.
---