keyboard_tabSkip to content
arrow_back All Posts

Oct 19, 2025

มาลองเล่น Apps SDK บน ChatGPT กัน

สร้าง App บน ChatGPT! เรียนรู้หลักการของ Apps SDK และ MCP Protocol พร้อม Demo สร้าง UI Component ด้วย React และ Typescript แสดงผลและสื่อสารกับ ChatGPT โดยตรง

สารบัญ

apps-sdk สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video

รู้จักกับ App SDK ก่อน

ref: https://openai.com/index/introducing-apps-in-chatgpt/

Apps SDK คือชุดเครื่องมือสำหรับนักพัฒนา (Software Development Kit) ที่ OpenAI สร้างขึ้นเพื่อให้นักพัฒนาสามารถสร้าง "Application" ที่ทำงานและโต้ตอบกับผู้ใช้ได้โดยตรงภายในหน้าต่าง Chat ของ ChatGPT

คิดง่ายๆ ว่ามันคือเครื่องมือที่เปลี่ยนให้ ChatGPT เป็นเหมือน "ระบบปฏิบัติการ" ที่สามารถรัน "Mini App" จากภายนอกได้ ไม่ใช่แค่การตอบข้อความธรรมดาอีกต่อไป โดย app เหล่านี้สามารถ:

  1. แสดงผลแบบ Interactive: แทนที่จะตอบเป็นข้อความอย่างเดียว app สามารถแสดงผลเป็น UI ที่ผู้ใช้โต้ตอบได้ เช่น แผนที่ (Zillow), การ์ดข้อมูลพร้อมปุ่ม (Booking.com), สไลด์นำเสนอ (Canva), หรือ Playlist เพลง (Spotify)
  2. เชื่อมต่อกับบริการภายนอก: นักพัฒนาสามารถเชื่อมต่อ app เข้ากับระบบ Backend ของตัวเองได้โดยตรง ทำให้สามารถดึงข้อมูลที่เป็นปัจจุบัน หรือสั่งงานที่ซับซ้อนในบริการของตนได้ เช่น การจองโรงแรม, การค้นหาบ้าน รวมถึงการสร้าง Design
  3. ทำงานร่วมกับการสนทนา: ผู้ใช้สามารถ "คุย" กับapp ได้ผ่าน ChatGPT เช่น พิมพ์ว่า "Spotify, สร้าง Playlist เพลงปาร์ตี้ให้หน่อย" แล้ว UI ของ Spotify ก็จะปรากฏขึ้นมาพร้อมกับ Playlist นั้น

หัวใจสำคัญของ Apps SDK คือ protocol ที่ชื่อว่า Model Context Protocol (MCP) ซึ่งเป็นมาตรฐานเปิดที่ออกแบบมาเพื่อให้โมเดลภาษา (LLM) สามารถเชื่อมต่อกับเครื่องมือและข้อมูลภายนอกได้อย่างปลอดภัยและมีประสิทธิภาพ

Apps SDK ทำงานร่วมกับ ChatGPT อย่างไร?

ref: https://developers.openai.com/apps-sdk/concepts/design-guidelines

เราจะขอเล่าผ่าน sequence diagram ด้านล่างนี้ละกัน

Loading diagram…

โดยกระบวนการทำงานตั้งแต่ผู้ใช้พิมพ์คำสั่งจนกระทั่ง app แสดงผลขึ้นมาใน chat มีขั้นตอนหลักๆ ดังนี้

ขั้นตอนที่ 1: ผู้ใช้เริ่มบทสนทนา (User Invocation)

  • ผู้ใช้พิมพ์คำสั่งใน ChatGPT โดยอาจจะเรียกชื่อ app โดยตรง (เช่น "Zillow, หาบ้านในกรุงเทพให้หน่อย") หรือ ChatGPT อาจจะฉลาดพอที่จะแนะนำ app ที่เหมาะสมกับบริบทการสนทนาเอง

ขั้นตอนที่ 2: ChatGPT เรียกใช้เครื่องมือ (Tool Call)

  • เมื่อ ChatGPT วิเคราะห์แล้วว่าต้องใช้ความสามารถจาก app ภายนอก มันจะทำการเรียกใช้ "Tool" ที่นักพัฒนาได้ลงทะเบียนไว้ โดยส่งคำสั่งและข้อมูลที่จำเป็นไปยัง Server ของนักพัฒนา (MCP Server)

ขั้นตอนที่ 3: Server ของนักพัฒนาประมวลผล (Backend Processing)

  • Server ของapp (ซึ่งเป็น Backend ที่นักพัฒนาสร้างขึ้น) จะได้รับคำสั่งจาก ChatGPT
  • Server จะทำการประมวลผลตามที่ร้องขอ เช่น ค้นหาข้อมูลในฐานข้อมูล, เรียกใช้ API ภายนอก, หรือคำนวณค่าต่างๆ

ขั้นตอนที่ 4: Server ส่งข้อมูลกลับไปยัง ChatGPT (Response)

Server จะส่งข้อมูลกลับมาให้ ChatGPT ในรูปแบบโครงสร้างที่กำหนดไว้ ซึ่งประกอบด้วย 3 ส่วนหลัก:

  1. structuredContent: ข้อมูลที่มีโครงสร้างชัดเจน (เช่น JSON) ที่จะถูกส่งเข้าไป "เติม" ใน UI Component เพื่อให้แสดงผลได้อย่างถูกต้อง (เช่น รายชื่อโรงแรม, พิกัดบนแผนที่)
  2. content: ข้อความธรรมดา (Markdown) สำหรับให้ ChatGPT อ่านและนำไปสรุปเป็นคำพูดเพื่อสนทนากับผู้ใช้ต่อไป
  3. UI Template: ที่อยู่ (URI) ของ code UI (HTML/JS/CSS) ที่จะใช้แสดงผล ซึ่งนักพัฒนาได้สร้างและลงทะเบียนไว้ล่วงหน้า

ขั้นตอนที่ 5: ChatGPT แสดงผล UI Component (Rendering)

  • ChatGPT ได้รับข้อมูลทั้งหมดกลับมา
  • มันจะทำการโหลด UI Template (ซึ่งมักจะเป็น React Component) มาแสดงผลภายใน iframe (กรอบสี่เหลี่ยมที่ฝังอยู่ในหน้าเว็บ)
  • จากนั้นนำข้อมูล structuredContent จากขั้นตอนที่ 4 มาใส่ใน UI นั้น ทำให้ผู้ใช้เห็นเป็นหน้าตาapp ที่สวยงามและมีข้อมูลครบถ้วน

ขั้นตอนที่ 6: ผู้ใช้โต้ตอบกับ UI (User Interaction)

ผู้ใช้สามารถคลิกปุ่ม, เลื่อนดูข้อมูล, หรือกระทำการใดๆ บน UI ที่แสดงผลได้โดยตรง เช่น กดปุ่ม "บันทึก" บ้านใน app Zillow

  • การทำงานเบื้องหลัง: เมื่อผู้ใช้โต้ตอบกับ UI ตัว UI Component ที่รันอยู่ใน iframe สามารถเรียกใช้ Tool บน Server ของนักพัฒนาได้อีกครั้งผ่าน window.openai.callTool API เพื่ออัปเดตข้อมูลหรือทำงานต่อเนื่องได้ทันที โดยไม่จำเป็นต้องให้ผู้ใช้พิมพ์คำสั่งใหม่

MCP คืออะไร ?

ref: https://mikelopster.dev/posts/mcp-basic/

MCP (Model Context Protocol) คือ "ภาษากลาง" หรือ "มาตรฐานการสื่อสาร" ที่ออกแบบมาโดยเฉพาะเพื่อให้โมเดลภาษาขนาดใหญ่ (LLM) อย่าง ChatGPT สามารถ "คุย" และทำงานร่วมกับ "เครื่องมือ" หรือ "app พลิเคชัน" ภายนอกได้อย่างเป็นระบบและปลอดภัย

ถ้าเปรียบเทียบกับโลกของเว็บ

  • HTTP คือ protocol ที่ทำให้ Browser กับเว็บ server คุยกันรู้เรื่อง
  • MCP ก็คือ protocol ที่ทำให้ ChatGPT (Client) กับ Server ของนักพัฒนา (Server) คุยกันรู้เรื่องนั่นเอง

องค์ประกอบหลักในการสื่อสารของ MCP

  • การสื่อสารผ่าน MCP จะมีรูปแบบเป็น JSON-RPC ซึ่งเป็นมาตรฐานการเรียกใช้ function ระยะไกล (Remote Procedure Call) โดยมีการเรียกใช้หลักๆ คือ
  • initialize (การทักทาย):
    • เป็นขั้นตอนแรกสุดที่ ChatGPT จะ "แนะนำตัว" กับ server และ server ก็จะตอบกลับความสามารถพื้นฐานของตัวเอง เช่น รองรับภาษาอะไรบ้าง
  • listTools (การถามว่าทำอะไรได้บ้าง):
    • ChatGPT จะถาม server ว่า "คุณมีเครื่องมือ (Tool) อะไรให้ฉันใช้บ้าง?"
    • server จะตอบกลับเป็นรายการเครื่องมือทั้งหมด พร้อมคำอธิบาย, ชื่อ, และโครงสร้างข้อมูลที่ต้องใช้ (Input Schema) ซึ่งข้อมูลส่วนนี้สำคัญมาก เพราะ ChatGPT จะใช้ในการตัดสินใจว่าจะเรียกใช้เครื่องมือไหนและเมื่อไหร่
  • callTool (การสั่งให้ทำงาน):
    • เมื่อ ChatGPT ตัดสินใจได้แล้วว่าจะใช้เครื่องมือไหน มันจะส่งคำสั่งนี้ไปยัง server พร้อมกับข้อมูล (Arguments) ที่จำเป็น
    • ตัวอย่าง: callTool(name: "search_hotels", args: { city: "Paris", checkin_date: "..." })
  • Response (การตอบกลับ):
    • หลังจาก server ทำงานเสร็จ ก็จะส่งผลลัพธ์กลับมาในโครงสร้างที่ MCP กำหนดไว้ ซึ่งก็คือ structuredContent, content, และ _meta ที่เราคุยกันไปก่อนหน้านี้

คำถาม “MCP กับ Apps SDK ต่างกันอย่างไร ?”

  • MCP คือ "กฎ" หรือ " protocol " (The Rules):
    • มันเป็นเพียงข้อกำหนดทางเทคนิคล้วนๆ ว่าต้องส่งข้อมูลหน้าตาแบบไหนไปมา
  • Apps SDK คือ "ชุดเครื่องมือ" ที่ช่วยให้ทำตามกฎได้ง่ายขึ้น (The Toolkit):
    • นักพัฒนาไม่จำเป็นต้องมานั่งเขียน code เพื่อสร้าง JSON-RPC message ตามมาตรฐาน MCP ด้วยตัวเองทั้งหมด
    • Apps SDK (เช่น Library FastMCP ใน Python หรือ @modelcontextprotocol/sdk ใน TypeScript) ได้เตรียม function และคลาสต่างๆ ที่ห่อหุ้มความซับซ้อนเหล่านี้ไว้ให้แล้ว
    • นักพัฒนาแค่เรียกใช้ function ง่ายๆ เช่น server.registerTool(...) แล้ว SDK จะจัดการสร้าง Response ตามรูปแบบของ MCP ให้โดยอัตโนมัติ

มาเริ่มกัน

Ref: https://developers.openai.com/apps-sdk

เนื่องจากการสื่อสารทั้งหมดระหว่าง ChatGPT และ Server ของนักพัฒนา จะเกิดขึ้นบน protocol HTTP/HTTPS ดังนั้น ผมจะขอหยิบ typescript มาเป็นจุดเริ่มต้นนะครับ

เราจะมาลองตัวอย่างง่ายๆ ตาม document กันก่อน โดยการสร้างapp สำหรับ ChatGPT จะแบ่งออกเป็น 2 ส่วนหลักๆ คือ:

  1. Backend (MCP Server): ส่วนที่จัดการ logic และกำหนดว่า app ทำอะไรได้บ้าง (Tools), และส่งข้อมูลพร้อมกับ "HTML" สำหรับแสดงผล UI ของคุณ
  2. Frontend (UI Component): ส่วนที่แสดงผลให้ผู้ใช้เห็น ซึ่งมักจะเป็น React Component ที่ทำงานอยู่ภายใน iframe ที่ ChatGPT สร้างขึ้น

โดย project structure จะมีหน้าตาประมาณนี้

├── server/
   └── index.ts              # MCP server implementation
├── web/
   ├── src/
      └── components.jsx    # React component for UI
   └── dist/                 # Built component output
├── package.json
├── tsconfig.json
└── README.md

ขั้นตอนที่ 1: ตั้งค่า project และ server (Backend)

นี่คือส่วนรากฐานของ app ทั้งหมด เราจะสร้าง server ที่สื่อสารกับ ChatGPT

1.1. สร้างโครงสร้าง project : สร้างโฟลเดอร์สำหรับ project ของคุณ และแบ่งเป็นส่วนของ server และ web (สำหรับ UI)

bash
mkdir my-chatgpt-app
cd my-chatgpt-app
mkdir server web

1.2. ติดตั้ง Dependencies สำหรับ Server: เข้าไปที่โฟลเดอร์ server แล้วติดตั้ง SDK ที่จำเป็น

bash
cd server
npm init -y
npm install @modelcontextprotocol/sdk zod express @types/express
# express เป็น web framework ที่นิยม แต่จะใช้อะไรก็ได้

1.3. สร้างไฟล์ server.ts และเขียน code พื้นฐาน: เราจะสร้าง server ที่กำหนด "Tool" หนึ่งตัวชื่อว่า show_greeting และ "Resource" สำหรับ UI ของมัน

  • ไฟล์ server/index.ts
typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { z } from 'zod'
import { readFileSync } from 'node:fs'
import express, { Request, Response } from 'express'
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'

// 1. สร้าง MCP Server
const server = new McpServer({
  name: 'greeting-server',
  version: '1.0.0',
})

// 2. กำหนด Resource สำหรับ UI 
server.registerResource(
  'greeting-ui-new', // ชื่อ resource
  'ui://widget/greeting-new.html', // URI ที่ไม่ซ้ำกันสำหรับ UI นี้
  {},
  async () => {
    // อ่านไฟล์ UI ที่ build เสร็จแล้ว (จะสร้างในขั้นตอนถัดไป)
    const componentJs = readFileSync('./web/dist/component.js', 'utf8')

    return {
      contents: [
        {
          uri: 'ui://widget/greeting-new.html',
          mimeType: 'text/html+skybridge',
          text: `
            <div id="greeting-root"></div>
            <script type="module">${componentJs}</script>

            <script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
          </div>
          `.trim(),
        },
      ],
    }
  }
)

// 3. ลงทะเบียน Tool เพื่อให้ ChatGPT เรียกใช้ได้
server.registerTool(
  'show_greeting',
  {
    title: 'Show Greeting',
    _meta: {
      'openai/outputTemplate': 'ui://widget/greeting-new.html',
    },
    description: 'Displays a simple greeting message.',
    inputSchema: {
      name: z.string().describe('The name to greet'),
    },
  },
  async ({ name }) => {
    const message = `Hello, ${name}!`
    // สิ่งที่ Tool จะทำเมื่อถูกเรียก
    return {
      // structuredContent คือข้อมูลที่จะถูกส่งไปให้ UI Component ของเรา
      structuredContent: {
        greetingMessage: message,
      },
      // content คือข้อความที่ Model (ChatGPT) จะเห็น
      content: [{ type: 'text', text: 'Displayed a greeting.' }],
    }
  }
)

// 4. run express.js
const app: express.Express = express()
const PORT = 3000

app.get('/', (req: Request, res: Response) => {
  res.status(200).send('Greeting Server is up and running!')
})
app.post('/mcp', async (req: Request, res: Response) => {
  // Create a new transport for each request to prevent request ID collisions
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined,
    enableJsonResponse: true,
  })

  res.on('close', () => {
    transport.close()
  })

  await server.connect(transport)
  await transport.handleRequest(req, res, req.body)
})

app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`)
  console.log(`MCP endpoint available at http://localhost:${PORT}/mcp`)
})
  • โดยตัว code มีการ follow practice mcp ของ typescript ตาม url นี้ สำหรับในส่วนของการเปิด server สำหรับ post /mcp https://github.com/modelcontextprotocol/typescript-sdk
    • new StreamableHTTPServerTransport(...) คือ class ที่ใช้เป็น transport layer สำหรับสื่อสารระหว่าง MCP Server กับ Client ผ่าน HTTP แบบ “streamable” — หมายถึงมันสามารถส่งข้อมูลกลับแบบต่อเนื่อง (stream) ได้ ไม่จำเป็นต้องรอประมวลผลทั้งหมดเสร็จก่อนถึงจะตอบกลับ
    • ทั้งสองฝั่ง (mcp client และ mcp server) "คุยกัน" ด้วยภาษา JSON-RPC 2.0 และ "ส่งข้อความ" หากันผ่านทาง Stdio หรือ HTTP

ขั้นตอนที่ 2: สร้าง UI ของคุณ (Frontend)

ตอนนี้เราจะสร้าง React Component ที่จะแสดงผลข้อมูลที่ได้จาก structuredContent

2.1. ติดตั้ง Dependencies สำหรับ Frontend: เข้าไปที่โฟลเดอร์ web

bash
cd ../web
npm init -y
npm install react@^18 react-dom@^18
npm install -D esbuild

2.2. สร้างไฟล์ UI Component: สร้างไฟล์ web/src/GreetingComponent.jsx

typescript
	import React, { useState, useEffect } from 'react'
	import { createRoot } from 'react-dom/client'
	
	const getInitialToolOutput = () => {
	  return window.openai?.toolOutput
	}
	
	function App() {
	  const [toolOutput, setToolOutput] = useState(getInitialToolOutput)
	
	  useEffect(() => {
	    const handleGlobalsChange = () => {
	      console.log(
	        "Received 'openai:set_globals' event. Updating component state."
	      )
	      // ดึงข้อมูลใหม่แล้วอัปเดต state ของเรา
	      setToolOutput(window.openai?.toolOutput)
	    }
	
	    window.addEventListener('openai:set_globals', handleGlobalsChange)
	
	    return () => {
	      window.removeEventListener('openai:set_globals', handleGlobalsChange)
	    }
	  }, [])
	
	  const message = toolOutput?.greetingMessage || 'Loading...'
	
	  return (
	    <div className="flex justify-center py-8 bg-gray-100 font-sans">
	      <div className="bg-white rounded-2xl px-6 py-4 shadow-xl border border-gray-200 text-center max-w-md w-full mx-5">
	        <div className="text-2xl font-semibold text-gray-800 tracking-wide">
	          {message}
	        </div>
	        <p className="mt-4 text-sm text-gray-600">
	          This UI is rendered by your custom component!
	        </p>
	      </div>
	    </div>
	  )
	}
	
	const container = document.getElementById('greeting-root')
	if (container) {
	  createRoot(container).render(<App />)
	}
	
  • openai:set_globals คือ ชื่อของ Custom Event ที่ ChatGPT Host (หน้าเว็บ chat.openai.com) ยิงออกมา เพื่อแจ้งเตือน UI Component ที่รันอยู่ใน iframe ว่า "ข้อมูลส่วนกลาง (Global State) บางอย่างมีการเปลี่ยนแปลงนะ!"

2.3. ตั้งค่าการ Build: ในไฟล์ web/package.json เพิ่ม script สำหรับ build

json
{
  "scripts": {
    "build:esbuild": "esbuild src/GreetingComponent.jsx --bundle --format=esm --outfile=dist/component.js"
  }
}

2.4. Build Component: รันคำสั่ง build เพื่อสร้างไฟล์ JavaScript ที่เราจะให้ Server อ่าน

bash
npm run build:esbuild

ตอนนี้ควรจะมีไฟล์ web/dist/component.js เรียบร้อยแล้ว

ขั้นตอนที่ 3: รันและทดสอบในเครื่อง

3.1. รัน Server ของคุณ:

  • กลับไปที่โฟลเดอร์ server แล้วรัน server.ts (อาจจะต้องใช้ ts-node หรือ build เป็น JS ก่อน)
bash
npm install --save-dev ts-node typescript
npx ts-node server.ts

# หรือจะ build ไปเลย
npx tsc server.ts
node server.js

เมื่อ run เรียบร้อย server จะแสดงออกมาที่ http://localhost:3000 และสามารถยิง POST ผ่าน path http://localhost:3000/mcp ได้

  • ในขั้นนี้สามารถใช้ MCP Inspector https://modelcontextprotocol.io/docs/tools/inspector เพื่อตรวจสอบได้ว่า implement MCP มาถูกต้องหรือไม่ (สามารถดูได้จากหัวข้อ MCP ก่อนหน้าได้เลย)

3.2. ทำให้ Server เข้าถึงได้จากภายนอกด้วย ngrok:

ChatGPT ต้องเรียก server ของคุณผ่าน HTTPS เราจะใช้ ngrok เพื่อสร้าง tunnel ชั่วคราว

bash
# หาก server ของคุณรันบน port 3000
ngrok http 3000
  • ngrok จะให้ URL ที่เป็น HTTPS มา เช่น https://random-string.ngrok.app ให้คัดลอก URL นี้ไว้

ขั้นตอนที่ 4: เชื่อมต่อapp ของคุณเข้ากับ ChatGPT

4.1. เปิด Developer Mode ใน ChatGPT: ไปที่ Settings → Connectors → Advanced แล้วเปิด Developer mode

setting-1.webp

setting-2.webp

setting-3.webp

    • ข้อสังเกตตอนนี้ : สามารถเปิดได้กับ Account ChatGPT Plus เป็นต้นไป ไม่เจอ option นี้ใน ChatGPT จริง

4.2. สร้าง Connector: เมื่อเปิด Developer Mode แล้ว จะมีปุ่ม Create ปรากฏขึ้นมา ให้กดสร้างและกรอกข้อมูล:-

connector-1.webp

example-setup.webp

  • Connector name: Greeting App (ชื่อที่แสดงให้ผู้ใช้เห็น)
  • Description: A simple app to show a greeting. (คำอธิบาย)
  • Connector URL: ใส่ URL จาก ngrok ที่คัดลอกมา แล้วต่อท้ายด้วย /mcp

4.3. เปิดใช้งานและทดสอบในหน้า chat :

  1. เปิดหน้า chat ใหม่

  2. คลิกที่ปุ่ม + ใกล้ๆ กล่องข้อความ แล้วเลือก Developer mode

  3. เปิดใช้งาน "Greeting App" ของคุณ

  4. ลองพิมพ์ Prompt เพื่อเรียกใช้ Tool ของคุณ:

    "Show Greeting Mike"

demo-1.webp

demo-2.webp

demo-3.webp

ถ้าทุกอย่างถูกต้อง ChatGPT จะเรียก show_greeting tool ของคุณ, server จะตอบกลับด้วยข้อมูลและ UI, และคุณจะเห็น Component ที่คุณสร้างแสดงผลขึ้นมาในหน้า chat ทันที!

สรุปหัวข้อส่งท้าย

นี่คือภาพรวมทั้งหมดของการพัฒนา Application บน ChatGPT ด้วย Apps SDK ครับ จะเห็นได้ว่าหัวใจสำคัญคือการทำความเข้าใจใน protocol กลางอย่าง MCP และการเรียนรู้ที่จะสร้าง UI Component ที่สามารถสื่อสารกลับมายัง ChatGPT ได้ผ่าน window.openai

แม้ว่าตอนนี้จะยังอยู่ในช่วงเริ่มต้นและจำกัดการพัฒนาสำหรับ ChatGPT Plus เท่านั้น แต่ก็นับเป็นก้าวที่น่าตื่นเต้นอย่างมากที่เปลี่ยนให้ ChatGPT เป็นเหมือน Platform ที่เราสามารถสร้าง App ที่มีหน้าตา Interactive และเชื่อมต่อกับบริการของเราได้โดยตรง ซึ่งแตกต่างจาก GPTs หรือ Plugin แบบเดิมๆ อย่างสิ้นเชิง และด้วยการประกาศว่าจะมีการเปิด Store ภายในปีนี้ ก็ยิ่งทำให้การเรียนรู้เรื่องนี้เป็นสิ่งที่น่าจับตามองมากยิ่งขึ้นไปอีกครับ

จะเป็นยังไงกันต่อ เราก็มารอติดตามกันต่อไปนะครับ 😁

forumComments (0)

lockLog in to join the conversation.

Log In

Loading comments…