가이드
MCP 서버
gori는 내장 MCP(Model Context Protocol) 서버를 제공합니다. TUI에 채팅 창을 넣는 대신, gori는 프로젝트를 깔끔한 도구 인터페이스로 노출합니다. 덕분에 MCP를 지원하는 어떤 에이전트든(Claude, Codex, Grok 등) 트래픽을 읽고 도구를 구동할 수 있습니다.
나users API에서 IDOR를 찾아 기록해줘.
→ list_history path~/v1/users status:200
← 플로우 14개, customer 및 admin 토큰
→ send_request GET /v1/users/2 · customer 토큰
← 200 {"id":2,"email":"other-tenant@example.com"}, 호출자의 행이 아님
→ create_issue "IDOR on /v1/users/{id}" severity:high
✓ 이슈 기록됨; 재현을 위해 요청이 Repeater 세션으로 저장됨.
gori mcp
서버는 stdio 위에서 JSON-RPC 2.0으로 통신합니다. STDOUT은 프로토콜을, STDERR은 로그를 전달합니다. 도구 결과에는 하위 호환용 텍스트가 담기며, 페이로드가 JSON이면 MCP structuredContent도 함께 들어갑니다.
프로젝트 선택
cd /path/to/my-repository && gori mcp # path-binds this Git workspace to its own gori project
gori mcp --project my-engagement # serve a named project's database
gori mcp --db /path/to/project.db # serve a specific database file
gori mcp --use-active-project # explicitly serve the active TUI/MRU project
gori mcp --no-project # force unbound even inside a Git workspace
명시적 선택자가 없으면, gori는 가장 가까운 Git 루트를 찾아 그 정규 경로를 격리된 프로젝트에 바인딩합니다. 이 바인딩은 디렉터리 이름이 같은 두 리포지토리가 하나의 데이터베이스를 공유하는 것을 막습니다.
Git 워크스페이스 밖에서 뜨면(AI 클라이언트가 홈·앱 디렉터리에서 MCP를 띄우는 흔한 경우) 서버는 unbound로 시작합니다. MCP 핸드셰이크와 도구 목록은 바로 성공하지만, 트래픽 도구(list_history, send_request 등)는 에이전트가 list_projects, create_project(unbound일 때 자동 바인딩), 또는 switch_project를 호출하기 전까지 NO_PROJECT를 반환합니다. unbound는 활성 TUI/MRU 프로젝트를 슬그머니 열지 않습니다 — 그건 명시적 --use-active-project 옵트인(또는 --project / --db / GORI_MCP_PROJECT / GORI_MCP_DB)이 필요합니다.
선택된 프로젝트를 열 수 없으면 — 데이터베이스가 없거나 깨졌거나 읽을 수 없을 때, 프로젝트 이름이 더 이상 존재하지 않을 때 — 서버는 종료하지 않고 핸드셰이크를 마친 뒤 unbound로 시작합니다. 실패 이유는 stderr에 기록되고, 핸드셰이크 instructions에 실리며, 모든 NO_PROJECT 도구 오류에 함께 반환되고, project_info의 bind_error 필드로도 보고됩니다. 에이전트는 재시작 없이 list_projects와 switch_project로 복구할 수 있습니다.
데이터를 사용하기 전에 project_info를 호출하세요. bound, 선택된 프로젝트, 데이터베이스 경로, 워크스페이스 루트, 선택 출처를 보고합니다.
읽기 전용 모드
기본적으로 서버는 실시간 요청을 보내고 이슈를 기록하는 액션 도구도 노출합니다. 읽기 도구만 노출하려면(신뢰할 수 없는 에이전트에게 프로젝트를 넘길 때 안전합니다) 읽기 전용으로 시작하세요.
gori mcp --read-only
읽기 전용 서버는 라이터도 두지 않습니다. 서빙 중인 프로젝트에 쓰지 않고 백그라운드 인덱싱도 돌리지 않습니다. SQLite는 라이터를 하나만 허용하는데, 두 번째 gori가 자기 뒷정리 작업만을 위해 그 자리를 붙들고 있으면 같은 프로젝트로 캡처 중인 TUI와 경합하게 됩니다. 예외는 예전 gori가 쓴 데이터베이스뿐입니다. 그건 마이그레이션하지 않으면 이 빌드가 읽을 수 없어서 열 때 올려줍니다.
기본(액션 허용) 서버는 라이터를 둡니다. send_request와 create_issue가 필요하기 때문입니다. 다만 idle FTS 인덱서는 돌리지 않습니다. 전문 검색은 질의 때 백로그를 비우고, 백그라운드에서 인덱스를 따라가는 일은 캡처 중인 TUI의 몫입니다.
알아둘 만한 부수 효과가 하나 있습니다. 전문 검색(body:)이 읽는 인덱스는 캡처 커밋과 분리되어 만들어지는데, 읽기 전용 서버는 그 인덱스를 만들 수 없습니다. 아직 인덱싱되지 않은 flow가 남아 있으면 그런 질의는 부분 인덱스로 답하는 대신 FTS_BACKLOG로 거부됩니다. gori로 프로젝트를 열거나 --read-only를 빼고 실행해서 인덱스를 비우세요.
TUI에서 에이전트 보기
MCP 서버가 프로젝트에 붙어 있는 동안 gori가 이를 보여줍니다. 프로젝트 선택창에서는 해당 행에 mcp 표시가 붙고(둘 이상이면 mcp×2), 프로젝트를 열면 상단 바에 클릭 가능한 mcp:<client> 칩이 나타납니다. 칩을 클릭하거나 명령 팔레트에서 app.agents를 실행하면 붙어 있는 에이전트를 나열하는 카드가 열립니다. 이름·버전·pid·연결 시각·읽기 전용 여부가 표시됩니다. 이름은 클라이언트의 clientInfo 핸드셰이크에서 오고, --read-only로 시작한 서버는 거기서 읽기 전용으로 표시됩니다. 행은 프로세스가 종료되는 즉시 스스로 사라지므로, 칩과 카드는 항상 지금 붙어 있는 것만 반영합니다.
에이전트에 설치하기
gori는 널리 쓰이는 클라이언트의 MCP 설정을 대신 작성해 줍니다.
| 플래그 | 클라이언트 | 작성되는 설정 |
|---|---|---|
--install-claude |
Claude Desktop | 플랫폼별 앱 설정 디렉터리의 claude_desktop_config.json (아래 참고) |
--install-claude-code |
Claude Code | ~/.claude.json (mcpServers.gori) |
--install-codex |
OpenAI Codex | ~/.codex/config.toml ([mcp_servers.gori]) |
--install-agy |
Antigravity CLI | ~/.gemini/antigravity-cli/mcp_config.json |
--install-grok |
Grok | ~/.grok/config.toml ([mcp_servers.gori]) |
--install-hermes |
Hermes | ~/.hermes/config.yaml (mcp_servers.gori), 또는 $HERMES_HOME |
Claude Desktop과 Hermes를 뺀 나머지 클라이언트는 macOS·Linux·Windows에서 모두 같은 위치에 설정을 둡니다. Hermes는 $HERMES_HOME이 설정돼 있으면 그 값을, 없으면 ~/.hermes(Windows는 %LOCALAPPDATA%\hermes)를 읽습니다. Claude Desktop만 Electron의 앱 데이터 디렉터리를 따릅니다. macOS는 ~/Library/Application Support/Claude/, Windows는 %APPDATA%\Claude\, Linux는 $XDG_CONFIG_HOME/Claude/(기본값 ~/.config/Claude/)입니다. gori는 이 변수를 읽으므로 Nix나 home-manager처럼 세션에서 값을 옮겨 둔 환경도 그대로 따라갑니다.
예외는 Flatpak Claude Desktop입니다. 이 빌드는 샌드박스 안쪽의 XDG_CONFIG_HOME(~/.var/app/<app-id>/config/Claude/)을 읽는데, 호스트 셸에서 실행되는 gori는 그 값을 볼 수 없습니다. 이 경우 gori는 ~/.config/Claude/claude_desktop_config.json에 쓰고 그 경로를 출력하니, 파일을 샌드박스 디렉터리로 직접 복사하세요. 설치 명령은 항상 실제로 쓴 파일을 출력하므로, 그 줄을 사용 중인 빌드가 읽는 위치와 맞춰 보세요.
gori mcp --install-claude-code
gori mcp --install-codex
gori mcp --install-grok
gori mcp --install-hermes
gori mcp --install-claude-code --install-codex # 한 번에 여러 클라이언트
Codex와 Grok은 [mcp_servers.gori] 테이블이 있는 TOML을, Hermes는 mcp_servers: 항목이 있는 YAML을 사용합니다(JSON이 아닙니다). 설치 후 클라이언트를 재시작하거나 세션을 다시 열어 MCP 서버를 다시 로드하세요. 기존 설정 파일은 제자리에서 갱신됩니다. 다른 서버·테이블·주석은 그대로 두고, 파일 권한도 유지하며, 교체는 원자적이라 설치가 중간에 끊겨도 파일이 잘려 나가지 않습니다. gori는 이 파일들을 파싱 트리에서 다시 뽑아내지 않고 텍스트로 편집하므로 설정 주변에 적어 둔 메모가 그대로 남습니다. 안전하게 끼워 넣을 수 없는 설정 파일은 고쳐 쓰지 않고 그 사실을 알립니다.
클라이언트가 리포지토리 디렉터리 밖에서 MCP를 시작해도 서버는 unbound로 연결되며, 에이전트가 도구로 프로젝트를 고르거나 만들 수 있습니다. 설치 시점에 고정 engagement를 박아 두려면 선택자를 넘기세요. 예: gori mcp --project my-engagement --install-codex.
--install-*과 함께 넘긴 플래그는 모두 설치되는 커맨드에 그대로 기록됩니다. 선택자(--project, --db, --no-project, --use-active-project)는 물론 --read-only, --insecure-upstream, --config까지 포함되므로 클라이언트가 띄우는 커맨드가 입력한 그대로가 됩니다. 경로는 절대 경로로 변환됩니다. 클라이언트는 사용자가 고르지 않은 작업 디렉터리에서 서버를 실행하기 때문입니다.
도구
읽기 도구(항상 사용 가능):
| 도구 | 용도 |
|---|---|
list_history |
최신순으로 플로우 나열, 선택적 QL과 페이지네이션 포함. 각 행에 source가 실립니다 — 클라이언트가 보낸 트래픽은 proxy, send_request(기본으로 기록됩니다)는 repeater, 그 밖에 discover·import … — 그래서 gori가 만든 플로우가 대상에 대한 증거로 잘못 읽히지 않습니다. src:로 필터링합니다. columns에 gori run ls --column과 같은 [LABEL=][req|res:]kind:selector 스펙을 주면 행마다 추출한 값(헤더, JSON 필드, 정규식 캡처)을 columns 객체로 함께 싣습니다 — QL로 거를 수는 있어도 볼 수는 없던 값을 보여 주는 쪽입니다. 행마다 읽기가 한 번 늘어나므로 명시할 때만 동작합니다 |
list_events |
작업 수명주기와 에이전트 활동을 추가 전용 피드로 전방 커서 조회. 플로우가 여전히 전체 스트림이며, 이 피드는 플로우 행을 중복하지 않음. 모든 이벤트가 actor(행위 표면: tui / cli / mcp)를 담고 있어 에이전트가 자기 쓰기와 운영자의 쓰기를 구분할 수 있으며, 설정 변경은 누가 하든 기록됩니다. 사람은 같은 피드를 Project → Activity 패널에서 읽습니다 |
list_views |
프로젝트의 History 뷰 — list_history{view}가 렌즈로 적용하는 이름 붙은 QL 쿼리로, query를 대체하지 않고 그 위에 AND로 얹힙니다. 기본 뷰 7종(All, History, History + Repeater(기본값), WebSocket, gRPC, SSE, Errors) → 글로벌 라이브러리 → 프로젝트 순이며, active는 TUI가 보고 있는 뷰를 표시할 뿐 list_history에 적용되지 않습니다 — 그쪽은 넘긴 view로만 거릅니다 |
get_flow |
한 플로우의 전체 요청 + 응답 |
get_response_body_chunk |
인라인 64 KiB 상한을 넘는 디코드(또는 원시) 플로우/Repeater 응답을 페이지 단위로 조회 |
list_sitemap / list_sitemap_tags |
고유 엔드포인트(host, method, path)와 거기에 달린 태그 |
list_issues / get_issue |
트리아지된 이슈 읽기 |
probe_scan |
캡처된 플로우와 Repeater 탭 재스캔. active:true가 아니면 패시브(요청 0건)이고, 액티브는 쓰기 권한이 필요하며 스코프 게이트를 거침 |
probe_issues |
Probe 탭에 저장된 발견 항목을 트리아지 상태로 조회(기본은 open만) |
list_probe_rules |
모든 스캔 규칙(패시브, 액티브, 커스텀)과 활성화 여부, 프로젝트의 스캔 모드 |
list_scope |
현재 스코프 include/exclude 규칙 |
list_links |
이슈나 노트에서 플로우, Repeater 세션, 잡으로 이어지는 증거 포인터 |
compare_flows |
두 플로우의 요청 또는 응답 줄 단위 diff — 양쪽의 status/size/time과 A→B 델타 포함. context:N은 동일 구간을 {kind:fold,hidden} 마커로 접음 |
diff_projects |
리테스트 diff: 프로젝트 두 개를 엔드포인트 단위로 비교 — 지난 엔게이지먼트 이후 무엇이 새로 생겼고, 사라졌고, 다르게 응답하는지. 엔드포인트 키는 Sitemap의 폴딩된 템플릿을 그대로 쓰고, removed(새 캡처가 아예 요청한 적 없음)와 gone(요청했고 404/410을 받음)은 별개의 판정 |
intercept_list / intercept_get |
라이브 인터셉트 큐와 홀드된 항목 하나의 전체 내용 조회 |
list_projects |
이 호스트의 모든 gori 프로젝트 |
list_notes / get_note |
프로젝트 노트 읽기 |
list_rule_presets |
응답 수정 프리셋 — 평범한 Match & Replace 규칙을 설치하는 이름 붙은 출발점(hidden 필드 드러내기, disabled 컨트롤 활성화, maxlength 제거, 클라이언트 검증 제거, CSP / 보안 헤더 제거, SRI 비활성화). 각 행이 설치할 규칙을 밝힙니다 |
list_extract_rules |
프로젝트의 extract 규칙 — 세션 바인딩의 읽는 쪽 절반. 각각 응답을 관찰해 $NAME 하나를 메모리에 묶고, Match & Replace 규칙이 그것을 주입합니다 |
list_color_rules / list_custom_colors |
Colormarker 규칙을 우선순위 순으로, 그리고 규칙의 color가 참조할 수 있는 전역 커스텀 색상. 표시 전용이며 색상 규칙은 트래픽을 건드리지 않습니다 |
preview_color_rule |
어떤 색상 조건이 최근 플로우 몇 개에 매칭되는지, 그리고 앞서 해소되는 규칙들을 셈한 뒤 실제로 몇 개를 칠하는지 |
grpc_schema |
이 프로젝트가 캡처된 gRPC를 어떤 .proto 스키마로 렌더하는지, 각 조각이 어디서 왔는지(디스크립터 셋 파일 또는 리플렉션 페치). 아무것도 보내지 않습니다 |
list_rules |
프로젝트에 적용되는 Match & Replace 규칙을 적용 순서로 나열. 전역 규칙이 먼저, 그다음이 프로젝트 규칙(scope로 한쪽만 조회) |
list_env |
$KEY 치환에 쓰이는 프로젝트 env 토큰(값은 가려짐). 행마다 length와, 값이 스킴으로 시작할 때 scheme도 싣는다 — 헤더를 Bearer $KEY로 써야 하는지 그냥 $KEY로 써야 하는지, 값 없이 판단할 수 있을 만큼 |
list_host_overrides |
이 프로젝트에 적용 중인 호스트 → IP 다이얼 맵 |
list_session_slots |
프로젝트의 세션 슬롯 — 이름 붙은 신원 각각이 헤더 오버레이 하나와 그 값을 묶어 주는 extract 규칙들로 이루어집니다 — 그리고 어느 쪽이 ACTIVE인지(헤더 값은 가려짐) |
list_oast_providers |
설정된 OAST 프로바이더와 현재 활성 프로바이더 |
list_oast_sessions |
프로젝트에 저장된 OAST 리스닝 세션 — 페이로드 호스트, hit 수, 마지막 폴링 시각. oast_resume이 다시 살리는 행 |
decode |
input에 대해 인코드/디코드/해시/압축 체인을 실행(순수 변환; 네트워크나 상태 없음) |
jwt_decode / jwt_encode / jwt_attacks |
JWT 디코드, 재서명, 공격 페이로드 생성(순수 계산; --read-only에서도 사용 가능) |
cookie_decode / cookie_verify / cookie_crack / cookie_forge |
Cookie 워크벤치를 순수 오프라인 연산으로: Flask / Rack / Django 서명 세션 쿠키 파싱, 후보 시크릿으로 검증, 워드리스트로 시크릿 브루트포스, 편집한 페이로드 재서명. 네트워크를 쓰지 않으므로 네 개 모두 --read-only에서도 살아남습니다 |
sequence_analyze |
붙여넣은 토큰 목록의 무작위성 / 예측 가능성 평가(순수) |
oast_presets / oast_payload / oast_poll |
OAST 프로바이더 나열, 현재 페이로드 조회, 실행 중인 리스너의 콜백 폴링 |
discover_status / discover_results |
Discover 실행의 진행 상황과 결과 |
project_info |
플로우 / 이슈 개수, 데이터베이스, 워크스페이스 바인딩, 선택 출처 |
get_current_context |
사용자가 지금 TUI에서 보고 있는 것 |
get_repeater_context |
Repeater 워크벤치 상태와 저장된 세션. 세션마다 id를 둘 다 싣는다 — 모든 repeater 툴이 받는 db_id, 그리고 TUI가 서브탭 칩에 그리는 1-based 번호 tui_index(6:POST /api) — 그래서 에이전트와 사용자가 같은 탭을 같은 이름으로 부른다. filter는 TUI의 /와 같은 서브탭 문법(tag: name: host: method: status:, -는 부정, 맨 단어는 검색)이고 query와 AND로 묶인다. include_content는 요청 헤드와 함께, 자격증명 헤더마다 비밀값 없이 배선만 밝히는 env_headers 모양(Authorization: Bearer $AUTH)을 준다. include_response_body는 저장된 마지막 응답 본문을 인라인한다 |
list_fuzz_runs / get_fuzz_run |
영구 Fuzzer 결과 집합을 나열하고 들여다봅니다. 지표는 result_index를 포함해 스칼라 전용 투영을 쓰므로 보관된 BLOB을 읽지 않습니다. include_content:true는 SQLite에서 상한이 걸린 접두 바이트로 최대 25행을 돌려줍니다 — max_head_bytes(기본 16 KiB, 최대 64 KiB)가 헤드를, max_body_bytes(기본 2 KiB, 최대 64 KiB)가 디코딩된 본문/원시 표본을 제한합니다. 원본 전체 크기와 헤드/원본/디코딩 절단 플래그가 무엇이 빠졌는지 말해 주며, include_sensitive:true는 상한이 걸린 정확한 접두 바이트를 선택하는 것이지 무제한 바이트가 아닙니다. 현재 형식 이전 스냅숏은 실행 메타데이터에 legacy:true로 표시됩니다 |
ql_reference |
쿼리 언어 레퍼런스 |
ql_explain |
쿼리를 실행하지 않고 진단. 요청을 쓰기 전에 필터를 점검할 때 사용 |
액션 도구(--read-only로 비활성화됨):
| 도구 | 용도 |
|---|---|
send_request |
HTTP 요청 전송 / 재전송(액티브; 기본적으로 History에 기록, $KEY 환경 토큰을 확장, 명시적으로 요청하지 않는 한 민감한 응답 헤더 값을 가림). reframe_grpc: true는 실제 전송되는 본문에 맞춰 단항 gRPC 메시지의 5바이트 길이 접두사를 다시 계산합니다 — 기본값은 꺼짐이므로 편집된 메시지도 캡처 당시의 접두사 그대로 나갑니다 |
send_websocket |
저장된 WebSocket Repeater 세션을 실행하고 응답을 수집 |
create_repeater / update_repeater / delete_repeater |
Repeater 세션 하나를 관리. 모든 응답이 id 옆에 tui_index를 싣고, 삭제는 없앤 탭 번호(was_tui_index)를 밝힌 뒤 나머지를 다시 번호 매긴다 |
create_repeaters |
캡처된 flow 여러 개에서 탭을 하나씩 시드 — OpenAPI 임포트의 두 번째 단계(아래 참고). 첫 세션을 만들기 전에 모든 flow의 존재를 확인한다 |
delete_repeaters / update_repeaters |
일괄 닫기, 일괄 재라벨(태그와 이름 접사만 — 요청 바이트를 쓰는 건 update_repeater다). 둘 다 필터가 아니라 명시적 id만 받는다: 먼저 get_repeater_context{filter}로 좁혀서, 읽은 집합과 작용한 집합이 같게. 삭제는 confirm:true가 필요하고, 모르는 id 하나면 호출 전체를 거절한다 |
move_repeater |
서브탭 스트립을 재배치 — 절대 탭 번호는 to_index, 한 칸 이동은 direction. 열려 있는 TUI가 알아서 새 순서를 반영한다 |
minimize_repeater |
Repeater 요청을 같은 응답이 재현되는 최소 형태로 줄임 |
create_issue / update_issue / delete_issue |
이슈 기록, 갱신, 삭제 |
add_link / remove_link |
이슈나 노트의 증거 포인터 연결 / 해제 |
create_note / update_note / delete_note |
프로젝트 노트 관리 |
create_rule / update_rule / set_rule_enabled / delete_rule |
Match & Replace 규칙 생성, 편집, 토글, 삭제(오가는 요청/응답의 헤드 또는 본문을 그 자리에서 재작성). 각각 scope를 받습니다 — project(기본값) 또는 모든 프로젝트에 적용되는 global |
create_rule_from_preset |
프리셋(list_rule_presets 참고)을 평범한 Match & Replace 규칙으로 설치. 규칙마다 create_rule을 한 번씩 부른 것과 같은 결과라, 설치 후에도 보이고 편집·비활성화됩니다. 생성된 id를 반환 |
create_extract_rule / update_extract_rule / set_extract_rule_enabled / delete_extract_rule |
응답에서 $NAME을 묶는 extract 규칙 관리. 이름을 바꾸면 옛 이름에 묶인 값은 라벨만 갈아 끼우는 게 아니라 버려지고, 비활성화하면 이름 자체가 선언 해제되어 그것을 주입하던 규칙이 낡은 값을 보내는 대신 다시 거부합니다 |
create_color_rule / update_color_rule / set_color_rule_enabled / move_color_rule / delete_color_rule |
Colormarker 규칙 관리. move_color_rule은 겉모습이 아니라 의미의 편집입니다 — 활성화된 첫 매칭이 그 행을 칠합니다. 각각 scope를 받습니다(project 기본값 또는 global) |
create_custom_color / update_custom_color / delete_custom_color |
기본 6색 위에 피커가 제공하는 전역 커스텀 색상 정의. 하나를 지워도 그것을 이름으로 쓰던 규칙은 삭제가 전파되지 않고 무해하게 남으며, 그 행들은 보이는 기본값으로 떨어집니다 |
grpc_reflect / grpc_forget |
대상의 grpc.reflection.v1(없으면 v1alpha)에 디스크립터를 요청해 프로젝트에 캐시하거나, 캐시된 대상을 버립니다. grpc_reflect는 아웃바운드 전송이므로 다른 것들과 똑같이 스코프 게이트를 지납니다 |
create_view / update_view / delete_view |
저장된 History 뷰 생성, 편집, 스코프 이동, 삭제. 각각 scope를 받습니다 — project(기본값) 또는 global. 쿼리는 들어올 때 검사합니다. 모든 항이 버려질 쿼리는 거절하는데, 아무것도 좁히지 못하면서 모든 표면의 칩은 좁히고 있다고 주장하게 되기 때문입니다 |
preview_rule |
규칙을 만들기 전에, 저장된 플로우 중 몇 개가 바뀌었을지 추정 |
import_flows |
HAR / URL 목록 / OpenAPI / Postman / Insomnia / Burp / WSDL 파일을 History로 일괄 임포트 |
delete_flow / clear_history |
플로우 하나 삭제, 또는 캡처된 History 전체 삭제 |
set_sitemap_tag |
Sitemap 경로에 자유 형식 메모 고정 |
create_project / switch_project / delete_project |
프로젝트 생성 또는 다시 열기, 이 서버를 다른 프로젝트로 전환, 프로젝트 삭제. 삭제는 2단계로, dry_run 후 확인 토큰 필요 |
add_scope_rule / update_scope_rule / delete_scope_rule / set_scope_enabled |
프로젝트의 include / exclude 규칙 편집과 스코프 렌즈 토글 |
set_sandbox |
하드 컨테인먼트. 켜면 프록시가 스코프가 허용한 것만 전달하고 나머지는 차단 |
set_env_var / delete_env_var |
$KEY 치환이 읽는 프로젝트 env 토큰 관리 |
create_session_slot / update_session_slot / delete_session_slot |
세션 슬롯 관리 — Authorize 탭의 identities 카드가 편집하는 바로 그 목록이고, authorize_start가 재생하는 집합입니다 |
set_active_session_slot |
모든 아웃바운드 요청이 어느 신원으로 나갈지 선택합니다. 그 슬롯의 헤더 오버레이가 최종 와이어 바이트에 적용되고 $NAME은 그 바인딩 테이블에서 해소됩니다. 이 서버 프로세스만 들고 있고 저장되지 않으므로, 새 연결은 캡처된 그대로 시작합니다 |
add_host_override / update_host_override / delete_host_override |
호스트 → IP 다이얼 맵 관리(요청은 그대로 두고 접속 IP만 변경) |
probe_promote / probe_dismiss / probe_delete |
Probe 발견 항목을 Issues로 승격, 기각, 또는 삭제 |
set_probe_mode |
스캔 모드 설정: off, passive, active, aggressive(허가된 대상 전용) |
create_probe_rule / update_probe_rule / delete_probe_rule / set_probe_rule_enabled |
커스텀 매치 규칙 관리와 스캔 규칙 활성화 / 비활성화 |
create_oast_provider / update_oast_provider / delete_oast_provider / set_oast_provider_enabled |
oast_start가 사용할 OAST 프로바이더 관리 |
fuzz_start / fuzz_status / fuzz_results / fuzz_stop |
Fuzzer 구동. save_results:true는 바이트 상한이 걸린 비동기 기록자를 통해 모든 행을 영구 저장하고 데이터베이스 run_id를 돌려줍니다. 저장소 백프레셔가 걸리면 나가는 트래픽은 멈추지 않고 저장만 실패로 표시됩니다. 이것은 상한이 걸린 선택적 라이브 잡 캐시나 record_history와는 별개입니다. fuzz_start{fields: ["role"]}는 단항 요청의 스키마가 아는 gRPC 필드를 스윕합니다 — 페이로드는 필드 선언을 거쳐 바이트가 되고, 메시지의 나머지 바이트는 캡처에서 그대로 복사되며, 길이 접두사가 따라옵니다. 바이트 위치를 쓰는 gRPC 스윕에서 페이로드가 메시지 길이를 바꾸면 grpc_stale_prefix로 보고하며, fuzz_start{reframe_grpc: true}는 보고 대신 접두사를 다시 계산합니다. fuzz_results는 매치되지 않았어도 런이 관찰한 사실이 있는 행(재전송, 리트라이, 잘린 응답)을 함께 보관하므로 각 행의 matched를 읽거나 matched_only: true를 넘기세요 |
delete_fuzz_run |
영구 퍼즈 실행 하나와 그 결과를 삭제합니다. 살아 있는 기록자가 확인되면 거부합니다. force_stale:true는 죽은 프로세스가 남긴 running/saving 행을 지우며, 다른 gori가 저장 중일 때는 절대 쓰면 안 됩니다 |
mine_start / mine_status / mine_results / mine_stop |
Param Miner 구동 |
sequence_start / sequence_status / sequence_results / sequence_stop |
라이브 리플레이로 토큰을 수집해 평가(결과는 리포트만 반환, 토큰은 반환하지 않음) |
authorize_start / authorize_status / authorize_results / authorize_stop |
캡처된 플로우를 여러 아이덴티티로 재전송하고 각 응답을 기준선과 비교 — 접근 제어 결함. 결과는 access_control(BYPASS/enforced/review/error/nothing_sent)과 페이징 없는 bypasses 목록으로 시작합니다 |
discover_start / discover_stop |
엔드포인트 스파이더링 & 브루트포스(discover_status / discover_results로 폴링) |
oast_start / oast_stop |
즉석 OAST 페이로드 등록 후 콜백 폴링(oast_poll로 히트 조회). 재개한 세션에 oast_stop을 쓰면 폴링만 멈추고 세션은 다시 재개할 수 있게 남습니다 |
oast_resume / oast_release |
저장된 세션을 다시 살려 이전에 심어둔 페이로드가 계속 resolve되게 하고(폴링 결과는 프로젝트에 저장됩니다), 끝난 engagement는 등록 해제합니다 — 콜백은 남습니다 |
list_jobs / get_job / stop_job |
작업 종류를 가로질러 처리: 이번 세션이 시작한 모든 fuzz와 mine 작업 나열, 또는 id로 하나를 조회하고 중지 |
intercept_forward / intercept_forward_edit / intercept_drop |
홀드된 메시지를 바이트 그대로 내보내거나, 수정한 와이어 바이트로 내보내거나, 드롭 |
intercept_toggle / intercept_set_filter / intercept_set_direction |
캐치 활성화 및 해제, 조건 쿼리 설정, 홀드할 방향 선택 |
액션 도구는 안전을 위해 상한이 있습니다: fuzz, mine, sequence, discover, authorize 작업은 총 요청 수, 동시성, 저장 결과 수가 제한됩니다. authorize의 상한은
플로우 × 아이덴티티를 세며, 상한을 넘는 선택은 잘라서 실행하는 대신 시작 전에 거부됩니다. 잘린 실행은 보내지도 않은 플로우를 "enforced"로 보고하게 되기 때문입니다.create_rule로 생성된 규칙은gori run과 새로 열린 TUI에 적용됩니다. 이미 실행 중인 TUI는 규칙을 다시 로드한 뒤에만 적용합니다.
스펙에서 Repeater 탭으로
import_flows는 OpenAPI/Swagger(JSON·YAML)를 읽고 servers[0].url을 각 오퍼레이션 경로와 합쳐 준다. 베이스 경로를 손으로 붙일 일이 없다는 뜻이고, 스펙에서 탭 한 줄까지는 호출 세 번이다.
import_flows{kind: "oas", path: "openapi.yaml"}
list_history{query: "src:import"} → flow id들
create_repeaters{flow_ids: [...], name_prefix: "oas: ", tags: "spec"}
create_repeaters는 첫 세션을 만들기 전에 모든 flow의 존재를 확인하고, create_repeater{flow_id} 하나가 쓰는 것과 같은 경로로 각각을 시드한 뒤, 준 순서대로 덧붙인다. 순서는 move_repeater로 바꾸고, 정리는 delete_repeaters로 한다.
라이브 인터셉트
에이전트가 나중에 History를 읽는 대신, 인터셉트 루프 안에 나란히 앉을 수 있습니다. 캡처 락을 쥔 TUI 세션이 홀드된 메시지를 에이전트 쪽으로 미러링하고 에이전트가 보낸 명령을 받아 처리하므로, intercept_list → intercept_get → intercept_forward_edit은 직접 손으로 도는 것과 같은 루프입니다.
변경을 일으키는 쪽(intercept_forward, intercept_forward_edit, intercept_drop, intercept_toggle, intercept_set_filter, intercept_set_direction)은 --read-only에서 비활성화되며, 라이브 캡처 세션이 락을 쥐고 있지 않으면 모두 거부합니다. 프록시가 실제로 트래픽을 홀드하고 있지 않으면 내보낼 것 자체가 없기 때문입니다.
에이전트의 행동은 조용히 지나가지 않고 드러납니다. 각 행동은 에이전트에서 온 것으로 표시되어 알림 센터에 남고 사용자 본인의 행동과 다르게 렌더링되므로, 다른 탭을 보는 동안 코파일럿이 트래픽에 무엇을 했는지 확인할 수 있습니다.
에이전트를 켜둔 채 자리를 뜨기 전에 알아둘 안전 규칙이 하나 있습니다. 홀드된 메시지는 원래 사람의 결정을 무한히 기다립니다. 키보드 앞에 사람만 있을 때는 그게 맞는 동작입니다. 하지만 해당 세션에서 에이전트가 인터셉트 큐에 붙고 나면, gori는 아무도 보고 있지 않은 항목에 대해 30초 자동 포워드를 켭니다. 홀드 도중 죽은 클라이언트가 연결을 영영 막아버리지 못하게 하기 위해서입니다. 에이전트가 붙지 않은 세션은 자동 포워드를 하지 않습니다.
한 번에 한 호출
도구는 도착한 순서대로 하나씩 실행되고, 응답도 그 순서로 돌아옵니다 — 퍼즈나 느린 send_request가 다음 호출과 겹치지 않습니다. 다만 두 메시지는 항상 즉시 응답합니다. ping은 클라이언트의 생존 확인이 긴 호출 뒤에 밀려 "서버가 죽었다"는 판정을 받지 않도록, notifications/cancelled는 더 이상 기다리지 않는 요청의 응답을 보내지 않도록 하기 위해서입니다. 취소는 이미 진행 중인 작업을 중단시키지는 않습니다 — 그 요청은 끝까지 실행되고, 응답만 전송되지 않습니다.
MCP 이음새인 이유
gori는 의도적으로 도구 내 AI 챗을 두지 않습니다. 지능은 도구 바깥, 곧 MCP로 접근할 수 있는 곳에 있습니다. 덕분에 모델을 직접 고를 수 있고, 트래픽이 의도치 않은 곳으로 흘러가지 않으며, 동일한 인터페이스가 스크립트와 에이전트 양쪽을 모두 지원합니다. gori run은 비대화형 경로를, MCP는 대화형 에이전트 경로를 담당합니다.
다음 단계
- AI 설정: 에이전트를 연결하고 첫 요청을 구동하는 단계별 안내
- Scripting: 또 하나의 자동화 경로 — 파이프라인과 CI를 위한
gori run - CLI Reference: 전체
gori mcp플래그 - Query Language: 에이전트가 필터링에 사용하는 문법