Skip to main content
Glama

Synartesis

AI 에이전트를 위한 실행 취소(undo) 레이어.

check MIT

실제 시스템에 쓰기 권한을 가진 에이전트가 스무 단계를 실행하다가 일곱 번째 단계를 잘못 읽고, 나머지 단계를 잘못된 레코드에 적용한다고 가정해 보자. 오늘날 당신이 선택할 수 있는 옵션은 트랜스크립트를 보고 수작업으로 되돌리거나, 백업을 복원해서 같은 시간대에 이루어진 모든 정당한 변경을 잃거나, 피해를 감수하는 것뿐이다.

Synartesis는 MCP 클라이언트와 그 클라이언트가 통신하는 서버 사이에 위치한다. 모든 도구 호출과 그 호출이 대체한 상태를 기록하며, 그 상태를 되돌릴 수 있다. 되돌릴 수 없는 것은 에이전트가 감독 없이 수행하지 못하도록 거부한다.

샌드박스가 아니다: 에이전트가 실행되는 컨테이너는 일회용이지만, 네트워크를 통해 업데이트한 CRM 행은 그렇지 않다. 추적 도구도 아니다: 추적은 update_customer가 40번 실행되었다는 것을 알려줄 뿐, 그 이전의 값이 무엇이었는지는 알려주지 않는다.

할 수 있는 것과 할 수 없는 것

모든 도구는 네 가지 분류 중 하나를 가지며, 이를 매니페스트에 작성한다:

분류

의미

예시

수행되는 작업

readonly

아무것도 변경하지 않음

get_customer

기록되고, 전달됨

reversible

이전 상태를 정확히 복원 가능

update_customer

쓰기 전에 상태를 캡처하고, 실행 취소 시 다시 기록함

compensable

되돌릴 수는 없지만 상쇄 가능

create_charge

다른 호출이 이를 중화시킴

irreversible

둘 다 아님

send_email

인간의 승인을 받을 때까지 보류됨

매니페스트에 언급되지 않은 도구는 irreversible로 취급된다. 이는 의도적인 설계다: 알 수 없는 파괴적 호출을 조용히 전달하는 것이 가장 피해야 할 실패이기 때문이다.

Related MCP server: mcp-compensator

요구 사항

도구

버전

확인 방법

Node

22 이상

node --version

pnpm

9 이상

pnpm --version

C 툴체인

아무거나

cc --version

pnpm은 corepack을 통해 Node와 함께 제공된다:

corepack enable pnpm

C 툴체인은 SQLite의 네이티브 바인딩을 컴파일하기 위해 한 번 필요하다. macOS에서는 xcode-select --install을 실행하고, Debian이나 Ubuntu에서는 apt install build-essential을 실행한다.

설치

curl -fsSL https://raw.githubusercontent.com/ArhaanDev24/Synartesis/main/install.sh | bash

또는 먼저 읽어보고 싶다면 클론에서 설치한다:

git clone https://github.com/ArhaanDev24/Synartesis.git && cd Synartesis && ./install.sh

이 스크립트는 Node 버전을 확인하고, 빌드한 다음, PATH에 이미 있는 첫 번째 쓰기 가능한 디렉토리에 synartesissynartesis-proxy를 링크한다. 셸 프로필을 편집하지 않으며 sudo도 필요 없다. 빌드만 하려면 --no-link를 전달한다.

synartesis --help

링크할 수 있는 것이 없다면 아무 문제도 없다: Synartesis가 출력하는 모든 명령은 실제로 실행되는 형태 그대로 스스로를 표기한다.

둘러보기

이 저장소에 포함된 장난감 CRM을 사용하므로 실제 데이터를 가리키지 않고 전체 루프를 볼 수 있다. 임시 디렉토리에서 실행한다.

mkdir -p /tmp/synartesis-demo && cd /tmp/synartesis-demo

1. 정책 작성

init은 서버를 시작하고, 어떤 도구가 있는지 묻고, 매니페스트를 작성한다. SYNARTESIS를 클론한 경로로 바꾼다.

node SYNARTESIS/dist/cli.js init crm -- node SYNARTESIS/dist/toy-crm.js --state ./crm.json

synartesis.yaml을 연다. 자체 선언된 읽기 도구가 아닌 모든 도구는 TODO와 함께 irreversible로 시작한다. 그 TODO들을 처리하는 것이 작업이다. 이 픽스처에 대한 완성된 정책이 저장소에 포함되어 있으므로 직접 입력하는 대신 복사한다:

cp SYNARTESIS/manifests/toy-crm.yaml ./synartesis.yaml

그런 다음 서버 위치를 나타내는 한 줄을 편집하여 클론을 가리키고 데이터를 이 디렉토리에 유지하도록 한다:

servers:
  crm:
    command: node
    args: ["SYNARTESIS/dist/toy-crm.js", "--state", "./crm.json"]

2. 에이전트를 프록시에 연결

MCP 클라이언트가 서버를 나열하는 곳마다, 보호하려는 서버의 항목을 프록시로 바꾼다. Claude Desktop이나 Claude Code의 경우 mcpServers 블록이다:

{
  "mcpServers": {
    "crm": {
      "command": "node",
      "args": ["SYNARTESIS/dist/proxy.js", "--manifest", "/tmp/synartesis-demo/synartesis.yaml"]
    }
  }
}

에이전트는 동일한 이름과 동일한 결과를 가진 동일한 도구를 본다. 그것이 핵심이다: 에이전트에 대해 변경되는 것은 아무것도 없다.

이 둘러보기에는 실제 에이전트가 필요 없다. 다음이 동일한 작업을 수행한다:

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"demo-agent","version":"0"}}}' '{"jsonrpc":"2.0","method":"notifications/initialized"}' '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"update_customer","arguments":{"id":"c_001","plan":"free","notes":"wrong edit"}}}' '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"delete_customer","arguments":{"id":"c_002"}}}' | node SYNARTESIS/dist/proxy.js --manifest ./synartesis.yaml --journal ./journal.db > /dev/null

피해를 확인한다:

cat crm.json

Ada는 잘못된 요금제에 잘못된 메모가 있고, Grace는 사라졌다.

3. 무엇을 했는지 확인

node SYNARTESIS/dist/cli.js list --journal ./journal.db
node SYNARTESIS/dist/cli.js show RUN_ID --journal ./journal.db

show는 각 호출을 분류, 상태, 그리고 그 호출을 실행 취소할 정확한 호출(이미 리터럴 값으로 해석됨)과 함께 출력한다.

4. 실행 취소

뛰어들기 전에 확인한다:

node SYNARTESIS/dist/cli.js undo RUN_ID --dry-run --journal ./journal.db

그런 다음 실행한다:

node SYNARTESIS/dist/cli.js undo RUN_ID --journal ./journal.db
cat crm.json

Grace는 돌아왔고 Ada는 원래 요금제와 원래 메모를 유지한다.

5. 거부를 확인

실행 취소는 무딘 도구가 아니다. 에이전트가 레코드를 건드린 후 다른 무언가가 그 레코드를 변경했다면, 이전 값을 다시 쓰는 것은 그 작업을 파괴하므로 Synartesis는 멈추고 두 값을 모두 보여준다.

2단계의 피해 명령을 다시 실행한다. 그러면 두 번째 실행이 생성되므로, 가장 최근 것이 먼저 정렬된 list의 상단에서 실행 ID를 가져온다. 그런 다음 레코드를 수동으로 편집한다:

node -e 'const f="./crm.json",s=JSON.parse(require("fs").readFileSync(f));s.customers.c_001.notes="a human wrote this";require("fs").writeFileSync(f,JSON.stringify(s,null,2))'
node SYNARTESIS/dist/cli.js undo RUN_ID --journal ./journal.db

중단되고, 예상 상태와 실제 상태를 출력하고, 0이 아닌 종료 코드로 종료되며, 아무것도 변경하지 않는다.

되돌릴 수 없는 것 승인

send_emailirreversible로 분류되므로 에이전트가 단독으로 보낼 수 없다. 호출은 즉시 거부되며 작업 ID와 승인할 명령이 함께 제공된다. 에이전트가 알려주면 당신이 결정하고, 다시 시도한다.

대기하는 동안 호출을 열어두지 않는다. 그것이 첫 번째 설계였지만 실제 클라이언트와 맞닥뜨리면 성립하지 않는다: 사람이 알아차리고, 터미널을 열고, 결정할 수 있는 유용한 시간 창은 클라이언트가 도구를 기다리는 시간보다 항상 길기 때문에, 더 나은 타임아웃을 선택하는 것으로는 둘을 조화시킬 수 없다.

승인은 에이전트가 사용하는 터미널에서도 이루어지지 않는다: 프록시는 stdin과 stdout을 통해 MCP를 사용하므로 거기에는 프롬프트를 표시할 것이 없고, 데스크톱 클라이언트에는 터미널이 전혀 없다. 요청은 저널로 이동하며, 당신은 어디서든 응답한다:

node SYNARTESIS/dist/cli.js gates --journal ./journal.db
node SYNARTESIS/dist/cli.js approve ACTION_ID --by your-name --journal ./journal.db
node SYNARTESIS/dist/cli.js deny ACTION_ID --by your-name --reason "not this one" --journal ./journal.db

승인은 일회용이며 한 시간 후 만료되므로, 승인이 부여된 재시도를 커버하고 내일 같은 호출을 조용히 승인할 수 없다. 특정 세션에 묶이지 않는데, 사람들은 클라이언트를 다시 시작하며 죽은 세션에 갇힌 승인은 승인이 아니기 때문이다.

침묵으로 승인되는 것은 없다. 응답하지 않은 요청은 누군가 결정할 때까지 synartesis gates에 표시된 채로 남아 있다.

에이전트는 연결 시 이 모든 내용을 전달받으므로 불투명한 실패를 보고하는 대신 스스로 설명할 수 있다.

실제 서버

읽는 것보다 단계를 따르는 것을 선호한다면, 자체 파일에 대해 실행하는 가이드가 있으며, 게이트와 드리프트 검사가 의도적으로 테스트할 가치가 있는 두 가지다.

Synartesis는 특히 이메일과 관련이 없다. MCP 프로토콜 위에 위치하므로, 그 대상은 연결된 서버가 할 수 있는 모든 것이다: 파일, 저장소, 데이터베이스, 티켓, 에이전트 자신의 메모. 무엇을 실행 취소할 수 있는지는 전적으로 해당 서버가 노출하는 것에 달려 있으며, 아래 각 매니페스트는 그 한계가 어디인지 명확히 밝힌다.

매니페스트

서버

관리하는 상태

filesystem.yaml

@modelcontextprotocol/server-filesystem

디스크의 실제 파일

memory.yaml

@modelcontextprotocol/server-memory

에이전트가 당신에 대해 유지하는 지식 그래프

git.yaml

mcp-server-git

실제 저장소의 인덱스와 히스토리

github.yaml

github/github-mcp-server

이슈, 풀 리퀘스트, 파일 내용

toy-crm.yaml

이 저장소의 픽스처

모든 분류의 작업 예시

github.yaml을 제외한 모든 매니페스트는 실제 실행 중인 서버에 대해 검증되었다. 두 개의 데모가 전체 루프를 실제로 실행한다:

./demo/filesystem-demo.sh
./demo/memory-demo.sh

파일시스템 데모는 파일 하나를 덮어쓰고 다른 파일을 이동한 다음 둘 다 복원하고, 그 사이에 인간이 파일을 편집했을 때 실행 취소가 거부되는 것과, 이 서버가 제거할 방법이 없는 디렉토리 생성을 게이트가 거부하는 것을 보여준다.

메모리 데모가 더 예리하다. 에이전트는 그래프에 두 사람을 추가하는데, 그중 한 명은 이미 존재했고 서버는 중복을 조용히 무시한다. 따라서 실행 취소는 정확히 한 명만 제거해야 한다: 역연산은 에이전트가 요청한 것이 아니라 서버가 생성했다고 말한 것에서 구성되므로, 먼저 있던 사람은 실행 취소에서 살아남는다. 같은 세션에서 엔티티 삭제를 시도하면 보류되는데, 엔티티를 삭제하면 그와 연결된 모든 관계도 삭제되고 하나의 역호출로 둘 다 되돌릴 수 없기 때문이다.

각각의 한계

한계가 흥미로운 부분이며, 이는 Synartesis가 아니라 서버의 속성이다.

  • filesystem: move_file은 인수만으로 되돌릴 수 있으므로 사전 읽기가 선언되지 않고 드리프트 검사도 수행할 수 없다. create_directory는 디렉토리가 소중해서가 아니라 이 서버가 디렉토리를 제거할 방법을 노출하지 않기 때문에 irreversible이다.

  • memory: add_observationsdelete_observations는 같은 필드를 다르게 부르는 정확한 반대 연산이다. 경로는 필드를 읽을 수 있지만 이름을 바꿀 수는 없으므로 그 역연산은 전혀 작성할 수 없고 호출은 대신 게이트된다.

  • git: 이 서버가 제공하는 거의 모든 읽기는 사람을 위한 산문으로 답하며, 기본 git 작업이 아무리 되돌릴 수 있어도 캡처된 상태에서 역연산할 수 있는 것은 거의 없다. 커밋은 이 서버가 reset, revert, 브랜치 이동 방법을 노출하지 않기 때문에 게이트된다.

직접 작성한다면 알아둘 가치가 있는 두 가지가 있는데, 둘 다 문서를 읽는 대신 실제 서버에 대해 실행하여 발견된 것이다:

$result는 구조화된 블록이며 텍스트 블록과 일치할 필요가 없다. 메모리 서버는 create_entities에 텍스트 블록의 단순 목록과 structuredContent{"entities": [...]}로 응답한다. Synartesis는 구조화된 것을 탐색하는데, 그것이 기계가 읽을 수 있는 계약이기 때문이다.

그리고 synartesis check는 도구가 존재한다는 것을 증명할 뿐, 경로가 해석된다는 것을 증명하지 않는다. 증명할 수 없다: 호출이 이루어진 적이 없으므로 탐색할 결과가 없다. 역연산에 의존하기 전에 한 번 실행하고 synartesis show를 읽어라.

매니페스트 작성

매니페스트가 제품의 전부다. 아는 API라면 15분이면 작성할 수 있어야 한다.

version: 1

servers:
  crm:
    command: node
    args: ["./crm-server.js"]

tools:
  - match: "crm.get_customer"
    class: readonly

  # Read the record before overwriting it, then write that record back.
  - match: "crm.update_customer"
    class: reversible
    snapshot:
      tool: "crm.get_customer"
      args:
        id: "$.id"
    inverse:
      tool: "crm.update_customer"
      args:
        id: "$.id"
        name: "$snapshot.name"
        plan: "$snapshot.plan"

  # Nothing to read beforehand; the id only exists once the call returns.
  - match: "crm.create_customer"
    class: compensable
    inverse:
      tool: "crm.delete_customer"
      args:
        id: "$result.id"

  - match: "crm.send_*"
    class: irreversible
    gate: always

값이 참조할 수 있는 것은 정확히 세 가지다:

접두사

참조 대상

사용 가능 위치

$.

에이전트가 보낸 인수

snapshotinverse

$snapshot.

사전 읽기가 캡처한 것

inverse

$result.

정방향 호출이 반환한 것

inverse

그 외의 모든 것은 리터럴이다. 참조는 단독으로 사용될 수 있으며, 이 경우 값은 타입을 유지하거나, 문장 안에 들어가면 텍스트로 치환된다:

sha: "$result.content.sha"                 # the value itself
message: "Revert agent change to $.path"   # text with the path substituted

리터럴 달러 기호는 $$로 작성한다. 표현식, 조건문, 함수는 없으며 앞으로도 없을 것이다: 이것이 언어가 되는 순간 15분 안에 작성할 수 있는 것이 아니게 된다.

경로는 [0]으로 목록을 인덱싱하고 []로 모든 요소에서 하나의 필드를 읽을 수 있다:

labels: "$snapshot.labels[].name"   # [{name: "bug"}, ...] becomes ["bug", ...]

이는 API가 받을 때보다 더 풍부한 필드를 돌려주는 일반적인 경우를 다루는데, GitHub가 이슈 레이블에서 그렇게 하는 것과 같다. []는 각 요소에서 동일한 키를 읽을 뿐 그 외에는 아무것도 하지 않는다: 여전히 경로이지 변환이 아니다. 참조는 값을 복사할 뿐 계산할 수 없으므로, 진정으로 다른 형태가 필요한 API는 역연산이 해당 필드를 제외하고 그렇게 명시해야 한다.

알아둘 다른 사항들:

  • match는 한 세그먼트 내에서 매칭되는 *를 지원합니다. crm.send_*crm.send_email과 매칭되지만 crm.a.b와는 매칭되지 않습니다. 규칙이 작성된 순서와 무관하게 가장 구체적인 패턴이 우선합니다.

  • 패치의 역연산은 패치를 다시 적용하는 것이 아니라 모든 필드를 복원해야 합니다. 한 번의 실행에서 같은 레코드가 두 번 편집되면, 부분 역연산은 두 번째 편집이 건드린 필드를 남겨둡니다.

  • gate: on_write는 원시 SQL 실행기 같은 도구를 위한 휴리스틱으로, 파괴성을 도구 이름에서 읽을 수 없습니다. 단일 읽기 문으로 확실하게 읽을 수 없는 것은 모두 게이트에 걸립니다. 확실성이 중요한 곳에서는 gate: always를 사용하세요.

  • 잘못된 매니페스트는 프록시가 시작되지 못하게 하며, 수정해야 할 파일과 줄을 알려줍니다. 이해할 수 없는 정책으로 실행되는 일은 결코 없습니다.

명령어

명령어

기능

init <server> -- <cmd>

서버를 인트로스펙트하고 매니페스트 초안 작성

list

기록된 모든 실행 표시

show <runId>

각 단계의 실행 취소와 함께 한 실행의 타임라인 표시

gates

결정을 기다리는 항목 표시

approve <actionId>

일시 중단된 호출 허용

deny <actionId>

하나 거부

undo <runId>

실행을 최신 작업부터 역순으로 되돌림

undo <runId> --replan

동일하되, 현재 매니페스트에서 각 실행 취소를 재구축

check

매니페스트를 로드하고 이름이 지정된 서버에 대해 검증

--manifest--journal은 입력하는 것이 아니라 찾아냅니다. 둘 다 현재 디렉터리에서 위쪽으로 찾아보는데, 버전 관리 도구가 루트를 찾는 방식과 동일합니다. 따라서 synartesis.yaml이 있는 프로젝트 안에서는 플래그 없이도 모든 명령이 작동합니다. 아직 존재하지 않는 저널은 정책 옆에 배치되므로, 저널을 생성하는 프록시와 이를 읽는 CLI가 서로 지시받지 않아도 일치합니다.

기타 플래그: undo--dry-run, --to <seq>, --replan, approvedeny--all, list, show, gates--json.

종료 코드: 0 성공, 1 중단 또는 거부, 2 잘못된 사용법 또는 구성.

프록시는 --manifest, --journal, --gate-timeout <seconds>, --log-level을 받습니다. 구조화된 JSON을 stderr에 기록하며, stdout은 프로토콜 트래픽 전용입니다.

하지 않는 일

  • 이미 본 것을 보내지 않은 상태로 되돌릴 수 없습니다. 읽은 이메일, 게시된 메시지, 백업 없이 삭제된 파일. 이것이 게이트가 존재하는 이유입니다.

  • 보상 가능한 작업은 드리프트를 확인할 수 없습니다. 사전 읽기를 선언하지 않으므로, 실행 취소는 이를 보상하고 보고서에서 [unverified]로 표시합니다.

  • 실행 취소는 불확실성에서 멈추고, 단순히 영구적인 것은 건너뜁니다. 드리프트, 알 수 없는 결과, 또는 실패한 역방향 호출은 실행 취소를 중단시킵니다. 그런 것들을 지나 계속하면 무언가를 파괴할 수 있기 때문입니다. 보낸 이메일처럼 단순히 취소할 수 없는 작업은 보고되고 그 자리에 남겨지며, 다른 모든 것은 되돌려집니다. 아무리 멈춰도 보내지지 않은 상태로 되돌릴 수 없고, 멈추면 나머지도 잘못된 상태로 남을 뿐입니다. 어느 쪽이든 실행은 partial로 표시됩니다.

  • 도중에 중단된 호출은 실패가 아닌 알 수 없음으로 기록됩니다. 실행 취소는 이를 지나가기를 거부합니다. 적용되었는지 여부를 판단할 수 없기 때문입니다.

  • 실행 취소는 이를 기록한 정책만큼만 정확합니다. 역연산은 실행 취소할 때가 아니라 호출이 발생할 때 해석되므로, 매니페스트의 실수는 그 아래에서 이루어진 모든 실행에 각인됩니다. undo --replan은 이미 캡처된 상태를 사용하여 수정된 매니페스트에서 이를 재구축하며, 이것이 해결 방법입니다.

작동 확인

Synartesis는 데몬이 아니며 데몬이 될 수 없습니다. MCP 클라이언트가 stdio 서버를 직접 생성하고 그 수명을 소유하므로, 그 사이에 장기 실행 프로세스가 앉아서 호출을 볼 수 없습니다. 사람이 데몬에서 원하는 것은 대개 그것이 존재하고 무언가 하고 있다는 안심이며, 그런 안심은 백그라운드 프로세스가 아닌 볼 곳이 필요합니다:

synartesis watch

에이전트가 작업하는 동안 다시 그려집니다: 호출된 것, 각 호출의 클래스, 결정을 기다리는 것, 그리고 이를 승인하는 명령. Ctrl-C로 중지합니다. 터미널에서 실행하는 대신 파이프로 연결하면 상태를 한 번 출력하고 종료합니다.

신뢰

매니페스트는 명령을 지정하고 Synartesis는 이를 실행합니다. 직접 작성하지 않은 매니페스트는 같은 출처의 셸 스크립트를 대하듯 취급하세요: 먼저 읽으십시오. 여기에는 샌드박스가 없으며, 있을 의도도 없습니다.

개발

pnpm test
pnpm typecheck && pnpm lint

모든 푸시는 Linux와 macOS에서 Node 22와 24로 실행되며, 데모와 설치 프로그램도 포함합니다.

라이선스

MIT. LICENSE 참조.

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    A policy-enforcing MCP gateway that intercepts all tool calls to downstream MCP servers, applying allow/deny/ask rules with human approval and audit logging for safe access to dangerous tools.
    23
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    MCP proxy that journals mutating tool calls and enables undo via compensation. It adds checkpoint, list_changes, undo_to, and explain_blast_radius meta-tools while forwarding all original downstream tools unchanged.
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Provides a secure MCP boundary for AI agents, intercepting and validating tool calls, redacting secrets, and requiring human approval for sensitive actions with a tamper-evident audit trail.
  • A
    license
    A
    quality
    A
    maintenance
    An MCP proxy that enforces policy on every tool call, blocking or flagging actions before they reach downstream MCP servers.
    1
    249
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ArhaanDev24/Synartesis'

If you have feedback or need assistance with the MCP directory API, please join our Discord server