[DEVELOP]
- [DEVELOP] DDD, TDD, BDD
- [DEVELOP] 개인정보 보호 웹사이트 구축을 위한
- [DEVELOP] 예제로 이해하는 웹 접근성 (accessibility)
- [DEVELOP] 예제로 보는 이미지 사용법 (Images)
- [DEVELOP] 예제로 보는 반응형 디자인 사용법 (Responsive Design)
- [DEVELOP] PWA 이해하기 (Progressive Web App)
- [DEVELOP] 개발 프로세스 Agile / Waterfall 이란?
- [DEVELOP] 주니어 개발자의 역습
- [DEVELOP] MCP(Model Context Protocol)
- [DEVELOP] MCP claude 적용하고 사용해보기
- [DEVELOP] 실시간 통신 방식 비교 (HTTP, SSE, WebSocket, stdio)
- [DEVELOP] 클로드 코드 50만 줄 소스코드 유출 사건 분석
- [DEVELOP] Claude Code ‘컨텍스트 로트’ 현상과 비용 80% 절감법
- [DEVELOP] 에이전트 하네스 엔지니어링이란? — AI 에이전트를 제대로 다루는 기술
- [DEVELOP] Turborepo 캐시로 CI/CD 빌드 시간을 90% 줄이는 법
- [DEVELOP] 모노레포 빌드 10분 → 2분, Turborepo 캐시와 CI/CD 최적화 실전기
- [DEVELOP] Cursor Agent로 PR 자동 리뷰 시스템 만들기 — Skill 기반 워크플로우 설계
- [DEVELOP] Claude Fable 5·Mythos 5 전면 중단 — 출시 3일 만에 꺼진 AI
- [DEVELOP] 실무 모노레포에 에이전트 하네스를 깔고 운영해본 기록
- [DEVELOP] Chrome DevTools에 생긴 WebMCP — 웹사이트가 에이전트에게 도구를 넘기는 법
- [DEVELOP] Cursor Plugin — .cursor를 패키지로 배포하는 공식 방법
프로젝트
.cursor를 npm 패키지로 올리고 싶었다. Cursor는node_modules를 설정으로 읽지 않는다. 공식 배포 단위는 Plugin이다. 이 글은 Plugins reference를 기준으로, 문서에만 있고 평소.cursor에는 안 보이던homepage,repository,hooks까지 한국어로 정리한 노트다.
원문: cursor.com/docs/reference/plugins
템플릿: github.com/cursor/plugin-template
한 줄 요약
.cursor/rules, .cursor/skills, .cursor/mcp.json은 이 레포에서만 먹힌다. 여러 레포에 같은 규칙·스킬·MCP를 뿌리고 싶으면 폴더를 npm에 넣는 게 아니라, Cursor가 인식하는 플러그인 디렉터리로 묶는다.
플러그인은 rules, skills, agents, commands, MCP, hooks를 한 묶음으로 배포하는 패키지다. VS Code extension과 비슷한 역할이지만, 런타임은 에디터 UI가 아니라 에이전트 행동이다.
포맷이 두 개다
Cursor는 매니페스트 위치로 포맷을 구분한다.
| 포맷 | 매니페스트 | 실어 나를 수 있는 것 |
|---|---|---|
| Agent Plugins (공개 표준) | 루트 plugin.json |
Skills, MCP |
| Cursor Plugins | .cursor-plugin/plugin.json |
Skills, MCP, rules, agents, commands, hooks, variables |
Agent Plugins 스펙을 지키면 Cursor에서도 로드된다. 다만 mcp.json의 ${PLUGIN_ROOT}, ${PLUGIN_DATA}는 Cursor가 치환하지 않는다. 플러그인 설치 경로는 ${CURSOR_PLUGIN_ROOT}(또는 ${CLAUDE_PLUGIN_ROOT})를 쓴다.
팀이 쓰는 코딩 규칙(.mdc)까지 같이 배포하려면 Cursor Plugin이 맞다. 스킬+MCP만 다른 도구와 공유하려면 Agent Plugin도 후보가 된다.
Agent Plugin 구조
1
2
3
4
5
6
my-plugin/
├── plugin.json # 필수
├── skills/
│ └── code-reviewer/
│ └── SKILL.md
└── mcp.json
Cursor Plugin 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
my-plugin/
├── .cursor-plugin/
│ └── plugin.json # 필수
├── rules/
│ ├── coding-standards.mdc
│ └── review-checklist.mdc
├── skills/
│ └── code-reviewer/
│ └── SKILL.md
├── agents/
│ └── security-reviewer.md
├── commands/
│ └── deploy.md
├── hooks/
│ └── hooks.json
├── mcp.json
├── assets/
│ └── logo.svg
├── scripts/
│ └── format-code.py
└── README.md
프로젝트 .cursor와 거의 같다. 차이는 루트에 .cursor-plugin/plugin.json이 생기고, 이 레포에서만 쓰는 로그·시크릿·조직 고유 값은 빼는 점이다.
plugin.json — 필수와 선택
필수 필드는 name 하나다. 소문자 kebab-case, 영숫자·하이픈·점. 처음과 끝은 영숫자. 예: my-plugin, prompts.chat.
나머지 필드는 두 덩어리다.
- 마켓플레이스 메타데이터 — 사람이 읽고, 검색하고, 링크로 가는 값
- 컴포넌트 경로 — Cursor가 실제로 로드하는 파일 위치
메타데이터: homepage, repository는 “기능”이 아니다
문서에 homepage, repository가 있어서 뭔가 런타임 훅이 있을 것처럼 보이지만, npm의 package.json과 같다. 플러그인이 동작하는 방식은 바꾸지 않는다.
| 필드 | 타입 | 하는 일 |
|---|---|---|
description |
string | 한 줄 설명 |
version |
string | semver (1.0.0) |
author |
object | name 필수, email 선택 |
homepage |
string | 문서/소개 사이트 URL. 마켓 카드에서 “홈페이지로” 링크 |
repository |
string | 소스 Git URL. 이슈·PR·코드 보러 가는 링크 |
license |
string | MIT 같은 SPDX |
keywords |
array | 검색·분류 태그 |
logo |
string | 로고. 상대경로 권장 (assets/logo.svg) |
homepage와 repository를 헷갈리면 이렇게 나누면 된다.
repository: 이 플러그인 코드가 있는 곳homepage: 설치 가이드, 팀 위키, 랜딩 페이지처럼 사람이 읽는 곳
로고 상대경로는 GitHub raw.githubusercontent.com으로 풀린다. 예: acme/plugins 커밋 abc123의 assets/logo.svg →
1
https://raw.githubusercontent.com/acme/plugins/abc123/my-plugin/assets/logo.svg
절대 URL도 된다. 시크릿은 여기 넣지 않는다.
컴포넌트 경로: 폴더를 안 쓰면 기본 위치를 스캔한다
| 필드 | 타입 | 의미 |
|---|---|---|
rules |
string 또는 배열 | 룰 파일/디렉터리 |
agents |
string 또는 배열 | 에이전트 정의 |
skills |
string 또는 배열 | 스킬 디렉터리 |
commands |
string 또는 배열 | 커맨드 파일 |
hooks |
string 또는 object | hooks 설정 경로이거나 인라인 JSON |
mcpServers |
string, object, 배열 | MCP 설정. 있으면 기본 mcp.json 탐색을 덮어씀 |
variables |
object | 시크릿 이름만 선언하는 JSON Schema |
매니페스트에 skills를 안 쓰면 skills/ 아래 SKILL.md가 있는 폴더를 자동 탐색한다. 한 번 필드를 쓰면 그 경로만 보고 기본 폴더는 안 본다. "skills": "./my-skills/"이면 루트 skills/는 무시된다.
기본 탐색:
| 컴포넌트 | 기본 위치 | 찾는 방법 |
|---|---|---|
| Skills | skills/ |
SKILL.md가 있는 하위 폴더 |
| Rules | rules/ |
.md, .mdc, .markdown |
| Agents | agents/ |
위와 같음 |
| Commands | commands/ |
위 + .txt |
| Hooks | hooks/hooks.json |
이벤트 이름 파싱 |
| MCP | mcp.json |
서버 엔트리 |
| Root Skill | 루트 SKILL.md |
skills/도 없고 매니페스트 skills도 없을 때만 단일 스킬 플러그인 |
예시 매니페스트:
1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "enterprise-plugin",
"version": "1.2.0",
"description": "Enterprise development tools with security scanning and compliance checks",
"author": {
"name": "ACME DevTools",
"email": "devtools@acme.com"
},
"homepage": "https://docs.example.com/cursor-plugin",
"repository": "https://github.com/acme/cursor-plugins",
"keywords": ["enterprise", "security", "compliance"],
"logo": "assets/logo.svg"
}
homepage/repository가 없어도 플러그인은 동작한다. 마켓에 올릴 때 사람용 링크가 비는 것뿐이다.
Variables — 시크릿은 레포에 안 넣는다
variables는 API 토큰 같은 값이 아니라 이름과 스키마다. 실제 값은 대시보드 Plugins → Configure에서 팀 관리자가 넣는다.
플러그인 설정에는 ${VAR}만 둔다. 셸의 ${env:...}가 아니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "example-plugin",
"variables": {
"type": "object",
"properties": {
"API_TOKEN": {
"type": "string",
"title": "API token",
"description": "Bearer token for the example HTTP MCP"
}
},
"required": ["API_TOKEN"]
}
}
1
2
3
4
5
6
7
8
9
10
{
"mcpServers": {
"example-api": {
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
}
}
}
}
최상위는 { "type": "object", "properties": { ... } }여야 한다. 허용 키워드가 고정돼 있다: type, title, description, default, enum, const, properties, required, items, 길이·숫자 제약 일부.
플러그인이 관리하는 MCP 설정은 대시보드에서 읽기 전용이다. 토큰을 바꾸려면 Configure의 변수만 바꾼다.
Rules / Skills / Agents / Commands
프로젝트 .cursor와 파일 형식이 같다. 플러그인은 그걸 설치 가능한 단위로 옮긴 것이다.
Rules — rules/*.mdc. 프론트매터: description, alwaysApply, globs.
Skills — skills/<name>/SKILL.md. name, description. 에이전트가 “언제 이 워크플로를 쓸지” 고르는 문장이다.
Agents — agents/*.md. 커스텀 에이전트 페르소나. 예: 보안 리뷰만 하는 리뷰어.
Commands — commands/*.md 등. 에이전트가 실행하는 고정 액션 설명. “스테이징 배포”처럼 단계가 정해진 작업.
자세한 필드 설명은 원문과 Rules, Skills 문서를 보면 된다. 이 글에서는 플러그인에만 있는 hooks를 길게 본다.
Hooks — 플러그인에서 제일 낯선 기능
룰은 프롬프트다. 스킬은 워크플로 문서다. Hooks는 프로세스다.
에이전트 루프의 특정 시점에 스크립트를 띄운다. stdio로 JSON을 주고받는다. 관찰만 할 수도 있고, 위험한 명령을 막을 수도 있고, 파일 저장 후 포맷터를 돌릴 수도 있다.
정의 위치는 세 곳이다.
- 프로젝트:
.cursor/hooks.json - 유저:
~/.cursor/hooks.json - 플러그인:
hooks/hooks.json또는 매니페스트hooks인라인
플러그인 쪽 예:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"hooks": {
"afterFileEdit": [
{
"command": "./scripts/format-code.sh"
}
],
"beforeShellExecution": [
{
"command": "./scripts/validate-shell.sh",
"matcher": "rm|curl|wget"
}
],
"sessionEnd": [
{
"command": "./scripts/audit.sh"
}
]
}
}
matcher는 그 이벤트 중 일부만 거르는 필터다. 위 예는 셸에 rm, curl, wget이 있을 때만 검증 스크립트를 탄다.
이벤트 세 종류
Agent (채팅/에이전트 세션)
| 이벤트 | 언제 |
|---|---|
sessionStart / sessionEnd |
세션 시작·끝. 컨텍스트 주입, 감사 로그 |
preToolUse / postToolUse / postToolUseFailure |
도구 호출 전후. 모든 툴 |
subagentStart / subagentStop |
Task 서브에이전트 생명주기 |
beforeShellExecution / afterShellExecution |
셸 명령 게이트 |
beforeMCPExecution / afterMCPExecution |
MCP 도구 게이트 |
beforeReadFile / afterFileEdit |
파일 읽기·수정 |
beforeSubmitPrompt |
프롬프트 제출 전 검증 |
preCompact |
컨텍스트 압축 직전 |
stop |
에이전트 종료 |
afterAgentResponse / afterAgentThought |
응답·사고 추적 |
Tab (인라인 자동완성)
beforeTabFileReadafterTabFileEdit
에이전트와 Tab을 나눈 이유: Tab은 사용자 확인 없이 파일을 만진다. 정책을 다르게 걸 수 있다.
앱 수명
workspaceOpen— 워크스페이스를 열거나 폴더가 바뀔 때. 현재 워크스페이스용 추가 플러그인 경로를 돌려줄 수 있다.
실무에서 바로 쓸 만한 조합:
afterFileEdit→ prettier / eslint –fixbeforeShellExecution→rm -rf, DB write, 프로덕션 curl 차단sessionStart→ 팀 공지, 브랜치 이름, 금지 경로를 컨텍스트로 주입beforeReadFile→ 시크릿 파일 읽기 거부
상세 프로토콜은 Hooks 문서가 길다. 플러그인 레퍼런스는 “hooks.json을 플러그인에 실을 수 있다”는 선언이고, 동작 명세는 그쪽이다.
Cloud Agent는 프로젝트 .cursor/hooks.json은 가져가지만, ~/.cursor/hooks.json은 홈 디렉터리가 없어서 안 탄다. sessionStart/Tab/workspaceOpen 일부는 클라우드에서 빠진다. 팀 공용 가드는 플러그인 또는 레포 hooks에 넣는 편이 맞다.
MCP를 플러그인에 실을 때
두 포맷 모두 플러그인 루트 mcp.json을 쓴다.
Cursor Plugin은 command 또는 url로 트랜스포트를 추론한다. 경로를 커스텀하거나 인라인으로 넣으려면 매니페스트 mcpServers를 쓴다. 그 필드를 쓰는 순간 기본 mcp.json 탐색은 덮인다.
1
2
3
4
5
6
7
8
9
10
11
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres"],
"env": {
"POSTGRES_CONNECTION_STRING": "${POSTGRES_URL}"
}
}
}
}
${POSTGRES_URL}은 위에서 말한 plugin variable이다. plugin.json의 variables에 이름을 선언하고, 값은 대시보드에서 넣는다.
Agent Plugin 예:
1
2
3
4
5
6
7
8
9
10
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"code-review": {
"type": "stdio",
"command": "./bin/code-review",
"cwd": "${CURSOR_PLUGIN_ROOT}"
}
}
}
${CURSOR_PLUGIN_ROOT}는 command, args, env, cwd에서 플러그인 설치 경로로 치환된다.
한 레포에 플러그인 여러 개 — marketplace.json
팀 플러그인을 레포 하나에 모을 수 있다. 루트 .cursor-plugin/marketplace.json이 목록이다. 파일 전체 10MB 제한.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"name": "my-marketplace",
"owner": {
"name": "Your Org",
"email": "plugins@yourorg.com"
},
"metadata": {
"description": "A collection of developer tool plugins"
},
"plugins": [
{
"name": "plugin-one",
"source": "plugin-one",
"description": "First plugin"
},
{
"name": "plugin-two",
"source": "plugin-two",
"description": "Second plugin"
}
]
}
필수: name, owner, plugins.
metadata.pluginRoot는 모든 source 앞에 붙는 접두 경로다.
엔트리에도 homepage, repository가 다시 나온다. 플러그인 단위 링크다. plugin.json과 마켓 엔트리가 둘 다 있으면 마켓 엔트리가 이긴다. variables도 같다. 값은 plugin.json에 두고, 덮어쓸 일 있을 때만 마켓 엔트리에 적는 게 덜 헷갈린다.
source: "my-plugin"이면:
my-plugin/.cursor-plugin/plugin.json을 찾는다- 있으면 마켓 엔트리와 머지 (엔트리 우선)
my-plugin/안에서 컴포넌트 탐색
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
my-plugins/
├── .cursor-plugin/
│ └── marketplace.json
├── eslint-rules/
│ ├── .cursor-plugin/
│ │ └── plugin.json
│ └── rules/
│ ├── prefer-const.mdc
│ └── no-any.mdc
├── docker/
│ ├── .cursor-plugin/
│ │ └── plugin.json
│ ├── skills/
│ │ └── containerize-app/
│ │ └── SKILL.md
│ └── mcp.json
└── README.md
룰만 있는 플러그인과 스킬+MCP 플러그인을 한 마켓에서 나눠 설치할 수 있다.
마켓에 제출
Cursor 팀이 리뷰한다.
- 루트
plugin.json또는.cursor-plugin/plugin.json - 공개 Git 레포에 푸시. 로고는 커밋하는 쪽이 안전하다
- cursor.com/marketplace/publish에 레포 URL
체크리스트에서 자주 빠질 것:
name은 전역에서 유일해야 한다- 매니페스트 경로는 상대경로만.
..와 절대경로 금지 mcp.json의${VAR}는 전부variables에 선언- 멀티 플러그인이면 루트
marketplace.json과 플러그인 이름 중복 없음 - 로컬에서 한 번 로드해 볼 것
사내만 쓸 거면 퍼블릭 마켓이 필수는 아니다. Git에 플러그인 레포를 두고 팀 설치로도 된다. 공식 제출은 “남들이 검색해서 깔게”일 때다.
.cursor를 옮길 때 뭐를 빼나
플러그인은 여러 레포에서 재사용할 행동이다. 한 프로젝트의 이력·시크릿·로컬 취향은 그대로 둔다.
| 넣어도 됨 | 빼는 편이 나음 |
|---|---|
| 공통 코딩 룰 | 에이전트 작업 로그, 로컬 캐시 |
스킬 워크플로 (SKILL.md, scripts) |
그 레포 아키텍처만 아는 컨텍스트 문서 |
MCP 서버 정의 + variables |
토큰, org slug를 파일에 하드코딩 |
| hooks (포맷, 위험 명령 가드) | 개인 ~/.cursor 취향 |
한 서비스 레포만 쓰면 지금처럼 .cursor를 커밋하는 게 제일 단순하다. 웹·앱·서버에 같은 룰을 깔 때부터 플러그인이 이득이다.
npm postinstall로 .cursor에 복사하는 우회는 여전히 가능하다. Cursor가 그 패키지를 플러그인으로 인식하지는 않는다.