meshy-forge
by v0o0v
README.md
# meshy-forge
Meshy MCP 위에 얹는 **3일 만료 방어 + 로컬 모델 보관·검색** 플러그인.
Meshy 는 비-Enterprise 생성물을 **3일 뒤 서버에서 삭제**한다([asset retention](https://docs.meshy.ai/en/api/asset-retention)).
다운로드를 놓치면 20~30크레딧이 그대로 증발하고, `retexture`/`remesh` 가 받는 `input_task_id` 도 함께 죽는다.
이 플러그인은 ① 만료 전에 원본을 로컬로 옮기고 ② 프롬프트로 다시 찾아 쓰게 만든다.
## 무엇이 어디에 있나
| | 위치 | git |
|---|---|---|
| 플러그인 코드·스킬 | 이 리포 | ✅ 커밋 |
| GLB 원본·썸네일·원장 | `library/` (기본값) | ❌ **절대 커밋 안 함** — 모델 한 건이 10MB 대 |
| Meshy MCP 설정(API 키) | `.mcp.json` | ❌ gitignore(로컬 디렉터리 마켓플레이스가 설치 사본으로 복사) |
라이브러리 경로 해석 순서:
`MESHY_FORGE_LIBRARY` → `~/.claude/meshy-forge.json` 의 `"library"` → **마켓플레이스 소스 리포**`/library` → 플러그인 루트.
> 세 번째가 중요하다. 플러그인은 설치 시 `~/.claude/plugins/cache/...` 로 **복사**되므로,
> 라이브러리를 플러그인 루트에 두면 재설치 한 번에 유일본이 사라진다.
> (pixellab-forge 에서 실제로 밟은 함정이다.)
## 설치
```
claude plugin marketplace add D:\ClaudeCowork\meshy-forge
claude plugin install meshy-forge@meshy-forge
node scripts/meshy-cache.mjs setup
```
Meshy MCP 서버까지 이 플러그인이 제공하게 하려면 `.mcp.json` 을 만들고 설치 사본을 갱신한다
(키는 환경변수로만 전달 — 리포에는 절대 커밋되지 않는다):
```
$env:MESHY_API_KEYS = "msy_첫번째,msy_두번째"; node scripts/mcp-setup.mjs
claude plugin marketplace update meshy-forge
claude plugin update meshy-forge@meshy-forge
```
키가 하나뿐이면 하나만 적어도 된다(`MESHY_API_KEY` 단일 변수도 그대로 받는다).
## 복수 API 키 — 크레딧 풀링
등록되는 MCP 서버는 업스트림이 아니라 `scripts/meshy-proxy.mjs` 프록시다.
프록시가 키마다 업스트림을 하나씩 상시 띄우고 하나의 `meshy` 서버처럼 보이게 중계한다.
- **서버 이름은 `meshy` 고정.** 바꾸면 툴 이름이 `mcp__meshy-a__…` 가 되어 `hooks/hooks.json` 의
`mcp__.*meshy__.*` matcher 가 죽고 **자동 보관이 통째로 멈춘다**(2026-08-19 에 이 형태로 180크레딧을 잃었다).
- **생성**은 활성 키로 보낸다. 잔액 부족이면 다음 키로 같은 요청을 재시도하고 활성 키를 옮긴 뒤,
응답 맨 앞에 `[meshy-forge] key1 잔액 부족 → key2 로 전환` 한 줄을 붙인다.
- **task 는 계정에 귀속된다.** `input_task_id`·`task_id` 가 있는 호출은 그 task 를 만든 키로 라우팅한다.
매핑은 `library/key-state.json` 에, 보관된 모델은 원장의 `meshy.keyLabel` 에 남는다(키 값이 아니라 라벨만).
- `meshy_check_balance` 는 모든 키를 조회해 키별 잔액과 합계를 함께 보여준다.
### 키를 하나 더 넣을 때
`mcp-setup.mjs --force` 는 키 전체를 다시 받아 파일을 새로 쓴다. 오타 하나로 살아 있던 계정과
그 계정 소유 task 를 통째로 잃을 수 있으니, 키 추가에는 전용 스크립트를 쓴다 —
**기존 키를 읽어서 그대로 두고 뒤에만 붙인다**.
새 키를 한 줄에 하나씩 적은 텍스트 파일을 만들고(`#` 로 시작하는 줄은 주석):
```
node scripts/add-api-key.mjs <입력파일>
```
`msy_` 로 시작하지 않는 줄과 이미 등록된 키는 걸러낸다. 출력에는 키를 마스킹해서만 찍고,
병합이 끝나면 입력 파일을 지운다(평문 키를 임시 파일로 남기지 않는다).
옛 원장에 라벨을 채우려면(도입 전 보관분은 전부 첫 키 소유다):
```
node scripts/backfill-key-label.mjs
```
폴백 동작을 크레딧 없이 확인하려면:
```
node test/proxy.test.mjs
```
기존에 user scope 로 등록된 `meshy` 서버가 있으면 이름이 겹치므로 먼저 제거한다:
`claude mcp remove meshy --scope user`
`setup` 은 검색 인덱스 백엔드(better-sqlite3 FTS5)를 설치·검증한다.
Node 메이저 버전을 올린 뒤 인덱스가 죽으면 같은 명령이 `npm rebuild` 로 복구한다.
## 명령
```
node scripts/meshy-cache.mjs pending 미보관 task 와 남은 시간(🔥 = 24h 미만)
node scripts/meshy-cache.mjs find "<영문 설명>" [--style …] 보관본 조회(score ≥ 0.6 이면 후보 제시)
node scripts/meshy-cache.mjs store --id … --file … --task-id … 보관(GLB 실측 + 원장 등록)
node scripts/meshy-cache.mjs export --id … --to <경로> 소비 프로젝트로 복사
node scripts/meshy-cache.mjs list | get <id> | config | prune | rebuild | test
node scripts/gallery.mjs --open 브라우저에서 실물 GLB 회전(검수용)
node scripts/plugin-sync-check.mjs 설치 사본이 소스보다 낡았는지
```
## 변경을 설치 사본에 반영하려면 버전을 올려라
`claude plugin update` 는 **`.claude-plugin/plugin.json` 의 `version` 이 같으면 "이미 최신"이라며 거부한다**
— 커밋이 아무리 앞서 있어도 설치 사본은 그대로다(실측: sync-check 는 STALE 인데 update 는 no-op).
그래서 설치 사본에 반영되어야 하는 변경은 **반드시 `version` 을 함께 올린다**:
```
claude plugin marketplace update meshy-forge
claude plugin update meshy-forge@meshy-forge
```
## 훅 (둘 다 비차단)
- **PreToolUse** `reuse-guard.mjs` — 생성 직전에 ① 만료 임박 미보관 task ② 유사 보관본을 알린다.
차단하지 않는다: 3D 는 미세한 형상 차이가 용도를 가르므로 재사용 강제는 해롭다.
- **PostToolUse** `pending-record.mjs` — 생성 호출을 pending 원장에 남긴다(응답에서 task id 추출).
"만들었는데 안 받았다"를 다음 호출·다음 `pending` 에서 잡는다.
## 문서
- `CONTEXT.md` — 용어·관계(보관/변형 창/미보관/스타일 프리셋)
- `skills/meshy/SKILL.md` — 실제 작업 순서
- `skills/meshy/references/meshy-guide.md` — 파라미터·비용·함정(`should_remesh` 누락, 2k 텍스처 하한, 리깅 한계)
- `styles/README.md` — 세트 일관성용 프리셋 스키마
## 테스트
```
node scripts/meshy-cache.mjs test 13건
node scripts/plugin-sync-check.mjs test 6건
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues