@morit/cli 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -5
- package/assets/plugin_contract.json +4 -2
- package/bin/morit.js +0 -0
- package/package.json +1 -1
- package/src/cli.js +5 -0
- package/src/workspace.js +83 -12
- package/assets/docs/README.md +0 -107
- package/assets/docs/ai-response-and-timeline.md +0 -211
- package/assets/docs/ai-skill-and-docx-workflow.md +0 -83
- package/assets/docs/app-builder.md +0 -56
- package/assets/docs/authentication.md +0 -140
- package/assets/docs/components.md +0 -225
- package/assets/docs/design-tokens-responsive.md +0 -171
- package/assets/docs/docs-index.json +0 -94
- package/assets/docs/examples-notion.md +0 -83
- package/assets/docs/examples-school-life.md +0 -79
- package/assets/docs/getting-started.md +0 -132
- package/assets/docs/information-hierarchy.md +0 -81
- package/assets/docs/instances-and-connectors.md +0 -93
- package/assets/docs/lifecycle-and-api.md +0 -169
- package/assets/docs/local-cli.md +0 -125
- package/assets/docs/manifest.md +0 -247
- package/assets/docs/packaging-and-testing.md +0 -131
- package/assets/docs/permissions-and-data.md +0 -149
- package/assets/docs/platform-compatibility.md +0 -62
- package/assets/docs/plugin-storage.md +0 -175
- package/assets/docs/project-structure.md +0 -102
- package/assets/docs/remote-mcp.md +0 -158
- package/assets/docs/school-life-privacy.md +0 -55
- package/assets/docs/screens-layout-navigation.md +0 -95
- package/assets/docs/sdk-and-mcp.md +0 -199
- package/assets/docs/tool-and-skill.md +0 -188
- package/assets/docs/troubleshooting.md +0 -121
- package/assets/docs/ui-extensions.md +0 -75
- package/assets/docs/ui-runtime-v2.md +0 -406
- package/assets/docs/verification.md +0 -133
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# 학교 생활 플러그인 개인정보 처리 안내
|
|
2
|
-
|
|
3
|
-
최종 수정일: 2026-08-17
|
|
4
|
-
|
|
5
|
-
학교 생활 플러그인은 학생이 선택한 학교, 학년, 반을 기준으로 시간표·급식·학사일정을
|
|
6
|
-
보여 주고 기기 안에서 알림과 아침 브리핑을 구성합니다. 플러그인 설치 전에 아래 데이터
|
|
7
|
-
사용 범위를 확인할 수 있으며, 설치 후 설정에서 언제든 초기화하거나 삭제할 수 있습니다.
|
|
8
|
-
|
|
9
|
-
## 사용하는 정보
|
|
10
|
-
|
|
11
|
-
- 사용자가 직접 선택한 학교 코드, 교육청 코드, 학년, 반
|
|
12
|
-
- 알레르기 필터, 알림 사용 여부와 알림 시간
|
|
13
|
-
- 중복 알림을 막기 위한 마지막 실행 날짜
|
|
14
|
-
- 사용자가 Morit AI에 추가·수정·완료·삭제를 요청한 학교 할 일
|
|
15
|
-
- 교육부 NEIS 공개 API가 제공하는 시간표, 급식, 학사일정
|
|
16
|
-
|
|
17
|
-
이 플러그인은 이름, 주민등록번호, 학생 번호, 성적, 연락처, 계정 비밀번호를 요청하거나
|
|
18
|
-
수집하지 않습니다. 별도의 OAuth 계정이나 API key도 요구하지 않습니다.
|
|
19
|
-
|
|
20
|
-
## 처리 목적과 저장 위치
|
|
21
|
-
|
|
22
|
-
학교·학년·반 설정은 사용자가 요청한 학교 정보를 반복 입력하지 않도록 해당 Morit
|
|
23
|
-
사용자의 Plugin Instance 저장소에만 보관합니다. 조회한 공개 학교 정보는 화면 표시와
|
|
24
|
-
알림 생성에만 사용합니다. 다른 사용자나 다른 Instance와 공유하지 않습니다.
|
|
25
|
-
|
|
26
|
-
학교 할 일은 `tasks` namespace에 저장됩니다. 사용자가 설치/설정에서 `ai_storage` 권한을 허용하면
|
|
27
|
-
Morit AI가 이 namespace를 별도 매 작업 승인 없이 읽고 변경할 수 있습니다. Host는 현재 사용자와
|
|
28
|
-
활성 Instance를 고정하고 다른 namespace·사용자·플러그인 접근을 차단합니다.
|
|
29
|
-
|
|
30
|
-
외부 요청은 선언된 NEIS HTTPS endpoint로만 제한되며 Morit의 SSRF filtering proxy를
|
|
31
|
-
통과합니다. 플러그인 코드에는 운영 credential이나 사용자 token이 포함되지 않습니다.
|
|
32
|
-
|
|
33
|
-
## 보관과 삭제
|
|
34
|
-
|
|
35
|
-
Manifest의 data policy는 `purge`입니다. 사용자가 Plugin Instance 또는 플러그인을
|
|
36
|
-
삭제하면 해당 Instance의 설정, 조회 cache, 알림 상태를 함께 삭제합니다. 앱 데이터
|
|
37
|
-
초기화 후 서버에 남은 개인 Plugin Instance도 계정의 플러그인 관리 화면에서 삭제할 수
|
|
38
|
-
있습니다.
|
|
39
|
-
|
|
40
|
-
## 권한
|
|
41
|
-
|
|
42
|
-
- 네트워크: NEIS 공개 학교 정보 조회
|
|
43
|
-
- 저장소: 학교·학년·반과 사용자 설정 저장
|
|
44
|
-
- AI 저장소: 사용자가 허용한 경우 학교 할 일 목록의 조회·추가·수정·삭제
|
|
45
|
-
- 알림: 사용자가 켠 경우 다음 학교 일정과 아침 브리핑 알림 예약
|
|
46
|
-
- 백그라운드: 사용자가 켠 경우 공개 학교 정보 갱신과 브리핑 준비
|
|
47
|
-
|
|
48
|
-
권한이 꺼져 있거나 네트워크가 실패하면 해당 기능만 사용할 수 없다는 상태를 표시하며,
|
|
49
|
-
오류를 숨기거나 임의의 학교 정보를 생성하지 않습니다.
|
|
50
|
-
|
|
51
|
-
## 문의와 변경
|
|
52
|
-
|
|
53
|
-
플러그인 기능과 구현은 [학교 생활 예제 문서](examples-school-life.md)에서 확인할 수
|
|
54
|
-
있습니다. 처리 항목이나 외부 제공 범위가 바뀌면 새 Plugin version과 이 문서를 함께
|
|
55
|
-
갱신하며, 권한이 추가되는 경우 사용자가 설치 전에 다시 확인할 수 있게 표시합니다.
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# 화면, 레이아웃, 내비게이션
|
|
2
|
-
|
|
3
|
-
플러그인 UI는 “어디에 나타나는가”를 정하는 extension point와 “무엇을 그리는가”를 정하는 Runtime
|
|
4
|
-
tree로 나뉩니다. 먼저 사용자 흐름을 화면 단위로 나눈 뒤 각 화면의 JSON을 작성하세요.
|
|
5
|
-
|
|
6
|
-
## 화면 역할부터 정하기
|
|
7
|
-
|
|
8
|
-
| 사용자 목적 | extension point | 권장 내용 |
|
|
9
|
-
|---|---|---|
|
|
10
|
-
| 홈에서 상태를 빠르게 확인 | `card` | 핵심 상태 1~3개와 명확한 다음 행동 |
|
|
11
|
-
| 홈에서 즉시 실행 | `action` | 짧은 라벨의 단일 행동 |
|
|
12
|
-
| 플러그인의 주 작업 | `screen` | 목록, 상세, 편집 등 독립 화면 |
|
|
13
|
-
| 홈 메뉴 진입점 | `menu` | 자주 찾지만 상시 노출할 필요 없는 화면 |
|
|
14
|
-
| 플러그인 설정 | `settings` | 권한, 알림, 표시 옵션, 계정 진입 |
|
|
15
|
-
| AI와 함께 작업 | `workspace` | 대화 맥락과 함께 보는 도구 화면 |
|
|
16
|
-
| AI 실행 결과 | `response` | 해당 AI 메시지에 붙는 읽기 중심 결과 |
|
|
17
|
-
|
|
18
|
-
`surface`는 기존 패키지 호환용 일반 화면 point입니다. 새 독립 화면에는 역할이 더 명확한
|
|
19
|
-
`screen`을 사용합니다.
|
|
20
|
-
|
|
21
|
-
## 권장 화면 흐름
|
|
22
|
-
|
|
23
|
-
```text
|
|
24
|
-
Home card/action
|
|
25
|
-
→ Overview screen
|
|
26
|
-
├─ List/filter state
|
|
27
|
-
├─ Detail screen
|
|
28
|
-
└─ Edit dialog 또는 sheet
|
|
29
|
-
|
|
30
|
-
Plugin detail
|
|
31
|
-
→ Settings screen
|
|
32
|
-
├─ Account connections
|
|
33
|
-
├─ Notifications
|
|
34
|
-
└─ Display preferences
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
첫 화면은 사용자가 현재 상태와 다음 행동을 5초 안에 파악할 수 있어야 합니다. 원시 JSON,
|
|
38
|
-
capability ID, package ID, 디버그 상태는 사용자 화면에 두지 않습니다.
|
|
39
|
-
|
|
40
|
-
## 레이아웃 선택
|
|
41
|
-
|
|
42
|
-
- `column`: 기본 읽기 흐름. 모바일에서 가장 안전한 시작점입니다.
|
|
43
|
-
- `row`: 짧은 상태와 행동을 나란히 놓을 때 사용합니다. 좁은 폭에서는 `stack_at`으로 세로 전환합니다.
|
|
44
|
-
- `wrap`: chip이나 짧은 필터처럼 항목 너비가 다른 반복 요소에 사용합니다.
|
|
45
|
-
- `grid`: 같은 중요도의 카드·metric을 반복할 때 사용합니다. `min_item_width`로 열 수를 줄입니다.
|
|
46
|
-
- `section`: 제목이 있는 정보 그룹입니다. 한 화면에 section이 너무 많아지면 화면을 분리합니다.
|
|
47
|
-
- `card`: 배경과 경계가 필요한 독립 정보나 누를 수 있는 요약에 사용합니다.
|
|
48
|
-
|
|
49
|
-
중첩 card와 section을 장식 목적으로 반복하지 마세요. 부모 하나로 관계가 설명되면 추가 surface는
|
|
50
|
-
정보 계층을 흐립니다.
|
|
51
|
-
|
|
52
|
-
## 내비게이션
|
|
53
|
-
|
|
54
|
-
같은 플러그인의 다른 UI extension으로 이동할 때 `navigate`를 사용합니다.
|
|
55
|
-
|
|
56
|
-
```json
|
|
57
|
-
{
|
|
58
|
-
"type": "button",
|
|
59
|
-
"props": {"label": "전체 시간표 보기", "style": "tonal"},
|
|
60
|
-
"action": {"type": "navigate", "target": "school_life.week"}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
`target`은 같은 manifest에 선언된 UI extension ID여야 합니다. 현재 화면을 닫고 이전 Host 화면으로
|
|
65
|
-
돌아갈 때는 `back`을 사용합니다.
|
|
66
|
-
|
|
67
|
-
```json
|
|
68
|
-
{"type": "back"}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
탭처럼 보여야 하는 단순 필터는 새 화면을 만들지 말고 `chip`과 `set_state`로 처리합니다. 반대로
|
|
72
|
-
목록과 상세처럼 제목, 스크롤 위치, 뒤로가기 의미가 달라지는 정보는 별도 `screen`으로 나눕니다.
|
|
73
|
-
|
|
74
|
-
## 상단 메뉴와 설정
|
|
75
|
-
|
|
76
|
-
Host가 화면 제목, 뒤로가기, 플러그인 메뉴를 담당합니다. JSON tree에서 별도 앱 바를 흉내 내지
|
|
77
|
-
않습니다. 화면의 overflow 메뉴는 해당 화면에서 자주 쓰지 않는 보조 행동만 담습니다. 계정 연결은
|
|
78
|
-
settings 화면의 Connection 관리로 제공하며 동일 패키지 root Instance를 복제하는 “계정 추가”
|
|
79
|
-
동작을 만들지 않습니다.
|
|
80
|
-
|
|
81
|
-
## 상태별 화면
|
|
82
|
-
|
|
83
|
-
한 data source에는 적어도 다음 상태를 고려합니다.
|
|
84
|
-
|
|
85
|
-
| 상태 | 화면 동작 |
|
|
86
|
-
|---|---|
|
|
87
|
-
| 첫 로딩 | 기존 레이아웃 크기를 유지하는 진행 표시 |
|
|
88
|
-
| 데이터 없음 | 무엇이 비었는지와 시작 행동을 설명하는 `empty` |
|
|
89
|
-
| 재시도 가능 오류 | 원인 요약과 `refresh` 행동 |
|
|
90
|
-
| 권한·연결 필요 | 필요한 권한/계정과 설정 진입 |
|
|
91
|
-
| 갱신 실패 + 기존 데이터 | 기존 내용을 유지하고 작은 상태 안내 |
|
|
92
|
-
|
|
93
|
-
전체 화면을 한 data source의 오류로 교체하지 않습니다. 오류는 영향을 받은 section에 격리합니다.
|
|
94
|
-
|
|
95
|
-
다음은 [기본·커스텀 컴포넌트](components.md)입니다.
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
# CLI와 AI 에이전트 MCP
|
|
2
|
-
|
|
3
|
-
Morit은 두 MCP 연결을 제공합니다.
|
|
4
|
-
|
|
5
|
-
| 연결 | 실행 위치 | 적합한 작업 |
|
|
6
|
-
|---|---|---|
|
|
7
|
-
| Local `@morit/plugin-mcp` | 개발자 PC의 stdio process | 로컬 파일 직접 편집, 빠른 preview/build |
|
|
8
|
-
| Remote `https://morit-api.moring.co/mcp` | Morit Cloud Streamable HTTP | 파일시스템 없는 AI client, 조직 Project·Secret·Deployment |
|
|
9
|
-
|
|
10
|
-
두 연결은 SDK 1.7.12의 `@morit/cli`와 Host Plugin contract를 사용합니다. 계약에는 mode별 Plugin
|
|
11
|
-
Theme, Local Storage/AI access, 표준 Storage Tool, UI binding, A2UI Response catalog와 safe
|
|
12
|
-
preview가 포함됩니다. Local source에 접근할 필요가 없으면 Remote를, 현재 저장소 파일을 직접
|
|
13
|
-
고쳐야 하면 Local을 선택합니다.
|
|
14
|
-
|
|
15
|
-
## 준비
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
node --version
|
|
19
|
-
npx -y @morit/cli login
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Node.js 20.12 이상이 필요합니다. Local MCP의 Cloud Tool을 쓸 때만 CLI 로그인이 필요합니다.
|
|
23
|
-
MCP 설정이나 Project source에 access token을 직접 넣지 않습니다.
|
|
24
|
-
|
|
25
|
-
## Codex에 Local MCP 추가
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
codex mcp add morit-plugin-local -- npx -y @morit/plugin-mcp
|
|
29
|
-
codex mcp list
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
특정 workspace만 허용하려면 환경변수로 절대 경로를 전달합니다.
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
codex mcp add morit-plugin-local \
|
|
36
|
-
--env MORIT_PLUGIN_WORKSPACE=/absolute/development/path \
|
|
37
|
-
-- npx -y @morit/plugin-mcp
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Codex CLI/IDE/Desktop의 현재 MCP 설정 방식은
|
|
41
|
-
[공식 OpenAI MCP 문서](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)를 참고하세요. Codex
|
|
42
|
-
TUI에서는 `/mcp`로 활성 서버를 확인합니다.
|
|
43
|
-
|
|
44
|
-
## Codex에 Remote MCP 추가
|
|
45
|
-
|
|
46
|
-
```bash
|
|
47
|
-
codex mcp add morit-plugin-remote --url https://morit-api.moring.co/mcp
|
|
48
|
-
codex mcp login morit-plugin-remote
|
|
49
|
-
codex mcp list
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
브라우저에서 Morit 계정으로 로그인하고 조직과 scope를 승인합니다. static bearer token을 config에
|
|
53
|
-
넣지 않습니다.
|
|
54
|
-
|
|
55
|
-
## Claude Code
|
|
56
|
-
|
|
57
|
-
Local stdio:
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
claude mcp add --scope user morit-plugin-local -- npx -y @morit/plugin-mcp
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Remote Streamable HTTP:
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
claude mcp add --transport http --scope user \
|
|
67
|
-
morit-plugin-remote https://morit-api.moring.co/mcp
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
클라이언트가 OAuth를 요청하면 Morit SSO 승인을 완료한 뒤 Tool 목록을 새로고침합니다. Claude
|
|
71
|
-
Web/Desktop에서는 Custom Connector에 같은 Remote URL을 등록합니다. Local stdio는 로컬 process를
|
|
72
|
-
실행할 수 있는 client에서만 사용할 수 있습니다.
|
|
73
|
-
|
|
74
|
-
## ChatGPT와 기타 Remote MCP client
|
|
75
|
-
|
|
76
|
-
ChatGPT에서 custom MCP app을 사용할 수 있는 계정·workspace라면 Apps의 custom connector 생성
|
|
77
|
-
화면에 다음 URL을 입력하고 Morit OAuth를 승인합니다.
|
|
78
|
-
|
|
79
|
-
```text
|
|
80
|
-
https://morit-api.moring.co/mcp
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
ChatGPT Web은 개발자 PC의 stdio process나 Codex `config.toml`을 읽지 않으므로 Local MCP 대신
|
|
84
|
-
Remote MCP를 사용합니다. 조직 계정은 관리자가 custom app과 필요한 read/write scope를 허용해야
|
|
85
|
-
할 수 있습니다. Tool이 변경된 뒤에는 connector의 Tool schema를 새로고침하고 새 대화에서
|
|
86
|
-
확인합니다.
|
|
87
|
-
|
|
88
|
-
stdio MCP를 지원하는 다른 desktop agent의 일반적인 설정은 다음 형태입니다. 실제 설정 파일 위치와
|
|
89
|
-
key 이름은 해당 client 문서를 따릅니다.
|
|
90
|
-
|
|
91
|
-
```json
|
|
92
|
-
{
|
|
93
|
-
"mcpServers": {
|
|
94
|
-
"morit-plugin-local": {
|
|
95
|
-
"command": "npx",
|
|
96
|
-
"args": ["-y", "@morit/plugin-mcp"],
|
|
97
|
-
"env": {
|
|
98
|
-
"MORIT_PLUGIN_WORKSPACE": "/absolute/development/path"
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
Remote를 지원하는 client에는 command나 server Secret 대신 endpoint와 OAuth만 설정합니다. 연결 뒤
|
|
106
|
-
Tool 목록에서 `morit_sdk_contract`, `morit_docs_search`, `morit_project_create`가 보이는지 확인합니다.
|
|
107
|
-
[OpenAI의 Apps 안내](https://help.openai.com/en/articles/11487775-connectors-in-chatgpt)에서 현재 계정과
|
|
108
|
-
workspace의 custom app 지원 범위를 확인할 수 있습니다.
|
|
109
|
-
|
|
110
|
-
## Local MCP 작업 순서
|
|
111
|
-
|
|
112
|
-
```text
|
|
113
|
-
morit_sdk_contract
|
|
114
|
-
→ morit_docs_search / morit_docs_get
|
|
115
|
-
→ morit_project_create 또는 morit_project_open
|
|
116
|
-
→ morit_project_files_get
|
|
117
|
-
→ morit_project_files_put
|
|
118
|
-
→ morit_project_validate
|
|
119
|
-
→ UI가 있으면 morit_project_preview(light/dark 확인)
|
|
120
|
-
→ morit_build_start / morit_build_status
|
|
121
|
-
→ morit_artifact_download
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
주요 Local Tool:
|
|
125
|
-
|
|
126
|
-
- 계약·문서: `morit_sdk_contract`, `morit_docs_search`, `morit_docs_get`
|
|
127
|
-
- local project: `morit_project_create`, `morit_project_open`, `morit_project_files_get`,
|
|
128
|
-
`morit_project_files_put`
|
|
129
|
-
- 산출물: `morit_project_validate`, `morit_project_preview`, `morit_project_source_download`,
|
|
130
|
-
`morit_build_start`, `morit_build_status`, `morit_artifact_download`
|
|
131
|
-
- Cloud 상태·Project: `morit_cloud_status`, `morit_cloud_project_list`, `morit_cloud_project_get`,
|
|
132
|
-
`morit_cloud_project_add`, `morit_cloud_project_sync`, `morit_cloud_project_delete`
|
|
133
|
-
- Cloud Deployment: `morit_cloud_deployment_start`, `morit_cloud_deployment_list`,
|
|
134
|
-
`morit_cloud_deployment_get`, `morit_cloud_deployment_publish`
|
|
135
|
-
- Cloud Secret: `morit_cloud_secret_list`, `morit_cloud_secret_put`, `morit_cloud_secret_delete`
|
|
136
|
-
- Cloud Connection: `morit_cloud_connection_list`, `morit_cloud_connection_put`,
|
|
137
|
-
`morit_cloud_connection_delete`
|
|
138
|
-
|
|
139
|
-
MCP가 반환한 project ID, revision, artifact ID를 다음 호출에 그대로 사용합니다. 경로를 추측하거나
|
|
140
|
-
build 전에 완료했다고 보고하지 않습니다.
|
|
141
|
-
|
|
142
|
-
PNG/JPEG/GIF/WebP icon·UI asset과 `children/*.mplg`는
|
|
143
|
-
`morit-base64-v1:<canonical-base64>` 문자열로 `files_put`할 수 있습니다. text 경로에 envelope를
|
|
144
|
-
쓰거나 이미지 확장자와 magic byte가 다르면 Local/Remote MCP가 거부합니다. `files_get`도 같은
|
|
145
|
-
envelope를 반환하므로 수정하지 않은 바이트는 그대로 다음 revision에 유지합니다.
|
|
146
|
-
|
|
147
|
-
Storage 기능을 생성할 때는 먼저 `morit_sdk_contract`의 `permissions`, `storage_tools`,
|
|
148
|
-
`storage_ai_access`, `storage_limits`를 읽고 Manifest `storage`와 UI data source를 함께 작성합니다.
|
|
149
|
-
AI가 쓰는 namespace는 `ai_access: read_write`와 `ai_storage` 권한을 모두 선언합니다. MCP는 package
|
|
150
|
-
runtime 데이터를 읽는 우회 경로가 아니며 개발 source와 artifact만 다룹니다.
|
|
151
|
-
|
|
152
|
-
Response extension을 생성할 때는 `ai_response`와 `ui_runtime` 계약을 함께 읽습니다. MCP 에이전트는
|
|
153
|
-
`point: "response"`, Runtime v2, `a2ui.version/description/schema`, 실제 capability data source를 한
|
|
154
|
-
단위로 작성해야 합니다. AI가 결과 JSON을 만들게 하지 말고 schema에는 표시 가능한 최소 데이터와
|
|
155
|
-
empty 조건을 명시합니다. validate가 schema·권한·version을 거부하면 파일을 우회 패키징하지 않습니다.
|
|
156
|
-
|
|
157
|
-
## Remote MCP 작업 순서
|
|
158
|
-
|
|
159
|
-
```text
|
|
160
|
-
morit_organization_list
|
|
161
|
-
→ morit_project_create 또는 morit_project_list
|
|
162
|
-
→ morit_project_files_get
|
|
163
|
-
→ morit_project_files_put(revision 포함)
|
|
164
|
-
→ morit_project_validate
|
|
165
|
-
→ morit_project_preview
|
|
166
|
-
→ morit_build_start
|
|
167
|
-
→ morit_build_status
|
|
168
|
-
→ morit_deployment_get
|
|
169
|
-
→ morit_artifact_download
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
Secret과 Connection은 build 전에 별도 Tool로 설정합니다. Secret 조회는 ID와 masked metadata만
|
|
173
|
-
반환하며 평문 값을 다시 읽을 수 없습니다.
|
|
174
|
-
|
|
175
|
-
## 에이전트 완료 규칙
|
|
176
|
-
|
|
177
|
-
AI 에이전트는 다음을 모두 확인한 뒤 완료를 보고합니다.
|
|
178
|
-
|
|
179
|
-
- source를 다시 읽고 요청한 파일이 존재함
|
|
180
|
-
- 최신 revision으로 validate 성공
|
|
181
|
-
- UI가 있으면 preview artifact 생성
|
|
182
|
-
- build state가 completed
|
|
183
|
-
- `.mplg` artifact ID, 크기, SHA-256, MIME 확인
|
|
184
|
-
- 다운로드한 package가 다시 열리고 signature가 유효함
|
|
185
|
-
- 배포 요청이면 고유 Deployment와 visibility 확인
|
|
186
|
-
|
|
187
|
-
## 연결 문제
|
|
188
|
-
|
|
189
|
-
| 증상 | 확인 |
|
|
190
|
-
|---|---|
|
|
191
|
-
| Local server가 보이지 않음 | Node 버전, `npx -y @morit/plugin-mcp`, client restart |
|
|
192
|
-
| workspace 밖 경로 거부 | `MORIT_PLUGIN_WORKSPACE`와 project 상대 경로 |
|
|
193
|
-
| Remote `401` | 정상 OAuth 시작 여부, `codex mcp login` |
|
|
194
|
-
| Remote `404` | `/mcp` ingress route |
|
|
195
|
-
| contract parse 오류 | server가 전체 JSON stdout을 반환하는지, stderr 혼합 여부 |
|
|
196
|
-
| revision conflict | `files_get`으로 최신 revision을 읽고 변경을 합침 |
|
|
197
|
-
| artifact 없음 | build status가 completed인지 확인 |
|
|
198
|
-
|
|
199
|
-
Remote Tool의 전체 필드는 [원격 Plugin MCP](remote-mcp.md)에 있습니다.
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
# Tool, Skill, Search, Slash Command
|
|
2
|
-
|
|
3
|
-
Capability는 사용자가 실행할 수 있는 한 가지 작업과 그 실행 경계를 선언합니다. UI에서 버튼을
|
|
4
|
-
누르거나 AI가 Tool을 선택해도 같은 capability가 실행됩니다.
|
|
5
|
-
|
|
6
|
-
## Capability kind
|
|
7
|
-
|
|
8
|
-
| kind | 목적 | 실행 방식 |
|
|
9
|
-
|---|---|---|
|
|
10
|
-
| `tool` | 명시적인 한 번의 작업 | UI, AI, Slash Command |
|
|
11
|
-
| `skill` | 여러 입력을 해석하는 작업 | AI, UI, Slash Command |
|
|
12
|
-
| `provider` | AI 검색 후보 제공 | Search discovery |
|
|
13
|
-
| `background` | 일정 간격의 자동 작업 | Host scheduler |
|
|
14
|
-
| `notification` | 알림 생성·취소 작업 | UI, AI 또는 background |
|
|
15
|
-
|
|
16
|
-
`tool`과 `skill`은 runtime adapter가 필수입니다. `provider`는
|
|
17
|
-
`runtime.role: "search"`가 필요합니다. background runtime을 선언하면
|
|
18
|
-
`interval_minutes`는 15~10,080입니다.
|
|
19
|
-
|
|
20
|
-
## Runtime adapter
|
|
21
|
-
|
|
22
|
-
| adapter | 용도 | 주요 설정·권한 |
|
|
23
|
-
|---|---|---|
|
|
24
|
-
| `text_stats` | 텍스트 통계 | 별도 권한 없음 |
|
|
25
|
-
| `text_template` | 제한된 텍스트 template | `template` 최대 2,000자 |
|
|
26
|
-
| `settings_search` | Instance settings의 검색 항목 | Search Provider용 |
|
|
27
|
-
| `notification_template` | 검증된 알림 Host action 생성 | `notifications` |
|
|
28
|
-
| `calendar_store` | 일정 저장·조회·reminder | `storage`, reminder는 `notifications` |
|
|
29
|
-
| `http_json` | 공개 HTTPS JSON API | `network`, credential 사용 시 `credentials` |
|
|
30
|
-
| `mcp_http` | Remote MCP Tool 호출 | `network`, 선택적 `credentials` |
|
|
31
|
-
| `child_plugin` | dependency capability 호출 | 노출된 dependency와 capability |
|
|
32
|
-
| `neis_school` | 학교 검색·시간표·급식·일정 | `network`, `storage`; reminder는 `notifications` |
|
|
33
|
-
| `sandbox_python` | 격리된 Python 데이터 작업 | signed package와 고급 worker |
|
|
34
|
-
|
|
35
|
-
## 선언형 adapter 예
|
|
36
|
-
|
|
37
|
-
### Text template
|
|
38
|
-
|
|
39
|
-
```json
|
|
40
|
-
{
|
|
41
|
-
"adapter": "text_template",
|
|
42
|
-
"template": "요청을 다음 형식으로 정리합니다: {query}"
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
복잡한 표현식이나 코드를 template에 넣지 않습니다.
|
|
47
|
-
|
|
48
|
-
### Calendar store
|
|
49
|
-
|
|
50
|
-
```json
|
|
51
|
-
{
|
|
52
|
-
"adapter": "calendar_store",
|
|
53
|
-
"operation": "reminder",
|
|
54
|
-
"namespace": "study.schedule"
|
|
55
|
-
}
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
일반 저장에는 `storage`, 알림 예약에는 `storage`와 `notifications`가 모두 필요합니다.
|
|
59
|
-
|
|
60
|
-
### HTTP JSON
|
|
61
|
-
|
|
62
|
-
```json
|
|
63
|
-
{
|
|
64
|
-
"adapter": "http_json",
|
|
65
|
-
"connector_id": "notes_api",
|
|
66
|
-
"method": "POST",
|
|
67
|
-
"request_body": {"query": "{query}"},
|
|
68
|
-
"response": {"summary_path": "message", "data_path": "data"}
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
GET과 POST만 지원하며 redirect를 따라가지 않습니다. endpoint는 공개 HTTPS이고 SSRF 보호 경계를
|
|
73
|
-
통과합니다. bearer token은 Host가 Connection에서 주입하며 fragment에 직접 쓰지 않습니다.
|
|
74
|
-
|
|
75
|
-
### Remote MCP
|
|
76
|
-
|
|
77
|
-
Morit Host 1.7.12의 plugin outbound MCP adapter는 `mcp_http`입니다. 개발자용 Local/Remote Plugin
|
|
78
|
-
MCP 서버와 이름이 비슷하지만, 설치된 플러그인이 선언한 외부 MCP Tool을 Host 경계에서 호출하는
|
|
79
|
-
runtime입니다.
|
|
80
|
-
|
|
81
|
-
```json
|
|
82
|
-
{
|
|
83
|
-
"adapter": "mcp_http",
|
|
84
|
-
"connector_id": "research_mcp",
|
|
85
|
-
"tool_name": "search",
|
|
86
|
-
"protocol_version": "2025-11-25",
|
|
87
|
-
"argument_template": {"query": "{query}"}
|
|
88
|
-
}
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
Host가 initialize → initialized → `tools/call` session을 관리합니다. credential이 거부되면 지원되는
|
|
92
|
-
연결에서 한 번 갱신한 뒤 새 session으로 재시도합니다. MCP의 `isError: true`는 성공 결과로 바꾸지
|
|
93
|
-
않습니다.
|
|
94
|
-
|
|
95
|
-
### Sandbox Python
|
|
96
|
-
|
|
97
|
-
```json
|
|
98
|
-
{
|
|
99
|
-
"adapter": "sandbox_python",
|
|
100
|
-
"entrypoint": "src/analyze.py"
|
|
101
|
-
}
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
entrypoint는 package 상대 POSIX 경로이고 각 `src/*.py` 파일과 정확히 1:1이어야 합니다. 코드는
|
|
105
|
-
Archive API process에서 실행하지 않고 별도 rootless Docker + gVisor worker와 lease-fenced broker를
|
|
106
|
-
거칩니다. 결과는 다음 객체여야 합니다.
|
|
107
|
-
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"summary": "분석을 완료했습니다.",
|
|
111
|
-
"data": {},
|
|
112
|
-
"evidence": []
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
`summary` 없이 sandbox 경로나 내부 로그만 반환하면 완료된 결과가 아닙니다.
|
|
117
|
-
|
|
118
|
-
## UI와 AI 노출
|
|
119
|
-
|
|
120
|
-
AI는 enabled 상태이고 사용자에게 허용된 capability만 발견합니다. 제목과 설명은 모델이 언제 이
|
|
121
|
-
기능을 선택할지 판단할 수 있게 동작과 입력을 구체적으로 씁니다.
|
|
122
|
-
|
|
123
|
-
Slash Command나 UI extension은 선택적 진입점일 뿐 discovery 조건이 아닙니다. 활성 Plugin의 모든
|
|
124
|
-
`tool`·`skill`은 매 AI turn의 actor-scoped capability catalog에 자동 포함됩니다. 설치·업데이트·
|
|
125
|
-
enable/disable·권한 변경·Connection 생성/수정/삭제·OAuth 완료·삭제 뒤 Host는 Instance route cache를
|
|
126
|
-
즉시 비워 다음 turn과 실행이 새 generation을 사용하게 합니다.
|
|
127
|
-
|
|
128
|
-
```json
|
|
129
|
-
{
|
|
130
|
-
"id": "school_life.today",
|
|
131
|
-
"kind": "tool",
|
|
132
|
-
"title": "오늘 학교 생활 조회",
|
|
133
|
-
"description": "연결한 학교의 오늘 시간표, 급식, 학사일정을 조회합니다.",
|
|
134
|
-
"permissions": ["network", "storage"],
|
|
135
|
-
"timeout_seconds": 10,
|
|
136
|
-
"input_schema": {
|
|
137
|
-
"type": "object",
|
|
138
|
-
"properties": {"date": {"type": "string"}},
|
|
139
|
-
"additionalProperties": false
|
|
140
|
-
},
|
|
141
|
-
"output_schema": {
|
|
142
|
-
"type": "object",
|
|
143
|
-
"properties": {"timetable": {"type": "array", "items": {"type": "object"}}},
|
|
144
|
-
"required": ["timetable"]
|
|
145
|
-
},
|
|
146
|
-
"runtime": {"adapter": "neis_school", "operation": "overview"}
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
`input_schema`/`output_schema`는 root `object`인 안전한 JSON Schema 부분집합입니다. AI Tool,
|
|
151
|
-
UI data source·invoke, Host 실행과 결과 binding이 같은 schema를 사용합니다. CLI/MCP validate는
|
|
152
|
-
필수 필드·타입·범위와 `data.<source>.data...` 경로를 package build 전에 검사합니다.
|
|
153
|
-
실행 시 schema와 다른 외부 응답은 정상 결과로 변환하지 않고 해당 capability 오류로
|
|
154
|
-
격리합니다.
|
|
155
|
-
|
|
156
|
-
UI `invoke`가 capability를 참조할 때도 enabled, generation, permission, timeout을 다시 확인합니다.
|
|
157
|
-
UI에 capability ID를 사용자용 라벨로 표시하지 않습니다.
|
|
158
|
-
|
|
159
|
-
## Slash Command
|
|
160
|
-
|
|
161
|
-
Slash Command는 같은 package의 `tool` 또는 `skill`에 고정된 진입점입니다.
|
|
162
|
-
|
|
163
|
-
```json
|
|
164
|
-
{
|
|
165
|
-
"id": "school_life.today_command",
|
|
166
|
-
"command": "school",
|
|
167
|
-
"title": "오늘 학교 생활",
|
|
168
|
-
"description": "오늘 시간표와 급식을 확인합니다.",
|
|
169
|
-
"capability": "school_life.today",
|
|
170
|
-
"argument_template": {}
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
Slash Command를 선언하지 않아도 Tool discovery는 유지됩니다. 여러 command가 같은 이름을 사용할 수
|
|
175
|
-
없고 `/`는 `command` 값에 포함하지 않습니다.
|
|
176
|
-
|
|
177
|
-
## 실패와 복구
|
|
178
|
-
|
|
179
|
-
Capability는 권한 부족, 연결 필요, timeout, retryable network 오류, 잘못된 응답을 구분합니다.
|
|
180
|
-
retry는 Connector에 선언한 bounded 정책만 사용합니다. Tool 하나가 실패했다고 AI 요청 전체를
|
|
181
|
-
즉시 포기하지 말고, 오케스트레이터가 대체 Tool·재연결·부분 결과를 선택할 수 있도록 공개 오류를
|
|
182
|
-
구체적으로 유지합니다.
|
|
183
|
-
|
|
184
|
-
실행 준비 상태의 대표 code는 `EXTENSION_NOT_FOUND`, `EXTENSION_PERMISSION_DENIED`,
|
|
185
|
-
`EXTENSION_CONNECTION_REQUIRED`, `EXTENSION_DISABLED`, `EXTENSION_VERSION_MISMATCH`,
|
|
186
|
-
`EXTENSION_DEPENDENCY_REQUIRED`입니다. 빈 Tool 목록이나 일반 실패 문구로 합치지 마세요.
|
|
187
|
-
|
|
188
|
-
권한과 알림은 [권한, 설정, 저장소, 알림](permissions-and-data.md)을 참고하세요.
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# 오류 해결
|
|
2
|
-
|
|
3
|
-
오류를 catch로 숨기기 전에 어느 경계에서 발생했는지 구분합니다.
|
|
4
|
-
|
|
5
|
-
## 빠른 진단 순서
|
|
6
|
-
|
|
7
|
-
```text
|
|
8
|
-
source를 읽을 수 있는가
|
|
9
|
-
→ validate가 성공하는가
|
|
10
|
-
→ preview artifact가 생기는가
|
|
11
|
-
→ signed build와 reopen이 성공하는가
|
|
12
|
-
→ API 인증과 목록 조회가 성공하는가
|
|
13
|
-
→ 설치 상태가 active인가
|
|
14
|
-
→ Connection과 권한이 ready인가
|
|
15
|
-
→ capability가 실행되는가
|
|
16
|
-
→ UI가 결과를 렌더링하는가
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
앞 단계가 실패한 상태에서 다음 단계 오류를 우회하지 않습니다.
|
|
20
|
-
|
|
21
|
-
## Validate 오류
|
|
22
|
-
|
|
23
|
-
| 증상 | 확인 항목 |
|
|
24
|
-
|---|---|
|
|
25
|
-
| unknown field | 문서에 없는 key 제거, v1/v2 필드 혼합 여부 |
|
|
26
|
-
| duplicate ID | manifest 배열과 모든 fragment를 함께 검색 |
|
|
27
|
-
| unknown permission | 지원 목록과 Manifest 전역 권한 확인 |
|
|
28
|
-
| capability reference | UI, Slash Command, child dependency 대상 ID 확인 |
|
|
29
|
-
| state/data binding | `initial_state`, `data_sources`, `item` scope 확인 |
|
|
30
|
-
| source entrypoint | 각 `src/*.py`와 `sandbox_python` 1:1 확인 |
|
|
31
|
-
| image asset | `assets/` 경로·확장자·실제 파일 확인 |
|
|
32
|
-
|
|
33
|
-
진단에 표시된 첫 오류부터 고치고 다시 validate합니다. 허용 필드를 늘리거나 unknown key를 무시하게
|
|
34
|
-
바꾸지 않습니다.
|
|
35
|
-
|
|
36
|
-
## Preview 오류
|
|
37
|
-
|
|
38
|
-
preview 전에 validate가 실행됩니다. artifact가 없으면 출력 디렉터리 권한과 source size를 확인합니다.
|
|
39
|
-
preview가 열려도 실제 앱의 typography, navigation, scroll, async state는 별도 검증 대상입니다.
|
|
40
|
-
|
|
41
|
-
## Build·signature 오류
|
|
42
|
-
|
|
43
|
-
- output 확장자가 `.mplg`인지 확인합니다.
|
|
44
|
-
- package가 2 MiB, 64 entry 한도 안인지 확인합니다.
|
|
45
|
-
- private key 파일 권한과 publisher identity를 확인합니다.
|
|
46
|
-
- source ZIP을 `.mplg`로 이름만 바꾸지 않았는지 확인합니다.
|
|
47
|
-
- `verify`가 반환한 plugin ID, publisher, version, SHA-256을 기록합니다.
|
|
48
|
-
- trusted publisher에 등록된 key와 embedded public key가 다른 경우 올바른 조직 key로 다시 build합니다.
|
|
49
|
-
|
|
50
|
-
## 목록 조회와 설치 오류
|
|
51
|
-
|
|
52
|
-
`Not Found`는 URL 이동으로 해결하지 않습니다. API base URL, 배포 route, 인증 header, 사용자 RLS를
|
|
53
|
-
확인합니다. `401`은 endpoint가 없다는 뜻이 아니라 인증이 필요하다는 뜻입니다.
|
|
54
|
-
|
|
55
|
-
설치 뒤 목록 조회가 실패했다가 재시도에서 duplicate가 나오면 package file, Installation row,
|
|
56
|
-
default Instance, cache가 하나의 atomic transaction으로 정리되는지 확인합니다. 앱 데이터 삭제만으로
|
|
57
|
-
서버 DB와 외부 저장소는 삭제되지 않습니다. 사용자별 API 목록과 서버 package store를 함께 확인합니다.
|
|
58
|
-
|
|
59
|
-
정상 duplicate는 같은 plugin ID·publisher·version 정책으로 판단하고 파일명만 사용하지 않습니다.
|
|
60
|
-
|
|
61
|
-
## Connection 오류
|
|
62
|
-
|
|
63
|
-
| 상태 | 의미 | 사용자 동작 |
|
|
64
|
-
|---|---|---|
|
|
65
|
-
| `not_configured` | 필수 Connection/Secret 없음 | 계정 연결 또는 Developer Secret 설정 |
|
|
66
|
-
| `unavailable` | provider·암호화·runtime 사용 불가 | 운영 설정 확인 |
|
|
67
|
-
| credential rejected | token 만료·철회 | 한 번 refresh 후 다시 연결 |
|
|
68
|
-
| rate limited | Connector 한도 초과 | 제한된 시간 뒤 재시도 |
|
|
69
|
-
|
|
70
|
-
token을 로그에 출력해 진단하지 않습니다. 연결 metadata와 공개 오류 코드만 사용합니다.
|
|
71
|
-
|
|
72
|
-
## Capability 오류
|
|
73
|
-
|
|
74
|
-
- permission required: Manifest와 capability 권한, 사용자 grant 확인
|
|
75
|
-
- disabled/incompatible: Instance state와 Morit version 확인
|
|
76
|
-
- timeout: capability timeout과 Connector timeout을 비교하고 작업을 작게 나눔
|
|
77
|
-
- retryable network: Connector의 bounded retry만 사용
|
|
78
|
-
- invalid response: `summary`, `data`, `evidence` 형태와 외부 response mapping 확인
|
|
79
|
-
- code worker unavailable: broker/worker health와 lease 확인; Archive process에서 직접 실행하지 않음
|
|
80
|
-
|
|
81
|
-
Tool 하나의 실패가 AI 요청 전체 실패를 뜻하지 않는 경우 대체 Tool·부분 결과·재연결을 시도하고
|
|
82
|
-
최종 답변에 남은 제한을 설명합니다.
|
|
83
|
-
|
|
84
|
-
## UI 오류
|
|
85
|
-
|
|
86
|
-
- 해당 extension만 fallback되고 대화·앱 전체가 계속 동작하는지 확인합니다.
|
|
87
|
-
- 없는 state/data/route를 참조하지 않는지 확인합니다.
|
|
88
|
-
- list/timeline child가 정확히 하나인지 확인합니다.
|
|
89
|
-
- `surface`가 children을 가지는지 확인합니다.
|
|
90
|
-
- `positioned`는 `stack`, `expanded`는 `row`/`column` 안인지 확인합니다.
|
|
91
|
-
- literal image URL 대신 capability result binding을 사용했는지 확인합니다.
|
|
92
|
-
- 원격 이미지가 안 보이면 Manifest와 활성 Instance의 `network` 권한, public HTTPS 여부를 먼저
|
|
93
|
-
확인합니다. redirect, MIME/magic 불일치, 손상된 파일, 512 KiB 또는 가로·세로 4096 px 초과는
|
|
94
|
-
Host가 의도적으로 차단합니다. 애니메이션은 최대 128 frame이고 `width × height × frame 수`로
|
|
95
|
-
계산한 frame 합산 pixel이 16,000,000 이하여야 합니다.
|
|
96
|
-
- 원격 이미지 실패를 앱 직접 fetch나 임의 외부 URL widget으로 우회하지 않습니다.
|
|
97
|
-
- Response UI가 원래 AI 메시지 안의 한 container에 붙는지 확인합니다.
|
|
98
|
-
- Response UI가 안 보이면 `config.a2ui.version`이 `v0.9`인지, schema가 실제 완료 데이터와 맞는지,
|
|
99
|
-
response의 data source가 실행 capability를 가리키는지, Instance 권한이 승인됐는지 확인합니다.
|
|
100
|
-
- 빈 데이터·무관한 답변·중복 결과·Tool 실패에서 Response UI가 생기지 않는 것은 정상입니다. AI가
|
|
101
|
-
catalog 항목을 선택하지 않은 경우에도 서버가 임의 fallback UI를 만들지 않고 텍스트만 남깁니다.
|
|
102
|
-
|
|
103
|
-
## MCP 오류
|
|
104
|
-
|
|
105
|
-
Local MCP:
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
node --version
|
|
109
|
-
npx -y @morit/plugin-mcp
|
|
110
|
-
codex mcp list
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Remote MCP:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
curl -i https://morit-api.moring.co/mcp
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
인증 전 `401`과 `WWW-Authenticate`는 정상 OAuth 시작 신호입니다. `404`는 ingress route, `5xx`는
|
|
120
|
-
MCP service health와 server log를 확인합니다. Codex에서는 `codex mcp login <name>`, Tool 목록에서는
|
|
121
|
-
`morit_sdk_contract`와 `morit_docs_search`를 먼저 확인합니다.
|