|
| 1 | +# Beeper Desktop TypeScript MCP Server |
| 2 | + |
| 3 | +It is generated with [Stainless](https://www.stainless.com/). |
| 4 | + |
| 5 | +## Installation |
| 6 | + |
| 7 | +### Direct invocation |
| 8 | + |
| 9 | +You can run the MCP Server directly via `npx`: |
| 10 | + |
| 11 | +```sh |
| 12 | +export BEEPER_ACCESS_TOKEN="My Access Token" |
| 13 | +npx -y beeper/desktop-api-typescript-mcp@latest |
| 14 | +``` |
| 15 | + |
| 16 | +### Via MCP Client |
| 17 | + |
| 18 | +There is a partial list of existing clients at [modelcontextprotocol.io](https://modelcontextprotocol.io/clients). If you already |
| 19 | +have a client, consult their documentation to install the MCP server. |
| 20 | + |
| 21 | +For clients with a configuration JSON, it might look something like this: |
| 22 | + |
| 23 | +```json |
| 24 | +{ |
| 25 | + "mcpServers": { |
| 26 | + "desktop_api_typescript_api": { |
| 27 | + "command": "npx", |
| 28 | + "args": ["-y", "beeper/desktop-api-typescript-mcp", "--client=claude", "--tools=all"], |
| 29 | + "env": { |
| 30 | + "BEEPER_ACCESS_TOKEN": "My Access Token" |
| 31 | + } |
| 32 | + } |
| 33 | + } |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +## Exposing endpoints to your MCP Client |
| 38 | + |
| 39 | +There are two ways to expose endpoints as tools in the MCP server: |
| 40 | + |
| 41 | +1. Exposing one tool per endpoint, and filtering as necessary |
| 42 | +2. Exposing a set of tools to dynamically discover and invoke endpoints from the API |
| 43 | + |
| 44 | +### Filtering endpoints and tools |
| 45 | + |
| 46 | +You can run the package on the command line to discover and filter the set of tools that are exposed by the |
| 47 | +MCP Server. This can be helpful for large APIs where including all endpoints at once is too much for your AI's |
| 48 | +context window. |
| 49 | + |
| 50 | +You can filter by multiple aspects: |
| 51 | + |
| 52 | +- `--tool` includes a specific tool by name |
| 53 | +- `--resource` includes all tools under a specific resource, and can have wildcards, e.g. `my.resource*` |
| 54 | +- `--operation` includes just read (get/list) or just write operations |
| 55 | + |
| 56 | +### Dynamic tools |
| 57 | + |
| 58 | +If you specify `--tools=dynamic` to the MCP server, instead of exposing one tool per endpoint in the API, it will |
| 59 | +expose the following tools: |
| 60 | + |
| 61 | +1. `list_api_endpoints` - Discovers available endpoints, with optional filtering by search query |
| 62 | +2. `get_api_endpoint_schema` - Gets detailed schema information for a specific endpoint |
| 63 | +3. `invoke_api_endpoint` - Executes any endpoint with the appropriate parameters |
| 64 | + |
| 65 | +This allows you to have the full set of API endpoints available to your MCP Client, while not requiring that all |
| 66 | +of their schemas be loaded into context at once. Instead, the LLM will automatically use these tools together to |
| 67 | +search for, look up, and invoke endpoints dynamically. However, due to the indirect nature of the schemas, it |
| 68 | +can struggle to provide the correct properties a bit more than when tools are imported explicitly. Therefore, |
| 69 | +you can opt-in to explicit tools, the dynamic tools, or both. |
| 70 | + |
| 71 | +See more information with `--help`. |
| 72 | + |
| 73 | +All of these command-line options can be repeated, combined together, and have corresponding exclusion versions (e.g. `--no-tool`). |
| 74 | + |
| 75 | +Use `--list` to see the list of available tools, or see below. |
| 76 | + |
| 77 | +### Specifying the MCP Client |
| 78 | + |
| 79 | +Different clients have varying abilities to handle arbitrary tools and schemas. |
| 80 | + |
| 81 | +You can specify the client you are using with the `--client` argument, and the MCP server will automatically |
| 82 | +serve tools and schemas that are more compatible with that client. |
| 83 | + |
| 84 | +- `--client=<type>`: Set all capabilities based on a known MCP client |
| 85 | + |
| 86 | + - Valid values: `openai-agents`, `claude`, `claude-code`, `cursor` |
| 87 | + - Example: `--client=cursor` |
| 88 | + |
| 89 | +Additionally, if you have a client not on the above list, or the client has gotten better |
| 90 | +over time, you can manually enable or disable certain capabilities: |
| 91 | + |
| 92 | +- `--capability=<name>`: Specify individual client capabilities |
| 93 | + - Available capabilities: |
| 94 | + - `top-level-unions`: Enable support for top-level unions in tool schemas |
| 95 | + - `valid-json`: Enable JSON string parsing for arguments |
| 96 | + - `refs`: Enable support for $ref pointers in schemas |
| 97 | + - `unions`: Enable support for union types (anyOf) in schemas |
| 98 | + - `formats`: Enable support for format validations in schemas (e.g. date-time, email) |
| 99 | + - `tool-name-length=N`: Set maximum tool name length to N characters |
| 100 | + - Example: `--capability=top-level-unions --capability=tool-name-length=40` |
| 101 | + - Example: `--capability=top-level-unions,tool-name-length=40` |
| 102 | + |
| 103 | +### Examples |
| 104 | + |
| 105 | +1. Filter for read operations on cards: |
| 106 | + |
| 107 | +```bash |
| 108 | +--resource=cards --operation=read |
| 109 | +``` |
| 110 | + |
| 111 | +2. Exclude specific tools while including others: |
| 112 | + |
| 113 | +```bash |
| 114 | +--resource=cards --no-tool=create_cards |
| 115 | +``` |
| 116 | + |
| 117 | +3. Configure for Cursor client with custom max tool name length: |
| 118 | + |
| 119 | +```bash |
| 120 | +--client=cursor --capability=tool-name-length=40 |
| 121 | +``` |
| 122 | + |
| 123 | +4. Complex filtering with multiple criteria: |
| 124 | + |
| 125 | +```bash |
| 126 | +--resource=cards,accounts --operation=read --tag=kyc --no-tool=create_cards |
| 127 | +``` |
| 128 | + |
| 129 | +## Running remotely |
| 130 | + |
| 131 | +Launching the client with `--transport=http` launches the server as a remote server using Streamable HTTP transport. The `--port` setting can choose the port it will run on, and the `--socket` setting allows it to run on a Unix socket. |
| 132 | + |
| 133 | +Authorization can be provided via the `Authorization` header using the Bearer scheme. |
| 134 | + |
| 135 | +Additionally, authorization can be provided via the following headers: |
| 136 | +| Header | Equivalent client option | Security scheme | |
| 137 | +| ----------------------- | ------------------------ | --------------- | |
| 138 | +| `x-beeper-access-token` | `accessToken` | bearerAuth | |
| 139 | + |
| 140 | +A configuration JSON for this server might look like this, assuming the server is hosted at `http://localhost:3000`: |
| 141 | + |
| 142 | +```json |
| 143 | +{ |
| 144 | + "mcpServers": { |
| 145 | + "desktop_api_typescript_api": { |
| 146 | + "url": "http://localhost:3000", |
| 147 | + "headers": { |
| 148 | + "Authorization": "Bearer <auth value>" |
| 149 | + } |
| 150 | + } |
| 151 | + } |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +The command-line arguments for filtering tools and specifying clients can also be used as query parameters in the URL. |
| 156 | +For example, to exclude specific tools while including others, use the URL: |
| 157 | + |
| 158 | +``` |
| 159 | +http://localhost:3000?resource=cards&resource=accounts&no_tool=create_cards |
| 160 | +``` |
| 161 | + |
| 162 | +Or, to configure for the Cursor client, with a custom max tool name length, use the URL: |
| 163 | + |
| 164 | +``` |
| 165 | +http://localhost:3000?client=cursor&capability=tool-name-length%3D40 |
| 166 | +``` |
| 167 | + |
| 168 | +## Importing the tools and server individually |
| 169 | + |
| 170 | +```js |
| 171 | +// Import the server, generated endpoints, or the init function |
| 172 | +import { server, endpoints, init } from "beeper/desktop-api-typescript-mcp/server"; |
| 173 | + |
| 174 | +// import a specific tool |
| 175 | +import listAccounts from "beeper/desktop-api-typescript-mcp/tools/accounts/list-accounts"; |
| 176 | + |
| 177 | +// initialize the server and all endpoints |
| 178 | +init({ server, endpoints }); |
| 179 | + |
| 180 | +// manually start server |
| 181 | +const transport = new StdioServerTransport(); |
| 182 | +await server.connect(transport); |
| 183 | + |
| 184 | +// or initialize your own server with specific tools |
| 185 | +const myServer = new McpServer(...); |
| 186 | + |
| 187 | +// define your own endpoint |
| 188 | +const myCustomEndpoint = { |
| 189 | + tool: { |
| 190 | + name: 'my_custom_tool', |
| 191 | + description: 'My custom tool', |
| 192 | + inputSchema: zodToJsonSchema(z.object({ a_property: z.string() })), |
| 193 | + }, |
| 194 | + handler: async (client: client, args: any) => { |
| 195 | + return { myResponse: 'Hello world!' }; |
| 196 | + }) |
| 197 | +}; |
| 198 | + |
| 199 | +// initialize the server with your custom endpoints |
| 200 | +init({ server: myServer, endpoints: [listAccounts, myCustomEndpoint] }); |
| 201 | +``` |
| 202 | + |
| 203 | +## Available Tools |
| 204 | + |
| 205 | +The following tools are available in this MCP server. |
| 206 | + |
| 207 | +### Resource `accounts`: |
| 208 | + |
| 209 | +- `list_accounts` (`read`): List connected Beeper accounts available on this device. |
| 210 | + - When to use: select account context before account-scoped operations. |
| 211 | + - Scope: only accounts currently Connected on this device are included. |
| 212 | + Returns: connected accounts. |
| 213 | + |
| 214 | +### Resource `app`: |
| 215 | + |
| 216 | +- `focus_app` (`write`): Bring Beeper Desktop to the foreground on this device. Optionally focuses a specific chat if chatID is provided. |
| 217 | + - When to use: open Beeper, or jump to a specific chat. |
| 218 | + - Constraints: requires Beeper Desktop running locally; no-op in headless environments. |
| 219 | + - Idempotent: safe to call repeatedly. Returns an error if chatID is not found. |
| 220 | + Returns: success. |
| 221 | + |
| 222 | +### Resource `messages`: |
| 223 | + |
| 224 | +- `draft_messages` (`write`): Draft a message in a specific chat. This will be placed in the message input field without sending |
| 225 | +- `search_messages` (`read`): Search messages across chats using Beeper's message index. |
| 226 | + - When to use: find messages by text and/or filters (chatIDs, accountIDs, chatType, media type filters, sender, date ranges). |
| 227 | + - CRITICAL: Query is LITERAL WORD MATCHING, NOT semantic search! Only finds messages containing these EXACT words. |
| 228 | + • ✅ RIGHT: query="dinner" or query="sick" or query="error" (single words users type) |
| 229 | + • ❌ WRONG: query="dinner plans tonight" or query="health issues" (phrases/concepts) |
| 230 | + • The query matches ALL words provided (in any order). Example: query="flight booking" finds messages with both "flight" AND "booking". |
| 231 | + - Media filters: Use onlyWithMedia for any media, or specific filters like onlyWithVideo, onlyWithImage, onlyWithLink, onlyWithFile for specific types. |
| 232 | + - Pagination: use 'oldestCursor' + direction='before' for older; 'newestCursor' + direction='after' for newer. |
| 233 | + - Performance: provide chatIDs/accountIDs when known. Omitted 'query' returns results based on filters only. Partial matches enabled; 'excludeLowPriority' defaults to true. |
| 234 | + - Workflow tip: To search messages in specific conversations: 1) Use find-chats to get chatIDs, 2) Use search-messages with those chatIDs. |
| 235 | + - IMPORTANT: Chat names vary widely. ASK the user for clarification: |
| 236 | + • "Which chat do you mean by family?" (could be "The Smiths", "Mom Dad Kids", etc.) |
| 237 | + • "What's the name of your work chat?" (could be "Team", company name, project name) |
| 238 | + • "Who are the participants?" (use participantQuery in find-chats) |
| 239 | + Returns: matching messages and referenced chats. |
| 240 | +- `send_messages` (`write`): Send a text message to a specific chat. Supports replying to existing messages. Returns the sent message ID and a deeplink to the chat |
| 241 | + |
| 242 | +### Resource `chats`: |
| 243 | + |
| 244 | +- `retrieve_chats` (`read`): Retrieve chat details: metadata, participants (limited), and latest message. |
| 245 | + - When to use: fetch a complete view of a chat beyond what search returns. |
| 246 | + - Constraints: not available for iMessage chats ('imsg##'). Participants limited by 'maxParticipantCount' (default 20, max 500). |
| 247 | + Returns: chat details.Agents: ALWAYS use linkToChat to make clickable links in your response |
| 248 | +- `archive_chats` (`write`): Archive or unarchive a chat. Set archived=true to move to archive, archived=false to move back to inbox |
| 249 | +- `find_chats` (`read`): Search and filter conversations across all messaging accounts. |
| 250 | + - When to use: browse chats by inbox (primary/low-priority/archive), type, unread status, or search terms. |
| 251 | + - Pagination: use cursor + direction for pagination. |
| 252 | + - Performance: provide accountIDs when known for faster filtering. |
| 253 | + Returns: matching chats with pagination. |
| 254 | + Agents: ALWAYS use linkToChat to make clickable links in your response |
| 255 | +- `get_link_chats` (`write`): Generate a deep link to a specific chat or message. This link can be used to open the chat directly in the Beeper app. |
| 256 | + |
| 257 | +### Resource `reminders`: |
| 258 | + |
| 259 | +- `clear_reminders` (`write`): Clear an existing reminder from a chat |
| 260 | +- `set_reminders` (`write`): Set a reminder for a chat at a specific time |
| 261 | + |
| 262 | +### Resource `oauth`: |
| 263 | + |
| 264 | +- `get_user_info_oauth` (`read`): Returns information about the authenticated user/token |
| 265 | +- `revoke_token_oauth` (`write`): Revoke an access token or refresh token (RFC 7009) |
0 commit comments