@pubnub/mcp
Version: 
PubNub Model Context Protocol MCP Server for Cursor and Claude
282 lines (165 loc) • 15.7 kB
Markdown
On this page
Sample chat app
===============
We've created a sample direct (1:1) chat app to show you what you can use our Chat SDK for.
Underneath, the app uses a set of basic Chat SDK methods to initialize the SDK, create a sample channel and users, and let you send messages. On the outside, the app comes with a custom UI layer added to show what a final chat app could look like - how you present your own apps, however, is entirely up to you.
Run and test the app first, then read on to understand how it works.

### Tools used
Prerequisites[](#prerequisites "Direct link to Prerequisites")
---------------------------------------------------------------
To run the chat app, make sure to have the following:
*   [yarn](https://classic.yarnpkg.com/en/docs/install) (>=1.22.19)
    
*   [Node.js](https://nodejs.org/en/download/package-manager/) (>=18.10.0)
    
*   Code editor (for example, [Visual Studio Code](https://code.visualstudio.com/download))
    
*   Publish and subscribe keys to initialize the PubNub object in your app and send and receive messages through the PubNub Network.
    
    ##### Keys setup and configuration
    
    To get both keys, [sign in](https://admin.pubnub.com/) or [create an account](/docs/general/setup/account-setup) on the Admin Portal. The autogenerated **Demo Keyset** in **My First App** already contains the configuration required to complete this guide (enabled App Context and selected region closest to your location). Follow [this guide](https://www.youtube.com/watch?v=ou5RMN1LQ1Y&t=19s&ab_channel=PubNub) if you need help creating the keys.
    
Run the chat app[](#run-the-chat-app "Direct link to Run the chat app")
------------------------------------------------------------------------
Check how to set up a 1:1 chat app by running a ready sample React app or adding the Chat SDK to your own app.
*   Run a sample React app
*   Add Chat SDK to your app
1.  [**Download**](https://github.com/pubnub/js-chat/archive/refs/heads/master.zip) the `getting-started` app from the `js-chat` repository - the monorepo that houses the source code of the Chat SDK.
    
2.  Unpack the archive into the folder of your choice.
    
3.  Open the terminal, navigate to the `js-chat-master` root folder, and install the dependencies.
    
        yarn
    
4.  Go to the `lib` folder that contains the TypeScript sources.
    
        cd lib
    
5.  Build the app.
    
        yarn build
    
6.  Go to the `samples/getting-started` folder with the app's source code.
    
        cd ../samples/getting-started
    
7.  Add required configuration.
    
    Open the app in the code editor and navigate to the `.env.example` file under `samples/getting-started`. Duplicate the file in the same location, rename the copy to `.env`, and use it to specify values for [your publish and subscribe keys](https://www.youtube.com/watch?v=ou5RMN1LQ1Y&t=19s&ab_channel=PubNub) from your app's keyset on the Admin Portal.
    
        VITE_PUBNUB_PUB_KEY=// for example: VITE_PUBNUB_PUB_KEY=pub-c-jbgh67-a796-c36-z81-17dvbf4VITE_PUBNUB_SUB_KEY=// for example: VITE_PUBNUB_SUB_KEY=sub-c-jb46h67-z796-g36-z91-19vdfb1
    
    Both keys are referenced in the `src/App.tsx` file containing the whole app's source code.
    
        const chat = await Chat.init({  publishKey: import.meta.env.VITE_PUBNUB_PUB_KEY,  subscribeKey: import.meta.env.VITE_PUBNUB_SUB_KEY,  userId: randomizedUsers[0].id,})
    
    To associate a sender/current user with the PubNub messages, you must configure the [`userId`](/docs/general/setup/users-and-devices) parameter for your user ID in the database. If you wish, modify the default value for the chat user.
    
    For simplicity, the getting started app sets a static `userId` that randomly selects one of the two default users (to simulate a 1:1 conversation). However, if you implement chat in your app, you should generate a `userId` per user, device, and server and reuse it for their lifetime. Using separate User IDs in real-life apps is particularly important as it impacts your overall [billing](/docs/general/setup/users-and-devices#billing) and the way your app [works](/docs/general/setup/users-and-devices#presence).
    
8.  Run the app in the terminal.
    
        yarn dev
    
9.  When the app compiles successfully, you will see the link to the localhost in the terminal.
    
10.  You can open the app in two separate browser windows - if you get randomly assigned two different users, simulate a real-life chat conversation.
    
    A sample conversation between a support agent and a customer may look as follows:
    
1.  Open the terminal in the location of your app files and add the Chat SDK package.
    
        yarn add /chat
    
2.  Open your application source files in your favorite code editor. Copy the snippet below into your app source file (for example, `src/App.tsx`). To set the placeholder values for `subscribeKey` and `publishKey` with [your publish and subscribe keys](https://www.youtube.com/watch?v=ou5RMN1LQ1Y&t=19s&ab_channel=PubNub) from the keyset on the Admin Portal, copy the `.env.example` file and rename it to `.env`. Then use the copy to set values for `VITE_PUBNUB_SUB_KEY` and `VITE_PUBNUB_PUB_KEY`.
    
    
    
    ### Source code
    
      
    
3.  Add the app's CSS styles, for example, in `src/App.css`.
    
    
    
    ### CSS styles
    
      
    
4.  Run the app in the terminal.
    
        yarn dev
    
5.  When the app compiles successfully, you will see the link to the localhost in the terminal.
    
6.  You can open the app in two separate browser windows - if you get randomly assigned two different users, simulate a real-life chat conversation.
    
    A sample conversation between a support agent and a customer may look as follows:
    

How this works[](#how-this-works "Direct link to How this works")
------------------------------------------------------------------
Let's walk you through all the Chat SDK methods this app uses to show you what you need to create your own chat. This section analyzes the `src/App.tsx` file that contains the app's source file.
### Initialize chat[](#initialize-chat "Direct link to Initialize chat")
Initialize [Chat SDK](/docs/chat/chat-sdk/build/configuration#initialize-pubnub), providing your publish and subscribe keys from the Admin Portal.
    const chat = await Chat.init({  subscribeKey: import.meta.env.VITE_PUBNUB_SUB_KEY,  publishKey: import.meta.env.VITE_PUBNUB_PUB_KEY,  userId: randomizedUsers[0].id,})
For this app, you must set these envs for `VITE_PUBNUB_SUB_KEY` and `VITE_PUBNUB_PUB_KEY` in the separate `.env` file (copy of the `.env.example` file).
### Chat SDK Configuration with Access Manager V3 Authentication
When using Access Manager V3 authentication with the Chat SDK, you must use the `authKey` parameter (not `token`) for client configuration:
```javascript
// ✅ CORRECT: Use authKey for Chat SDK configuration
const chat = await Chat.init({
  subscribeKey: import.meta.env.VITE_PUBNUB_SUB_KEY,
  publishKey: import.meta.env.VITE_PUBNUB_PUB_KEY,
  userId: randomizedUsers[0].id,
  authKey: 'myAccessManagerToken'  // ✅ CORRECT: Use authKey for client configuration
});
// ❌ INCORRECT: Do not use 'token' in Chat SDK configuration
const chat = await Chat.init({
  subscribeKey: import.meta.env.VITE_PUBNUB_SUB_KEY,
  publishKey: import.meta.env.VITE_PUBNUB_PUB_KEY,
  userId: randomizedUsers[0].id,
  token: 'myAccessManagerToken'  // ❌ WRONG: This will not work
});
```
**Important:** The Chat SDK uses the same parameter naming convention as the core PubNub JavaScript SDK. Always use `authKey` for client configuration, while Access Manager methods use the `token` parameter.
### Set up users[](#set-up-users "Direct link to Set up users")
This sample app uses two hardcoded sample users - a support agent and a customer.
    const userData = [  {    id: "support-agent",    data: { name: "John (Support Agent)", custom: { initials: "SA", avatar: "#9fa7df" } },  },  {    id: "supported-user",    data: { name: "Mary Watson", custom: { initials: "MW", avatar: "#ffab91" } },  },]
When you run the app, you get randomly assigned an ID of one of these two users (`support-agent` or `supported-user`). Based on the provided details, the user metadata gets created ([`createUser()`](/docs/chat/chat-sdk/build/features/users/create)), updated ([`update()`](/docs/chat/chat-sdk/build/features/users/updates#update-user-details)), and retrieved ([`getUser()`](/docs/chat/chat-sdk/build/features/users/details#get-user-details)) from PubNub's App Context.
    const randomizedUsers = Math.random() < 0.5 ? userData : userData.reverse()
    const currentUser = await chat.currentUser.update(randomizedUsers[0].data)
    // Ensure users exist before creating channels
    const interlocutor = (await chat.getUser(randomizedUsers[1].id)) ||
                        (await chat.createUser(randomizedUsers[1].id, randomizedUsers[1].data))
> **Note:** The Chat SDK requires you to explicitly create or retrieve users before using them in conversations. Use `chat.getUser()` with a fallback to `chat.createUser()` to enforce proper user management.
The advantage of having that random logic here is that if you open the app in another browser window and get assigned another user, you can simulate a real-life discussion.
For production apps, you would assign a fixed [User ID](https://www.pubnub.com/docs/general/basics/identify-users-and-devices) that becomes the app's current user. User ID identifies your client when communicating with PubNub and is used by PubNub to calculate pricing.
### Create channel[](#create-channel "Direct link to Create channel")
Now, you need a channel where these two users can talk and receive messages from each other.
The Chat SDK supports the following channel types:
*   [Direct](/docs/chat/chat-sdk/build/features/channels/create#create-direct-channel)
*   [Group](/docs/chat/chat-sdk/build/features/channels/create#create-group-channel)
*   [Public](/docs/chat/chat-sdk/build/features/channels/create#create-public-channel)
To simulate a conversation between two users, you'll need a direct channel type.
The ID of the communication channel is hardcoded to `Support Channel`. When you run the app for the first time, you create the direct (1:1) channel ([`createDirectConversation()`](/docs/chat/chat-sdk/build/features/channels/create#create-direct-channel)) on the PubNub server.
    const { channel } = await chat.createDirectConversation({
      user: interlocutor,
      channelData: { name: "Support Channel" },
    })
#### Channel caching and helper
To avoid recreating channels on each load, you can cache active channels and use a helper:
```js
const activeChannels = {};
async function getOrCreateDirectChannel(chat, interlocutor, channelData) {
  const cacheKey = `${chat.currentUser.id}_${interlocutor.id}`;
  if (activeChannels[cacheKey]) {
    return activeChannels[cacheKey];
  }
  const { channel } = await chat.createDirectConversation({ user: interlocutor, channelData });
  activeChannels[cacheKey] = channel;
  return channel;
}
```
Remember to clear the `activeChannels` cache on logout to free resources.
To receive messages from another user, you must be subscribed to the channel, or "connected," as we call it. This guide uses the Chat SDK [`connect()`](/docs/chat/chat-sdk/build/features/channels/watch) method to listen for messages, called by the Getting Started app within `useEffect()` to update its messages state.
    useEffect(() => {  if (!channel) return  return channel.connect((message) => setMessages((messages) => [...messages, message]))}, [channel])
### Send messages[](#send-messages "Direct link to Send messages")
The app handles:
*   Incoming messages through the `setMessages()` function that uses the [`connect()` method](/docs/chat/chat-sdk/build/features/channels/watch) to receive messages from another user and then add these messages to the collective message list.
*   Outgoing messages through the `handleSend()` function that uses the [`sendText()` method](/docs/chat/chat-sdk/build/features/messages/send-receive) to publish messages on the channel.
      useEffect(() => {    if (!channel) return    return channel.connect((message) => setMessages((messages) => [...messages, message]))  }, [channel])...async function handleSend(event: React.SyntheticEvent) {  event.preventDefault()  if (!text || !channel) return  await channel.sendText(text)  setText("")}
The Getting Started app that this guide is based on only supports sending text messages. The Chat SDK, however, also supports [files](/docs/chat/chat-sdk/build/features/messages/files) and rich message features like [links](/docs/chat/chat-sdk/build/features/messages/links), [ mentions](/docs/chat/chat-sdk/build/features/users/mentions), or [channel references](/docs/chat/chat-sdk/build/features/channels/references) that are similar in logic to  mentions.
### Show time indicator[](#show-time-indicator "Direct link to Show time indicator")
Another addition to the basic setup is the time indicator in the app. The Chat SDK comes with [utility methods](/docs/chat/chat-sdk/build/features/utility-methods#timetoken-to-date) handling Unix timestamp to [PubNub message timetoken](/docs/sdks/javascript/api-reference/misc#time) conversion (and vice versa). Our sample chat app converts a message timetoken to a "short" time style, like `08:25`.
    <time>    {TimetokenUtils.timetokenToDate(message.timetoken).toLocaleTimeString([], {    timeStyle: "short",    })}</time>
### Add look and feel[](#add-look-and-feel "Direct link to Add look and feel")
The UI for the app is completely customized, and it's up to you to decide what you want your chat to look like. Check the CSS styles used to beautify this sample chat app if interested.

### CSS styles
Next steps[](#next-steps "Direct link to Next steps")
------------------------------------------------------
You can extend this basic chat app by adding other features, like [message reactions](/docs/chat/chat-sdk/build/features/messages/reactions), [typing indicator](/docs/chat/chat-sdk/build/features/channels/typing-indicator), [mentions](/docs/chat/chat-sdk/build/features/users/mentions), and many more.
### Logout cleanup
If your app supports logout, disconnect all channels and clear local caches:
```js
async function logout(chat) {
  await chat.disconnect();
  Object.keys(activeChannels).forEach((key) => delete activeChannels[key]);
}
```
### Message reactions (real-time sync)
To toggle reactions and keep them synchronized in real time, use the following:
```js
// Toggle a reaction on a message
async function toggleReaction(channel, messageTimetoken, actionType, value) {
  await channel.sendReaction({ messageTimetoken, actionType, value });
}
// Listen for real-time reaction events
channel.on('reaction', ({ event, data }) => {
  console.log(`Reaction ${event}:`, data);
  // Use data.messageTimetoken to find and update your message in local state
});
```
You can also try running another sample and see what a [group chat in React Native](https://github.com/pubnub/js-chat/tree/master/samples/react-native-group-chat) can look like.
Head to the [Features](/docs/chat/chat-sdk/build/features/channels/create) section to explore everything the Chat SDK offers.
Last updated on **Nov 19, 2024**