Tool, Skill, Search, Slash Command
Capability는 사용자가 실행할 수 있는 한 가지 작업과 그 실행 경계를 선언합니다. UI에서 버튼을 누르거나 AI가 Tool을 선택해도 같은 capability가 실행됩니다.
Capability kind
tool과 skill은 runtime adapter가 필수입니다. provider는
runtime.role: "search"가 필요합니다. background runtime을 선언하면
interval_minutes는 15~10,080입니다.
Runtime adapter
선언형 adapter 예
Text template
{
"adapter": "text_template",
"template": "요청을 다음 형식으로 정리합니다: {query}"
}
복잡한 표현식이나 코드를 template에 넣지 않습니다.
Calendar store
{
"adapter": "calendar_store",
"operation": "reminder",
"namespace": "study.schedule"
}
일반 저장에는 storage, 알림 예약에는 storage와 notifications가 모두 필요합니다.
HTTP JSON
{
"adapter": "http_json",
"connector_id": "notes_api",
"method": "POST",
"request_body": {"query": "{query}"},
"response": {"summary_path": "message", "data_path": "data"}
}
GET 검색 API는 request_params로 실제 API의 인수 이름과 입력을 연결합니다.
{
"adapter": "http_json",
"endpoint": "https://api.github.com/search/repositories?per_page=2&sort=stars",
"method": "GET",
"request_params": {"q": "{query}", "page": "{arguments.page}"},
"response": {
"collection_path": "items",
"id_path": "id",
"title_paths": ["full_name"],
"url_path": "html_url",
"content_paths": ["description"],
"max_items": 2,
"summary_prefix": "저장소 검색"
}
}
request_params는 GET·POST 모두 URL 인수에 적용합니다. endpoint의 기존 인수를 유지하며 이름이 겹치면 새 값으로 교체합니다. 문자열을 URL에 직접 이어 붙이지 않습니다.- 값은 문자열·숫자·참거짓 또는 그 목록입니다. 목록은 같은 이름을 여러 번 보냅니다.
null과 누락된 argument는 생략합니다. 최대 32개 이름, 이름당 64개 값, URL encoding 후 16 KiB까지 허용합니다. request_params를 생략한 GET은 기존처럼query=<입력>을 보냅니다.{}로 지정하면 자동query를 추가하지 않습니다. POST의request_body는 URL 인수와 별도로 전송합니다.{query}와{arguments.<key>}를 값 전체로 쓰면 JSON 타입을 유지합니다. 예를 들어 숫자0과 참거짓false를 빈 값으로 바꾸지 않습니다.
응답 형태에 맞춰 둘 중 하나를 사용합니다.
- 단일 응답:
summary_path는 비어 있지 않은 문자열,data_path는 객체를 가리킵니다.data_path를 생략하면 응답 객체 전체를 반환합니다. 고정 요약이 필요하면summary_path대신summary_prefix를 지정합니다. 지정한 경로가 없거나 타입이 다르면 성공으로 처리하지 않습니다. 단일 경로에는*를 쓰지 않습니다. - 목록 응답: 위 예제의
collection_path,title_paths등으로 항목을 정규화해data.items와data.results로 반환합니다. 단일 응답의 경로 설정과 혼합하지 않습니다.
response를 생략하면 최상위 summary 또는 message 문자열과 응답 객체 전체를 사용합니다.
GET과 POST만 지원하며 redirect를 따라가지 않습니다. endpoint는 공개 HTTPS이고 SSRF 보호 경계를 통과합니다. bearer token은 Host가 Connection에서 주입하며 fragment에 직접 쓰지 않습니다.
Remote MCP
Morit Host 1.7.12의 plugin outbound MCP adapter는 mcp_http입니다. 개발자용 Local/Remote Plugin
MCP 서버와 이름이 비슷하지만, 설치된 플러그인이 선언한 외부 MCP Tool을 Host 경계에서 호출하는
runtime입니다.
{
"adapter": "mcp_http",
"connector_id": "research_mcp",
"tool_name": "search",
"protocol_version": "2025-11-25",
"argument_template": {"query": "{query}"}
}
Host가 initialize → initialized → tools/call session을 관리합니다. credential이 거부되면 지원되는
연결에서 한 번 갱신한 뒤 새 session으로 재시도합니다. MCP의 isError: true는 성공 결과로 바꾸지
않습니다.
Sandbox Python
{
"adapter": "sandbox_python",
"entrypoint": "src/analyze.py"
}
entrypoint는 package 상대 POSIX 경로이고 각 src/*.py 파일과 정확히 1:1이어야 합니다. 코드는
Archive API process에서 실행하지 않고 별도 rootless Docker + gVisor worker와 lease-fenced broker를
거칩니다. 결과는 다음 객체여야 합니다.
{
"summary": "분석을 완료했습니다.",
"data": {},
"evidence": []
}
summary 없이 sandbox 경로나 내부 로그만 반환하면 완료된 결과가 아닙니다.
AI 채팅이 직접 호출한 signed sandbox_python 작업은 API 재시작이나 결과 조회 지연 시
같은 실행 ID로 이어집니다. 완료한 계산을 다시 실행하지 않으며, worker의 실제 장애 복구는
기존 실행권과 재시도 제한을 따릅니다. 사용자가 채팅을 중지하면 연결된 작업도 취소합니다.
실행 중 package·설정·권한이 바뀌면 이전 결과를 새 플러그인 상태에 적용하지 않습니다.
이 경로에서 반환한 저장소·알림 Host action은 적용 기록과 함께 저장하여 복구 때 중복
적용하지 않습니다. 계산이 읽은 저장소가 적용 전에 변경되면 PLUGIN_STORAGE_CONFLICT를
반환하고 현재 값을 보존합니다. 새 요청으로 최신 데이터를 다시 읽어 계산하세요. 같은 작업이
이전과 다른 action을 내면 PLUGIN_OUTCOME_CHANGED로 거절합니다.
이 복구 계약은 UI 직접 호출이나 다른 adapter에 일괄 적용되지 않습니다. HTTP/MCP 등
외부 변경의 결과를 확인할 수 없는 중단은 outcome_unknown으로 전달하며 자동 반복하지 않습니다.
UI와 AI 노출
AI는 enabled 상태이고 사용자에게 허용된 capability만 발견합니다. 제목과 설명은 모델이 언제 이 기능을 선택할지 판단할 수 있게 동작과 입력을 구체적으로 씁니다.
Slash Command나 UI extension은 선택적 진입점일 뿐 discovery 조건이 아닙니다. 활성 Plugin의 모든
tool·skill은 매 AI turn의 actor-scoped capability catalog에 자동 포함됩니다. 설치·업데이트·
enable/disable·권한 변경·Connection 생성/수정/삭제·OAuth 완료·삭제 뒤 Host는 Instance route cache를
즉시 비워 다음 turn과 실행이 새 generation을 사용하게 합니다.
{
"id": "workspace.notes.search",
"kind": "tool",
"title": "노트 검색",
"description": "연결한 서비스에서 노트를 검색합니다.",
"permissions": ["network", "credentials"],
"timeout_seconds": 10,
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"additionalProperties": false
},
"output_schema": {
"type": "object",
"properties": {"items": {"type": "array", "items": {"type": "object"}}},
"required": ["items"]
},
"runtime": {"adapter": "http_json", "connector_id": "workspace_api", "method": "GET"}
}
input_schema/output_schema는 root object인 안전한 JSON Schema 부분집합입니다. AI Tool,
UI data source·invoke, Host 실행과 결과 binding이 같은 schema를 사용합니다. CLI/MCP validate는
필수 필드·타입·범위와 data.<source>.data... 경로를 package build 전에 검사합니다.
Host는 실행 전에 arguments를 검사합니다. 필수 입력 누락·타입·범위·추가 필드 위반은
EXTENSION_INPUT_INVALID이며 adapter를 호출하지 않습니다. 실행 후 result.data가
output schema와 다르면 EXTENSION_OUTPUT_INVALID로 처리하며 결과나 Host action을
전달하지 않습니다. 오류 메시지에 입력값·외부 응답·credential을 포함하지 않으며 자동으로
재시도하지 않습니다. 이미 외부에서 일어난 변경까지 되돌리는 계약은 아닙니다.
schema를 선언하지 않은 capability는 기존 동작을 유지합니다. 일반 capability 데이터에 A2UI의 전체 문자열·배열 제한을 추가로 적용하지 않으며, 선언한 schema와 기존 JSON 크기 제한을 검사합니다. Response UI에는 별도의 A2UI 데이터 제한도 적용합니다.
UI invoke가 capability를 참조할 때도 enabled, generation, permission, timeout을 다시 확인합니다.
UI에 capability ID를 사용자용 라벨로 표시하지 않습니다.
Slash Command
Slash Command는 같은 package의 tool 또는 skill에 고정된 진입점입니다.
{
"id": "workspace.notes.search_command",
"command": "notes",
"title": "노트 검색",
"description": "연결한 서비스에서 노트를 검색합니다.",
"capability": "workspace.notes.search",
"argument_template": {}
}
Slash Command를 선언하지 않아도 Tool discovery는 유지됩니다. 여러 command가 같은 이름을 사용할 수
없고 /는 command 값에 포함하지 않습니다.
실패와 복구
Capability는 권한 부족, 연결 필요, timeout, retryable network 오류, 잘못된 응답을 구분합니다. retry는 Connector에 선언한 bounded 정책만 사용합니다. Tool 하나가 실패했다고 AI 요청 전체를 즉시 포기하지 말고, 오케스트레이터가 대체 Tool·재연결·부분 결과를 선택할 수 있도록 공개 오류를 구체적으로 유지합니다.
실행 준비 상태의 대표 code는 EXTENSION_NOT_FOUND, EXTENSION_PERMISSION_DENIED,
EXTENSION_CONNECTION_REQUIRED, EXTENSION_DISABLED, EXTENSION_VERSION_MISMATCH,
EXTENSION_DEPENDENCY_REQUIRED입니다. 빈 Tool 목록이나 일반 실패 문구로 합치지 마세요.
권한과 알림은 권한, 설정, 저장소, 알림을 참고하세요.
SDK 1.7.13: 슬래시 명령 선택의 식별자
앱이 메뉴에서 명령을 선택하면 메시지 텍스트와 함께 선택한 설치 인스턴스 ID와 명령 ID를 전송합니다. 서버는 클라이언트가 주장하는 capability를 신뢰하지 않고 현재 설치·권한·연결 카탈로그에서 대상을 다시 찾습니다. 전송 재시도도 같은 선택 정보를 사용합니다.
직접 입력한 /command 인자는 명령 이름이 유일하면 기존대로 동작합니다. 같은 이름의 명령이
여러 설치에 존재하면 SLASH_COMMAND_AMBIGUOUS로 플러그인 선택을 요청합니다. 삭제되거나
권한을 잃은 선택은 SLASH_COMMAND_UNAVAILABLE로 종료하며 다른 플러그인으로 바꾸어 실행하지 않습니다.