@morit/cli 1.0.0 → 1.1.1
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 +53 -18
- package/assets/docs/README.md +105 -0
- package/assets/docs/ai-response-and-timeline.md +125 -0
- package/assets/docs/ai-skill-and-docx-workflow.md +83 -0
- package/assets/docs/app-builder.md +56 -0
- package/assets/docs/authentication.md +140 -0
- package/assets/docs/components.md +159 -0
- package/assets/docs/design-tokens-responsive.md +148 -0
- package/assets/docs/docs-index.json +93 -0
- package/assets/docs/examples-notion.md +83 -0
- package/assets/docs/examples-school-life.md +74 -0
- package/assets/docs/getting-started.md +132 -0
- package/assets/docs/information-hierarchy.md +81 -0
- package/assets/docs/instances-and-connectors.md +93 -0
- package/assets/docs/lifecycle-and-api.md +158 -0
- package/assets/docs/local-cli.md +124 -0
- package/assets/docs/manifest.md +208 -0
- package/assets/docs/packaging-and-testing.md +115 -0
- package/assets/docs/permissions-and-data.md +131 -0
- package/assets/docs/platform-compatibility.md +62 -0
- package/assets/docs/project-structure.md +102 -0
- package/assets/docs/remote-mcp.md +152 -0
- package/assets/docs/school-life-privacy.md +49 -0
- package/assets/docs/screens-layout-navigation.md +95 -0
- package/assets/docs/sdk-and-mcp.md +182 -0
- package/assets/docs/tool-and-skill.md +163 -0
- package/assets/docs/troubleshooting.md +117 -0
- package/assets/docs/ui-extensions.md +70 -0
- package/assets/docs/ui-runtime-v2.md +273 -0
- package/assets/docs/verification.md +128 -0
- package/assets/plugin_contract.json +222 -1
- package/package.json +1 -1
- package/src/cli.js +27 -3
- package/src/secure-store.js +113 -43
- package/src/workspace.js +297 -86
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# 원격 Plugin MCP
|
|
2
|
+
|
|
3
|
+
Remote Plugin MCP는 파일시스템이 없는 AI client가 Morit 조직의 Cloud Project를 만들고 검증·배포할
|
|
4
|
+
수 있게 하는 OAuth 보호 Streamable HTTP 서비스입니다.
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
https://morit-api.moring.co/mcp
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
인증 전 응답은 `401`과 RFC 9728 protected-resource metadata 위치를 반환합니다. Morit SSO의
|
|
11
|
+
authorization code + PKCE 흐름으로 로그인하며 승인된 scope와 사용자 RLS를 모든 Tool에 적용합니다.
|
|
12
|
+
|
|
13
|
+
## 연결
|
|
14
|
+
|
|
15
|
+
Codex:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
codex mcp add morit-plugin-remote --url https://morit-api.moring.co/mcp
|
|
19
|
+
codex mcp login morit-plugin-remote
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Claude Code:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
claude mcp add --transport http --scope user \
|
|
26
|
+
morit-plugin-remote https://morit-api.moring.co/mcp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Tool 목록
|
|
30
|
+
|
|
31
|
+
### 계약과 문서
|
|
32
|
+
|
|
33
|
+
| Tool | 목적 |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `morit_sdk_contract` | 현재 SDK/Host capability contract 전체 반환 |
|
|
36
|
+
| `morit_docs_search` | bundled 공식 문서 검색 |
|
|
37
|
+
| `morit_docs_get` | 검색 결과의 Markdown 한 파일 읽기 |
|
|
38
|
+
|
|
39
|
+
### 조직과 Project
|
|
40
|
+
|
|
41
|
+
| Tool | 목적 |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `morit_organization_list` | 접근 가능한 조직과 role 조회 |
|
|
44
|
+
| `morit_project_list` | 조직의 Cloud Project 목록 |
|
|
45
|
+
| `morit_project_create` | 표준 source를 가진 Project 생성 |
|
|
46
|
+
| `morit_project_files_get` | 최신 revision과 source snapshot 읽기 |
|
|
47
|
+
| `morit_project_files_put` | revision 보호를 적용한 파일 생성·수정·삭제 |
|
|
48
|
+
| `morit_project_metadata_update` | Marketplace 표시 metadata 변경 |
|
|
49
|
+
| `morit_project_delete` | 활성 Deployment가 없는 Project 삭제 |
|
|
50
|
+
|
|
51
|
+
### 검증과 artifact
|
|
52
|
+
|
|
53
|
+
| Tool | 목적 |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `morit_project_validate` | 현재 revision의 source 계약 검사 |
|
|
56
|
+
| `morit_project_preview` | private HTML preview artifact 생성 |
|
|
57
|
+
| `morit_project_source_download` | 편집 가능한 source ZIP 생성 |
|
|
58
|
+
| `morit_build_start` | immutable build/Deployment 시작 |
|
|
59
|
+
| `morit_build_status` | build state와 package artifact 조회 |
|
|
60
|
+
| `morit_artifact_download` | owned artifact의 짧은 수명 download URL 생성 |
|
|
61
|
+
|
|
62
|
+
### Deployment
|
|
63
|
+
|
|
64
|
+
| Tool | 목적 |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `morit_deployment_list` | Project의 immutable Deployment history |
|
|
67
|
+
| `morit_deployment_get` | build 결과, hash, logs, visibility 조회 |
|
|
68
|
+
| `morit_deployment_publish` | completed Deployment를 private/public current로 설정 |
|
|
69
|
+
|
|
70
|
+
### Secret과 Connection
|
|
71
|
+
|
|
72
|
+
| Tool | 목적 |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `morit_project_secret_list` | Secret ID와 masked metadata 조회 |
|
|
75
|
+
| `morit_project_secret_put` | encrypted Secret 생성·rotation |
|
|
76
|
+
| `morit_project_secret_delete` | 참조되지 않는 Secret 삭제 |
|
|
77
|
+
| `morit_project_connection_list` | OAuth/API Connection 선언 조회 |
|
|
78
|
+
| `morit_project_connection_put` | Connection 생성·수정 |
|
|
79
|
+
| `morit_project_connection_delete` | Connection 선언 삭제 |
|
|
80
|
+
|
|
81
|
+
Secret Tool은 평문을 반환하지 않습니다. source, log, Tool 응답에 Secret 값을 복사하지 마세요.
|
|
82
|
+
|
|
83
|
+
## ID와 경로
|
|
84
|
+
|
|
85
|
+
- organization, Project, Deployment, artifact ID는 `format: uuid`입니다.
|
|
86
|
+
- Manifest plugin ID는 소문자 reverse-domain ID입니다.
|
|
87
|
+
- source path는 상대 POSIX 형식입니다.
|
|
88
|
+
- `..`, 역슬래시, 절대 경로, Windows 예약명, symlink는 거부합니다.
|
|
89
|
+
- `manifest.json`은 삭제할 수 없습니다.
|
|
90
|
+
- 한 파일 512 KiB, source 최대 64개 파일·1 MiB입니다.
|
|
91
|
+
- 한 `files_put` 변경은 최대 64개입니다.
|
|
92
|
+
|
|
93
|
+
AI가 UUID나 경로를 추측하지 않고 list/create/get 결과를 그대로 다음 Tool에 전달해야 합니다.
|
|
94
|
+
|
|
95
|
+
## Revision과 파일 변경
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"project_id": "00000000-0000-4000-8000-000000000000",
|
|
100
|
+
"revision": 3,
|
|
101
|
+
"files": {
|
|
102
|
+
"ui/home.json": "{...}\n",
|
|
103
|
+
"ui/old.json": null
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`null`은 파일 삭제입니다. revision이 오래되면 전체 요청을 거부하고 최신 source를 반환하므로
|
|
109
|
+
`files_get` → 변경 병합 → `files_put`을 반복합니다. conflict를 `force`로 숨기지 않습니다.
|
|
110
|
+
|
|
111
|
+
이미지 asset과 child `.mplg`는 검증된 binary envelope로만 JSON 경계를 지나며 최종 source ZIP과
|
|
112
|
+
package에서는 원래 바이트로 복원됩니다.
|
|
113
|
+
|
|
114
|
+
## Build와 다운로드
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
morit_build_start
|
|
118
|
+
→ job_id와 deployment_id
|
|
119
|
+
morit_build_status 반복
|
|
120
|
+
→ completed + artifact_id + size + sha256
|
|
121
|
+
morit_deployment_get
|
|
122
|
+
→ exact artifact hash와 visibility
|
|
123
|
+
morit_artifact_download
|
|
124
|
+
→ 짧은 수명 URL
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
같은 요청을 재시도할 때 새 build가 생성될 수 있으므로 반환된 ID를 섞지 않습니다. status가
|
|
128
|
+
`failed`이면 logs의 공개 진단을 source에 반영하고 새 build를 시작합니다. `processing`을 완료로
|
|
129
|
+
간주하지 않습니다.
|
|
130
|
+
|
|
131
|
+
## Protocol compatibility
|
|
132
|
+
|
|
133
|
+
서비스는 직접 `tools/list`/`tools/call`을 보내는 최신 handshake-free client와
|
|
134
|
+
initialize → initialized → Tool 호출 방식의 client를 모두 지원합니다. `MCP-Protocol-Version`과
|
|
135
|
+
`Mcp-Method`/`Mcp-Name` 같은 routing header는 서버가 협상한 값과 맞춰야 합니다. 지원 protocol
|
|
136
|
+
date는 서비스 응답을 사용하며 client가 임의로 고정하지 않습니다.
|
|
137
|
+
|
|
138
|
+
## 복구
|
|
139
|
+
|
|
140
|
+
| 오류 | 대응 |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `401` | OAuth metadata를 따라 로그인 |
|
|
143
|
+
| scope 부족 | 필요한 scope를 사용자에게 명시하고 재승인 |
|
|
144
|
+
| Project 없음 | 현재 조직과 Project ID 확인 |
|
|
145
|
+
| revision conflict | 최신 source를 읽고 병합 |
|
|
146
|
+
| validation 오류 | 반환된 정확한 field/reference 진단 수정 |
|
|
147
|
+
| build timeout | status로 기존 build 확인 후 필요한 경우 새 build |
|
|
148
|
+
| artifact 만료 | 같은 artifact ID로 새 download URL 발급 |
|
|
149
|
+
| Secret 참조 중 | Connection 참조를 먼저 정리 |
|
|
150
|
+
|
|
151
|
+
로컬 파일을 직접 편집해야 하면 [Local MCP](sdk-and-mcp.md)를 사용합니다. Remote 서비스에 local
|
|
152
|
+
absolute path나 publisher private key를 전달하지 않습니다.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# 학교 생활 플러그인 개인정보 처리 안내
|
|
2
|
+
|
|
3
|
+
최종 수정일: 2026-08-13
|
|
4
|
+
|
|
5
|
+
학교 생활 플러그인은 학생이 선택한 학교, 학년, 반을 기준으로 시간표·급식·학사일정을
|
|
6
|
+
보여 주고 기기 안에서 알림과 아침 브리핑을 구성합니다. 플러그인 설치 전에 아래 데이터
|
|
7
|
+
사용 범위를 확인할 수 있으며, 설치 후 설정에서 언제든 초기화하거나 삭제할 수 있습니다.
|
|
8
|
+
|
|
9
|
+
## 사용하는 정보
|
|
10
|
+
|
|
11
|
+
- 사용자가 직접 선택한 학교 코드, 교육청 코드, 학년, 반
|
|
12
|
+
- 알레르기 필터, 알림 사용 여부와 알림 시간
|
|
13
|
+
- 중복 알림을 막기 위한 마지막 실행 날짜
|
|
14
|
+
- 교육부 NEIS 공개 API가 제공하는 시간표, 급식, 학사일정
|
|
15
|
+
|
|
16
|
+
이 플러그인은 이름, 주민등록번호, 학생 번호, 성적, 연락처, 계정 비밀번호를 요청하거나
|
|
17
|
+
수집하지 않습니다. 별도의 OAuth 계정이나 API key도 요구하지 않습니다.
|
|
18
|
+
|
|
19
|
+
## 처리 목적과 저장 위치
|
|
20
|
+
|
|
21
|
+
학교·학년·반 설정은 사용자가 요청한 학교 정보를 반복 입력하지 않도록 해당 Morit
|
|
22
|
+
사용자의 Plugin Instance 저장소에만 보관합니다. 조회한 공개 학교 정보는 화면 표시와
|
|
23
|
+
알림 생성에만 사용합니다. 다른 사용자나 다른 Instance와 공유하지 않습니다.
|
|
24
|
+
|
|
25
|
+
외부 요청은 선언된 NEIS HTTPS endpoint로만 제한되며 Morit의 SSRF filtering proxy를
|
|
26
|
+
통과합니다. 플러그인 코드에는 운영 credential이나 사용자 token이 포함되지 않습니다.
|
|
27
|
+
|
|
28
|
+
## 보관과 삭제
|
|
29
|
+
|
|
30
|
+
Manifest의 data policy는 `purge`입니다. 사용자가 Plugin Instance 또는 플러그인을
|
|
31
|
+
삭제하면 해당 Instance의 설정, 조회 cache, 알림 상태를 함께 삭제합니다. 앱 데이터
|
|
32
|
+
초기화 후 서버에 남은 개인 Plugin Instance도 계정의 플러그인 관리 화면에서 삭제할 수
|
|
33
|
+
있습니다.
|
|
34
|
+
|
|
35
|
+
## 권한
|
|
36
|
+
|
|
37
|
+
- 네트워크: NEIS 공개 학교 정보 조회
|
|
38
|
+
- 저장소: 학교·학년·반과 사용자 설정 저장
|
|
39
|
+
- 알림: 사용자가 켠 경우 다음 학교 일정과 아침 브리핑 알림 예약
|
|
40
|
+
- 백그라운드: 사용자가 켠 경우 공개 학교 정보 갱신과 브리핑 준비
|
|
41
|
+
|
|
42
|
+
권한이 꺼져 있거나 네트워크가 실패하면 해당 기능만 사용할 수 없다는 상태를 표시하며,
|
|
43
|
+
오류를 숨기거나 임의의 학교 정보를 생성하지 않습니다.
|
|
44
|
+
|
|
45
|
+
## 문의와 변경
|
|
46
|
+
|
|
47
|
+
플러그인 기능과 구현은 [학교 생활 예제 문서](examples-school-life.md)에서 확인할 수
|
|
48
|
+
있습니다. 처리 항목이나 외부 제공 범위가 바뀌면 새 Plugin version과 이 문서를 함께
|
|
49
|
+
갱신하며, 권한이 추가되는 경우 사용자가 설치 전에 다시 확인할 수 있게 표시합니다.
|
|
@@ -0,0 +1,95 @@
|
|
|
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)입니다.
|
|
@@ -0,0 +1,182 @@
|
|
|
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
|
+
두 연결은 `@morit/cli`와 Host의 같은 Plugin contract를 사용합니다. Local source에 접근할 필요가
|
|
11
|
+
없으면 Remote를, 현재 저장소 파일을 직접 고쳐야 하면 Local을 선택합니다.
|
|
12
|
+
|
|
13
|
+
## 준비
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
node --version
|
|
17
|
+
npx -y @morit/cli login
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Node.js 20.12 이상이 필요합니다. Local MCP의 Cloud Tool을 쓸 때만 CLI 로그인이 필요합니다.
|
|
21
|
+
MCP 설정이나 Project source에 access token을 직접 넣지 않습니다.
|
|
22
|
+
|
|
23
|
+
## Codex에 Local MCP 추가
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
codex mcp add morit-plugin-local -- npx -y @morit/plugin-mcp
|
|
27
|
+
codex mcp list
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
특정 workspace만 허용하려면 환경변수로 절대 경로를 전달합니다.
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
codex mcp add morit-plugin-local \
|
|
34
|
+
--env MORIT_PLUGIN_WORKSPACE=/absolute/development/path \
|
|
35
|
+
-- npx -y @morit/plugin-mcp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Codex CLI/IDE/Desktop의 현재 MCP 설정 방식은
|
|
39
|
+
[공식 OpenAI MCP 문서](https://learn.chatgpt.com/docs/extend/mcp?surface=cli)를 참고하세요. Codex
|
|
40
|
+
TUI에서는 `/mcp`로 활성 서버를 확인합니다.
|
|
41
|
+
|
|
42
|
+
## Codex에 Remote MCP 추가
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
codex mcp add morit-plugin-remote --url https://morit-api.moring.co/mcp
|
|
46
|
+
codex mcp login morit-plugin-remote
|
|
47
|
+
codex mcp list
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
브라우저에서 Morit 계정으로 로그인하고 조직과 scope를 승인합니다. static bearer token을 config에
|
|
51
|
+
넣지 않습니다.
|
|
52
|
+
|
|
53
|
+
## Claude Code
|
|
54
|
+
|
|
55
|
+
Local stdio:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
claude mcp add --scope user morit-plugin-local -- npx -y @morit/plugin-mcp
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Remote Streamable HTTP:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
claude mcp add --transport http --scope user \
|
|
65
|
+
morit-plugin-remote https://morit-api.moring.co/mcp
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
클라이언트가 OAuth를 요청하면 Morit SSO 승인을 완료한 뒤 Tool 목록을 새로고침합니다. Claude
|
|
69
|
+
Web/Desktop에서는 Custom Connector에 같은 Remote URL을 등록합니다. Local stdio는 로컬 process를
|
|
70
|
+
실행할 수 있는 client에서만 사용할 수 있습니다.
|
|
71
|
+
|
|
72
|
+
## ChatGPT와 기타 Remote MCP client
|
|
73
|
+
|
|
74
|
+
ChatGPT에서 custom MCP app을 사용할 수 있는 계정·workspace라면 Apps의 custom connector 생성
|
|
75
|
+
화면에 다음 URL을 입력하고 Morit OAuth를 승인합니다.
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
https://morit-api.moring.co/mcp
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
ChatGPT Web은 개발자 PC의 stdio process나 Codex `config.toml`을 읽지 않으므로 Local MCP 대신
|
|
82
|
+
Remote MCP를 사용합니다. 조직 계정은 관리자가 custom app과 필요한 read/write scope를 허용해야
|
|
83
|
+
할 수 있습니다. Tool이 변경된 뒤에는 connector의 Tool schema를 새로고침하고 새 대화에서
|
|
84
|
+
확인합니다.
|
|
85
|
+
|
|
86
|
+
stdio MCP를 지원하는 다른 desktop agent의 일반적인 설정은 다음 형태입니다. 실제 설정 파일 위치와
|
|
87
|
+
key 이름은 해당 client 문서를 따릅니다.
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"morit-plugin-local": {
|
|
93
|
+
"command": "npx",
|
|
94
|
+
"args": ["-y", "@morit/plugin-mcp"],
|
|
95
|
+
"env": {
|
|
96
|
+
"MORIT_PLUGIN_WORKSPACE": "/absolute/development/path"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Remote를 지원하는 client에는 command나 server Secret 대신 endpoint와 OAuth만 설정합니다. 연결 뒤
|
|
104
|
+
Tool 목록에서 `morit_sdk_contract`, `morit_docs_search`, `morit_project_create`가 보이는지 확인합니다.
|
|
105
|
+
[OpenAI의 Apps 안내](https://help.openai.com/en/articles/11487775-connectors-in-chatgpt)에서 현재 계정과
|
|
106
|
+
workspace의 custom app 지원 범위를 확인할 수 있습니다.
|
|
107
|
+
|
|
108
|
+
## Local MCP 작업 순서
|
|
109
|
+
|
|
110
|
+
```text
|
|
111
|
+
morit_sdk_contract
|
|
112
|
+
→ morit_docs_search / morit_docs_get
|
|
113
|
+
→ morit_project_create 또는 morit_project_open
|
|
114
|
+
→ morit_project_files_get
|
|
115
|
+
→ morit_project_files_put
|
|
116
|
+
→ morit_project_validate
|
|
117
|
+
→ UI가 있으면 morit_project_preview
|
|
118
|
+
→ morit_build_start / morit_build_status
|
|
119
|
+
→ morit_artifact_download
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
주요 Local Tool:
|
|
123
|
+
|
|
124
|
+
- 계약·문서: `morit_sdk_contract`, `morit_docs_search`, `morit_docs_get`
|
|
125
|
+
- local project: `morit_project_create`, `morit_project_open`, `morit_project_files_get`,
|
|
126
|
+
`morit_project_files_put`
|
|
127
|
+
- 산출물: `morit_project_validate`, `morit_project_preview`, `morit_project_source_download`,
|
|
128
|
+
`morit_build_start`, `morit_build_status`, `morit_artifact_download`
|
|
129
|
+
- Cloud 상태·Project: `morit_cloud_status`, `morit_cloud_project_list`, `morit_cloud_project_get`,
|
|
130
|
+
`morit_cloud_project_add`, `morit_cloud_project_sync`, `morit_cloud_project_delete`
|
|
131
|
+
- Cloud Deployment: `morit_cloud_deployment_start`, `morit_cloud_deployment_list`,
|
|
132
|
+
`morit_cloud_deployment_get`, `morit_cloud_deployment_publish`
|
|
133
|
+
- Cloud Secret: `morit_cloud_secret_list`, `morit_cloud_secret_put`, `morit_cloud_secret_delete`
|
|
134
|
+
- Cloud Connection: `morit_cloud_connection_list`, `morit_cloud_connection_put`,
|
|
135
|
+
`morit_cloud_connection_delete`
|
|
136
|
+
|
|
137
|
+
MCP가 반환한 project ID, revision, artifact ID를 다음 호출에 그대로 사용합니다. 경로를 추측하거나
|
|
138
|
+
build 전에 완료했다고 보고하지 않습니다.
|
|
139
|
+
|
|
140
|
+
## Remote MCP 작업 순서
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
morit_organization_list
|
|
144
|
+
→ morit_project_create 또는 morit_project_list
|
|
145
|
+
→ morit_project_files_get
|
|
146
|
+
→ morit_project_files_put(revision 포함)
|
|
147
|
+
→ morit_project_validate
|
|
148
|
+
→ morit_project_preview
|
|
149
|
+
→ morit_build_start
|
|
150
|
+
→ morit_build_status
|
|
151
|
+
→ morit_deployment_get
|
|
152
|
+
→ morit_artifact_download
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Secret과 Connection은 build 전에 별도 Tool로 설정합니다. Secret 조회는 ID와 masked metadata만
|
|
156
|
+
반환하며 평문 값을 다시 읽을 수 없습니다.
|
|
157
|
+
|
|
158
|
+
## 에이전트 완료 규칙
|
|
159
|
+
|
|
160
|
+
AI 에이전트는 다음을 모두 확인한 뒤 완료를 보고합니다.
|
|
161
|
+
|
|
162
|
+
- source를 다시 읽고 요청한 파일이 존재함
|
|
163
|
+
- 최신 revision으로 validate 성공
|
|
164
|
+
- UI가 있으면 preview artifact 생성
|
|
165
|
+
- build state가 completed
|
|
166
|
+
- `.mplg` artifact ID, 크기, SHA-256, MIME 확인
|
|
167
|
+
- 다운로드한 package가 다시 열리고 signature가 유효함
|
|
168
|
+
- 배포 요청이면 고유 Deployment와 visibility 확인
|
|
169
|
+
|
|
170
|
+
## 연결 문제
|
|
171
|
+
|
|
172
|
+
| 증상 | 확인 |
|
|
173
|
+
|---|---|
|
|
174
|
+
| Local server가 보이지 않음 | Node 버전, `npx -y @morit/plugin-mcp`, client restart |
|
|
175
|
+
| workspace 밖 경로 거부 | `MORIT_PLUGIN_WORKSPACE`와 project 상대 경로 |
|
|
176
|
+
| Remote `401` | 정상 OAuth 시작 여부, `codex mcp login` |
|
|
177
|
+
| Remote `404` | `/mcp` ingress route |
|
|
178
|
+
| contract parse 오류 | server가 전체 JSON stdout을 반환하는지, stderr 혼합 여부 |
|
|
179
|
+
| revision conflict | `files_get`으로 최신 revision을 읽고 변경을 합침 |
|
|
180
|
+
| artifact 없음 | build status가 completed인지 확인 |
|
|
181
|
+
|
|
182
|
+
Remote Tool의 전체 필드는 [원격 Plugin MCP](remote-mcp.md)에 있습니다.
|
|
@@ -0,0 +1,163 @@
|
|
|
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.6의 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
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"id": "school_life.today",
|
|
126
|
+
"kind": "tool",
|
|
127
|
+
"title": "오늘 학교 생활 조회",
|
|
128
|
+
"description": "연결한 학교의 오늘 시간표, 급식, 학사일정을 조회합니다.",
|
|
129
|
+
"permissions": ["network", "storage"],
|
|
130
|
+
"timeout_seconds": 10,
|
|
131
|
+
"runtime": {"adapter": "neis_school", "operation": "overview"}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
UI `invoke`가 capability를 참조할 때도 enabled, generation, permission, timeout을 다시 확인합니다.
|
|
136
|
+
UI에 capability ID를 사용자용 라벨로 표시하지 않습니다.
|
|
137
|
+
|
|
138
|
+
## Slash Command
|
|
139
|
+
|
|
140
|
+
Slash Command는 같은 package의 `tool` 또는 `skill`에 고정된 진입점입니다.
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"id": "school_life.today_command",
|
|
145
|
+
"command": "school",
|
|
146
|
+
"title": "오늘 학교 생활",
|
|
147
|
+
"description": "오늘 시간표와 급식을 확인합니다.",
|
|
148
|
+
"capability": "school_life.today",
|
|
149
|
+
"argument_template": {}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Slash Command를 선언하지 않아도 Tool discovery는 유지됩니다. 여러 command가 같은 이름을 사용할 수
|
|
154
|
+
없고 `/`는 `command` 값에 포함하지 않습니다.
|
|
155
|
+
|
|
156
|
+
## 실패와 복구
|
|
157
|
+
|
|
158
|
+
Capability는 권한 부족, 연결 필요, timeout, retryable network 오류, 잘못된 응답을 구분합니다.
|
|
159
|
+
retry는 Connector에 선언한 bounded 정책만 사용합니다. Tool 하나가 실패했다고 AI 요청 전체를
|
|
160
|
+
즉시 포기하지 말고, 오케스트레이터가 대체 Tool·재연결·부분 결과를 선택할 수 있도록 공개 오류를
|
|
161
|
+
구체적으로 유지합니다.
|
|
162
|
+
|
|
163
|
+
권한과 알림은 [권한, 설정, 저장소, 알림](permissions-and-data.md)을 참고하세요.
|