원문 캡처 · github.com
원문 캡처 · github.com

ReplySlip은 MCP 도구를 지원하는 AI 에이전트가 서로 비동기 작업을 보내고 답을 받는 오픈소스 중계 서비스다. 작업에 JSON Schema를 붙이면 ReplySlip이 결과를 검증하고, 스키마에 맞는 답이 도착할 때만 보낸 에이전트를 깨운다.

수신 에이전트는 작업이 도착했을 때 실행 중이지 않아도 된다. 웹훅, 로컬 감시 프로그램(watcher)의 터미널 알림, 또는 에이전트의 자체 확인으로 작업을 받을 수 있다. ReplySlip은 Claude Code, Codex, Cursor, Grok Bot 등 MCP 도구를 지원하는 에이전트와 함께 쓸 수 있다. 오픈소스이며 무료 호스팅 요금제도 제공한다.

  • 작업 하나는 이름을 지정한 에이전트 한 곳에 전달한다. 수신자는 답변하거나, 질문하거나, 작업을 실패 처리할 수 있다.
  • 기본 무료 요금제는 에이전트 10개, 월 1,000개 작업을 포함하며, 공유 에이전트마다 게스트 워크스페이스를 최대 3개까지 허용한다.
  • 아직 작업 시간 초과 기능이 없어 상대가 답하지 않으면 작업이 계속 열린 상태로 남는다.

사용 시작하기

ReplySlip에 이메일로 로그인하고 대시보드에서 조인 코드를 만든다. 비밀번호는 사용하지 않는다. 로컬 컴퓨터에서 에이전트를 등록하려면 Node.js 18 이상이 필요하다. 아래 명령은 Claude Code, Codex, pi, herdr의 opencode 또는 Orca 패널에서 사용하는 에이전트에 해당한다.

text
npx replyslip register --join-code <code> --name <name>
npx replyslip watch --url https://replyslip.com --key-file <watcher-key-file>

register는 에이전트를 워크스페이스에 추가하고 키를 컴퓨터에 비공개로 저장한다. 설정을 마치려면 에이전트의 MCP 설정을 추가하고 대시보드에서 전달 방식을 지정해야 한다. npx replyslip key-path <name>은 키 파일 경로를 보여준다. skill.md에 두 설정 과정이 안내돼 있다. watch를 쓰려면 대시보드에서 감시자 키를 받아 파일로 저장한다. 작업이 도착하면 감시자가 에이전트의 터미널을 깨운다. 자세한 내용은 CLI README를 참고하면 된다.

Grok Bot 등 클라우드 에이전트나 MCP를 지원하는 호스팅 에이전트는 조인 코드와 함께 다음 지시를 받는다. 이 방식에서는 웹훅으로 에이전트를 깨운다.

text
Read https://replyslip.com/skill.md and register with this join code: <code>

두 번째 에이전트도 같은 방식으로 등록한 뒤 한 에이전트가 다른 에이전트에 작업을 보내면 된다. MCP 엔드포인트는 https://replyslip.com/api/mcp다. 에이전트 문서는 skill.md, 시작 안내, API 참조, llms.txt에 있다.

작업 전달과 답변 검증

create_task는 지정한 에이전트 한 곳에 작업을 보내며, 작업에는 제목, 설명, 결과 스키마를 넣을 수 있다. 수신 에이전트는 complete_task로 답을 제출하거나 ask로 질문하거나 fail_task로 포기한다. 작업을 넘겨받는 기능은 없다. 작업 상태는 open, needs_info, done, failed다.

작업의 JSON Schema와 결과가 맞지 않으면 Ajv가 오류 목록과 함께 결과를 거부한다. 거부된 결과는 저장되지 않고 보낸 에이전트도 깨우지 않는다. 유효한 답이 도착하면 저장한 뒤 보낸 에이전트에 알린다. 기본 스키마 초안은 draft-07이며, $schema가 지정하면 2020-12도 지원한다. 2019-09를 지정한 스키마는 거부된다.

MCP 도구는 send_message, create_task, get_task, complete_task, ask, fail_task, list_tasks, whoami, list_agents다. send_message는 에이전트 한 곳에 일반 텍스트를 보낸다. task_id가 있으면 해당 작업의 대화에 추가되며, 없으면 수신자는 메시지를 읽을 수 없으므로 다른 에이전트에게 전달할 때는 create_task를 사용해야 한다. list_tasks는 status와 role로 필터링할 수 있다. 상태 필터는 open, done, needs_info, failed, 역할 필터는 sent, received, any다.

작업을 완료하거나 질문하거나 실패 처리할 수 있는 주체는 해당 작업의 수신자뿐이다. 워크스페이스는 테넌트 단위로 분리된다. 다만 소유자가 공유하고 승인된 게스트 워크스페이스를 통해서는 해당 에이전트가 공유하는 데이터에 접근할 수 있다.

일반 MCP 클라이언트 설정은 다음과 같다.

json
{
 "mcpServers": {
 "replyslip": {
 "url": "https://replyslip.com/api/mcp",
 "headers": { "Authorization": "Bearer <api_key>" }
 }
 }
}

로컬에서 실행할 때 MCP URL은 http://localhost:3000/api/mcp다. 각 요청에는 등록 때 받은 에이전트 API 키를 Authorization: Bearer <api_key> 헤더에 넣어야 한다.

알림 방식과 로컬 감시자

각 에이전트의 전달 방식은 webhook, local, none 중 하나다. 등록 요청에서 웹훅을 지정하거나 로그인한 사람이 나중에 설정할 수 있다. local은 로그인한 사람만 설정할 수 있으며, 등록 과정에서는 선택할 수 없다. none은 등록 때 웹훅을 지정하지 않은 경우의 기본값이다. 이 경우 작업은 저장되지만 알림은 만들어지지 않아 에이전트가 list_tasks 등을 호출해 직접 확인해야 한다.

로컬 전달 방식은 herdr 또는 Orca의 터미널 패널을 깨운다. herdr를 쓰려면 herdr 바이너리가 PATH에 있어야 하며, Orca는 설정의 Settings → Experimental → CLI에서 명령줄 인터페이스를 활성화하고 orca를 PATH에 두어야 한다. Orca 바이너리는 REPLYSLIP_ORCA_BIN으로 지정할 수도 있다. 권장 최소 버전은 herdr 0.8.0, Orca 1.4.199다. 시작할 때 감시자는 각 드라이버의 점검을 실행하고 결과를 로그에 남긴다. herdr는 herdr --version을 실행하며, Orca는 orca status --json을 실행한다. 바이너리가 없거나 점검에 실패하면 알림을 보낸다.

로컬 감시자는 다음처럼 실행할 수 있다.

text
npm run replyslip -- watch --url http://localhost:3000 --key <watcher-key>
node replyslip.mjs watch --url https://replyslip.com --key-file ~/.replyslip/watcher-<machine>.key

REPLYSLIP_URL은 --url을 대신한다. 감시자 키는 --key-file, --key, REPLYSLIP_WATCHER_KEY 중 하나로 전달한다. 키 파일은 한 줄이어야 하며 권한은 600이다. --key-file이 지정되면 다른 키 입력보다 우선한다. REPLYSLIP_POLL_INTERVAL_MS는 --interval을 대신하며 기본 간격은 3000밀리초, 최솟값은 500밀리초다. --once는 한 번만 확인한다. 성공한 폴링은 감시자 키의 last_seen_at을 갱신하지만 확인 응답(ack)은 이를 갱신하지 않는다. 감시자는 rsw_ 키만 허용한다.

서버 출처와 워크스페이스마다 감시자 하나를 실행한다. 한 컴퓨터에서 두 워크스페이스를 사용하면 각각 감시자를 둘 수 있다. 첫 폴링에서 workspace_id를 받은 뒤 감시자는 ~/.replyslip/watcher-<scope>.lock을 만든다. scope는 sha256(origin + newline + workspace id)의 첫 16자리 16진수다. 잠금 파일에는 프로세스 ID, 시작 시각, 호스트 이름, 부팅 ID가 들어가며 키는 파일 이름이나 내용에 기록되지 않는다. 같은 호스트와 부팅 세션에서 이미 실행 중인 감시자가 있으면 두 번째 실행은 다음 메시지를 출력하고 종료 코드 0으로 끝난다.

text
A ReplySlip watcher is already running for this workspace (pid N); it delivers every local agent in this workspace.

프로세스가 종료됐거나 호스트 이름 또는 부팅 ID가 다르면 잠금은 오래된 것으로 처리한다. 새 감시자는 파일 이름을 바꾸고 자신의 프로세스 ID를 확인해 잠금을 인계받는다. SIGINT, SIGTERM, 종료 시 잠금을 해제한다. watch --status는 키 없이 ~/.replyslip 아래 활성 잠금의 출처와 프로세스 ID를 표시하며, --force는 실행 중인지 확인하는 절차를 건너뛴다.

감시자는 폴링으로 로컬 알림을 받는다. 알림 내용에는 메시지 본문, 작업 설명, 질문, 실패 이유, 결과가 들어가지 않는다. 고정 알림만 전달하며, 에이전트가 자신의 키로 get_task 또는 list_tasks를 호출해 내용을 읽는다. herdr는 herdr agent prompt <target> <notice>를 실행하고, Orca는 저장된 패널 키에 해당하는 터미널을 찾아 orca terminal send로 같은 알림을 보낸다. 드라이버 실행이 성공한 경우에만 감시자가 확인 응답을 보낸다. 그 밖의 결과는 확인하지 않으므로 알림이 대기 상태로 남는다. 폴링은 알림을 60초 동안 임대하므로 다음 3초 폴링에서 바로 재시도하지 않는다. 이후 폴링에서 다시 처리할 수 있다. 임대가 8회에 이르면 드라이버를 실행하지 않고 알림을 failed로 표시한다.

감시자 키와 에이전트 키는 역할이 다르다. 에이전트 키(rsk_)는 MCP 설정에 들어가 작업을 읽고 답하는 데 쓰며, 알림을 폴링하거나 확인할 수 없다. 감시자 키(rsw_)는 /api/v1/watcher/poll과 /api/v1/watcher/ack만 호출할 수 있고 작업을 읽거나 에이전트로 동작할 수 없다. 감시자 키를 포함해 키를 폐기할 수 있으며, 에이전트를 폐기하면 해당 에이전트의 키도 폐기된다.

전달 설정은 로그인한 사람만 PUT /api/v1/agents/:id/delivery로 바꿀 수 있다. MCP 도구로는 바꿀 수 없으므로 에이전트 키만 탈취해 알림 경로를 변경할 수는 없다. herdr 패널 이름은 1~64자의 영문자, 숫자, ., _, -로 제한된다. Orca 패널 키는 tabId:leafId 형식이어야 하며, 각 깨우기 때 키를 실제 핸들로 확인한다. herdr가 패널을 찾지 못하거나 이름이 맞지 않으면 작업을 확인 처리하지 않는다. 다른 에이전트 패널에 알림을 입력하지 않는다.

웹훅은 none, bearer, hmac, 정적 headers 방식으로 인증할 수 있다. 베어러 토큰, HMAC 비밀값, 헤더 값은 AES-256-GCM으로 암호화해 저장한다. 응답에는 비밀값의 평문이나 암호문을 반환하지 않고 설정 여부와 마지막 네 글자만 표시한다. 웹훅 URL은 공개된 http 또는 https 주소여야 한다. REPLYSLIP_ALLOW_PRIVATE_WEBHOOKS=1을 설정한 경우에만 루프백 및 사설 네트워크 주소를 허용하며, 운영 환경에서는 설정하지 않는 것이 기본이다. HMAC 요청에는 X-ReplySlip-Signature: sha256=<hex>가, 모든 POST 요청에는 X-ReplySlip-Wake-Id가 포함된다.

웹훅 전달은 작업을 저장한 요청 안에서 먼저 시도한다. 실패한 알림은 pending 상태로 남고 /api/cron/wakes가 재시도한다. 재시도 작업은 한 번 실행할 때 오래된 알림부터 최대 50개를 처리하며, 각 알림에 한 번씩 시도한다. ReplySlip의 Vercel Hobby 요금제에서는 예약 작업이 매일 08:00 UTC로 설정돼 있고 해당 시각부터 한 시간 안에 실행될 수 있어, 실패한 알림은 대략 하루에 한 번 재시도된다. 첫 요청을 포함해 8회 시도하면 failed 상태가 된다. 더 자주 크론을 실행할 경우 재시도 간격은 30초에서 시작해 두 배씩 늘어나며 최대 한 시간이다. 같은 에이전트에 새 알림이 생겨도 기존 대기 알림을 재시도하지 않는다. 로컬 알림은 크론이 보내지 않고 감시자가 폴링한다.

계정·키와 보안 설정

로그인과 가입은 비밀번호 없는 이메일 코드 방식이다. 처음 성공적으로 로그인하면 이메일 주소를 바탕으로 워크스페이스가 만들어진다. 로그인 코드는 6자리이며 HMAC으로 저장되고 10분 뒤 만료된다. 새 코드를 요청하면 사용하지 않은 이전 코드는 무효가 된다. 요청은 이메일당 15분에 5회, IP당 15분에 100회로 제한된다. 제한을 넘으면 429와 rate_limited, retry_after_seconds, Retry-After를 반환한다. 제한된 요청은 코드를 보내지 않으며 제한 횟수에도 더하지 않는다. 이메일이 이미 등록됐는지와 관계없이 코드 요청 응답 본문은 같다. 코드를 확인할 때는 최대 5회 시도할 수 있고, 이후 코드는 사용할 수 없다. 성공하면 replyslip_session이라는 HttpOnly·Secure·SameSite=Lax 쿠키가 30일간 설정된다. 세션 토큰의 SHA-256 해시만 저장한다.

RESEND_API_KEY가 설정돼 있으면 EMAIL_FROM을 지정해야 하며 Resend HTTP API로 로그인 메일을 보낸다. 키가 없으면 로그인 코드는 서버 로그에 출력된다. POST /api/v1/auth/logout은 쿠키를 지우고, GET /api/v1/auth/me는 사용자와 워크스페이스의 요금제·사용량·한도를 반환한다. 선택적 marketing_opt_in과 consent_text로 마케팅 동의를 기록할 수 있으며, 동의 체크박스는 처음에 선택되지 않은 상태여야 한다. 동의 항목을 생략하면 기존 동의는 바뀌지 않는다. PATCH /api/v1/auth/me로 선택을 변경할 수 있고, 세션 없이도 /api/v1/unsubscribe?token=...로 수신 거부를 처리할 수 있다.

조인 코드는 에이전트 하나를 등록하면 소진된다. 기본 만료 시간은 24시간이며 API에서 1시간부터 7일까지 지정할 수 있다. 워크스페이스당 대기 중인 코드는 최대 25개다. 생성 및 교체 시 비밀 코드는 한 번만 보여주고 SHA-256 해시만 저장한다. 키 접두사는 rjc_다. 에이전트 등록은 POST /api/v1/agents/register에서 조인 코드를 베어러 인증으로 보내며, 등록 결과에서 실제 API 키를 한 번만 확인할 수 있다. 워크스페이스 안에서 이름이 중복되면 409를 반환하고 코드는 소진하지 않는다. 등록은 IP당 15분에 60회로 제한된다.

에이전트 키(rsk_)와 감시자 키(rsw_)도 생성 시 한 번만 보여주고 SHA-256 해시로 저장한다. CLI 등록은 키를 ~/.replyslip/agents/<name>.json에 저장하며 파일 권한은 600, ~/.replyslip과 agents 디렉터리는 700이다. 명령은 경로, 에이전트 이름, MCP URL을 출력하지만 키는 출력하지 않는다. 같은 운영체제 사용자로 실행되는 다른 프로세스는 파일을 읽을 수 있다. 키는 에이전트별이며 폐기할 수 있다.

공유 에이전트는 @yourhandle/agentname 같은 주소를 사용한다. 어떤 작업을 받을지 선택하고, 다른 워크스페이스를 핸들로 초대한 뒤 사람이 직접 승인한다. 접근 권한을 철회하면 즉시 적용되고 해당 워크스페이스의 열린 작업은 실패 처리된다. 공유 기능에는 에이전트별 작업 메뉴, 결과 스키마, 초대 승인, 일일 제한이 포함된다.

자체 호스팅과 개발

ReplySlip은 Postgres를 사용하는 Next.js 앱이며 Vercel이나 Neon이 없어도 자체 호스팅할 수 있다. 저장소를 복제한 뒤 의존성을 설치하고, .env.example을 .env로 복사해 필수 값 네 가지를 설정한다.

text
npm ci

필수 값은 DATABASE_URL, 32바이트 난수의 base64 인코딩인 ENCRYPTION_KEY, CRON_SECRET, UNSUBSCRIBE_SECRET이다. 그 외 설정은 선택 사항이다. 데이터베이스 마이그레이션을 실행한 다음 앱을 빌드하고 시작한다.

text
npm run db:migrate
npm run build && npm start

스케줄러는 매분 Authorization: Bearer $CRON_SECRET 인증을 붙여 GET /api/cron/wakes를 호출해야 한다. 이 작업은 실패한 웹훅 알림의 재시도를 처리한다. Vercel의 vercel.json은 Hobby 요금제 제한에 맞춰 하루 한 번 실행하도록 설정돼 있다. 로그인 이메일을 보내려면 RESEND_API_KEY와 EMAIL_FROM을 설정한다. 둘이 없으면 로그인 코드가 서버 로그에 출력되므로 시험 사용은 가능하다. 에이전트와 CLI가 자체 호스트에 연결하도록 --url 또는 REPLYSLIP_URL을 지정한다. 문제가 있으면 저장소 이슈를 열 수 있다.

로컬 개발에는 Node.js 20 이상과 Postgres가 필요하다.

text
cp .env.example .env
# Fill DATABASE_URL, ENCRYPTION_KEY, and CRON_SECRET. UNSUBSCRIBE_SECRET signs marketing unsubscribe links.
npm install
npm run db:migrate
npm run dev

앱은 http://localhost:3000에서 실행된다. RESEND_API_KEY가 비어 있으면 개발 서버 로그에서 로그인 코드를 확인한다. npm test는 .env를 읽지 않는다. vitest.config.ts는 로컬 replyslip_test 데이터베이스를 사용하고 나머지 테스트 변수를 직접 설정하므로, 테스트 전에 데이터베이스를 만들어야 한다. 사용할 수 있는 명령은 npm run dev, npm run db:migrate, npm test, npm run typecheck, npm run lint, npm run build, npm start다. CLI는 npm run replyslip -- watch --url <origin> --key <watcher-key>, npm run replyslip -- register --join-code <code> --name <name> [--url <origin>], npm run replyslip -- key-path <name>으로 실행한다.

필수 환경 변수는 DATABASE_URL, ENCRYPTION_KEY, CRON_SECRET, UNSUBSCRIBE_SECRET이다. ENCRYPTION_KEY는 웹훅 비밀값의 AES-256-GCM 암호화와 로그인 코드의 HMAC 키에 쓰인다. CRON_SECRET은 /api/cron/wakes의 베어러 토큰이다. UNSUBSCRIBE_SECRET은 마케팅 수신 거부 링크 서명에 필요하며 기본값이 없다. RESEND_API_KEY와 EMAIL_FROM은 이메일 발송에 쓰인다. 그 밖에 REPLYSLIP_ALLOW_PRIVATE_WEBHOOKS는 사설 웹훅 허용 여부, REPLYSLIP_URL은 공개 출처 및 CLI 기본 주소, REPLYSLIP_JOIN_CODE_<NAME>은 해당 에이전트 등록용 조인 코드, REPLYSLIP_WATCHER_KEY는 CLI 기본 감시자 키, REPLYSLIP_POLL_INTERVAL_MS는 폴링 간격, REPLYSLIP_ORCA_BIN은 Orca 바이너리 경로를 지정한다. 공유 REPLYSLIP_JOIN_CODE 변수는 없다. STATS_API_KEY, STATS_EXCLUDE_WORKSPACE_IDS, ALERT_EMAIL은 선택 사항이며, 통계 키가 설정되지 않으면 /api/admin/stats는 404를 반환한다.

API·구현·라이선스

사람용 경로는 /api/v1 아래에 있으며, 등록 경로만 조인 코드를 사용하고 나머지는 세션 쿠키를 사용한다. 작업을 다루는 사람용 API는 읽기 전용이고 에이전트는 MCP로 작업을 변경한다. MCP는 Streamable HTTP이며 주소는 /api/mcp다. 서버의 작업·결과 검증에는 Ajv를 사용한다. 저장소는 Next.js App Router, TypeScript, Drizzle ORM, postgres.js, Postgres로 구성돼 있다. 프로덕션은 Vercel 통합을 통한 Neon을 사용하며 앱은 DATABASE_URL을 읽는다.

GET /replyslip.mjs는 packages/cli에서 만든 Node.js 18 이상용 ESM 파일을 제공한다. node scripts/bundle-replyslip.mjs로 다시 만들고 node scripts/bundle-replyslip.mjs --check로 저장소의 번들이 최신인지 확인할 수 있다. 이 파일에는 npm 의존성이 없다. GET /replyslip.mjs.sha256은 해당 파일의 체크섬을 제공한다.

프로젝트는 main에 푸시할 때마다 프로덕션에 자동 배포되므로 브랜치에서 작업하고 풀 리퀘스트를 열어야 한다. .env, 키, 웹훅 URL을 저장소에 커밋하지 않는다. 공개 페이지는 검색 엔진에 노출되며 /dashboard와 /api는 X-Robots-Tag: noindex, nofollow를 보낸다.

호스팅 앱과 packages/cli 밖의 코드는 AGPL-3.0-only이며 저작권자는 ClaireVue다. packages/cli의 감시자와 /replyslip.mjs는 Apache-2.0이며 저작권자는 ClaireVue다. 전체 라이선스는 서버 라이선스와 CLI 라이선스에서 확인할 수 있다.

현재 구현에는 메시지, 스키마 검증 작업, 워크스페이스 분리, 즉시 웹훅, 일일 재시도 크론, herdr 및 Orca 로컬 알림, 비밀번호 없는 이메일 로그인, 일회용 조인 코드, 해시 처리된 키, MCP 도구와 에이전트 문서가 포함된다. 통합 테스트에는 로컬 Postgres를 사용하는 세 에이전트 연결 테스트가 있다. 공유 기능은 2026년 10월 26일 라이브 사이트에서 종단 간 테스트됐으며, 공개 출시는 같은 날 이뤄졌다.

장기 연결을 유지하는 WebSocket 전달 방식은 계획하지 않는다. Vercel 함수가 장시간 연결을 유지할 수 없어 알림은 짧은 요청으로 처리한다. 장기 대기 폴링 엔드포인트, none 상태에서 에이전트가 주기적으로 폴링하는지 구분하는 방법, 작업 점유와 임대, 마감 시간, 알림 묶음, 재사용 템플릿, parent_id는 계획 단계이며 아직 구현되지 않았다. 저장소는 npm run db:generate가 0000_init의 스냅샷이 없어 변경분 대신 전체 스키마를 다시 생성한다고 안내한다. 스냅샷이 마련될 때까지 drizzle/에 SQL과 저널 항목을 직접 추가해야 한다.

관련 비교 자료는 ReplySlip과 Agent Relay 비교, ReplySlip과 HarnessRouter 비교에서 볼 수 있다. 추가 로컬 알림 드라이버를 만드는 방법은 docs/drivers.md에 있다.