
takma MCP
ให้ Claude หรือ AI ตัวอื่นสร้าง อ่าน และแก้โปรเจคดีไซน์ใน takma ได้โดยตรงผ่าน Model Context Protocol โดยไม่ต้องใช้ token และ ข้ามรหัสผ่านของโปรเจค (ออกแบบมาให้เจ้าของระบบใช้เอง) สิ่งที่ AI สร้างจะเปิดดู/แก้ต่อในเว็บได้ทันทีผ่านลิงก์ /p/<id>
1. Endpoint
| ใช้เมื่อ | URL |
|---|---|
| local (ผ่าน web) | http://localhost:21401/mcp |
| local (ตรงเข้า api) | http://localhost:21402/mcp |
| public (ผ่าน tunnel ของคุณเอง) | https://takma.sabuytube.xyz/mcp — endpoint ไม่มี auth ระวังอย่าเปิดสาธารณะโดยไม่มีอะไรกั้น |
- Transport: Streamable HTTP · รับเฉพาะ
POST(GET/DELETE ได้ 405) · stateless ไม่มี session - Request ต้องส่ง
accept: application/json, text/event-stream; response เป็น SSE (data: …) - ทางเลือก stdio:
pnpm --filter api mcp:stdio(ต้องมีDATABASE_URLและ build แล้ว)
2. วิธีต่อ
Claude Code (local)
claude mcp add --transport http takma http://localhost:21401/mcpClaude Code (public)
claude mcp add --transport http takma https://takma.sabuytube.xyz/mcpClient อื่น (Cursor / Codex / Gemini ที่ใช้รูปแบบ mcpServers)
{
"mcpServers": {
"takma": {
"type": "http",
"url": "http://localhost:21401/mcp"
}
}
}stdio
{
"mcpServers": {
"takma": {
"command": "pnpm",
"args": [
"--filter",
"api",
"mcp:stdio"
],
"cwd": "<path-to-takma-repo>"
}
}
}3. Tools ทั้ง 9
| Tool | Input | Output | ตัวอย่าง arguments |
|---|---|---|---|
| list_projects รายการโปรเจคทั้งหมด (ใหม่สุดก่อน) | — (ไม่มี) | [{id, title, visibility, updatedAt, url}] | {} |
| get_project อ่านโปรเจคพร้อม data (nodes) — ไม่มี password hash | id: string (required) | {id, title, visibility, createdAt, updatedAt, data:{version, nodes[], nextId}} | {"id":"aB3dE5gH7j"} |
| create_project สร้างโปรเจคใหม่ · private ต้องมี editPassword + viewPassword ที่ต่างกัน · public ไม่ใส่ editPassword = open (ใครมีลิงก์ก็แก้ได้) · รหัสยาว ≥ 4 ตัว | code?: string (รหัสงาน 4-32 ตัว a-z 0-9 ขีด; ซ้ำ=code_taken, สงวน=code_reserved) · title: string (required) · visibility: "public"|"private" (required) · editPassword?: string (บังคับเมื่อ private) · viewPassword?: string · data?: {version?, nodes[], nextId?} | {id, url} (data ว่าง = {version:1, nodes:[], nextId:1}) | {"title":"Login screen","visibility":"private","editPassword":"edit1234","viewPassword":"view1234"} |
| update_project แก้ชื่อ / visibility / รหัส / data ทั้งก้อน (ไม่ต้องใส่รหัสเดิม) — เปลี่ยนรหัสแล้ว token เก่าของเว็บใช้ไม่ได้ · open → private ต้องส่ง editPassword + viewPassword ครบ · ใส่ editPassword ให้ open project = ล็อก | id: string (required) · title?: string · visibility?: "public"|"private" · editPassword?: string · viewPassword?: string · data?: {version?, nodes[], nextId?} | {id, updatedAt} | {"id":"aB3dE5gH7j","title":"Login v2"} |
| delete_project ลบโปรเจคถาวร | id: string (required) | {deleted: id} | {"id":"aB3dE5gH7j"} |
| add_nodes ต่อท้าย nodes (≥ 1) · node ที่ไม่ส่ง id จะได้ node-<nextId> · id ซ้ำ = error duplicate_node_id | id: string (required) · nodes: object[] (required, ≥ 1) | {added: [id, ...]} | {"id":"aB3dE5gH7j","nodes":[{"type":"rect","name":"Button","x":24,"y":700,"w":342,"h":52,"fill":"#9a80ff","radius":12}]} |
| update_node shallow-merge patch เข้า node เดียว (เปลี่ยน id ไม่ได้) | id: string (required) · nodeId: string (required) · patch: object (required) | {updated: nodeId} · ไม่พบ node = error node_not_found | {"id":"aB3dE5gH7j","nodeId":"node-3","patch":{"fill":"#ff5577","text":"Sign in"}} |
| remove_nodes ลบ nodes ตาม id (ไม่ลบลูกของ frame ให้อัตโนมัติ — ส่ง id ลูกมาด้วย) | id: string (required) · nodeIds: string[] (required, ≥ 1) | {removed: จำนวนที่ลบจริง} | {"id":"aB3dE5gH7j","nodeIds":["node-2","node-3"]} |
| get_share_link ลิงก์เปิดในเว็บ (PUBLIC_URL หรือ WEB_URL + /p/<id>) — ถ้าเป็น private คนเปิดยังต้องใช้รหัส | id: string (required) | {url, visibility} | {"id":"aB3dE5gH7j"} |
Error ของ tool จะกลับมาเป็น isError: true พร้อมข้อความ เช่น not_found, invalid_input, passwords_must_differ, view_password_required
4. โครงสร้าง node (สำหรับ add_nodes)
document คือ {version:1, nodes:[], nextId} — แต่ละ node เป็น object ธรรมดา editor ไม่เติมค่า default ให้ ดังนั้นควรส่ง field ร่วมให้ครบทุกชิ้น
| กลุ่ม | Fields (ค่า default ของ editor) |
|---|---|
| type | rect · ellipse · text · path · frame · image |
| ร่วมทุกชนิด | id · type · name · x y w h (px, 160×90) · fill (#ffffff, text = #20243a, ไม่มี = none) · stroke (none) · strokeWidth (1) · radius (0) · opacity (0–100, 100) · parentId (null) · hidden (false) · locked (false) |
| text | text · fontSize (22) · fontWeight (500) · align (left|center|right) · autoSize (false) |
| path | d (SVG path data) · baseW · baseH (ขนาดตั้งต้นของ d ใช้ย่อ/ขยายตาม w/h) · stroke · strokeWidth · fill |
| image | src — data URI (data:image/png;base64,…) · fill และ stroke ใช้ none |
| frame | container — node อื่นอ้าง parentId เป็น id ของ frame |
ข้อควรระวัง: พิกัด
x/y ของลูกเป็นพิกัด canvas แบบ absolute (ไม่ใช่ offset จาก frame) · ถ้าจะให้ลูกชี้ parentId ถึง frame ที่เพิ่งสร้างใน call เดียวกัน ให้ระบุ id เองแบบ node-<n> (server จะขยับ nextId ให้) · ลำดับใน array = ลำดับซ้อน (ท้ายสุดอยู่บนสุด) · ใส่ id ซ้ำกับของเดิมไม่ได้ตัวอย่าง arguments ของ add_nodes: frame มือถือ 390×844 + ข้อความ + ปุ่ม (frame อยู่ที่ 0,0 จึงใช้พิกัดตรงๆ)
{
"id": "aB3dE5gH7j",
"nodes": [
{
"id": "node-1",
"type": "frame",
"name": "Mobile 390x844",
"x": 0,
"y": 0,
"w": 390,
"h": 844,
"fill": "#ffffff",
"stroke": "none",
"strokeWidth": 1,
"radius": 0,
"opacity": 100,
"text": "",
"fontSize": 22,
"fontWeight": 500,
"align": "left",
"autoSize": false,
"parentId": null,
"hidden": false,
"locked": false
},
{
"id": "node-2",
"type": "text",
"name": "Title",
"x": 24,
"y": 120,
"w": 342,
"h": 40,
"fill": "#20243a",
"stroke": "none",
"strokeWidth": 1,
"radius": 0,
"opacity": 100,
"text": "Welcome back",
"fontSize": 32,
"fontWeight": 700,
"align": "left",
"autoSize": false,
"parentId": "node-1",
"hidden": false,
"locked": false
},
{
"id": "node-3",
"type": "rect",
"name": "Sign in button",
"x": 24,
"y": 700,
"w": 342,
"h": 52,
"fill": "#9a80ff",
"stroke": "none",
"strokeWidth": 1,
"radius": 12,
"opacity": 100,
"text": "",
"fontSize": 22,
"fontWeight": 500,
"align": "left",
"autoSize": false,
"parentId": "node-1",
"hidden": false,
"locked": false
}
]
}5. ตัวอย่าง workflow
create_project—{"title":"Login screen","visibility":"private","editPassword":"edit1234","viewPassword":"view1234"}→ ได้{id, url}add_nodesด้วยidที่ได้ + nodes ตามตัวอย่างด้านบนget_projectเช็คผล (ดู nodes กลับมา)get_share_link→ เปิดลิงก์ในเบราว์เซอร์ (private ใส่ view/edit password ที่ตั้งไว้)- ปรับเพิ่มด้วย
update_node/remove_nodes
ลองด้วย curl (JSON-RPC)
# 1) initialize
curl -s -X POST http://localhost:21401/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
# 2) tools/list
curl -s -X POST http://localhost:21401/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# 3) เรียก tool
curl -s -X POST http://localhost:21401/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"list_projects","arguments":{}}}'6. ความปลอดภัย
MCP ไม่มี auth และ ข้ามรหัสผ่านของโปรเจค — ใครเข้า endpoint ได้ก็ อ่าน แก้ ลบ ทุกโปรเจคได้
- docker compose bind พอร์ต 21401–21403 ที่
127.0.0.1เท่านั้น (เข้าได้จากเครื่องนี้เครื่องเดียว) - เปิดออกเน็ตผ่าน tunnel/proxy ของคุณเอง:
/mcpไม่มี auth และข้ามรหัสผ่านโปรเจกต์ ควรมีอะไรกั้นก่อนเปิดสาธารณะ - REST API ของเว็บ (
/api/projects) ยังใช้รหัสผ่านตามปกติ ต่างจาก MCP
← กลับหน้าแรก · รายละเอียดการรัน: ดู README.md ใน repo