eve 에이전트 확장 패키지 만들기: tools·skills·hooks를 npm으로 배포하기
eve extension으로 도구·연결·SKILL.md·instructions·hooks를 하나의 버전 관리 패키지로 만들고, 네임스페이스·승인·설정·업그레이드를 안전하게 운영하는 방법을 설명합니다.
에이전트를 여러 프로젝트에 배포하다 보면 프롬프트보다 먼저 복제되는 것이 있습니다. 같은 도구 파일, 같은 SKILL.md, 같은 인증 연결과 같은 audit hook입니다. 폴더를 복사해 시작할 수는 있지만, 어느 프로젝트가 최신 버전인지 알기 어렵고 보안 수정도 모든 저장소에 따로 반영해야 합니다.
Vercel은 2026년 7월 22일 eve의 installable extension을 발표했습니다. 이제 tools, connections, skills, instructions, hooks를 하나의 패키지로 묶고 npm처럼 설치·버전 관리·업그레이드할 수 있습니다.
이 기능의 핵심은 재사용 자체보다 경계를 만드는 데 있습니다.
- 확장이 제공하는 능력은 namespace 아래에 들어갑니다.
- 소비자는 설정 schema로 잘못된 구성을 조기에 막을 수 있습니다.
- 위험한 도구는 승인 대상으로 바꾸거나 제거할 수 있습니다.
- 패키지 버전으로 여러 에이전트의 기능을 통제할 수 있습니다.
이 글에서는 CRM 확장을 예로 들어 scaffold부터 mount, 승인 정책과 배포 체크리스트까지 정리합니다.
extension이 해결하는 문제
도구 한두 개만 공유한다면 일반 npm 패키지로도 충분합니다. 그러나 실제 에이전트 기능은 함수 하나보다 넓습니다.
CRM 기능
├── API 인증 connection
├── 고객 검색 tool
├── 고객 메모 쓰기 tool
├── 상담 분류 SKILL.md
├── 공통 instructions
└── 실행 기록 audit hook
이 구성을 일반 라이브러리로 만들면 소비하는 프로젝트가 각 요소를 다시 등록해야 합니다. eve extension은 agent directory와 유사한 파일 규칙을 패키지 경계 안으로 옮깁니다.
| 방식 | 적합한 상황 | 한계 |
|---|---|---|
| 파일 복사 | 빠른 실험, 단일 저장소 | 버전과 보안 수정이 분산됨 |
| 일반 npm 라이브러리 | 순수 함수와 SDK 공유 | skills·hooks·connections 등록은 소비자가 다시 해야 함 |
| MCP 서버 | 언어·런타임을 넘는 원격 도구 공유 | 네트워크·인증·운영 서버가 필요함 |
| eve extension | eve 에이전트 사이에서 능력 묶음 재사용 | eve 런타임과 패키지 계약에 맞춰야 함 |
MCP와 extension은 경쟁 관계라기보다 계층이 다릅니다. extension 안에 MCP connection을 포함해 인증과 도구 사용 지침까지 함께 배포할 수도 있습니다.
확장 scaffold 만들기
공식 CLI는 한 명령으로 extension 프로젝트를 생성합니다.
npx eve@latest extension init crm
생성되는 패키지는 다음과 같은 구조를 가집니다.
@acme/crm/
├── package.json
└── extension/
├── extension.ts
├── tools/
│ ├── search.ts
│ └── add-note.ts
├── connections/
│ └── api.ts
├── skills/
│ └── triage/
│ └── SKILL.md
├── instructions.md
├── hooks/
│ └── audit.ts
└── lib/
└── http.ts
extension.ts는 확장의 설정 계약을 정의하고, 나머지 폴더는 일반 eve agent와 같은 convention으로 능력을 제공합니다.
// extension/extension.ts
import { defineExtension } from "eve/extension";
import { z } from "zod";
export default defineExtension({
config: z.object({
apiBaseUrl: z.string().url(),
readOnly: z.boolean().default(true),
}),
});
설정 schema는 문서용 메타데이터가 아닙니다. 확장을 mount할 때 값이 검증되고, 내부 코드에서도 타입이 유지됩니다. URL 오타나 필수 설정 누락을 에이전트가 실행된 뒤 발견하는 대신 시작 단계에서 차단할 수 있습니다.
도구는 한 가지 책임만 갖게 만든다
확장 도구가 너무 많은 일을 수행하면 소비자가 승인 정책을 세밀하게 적용하기 어렵습니다. 조회와 변경을 분리하는 편이 좋습니다.
// extension/tools/search.ts
import { defineTool } from "eve/tools";
import { z } from "zod";
export default defineTool({
description: "Search CRM contacts by email or name",
inputSchema: z.object({
query: z.string().min(2),
}),
async execute({ query }) {
// 실제 구현에서는 extension config와 connection을 사용합니다.
return {
contacts: [],
query,
};
},
});
// extension/tools/add-note.ts
import { defineTool } from "eve/tools";
import { z } from "zod";
export default defineTool({
description: "Add a note to an existing CRM contact",
inputSchema: z.object({
contactId: z.string(),
note: z.string().min(1).max(2_000),
}),
async execute({ contactId, note }) {
return {
contactId,
noteLength: note.length,
status: "queued",
};
},
});
search는 읽기 도구이고 add-note는 외부 상태를 바꾸는 도구입니다. 두 작업을 한 함수로 묶으면 소비자는 조회까지 매번 승인받거나, 반대로 쓰기 작업을 승인 없이 허용하는 선택을 하게 됩니다.
SKILL.md는 도구 사용법과 판단 기준을 담는다
도구 schema만으로는 언제 검색하고 언제 메모를 작성해야 하는지 설명하기 어렵습니다. extension은 Markdown skill도 함께 제공할 수 있습니다.
---
name: crm-triage
description: 고객 문의를 분류하고 CRM 기록을 찾을 때 사용
---
1. 고객 식별 정보가 부족하면 쓰기 도구를 실행하지 않는다.
2. 먼저 검색 도구로 기존 연락처를 확인한다.
3. 여러 연락처가 일치하면 사용자에게 선택을 요청한다.
4. 메모에는 민감한 인증 정보나 결제 정보를 저장하지 않는다.
5. 외부 상태를 변경하는 도구는 승인 후 실행한다.
이 방식은 Restato Content OS가 GitHub에서 관리하는 SKILL.md와 목적이 비슷합니다. 차이는 배포 단위입니다.
- GitHub의
.agents/skills: ChatGPT·Codex가 저장소 작업 규칙으로 읽는 장기 지침 - eve extension의
skills: 실행 중인 eve agent에 패키지 능력과 함께 mount되는 지침
같은 Markdown 원칙을 재사용할 수 있지만, 어느 런타임에서 읽히는지 명확히 구분해야 합니다.
build와 mount
확장이 준비되면 publish 가능한 패키지를 생성합니다.
eve extension build
소비하는 에이전트는 패키지를 설치하고 agent/extensions/ 아래에서 import합니다.
// agent/extensions/crm.ts
import crm from "@acme/crm";
export default crm({
apiBaseUrl: process.env.CRM_API_BASE_URL!,
readOnly: process.env.NODE_ENV !== "production",
});
파일명 crm.ts가 namespace가 됩니다. 확장의 search 도구는 에이전트에서 crm__search로 노출됩니다.
namespace는 단순한 이름 장식이 아닙니다.
- 여러 확장의
search나create도구 충돌을 막습니다. - 로그에서 어떤 패키지의 도구가 실행됐는지 확인할 수 있습니다.
- 승인과 관측 정책을 확장 단위로 적용하기 쉬워집니다.
- 패키지를 제거했을 때 사라지는 능력의 범위가 명확합니다.
소비자가 위험한 도구를 통제해야 한다
확장 제작자가 안전한 기본값을 제공해도 최종 권한은 소비하는 에이전트가 결정해야 합니다. 공식 발표에 따르면 소비자는 extension tool을 승인 대상으로 바꾸거나, 자체 구현으로 교체하거나, disableTool()로 제거할 수 있습니다.
운영 정책은 다음 순서로 설계하는 편이 좋습니다.
- 읽기 전용 도구만 기본 활성화
- 쓰기 도구는 approval 필수
- 삭제·구매·배포 도구는 필요하지 않으면 비활성화
- 입력과 출력은 audit hook에서 민감 정보 제거 후 기록
- extension 업그레이드 시 새 도구가 추가됐는지 diff 확인
특히 wildcard로 최신 버전을 자동 설치하기보다 lockfile을 커밋하고, 의존성 업데이트 Pull Request에서 도구·connection·hook 변경을 검토하는 편이 안전합니다.
config와 secret을 분리한다
extension config에 API key 값을 직접 저장하거나 패키지 기본값으로 넣으면 안 됩니다. 설정은 동작 선택에 사용하고 secret은 환경 또는 connection 계층에서 주입합니다.
// 좋은 경계의 예
export default crm({
apiBaseUrl: "https://crm.example.com",
readOnly: true,
});
// API key는 process.env 또는 connection이 관리합니다.
확장이 외부 SaaS에 연결된다면 다음 항목을 문서화해야 합니다.
- 필요한 환경 변수 또는 connection
- 최소 권한 scope
- 사용자별 인증인지 앱 공용 인증인지
- 네트워크 허용 도메인
- 데이터 보존과 logging 범위
- 승인 없이 실행 가능한 도구 목록
설치 명령만 있고 권한 표가 없는 확장은 운영 환경에서 사용하기 어렵습니다.
실제 extension 사례에서 볼 수 있는 패턴
@agent-browser/eve는 browser automation 도구 묶음을 eve extension으로 제공합니다. 소비자는 agent/extensions/browser.ts에서 패키지를 mount하고, allowedDomains 같은 설정으로 접근 범위를 제한합니다.
// agent/extensions/browser.ts
import browser from "@agent-browser/eve";
export default browser({
allowedDomains: ["docs.example.com", "*.example.com"],
maxOutputChars: 50_000,
});
에이전트에는 browser__navigate, browser__snapshot, browser__click처럼 namespace가 붙은 도구가 제공됩니다. 이 사례가 보여주는 중요한 점은 extension이 단순한 코드 묶음이 아니라 sandbox 실행 방식, 네트워크 경계, 출력 제한까지 포함하는 운영 계약이라는 것입니다.
테스트 전략
extension은 독립 패키지와 소비 에이전트 양쪽에서 테스트해야 합니다.
패키지 테스트
- config schema가 잘못된 값을 거부하는가
- 각 tool의 입력 schema와 오류 처리가 동작하는가
- hook이 민감한 값을 제거하는가
- SKILL.md와 instructions가 빌드 결과에 포함되는가
- build 결과에 의도하지 않은 소스와 secret이 들어가지 않는가
소비 에이전트 테스트
- namespace가 예상대로 생성되는가
- 쓰기 도구에 approval이 적용되는가
- 비활성화한 도구가 모델에 노출되지 않는가
- connection 권한이 최소 범위인가
- 이전 버전에서 새 버전으로 올렸을 때 기존 세션이 깨지지 않는가
버전 업그레이드는 기능 테스트만으로 끝내지 말고 모델 행동도 평가해야 합니다. skill과 instructions의 변경은 TypeScript 타입 오류를 만들지 않아도 도구 선택 순서를 바꿀 수 있습니다.
package version을 어떻게 올릴까
권장 기준은 다음과 같습니다.
| 변경 | 버전 판단 |
|---|---|
| 설명 보완, 내부 버그 수정 | patch |
| 하위 호환 도구·skill 추가 | minor |
| tool 이름·입력 schema 변경 | major |
| 기본 approval 완화 | major에 준하는 보안 검토 |
| connection scope 확대 | 명시적 release note와 재승인 필요 |
새 도구 추가가 코드 관점에서는 minor라도 에이전트의 권한 면적은 넓어집니다. 소비자가 새 능력을 자동 승인하지 않도록 changelog에 권한 변화를 별도로 표시하는 편이 좋습니다.
extension 발행 전 체크리스트
- extension의 단일 책임을 한 문장으로 설명할 수 있다.
- 조회와 변경 도구가 분리되어 있다.
- config schema가 필수 값과 안전한 기본값을 검증한다.
- secret은 package config에 포함되지 않는다.
- tool namespace와 예상 이름을 문서화했다.
- 쓰기·삭제·구매 작업에 approval 정책이 있다.
- 소비자가 불필요한 도구를 제거할 수 있다.
- hook 로그에서 민감 정보가 제거된다.
- build artifact와 package contents를 확인했다.
- 버전별 권한 변화와 breaking change를 기록했다.
- 최소 한 개의 소비 에이전트에서 통합 테스트했다.
결론
eve extension의 가장 큰 가치는 도구를 쉽게 복사하는 것이 아닙니다. 도구, 인증, skill, hook과 권한 정책을 하나의 버전 관리 가능한 능력 단위로 만든다는 점이 중요합니다.
처음부터 모든 기능을 하나의 거대한 extension에 넣지 마세요. CRM 조회, browser automation, 배포 운영처럼 권한과 변경 주기가 같은 기능을 작은 패키지로 분리하고, 소비 에이전트가 필요한 것만 mount하는 편이 안전합니다.
GitHub에서 에이전트의 장기 규칙과 발행 기억을 관리하는 방식은 Content OS 구축기에서, 여러 agent harness의 실행 경계를 비교하는 방법은 AI SDK 7 프로덕션 에이전트 가이드에서 이어서 볼 수 있습니다.