From 6b672f27476248d98873ae697cfca3c239e0f236 Mon Sep 17 00:00:00 2001 From: rajdubey Date: Fri, 16 Oct 2026 12:31:07 +0530 Subject: [PATCH 1/4] Move the Docs MCP bundles here, check them weekly; fix stale iOS, Android and Angular pages The Docs MCP server's implementation bundles were hand-written files in the MCP repo, untouched since April: they pinned old kit majors and used removed components (the Flutter one installs flutter_chat_ui_kit and cometchat_chat_uikit ^5). They now live here, in mcp-bundles/, and the MCP index build serves them, so a merged change is live within ~10 min. - mcp-bundles/: the 11 bundles rewritten for the latest kits (React 7.2.4, React Native 5.5.2, Angular 5.2.2 (new), Flutter 6.2.0, Android 6.1.1, iOS 5.2.3, JS SDK 4.2.1, plus moderation, widget, presence, multi-tenant), each compiled or run against the latest release; README; removed-apis.json - .github/scripts/check-mcp-bundles.mjs + workflow: fails a bundle whose pinned package is behind its latest release, whose body and frontmatter versions differ, whose code uses a removed API, whose last_verified is over 90 days old, or whose docs link no longer resolves; weekly run opens an issue - .mintignore/.mintlifyignore: keep mcp-bundles/ off the site - iOS getting-started: 5.2.3 via SPM (Up to Next Major), CometChatSDK resolved by the kit, iOS 15.1+; CocoaPods tops out at 5.1.18 - iOS message-list: MessageListAlignment (.standard/.leftAligned), hideAvatar Bool? - Android: chatuikit-*-android 6.1.1, calls-sdk-android 5.0.4; Compose 1.8 BOM warning (6.1.1 crashes on Compose 1.7) - Angular message-list: MessageListAlignment/MessageBubbleAlignment are numeric enums Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Efu4EKfePzK3Z6QQVp2hm4 --- .github/scripts/check-mcp-bundles.mjs | 216 ++++++++++++++++++ .github/workflows/mcp-bundles.yml | 59 +++++ .mintignore | 3 +- .mintlifyignore | 2 + mcp-bundles/README.md | 63 +++++ mcp-bundles/android-uikit-quickstart.md | 195 ++++++++++++++++ mcp-bundles/angular-uikit-quickstart.md | 181 +++++++++++++++ mcp-bundles/flutter-uikit-quickstart.md | 195 ++++++++++++++++ mcp-bundles/ios-uikit-quickstart.md | 184 +++++++++++++++ mcp-bundles/js-sdk-messaging-basics.md | 128 +++++++++++ mcp-bundles/moderation-setup.md | 113 +++++++++ mcp-bundles/multi-tenant-chat.md | 160 +++++++++++++ mcp-bundles/presence-and-typing.md | 142 ++++++++++++ mcp-bundles/react-native-uikit-quickstart.md | 167 ++++++++++++++ mcp-bundles/react-uikit-quickstart.md | 191 ++++++++++++++++ mcp-bundles/removed-apis.json | 28 +++ mcp-bundles/widget-embed.md | 113 +++++++++ ui-kit/android/call-features.mdx | 8 +- ui-kit/android/calling-integration.mdx | 8 +- ui-kit/android/getting-started-jetpack.mdx | 12 +- ui-kit/android/getting-started-kotlin.mdx | 4 +- ui-kit/android/getting-started.mdx | 12 +- .../components/cometchat-message-list.mdx | 20 +- ui-kit/ios/getting-started.mdx | 67 +----- ui-kit/ios/message-list.mdx | 22 +- 25 files changed, 2200 insertions(+), 93 deletions(-) create mode 100644 .github/scripts/check-mcp-bundles.mjs create mode 100644 .github/workflows/mcp-bundles.yml create mode 100644 mcp-bundles/README.md create mode 100644 mcp-bundles/android-uikit-quickstart.md create mode 100644 mcp-bundles/angular-uikit-quickstart.md create mode 100644 mcp-bundles/flutter-uikit-quickstart.md create mode 100644 mcp-bundles/ios-uikit-quickstart.md create mode 100644 mcp-bundles/js-sdk-messaging-basics.md create mode 100644 mcp-bundles/moderation-setup.md create mode 100644 mcp-bundles/multi-tenant-chat.md create mode 100644 mcp-bundles/presence-and-typing.md create mode 100644 mcp-bundles/react-native-uikit-quickstart.md create mode 100644 mcp-bundles/react-uikit-quickstart.md create mode 100644 mcp-bundles/removed-apis.json create mode 100644 mcp-bundles/widget-embed.md diff --git a/.github/scripts/check-mcp-bundles.mjs b/.github/scripts/check-mcp-bundles.mjs new file mode 100644 index 000000000..b99cf2766 --- /dev/null +++ b/.github/scripts/check-mcp-bundles.mjs @@ -0,0 +1,216 @@ +#!/usr/bin/env node +/** + * check-mcp-bundles.mjs: keeps the Docs MCP implementation bundles (mcp-bundles/*.md) current. + * + * node .github/scripts/check-mcp-bundles.mjs # all checks (needs the network) + * node .github/scripts/check-mcp-bundles.mjs --offline # format, removed APIs, age only + * + * The Docs MCP server (mcp.cometchat.com) serves these files to AI coding agents from its + * search index, which follows this repo's main branch. They went stale once: pinned to old + * kit majors and using removed components, for months, because nothing checked them. A + * bundle fails here when: + * - its frontmatter is incomplete (title, framework, prerequisites, last_verified, packages) + * - a package it declares is behind the latest release in the same major + * - a version written in the body for a declared package differs from the declared one, + * or the body pins a CometChat package it does not declare + * - its code uses an API listed in mcp-bundles/removed-apis.json + * - last_verified is older than MAX_AGE_DAYS (re-verify on the latest kit, then bump it) + * - a cometchat.com/docs link in it does not resolve + * A newer MAJOR is a warning: moving a bundle to a new major is a rewrite, done on purpose. + */ +import { readdirSync, readFileSync } from "node:fs"; +import { join, dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); +const DIR = join(ROOT, "mcp-bundles"); +const OFFLINE = process.argv.includes("--offline"); +const MAX_AGE_DAYS = 90; +const MAX_BYTES = 60 * 1024; // the server skips a bundle bigger than this +const NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; +const REGISTRIES = new Set(["npm", "pub", "maven", "github"]); + +const problems = []; +const warnings = []; +const fail = (file, msg) => problems.push(`${file}: ${msg}`); + +// ── frontmatter ─────────────────────────────────────────────────────────────── +// A deliberately small YAML reader for the one shape bundles use: scalars, lists of +// strings, and lists of flat maps. Anything else is an error, so a typo cannot pass. +function unquote(v) { + const t = v.trim(); + if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) return t.slice(1, -1); + return t; +} +function parseFrontmatter(raw) { + const m = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/); + if (!m) throw new Error("no YAML frontmatter (--- … ---) at the top"); + const data = {}; + let key = null; + let item = null; + for (const line of m[1].split(/\r?\n/)) { + if (!line.trim() || line.trim().startsWith("#")) continue; + let r; + if ((r = line.match(/^([a-z_]+):\s*(.*)$/))) { + key = r[1]; + item = null; + const v = r[2].replace(/\s+#.*$/, ""); + data[key] = v === "" ? [] : v.trim() === "[]" ? [] : unquote(v); + } else if ((r = line.match(/^ {2}- ([a-z_]+):\s*(.+)$/)) && Array.isArray(data[key])) { + item = { [r[1]]: unquote(r[2].replace(/\s+#.*$/, "")) }; + data[key].push(item); + } else if ((r = line.match(/^ {2}- (.+)$/)) && Array.isArray(data[key])) { + item = null; + data[key].push(unquote(r[1])); + } else if ((r = line.match(/^ {4}([a-z_]+):\s*(.+)$/)) && item) { + item[r[1]] = unquote(r[2].replace(/\s+#.*$/, "")); + } else { + throw new Error(`frontmatter line not understood: ${JSON.stringify(line)}`); + } + } + return { data, body: m[2] }; +} + +// ── registries ──────────────────────────────────────────────────────────────── +const semver = (v) => String(v).replace(/^v/, "").split(/[.-]/).slice(0, 3).map((n) => parseInt(n, 10) || 0); +const cmp = (a, b) => { + const x = semver(a), y = semver(b); + for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return x[i] - y[i]; + return 0; +}; +const stable = (v) => /^v?\d+\.\d+\.\d+$/.test(v); + +async function get(url, json = true) { + const headers = { "user-agent": "cometchat-docs-mcp-bundles-check" }; + if (json) headers.accept = "application/json"; + if (url.startsWith("https://api.github.com/") && process.env.GITHUB_TOKEN) headers.authorization = `Bearer ${process.env.GITHUB_TOKEN}`; + const res = await fetch(url, { headers, signal: AbortSignal.timeout(20000) }); + if (!res.ok) throw new Error(`${url}: HTTP ${res.status}`); + return json ? res.json() : res.text(); +} +const cache = new Map(); +function published({ registry, name }) { + const key = `${registry}:${name}`; + if (!cache.has(key)) cache.set(key, (async () => { + if (registry === "npm") return Object.keys((await get(`https://registry.npmjs.org/${name}`)).versions); + if (registry === "pub") return (await get(`https://pub.dev/api/packages/${name}`)).versions.map((v) => v.version); + if (registry === "maven") { + const [g, a] = name.split(":"); + const xml = await get(`https://dl.cloudsmith.io/public/cometchat/cometchat/maven/${g.replace(/\./g, "/")}/${a}/maven-metadata.xml`, false); + return [...xml.matchAll(/([^<]+)<\/version>/g)].map((m) => m[1]); + } + return (await get(`https://api.github.com/repos/${name}/tags?per_page=100`)).map((t) => t.name); + })()); + return cache.get(key); +} + +// ── body checks ─────────────────────────────────────────────────────────────── +const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +// Every version written right after a package's name: `pkg@1.2.3`, `pkg: ^1.2.3`, +// `group:artifact:1.2.3`, `.package(url: ".../repo", from: "1.2.3")`. +function versionsInBody(body, pkg) { + const id = pkg.registry === "maven" ? pkg.name : pkg.registry === "github" ? `github.com/${pkg.name}` : pkg.name; + const re = new RegExp(`${esc(id)}(?![\\w-])(?:\\.git)?["']?(?:@|:\\s*["']?[\\^~]?|,\\s*(?:from|exact):\\s*["'])(\\d+\\.\\d+\\.\\d+)`, "g"); + return [...body.matchAll(re)].map((m) => m[1]); +} +// CometChat packages pinned in the body: they must be declared so their versions are checked. +const PINNED = [ + /(@cometchat\/[a-z0-9-]+)@\d+\.\d+\.\d+/g, + /^\s*(cometchat_[a-z_]+):\s*["']?[\^~]?\d+\.\d+\.\d+/gm, + /(com\.cometchat:[a-z0-9-]+):\d+\.\d+\.\d+/g, +]; + +async function checkLink(file, url) { + try { + const res = await fetch(url, { method: "GET", redirect: "follow", signal: AbortSignal.timeout(20000), headers: { "user-agent": "cometchat-docs-mcp-bundles-check" } }); + if (!res.ok) return fail(file, `link ${url} → HTTP ${res.status}`); + // The docs site answers a missing page by redirecting to its home page. + const home = (u) => u.split("#")[0].replace(/\/+$/, "") === "https://www.cometchat.com/docs"; + if (home(res.url) && !home(url)) fail(file, `link ${url} → page not found (redirects to the docs home page)`); + } catch (e) { + fail(file, `link ${url} → ${e.message}`); + } +} + +// ── main ────────────────────────────────────────────────────────────────────── +const removed = JSON.parse(readFileSync(join(DIR, "removed-apis.json"), "utf8")).removed; +const files = readdirSync(DIR).filter((f) => f.endsWith(".md") && f !== "README.md").sort(); +if (files.length === 0) fail("mcp-bundles/", "no bundles found"); +const today = Date.now(); +const jobs = []; + +for (const file of files) { + const name = file.replace(/\.md$/, ""); + const raw = readFileSync(join(DIR, file), "utf8"); + if (!NAME_RE.test(name)) fail(file, "file name must be lowercase kebab-case"); + if (Buffer.byteLength(raw) > MAX_BYTES) fail(file, `bigger than ${MAX_BYTES} bytes; the server would skip it`); + let fm, body; + try { + ({ data: fm, body } = parseFrontmatter(raw)); + } catch (e) { + fail(file, e.message); + continue; + } + for (const k of ["title", "framework", "last_verified"]) if (typeof fm[k] !== "string" || !fm[k]) fail(file, `frontmatter '${k}' missing`); + if (!Array.isArray(fm.prerequisites) || fm.prerequisites.some((p) => typeof p !== "string")) fail(file, "frontmatter 'prerequisites' must be a list of strings"); + if (!Array.isArray(fm.packages)) fail(file, "frontmatter 'packages' missing (use `packages: []` when the bundle installs nothing versioned)"); + const packages = Array.isArray(fm.packages) ? fm.packages : []; + + if (!/^\d{4}-\d{2}-\d{2}$/.test(fm.last_verified || "") || Number.isNaN(Date.parse(fm.last_verified))) { + fail(file, "last_verified must be a YYYY-MM-DD date"); + } else { + const age = Math.floor((today - Date.parse(fm.last_verified)) / 86400000); + if (age > MAX_AGE_DAYS) fail(file, `last_verified ${fm.last_verified} is ${age} days old (limit ${MAX_AGE_DAYS}): re-check it on the latest kit, then update the date`); + if (age < -1) fail(file, `last_verified ${fm.last_verified} is in the future`); + } + + // Code only: a pitfall may name a removed API to warn against it. + const code = [...body.matchAll(/```[^\n]*\n([\s\S]*?)```/g)].map((m) => m[1]).join("\n"); + for (const r of removed) { + if (r.frameworks && !r.frameworks.includes(fm.framework)) continue; + if (new RegExp(r.pattern).test(code)) fail(file, `code uses ${r.pattern.replace(/\\/g, "")}: ${r.reason}`); + } + + const declared = new Set(); + for (const p of packages) { + if (typeof p !== "object" || !REGISTRIES.has(p.registry) || !p.name || !/^\d+\.\d+\.\d+$/.test(p.version || "")) { + fail(file, `package entry needs registry (${[...REGISTRIES].join("|")}), name and an x.y.z version: ${JSON.stringify(p)}`); + continue; + } + declared.add(p.name); + for (const v of versionsInBody(body, p)) { + if (v !== p.version) fail(file, `body pins ${p.name} ${v} but frontmatter declares ${p.version}`); + } + if (!OFFLINE) jobs.push((async () => { + let all; + try { + all = (await published(p)).filter(stable).sort((a, b) => cmp(b, a)); + } catch (e) { + return fail(file, `could not read ${p.registry} ${p.name}: ${e.message}`); + } + if (all.length === 0) return fail(file, `${p.registry} ${p.name} has no stable releases`); + const sameMajor = all.find((v) => semver(v)[0] === semver(p.version)[0]); + if (!all.some((v) => cmp(v, p.version) === 0)) fail(file, `${p.name} ${p.version} is not a published version`); + else if (sameMajor && cmp(p.version, sameMajor) < 0) fail(file, `${p.name} ${p.version} is behind the latest ${sameMajor}: update the bundle and re-verify it`); + if (cmp(all[0], p.version) > 0 && semver(all[0])[0] > semver(p.version)[0]) warnings.push(`${file}: ${p.name} has a new major ${all[0]} (bundle is on ${p.version}); plan the migration`); + })()); + } + for (const re of PINNED) { + for (const m of body.matchAll(re)) if (!declared.has(m[1])) fail(file, `body pins ${m[1]} but 'packages' does not declare it`); + } + + if (!OFFLINE) { + const links = new Set([...raw.matchAll(/https:\/\/www\.cometchat\.com\/docs[^\s)>"'`\]]*/g)].map((m) => m[0].replace(/[.,;:]+$/, ""))); + for (const url of links) jobs.push(checkLink(file, url)); + } +} + +await Promise.all(jobs); +for (const w of warnings) console.log(`⚠️ ${w}`); +if (problems.length) { + console.error(`❌ ${problems.length} problem(s) in mcp-bundles/:`); + for (const p of problems) console.error(` - ${p}`); + console.error("\nSee mcp-bundles/README.md for how to update a bundle."); + process.exit(1); +} +console.log(`✅ ${files.length} MCP bundles current${OFFLINE ? " (offline: registries and links not checked)" : ""}`); diff --git a/.github/workflows/mcp-bundles.yml b/.github/workflows/mcp-bundles.yml new file mode 100644 index 000000000..98f43efdb --- /dev/null +++ b/.github/workflows/mcp-bundles.yml @@ -0,0 +1,59 @@ +name: MCP bundles + +# The Docs MCP server (mcp.cometchat.com) serves mcp-bundles/*.md to AI coding agents +# and picks up this repo's main branch within about 10 minutes. This keeps them current: +# on every PR that touches them, and weekly, because a kit release makes a bundle stale +# without any change here. + +on: + pull_request: + paths: + - "mcp-bundles/**" + - ".github/scripts/check-mcp-bundles.mjs" + - ".github/workflows/mcp-bundles.yml" + push: + branches: [main] + paths: + - "mcp-bundles/**" + schedule: + - cron: "0 5 * * 1" # Mondays 05:00 UTC + workflow_dispatch: + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + sparse-checkout: | + mcp-bundles + .github/scripts + - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 + with: + node-version: 22 + - name: Check bundles + id: check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: node .github/scripts/check-mcp-bundles.mjs 2>&1 | tee check.log; exit ${PIPESTATUS[0]} + # A scheduled run has no PR to fail on: open (or update) one issue instead. + - name: Report a stale bundle + if: failure() && github.event_name == 'schedule' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + title="MCP bundles are out of date" + body="$(printf 'The weekly check of \`mcp-bundles/\` failed. The Docs MCP serves these to AI coding agents, so fix them soon.\n\n\`\`\`\n%s\n\`\`\`\n\nRun: %s/%s/actions/runs/%s\nHow to update a bundle: mcp-bundles/README.md' "$(cat check.log)" "$GITHUB_SERVER_URL" "$GITHUB_REPOSITORY" "$GITHUB_RUN_ID")" + existing="$(gh issue list --repo "$GITHUB_REPOSITORY" --state open --search "\"$title\" in:title" --json number --jq '.[0].number')" + if [ -n "$existing" ]; then + gh issue comment "$existing" --repo "$GITHUB_REPOSITORY" --body "$body" + else + gh issue create --repo "$GITHUB_REPOSITORY" --title "$title" --body "$body" + fi diff --git a/.mintignore b/.mintignore index 9ddbca1cf..d892b37ba 100644 --- a/.mintignore +++ b/.mintignore @@ -5,4 +5,5 @@ /docs-templates /mintignore /doc-auditor -/docs-test-suite \ No newline at end of file +/docs-test-suite +/mcp-bundles diff --git a/.mintlifyignore b/.mintlifyignore index 67423c500..e079947aa 100644 --- a/.mintlifyignore +++ b/.mintlifyignore @@ -2,3 +2,5 @@ .claude/** chat-uikit-flutter chat-uikit-flutter/** +mcp-bundles +mcp-bundles/** diff --git a/mcp-bundles/README.md b/mcp-bundles/README.md new file mode 100644 index 000000000..591082918 --- /dev/null +++ b/mcp-bundles/README.md @@ -0,0 +1,63 @@ +# Docs MCP implementation bundles + +Each `.md` file here is an implementation bundle: a short, copyable recipe the +[CometChat Docs MCP server](https://www.cometchat.com/docs/mcp-server) returns from +`get_cometchat_implementation_bundle` and lists in `list_cometchat_bundles`. AI coding +agents paste this code into customer apps, so it must work on the **latest** kit. + +- **How they reach the server:** the server's search index is built from this repo's + `main` branch, and the build stores these files in it. A merged change is served + within about 10 minutes. The MCP server doesn't need a redeploy. +- **Not on the docs site:** `.mintignore` keeps this folder out of the Mintlify build. +- **Bundle names:** the file name is the bundle name (lowercase kebab-case). Renaming a + file breaks agents that ask for the old name, so add a new bundle rather than rename one. + +## Format + +```markdown +--- +title: React UI Kit Quickstart +framework: react +prerequisites: + - "Node.js 18+" + - "CometChat App ID, Auth Key, and Region from app.cometchat.com" +last_verified: "2026-10-16" +packages: + - registry: npm # npm | pub | maven | github + name: "@cometchat/chat-uikit-react" # maven: "group:artifact"; github: "owner/repo" (tags) + version: "7.2.4" +--- + +# React UI Kit Quickstart +… +``` + +`packages` lists every CometChat package the bundle installs. Use `packages: []` when +it installs nothing versioned. + +## The check + +`.github/scripts/check-mcp-bundles.mjs` runs on every PR that touches this folder and +every Monday. A scheduled failure opens an issue titled "MCP bundles are out of date". +It fails a bundle when: + +- a declared package is behind the latest release in its major version +- a version in the body differs from the declared one, or the body pins a CometChat + package that `packages` does not declare +- a code block uses something in [`removed-apis.json`](removed-apis.json) +- `last_verified` is more than 90 days old +- a `cometchat.com/docs` link does not resolve + +A new major version is a warning, not a failure, because moving a bundle to a new major +means rewriting it. + +Run it locally with `node .github/scripts/check-mcp-bundles.mjs`. Add `--offline` to skip +the registry and link checks. + +## Updating a bundle + +1. Install the latest kit in a scratch project and run the bundle's code on it. +2. Update the versions in `packages` and in the body. +3. Set `last_verified` to today. +4. When a kit release removes or renames an API, add it to `removed-apis.json`. The check + then also catches it in future edits. diff --git a/mcp-bundles/android-uikit-quickstart.md b/mcp-bundles/android-uikit-quickstart.md new file mode 100644 index 000000000..a8c583e5d --- /dev/null +++ b/mcp-bundles/android-uikit-quickstart.md @@ -0,0 +1,195 @@ +--- +title: Android UI Kit Quickstart (Jetpack Compose) +framework: android +prerequisites: + - "Android Studio with a Jetpack Compose app module (Kotlin)" + - "minSdk 28, compileSdk 36, AGP 8.9.1+, Gradle 8.11+, Kotlin 2.1+, JVM target 11" + - "CometChat App ID, Auth Key, and Region from app.cometchat.com" +last_verified: "2026-10-16" +packages: + - registry: maven + name: "com.cometchat:chatuikit-compose-android" + version: "6.1.1" + - registry: maven + name: "com.cometchat:chatuikit-kotlin-android" + version: "6.1.1" + - registry: maven + name: "com.cometchat:calls-sdk-android" + version: "5.0.4" +--- + +# Android UI Kit Quickstart (Jetpack Compose) + +Install the CometChat Android UI Kit v6, initialize it, log a user in, and show a conversation list that opens a chat screen (header, message list, composer) with threaded replies. + +Source: https://www.cometchat.com/docs/ui-kit/android/getting-started-jetpack + +v6 ships two artifacts. This recipe uses `chatuikit-compose-android`. For an XML Views app use `com.cometchat:chatuikit-kotlin-android:6.1.1` instead (https://www.cometchat.com/docs/ui-kit/android/getting-started-kotlin). Never install both. The old v5 artifact `com.cometchat:chat-uikit-android` does not have a 6.x release; remove it if present. + +## 1. Gradle + +```kotlin +// settings.gradle.kts → dependencyResolutionManagement { repositories { ... } } +maven("https://dl.cloudsmith.io/public/cometchat/cometchat/maven/") +``` + +```kotlin +// app/build.gradle.kts (Compose plugin already applied by the Android Studio Compose template) +import java.util.Properties // must be the first line of the file + +val localProps = Properties().apply { + rootProject.file("local.properties").takeIf { it.exists() }?.inputStream()?.use { load(it) } +} +android { + compileSdk = 36 + defaultConfig { + minSdk = 28 + listOf("COMETCHAT_APP_ID", "COMETCHAT_REGION", "COMETCHAT_AUTH_KEY").forEach { + buildConfigField("String", it, "\"${localProps.getProperty(it, "")}\"") + } + } + buildFeatures { compose = true; buildConfig = true } + compileOptions { sourceCompatibility = JavaVersion.VERSION_11; targetCompatibility = JavaVersion.VERSION_11 } +} +kotlin { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11) } } + +dependencies { + implementation("com.cometchat:chatuikit-compose-android:6.1.1") + implementation("androidx.navigation:navigation-compose:2.8.0") + // Compose 1.8+ is REQUIRED at runtime (the kit's POM only asks for 1.7); BOM 2025.06.00 or newer. + implementation(platform("androidx.compose:compose-bom:2025.06.00")) + implementation("androidx.compose.material3:material3") + // Voice/video only: implementation("com.cometchat:calls-sdk-android:5.0.4") +} +``` + +`gradle.properties` needs `android.useAndroidX=true`. Put `COMETCHAT_APP_ID=...`, `COMETCHAT_REGION=us` and `COMETCHAT_AUTH_KEY=...` in `local.properties` (git-ignored), never in source. No manifest edits: the kit declares the permissions it uses. + +## 2. Initialize and log in (once per process) + +```kotlin +// CometChatGate.kt +import android.content.Context +import androidx.compose.runtime.* +import com.cometchat.chat.core.CometChat +import com.cometchat.chat.exceptions.CometChatException +import com.cometchat.chat.models.User +import com.cometchat.uikit.core.CometChatUIKit +import com.cometchat.uikit.core.UIKitSettings + +object CometChatGate { + var ready by mutableStateOf(false); private set + var error by mutableStateOf(null); private set + private var started = false + + fun start(context: Context, uid: String) { // main thread; safe to call again + if (started) return else started = true + val settings = UIKitSettings.UIKitSettingsBuilder() + .setAppId(BuildConfig.COMETCHAT_APP_ID) + .setRegion(BuildConfig.COMETCHAT_REGION) + .setAuthKey(BuildConfig.COMETCHAT_AUTH_KEY) // dev only + .subscribePresenceForAllUsers() + .build() + CometChatUIKit.init(context.applicationContext, settings, object : CometChat.CallbackListener() { + override fun onSuccess(result: String) { // login only after init succeeds + if (CometChatUIKit.getLoggedInUser() != null) { ready = true; return } + CometChatUIKit.login(uid, object : CometChat.CallbackListener() { + override fun onSuccess(user: User) { ready = true } + override fun onError(e: CometChatException) { error = e.message; started = false } + }) + } + override fun onError(e: CometChatException) { error = e.message; started = false } + }) + } +} +``` + +## 3. Render: conversations → messages → thread + +```kotlin +// MainActivity.kt +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + CometChatGate.start(this, "cometchat-uid-1") // a UID that exists in your app + setContent { + // The Compose kit does not follow the system dark setting unless you pass a scheme. + CometChatTheme(colorScheme = if (isSystemInDarkTheme()) darkColorScheme() else lightColorScheme()) { + when { + CometChatGate.error != null -> Text("CometChat error: ${CometChatGate.error}") + !CometChatGate.ready -> Box(Modifier.fillMaxSize(), Alignment.Center) { CircularProgressIndicator() } + else -> ChatApp() + } + } + } + } +} + +@Composable +fun ChatApp() { + val nav = rememberNavController() + var user by rememberSaveable { mutableStateOf(null) } + var group by rememberSaveable { mutableStateOf(null) } + var parent by remember { mutableStateOf(null) } + + // One NavHost destination per screen: each gets its own ViewModel store, which the thread list needs. + NavHost(nav, startDestination = "conversations") { + composable("conversations") { + CometChatConversations( + modifier = Modifier.fillMaxSize().systemBarsPadding(), hideSearchBox = true, + onItemClick = { c -> + user = c.conversationWith as? User; group = c.conversationWith as? Group + nav.navigate("messages") + }, + ) + } + composable("messages") { + ChatColumn { + CometChatMessageHeader(user = user, group = group, onBackPress = { nav.popBackStack() }) + CometChatMessageList( + modifier = Modifier.fillMaxWidth().weight(1f), + user = user, group = group, + onThreadRepliesClick = { msg -> parent = msg; nav.navigate("thread") }, + ) + CometChatMessageComposer(user = user, group = group) + } + } + composable("thread") { + val p = parent ?: return@composable LaunchedEffect(Unit) { nav.popBackStack() } + ChatColumn { + CometChatThreadHeader(parentMessage = p) // display only: system back pops + CometChatMessageList(Modifier.fillMaxWidth().weight(1f), user = user, group = group, parentMessageId = p.id) + CometChatMessageComposer(user = user, group = group, parentMessageId = p.id) + } + } + } +} + +@Composable +fun ChatColumn(content: @Composable ColumnScope.() -> Unit) = + Column(Modifier.fillMaxSize().systemBarsPadding().imePadding(), content = content) +``` + +Imports: Compose UI from `androidx.compose.*`, `androidx.navigation.compose.*`; models from `com.cometchat.chat.models.*` (`User`, `Group`, `BaseMessage`); theme from `com.cometchat.uikit.compose.theme.*` (`CometChatTheme`, `lightColorScheme`, `darkColorScheme`); components from `com.cometchat.uikit.compose.presentation..ui.*` (`conversations`, `messageheader`, `messagelist`, `messagecomposer`, `threadheader`). + +## Common pitfalls + +- **v5 code or artifacts.** `CometChatConversationsWithMessages` and the other v5 combined screens do not exist in v6, and v5 imports (`com.cometchat.chatuikit.*`) do not compile; v6 is `com.cometchat.uikit.*`. Compose the screen from conversations + header + list + composer as above. +- **Compose older than 1.8 → crash when the list opens.** The 6.1.1 POM declares compose-bom 2024.09.00 (Compose 1.7), but the kit calls a Compose 1.8 `LazyColumn` overload. On 1.7 the app builds, then crashes with `NoSuchMethodError: ... LazyColumn(...)` as soon as `CometChatConversations` renders. Pin a compose BOM of 2025.06.00 or newer as above. +- **Build floors too low.** The kit pulls `androidx.core` 1.18, which needs minSdk 28, compileSdk 36, AGP 8.9.1+, Gradle 8.11+ and Kotlin 2.1+. A lower value fails `checkDebugAarMetadata`. +- **Missing CometChat Maven repository** → `Could not find com.cometchat:chatuikit-...`. +- **Login before init finishes, or UI before login.** Login silently fails, or the screens render empty. Gate on init → login → render as above. +- **Auth Key in production.** It is dev only. Mint an auth token on your server and call `CometChatUIKit.loginWithAuthToken(token, listener)`. +- **Inventing a UID.** `login()` needs an existing user. New apps include `cometchat-uid-1` to `cometchat-uid-5`. +- **Thread screen without `user`/`group`.** `parentMessageId` alone means replies never send. Pass the same target as the main chat. +- **Thread in the same composition as the main list** (an if/else instead of NavHost routes): both lists share one ViewModel and the thread shows the whole chat. +- **No `weight(1f)` on the list or no `imePadding()`:** the composer is pushed off screen or hidden by the keyboard. +- **Calling turned on without the calls SDK.** `setEnableCalling(true)` needs `com.cometchat:calls-sdk-android`; without it 6.1.1 logs a warning and calling stays off. + +## What to read next + +- Components: https://www.cometchat.com/docs/ui-kit/android/components-overview +- Theme: https://www.cometchat.com/docs/ui-kit/android/theme-introduction +- Methods: https://www.cometchat.com/docs/ui-kit/android/methods +- Calling: https://www.cometchat.com/docs/ui-kit/android/calling-integration diff --git a/mcp-bundles/angular-uikit-quickstart.md b/mcp-bundles/angular-uikit-quickstart.md new file mode 100644 index 000000000..2ff51fec7 --- /dev/null +++ b/mcp-bundles/angular-uikit-quickstart.md @@ -0,0 +1,181 @@ +--- +title: Angular UI Kit Quickstart +framework: angular +prerequisites: + - "Node.js 18+" + - "Angular 17 to 21 project (Angular 22 is outside the kit's peer range and fails to install)" + - "CometChat App ID, Region and Auth Key from app.cometchat.com (Auth Key is for development only)" +last_verified: "2026-10-16" +packages: + - registry: npm + name: "@cometchat/chat-uikit-angular" + version: "5.2.2" + - registry: npm + name: "@cometchat/chat-sdk-javascript" + version: "4.2.1" + - registry: npm + name: "@cometchat/cards-angular" + version: "1.0.0" + - registry: npm + name: "@cometchat/calls-sdk-javascript" + version: "5.0.8" +--- + +# Angular UI Kit Quickstart + +Install the CometChat Angular UI Kit v5, initialize and log in before the app boots, and render a conversation list next to a message pane. All kit components are standalone. + +Source: https://www.cometchat.com/docs/ui-kit/angular/integration + +## 1. Install + +```bash +npx -y @angular/cli@21 new my-app --style css --ssr false # skip if you have an Angular 17-21 app +npm install @cometchat/chat-uikit-angular@5.2.2 @cometchat/chat-sdk-javascript@4.2.1 \ + @cometchat/cards-angular@1.0.0 dompurify@^3 +# Optional voice/video calling +npm install @cometchat/calls-sdk-javascript@5.0.8 +``` + +The kit needs a browser, so create the app without SSR. + +## 2. Configure + +In `angular.json`, under `projects..architect.build`: + +```json +"options": { + "styles": [ + "node_modules/@cometchat/chat-uikit-angular/styles/css-variables.css", + "src/styles.css" + ], + "assets": [ + { "glob": "**/*", "input": "public" }, + { "glob": "**/*", "input": "node_modules/@cometchat/chat-uikit-angular/src/lib/assets", "output": "assets" } + ], + "allowedCommonJsDependencies": ["@cometchat/chat-sdk-javascript"] +}, +"configurations": { + "production": { + "budgets": [{ "type": "initial", "maximumWarning": "5mb", "maximumError": "6mb" }] + } +} +``` + +The kit stylesheet goes **first**, or its tokens override your brand colors. The `assets` entry ships the kit's icons. The kit plus SDK is about 4 MB, so the default 1 MB budget fails `ng build` (`ng serve` still works). + +Credentials go in `src/environments/environment.ts` (Angular has no `.env`): + +```ts +// src/environments/environment.ts +export const environment = { + production: false, + cometchat: { appId: "YOUR_APP_ID", region: "YOUR_REGION", authKey: "YOUR_AUTH_KEY" }, +}; +``` + +## 3. Initialize and 4. Log in + +`init` must resolve before `login`, and `login` before any kit component renders, so do both in `main.ts` before `bootstrapApplication`. Angular's `login` takes a UID string; in production call `loginWithAuthToken(token)` with a token your server mints. + +```ts +// src/main.ts +import { bootstrapApplication } from "@angular/platform-browser"; +import { CometChatUIKit } from "@cometchat/chat-uikit-angular"; +import { environment } from "./environments/environment"; +import { appConfig } from "./app/app.config"; +import { App } from "./app/app"; // Angular 17-19: AppComponent from "./app/app.component" + +const { appId, region, authKey } = environment.cometchat; +(async () => { + try { + await CometChatUIKit.initFromSettings({ + appId, region, + credentials: { authKey }, // development only + chatSDK: { presenceSubscription: { type: "ALL_USERS" } }, + // uiKit: { callsSDK: {} }, // enables calling + }); + await CometChatUIKit.login("cometchat-uid-1"); // a UID that exists in your app + } catch (e) { + console.error("CometChat init/login failed", e); + } finally { + bootstrapApplication(App, appConfig).catch(console.error); // boot even if chat fails + } +})(); +``` + +`initFromSettings` records the integration as agent-built; the docs' `UIKitSettingsBuilder` + `CometChatUIKit.init()` is the hand-written equivalent. + +## 5. Render + +```ts +// src/app/chat/chat.component.ts +import { Component, inject } from "@angular/core"; +import { CometChat } from "@cometchat/chat-sdk-javascript"; +import { + CometChatConversationsComponent, CometChatMessageComposerComponent, CometChatMessageHeaderComponent, + CometChatMessageListComponent, CometChatUIKitConstants, ThemeService, +} from "@cometchat/chat-uikit-angular"; + +@Component({ + selector: "app-chat", + standalone: true, + imports: [ // every kit component used in the template, or it renders nothing + CometChatConversationsComponent, CometChatMessageHeaderComponent, + CometChatMessageListComponent, CometChatMessageComposerComponent, + ], + template: ` +
+ + @if (user || group) { +
+ + + +
+ } +
`, + styles: [` + .cc-shell { display: flex; height: 100dvh; overflow: hidden; } + .cc-side, .cc-main { display: flex; flex-direction: column; min-height: 0; } + .cc-side { width: 320px; } .cc-main { flex: 1; } + .cc-fill { flex: 1; min-height: 0; overflow: hidden; } + `], +}) +export class ChatComponent { + user: CometChat.User | undefined; // `| undefined`, not `| null`: strict templates reject null + group: CometChat.Group | undefined; + constructor() { inject(ThemeService).initFromPreference(); } // follow OS light/dark + + open(c: CometChat.Conversation) { + const isGroup = c.getConversationType() === CometChatUIKitConstants.MessageReceiverType.group; + this.user = isGroup ? undefined : (c.getConversationWith() as CometChat.User); + this.group = isGroup ? (c.getConversationWith() as CometChat.Group) : undefined; + } +} +``` + +Add `html, body { height: 100%; margin: 0; }` to `src/styles.css`, and render `` from your root component (add `ChatComponent` to its `imports`). Threads are hidden here; to add them, wire `(threadRepliesClick)` to a panel with ``. + +## Common pitfalls + +- **Component not in `imports: []`.** The tag renders nothing, with no error. Import the class (`CometChatConversationsComponent`), use the selector (``). +- **Kit stylesheet after yours, or `@import`ed from CSS/SCSS.** Use the `angular.json` path above, first in the list; the package exports do not expose an `@import` path. +- **Missing icon `assets` entry.** Icons are blank across the kit and nothing errors. +- **Unsized ancestors.** Each kit list needs `flex: 1; min-height: 0` inside a sized parent, or it collapses to zero height. +- **One variable for user and group.** `(itemClick)` emits 1:1 and group conversations; bind both `[user]` and `[group]`. +- **Concurrent `login()` calls** throw "Please wait until the previous login request ends". Log in once, before bootstrap. +- **No repaint after an SDK callback.** Angular 21 is zoneless by default; write to a signal or call `markForCheck()`. +- **Calling installed but no call buttons.** On the `initFromSettings` path, pass `uiKit: { callsSDK: {} }`. +- **Angular 22** fails `npm install` with `ERESOLVE` (peer range `>=17.0.0 <22.0.0`). +- **Auth Key in production.** Use `loginWithAuthToken` with a server-issued token. + +## What to read next + +- Components: https://www.cometchat.com/docs/ui-kit/angular/components/components-overview +- Theming: https://www.cometchat.com/docs/ui-kit/angular/customization/theming +- Calling: https://www.cometchat.com/docs/ui-kit/angular/call-features +- Methods reference: https://www.cometchat.com/docs/ui-kit/angular/methods diff --git a/mcp-bundles/flutter-uikit-quickstart.md b/mcp-bundles/flutter-uikit-quickstart.md new file mode 100644 index 000000000..913291490 --- /dev/null +++ b/mcp-bundles/flutter-uikit-quickstart.md @@ -0,0 +1,195 @@ +--- +title: Flutter UI Kit Quickstart +framework: flutter +prerequisites: + - "Flutter 3.38.9+ (Dart 3.10.8+)" + - "Android minSdk 26, iOS 15.1" + - "CometChat App ID, Auth Key, and Region from app.cometchat.com" +last_verified: "2026-10-16" +packages: + - registry: pub + name: "cometchat_chat_uikit" + version: "6.2.0" +--- + +# Flutter UI Kit Quickstart + +Install the CometChat Flutter UI Kit v6, initialize it, log a user in, and show a conversation list that opens a chat screen (header, message list, composer) with threaded replies. + +Source: https://www.cometchat.com/docs/ui-kit/flutter/getting-started + +## 1. Add the dependency + +```yaml +# pubspec.yaml → dependencies: (then run: flutter pub get) + cometchat_chat_uikit: ^6.2.0 +``` + +That is the only CometChat package you need. `cometchat_sdk` and `cometchat_calls_sdk` come in transitively, and the SDK types (`User`, `Group`, `BaseMessage`) are re-exported by the kit import. Do not add `flutter_chat_ui_kit` or `cometchat_calls_uikit`: they are older packages that do not work with v6. + +Platform floors (set by `cometchat_calls_sdk`, which the kit always pulls in, even without calling): Android `minSdk = 26` in `android/app/build.gradle.kts`; iOS `platform :ios, '15.1'` in `ios/Podfile`, then `pod install`. + +## 2. Initialize, log in, render + +```dart +// lib/main.dart +import 'package:cometchat_chat_uikit/cometchat_chat_uikit.dart'; +import 'package:flutter/material.dart'; + +const appId = String.fromEnvironment('COMETCHAT_APP_ID'); +const region = String.fromEnvironment('COMETCHAT_REGION'); +const authKey = String.fromEnvironment('COMETCHAT_AUTH_KEY'); // dev only +const uid = String.fromEnvironment('COMETCHAT_UID', defaultValue: 'cometchat-uid-1'); + +void main() => runApp(const MyApp()); + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) => MaterialApp( + localizationsDelegates: Translations.localizationsDelegates, + supportedLocales: Translations.supportedLocales, + home: const ChatGate(), + ); +} + +class ChatGate extends StatefulWidget { + const ChatGate({super.key}); + @override + State createState() => _ChatGateState(); +} + +class _ChatGateState extends State { + bool _ready = false; + String? _error; + + @override + void initState() { + super.initState(); + final settings = (UIKitSettingsBuilder() + ..subscriptionType = CometChatSubscriptionType.allUsers + ..autoEstablishSocketConnection = true + ..appId = appId + ..region = region + ..authKey = authKey) + .build(); + + // init must succeed before login; no CometChat widget renders before login succeeds. + CometChatUIKit.init( + uiKitSettings: settings, + onSuccess: (_) => CometChatUIKit.login( + uid, + onSuccess: (_) => mounted ? setState(() => _ready = true) : null, + onError: (e) => mounted ? setState(() => _error = e.message) : null, + ), + onError: (e) => mounted ? setState(() => _error = e.message) : null, + ); + } + + @override + Widget build(BuildContext context) { + if (_error != null) return Scaffold(body: Center(child: Text('CometChat error: $_error'))); + if (!_ready) return const Scaffold(body: Center(child: CircularProgressIndicator())); + return Scaffold( + body: SafeArea( + child: CometChatConversations( + hideSearch: true, + onItemTap: (conversation) { + final entity = conversation.conversationWith; + Navigator.push(context, MaterialPageRoute( + builder: (_) => MessageScreen( + user: entity is User ? entity : null, + group: entity is Group ? entity : null, + ), + )); + }, + ), + ), + ); + } +} + +class MessageScreen extends StatelessWidget { + const MessageScreen({super.key, this.user, this.group}); + final User? user; + final Group? group; + + @override + Widget build(BuildContext context) => Scaffold( // keyboard-safe by default + body: SafeArea( + child: Column(children: [ + CometChatMessageHeader(user: user, group: group, onBack: () => Navigator.pop(context)), + Expanded( + child: CometChatMessageList( + user: user, + group: group, + onThreadRepliesClick: (BaseMessage message, BuildContext context, + {CometChatMessageTemplate? template}) { + Navigator.push(context, MaterialPageRoute( + builder: (_) => ThreadScreen(parentMessage: message, user: user, group: group), + )); + }, + ), + ), + CometChatMessageComposer(user: user, group: group), + ]), + ), + ); +} + +class ThreadScreen extends StatelessWidget { + const ThreadScreen({super.key, required this.parentMessage, this.user, this.group}); + final BaseMessage parentMessage; + final User? user; + final Group? group; + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar(title: const Text('Thread')), // the thread header has no back button + body: SafeArea( + child: Column(children: [ + CometChatThreadedHeader( + parentMessage: parentMessage, + loggedInUser: CometChatUIKit.loggedInUser!, + ), + Expanded( + child: CometChatMessageList( + user: user, group: group, parentMessageId: parentMessage.id), + ), + CometChatMessageComposer( + user: user, group: group, parentMessageId: parentMessage.id), + ]), + ), + ); +} +``` + +Run with credentials passed at build time (never commit them): + +```bash +flutter run --dart-define=COMETCHAT_APP_ID=your_app_id \ + --dart-define=COMETCHAT_REGION=us \ + --dart-define=COMETCHAT_AUTH_KEY=your_auth_key +``` + +## Common pitfalls + +- **v5 widgets.** `CometChatConversationsWithMessages`, `CometChatUsersWithMessages`, `CometChatMessages` and `CometChatUI` were removed in v6. Build the screens from `CometChatConversations` + `CometChatMessageHeader` + `CometChatMessageList` + `CometChatMessageComposer` as above. Do not mix a v5 `cometchat_chat_uikit` with v6 code. +- **Platform floor too low.** Android `minSdk` below 26 fails the manifest merge; iOS below 15.1 fails `pod install`. Older docs that say 24 / 13.0 are wrong for 6.2.0. +- **Rendering before login.** Any `CometChat*` widget built before `login` succeeds shows an empty or broken screen. An auth error is usually the wrong Region. +- **Auth Key in production.** It is dev only. Mint an auth token on your server and call `CometChatUIKit.loginWithAuthToken(token, onSuccess: ..., onError: ...)`. +- **Inventing a UID.** `login()` needs an existing user. New apps include `cometchat-uid-1` to `cometchat-uid-5`. +- **Thread widget name and params.** It is `CometChatThreadedHeader(parentMessage:, loggedInUser:)`, not `CometChatThreadHeader`, and it has no `onBack`. +- **`onThreadRepliesClick` with one argument.** The callback takes `(BaseMessage, BuildContext, {CometChatMessageTemplate? template})`; a one-argument closure does not compile. +- **Thread list/composer without `user`/`group`.** `parentMessageId` alone means replies never send. +- **Message list not in `Expanded`.** It needs a bounded height, or you get an unbounded-height error or a collapsed list. +- **Localization class.** Flutter v6 uses `Translations` (as in `MaterialApp` above); there is no `CometChatLocalize` in 6.2.0. +- **Calls.** Calling is off by default. To enable it set `..enableCalls = true` on `UIKitSettingsBuilder`, set `navigatorKey: CallNavigationContext.navigatorKey` on `MaterialApp`, import `package:cometchat_chat_uikit/cometchat_calls_uikit.dart` for call widgets, and add camera/microphone permissions. Do not mount `CometChatIncomingCall` yourself; the kit shows it. + +## What to read next + +- Components: https://www.cometchat.com/docs/ui-kit/flutter/components-overview +- Theme: https://www.cometchat.com/docs/ui-kit/flutter/theme-introduction +- Methods: https://www.cometchat.com/docs/ui-kit/flutter/methods +- Calling: https://www.cometchat.com/docs/ui-kit/flutter/call-features diff --git a/mcp-bundles/ios-uikit-quickstart.md b/mcp-bundles/ios-uikit-quickstart.md new file mode 100644 index 000000000..00b1abbb0 --- /dev/null +++ b/mcp-bundles/ios-uikit-quickstart.md @@ -0,0 +1,184 @@ +--- +title: iOS UI Kit Quickstart (SwiftUI) +framework: ios +prerequisites: + - "Xcode 16+" + - "iOS 15.1+ deployment target" + - "Swift Package Manager" + - "CometChat App ID, Auth Key, and Region from app.cometchat.com" +last_verified: "2026-10-16" +packages: + - registry: github + name: "cometchat/cometchat-uikit-ios" + version: "5.2.3" + - registry: github + name: "cometchat/chat-sdk-ios" + version: "4.1.10" + - registry: github + name: "cometchat/calls-sdk-ios" + version: "5.0.4" +--- + +# iOS UI Kit Quickstart (SwiftUI) + +Install the CometChat iOS UI Kit with SPM, initialize and log in at launch, then show a conversation list that opens a chat screen. + +Source: https://www.cometchat.com/docs/ui-kit/ios/getting-started + +## 1. Install via Swift Package Manager + +In Xcode: File → Add Package Dependencies → `https://github.com/cometchat/cometchat-uikit-ios`. Dependency Rule: **Up to Next Major Version** from `5.2.3`. Add the `CometChatUIKitSwift` product to your app target. + +- The kit's package pulls in `CometChatSDK` (from `4.1.10`). Do not add it yourself. +- Only for voice/video calls, also add `https://github.com/cometchat/calls-sdk-ios` (Up to Next Major from `5.0.4`). +- Use SPM. The CocoaPods pod is no longer updated and does not have this release. + +## 2. Configure your Info.plist + +Add a usage description for each media feature you use, or iOS terminates the app when it is used: `NSCameraUsageDescription`, `NSMicrophoneUsageDescription`, `NSPhotoLibraryUsageDescription`. Write a real sentence for each; App Review rejects placeholders. + +## 3. Initialize and log in at launch + +Run `init → login` once, from an `AppDelegate`, and render chat only after login succeeds. + +```swift +// MyApp.swift +import SwiftUI +import CometChatUIKitSwift +import CometChatSDK + +final class ChatSession: ObservableObject { + static let shared = ChatSession() + @Published var ready = false +} + +final class AppDelegate: NSObject, UIApplicationDelegate { + func application(_ app: UIApplication, + didFinishLaunchingWithOptions opts: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + let settings = UIKitSettings() + .set(appID: "YOUR_APP_ID") + .set(region: "YOUR_REGION") // "us", "eu" or "in" + .set(authKey: "YOUR_AUTH_KEY") // development only + .subscribePresenceForAllUsers() + .build() + CometChatUIKit.init(uiKitSettings: settings) { result in + guard case .success = result else { print("init failed: \(result)"); return } // check App ID / Region + if CometChatUIKit.getLoggedInUser() != nil { + DispatchQueue.main.async { ChatSession.shared.ready = true }; return + } + CometChatUIKit.login(uid: "cometchat-uid-1") { loginResult in // the user must already exist + switch loginResult { + case .success: DispatchQueue.main.async { ChatSession.shared.ready = true } + case .onError(let e): print("login failed: \(e.errorDescription)") + @unknown default: break + } + } + } + return true + } +} + +@main +struct MyApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate + @ObservedObject private var session = ChatSession.shared + var body: some Scene { + WindowGroup { + if session.ready { ConversationsView().ignoresSafeArea() } else { ProgressView() } + } + } +} +``` + +## 4. Show conversations and a chat screen + +v5 has no combined "with messages" component. Push your own screen built from the message header, message list and composer. + +```swift +// ConversationsView.swift +import SwiftUI +import CometChatUIKitSwift +import CometChatSDK + +struct ConversationsView: UIViewControllerRepresentable { + func makeCoordinator() -> NavBarCoordinator { NavBarCoordinator() } + func makeUIViewController(context: Context) -> UINavigationController { + let conversations = CometChatConversations() + let nav = UINavigationController(rootViewController: conversations) + nav.delegate = context.coordinator // weak; the coordinator keeps it alive + conversations.set(onItemClick: { [weak nav] conversation, _ in + let chat = MessagesVC() + chat.user = conversation.conversationWith as? CometChatSDK.User + chat.group = conversation.conversationWith as? CometChatSDK.Group + nav?.pushViewController(chat, animated: true) + }) + return nav + } + func updateUIViewController(_ vc: UINavigationController, context: Context) {} +} + +// The kit header has its own back button, so hide the host bar on the chat screen only. +final class NavBarCoordinator: NSObject, UINavigationControllerDelegate { + func navigationController(_ nav: UINavigationController, willShow vc: UIViewController, animated: Bool) { + nav.setNavigationBarHidden(vc is MessagesVC, animated: animated) + } +} + +final class MessagesVC: UIViewController { + var user: CometChatSDK.User? + var group: CometChatSDK.Group? + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .systemBackground + let header = CometChatMessageHeader(), list = CometChatMessageList() + let composer = CometChatCompactMessageComposer() + if let user { header.set(user: user); list.set(user: user); composer.set(user: user) } + else if let group { header.set(group: group); list.set(group: group); composer.set(group: group) } + header.set(controller: self); list.set(controller: self); composer.set(controller: self) + + for v in [header, list, composer] as [UIView] { + v.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(v) + v.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + v.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + } + NSLayoutConstraint.activate([ + header.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + header.heightAnchor.constraint(equalToConstant: 50), + list.topAnchor.constraint(equalTo: header.bottomAnchor), + list.bottomAnchor.constraint(equalTo: composer.topAnchor), + composer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + ]) + } +} +``` + +## 5. Production auth + +The Auth Key can log in as any user. Before shipping, create an auth token on your server and log in with it: + +```swift +CometChatUIKit.login(authToken: serverIssuedToken) { result in /* .success(User) / .onError(error) */ } +``` + +REST: https://www.cometchat.com/docs/rest-api/auth-tokens/create + +## Common pitfalls + +- **Kit older than 5.2.3** — earlier 5.2 releases crash at launch when the Calls SDK is absent. An old exact pin on `CometChatSDK` should be loosened to Up to Next Major from 4.1.10 or removed. +- **`login` before `init` succeeds, or rendering before `login` succeeds** — the screen stays empty with no error. +- **Removed v4 components** — `CometChatConversationsWithMessages`, `CometChatMessages`, `CometChatUsersWithMessages` and `CometChatGroupsWithMessages` do not exist in v5. +- **Missing `set(controller:)`** — header, list and composer use it to present their own screens, such as the long-press message menu. +- **Composer pinned to `keyboardLayoutGuide`** — the composer handles the keyboard itself; pin it to the safe area or the list collapses when the keyboard opens. +- **`CometChatConversations` is a `UIViewController`** (push or wrap it); header, list and composer are `UIView`s. +- **`'Group' is ambiguous`** in files that import SwiftUI — write `CometChatSDK.Group` / `CometChatSDK.User`. +- **Alignment names** — `MessageListAlignment` with `.standard` / `.leftAligned` (`list.set(messageAlignment: .leftAligned)`). `MessageAlignment` and `.left` do not exist. +- **Auth Key in a TestFlight or App Store build** — switch to auth tokens first. + +## What to read next + +- Components: https://www.cometchat.com/docs/ui-kit/ios/components-overview +- Conversation + chat guide: https://www.cometchat.com/docs/ui-kit/ios/ios-conversation +- Theme: https://www.cometchat.com/docs/ui-kit/ios/theme-introduction +- Methods: https://www.cometchat.com/docs/ui-kit/ios/methods diff --git a/mcp-bundles/js-sdk-messaging-basics.md b/mcp-bundles/js-sdk-messaging-basics.md new file mode 100644 index 000000000..68d9f06cf --- /dev/null +++ b/mcp-bundles/js-sdk-messaging-basics.md @@ -0,0 +1,128 @@ +--- +title: JavaScript SDK — Messaging Basics +framework: javascript +prerequisites: + - "A modern browser app (the SDK is browser-first; it needs window and WebSocket)" + - "A bundler (Vite, webpack) for npm imports, or the CDN script" + - "CometChat App ID, Auth Key, and Region from app.cometchat.com" +last_verified: "2026-10-16" +packages: + - registry: npm + name: "@cometchat/chat-sdk-javascript" + version: "4.2.1" +--- + +# JavaScript SDK — Messaging Basics + +Install the JavaScript Chat SDK v4, initialize, log a user in, and send and receive text and media messages without the UI Kit. + +Source: https://www.cometchat.com/docs/sdk/javascript/overview + +## 1. Install + +```bash +npm install @cometchat/chat-sdk-javascript@4.2.1 +``` + +## 2. Initialize and log in + +Call `init()` once at startup and wait for it before any other SDK call. `login()` needs a user that already exists (new apps include `cometchat-uid-1` to `cometchat-uid-5`). + +```ts +import { CometChat } from "@cometchat/chat-sdk-javascript"; + +const APP_ID = import.meta.env.VITE_COMETCHAT_APP_ID; +const REGION = import.meta.env.VITE_COMETCHAT_REGION; // "us" | "eu" | "in" +const AUTH_KEY = import.meta.env.VITE_COMETCHAT_AUTH_KEY; // dev only + +const settings = new CometChat.AppSettingsBuilder() + .setRegion(REGION) + .subscribePresenceForAllUsers() + .autoEstablishSocketConnection(true) + .build(); + +await CometChat.init(APP_ID, settings); + +// Reuse a stored session instead of logging in again. +const me = (await CometChat.getLoggedinUser()) ?? (await CometChat.login("cometchat-uid-1", AUTH_KEY)); +``` + +In production, mint an Auth Token on your server and call `CometChat.login(authToken)` (one argument). Never ship the Auth Key. + +## 3. Send a text message + +```ts +const text = new CometChat.TextMessage( + "cometchat-uid-2", // receiver UID (or GUID for a group) + "Hello from the SDK!", + CometChat.RECEIVER_TYPE.USER, // CometChat.RECEIVER_TYPE.GROUP for groups +); +const sent = await CometChat.sendMessage(text); +``` + +## 4. Send a media message (image) + +```ts +const file = (document.getElementById("upload") as HTMLInputElement).files![0]; +const media = new CometChat.MediaMessage( + "cometchat-uid-2", + file, + CometChat.MESSAGE_TYPE.IMAGE, + CometChat.RECEIVER_TYPE.USER, +); +const sentMedia = await CometChat.sendMediaMessage(media); +``` + +For upload progress, retries, or several files per message, use `CometChat.createUploadFileRequest()` (see "Media & file messages" below). + +## 5. Listen for incoming messages + +Register after login. Use a unique listener ID per screen, and remove it when the screen goes away. + +```ts +const listenerId = "chat-screen"; +CometChat.addMessageListener( + listenerId, + new CometChat.MessageListener({ + onTextMessageReceived: (msg: CometChat.TextMessage) => console.log("text:", msg.getText()), + onMediaMessageReceived: (msg: CometChat.MediaMessage) => console.log("media:", msg.getAttachment()?.getUrl()), + }), +); + +// when leaving the screen: +CometChat.removeMessageListener(listenerId); +``` + +The sender does not receive its own message through the listener (other devices of the same user do). Use the value returned by `sendMessage()` to show it locally. + +## 6. Fetch message history + +```ts +const request = new CometChat.MessagesRequestBuilder() + .setUID("cometchat-uid-2") // .setGUID(guid) for a group + .setLimit(30) + .build(); + +const page1 = await request.fetchPrevious(); // call again on the same request for older pages +``` + +## Common pitfalls + +- **Calling `login()`, `sendMessage()`, or adding listeners before `init()` resolves.** They fail. Await `init()` first. +- **Wrong or missing region.** `setRegion()` must match the app's region in the dashboard; a mismatch makes login fail. +- **Logging in twice.** Check `CometChat.getLoggedinUser()` first. A second `login()` while one is in flight throws "Please wait until the previous login request ends". +- **Running in Node or during SSR.** The SDK expects a browser. In Next.js and similar, initialize it only on the client. +- **Untyped listener callbacks in strict TypeScript.** Listener callbacks are typed as `Function`, so annotate parameters (`msg: CometChat.TextMessage`) or you get implicit-any errors. +- **Not removing listeners.** Call `removeMessageListener(id)` on unmount; re-adding with new IDs leaks duplicate handlers. +- **Receiver type mismatch.** Sending to a GUID with `RECEIVER_TYPE.USER` (or the reverse) fails. +- **Building a new `MessagesRequest` for each page.** Pagination state lives on the request object; reuse it for `fetchPrevious()`. +- **Auth Key in production.** Use server-issued Auth Tokens. + +## What to read next + +- Setup: https://www.cometchat.com/docs/sdk/javascript/setup-sdk +- Authentication: https://www.cometchat.com/docs/sdk/javascript/authentication-overview +- Send a message: https://www.cometchat.com/docs/sdk/javascript/send-message +- Receive a message: https://www.cometchat.com/docs/sdk/javascript/receive-message +- Media & file messages: https://www.cometchat.com/docs/sdk/javascript/upload-files +- Real-time listeners: https://www.cometchat.com/docs/sdk/javascript/all-real-time-listeners diff --git a/mcp-bundles/moderation-setup.md b/mcp-bundles/moderation-setup.md new file mode 100644 index 000000000..7b0c6fa44 --- /dev/null +++ b/mcp-bundles/moderation-setup.md @@ -0,0 +1,113 @@ +--- +title: Moderation Setup +framework: any +prerequisites: + - "CometChat app with Moderation available on its plan" + - "App ID and Region" + - "REST API key (full access) for the management endpoints" +last_verified: "2026-10-16" +packages: [] +--- + +# Moderation Setup + +Configure CometChat moderation: built-in rules, your own keyword lists, the flagged/blocked review queues, and moderation webhooks. The UI Kits need no code for this; the rules apply to every message sent through CometChat. + +Source: https://www.cometchat.com/docs/moderation/getting-started + +## 1. Turn off legacy extensions, then enable rules + +If the app still uses the legacy **Profanity Filter**, **Data Masking** or **Image Moderation** extensions, disable them first. Running both processes every message twice and delays delivery. + +Dashboard → your app → **Moderation** → **Settings** → **Rules** → toggle on the default rules you need. Categories: + +- **Text** — Profanity Filter, Contact Details Filter, Email Filter, AI Toxicity, AI Spam, AI Scam, AI Platform Circumvention, plus OpenAI rules (hate & harassment, explicit content, privacy, self-harm, violence, impersonation, spam & scam). +- **Image** — AI Image Moderation, OpenAI rules (explicit, violence/gore, hate symbols, privacy data, minor safety, fraud, terrorism), Malware & Virus Scanner. +- **Video** — AI Video Moderation, Malware & Virus Scanner. **Audio / File** — Malware & Virus Scanner. + +AI image/video moderation supports JPEG/PNG and MP4/MOV/AVI only. + +## 2. Choose an action per rule + +| Action | Effect | +| - | - | +| **Block** (default) | Message is not delivered; it goes to **Blocked Messages** | +| **Flag** | Message is delivered and added to **Flagged Messages** for review | +| **Ban User / Kick User** | Sender is banned or removed from the group | +| **Block User** | Sender is blocked platform-wide | + +For AI conditions you can also set a confidence percentage: lower blocks more, higher gives fewer false positives. + +## 3. Add your own keyword lists + +Dashboard: **Moderation** → **Settings** → **Lists** → **Add** → name, category (word / pattern / sentence), terms. Then open a rule and select the list in its **Conditions**. + +REST (multipart form; `category` is `word`, `pattern` or `sentence-similarity`): + +```bash +curl -X POST "https://${APP_ID}.api-${REGION}.cometchat.io/v3/moderation/keywords" \ + -H "apikey: ${REST_API_KEY}" \ + -F "id=competitor-links" \ + -F "name=Competitor links" \ + -F "category=word" \ + -F "searchTerms=competitor1.com,competitor2.io" +``` + +Keyword lists are comma-separated. Pattern and sentence lists take one entry per line. Regex must be bounded: `.*`, `.+`, `\d+` and other unbounded quantifiers are rejected, so use `\d{3,10}`. + +Rules can also be created over REST (`POST /v3/moderation/rules`); fetch the Add Rule reference for the condition schema first. + +Source: https://www.cometchat.com/docs/moderation/lists-management + +## 4. Client side + +- **UI Kits** — moderation is built in, including the **Report Message** action in the message menu. Nothing to code. +- **Custom UI on the Chat SDK** — read the message's moderation status (approved / pending / disapproved) and listen for `onMessageModerated`. See the SDK moderation page for your platform, e.g. https://www.cometchat.com/docs/sdk/javascript/ai-moderation +- Users choose a reason when they report. Configure reasons in Dashboard → **Moderation** → **Advanced Settings** (max 5 custom reasons). + +## 5. Webhook for moderation events + +Dashboard → **Webhooks** → **Create Webhook** → **Triggers** → **Moderation**. Events: + +- `moderation_engine_approved` / `moderation_engine_blocked` — the engine decided automatically. +- `moderation_manual_approved` — a moderator approved previously blocked content. +- `moderation_engine_failed` — OpenAI or your Custom API returned no decision. + +Use an HTTPS URL, enable **Basic Authentication** on the webhook and check the `Authorization` header on every request, and return `200 OK` within 2 seconds (queue slow work). + +## 6. Review queue + +- Dashboard → **Moderation** → **Flagged Messages** / **Blocked Messages**. Decisions are recorded in **Reviewed Messages**, the audit trail. +- REST: + +```bash +# List flagged messages +curl "https://${APP_ID}.api-${REGION}.cometchat.io/v3/moderation/flagged-messages?limit=50" \ + -H "apikey: ${REST_API_KEY}" + +# Approve or block one ("approved" | "disapproved") +curl -X PATCH "https://${APP_ID}.api-${REGION}.cometchat.io/v3/moderation/flagged-messages/${MESSAGE_ID}" \ + -H "apikey: ${REST_API_KEY}" -H "Content-Type: application/json" \ + -d '{"status":"disapproved"}' +``` + +Source: https://www.cometchat.com/docs/moderation/flagged-messages + +## Common pitfalls + +- **Legacy extensions left on** next to the new rules — messages are processed twice and delayed. +- **Rules enabled only on the dev app** — production ships unmoderated; enable them on the production app too. +- **Rules set to Flag with no one watching the queue** — the backlog grows; assign an owner. +- **Greedy regex in a list or rule** (`.*`, `\d+`) — rejected; use bounded quantifiers. +- **Comma-separated entries in a pattern or sentence list** — stored as one entry and never matches. +- **REST API key in client code** — the moderation endpoints need a full-access key; call them only from your server. +- **Limits** — 25 custom rules, 25 custom lists, 10 conditions and 10 filters per rule. +- **Read receipts** — marking a message delivered/read also marks earlier pending or disapproved messages delivered/read. + +## What to read next + +- Rules management: https://www.cometchat.com/docs/moderation/rules-management +- Moderation REST API: https://www.cometchat.com/docs/rest-api/moderation-apis/overview +- Custom moderation API: https://www.cometchat.com/docs/moderation/custom/custom-api-overview +- OpenAI moderation: https://www.cometchat.com/docs/moderation/open-ai/openai-overview +- Constraints and limits: https://www.cometchat.com/docs/moderation/constraints-and-limits diff --git a/mcp-bundles/multi-tenant-chat.md b/mcp-bundles/multi-tenant-chat.md new file mode 100644 index 000000000..7c5f04e8a --- /dev/null +++ b/mcp-bundles/multi-tenant-chat.md @@ -0,0 +1,160 @@ +--- +title: Multi-Tenant SaaS Chat +framework: any +prerequisites: + - "Backend service that can call the CometChat REST API and issue Auth Tokens" + - "CometChat App ID, REST API Key (fullAccess, server-side only), and Region" + - "A unique ID per user and per tenant in your own database" +last_verified: "2026-10-16" +packages: + - registry: npm + name: "@cometchat/chat-sdk-javascript" + version: "4.2.1" +--- + +# Multi-Tenant SaaS Chat + +Recipe for SaaS apps where many tenants share one CometChat app but must not see each other's users, conversations, or messages. + +Source: https://www.cometchat.com/docs/rest-api/chat-apis + +## 1. Choose an isolation model + +CometChat has no built-in tenant object. Pick one of these: + +| Model | How | Fits | +|---|---|---| +| One CometChat app per tenant | Separate App ID per tenant | Strongest isolation; more apps to manage | +| Shared app + RBAC "friends" mode | Users of a tenant are friends; roles restrict listing and sending to friends | Tenants up to ~1,000 users (max 1,000 friends per user) | +| Shared app + one role per tenant | RBAC `allowedRoles` per tenant role | Few tenants (max 25 roles per app) | + +In every model, **prefix IDs with the tenant** so they never collide and are easy to audit: + +``` +UID = `${tenantId}-${userId}` // e.g. t42-u8911 (letters, digits, dashes; stored lowercase) +GUID = `${tenantId}-${groupId}` // e.g. t42-project-777 +``` + +ID prefixing alone does not stop a client SDK from listing or messaging other tenants' users. Enforce isolation on the server (RBAC, friends, or separate apps) and in your own token endpoint. + +## 2. Install (client) + +```bash +npm install @cometchat/chat-sdk-javascript@4.2.1 +``` + +UI Kit apps install the kit per its quickstart; the server steps are the same. + +## 3. Provision users via REST (server) + +REST base URL: `https://{APP_ID}.api-{REGION}.cometchat.io/v3`. Authenticate with the `apikey` header (REST API Key, never shipped to clients). + +```bash +curl -X POST "https://${APP_ID}.api-${REGION}.cometchat.io/v3/users" \ + -H "apikey: $COMETCHAT_REST_API_KEY" -H "Content-Type: application/json" \ + -d '{ + "uid": "t42-u8911", + "name": "Sam Rivera", + "tags": ["tenant-t42"], + "metadata": { "tenantId": "t42" } + }' +``` + +`tags` can be filtered (`GET /users?tags=tenant-t42`); `metadata` cannot, so keep the tenant in both. + +## 4. Issue Auth Tokens only after a tenant check (server) + +```ts +// Express handler. requireAuth sets req.user from YOUR session. +app.post("/chat-token", requireAuth, async (req, res) => { + const { tenantId, userId } = req.user; // from your session, never from the request body + const uid = `${tenantId}-${userId}`; + const r = await fetch( + `https://${APP_ID}.api-${REGION}.cometchat.io/v3/users/${uid}/auth_tokens`, + { + method: "POST", + headers: { apikey: REST_API_KEY, "Content-Type": "application/json" }, + body: JSON.stringify({ force: true }), + }, + ); + if (!r.ok) return res.status(502).json({ error: "token mint failed" }); + const { data } = await r.json(); + res.json({ authToken: data.authToken }); +}); +``` + +## 5. Log in from the client + +```ts +import { CometChat } from "@cometchat/chat-sdk-javascript"; + +const settings = new CometChat.AppSettingsBuilder() + .setRegion(REGION) + .subscribePresenceForFriends() // not ForAllUsers: that streams every tenant's presence + .build(); +await CometChat.init(APP_ID, settings); + +const { authToken } = await fetch("/chat-token", { method: "POST" }).then((r) => r.json()); +await CometChat.login(authToken); // one argument = Auth Token login +``` + +React UI Kit: `await CometChatUIKit.loginWithAuthToken(authToken)` after `CometChatUIKit.init(...)`. + +## 6. Restrict cross-tenant access (shared app) + +Make users of the same tenant friends (`POST /v3/users/{uid}/friends` with `{"accepted": [...]}`, max 15 UIDs per call), then limit the role your users have to friends: + +```bash +curl -X PUT "https://${APP_ID}.api-${REGION}.cometchat.io/v3/roles/default/permissions" \ + -H "apikey: $COMETCHAT_REST_API_KEY" -H "Content-Type: application/json" \ + -d '{ "permissions": { + "listUsers.mode": "friends", + "getUserDetails.mode": "friends", + "sendMessage.mode": "friends" + } }' +``` + +Denied calls return `ERR_PERMISSION_DENIED`. Test with two tenants before launch. + +## 7. Create groups with tenant scoping (server) + +```bash +curl -X POST "https://${APP_ID}.api-${REGION}.cometchat.io/v3/groups" \ + -H "apikey: $COMETCHAT_REST_API_KEY" -H "Content-Type: application/json" \ + -d '{ + "guid": "t42-project-777", + "name": "Project 777", + "type": "private", + "owner": "t42-u8911", + "tags": ["tenant-t42"], + "metadata": { "tenantId": "t42" } + }' +``` + +Use `private` groups: users cannot join them on their own. Add members from your server after checking they belong to the same tenant. + +## 8. Audit checklist + +- Every UID/GUID has a tenant prefix and tag; webhooks for new users/groups verify it. +- The token endpoint and group-member adds run on your server and check the tenant. +- User and group pickers are scoped server-side (RBAC friends mode or your own endpoint). + +## Common pitfalls + +- **Relying on ID prefixes alone.** `UsersRequestBuilder().fetchNext()` and `CometChatUsers` list every user in the app unless RBAC restricts it. Client-side filters such as `setTags()` are display filters, not security. +- **`subscribePresenceForAllUsers()` in a shared app.** It sends presence for every tenant. Use friends (or roles) and note presence stops above 1,000 concurrent online users per app. +- **Shipping the REST API Key or Auth Key to clients.** Either one allows cross-tenant impersonation. Keep keys on the server; clients use Auth Tokens. +- **Trusting `tenantId` from the request body** in the token endpoint. Take it from your authenticated session. +- **Public groups for tenant spaces.** Any user in the app can find and join a public group. Use `private`. +- **Old REST conventions.** Use `/v3/` paths and the lowercase `apikey` header. +- **Calling `CometChat.loginWithAuthToken`.** It does not exist in the JavaScript SDK; use `CometChat.login(authToken)` (UI Kit: `CometChatUIKit.loginWithAuthToken`). + +## What to read next + +- REST authentication: https://www.cometchat.com/docs/rest-api/authentication +- Auth tokens: https://www.cometchat.com/docs/rest-api/auth-tokens +- Users API: https://www.cometchat.com/docs/rest-api/users +- Groups API: https://www.cometchat.com/docs/rest-api/groups +- RBAC permissions: https://www.cometchat.com/docs/rest-api/rbac-overview +- Friends API: https://www.cometchat.com/docs/rest-api/friends +- Limits: https://www.cometchat.com/docs/articles/properties-and-constraints diff --git a/mcp-bundles/presence-and-typing.md b/mcp-bundles/presence-and-typing.md new file mode 100644 index 000000000..437f1fccf --- /dev/null +++ b/mcp-bundles/presence-and-typing.md @@ -0,0 +1,142 @@ +--- +title: Presence and Typing Indicators +framework: any +prerequisites: + - "CometChat JavaScript SDK installed and initialized (see js-sdk-messaging-basics)" + - "App ID, Region, and a logged-in user" +last_verified: "2026-10-16" +packages: + - registry: npm + name: "@cometchat/chat-sdk-javascript" + version: "4.2.1" +--- + +# Presence and Typing Indicators + +Show online presence, typing indicators, and delivery/read receipts. The recipes use the JavaScript SDK; the iOS, Android, Flutter, and React Native SDKs have equivalent APIs (check each platform's docs for exact names). + +If you use a CometChat UI Kit, its components already show presence, typing, and receipts. You only need the presence subscription at init (step 2). Use this bundle when building your own UI. + +Source: +- https://www.cometchat.com/docs/sdk/javascript/user-presence +- https://www.cometchat.com/docs/sdk/javascript/typing-indicators +- https://www.cometchat.com/docs/sdk/javascript/delivery-read-receipts + +## 1. Install + +```bash +npm install @cometchat/chat-sdk-javascript@4.2.1 +``` + +The React UI Kit already depends on this package. + +## 2. Subscribe to presence at init + +Presence is configured when you call `init()`. If you call none of the `subscribePresenceFor*` methods, no presence events are delivered. + +```ts +import { CometChat } from "@cometchat/chat-sdk-javascript"; + +const settings = new CometChat.AppSettingsBuilder() + .setRegion(REGION) + .subscribePresenceForAllUsers() // or .subscribePresenceForRoles(["role"]) / .subscribePresenceForFriends() + .build(); +await CometChat.init(APP_ID, settings); +``` + +With the React UI Kit, call `.subscribePresenceForAllUsers()` on `UIKitSettingsBuilder` instead. + +## 3. Listen for online/offline events + +```ts +const presenceListenerId = "presence-listener"; +CometChat.addUserListener( + presenceListenerId, + new CometChat.UserListener({ + onUserOnline: (user: CometChat.User) => setPresence(user.getUid(), "online"), + onUserOffline: (user: CometChat.User) => setPresence(user.getUid(), "offline"), + }), +); + +// on unmount: +CometChat.removeUserListener(presenceListenerId); +``` + +## 4. Read a user's status on demand + +```ts +const u = await CometChat.getUser("cometchat-uid-2"); +console.log(u.getStatus()); // "online" | "offline" +console.log(u.getLastActiveAt()); // Unix timestamp of last activity +``` + +Users fetched with `UsersRequestBuilder` carry the same `status` and `lastActiveAt` fields. + +## 5. Send typing indicators + +```ts +const typing = new CometChat.TypingIndicator("cometchat-uid-2", CometChat.RECEIVER_TYPE.USER); +// for a group: new CometChat.TypingIndicator(guid, CometChat.RECEIVER_TYPE.GROUP) + +CometChat.startTyping(typing); // on first keystroke, not every keystroke +// after ~2 s with no input, or when the message is sent: +CometChat.endTyping(typing); +``` + +Both calls are fire-and-forget (they return `void`). + +## 6. Receive typing indicators + +```ts +const typingListenerId = "typing-listener"; +CometChat.addMessageListener( + typingListenerId, + new CometChat.MessageListener({ + onTypingStarted: (t: CometChat.TypingIndicator) => + showTyping(t.getReceiverType(), t.getReceiverId(), t.getSender().getUid()), + onTypingEnded: (t: CometChat.TypingIndicator) => + hideTyping(t.getReceiverType(), t.getReceiverId(), t.getSender().getUid()), + }), +); +``` + +For a one-to-one chat the receiver ID is your own UID, so key the indicator on the sender. For a group, key it on the group ID and list the senders. + +## 7. Delivery and read receipts + +Mark a message read when the user actually sees it. `markAsRead(message)` marks every message up to and including that one, so pass only the latest visible message: + +```ts +CometChat.markAsRead(lastVisibleMessage); // also: CometChat.markAsDelivered(message) +``` + +Listen for receipts on the sender side: + +```ts +CometChat.addMessageListener( + "receipts-listener", + new CometChat.MessageListener({ + onMessagesDelivered: (r: CometChat.MessageReceipt) => markUiDelivered(r.getMessageId()), + onMessagesRead: (r: CometChat.MessageReceipt) => markUiRead(r.getMessageId()), + }), +); +``` + +Group "delivered to all" / "read by all" events (`onMessagesDeliveredToAll`, `onMessagesReadByAll`) require the Enhanced Messaging Status feature. + +## Common pitfalls + +- **No presence subscription at init.** Without a `subscribePresenceFor*` call, `onUserOnline`/`onUserOffline` never fire. +- **`subscribePresenceForFriends()` without friends.** Only friends' events arrive; add friends (REST Friends API) or use roles/all users. +- **Calling `startTyping` on every keystroke.** Send it once when typing starts, then `endTyping` after about 2 s of inactivity or on send. +- **Marking every message read on screen open.** Mark only the latest visible message; it covers all earlier ones. +- **Large groups.** Group receipts stop above 300 online users and typing indicators above 1,000; don't rely on them there. +- **Not removing listeners on unmount.** Causes duplicate handlers and leaks. Remove each listener ID you add. +- **Untyped callbacks in strict TypeScript.** Listener callbacks are typed as `Function`; annotate parameters to avoid implicit-any errors. +- **Wrong receiver type.** A typing indicator sent to a GUID with `RECEIVER_TYPE.USER` (or the reverse) does not reach anyone. + +## What to read next + +- All real-time listeners: https://www.cometchat.com/docs/sdk/javascript/all-real-time-listeners +- Connection status: https://www.cometchat.com/docs/sdk/javascript/connection-status +- Retrieve users: https://www.cometchat.com/docs/sdk/javascript/retrieve-users diff --git a/mcp-bundles/react-native-uikit-quickstart.md b/mcp-bundles/react-native-uikit-quickstart.md new file mode 100644 index 000000000..aea11aec6 --- /dev/null +++ b/mcp-bundles/react-native-uikit-quickstart.md @@ -0,0 +1,167 @@ +--- +title: React Native UI Kit Quickstart +framework: react-native +prerequisites: + - "Node.js 18+" + - "React Native 0.77+ (bare CLI) or Expo with a development build (Expo Go cannot load the kit's native modules)" + - "CometChat App ID, Region and Auth Key from app.cometchat.com (Auth Key is for development only)" +last_verified: "2026-10-16" +packages: + - registry: npm + name: "@cometchat/chat-uikit-react-native" + version: "5.5.2" + - registry: npm + name: "@cometchat/chat-sdk-react-native" + version: "4.1.0" + - registry: npm + name: "@cometchat/calls-sdk-react-native" + version: "5.0.6" +--- + +# React Native UI Kit Quickstart + +Install the CometChat React Native UI Kit v5, initialize, log in, and show a conversation list that opens a chat screen. React Native is not React: the web kit's `UIKitSettingsBuilder`, `login("uid")` and `onItemClick` do not exist here. + +Source: https://www.cometchat.com/docs/ui-kit/react-native/react-native-cli-integration + +## 1. Install + +```bash +npm install @cometchat/chat-uikit-react-native@5.5.2 @cometchat/chat-sdk-react-native@4.1.0 +# Required native peers, plus the navigation used in step 5 +npm install @react-native-clipboard/clipboard react-native-gesture-handler react-native-svg \ + react-native-video react-native-localize react-native-safe-area-context \ + @react-native-async-storage/async-storage@^2.1.2 \ + @react-navigation/native @react-navigation/native-stack react-native-screens +npm install punycode # Expo only: the kit's markdown parser imports it and Metro does not provide it +``` + +Optional calling. Installing the package turns calling on (there is no `setCallingEnabled` here): + +```bash +npm install @cometchat/calls-sdk-react-native@5.0.6 react-native-background-timer@^2.4.1 \ + react-native-performance@^5.1.2 react-native-url-polyfill@^2.0.0 react-native-webrtc@^124.0.7 +``` + +## 2. Configure native projects + +- **Bare RN, iOS:** inside your app target in `ios/Podfile` add `pod 'SPTPersistentCache', :modular_headers => true` and `pod 'DVAssetLoaderDelegate', :modular_headers => true`, then `cd ios && pod install`. Without them `pod install` fails. +- **Bare RN:** `import "react-native-gesture-handler";` must be the first line of `index.js` (otherwise release builds crash). +- **Android** `minSdkVersion` 24+. iOS deployment target 13.0+ if you add calling. +- **Expo:** use a development build (`npx expo run:ios` / `run:android`); declare camera/mic permissions in `app.json`, since prebuild overwrites `ios/` and `android/`. +- After adding any native module, rebuild the app. A Metro reload is not enough. + +## 3. Initialize and 4. Log in + +`init` must resolve before `login`, and `login` before any kit component renders. Settings are a plain object, and `login` takes an object: `{ uid }` in development, `{ authToken }` (minted by your server) in production. Cache the promise so re-renders and Fast Refresh do not start a second login. + +```ts +// src/cometchat.ts +import { CometChatUIKit } from "@cometchat/chat-uikit-react-native"; +import { CometChat } from "@cometchat/chat-sdk-react-native"; + +const settings: CometChat.CometChatSettings = { + appId: "YOUR_APP_ID", region: "YOUR_REGION", // Dashboard -> Credentials + credentials: { authKey: "YOUR_AUTH_KEY" }, // development only + chatSDK: { presenceSubscription: { type: "ALL_USERS" }, autoEstablishSocketConnection: true }, +}; + +let boot: Promise | null = null; +export function ensureReady(uid: string): Promise { + boot ??= CometChatUIKit.initFromSettings(settings) + .then(() => CometChatUIKit.login({ uid })) + .catch((e) => { boot = null; throw e; }); + return boot; +} +``` + +`initFromSettings` records the integration as agent-built. The classic `CometChatUIKit.init({ appId, region, authKey, subscriptionType: "ALL_USERS" })` takes the same values as a plain object. Use a UID that exists in your app (new apps include `cometchat-uid-1` to `cometchat-uid-5`). + +## 5. Render + +There is no `CometChatConversationsWithMessages` in v5. Show `CometChatConversations`, then push a screen with header + list + composer. + +```tsx +// App.tsx +import { useEffect, useState } from "react"; +import { ActivityIndicator, View } from "react-native"; +import { GestureHandlerRootView } from "react-native-gesture-handler"; +import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context"; +import { NavigationContainer } from "@react-navigation/native"; +import { createNativeStackNavigator, NativeStackScreenProps as P } from "@react-navigation/native-stack"; +import { CometChat } from "@cometchat/chat-sdk-react-native"; +import { + CometChatConversations, CometChatI18nProvider, CometChatMessageComposer, CometChatMessageHeader, + CometChatMessageList, CometChatThemeProvider, CometChatUiKitConstants, +} from "@cometchat/chat-uikit-react-native"; +import { ensureReady } from "./src/cometchat"; + +type Routes = { Conversations: undefined; Messages: { user?: CometChat.User; group?: CometChat.Group } }; +const Stack = createNativeStackNavigator(); + +const ConversationsScreen = ({ navigation }: P) => ( + { + const isUser = c.getConversationType() === CometChatUiKitConstants.ConversationTypeConstants.user; + const peer = c.getConversationWith(); + navigation.navigate("Messages", isUser ? { user: peer as CometChat.User } : { group: peer as CometChat.Group }); + }} + /> +); + +const MessagesScreen = ({ route, navigation }: P) => { + const { user, group } = route.params; + return ( + + {/* showBackButton defaults to false on RN; without it onBack never fires */} + navigation.goBack()} /> + + + + ); +}; + +export default function App() { + const [ready, setReady] = useState(false); + useEffect(() => { ensureReady("cometchat-uid-1").then(() => setReady(true), console.error); }, []); + return ( + + + + + + {!ready ? : ( + + + + + + + )} + + + + + + ); +} +``` + +## Common pitfalls + +- **Running in Expo Go**, or a missing native peer. The app crashes on mount with an error naming the peer (svg, video, gesture-handler), not CometChat. +- **Missing `GestureHandlerRootView`** (outermost, `flex: 1`). Swipes and long-press silently do nothing. +- **Rendering before `login()` resolves.** Blank screen, no error. +- **Porting React web code.** `UIKitSettingsBuilder`, `login(uid)`, `onItemClick`, CSS and `*WithMessages` components do not exist. Callbacks use `Press` (`onItemPress`, `onThreadRepliesPress`); view slots are PascalCase (`ItemView`, `TrailingView`). +- **`showSearchBar` defaults to `false`** on `CometChatConversations`. Set it and wire `onSearchBarClicked` to a `CometChatSearch` screen. +- **Calls SDK without its peers.** The kit loads the calls SDK at startup, so a missing `react-native-url-polyfill` or `react-native-performance` breaks the Metro bundle even for chat-only apps. Keep `react-native-performance` on 5.x. +- **Fixed heights or two headers.** Use `flex: 1` (fixed heights break with the keyboard) and `headerShown: false` on screens with `CometChatMessageHeader`. +- **`tsc` errors inside `node_modules/@cometchat/chat-uikit-react-native`.** The kit ships TypeScript source. Ignore them; gate on the Metro bundle. +- **Auth Key in production.** App bundles are readable. Log in with a server-issued auth token. + +## What to read next + +- Expo setup: https://www.cometchat.com/docs/ui-kit/react-native/expo-integration +- Components: https://www.cometchat.com/docs/ui-kit/react-native/components-overview +- Theme: https://www.cometchat.com/docs/ui-kit/react-native/theme +- Calling: https://www.cometchat.com/docs/ui-kit/react-native/calling-integration diff --git a/mcp-bundles/react-uikit-quickstart.md b/mcp-bundles/react-uikit-quickstart.md new file mode 100644 index 000000000..e720c56ae --- /dev/null +++ b/mcp-bundles/react-uikit-quickstart.md @@ -0,0 +1,191 @@ +--- +title: React UI Kit Quickstart +framework: react +prerequisites: + - "Node.js 18+" + - "React 18 or 19 project (Vite or CRA)" + - "CometChat App ID, Auth Key, and Region from app.cometchat.com" +last_verified: "2026-10-16" +packages: + - registry: npm + name: "@cometchat/chat-uikit-react" + version: "7.2.4" + - registry: npm + name: "@cometchat/chat-sdk-javascript" + version: "4.2.1" + - registry: npm + name: "@cometchat/calls-sdk-javascript" + version: "5.0.8" +--- + +# React UI Kit Quickstart + +Install the CometChat React UI Kit v7, initialize, log in, and render a conversation list next to a message view. + +Source: https://www.cometchat.com/docs/ui-kit/react/integration-react + +## 1. Install + +```bash +npm install @cometchat/chat-uikit-react@7.2.4 @cometchat/chat-sdk-javascript@4.2.1 dompurify +``` + +For voice/video calling, also run `npm install @cometchat/calls-sdk-javascript@5.0.8` and enable calling in step 3. + +## 2. Configure + +Put credentials in `.env.local` (Vite reads `VITE_*`; CRA uses `REACT_APP_*` and `process.env`): + +```bash +VITE_COMETCHAT_APP_ID=your-app-id +VITE_COMETCHAT_REGION=us +VITE_COMETCHAT_AUTH_KEY=your-auth-key +``` + +Import the kit stylesheet once, in your entry file. v7 ships it at `/styles` (`css-variables.css` no longer exists): + +```tsx +// src/main.tsx +import "@cometchat/chat-uikit-react/styles"; +import "./index.css"; +``` + +On TypeScript 6 (or with `noUncheckedSideEffectImports`), add a declaration so the CSS import type-checks. Do not name it after an existing `.ts` file: + +```ts +// src/cometchat-styles.d.ts +declare module "@cometchat/chat-uikit-react/styles"; +``` + +## 3. Initialize and log in (once) + +`init()` must resolve before `login()`, and `login()` must resolve before any UI Kit component renders. Cache the promises so React StrictMode's double effect does not start a second init or a concurrent login (that throws "Please wait until the previous login request ends"). + +```ts +// src/cometchat.ts +import { CometChatUIKit, UIKitSettingsBuilder } from "@cometchat/chat-uikit-react"; + +let initPromise: Promise | null = null; +let loginPromise: Promise | null = null; + +export function initCometChat(): Promise { + if (!initPromise) { + const settings = new UIKitSettingsBuilder() + .setAppId(import.meta.env.VITE_COMETCHAT_APP_ID) + .setRegion(import.meta.env.VITE_COMETCHAT_REGION) + .setAuthKey(import.meta.env.VITE_COMETCHAT_AUTH_KEY) // dev only + .subscribePresenceForAllUsers() + // .setCallingEnabled(true) // only if @cometchat/calls-sdk-javascript is installed + .build(); + initPromise = CometChatUIKit.init(settings); + } + return initPromise; +} + +export async function loginCometChat(uid: string): Promise { + await initCometChat(); + const current = CometChatUIKit.getLoggedInUser(); // sync, capital "In" + if (current?.getUid() === uid) return; + if (current) await CometChatUIKit.logout(); + if (!loginPromise) { + loginPromise = CometChatUIKit.login(uid).finally(() => (loginPromise = null)); + } + await loginPromise; +} +``` + +The user must already exist (new apps include `cometchat-uid-1` to `cometchat-uid-5`). + +## 4. Render conversations + messages + +There is no combined `CometChatConversationsWithMessages` in v7. Compose the list and the message pane yourself, inside `CometChatProvider`: + +```tsx +// src/Chat.tsx +import { useEffect, useState } from "react"; +// CometChat.User / Group / Conversation are used as types only; the SDK declares the +// CometChat namespace globally, so no import is needed (a value import fails noUnusedLocals). +import { + CometChatProvider, + CometChatErrorBoundary, + CometChatConversations, + CometChatMessageHeader, + CometChatMessageList, + CometChatMessageComposer, +} from "@cometchat/chat-uikit-react"; +import { loginCometChat } from "./cometchat"; + +export function Chat({ uid }: { uid: string }) { + const [ready, setReady] = useState(false); + const [error, setError] = useState(null); + const [user, setUser] = useState(); + const [group, setGroup] = useState(); + + useEffect(() => { + loginCometChat(uid).then(() => setReady(true), (e) => setError(String(e?.message ?? e))); + }, [uid]); + + if (error) return
Chat failed to start: {error}
; + if (!ready) return
Loading chat…
; + + const open = (c: CometChat.Conversation) => { + const w = c.getConversationWith(); + if (c.getConversationType() === "user") { setUser(w as CometChat.User); setGroup(undefined); } + else { setGroup(w as CometChat.Group); setUser(undefined); } + }; + + return ( + + +
+
+
+ {(user || group) && ( + <> + + + + + )} +
+
+
+
+ ); +} +``` + +The components fill their parent, so the parent needs a fixed height: + +```css +/* src/index.css — also remove the Vite/CRA template's #root max-width/padding/centering */ +html, body, #root { height: 100%; margin: 0; } +.cc-app { display: flex; height: 100dvh; overflow: hidden; } +.cc-list { width: 320px; flex-shrink: 0; height: 100%; } +.cc-pane { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; } +``` + +## 5. Production auth + +The Auth Key is for development only. In production, mint an Auth Token on your server (REST `POST /v3/users/{uid}/auth_tokens`) and call `CometChatUIKit.loginWithAuthToken(token)` instead of `login(uid)`. Omit `setAuthKey` from the client build. + +REST: https://www.cometchat.com/docs/rest-api/auth-tokens/create + +## Common pitfalls + +- **Logging in before `init()` resolves, or rendering before `login()` resolves.** Await both; gate rendering on a ready flag. +- **StrictMode double effects.** A boolean "initialized" flag or an unguarded `login()` races. Cache the init and login promises as above. +- **Removed v6 names.** `CometChatConversationsWithMessages` and `css-variables.css` do not exist in v7. Without the `/styles` import the UI renders unstyled. +- **Zero-height chat.** A parent with `height: auto` or `min-height` collapses the kit. Pin the height (`100dvh` or a fixed value) and set `min-height: 0` on flex columns. +- **`transform` or `filter` on an ancestor.** It clips the kit's fixed-position menus, emoji picker, and call screen. +- **Wrong region or env prefix.** Login errors usually mean the region is wrong. Vite needs `VITE_*`; restart the dev server after editing `.env`. +- **Server-side rendering.** The kit needs `window`. In Next.js, load the chat in a `"use client"` component via `dynamic(..., { ssr: false })`. +- **Shipping the Auth Key.** Switch to Auth Tokens before launch. + +## What to read next + +- CometChatProvider (theme, locale): https://www.cometchat.com/docs/ui-kit/react/cometchat-provider +- Theming: https://www.cometchat.com/docs/ui-kit/react/theming +- Methods (login, logout, auth tokens): https://www.cometchat.com/docs/ui-kit/react/methods +- Components: https://www.cometchat.com/docs/ui-kit/react/components-overview +- Next.js setup: https://www.cometchat.com/docs/ui-kit/react/integration-nextjs diff --git a/mcp-bundles/removed-apis.json b/mcp-bundles/removed-apis.json new file mode 100644 index 000000000..7e63fbf77 --- /dev/null +++ b/mcp-bundles/removed-apis.json @@ -0,0 +1,28 @@ +{ + "$comment": "APIs and packages the current kits no longer have. check-mcp-bundles.mjs fails a bundle whose code blocks match one (prose may still name them, to warn). Add an entry when a kit release removes or renames something a bundle could use.", + "removed": [ + { + "pattern": "CometChat(Conversations|Users|Groups)WithMessages", + "reason": "removed from every current UI Kit; compose the list with CometChatMessageHeader, CometChatMessageList and CometChatMessageComposer" + }, + { + "pattern": "flutter_chat_ui_kit", + "reason": "old Flutter package (3.0.x betas only); the Flutter UI Kit is cometchat_chat_uikit" + }, + { + "pattern": "css-variables\\.css", + "reason": "React UI Kit v7 ships its styles as @cometchat/chat-uikit-react/styles", + "frameworks": [ + "react" + ] + }, + { + "pattern": "CometChatLocalize", + "reason": "check per platform: Flutter (through 6.2.0) uses Translations and React Native uses CometChatI18nProvider; React, Angular, iOS and Android do have CometChatLocalize", + "frameworks": [ + "flutter", + "react-native" + ] + } + ] +} diff --git a/mcp-bundles/widget-embed.md b/mcp-bundles/widget-embed.md new file mode 100644 index 000000000..7f4abd6cb --- /dev/null +++ b/mcp-bundles/widget-embed.md @@ -0,0 +1,113 @@ +--- +title: Widget Embed (No-Code) +framework: widget +prerequisites: + - "Access to your site's HTML, or its custom-code settings" + - "CometChat App ID, Region, and Auth Key from app.cometchat.com" + - "A widget variant designed in the dashboard Widget Builder (optional Variant ID)" +last_verified: "2026-10-16" +packages: [] +--- + +# Widget Embed (No-Code) + +Add the CometChat Chat Widget (v2) to an existing site with one script tag and one config object. Works on plain HTML, Webflow, Squarespace, Wix, Shopify and WordPress. + +Source: https://www.cometchat.com/docs/widget/html/integration + +## 1. Design the widget and get credentials + +1. In the CometChat dashboard, open your app's **Widget Builder**. +2. Set layout, features and theme, then click **Get Embedded Code**. +3. Note the **App ID**, **Auth Key**, **Region** and **Variant ID**. + +Look and features come from the Widget Builder. For deeper customization use a UI Kit instead. + +## 2. Embed the script + +In ``: + +```html + +``` + +In ``: + +```html +
+ +``` + +- `mode: "guest"` — anyone chats without signing in; drop `uid` and optionally set `user.name`. +- `mode: "uid"` — your own user IDs drive chat identity. +- Optional: `language` (default `"en-US"`), `storageMode` (`"LOCAL"` or `"SESSION"`), `autoOpenFirstItem`. + +After start you can drive it from JavaScript: `CometChatApp.chatWithUser("UID")`, `CometChatApp.chatWithGroup("GUID")`, `CometChatApp.uiEvent("onOpenChat", fn)`. + +## 3. CMS-specific install paths + +Put the script tag in the site-wide head (or next to the config), and the config snippet where the docs say: + +- **Webflow**: Site Settings → Custom Code → Head Code for the script tag; the config goes in an **Embed** element. +- **Squarespace**: Custom Code → Code Injection → **Footer**. +- **Wix**: Settings → Custom Code → **+ Add Custom Code** (site-level). Do not use an Embed HTML element. +- **Shopify**: Online Store → Themes → Edit code → `layout/theme.liquid`, near ``. +- **WordPress**: install the CometChat WordPress plugin (it loads its own copy of the widget). + +## 4. Production auth + +The Auth Key can log in as any user. For signed-in users, create the user and an auth token on your server (REST), send the token to the page, and use `authToken` mode with no `authKey`: + +```html + +``` + +REST: https://www.cometchat.com/docs/rest-api/auth-tokens/create + +## Common pitfalls + +- **Old v3 script** (`widget-js.cometchat.io/v3/cometchatwidget.js` with `CometChatWidget.init/launch`) — that is the legacy widget. New embeds use `@cometchat/chat-embed@2.x.x` and `CometChatApp.CometChatAuth.start`. +- **`@latest` or a bare package name** — still installs v1. Pin `@2.x.x`. +- **Config in a plain ` -``` - -In ``: - -```html -
- -``` - -- `mode: "guest"` — anyone chats without signing in; drop `uid` and optionally set `user.name`. -- `mode: "uid"` — your own user IDs drive chat identity. -- Optional: `language` (default `"en-US"`), `storageMode` (`"LOCAL"` or `"SESSION"`), `autoOpenFirstItem`. - -After start you can drive it from JavaScript: `CometChatApp.chatWithUser("UID")`, `CometChatApp.chatWithGroup("GUID")`, `CometChatApp.uiEvent("onOpenChat", fn)`. - -## 3. CMS-specific install paths - -Put the script tag in the site-wide head (or next to the config), and the config snippet where the docs say: - -- **Webflow**: Site Settings → Custom Code → Head Code for the script tag; the config goes in an **Embed** element. -- **Squarespace**: Custom Code → Code Injection → **Footer**. -- **Wix**: Settings → Custom Code → **+ Add Custom Code** (site-level). Do not use an Embed HTML element. -- **Shopify**: Online Store → Themes → Edit code → `layout/theme.liquid`, near ``. -- **WordPress**: install the CometChat WordPress plugin (it loads its own copy of the widget). - -## 4. Production auth - -The Auth Key can log in as any user. For signed-in users, create the user and an auth token on your server (REST), send the token to the page, and use `authToken` mode with no `authKey`: - -```html - -``` - -REST: https://www.cometchat.com/docs/rest-api/auth-tokens/create - -## Common pitfalls - -- **Old v3 script** (`widget-js.cometchat.io/v3/cometchatwidget.js` with `CometChatWidget.init/launch`) — that is the legacy widget. New embeds use `@cometchat/chat-embed@2.x.x` and `CometChatApp.CometChatAuth.start`. -- **`@latest` or a bare package name** — still installs v1. Pin `@2.x.x`. -- **Config in a plain `