zip 묶음 — 카드+모듈+이미지 동봉 — 풀어서 개별 임포트
👍 7 · 조회 264 · 2026-04-19
Claude Desktop Emoticon MCP Server V3
Claude Desktop 채팅에서 캐릭터 이모티콘을 인라인으로 표시하는 MCP Apps 서버
MCP Apps
Node.js 20+
Claude Desktop
SECTION 01
1. 동작 원리
Claude Desktop MCP Server | | |-- tools/call express_emotion --> | | { emotion: "happy" } | | | | <-- result -------------------- | | "__emotion__:happy" (90 bytes) | | | |-- resources/read ui://... ----> | | <-- HTML (이미지 내장) --------- | | | [iframe: 이모티콘 표시] |
핵심:
• tool result에는 감정 이름과 한 줄 묘사만 전달 →
호출당 ~60-80 토큰
• 이미지는 빌드된 HTML에 base64 인라인 → 별도 서버 불필요
• ontoolinput으로 스트리밍 중에도 미리 표시
토큰 분석 (이론적 추정)
※ Claude Desktop은 토큰 카운터를 노출하지 않으므로 직접 실측이 아니라
tool result 글자수 → 토큰 환산
(한글 1자 ≈ 1.5–2 토큰, 영문 ≈ 0.25 토큰)과 일반 prompt-cache 가정(98.8%)을 조합한
추정값
입니다. 정확한 값은 API 응답 헤더(
anthropic-input-tokens
등)로만 측정 가능합니다.
항목
측정값
캐시 적용 시 (98.8%)
tool 정의 (매 컨텍스트 1회)
~50 토큰
effective ~0.6 토큰
호출당 응답 (express_emotion)
~60-80 토큰
동일 (응답은 캐시 X)
호출당 응답 (set_outfit)
~30 토큰
동일
100턴 세션 누적 (호출 × 1/턴)
~7,000 토큰
~7,000 토큰 (대부분 응답)
•
tool 정의는 cache hit 적용
되어 effective 토큰이 거의 0에 수렴
•
호출 응답은 매번 새 토큰
이지만 description 한 줄 한정이라 부담 적음
• description을 빈 채로 두면 위젯 placeholder 메시지가 따로 붙어 오히려 토큰 손해 (zod min(2)로 강제)
• 이미지는 iframe 안에서만 보이고 LLM 컨텍스트엔 전혀 안 들어감 (이게 가장 큰 절감)
SECTION 02
2. 필수 요건
런타임
Node.js 20+
클라이언트
Claude Desktop (MCP Apps 지원)
에셋
감정별 이모티콘 이미지 (.webp 또는 .png)
SECTION 03
3. 폴더 구조
CLAUDE CODE
•
"mcp-server 폴더 만들고 그 안에 app 폴더도 만들어줘"
•
"assets 폴더도 만들어줘"
your-project/ ├──
assets/
← 이모티콘 에셋 (별도 공유)
│ ├── neutral.webp ~ tired.webp (16개) ├──
mcp-server/
│ ├── index.js
← MCP 서버 메인
│ ├── package.json │ ├── vite.config.js │ ├──
app/
│ │ ├── index.html
← 뷰어 HTML
│ │ └── main.js
← 뷰어 로직
│ └──
dist/
← 빌드 결과 (자동 생성)
└──
CLAUDE.md
← 시스템 프롬프트
SECTION 04
4. 파일 생성
CLAUDE CODE — 한 번에 전부 만들기
"
README.md
보고 mcp-server 폴더에 이모티콘 MCP 서버 파일 전부 만들어줘. package.json, vite.config.js, index.js, app/index.html, app/main.js"
CLAUDE CODE — 또는 하나씩 만들기
•
"mcp-server/package.json 만들어줘. MCP 이모티콘 서버용이고,
@modelcontextprotocol
/sdk랑 ext-apps, zod 의존성 넣고, vite 빌드 스크립트 추가해줘"
•
"vite.config.js 만들어줘. viteSingleFile 플러그인 쓰고, app 폴더가 root, 빌드 결과는 dist에, 이미지 전부 인라인되게 assetsInlineLimit 크게 잡아줘"
•
"MCP 서버 index.js 만들어줘. 감정 26종류 enum으로 express_emotion tool 하나만 등록하고, tool result에는 이미지 안 넣고 감정 이름만 텍스트로 보내줘. registerAppResource 대신 server.resource() 직접 써야 해"
•
"app/index.html이랑 app/main.js 만들어줘. 뷰어에서 감정별 webp 이미지를 직접 import하고, ontoolresult랑 ontoolinput으로 이모티콘 표시하게"
package.json
mcp-server/package.json
{
"name"
:
"claude-chan-emoticon"
,
"version"
:
"3.0.0"
,
"type"
:
"module"
,
"main"
:
"index.js"
,
"scripts"
: {
"build"
:
"vite build"
,
"start"
:
"node index.js"
},
"dependencies"
: {
"@modelcontextprotocol/ext-apps"
:
"latest"
,
"@modelcontextprotocol/sdk"
:
"^1.10.0"
,
"zod"
:
"^4.3.6"
},
"devDependencies"
: {
"vite"
:
"^6.0.0"
,
"vite-plugin-singlefile"
:
"^2.0.0"
} }
vite.config.js
mcp-server/vite.config.js
import
{ defineConfig }
from
"vite"
;
import
{ viteSingleFile }
from
"vite-plugin-singlefile"
;
export default
defineConfig
({ plugins: [
viteSingleFile
()], root:
"app"
, build: { rollupOptions: { input:
"app/index.html"
}, outDir:
"../dist"
, emptyOutDir:
true
, assetsInlineLimit:
1024 * 1024
,
// 이미지를 base64로 인라인
}, });
index.js (MCP 서버)
mcp-server/index.js
#!/usr/bin/env node
import
{ McpServer }
from
"@modelcontextprotocol/sdk/server/mcp.js"
;
import
{ StdioServerTransport }
from
"@modelcontextprotocol/sdk/server/stdio.js"
;
import
{ registerAppTool, RESOURCE_MIME_TYPE }
from
"@modelcontextprotocol/ext-apps/server"
;
import
{ z }
from
"zod"
;
import
fs
from
"node:fs"
;
import
path
from
"node:path"
;
import
{ fileURLToPath }
from
"node:url"
;
const
__dirname = path.
dirname
(
fileURLToPath
(
import
.meta.url));
const
DIST_DIR = path.
join
(__dirname,
"dist"
);
// ─── 감정 목록 (에셋 파일명과 일치해야 함) ───
const
EMOTIONS = [
"neutral"
,
"happy"
,
"embarrassed"
,
"sad"
,
"angry"
,
"surprised"
,
"love"
,
"smug"
,
"confused"
,
"crying"
,
"excited"
,
"proud"
,
"scared"
,
"sleepy"
,
"thinking"
,
"tired"
, ];
const
EMOTION_KR = { neutral:
"기본"
, happy:
"기쁨"
, embarrassed:
"당황"
, sad:
"슬픔"
zip 8,189,774B · 원본 Download 2026-08-06T17-53-40-616Z.zip