├── src ├── utils │ ├── index.ts │ └── error.ts ├── schema │ ├── preprocess.ts │ ├── file.ts │ ├── icon.ts │ ├── color.ts │ ├── annotations.ts │ ├── number.ts │ ├── lang.ts │ ├── users.ts │ ├── page-properties.ts │ ├── comments.ts │ ├── rich-text.ts │ ├── mention.ts │ ├── page.ts │ ├── blocks.ts │ ├── timezone.ts │ └── database.ts ├── config │ └── index.ts ├── types │ ├── users.ts │ ├── database.ts │ ├── comments.ts │ ├── page.ts │ ├── blocks.ts │ └── emoji.ts ├── index.ts ├── services │ └── notion.ts ├── tools │ ├── createPage.ts │ ├── createDatabase.ts │ ├── updateDatabase.ts │ ├── updatePageProperties.ts │ ├── queryDatabase.ts │ ├── deleteBlock.ts │ ├── updateBlock.ts │ ├── retrieveBlock.ts │ ├── appendBlockChildren.ts │ ├── searchPage.ts │ ├── database.ts │ ├── batchDeleteBlocks.ts │ ├── batchUpdateBlocks.ts │ ├── batchAppendBlockChildren.ts │ ├── updatePage.ts │ ├── retrieveBlockChildren.ts │ ├── pages.ts │ ├── batchMixedOperations.ts │ ├── index.ts │ ├── blocks.ts │ ├── users.ts │ └── comments.ts └── server │ └── index.ts ├── page_connection.png ├── .gitignore ├── tsconfig.json ├── Dockerfile ├── smithery.yaml ├── LICENSE ├── package.json └── README.md /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./error.js"; 2 | -------------------------------------------------------------------------------- /page_connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awkoy/notion-mcp-server/HEAD/page_connection.png -------------------------------------------------------------------------------- /src/schema/preprocess.ts: -------------------------------------------------------------------------------- 1 | export const preprocessJson = (val: any) => 2 | typeof val === "string" ? JSON.parse(val) : val; 3 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | // Configuration 2 | export const CONFIG = { 3 | serverName: "notion-mcp-server", 4 | serverVersion: "1.0.1", 5 | }; 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | /dist 4 | /coverage 5 | /logs 6 | /tmp 7 | .env 8 | .cursor 9 | .cursorignore 10 | .cursorrules 11 | .cursorconfig 12 | .cursorignorerules 13 | .cursorignoreconfig 14 | .npmrc -------------------------------------------------------------------------------- /src/schema/file.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const FILE_SCHEMA = z 4 | .object({ 5 | external: z 6 | .object({ 7 | url: z.string().url().describe("URL of the external file"), 8 | }) 9 | .describe("External file source"), 10 | type: z.literal("external").describe("Type of file source"), 11 | }) 12 | .describe("File schema"); 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } 16 | -------------------------------------------------------------------------------- /src/schema/icon.ts: -------------------------------------------------------------------------------- 1 | import { EmojiRequest } from "../types/emoji.js"; 2 | import { z } from "zod"; 3 | 4 | export const ICON_SCHEMA = z.object({ 5 | emoji: z 6 | .string() 7 | .refine( 8 | (value) => /(\p{Emoji}\uFE0F|\p{Emoji_Presentation})/gu.test(value), 9 | { 10 | message: "Invalid emoji", 11 | } 12 | ) 13 | .transform((value) => value as EmojiRequest), 14 | type: z.literal("emoji"), 15 | }); 16 | -------------------------------------------------------------------------------- /src/schema/color.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const COLOR_SCHEMA = z.enum([ 4 | "default", 5 | "gray", 6 | "brown", 7 | "orange", 8 | "yellow", 9 | "green", 10 | "blue", 11 | "purple", 12 | "pink", 13 | "red", 14 | "gray_background", 15 | "brown_background", 16 | "orange_background", 17 | "yellow_background", 18 | "green_background", 19 | "blue_background", 20 | "purple_background", 21 | "pink_background", 22 | "red_background", 23 | ]); 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22.12-alpine AS builder 2 | 3 | WORKDIR /app 4 | 5 | COPY package*.json ./ 6 | COPY tsconfig.json ./ 7 | COPY src/ ./src/ 8 | 9 | RUN --mount=type=cache,target=/root/.npm npm install 10 | RUN npm run build 11 | 12 | FROM node:22.12-alpine AS release 13 | 14 | WORKDIR /app 15 | 16 | COPY --from=builder /app/build /app/build 17 | COPY --from=builder /app/package.json ./ 18 | COPY --from=builder /app/package-lock.json ./ 19 | 20 | ENV NODE_ENV=production 21 | 22 | RUN npm ci --ignore-scripts --omit-dev 23 | 24 | CMD ["node", "build/index.js"] 25 | -------------------------------------------------------------------------------- /src/types/users.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { 3 | LIST_USERS_SCHEMA, 4 | GET_USER_SCHEMA, 5 | USERS_OPERATION_SCHEMA, 6 | } from "../schema/users.js"; 7 | 8 | // Types inferred from schemas 9 | const listUsersSchema = z.object(LIST_USERS_SCHEMA); 10 | export type ListUsersParams = z.infer; 11 | 12 | const getUserSchema = z.object(GET_USER_SCHEMA); 13 | export type GetUserParams = z.infer; 14 | 15 | const usersOperationSchema = z.object(USERS_OPERATION_SCHEMA); 16 | export type UsersOperationParams = z.infer; 17 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { startServer } from "./server/index.js"; 3 | import { registerAllTools } from "./tools/index.js"; 4 | 5 | registerAllTools(); 6 | 7 | async function main() { 8 | try { 9 | await startServer(); 10 | } catch (error) { 11 | console.error( 12 | "Unhandled server error:", 13 | error instanceof Error ? error.message : String(error) 14 | ); 15 | process.exit(1); 16 | } 17 | } 18 | 19 | main().catch((error: unknown) => { 20 | console.error( 21 | "Unhandled server error:", 22 | error instanceof Error ? error.message : String(error) 23 | ); 24 | process.exit(1); 25 | }); 26 | -------------------------------------------------------------------------------- /src/services/notion.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "@notionhq/client"; 2 | 3 | export function getApiToken(): string { 4 | const token = process.env.NOTION_TOKEN; 5 | if (!token) { 6 | console.error("Error: NOTION_TOKEN environment variable is required"); 7 | process.exit(1); 8 | } 9 | return token; 10 | } 11 | 12 | export function getRootPageId(): string { 13 | const pageId = process.env.NOTION_PAGE_ID; 14 | if (!pageId) { 15 | console.error("Error: NOTION_PAGE_ID environment variable is required"); 16 | process.exit(1); 17 | } 18 | return pageId; 19 | } 20 | 21 | export const notion = new Client({ 22 | auth: process.env.NOTION_TOKEN, 23 | }); 24 | -------------------------------------------------------------------------------- /src/schema/annotations.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { COLOR_SCHEMA } from "./color.js"; 3 | 4 | export const ANNOTATIONS_SCHEMA = z 5 | .object({ 6 | bold: z.boolean().optional().describe("Whether text is bold"), 7 | italic: z.boolean().optional().describe("Whether text is italic"), 8 | strikethrough: z 9 | .boolean() 10 | .optional() 11 | .describe("Whether text has strikethrough"), 12 | underline: z.boolean().optional().describe("Whether text is underlined"), 13 | code: z.boolean().optional().describe("Whether text is code formatted"), 14 | color: COLOR_SCHEMA.optional().describe("Color of the text"), 15 | }) 16 | .describe("Text formatting annotations"); 17 | -------------------------------------------------------------------------------- /src/tools/createPage.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { CreatePageParams } from "../types/page.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const registerCreatePageTool = async ( 7 | params: CreatePageParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.pages.create(params); 11 | 12 | return { 13 | content: [ 14 | { 15 | type: "text", 16 | text: `Page created successfully: ${response.id}`, 17 | }, 18 | ], 19 | }; 20 | } catch (error) { 21 | return handleNotionError(error); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/tools/createDatabase.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { CreateDatabaseParams } from "../types/database.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const createDatabase = async ( 7 | params: CreateDatabaseParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.databases.create(params); 11 | 12 | return { 13 | content: [ 14 | { 15 | type: "text", 16 | text: `Database created successfully: ${response.id}`, 17 | }, 18 | ], 19 | }; 20 | } catch (error) { 21 | return handleNotionError(error); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/tools/updateDatabase.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { UpdateDatabaseParams } from "../types/database.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const updateDatabase = async ( 7 | params: UpdateDatabaseParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.databases.update(params); 11 | 12 | return { 13 | content: [ 14 | { 15 | type: "text", 16 | text: `Database updated successfully: ${response.id}`, 17 | }, 18 | ], 19 | }; 20 | } catch (error) { 21 | return handleNotionError(error); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/deployments 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - notionToken 10 | - notionPageId 11 | properties: 12 | notionToken: 13 | type: string 14 | description: The API key for the Notion API. 15 | notionPageId: 16 | type: string 17 | description: The ID of the Notion page to interact with. 18 | commandFunction: 19 | # A function that produces the CLI command to start the MCP on stdio. 20 | |- 21 | config=>({command:'node',args:['build/index.js'],env:{NOTION_TOKEN:config.notionToken,NOTION_PAGE_ID:config.notionPageId}}) -------------------------------------------------------------------------------- /src/schema/number.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const NUMBER_FORMAT = z.enum([ 4 | "number", 5 | "number_with_commas", 6 | "percent", 7 | "dollar", 8 | "canadian_dollar", 9 | "euro", 10 | "pound", 11 | "yen", 12 | "ruble", 13 | "rupee", 14 | "won", 15 | "yuan", 16 | "real", 17 | "lira", 18 | "rupiah", 19 | "franc", 20 | "hong_kong_dollar", 21 | "new_zealand_dollar", 22 | "krona", 23 | "norwegian_krone", 24 | "mexican_peso", 25 | "rand", 26 | "new_taiwan_dollar", 27 | "danish_krone", 28 | "zloty", 29 | "baht", 30 | "forint", 31 | "koruna", 32 | "shekel", 33 | "chilean_peso", 34 | "philippine_peso", 35 | "dirham", 36 | "colombian_peso", 37 | "riyal", 38 | "ringgit", 39 | "leu", 40 | "argentine_peso", 41 | "uruguayan_peso", 42 | "singapore_dollar", 43 | ]); 44 | -------------------------------------------------------------------------------- /src/tools/updatePageProperties.ts: -------------------------------------------------------------------------------- 1 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 2 | import { notion } from "../services/notion.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { UpdatePagePropertiesParams } from "../types/page.js"; 5 | 6 | export async function updatePageProperties( 7 | params: UpdatePagePropertiesParams 8 | ): Promise { 9 | try { 10 | const response = await notion.pages.update({ 11 | page_id: params.pageId, 12 | properties: params.properties, 13 | }); 14 | 15 | return { 16 | content: [ 17 | { 18 | type: "text", 19 | text: `Page properties updated successfully: ${response.id}`, 20 | }, 21 | ], 22 | }; 23 | } catch (error) { 24 | return handleNotionError(error); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/types/database.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { 3 | CREATE_DATABASE_SCHEMA, 4 | QUERY_DATABASE_SCHEMA, 5 | UPDATE_DATABASE_SCHEMA, 6 | DATABASE_OPERATION_SCHEMA, 7 | } from "../schema/database.js"; 8 | 9 | export const createDatabaseSchema = z.object(CREATE_DATABASE_SCHEMA); 10 | export type CreateDatabaseParams = z.infer; 11 | 12 | export const queryDatabaseSchema = z.object(QUERY_DATABASE_SCHEMA); 13 | export type QueryDatabaseParams = z.infer; 14 | 15 | export const updateDatabaseSchema = z.object(UPDATE_DATABASE_SCHEMA); 16 | export type UpdateDatabaseParams = z.infer; 17 | 18 | export const databaseOperationSchema = z.object(DATABASE_OPERATION_SCHEMA); 19 | export type DatabaseOperationParams = z.infer; 20 | -------------------------------------------------------------------------------- /src/tools/queryDatabase.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { QueryDatabaseParams } from "../types/database.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const queryDatabase = async ( 7 | params: QueryDatabaseParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.databases.query(params); 11 | 12 | return { 13 | content: [ 14 | { 15 | type: "text", 16 | text: `Database queried successfully. Found ${response.results.length} results.`, 17 | }, 18 | { 19 | type: "text", 20 | text: JSON.stringify(response, null, 2), 21 | }, 22 | ], 23 | }; 24 | } catch (error) { 25 | return handleNotionError(error); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/tools/deleteBlock.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { DeleteBlockParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const deleteBlock = async ( 7 | params: DeleteBlockParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.blocks.delete({ 11 | block_id: params.blockId, 12 | }); 13 | 14 | return { 15 | content: [ 16 | { 17 | type: "text", 18 | text: `Block ${params.blockId} deleted (moved to trash) successfully`, 19 | }, 20 | { 21 | type: "text", 22 | text: JSON.stringify(response, null, 2), 23 | }, 24 | ], 25 | }; 26 | } catch (error) { 27 | return handleNotionError(error); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/tools/updateBlock.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { UpdateBlockParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const updateBlock = async ( 7 | params: UpdateBlockParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.blocks.update({ 11 | block_id: params.blockId, 12 | ...params.data, 13 | }); 14 | 15 | return { 16 | content: [ 17 | { 18 | type: "text", 19 | text: `Block ${response.id} updated successfully`, 20 | }, 21 | { 22 | type: "text", 23 | text: JSON.stringify(response, null, 2), 24 | }, 25 | ], 26 | }; 27 | } catch (error) { 28 | return handleNotionError(error); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/types/comments.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { 3 | GET_COMMENTS_SCHEMA, 4 | ADD_PAGE_COMMENT_SCHEMA, 5 | ADD_DISCUSSION_COMMENT_SCHEMA, 6 | COMMENTS_OPERATION_SCHEMA, 7 | } from "../schema/comments.js"; 8 | 9 | // Types inferred from the schemas 10 | export const getCommentsSchema = z.object(GET_COMMENTS_SCHEMA); 11 | export type GetCommentsParams = z.infer; 12 | 13 | export const addPageCommentSchema = z.object(ADD_PAGE_COMMENT_SCHEMA); 14 | export type AddPageCommentParams = z.infer; 15 | 16 | export const addDiscussionCommentSchema = z.object( 17 | ADD_DISCUSSION_COMMENT_SCHEMA 18 | ); 19 | export type AddDiscussionCommentParams = z.infer< 20 | typeof addDiscussionCommentSchema 21 | >; 22 | 23 | export const commentsOperationSchema = z.object(COMMENTS_OPERATION_SCHEMA); 24 | export type CommentsOperationParams = z.infer; 25 | -------------------------------------------------------------------------------- /src/tools/retrieveBlock.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { RetrieveBlockParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const retrieveBlock = async ( 7 | params: RetrieveBlockParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.blocks.retrieve({ 11 | block_id: params.blockId, 12 | }); 13 | 14 | return { 15 | content: [ 16 | { 17 | type: "text", 18 | text: "Block retrieved successfully! Note: If this block has children, use the retrieve_block_children endpoint to get the list of child blocks.", 19 | }, 20 | { 21 | type: "text", 22 | text: JSON.stringify(response, null, 2), 23 | }, 24 | ], 25 | }; 26 | } catch (error) { 27 | return handleNotionError(error); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/tools/appendBlockChildren.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { AppendBlockChildrenParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const appendBlockChildren = async ( 7 | params: AppendBlockChildrenParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.blocks.children.append({ 11 | block_id: params.blockId, 12 | children: params.children, 13 | }); 14 | 15 | return { 16 | content: [ 17 | { 18 | type: "text", 19 | text: `Successfully appended ${params.children.length} block(s) to ${params.blockId}`, 20 | }, 21 | { 22 | type: "text", 23 | text: `Block ID: ${JSON.stringify(response, null, 2)}`, 24 | }, 25 | ], 26 | }; 27 | } catch (error) { 28 | return handleNotionError(error); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /src/tools/searchPage.ts: -------------------------------------------------------------------------------- 1 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 2 | import { notion } from "../services/notion.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { SearchPagesParams } from "../types/page.js"; 5 | 6 | export async function searchPages( 7 | params: SearchPagesParams 8 | ): Promise { 9 | try { 10 | const response = await notion.search({ 11 | query: params.query || "", 12 | sort: params.sort, 13 | start_cursor: params.start_cursor, 14 | page_size: params.page_size || 10, 15 | }); 16 | 17 | const resultsText = JSON.stringify(response, null, 2); 18 | 19 | return { 20 | content: [ 21 | { 22 | type: "text", 23 | text: `Found ${response.results.length} results. ${ 24 | response.has_more ? "More results available." : "" 25 | }\n\n${resultsText}`, 26 | }, 27 | ], 28 | }; 29 | } catch (error) { 30 | return handleNotionError(error); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/tools/database.ts: -------------------------------------------------------------------------------- 1 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 2 | import { handleNotionError } from "../utils/error.js"; 3 | import { DatabaseOperationParams } from "../types/database.js"; 4 | import { createDatabase } from "./createDatabase.js"; 5 | import { queryDatabase } from "./queryDatabase.js"; 6 | import { updateDatabase } from "./updateDatabase.js"; 7 | 8 | export const registerDatabaseOperationTool = async ( 9 | params: DatabaseOperationParams 10 | ): Promise => { 11 | switch (params.payload.action) { 12 | case "create_database": 13 | return createDatabase(params.payload.params); 14 | case "query_database": 15 | return queryDatabase(params.payload.params); 16 | case "update_database": 17 | return updateDatabase(params.payload.params); 18 | default: 19 | return handleNotionError( 20 | new Error( 21 | `Unsupported action, use one of the following: "create_database", "query_database", "update_database"` 22 | ) 23 | ); 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/server/index.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; 2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 3 | import { CONFIG } from "../config/index.js"; 4 | export const server = new McpServer( 5 | { 6 | name: CONFIG.serverName, 7 | version: CONFIG.serverVersion, 8 | }, 9 | { 10 | capabilities: { 11 | resources: {}, 12 | tools: {}, 13 | }, 14 | instructions: ` 15 | MCP server for the Notion. 16 | It is used to create, update and delete Notion entities. 17 | `, 18 | } 19 | ); 20 | 21 | export async function startServer() { 22 | try { 23 | const transport = new StdioServerTransport(); 24 | await server.connect(transport); 25 | console.log( 26 | `${CONFIG.serverName} v${CONFIG.serverVersion} running on stdio` 27 | ); 28 | } catch (error) { 29 | console.error( 30 | "Server initialization error:", 31 | error instanceof Error ? error.message : String(error) 32 | ); 33 | process.exit(1); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/tools/batchDeleteBlocks.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { BatchDeleteBlocksParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const batchDeleteBlocks = async ( 7 | params: BatchDeleteBlocksParams 8 | ): Promise => { 9 | try { 10 | const results = []; 11 | 12 | for (const blockId of params.blockIds) { 13 | const response = await notion.blocks.delete({ 14 | block_id: blockId, 15 | }); 16 | 17 | results.push({ 18 | blockId, 19 | success: true, 20 | response, 21 | }); 22 | } 23 | 24 | return { 25 | content: [ 26 | { 27 | type: "text", 28 | text: `Successfully deleted ${params.blockIds.length} blocks (moved to trash)`, 29 | }, 30 | { 31 | type: "text", 32 | text: JSON.stringify(results, null, 2), 33 | }, 34 | ], 35 | }; 36 | } catch (error) { 37 | return handleNotionError(error); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Yaroslav Boiko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/tools/batchUpdateBlocks.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { BatchUpdateBlocksParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const batchUpdateBlocks = async ( 7 | params: BatchUpdateBlocksParams 8 | ): Promise => { 9 | try { 10 | const results = []; 11 | 12 | for (const operation of params.operations) { 13 | const response = await notion.blocks.update({ 14 | block_id: operation.blockId, 15 | ...operation.data, 16 | }); 17 | 18 | results.push({ 19 | blockId: operation.blockId, 20 | success: true, 21 | response, 22 | }); 23 | } 24 | 25 | return { 26 | content: [ 27 | { 28 | type: "text", 29 | text: `Successfully updated ${params.operations.length} blocks`, 30 | }, 31 | { 32 | type: "text", 33 | text: JSON.stringify(results, null, 2), 34 | }, 35 | ], 36 | }; 37 | } catch (error) { 38 | return handleNotionError(error); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /src/types/page.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { 3 | ARCHIVE_PAGE_SCHEMA, 4 | CREATE_PAGE_SCHEMA, 5 | RESTORE_PAGE_SCHEMA, 6 | SEARCH_PAGES_SCHEMA, 7 | UPDATE_PAGE_PROPERTIES_SCHEMA, 8 | PAGES_OPERATION_SCHEMA, 9 | } from "../schema/page.js"; 10 | 11 | export const createPageSchema = z.object(CREATE_PAGE_SCHEMA); 12 | export type CreatePageParams = z.infer; 13 | 14 | export const archivePageSchema = z.object(ARCHIVE_PAGE_SCHEMA); 15 | export type ArchivePageParams = z.infer; 16 | 17 | export const restorePageSchema = z.object(RESTORE_PAGE_SCHEMA); 18 | export type RestorePageParams = z.infer; 19 | 20 | export const searchPagesSchema = z.object(SEARCH_PAGES_SCHEMA); 21 | export type SearchPagesParams = z.infer; 22 | 23 | export const updatePagePropertiesSchema = z.object( 24 | UPDATE_PAGE_PROPERTIES_SCHEMA 25 | ); 26 | export type UpdatePagePropertiesParams = z.infer< 27 | typeof updatePagePropertiesSchema 28 | >; 29 | 30 | export const pagesOperationSchema = z.object(PAGES_OPERATION_SCHEMA); 31 | export type PagesOperationParams = z.infer; 32 | -------------------------------------------------------------------------------- /src/tools/batchAppendBlockChildren.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { BatchAppendBlockChildrenParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const batchAppendBlockChildren = async ( 7 | params: BatchAppendBlockChildrenParams 8 | ): Promise => { 9 | try { 10 | const results = []; 11 | 12 | for (const operation of params.operations) { 13 | const response = await notion.blocks.children.append({ 14 | block_id: operation.blockId, 15 | children: operation.children, 16 | }); 17 | 18 | results.push({ 19 | blockId: operation.blockId, 20 | success: true, 21 | response, 22 | }); 23 | } 24 | 25 | return { 26 | content: [ 27 | { 28 | type: "text", 29 | text: `Successfully completed ${params.operations.length} append operations`, 30 | }, 31 | { 32 | type: "text", 33 | text: JSON.stringify(results, null, 2), 34 | }, 35 | ], 36 | }; 37 | } catch (error) { 38 | return handleNotionError(error); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notion-mcp-server", 3 | "version": "1.0.1", 4 | "type": "module", 5 | "bin": { 6 | "notion-mcp-server": "build/index.js" 7 | }, 8 | "scripts": { 9 | "build": "tsc && shx chmod +x build/*.js", 10 | "prepare": "npm run build", 11 | "watch": "tsc --watch", 12 | "inspector": "npx @modelcontextprotocol/inspector build/index.js -e NOTION_TOKEN=your_notion_token -e NOTION_PAGE_ID=your_notion_page_id" 13 | }, 14 | "homepage": "https://github.com/awkoy/notion-mcp-server", 15 | "keywords": [ 16 | "notion", 17 | "mcp", 18 | "modelcontextprotocol", 19 | "ai" 20 | ], 21 | "author": "Yaroslav Boiko ", 22 | "license": "MIT", 23 | "description": "MCP for Notion", 24 | "files": [ 25 | "build" 26 | ], 27 | "dependencies": { 28 | "@modelcontextprotocol/sdk": "^1.9.0", 29 | "@notionhq/client": "^2.3.0", 30 | "zod": "^3.24.2" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "^22.13.10", 34 | "shx": "^0.3.4", 35 | "typescript": "^5.8.2" 36 | }, 37 | "engines": { 38 | "node": ">=18" 39 | }, 40 | "repository": { 41 | "type": "git", 42 | "url": "git+https://github.com/awkoy/notion-mcp-server.git" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/tools/updatePage.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { ArchivePageParams, RestorePageParams } from "../types/page.js"; 5 | 6 | export async function archivePage( 7 | params: ArchivePageParams 8 | ): Promise { 9 | try { 10 | const response = await notion.pages.update({ 11 | page_id: params.pageId, 12 | archived: true, 13 | }); 14 | 15 | return { 16 | content: [ 17 | { 18 | type: "text", 19 | text: `Page archived successfully: ${response.id}`, 20 | }, 21 | ], 22 | }; 23 | } catch (error) { 24 | return handleNotionError(error); 25 | } 26 | } 27 | 28 | export async function restorePage( 29 | params: RestorePageParams 30 | ): Promise { 31 | try { 32 | const response = await notion.pages.update({ 33 | page_id: params.pageId, 34 | archived: false, 35 | }); 36 | 37 | return { 38 | content: [ 39 | { 40 | type: "text", 41 | text: `Page restored successfully: ${response.id}`, 42 | }, 43 | ], 44 | }; 45 | } catch (error) { 46 | return handleNotionError(error); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/tools/retrieveBlockChildren.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { RetrieveBlockChildrenParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const retrieveBlockChildren = async ( 7 | params: RetrieveBlockChildrenParams 8 | ): Promise => { 9 | try { 10 | const response = await notion.blocks.children.list({ 11 | block_id: params.blockId, 12 | start_cursor: params.start_cursor, 13 | page_size: params.page_size, 14 | }); 15 | 16 | return { 17 | content: [ 18 | { 19 | type: "text", 20 | text: `Successfully retrieved ${response.results.length} children of block ${params.blockId}`, 21 | }, 22 | { 23 | type: "text", 24 | text: `Has more: ${response.has_more ? "Yes" : "No"}${ 25 | response.has_more && response.next_cursor 26 | ? `, Next cursor: ${response.next_cursor}` 27 | : "" 28 | }`, 29 | }, 30 | { 31 | type: "text", 32 | text: JSON.stringify(response.results, null, 2), 33 | }, 34 | ], 35 | }; 36 | } catch (error) { 37 | return handleNotionError(error); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/tools/pages.ts: -------------------------------------------------------------------------------- 1 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 2 | import { handleNotionError } from "../utils/error.js"; 3 | import { PagesOperationParams } from "../types/page.js"; 4 | import { registerCreatePageTool } from "./createPage.js"; 5 | import { archivePage } from "./updatePage.js"; 6 | import { restorePage } from "./updatePage.js"; 7 | import { searchPages } from "./searchPage.js"; 8 | import { updatePageProperties } from "./updatePageProperties.js"; 9 | 10 | export const registerPagesOperationTool = async ( 11 | params: PagesOperationParams 12 | ): Promise => { 13 | switch (params.payload.action) { 14 | case "create_page": 15 | return registerCreatePageTool(params.payload.params); 16 | case "archive_page": 17 | return archivePage(params.payload.params); 18 | case "restore_page": 19 | return restorePage(params.payload.params); 20 | case "search_pages": 21 | return searchPages(params.payload.params); 22 | case "update_page_properties": 23 | return updatePageProperties(params.payload.params); 24 | default: 25 | return handleNotionError( 26 | new Error( 27 | `Unsupported action, use one of the following: "create_page", "archive_page", "restore_page", "search_pages", "update_page_properties"` 28 | ) 29 | ); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /src/schema/lang.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const LANGUAGE_SCHEMA = z 4 | .enum([ 5 | "abap", 6 | "arduino", 7 | "bash", 8 | "basic", 9 | "c", 10 | "clojure", 11 | "coffeescript", 12 | "c++", 13 | "c#", 14 | "css", 15 | "dart", 16 | "diff", 17 | "docker", 18 | "elixir", 19 | "elm", 20 | "erlang", 21 | "flow", 22 | "fortran", 23 | "f#", 24 | "gherkin", 25 | "glsl", 26 | "go", 27 | "graphql", 28 | "groovy", 29 | "haskell", 30 | "html", 31 | "java", 32 | "javascript", 33 | "json", 34 | "julia", 35 | "kotlin", 36 | "latex", 37 | "less", 38 | "lisp", 39 | "livescript", 40 | "lua", 41 | "makefile", 42 | "markdown", 43 | "markup", 44 | "matlab", 45 | "mermaid", 46 | "nix", 47 | "objective-c", 48 | "ocaml", 49 | "pascal", 50 | "perl", 51 | "php", 52 | "plain text", 53 | "powershell", 54 | "prolog", 55 | "protobuf", 56 | "python", 57 | "r", 58 | "reason", 59 | "ruby", 60 | "rust", 61 | "sass", 62 | "scala", 63 | "scheme", 64 | "scss", 65 | "shell", 66 | "sql", 67 | "swift", 68 | "typescript", 69 | "vb.net", 70 | "verilog", 71 | "vhdl", 72 | "visual basic", 73 | "webassembly", 74 | "xml", 75 | "yaml", 76 | "java/c/c++/c#", 77 | ]) 78 | .describe("Programming language for code blocks"); 79 | -------------------------------------------------------------------------------- /src/schema/users.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { preprocessJson } from "./preprocess.js"; 3 | 4 | // Schema for listing users with pagination 5 | export const LIST_USERS_SCHEMA = { 6 | start_cursor: z.string().optional().describe("Pagination cursor"), 7 | page_size: z 8 | .number() 9 | .optional() 10 | .describe("Number of users to return per page"), 11 | }; 12 | 13 | // Schema for getting a single user 14 | export const GET_USER_SCHEMA = { 15 | user_id: z.string().describe("The ID of the user to retrieve"), 16 | }; 17 | 18 | // Combined schema for all user operations 19 | export const USERS_OPERATION_SCHEMA = { 20 | payload: z 21 | .preprocess( 22 | preprocessJson, 23 | z.discriminatedUnion("action", [ 24 | z.object({ 25 | action: z 26 | .literal("list_users") 27 | .describe("Use this action to list users."), 28 | params: z.object(LIST_USERS_SCHEMA), 29 | }), 30 | z.object({ 31 | action: z 32 | .literal("get_user") 33 | .describe("Use this action to get a single user."), 34 | params: z.object(GET_USER_SCHEMA), 35 | }), 36 | z.object({ 37 | action: z 38 | .literal("get_bot_user") 39 | .describe("Use this action to get the bot user."), 40 | params: z.object({}), 41 | }), 42 | ]) 43 | ) 44 | .describe( 45 | "A union of all possible user operations. Each operation has a specific action and corresponding parameters. Use this schema to validate the input for user operations such as listing, getting a single user, and getting the bot user. Available actions include: 'list_users', 'get_user', and 'get_bot_user'. Each operation requires specific parameters as defined in the corresponding schemas." 46 | ), 47 | }; 48 | -------------------------------------------------------------------------------- /src/tools/batchMixedOperations.ts: -------------------------------------------------------------------------------- 1 | import { notion } from "../services/notion.js"; 2 | import { BatchMixedOperationsParams } from "../types/blocks.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 5 | 6 | export const batchMixedOperations = async ( 7 | params: BatchMixedOperationsParams 8 | ): Promise => { 9 | try { 10 | const results = []; 11 | const operationCounts = { 12 | append: 0, 13 | update: 0, 14 | delete: 0, 15 | }; 16 | 17 | for (const op of params.operations) { 18 | let response; 19 | 20 | switch (op.operation) { 21 | case "append": 22 | response = await notion.blocks.children.append({ 23 | block_id: op.blockId, 24 | children: op.children, 25 | }); 26 | operationCounts.append++; 27 | break; 28 | 29 | case "update": 30 | response = await notion.blocks.update({ 31 | block_id: op.blockId, 32 | ...op.data, 33 | }); 34 | operationCounts.update++; 35 | break; 36 | 37 | case "delete": 38 | response = await notion.blocks.delete({ 39 | block_id: op.blockId, 40 | }); 41 | operationCounts.delete++; 42 | break; 43 | } 44 | 45 | results.push({ 46 | operation: op.operation, 47 | blockId: op.blockId, 48 | success: true, 49 | response, 50 | }); 51 | } 52 | 53 | return { 54 | content: [ 55 | { 56 | type: "text", 57 | text: `Successfully performed ${params.operations.length} operations (${operationCounts.append} append, ${operationCounts.update} update, ${operationCounts.delete} delete)`, 58 | }, 59 | { 60 | type: "text", 61 | text: JSON.stringify(results, null, 2), 62 | }, 63 | ], 64 | }; 65 | } catch (error) { 66 | return handleNotionError(error); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /src/tools/index.ts: -------------------------------------------------------------------------------- 1 | import { server } from "../server/index.js"; 2 | import { PAGES_OPERATION_SCHEMA } from "../schema/page.js"; 3 | import { BLOCKS_OPERATION_SCHEMA } from "../schema/blocks.js"; 4 | import { DATABASE_OPERATION_SCHEMA } from "../schema/database.js"; 5 | import { COMMENTS_OPERATION_SCHEMA } from "../schema/comments.js"; 6 | import { USERS_OPERATION_SCHEMA } from "../schema/users.js"; 7 | import { registerPagesOperationTool } from "./pages.js"; 8 | import { registerBlocksOperationTool } from "./blocks.js"; 9 | import { registerDatabaseOperationTool } from "./database.js"; 10 | import { registerCommentsOperationTool } from "./comments.js"; 11 | import { registerUsersOperationTool } from "./users.js"; 12 | 13 | export const registerAllTools = () => { 14 | // Register combined pages operation tool 15 | server.tool( 16 | "notion_pages", 17 | "Perform various page operations (create, archive, restore, search, update)", 18 | PAGES_OPERATION_SCHEMA, 19 | registerPagesOperationTool 20 | ); 21 | 22 | // Register combined blocks operation tool 23 | server.tool( 24 | "notion_blocks", 25 | "Perform various block operations (retrieve, update, delete, append children, batch operations)", 26 | BLOCKS_OPERATION_SCHEMA, 27 | registerBlocksOperationTool 28 | ); 29 | 30 | // Register combined database operation tool 31 | server.tool( 32 | "notion_database", 33 | "Perform various database operations (create, query, update)", 34 | DATABASE_OPERATION_SCHEMA, 35 | registerDatabaseOperationTool 36 | ); 37 | 38 | // Register combined comments operation tool 39 | server.tool( 40 | "notion_comments", 41 | "Perform various comment operations (get, add to page, add to discussion)", 42 | COMMENTS_OPERATION_SCHEMA, 43 | registerCommentsOperationTool 44 | ); 45 | 46 | // Register combined users operation tool 47 | server.tool( 48 | "notion_users", 49 | "Perform various user operations (list, get, get bot)", 50 | USERS_OPERATION_SCHEMA, 51 | registerUsersOperationTool 52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/tools/blocks.ts: -------------------------------------------------------------------------------- 1 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 2 | import { handleNotionError } from "../utils/error.js"; 3 | import { BlocksOperationParams } from "../types/blocks.js"; 4 | import { appendBlockChildren } from "./appendBlockChildren.js"; 5 | import { retrieveBlock } from "./retrieveBlock.js"; 6 | import { retrieveBlockChildren } from "./retrieveBlockChildren.js"; 7 | import { updateBlock } from "./updateBlock.js"; 8 | import { deleteBlock } from "./deleteBlock.js"; 9 | import { batchAppendBlockChildren } from "./batchAppendBlockChildren.js"; 10 | import { batchUpdateBlocks } from "./batchUpdateBlocks.js"; 11 | import { batchDeleteBlocks } from "./batchDeleteBlocks.js"; 12 | import { batchMixedOperations } from "./batchMixedOperations.js"; 13 | 14 | export const registerBlocksOperationTool = async ( 15 | params: BlocksOperationParams 16 | ): Promise => { 17 | switch (params.payload.action) { 18 | case "append_block_children": 19 | return appendBlockChildren(params.payload.params); 20 | case "retrieve_block": 21 | return retrieveBlock(params.payload.params); 22 | case "retrieve_block_children": 23 | return retrieveBlockChildren(params.payload.params); 24 | case "update_block": 25 | return updateBlock(params.payload.params); 26 | case "delete_block": 27 | return deleteBlock(params.payload.params); 28 | case "batch_append_block_children": 29 | return batchAppendBlockChildren(params.payload.params); 30 | case "batch_update_blocks": 31 | return batchUpdateBlocks(params.payload.params); 32 | case "batch_delete_blocks": 33 | return batchDeleteBlocks(params.payload.params); 34 | case "batch_mixed_operations": 35 | return batchMixedOperations(params.payload.params); 36 | default: 37 | return handleNotionError( 38 | new Error( 39 | `Unsupported action, use one of the following: "append_block_children", "retrieve_block", "retrieve_block_children", "update_block", "delete_block", "batch_append_block_children", "batch_update_blocks", "batch_delete_blocks", "batch_mixed_operations"` 40 | ) 41 | ); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /src/types/blocks.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { 3 | APPEND_BLOCK_CHILDREN_SCHEMA, 4 | RETRIEVE_BLOCK_SCHEMA, 5 | RETRIEVE_BLOCK_CHILDREN_SCHEMA, 6 | UPDATE_BLOCK_SCHEMA, 7 | DELETE_BLOCK_SCHEMA, 8 | BATCH_APPEND_BLOCK_CHILDREN_SCHEMA, 9 | BATCH_UPDATE_BLOCKS_SCHEMA, 10 | BATCH_DELETE_BLOCKS_SCHEMA, 11 | BATCH_MIXED_OPERATIONS_SCHEMA, 12 | BLOCKS_OPERATION_SCHEMA, 13 | } from "../schema/blocks.js"; 14 | 15 | export const appendBlockChildrenSchema = z.object(APPEND_BLOCK_CHILDREN_SCHEMA); 16 | export type AppendBlockChildrenParams = z.infer< 17 | typeof appendBlockChildrenSchema 18 | >; 19 | 20 | export const retrieveBlockSchema = z.object(RETRIEVE_BLOCK_SCHEMA); 21 | export type RetrieveBlockParams = z.infer; 22 | 23 | export const retrieveBlockChildrenSchema = z.object( 24 | RETRIEVE_BLOCK_CHILDREN_SCHEMA 25 | ); 26 | export type RetrieveBlockChildrenParams = z.infer< 27 | typeof retrieveBlockChildrenSchema 28 | >; 29 | 30 | export const updateBlockSchema = z.object(UPDATE_BLOCK_SCHEMA); 31 | export type UpdateBlockParams = z.infer; 32 | 33 | export const deleteBlockSchema = z.object(DELETE_BLOCK_SCHEMA); 34 | export type DeleteBlockParams = z.infer; 35 | 36 | export const batchAppendBlockChildrenSchema = z.object( 37 | BATCH_APPEND_BLOCK_CHILDREN_SCHEMA 38 | ); 39 | export type BatchAppendBlockChildrenParams = z.infer< 40 | typeof batchAppendBlockChildrenSchema 41 | >; 42 | 43 | export const batchUpdateBlocksSchema = z.object(BATCH_UPDATE_BLOCKS_SCHEMA); 44 | export type BatchUpdateBlocksParams = z.infer; 45 | 46 | export const batchDeleteBlocksSchema = z.object(BATCH_DELETE_BLOCKS_SCHEMA); 47 | export type BatchDeleteBlocksParams = z.infer; 48 | 49 | export const batchMixedOperationsSchema = z.object( 50 | BATCH_MIXED_OPERATIONS_SCHEMA 51 | ); 52 | export type BatchMixedOperationsParams = z.infer< 53 | typeof batchMixedOperationsSchema 54 | >; 55 | 56 | export const blocksOperationSchema = z.object(BLOCKS_OPERATION_SCHEMA); 57 | export type BlocksOperationParams = z.infer; 58 | -------------------------------------------------------------------------------- /src/schema/page-properties.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { 3 | RICH_TEXT_ITEM_REQUEST_SCHEMA, 4 | TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA, 5 | } from "./rich-text.js"; 6 | 7 | export const CHECKBOX_PROPERTY_VALUE_SCHEMA = z.object({ 8 | checkbox: z.boolean(), 9 | }); 10 | 11 | export const DATE_PROPERTY_VALUE_SCHEMA = z.object({ 12 | date: z.object({ 13 | start: z.string(), 14 | end: z.string().optional(), 15 | }), 16 | }); 17 | 18 | export const EMAIL_PROPERTY_VALUE_SCHEMA = z.object({ 19 | email: z.string().email(), 20 | }); 21 | 22 | export const FILES_PROPERTY_VALUE_SCHEMA = z.object({ 23 | files: z.array( 24 | z.object({ 25 | name: z.string(), 26 | external: z.object({ 27 | url: z.string().url(), 28 | }), 29 | }) 30 | ), 31 | }); 32 | 33 | export const MULTI_SELECT_PROPERTY_VALUE_SCHEMA = z.object({ 34 | multi_select: z.array( 35 | z.object({ 36 | id: z.string().optional(), 37 | name: z.string().optional(), 38 | }) 39 | ), 40 | }); 41 | 42 | export const NUMBER_PROPERTY_VALUE_SCHEMA = z.object({ number: z.number() }); 43 | 44 | export const PEOPLE_PROPERTY_VALUE_SCHEMA = z.object({ 45 | people: z.array( 46 | z.object({ 47 | object: z.literal("user"), 48 | id: z.string(), 49 | }) 50 | ), 51 | }); 52 | 53 | export const PHONE_NUMBER_PROPERTY_VALUE_SCHEMA = z.object({ 54 | phone_number: z.string(), 55 | }); 56 | 57 | export const RELATION_PROPERTY_VALUE_SCHEMA = z.object({ 58 | relation: z.array( 59 | z.object({ 60 | id: z.string(), 61 | }) 62 | ), 63 | }); 64 | 65 | export const RICH_TEXT_PROPERTY_VALUE_SCHEMA = z.object({ 66 | rich_text: z.array(RICH_TEXT_ITEM_REQUEST_SCHEMA), 67 | }); 68 | 69 | export const SELECT_PROPERTY_VALUE_SCHEMA = z.object({ 70 | select: z.object({ 71 | name: z.string(), 72 | }), 73 | }); 74 | 75 | export const STATUS_PROPERTY_VALUE_SCHEMA = z.object({ 76 | status: z.object({ name: z.string() }), 77 | }); 78 | 79 | export const TITLE_PROPERTY_VALUE_SCHEMA = z.object({ 80 | title: z.array(TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA), 81 | }); 82 | 83 | export const URL_PROPERTY_VALUE_SCHEMA = z.object({ 84 | url: z.string().url(), 85 | }); 86 | -------------------------------------------------------------------------------- /src/schema/comments.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { RICH_TEXT_ITEM_REQUEST_SCHEMA } from "./rich-text.js"; 3 | import { preprocessJson } from "./preprocess.js"; 4 | // Schema for getting comments 5 | export const GET_COMMENTS_SCHEMA = { 6 | block_id: z 7 | .string() 8 | .describe("The ID of the block or page to get comments from"), 9 | start_cursor: z 10 | .string() 11 | .optional() 12 | .describe("The cursor to start from for pagination"), 13 | page_size: z 14 | .number() 15 | .optional() 16 | .describe("Number of comments to return per page"), 17 | }; 18 | 19 | // Schema for adding a comment to a page 20 | export const ADD_PAGE_COMMENT_SCHEMA = { 21 | parent: z.object({ 22 | page_id: z.string().describe("The ID of the page to add the comment to"), 23 | }), 24 | rich_text: z 25 | .array(RICH_TEXT_ITEM_REQUEST_SCHEMA) 26 | .describe("Rich text content for the comment"), 27 | }; 28 | 29 | // Schema for adding a comment to a discussion 30 | export const ADD_DISCUSSION_COMMENT_SCHEMA = { 31 | discussion_id: z 32 | .string() 33 | .describe("The ID of the discussion to add the comment to"), 34 | rich_text: z 35 | .array(RICH_TEXT_ITEM_REQUEST_SCHEMA) 36 | .describe("Rich text content for the comment"), 37 | }; 38 | 39 | // Combined schema for all comment operations 40 | export const COMMENTS_OPERATION_SCHEMA = { 41 | payload: z 42 | .preprocess( 43 | preprocessJson, 44 | z.discriminatedUnion("action", [ 45 | z.object({ 46 | action: z 47 | .literal("get_comments") 48 | .describe("Use this action to get comments from a block or page."), 49 | params: z.object(GET_COMMENTS_SCHEMA), 50 | }), 51 | z.object({ 52 | action: z 53 | .literal("add_page_comment") 54 | .describe("Use this action to add a comment to a page."), 55 | params: z.object(ADD_PAGE_COMMENT_SCHEMA), 56 | }), 57 | z.object({ 58 | action: z 59 | .literal("add_discussion_comment") 60 | .describe("Use this action to add a comment to a discussion."), 61 | params: z.object(ADD_DISCUSSION_COMMENT_SCHEMA), 62 | }), 63 | ]) 64 | ) 65 | .describe( 66 | "A union of all possible comment operations. Each operation has a specific action and corresponding parameters. Use this schema to validate the input for comment operations such as getting, adding to page, and adding to discussion. Available actions include: 'get_comments', 'add_page_comment', and 'add_discussion_comment'. Each operation requires specific parameters as defined in the corresponding schemas." 67 | ), 68 | }; 69 | -------------------------------------------------------------------------------- /src/tools/users.ts: -------------------------------------------------------------------------------- 1 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 2 | import { notion } from "../services/notion.js"; 3 | import { handleNotionError } from "../utils/error.js"; 4 | import { 5 | GetUserParams, 6 | ListUsersParams, 7 | UsersOperationParams, 8 | } from "../types/users.js"; 9 | 10 | export const registerGetListUsersTool = async ( 11 | params: ListUsersParams 12 | ): Promise => { 13 | try { 14 | const response = await notion.users.list(params); 15 | 16 | return { 17 | content: [ 18 | { 19 | type: "text", 20 | text: `Users retrieved successfully: ${response.results.length}`, 21 | }, 22 | { 23 | type: "text", 24 | text: JSON.stringify(response, null, 2), 25 | }, 26 | ], 27 | }; 28 | } catch (error) { 29 | return handleNotionError(error); 30 | } 31 | }; 32 | 33 | export const registerGetUserTool = async ( 34 | params: GetUserParams 35 | ): Promise => { 36 | try { 37 | const response = await notion.users.retrieve(params); 38 | 39 | return { 40 | content: [ 41 | { 42 | type: "text", 43 | text: `User retrieved successfully: ${response.id}`, 44 | }, 45 | { 46 | type: "text", 47 | text: JSON.stringify(response, null, 2), 48 | }, 49 | ], 50 | }; 51 | } catch (error) { 52 | return handleNotionError(error); 53 | } 54 | }; 55 | 56 | export const registerGetBotUserTool = async (): Promise => { 57 | try { 58 | const response = await notion.users.me({}); 59 | 60 | return { 61 | content: [ 62 | { 63 | type: "text", 64 | text: `Bot user retrieved successfully: ${response.id}`, 65 | }, 66 | { 67 | type: "text", 68 | text: JSON.stringify(response, null, 2), 69 | }, 70 | ], 71 | }; 72 | } catch (error) { 73 | return handleNotionError(error); 74 | } 75 | }; 76 | 77 | // Combined tool function that handles all user operations 78 | export const registerUsersOperationTool = async ( 79 | params: UsersOperationParams 80 | ): Promise => { 81 | switch (params.payload.action) { 82 | case "list_users": 83 | return registerGetListUsersTool(params.payload.params); 84 | case "get_user": 85 | return registerGetUserTool(params.payload.params); 86 | case "get_bot_user": 87 | return registerGetBotUserTool(); 88 | default: 89 | return handleNotionError( 90 | new Error( 91 | `Unsupported action, use one of the following: "list_users", "get_user", "get_bot_user"` 92 | ) 93 | ); 94 | } 95 | }; 96 | -------------------------------------------------------------------------------- /src/tools/comments.ts: -------------------------------------------------------------------------------- 1 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 2 | import { notion } from "../services/notion.js"; 3 | import { 4 | AddDiscussionCommentParams, 5 | AddPageCommentParams, 6 | CommentsOperationParams, 7 | GetCommentsParams, 8 | } from "../types/comments.js"; 9 | import { handleNotionError } from "../utils/error.js"; 10 | 11 | const registerGetCommentsTool = async ( 12 | params: GetCommentsParams 13 | ): Promise => { 14 | try { 15 | const response = await notion.comments.list(params); 16 | 17 | return { 18 | content: [ 19 | { 20 | type: "text", 21 | text: `Comments retrieved successfully: ${response.results.length}`, 22 | }, 23 | { 24 | type: "text", 25 | text: JSON.stringify(response, null, 2), 26 | }, 27 | ], 28 | }; 29 | } catch (error) { 30 | return handleNotionError(error); 31 | } 32 | }; 33 | 34 | const registerAddPageCommentTool = async ( 35 | params: AddPageCommentParams 36 | ): Promise => { 37 | try { 38 | const response = await notion.comments.create(params); 39 | 40 | return { 41 | content: [ 42 | { 43 | type: "text", 44 | text: `Comment created successfully: ${response.id}`, 45 | }, 46 | { 47 | type: "text", 48 | text: JSON.stringify(response, null, 2), 49 | }, 50 | ], 51 | }; 52 | } catch (error) { 53 | return handleNotionError(error); 54 | } 55 | }; 56 | 57 | const registerAddDiscussionCommentTool = async ( 58 | params: AddDiscussionCommentParams 59 | ): Promise => { 60 | try { 61 | const response = await notion.comments.create(params); 62 | 63 | return { 64 | content: [ 65 | { 66 | type: "text", 67 | text: `Comment created successfully: ${response.id}`, 68 | }, 69 | { 70 | type: "text", 71 | text: JSON.stringify(response, null, 2), 72 | }, 73 | ], 74 | }; 75 | } catch (error) { 76 | return handleNotionError(error); 77 | } 78 | }; 79 | 80 | // Combined tool function that handles all comment operations 81 | export const registerCommentsOperationTool = async ( 82 | params: CommentsOperationParams 83 | ): Promise => { 84 | try { 85 | const { payload } = params; 86 | 87 | switch (payload.action) { 88 | case "get_comments": 89 | return registerGetCommentsTool(payload.params); 90 | case "add_page_comment": 91 | return registerAddPageCommentTool(payload.params); 92 | case "add_discussion_comment": 93 | return registerAddDiscussionCommentTool(payload.params); 94 | default: 95 | throw new Error( 96 | `Unsupported action, use one of the following: "get_comments", "add_page_comment", "add_discussion_comment"` 97 | ); 98 | } 99 | } catch (error) { 100 | return handleNotionError(error); 101 | } 102 | }; 103 | -------------------------------------------------------------------------------- /src/schema/rich-text.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ANNOTATIONS_SCHEMA } from "./annotations.js"; 3 | import { MENTION_REQUEST_SCHEMA } from "./mention.js"; 4 | 5 | export const RICH_TEXT_BASE_SCHEMA = z.object({ 6 | plain_text: z.string().describe("Plain text content without formatting"), 7 | href: z.string().nullable().describe("URL for the link"), 8 | annotations: ANNOTATIONS_SCHEMA.describe("Text formatting annotations"), 9 | }); 10 | 11 | export const RICH_TEXT_BASE_REQUEST_SCHEMA = z.object({ 12 | annotations: ANNOTATIONS_SCHEMA.optional().describe( 13 | "Text formatting annotations" 14 | ), 15 | }); 16 | 17 | export const TEXT_CONTENT_REQUEST_SCHEMA = z 18 | .object({ 19 | content: z.string().describe("The actual text content"), 20 | link: z 21 | .object({ 22 | url: z.string().url().describe("URL for the link"), 23 | }) 24 | .optional() 25 | .nullable() 26 | .describe("Optional link associated with the text"), 27 | }) 28 | .describe("Text content request object"); 29 | 30 | export const EQUATION_CONTENT_SCHEMA = z 31 | .object({ 32 | expression: z.string().describe("LaTeX equation expression"), 33 | }) 34 | .describe("Equation content object"); 35 | 36 | export const TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA = z 37 | .object({ 38 | type: z.literal("text").describe("Type of rich text content"), 39 | text: TEXT_CONTENT_REQUEST_SCHEMA.describe("Text content"), 40 | annotations: ANNOTATIONS_SCHEMA.optional().describe( 41 | "Text formatting annotations" 42 | ), 43 | plain_text: z 44 | .string() 45 | .optional() 46 | .describe("Plain text content without formatting"), 47 | href: z.string().nullable().optional().describe("URL for the link"), 48 | }) 49 | .describe("Text rich text item request"); 50 | 51 | export const EQUATION_RICH_TEXT_ITEM_REQUEST_SCHEMA = z 52 | .object({ 53 | type: z.literal("equation").describe("Type of equation content"), 54 | equation: EQUATION_CONTENT_SCHEMA.describe("Equation content"), 55 | annotations: ANNOTATIONS_SCHEMA.optional().describe( 56 | "Text formatting annotations" 57 | ), 58 | plain_text: z 59 | .string() 60 | .optional() 61 | .describe("Plain text content without formatting"), 62 | href: z.string().nullable().optional().describe("URL for the link"), 63 | }) 64 | .describe("Equation rich text item request"); 65 | 66 | export const MENTION_RICH_TEXT_ITEM_REQUEST_SCHEMA = z 67 | .object({ 68 | type: z.literal("mention").describe("Type of mention content"), 69 | mention: MENTION_REQUEST_SCHEMA.describe("Mention content"), 70 | annotations: ANNOTATIONS_SCHEMA.optional().describe( 71 | "Text formatting annotations" 72 | ), 73 | plain_text: z 74 | .string() 75 | .optional() 76 | .describe("Plain text content without formatting"), 77 | href: z.string().nullable().optional().describe("URL for the link"), 78 | }) 79 | .describe("Mention rich text item request"); 80 | 81 | export const RICH_TEXT_ITEM_REQUEST_SCHEMA = z 82 | .discriminatedUnion("type", [ 83 | TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA, 84 | EQUATION_RICH_TEXT_ITEM_REQUEST_SCHEMA, 85 | MENTION_RICH_TEXT_ITEM_REQUEST_SCHEMA, 86 | ]) 87 | .describe("Union of all possible rich text item request types"); 88 | -------------------------------------------------------------------------------- /src/schema/mention.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const TEMPLATE_MENTION_USER_REQUEST_SCHEMA = z 4 | .object({ 5 | type: z 6 | .literal("template_mention") 7 | .describe("Specifies this is a template mention type"), 8 | template_mention: z 9 | .object({ 10 | type: z 11 | .literal("template_mention_user") 12 | .describe("Specifies this is a template mention user type"), 13 | template_mention_user: z 14 | .literal("me") 15 | .describe("Template mention user value"), 16 | }) 17 | .describe("Contains the template mention user information"), 18 | }) 19 | .describe("Schema for a template mention user block request"); 20 | 21 | export const TEMPLATE_MENTION_DATE_REQUEST_SCHEMA = z 22 | .object({ 23 | type: z 24 | .literal("template_mention") 25 | .describe("Specifies this is a template mention type"), 26 | template_mention: z 27 | .object({ 28 | type: z 29 | .literal("template_mention_date") 30 | .describe("Specifies this is a template mention date type"), 31 | template_mention_date: z 32 | .literal("today") 33 | .describe("Template mention date value"), 34 | }) 35 | .describe("Contains the template mention date information"), 36 | }) 37 | .describe("Schema for a template mention date block request"); 38 | 39 | export const USER_MENTION_REQUEST_SCHEMA = z 40 | .object({ 41 | type: z.literal("user").describe("Specifies this is a user mention type"), 42 | user: z 43 | .object({ 44 | id: z 45 | .string() 46 | .describe("The unique ID that identifies this specific user"), 47 | object: z 48 | .literal("user") 49 | .optional() 50 | .describe("Identifies this object as a user type"), 51 | }) 52 | .describe("Contains the user reference information"), 53 | }) 54 | .describe("Schema for a user mention block request"); 55 | 56 | export const PAGE_MENTION_REQUEST_SCHEMA = z 57 | .object({ 58 | type: z.literal("page").describe("Specifies this is a page mention type"), 59 | page: z 60 | .object({ 61 | id: z 62 | .string() 63 | .describe("The unique ID that identifies this specific page"), 64 | }) 65 | .describe("Contains the page reference information"), 66 | }) 67 | .describe("Schema for a page mention block request"); 68 | 69 | export const DATABASE_MENTION_REQUEST_SCHEMA = z 70 | .object({ 71 | type: z 72 | .literal("database") 73 | .describe("Specifies this is a database mention type"), 74 | database: z 75 | .object({ 76 | id: z 77 | .string() 78 | .describe("The unique ID that identifies this specific database"), 79 | }) 80 | .describe("Contains the database reference information"), 81 | }) 82 | .describe("Schema for a database mention block request"); 83 | 84 | export const DATE_MENTION_REQUEST_SCHEMA = z 85 | .object({ 86 | type: z.literal("date").describe("Specifies this is a date mention type"), 87 | date: z 88 | .object({ 89 | start: z.string().describe("The start date in YYYY-MM-DD format"), 90 | end: z 91 | .string() 92 | .nullable() 93 | .optional() 94 | .describe("The optional end date in YYYY-MM-DD format"), 95 | }) 96 | .describe("Contains the date information"), 97 | }) 98 | .describe("Schema for a date mention block request"); 99 | 100 | export const MENTION_REQUEST_SCHEMA = z.preprocess( 101 | (val) => (typeof val === "string" ? JSON.parse(val) : val), 102 | z 103 | .union([ 104 | DATE_MENTION_REQUEST_SCHEMA, 105 | USER_MENTION_REQUEST_SCHEMA, 106 | PAGE_MENTION_REQUEST_SCHEMA, 107 | DATABASE_MENTION_REQUEST_SCHEMA, 108 | TEMPLATE_MENTION_USER_REQUEST_SCHEMA, 109 | TEMPLATE_MENTION_DATE_REQUEST_SCHEMA, 110 | ]) 111 | .describe("Union of all possible mention request types") 112 | ); 113 | -------------------------------------------------------------------------------- /src/utils/error.ts: -------------------------------------------------------------------------------- 1 | import { ErrorCode } from "@modelcontextprotocol/sdk/types.js"; 2 | import { APIResponseError } from "@notionhq/client"; 3 | import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; 4 | 5 | /** 6 | * Error codes from Notion API 7 | * @see https://developers.notion.com/reference/status-codes#error-codes 8 | */ 9 | export enum NotionErrorCode { 10 | // 400 errors 11 | InvalidJson = "invalid_json", 12 | InvalidRequestUrl = "invalid_request_url", 13 | InvalidRequest = "invalid_request", 14 | ValidationError = "validation_error", 15 | MissingVersion = "missing_version", 16 | UnsupportedVersion = "unsupported_version", 17 | UnsupportedExport = "unsupported_export", 18 | UnsupportedJsonType = "unsupported_json_type", 19 | UnsupportedJsonKey = "unsupported_json_key", 20 | // 401 errors 21 | Unauthorized = "unauthorized", 22 | InvalidApiKey = "invalid_api_key", 23 | // 403 errors 24 | RestrictedResource = "restricted_resource", 25 | InsufficientPermissions = "insufficient_permissions", 26 | // 404 errors 27 | ObjectNotFound = "object_not_found", 28 | // 409 errors 29 | ConflictError = "conflict_error", 30 | AlreadyExists = "already_exists", 31 | // 429 errors 32 | RateLimited = "rate_limited", 33 | // 500 errors 34 | InternalServerError = "internal_server_error", 35 | // 503 errors 36 | ServiceUnavailable = "service_unavailable", 37 | DatabaseConnectionUnavailable = "database_connection_unavailable", 38 | } 39 | 40 | /** 41 | * Map of error messages for specific Notion error codes 42 | */ 43 | const ERROR_MESSAGES: Record = { 44 | // 400 errors 45 | [NotionErrorCode.InvalidJson]: 46 | "The request body could not be decoded as JSON", 47 | [NotionErrorCode.InvalidRequestUrl]: "The request URL is not valid", 48 | [NotionErrorCode.InvalidRequest]: "This request is not supported", 49 | [NotionErrorCode.ValidationError]: 50 | "The request body does not match the schema for the expected parameters", 51 | [NotionErrorCode.MissingVersion]: 52 | "The request is missing the required Notion-Version header", 53 | [NotionErrorCode.UnsupportedVersion]: 54 | "The specified version is not supported", 55 | [NotionErrorCode.UnsupportedExport]: 56 | "The specified export type is not supported", 57 | [NotionErrorCode.UnsupportedJsonType]: 58 | "The specified JSON type is not supported", 59 | [NotionErrorCode.UnsupportedJsonKey]: 60 | "The specified JSON key is not supported", 61 | // 401 errors 62 | [NotionErrorCode.Unauthorized]: "The bearer token is not valid", 63 | [NotionErrorCode.InvalidApiKey]: "The API key is invalid", 64 | // 403 errors 65 | [NotionErrorCode.RestrictedResource]: 66 | "The resource is restricted and cannot be accessed with this token", 67 | [NotionErrorCode.InsufficientPermissions]: 68 | "The bearer token does not have permission to perform this operation", 69 | // 404 errors 70 | [NotionErrorCode.ObjectNotFound]: "The requested resource does not exist", 71 | // 409 errors 72 | [NotionErrorCode.ConflictError]: 73 | "The transaction could not be completed due to a conflict", 74 | [NotionErrorCode.AlreadyExists]: "The resource already exists", 75 | // 429 errors 76 | [NotionErrorCode.RateLimited]: 77 | "The request was rate limited. Retry later with exponential backoff", 78 | // 500 errors 79 | [NotionErrorCode.InternalServerError]: 80 | "An unexpected error occurred on the Notion servers", 81 | // 503 errors 82 | [NotionErrorCode.ServiceUnavailable]: 83 | "The Notion service is unavailable. Retry later with exponential backoff", 84 | [NotionErrorCode.DatabaseConnectionUnavailable]: 85 | "The database connection is unavailable. Retry later with exponential backoff", 86 | }; 87 | 88 | /** 89 | * Get a more descriptive error message for a given Notion error code 90 | */ 91 | function getErrorMessage( 92 | notionErrorCode: string, 93 | defaultMessage?: string 94 | ): string { 95 | return ( 96 | ERROR_MESSAGES[notionErrorCode] || 97 | defaultMessage || 98 | "An unknown error occurred" 99 | ); 100 | } 101 | 102 | /** 103 | * Handles a Notion API error and returns an appropriate CallToolResult with error details 104 | */ 105 | export function handleNotionError(error: unknown): CallToolResult { 106 | if (error instanceof APIResponseError) { 107 | const code = error.code as string; 108 | const message = getErrorMessage(code, error.message); 109 | 110 | // Instead of mapping to specific error codes, just use InternalError as a fallback 111 | return { 112 | content: [ 113 | { 114 | type: "text", 115 | text: `Error: ${message} (${code})`, 116 | }, 117 | ], 118 | error: { 119 | code: ErrorCode.InternalError, 120 | message, 121 | }, 122 | }; 123 | } 124 | 125 | if (error instanceof Error) { 126 | return { 127 | content: [ 128 | { 129 | type: "text", 130 | text: `Error: ${error.message}`, 131 | }, 132 | ], 133 | error: { 134 | code: ErrorCode.InternalError, 135 | message: error.message, 136 | }, 137 | }; 138 | } 139 | 140 | // Handle unknown errors 141 | return { 142 | content: [ 143 | { 144 | type: "text", 145 | text: `Error: ${String(error)}`, 146 | }, 147 | ], 148 | error: { 149 | code: ErrorCode.InternalError, 150 | message: String(error), 151 | }, 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /src/schema/page.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { getRootPageId } from "../services/notion.js"; 3 | import { ICON_SCHEMA } from "./icon.js"; 4 | import { TEXT_BLOCK_REQUEST_SCHEMA } from "./blocks.js"; 5 | import { preprocessJson } from "./preprocess.js"; 6 | import { TEXT_CONTENT_REQUEST_SCHEMA } from "./rich-text.js"; 7 | import { FILE_SCHEMA } from "./file.js"; 8 | import { 9 | CHECKBOX_PROPERTY_VALUE_SCHEMA, 10 | DATE_PROPERTY_VALUE_SCHEMA, 11 | EMAIL_PROPERTY_VALUE_SCHEMA, 12 | FILES_PROPERTY_VALUE_SCHEMA, 13 | NUMBER_PROPERTY_VALUE_SCHEMA, 14 | PEOPLE_PROPERTY_VALUE_SCHEMA, 15 | PHONE_NUMBER_PROPERTY_VALUE_SCHEMA, 16 | RELATION_PROPERTY_VALUE_SCHEMA, 17 | RICH_TEXT_PROPERTY_VALUE_SCHEMA, 18 | SELECT_PROPERTY_VALUE_SCHEMA, 19 | STATUS_PROPERTY_VALUE_SCHEMA, 20 | } from "./page-properties.js"; 21 | 22 | export const TITLE_PROPERTY_SCHEMA = z.object({ 23 | title: z 24 | .array( 25 | z.object({ 26 | text: TEXT_CONTENT_REQUEST_SCHEMA.describe( 27 | "Text content for title segment" 28 | ), 29 | }) 30 | ) 31 | .describe("Array of text segments that make up the title"), 32 | }); 33 | 34 | export const PARENT_SCHEMA = z.preprocess( 35 | (val) => (typeof val === "string" ? JSON.parse(val) : val), 36 | z.union([ 37 | z.object({ 38 | type: z.literal("page_id").describe("Parent type for page"), 39 | page_id: z.string().describe("ID of the parent page"), 40 | }), 41 | z.object({ 42 | type: z.literal("database_id").describe("Parent type for database"), 43 | database_id: z.string().describe("ID of the parent database"), 44 | }), 45 | ]) 46 | ); 47 | 48 | export const CREATE_PAGE_SCHEMA = { 49 | parent: PARENT_SCHEMA.optional() 50 | .default({ 51 | type: "page_id", 52 | page_id: getRootPageId(), 53 | }) 54 | .describe( 55 | "Optional parent - if not provided, will use NOTION_PAGE_ID as parent page" 56 | ), 57 | properties: z 58 | .record( 59 | z.string().describe("Property name"), 60 | z.union([ 61 | TITLE_PROPERTY_SCHEMA, 62 | CHECKBOX_PROPERTY_VALUE_SCHEMA, 63 | EMAIL_PROPERTY_VALUE_SCHEMA, 64 | STATUS_PROPERTY_VALUE_SCHEMA, 65 | FILES_PROPERTY_VALUE_SCHEMA, 66 | DATE_PROPERTY_VALUE_SCHEMA, 67 | PEOPLE_PROPERTY_VALUE_SCHEMA, 68 | PHONE_NUMBER_PROPERTY_VALUE_SCHEMA, 69 | RELATION_PROPERTY_VALUE_SCHEMA, 70 | RICH_TEXT_PROPERTY_VALUE_SCHEMA, 71 | SELECT_PROPERTY_VALUE_SCHEMA, 72 | NUMBER_PROPERTY_VALUE_SCHEMA, 73 | ]) 74 | ) 75 | .describe("Properties of the page"), 76 | children: z 77 | .array(TEXT_BLOCK_REQUEST_SCHEMA) 78 | .optional() 79 | .describe("Optional array of paragraph blocks to add as page content"), 80 | icon: z.preprocess( 81 | preprocessJson, 82 | ICON_SCHEMA.nullable().optional().describe("Optional icon for the page") 83 | ), 84 | cover: z.preprocess( 85 | preprocessJson, 86 | FILE_SCHEMA.nullable() 87 | .optional() 88 | .describe("Optional cover image for the page") 89 | ), 90 | }; 91 | 92 | export const ARCHIVE_PAGE_SCHEMA = { 93 | pageId: z.string().describe("The ID of the page to archive"), 94 | }; 95 | 96 | export const RESTORE_PAGE_SCHEMA = { 97 | pageId: z.string().describe("The ID of the page to restore"), 98 | }; 99 | 100 | export const UPDATE_PAGE_PROPERTIES_SCHEMA = { 101 | pageId: z.string().describe("The ID of the page to restore"), 102 | properties: z 103 | .record( 104 | z.string().describe("Property name"), 105 | z.union([ 106 | TITLE_PROPERTY_SCHEMA, 107 | CHECKBOX_PROPERTY_VALUE_SCHEMA, 108 | EMAIL_PROPERTY_VALUE_SCHEMA, 109 | STATUS_PROPERTY_VALUE_SCHEMA, 110 | FILES_PROPERTY_VALUE_SCHEMA, 111 | DATE_PROPERTY_VALUE_SCHEMA, 112 | PEOPLE_PROPERTY_VALUE_SCHEMA, 113 | PHONE_NUMBER_PROPERTY_VALUE_SCHEMA, 114 | RELATION_PROPERTY_VALUE_SCHEMA, 115 | RICH_TEXT_PROPERTY_VALUE_SCHEMA, 116 | SELECT_PROPERTY_VALUE_SCHEMA, 117 | NUMBER_PROPERTY_VALUE_SCHEMA, 118 | ]) 119 | ) 120 | .describe("Properties of the page"), 121 | }; 122 | 123 | export const SEARCH_PAGES_SCHEMA = { 124 | query: z.string().optional().describe("Search query for filtering by title"), 125 | sort: z 126 | .object({ 127 | direction: z.enum(["ascending", "descending"]), 128 | timestamp: z.literal("last_edited_time"), 129 | }) 130 | .optional() 131 | .describe("Sort order for results"), 132 | start_cursor: z.string().optional().describe("Cursor for pagination"), 133 | page_size: z 134 | .number() 135 | .min(1) 136 | .max(100) 137 | .optional() 138 | .describe("Number of results to return (1-100)"), 139 | }; 140 | 141 | // Combined schema for all page operations 142 | export const PAGES_OPERATION_SCHEMA = { 143 | payload: z 144 | .preprocess( 145 | preprocessJson, 146 | z.discriminatedUnion("action", [ 147 | z.object({ 148 | action: z 149 | .literal("create_page") 150 | .describe("Use this action to create a new page in the database."), 151 | params: z.object(CREATE_PAGE_SCHEMA), 152 | }), 153 | z.object({ 154 | action: z 155 | .literal("archive_page") 156 | .describe( 157 | "Use this action to archive an existing page, making it inactive." 158 | ), 159 | params: z.object(ARCHIVE_PAGE_SCHEMA), 160 | }), 161 | z.object({ 162 | action: z 163 | .literal("restore_page") 164 | .describe("Use this action to restore a previously archived page."), 165 | params: z.object(RESTORE_PAGE_SCHEMA), 166 | }), 167 | z.object({ 168 | action: z 169 | .literal("search_pages") 170 | .describe("Use this action to search for pages based on a query."), 171 | params: z.object(SEARCH_PAGES_SCHEMA), 172 | }), 173 | z.object({ 174 | action: z 175 | .literal("update_page_properties") 176 | .describe( 177 | "Use this action to update the properties of an existing page." 178 | ), 179 | params: z.object(UPDATE_PAGE_PROPERTIES_SCHEMA), 180 | }), 181 | ]) 182 | ) 183 | .describe( 184 | "A union of all possible page operations. Each operation has a specific action and corresponding parameters. Use this schema to validate the input for page operations such as creating, archiving, restoring, searching, and updating pages. Available actions include: 'create_page', 'archive_page', 'restore_page', 'search_pages', and 'update_page_properties'. Each operation requires specific parameters as defined in the corresponding schemas." 185 | ), 186 | }; 187 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Notion MCP Server 2 | 3 | ![License](https://img.shields.io/badge/license-MIT-green) 4 | ![TypeScript](https://img.shields.io/badge/TypeScript-4.9+-blue) 5 | ![Model Context Protocol](https://img.shields.io/badge/MCP-Enabled-purple) 6 | [![smithery badge](https://smithery.ai/badge/@awkoy/notion-mcp-server)](https://smithery.ai/server/@awkoy/notion-mcp-server) 7 | ![NPM Downloads](https://img.shields.io/npm/dw/notion-mcp-server) 8 | ![Stars](https://img.shields.io/github/stars/awkoy/notion-mcp-server) 9 | 10 | **Notion MCP Server** is a Model Context Protocol (MCP) server implementation that enables AI assistants to interact with Notion's API. This production-ready server provides a complete set of tools and endpoints for reading, creating, and modifying Notion content through natural language interactions. 11 | 12 | > 🚧 **Active Development**: Database support is now available! Comments and user management tools have been added. If you find this project useful, please consider giving it a star - it helps me know that this work is valuable to the community and motivates further development. 13 | 14 | 15 | 16 | 17 | 18 | ## 📑 Table of Contents 19 | 20 | - [Getting Started & Integration](#-getting-started--integration) 21 | - [Setup Process](#setup-process) 22 | - [Cursor Integration](#cursor-integration) 23 | - [Claude Desktop Integration](#claude-desktop-integration) 24 | - [Features](#-features) 25 | - [Documentation](#-documentation) 26 | - [Available Tools](#available-tools) 27 | - [Available Resources](#available-resources) 28 | - [Development](#-development) 29 | - [Technical Details](#-technical-details) 30 | - [Troubleshooting](#-troubleshooting) 31 | - [Contributing](#-contributing) 32 | - [License](#-license) 33 | 34 | ## 🚀 Getting Started & Integration 35 | 36 | ### Setup Process 37 | 38 | 1. **Obtain a Notion API Key** 39 | - Create an integration at [Notion Developers](https://www.notion.so/my-integrations) 40 | - Copy your API key 41 | 42 | 2. **Enable Integration for Your Pages** 43 | - Select an existing page or create a new one in Notion 44 | - Click the "..." menu in the top right corner 45 | - Go to "Connections" 46 | - Find and enable your integration from the list 47 | 48 | ![Notion Page Connection](page_connection.png) 49 | 50 | 3. **Choose Your Integration Method** 51 | - Follow one of the integration options below based on your preferred MCP client 52 | 53 | 4. **Ask Your AI Assistant to Interact with Notion** 54 | - "Create a new page with today's tasks" 55 | - "Update my meeting notes in Notion" 56 | - "Add bullet points to my meeting notes page" 57 | - "Create a new database for tracking projects" 58 | - "Add new entries to my task database" 59 | - "Add a comment to my project page" 60 | - "Show me all comments on this document" 61 | - "List all users in my workspace" 62 | - "Get information about a specific user" 63 | 64 | ### Cursor Integration 65 | 66 | #### Method 1: Using mcp.json 67 | 68 | 1. Create or edit the `.cursor/mcp.json` file in your project directory: 69 | 70 | ```json 71 | { 72 | "mcpServers": { 73 | "notion-mcp-server": { 74 | "command": "env NOTION_TOKEN=YOUR_KEY NOTION_PAGE_ID=YOUR_PAGE_ID npx", 75 | "args": ["-y", "notion-mcp-server"] 76 | } 77 | } 78 | } 79 | ``` 80 | 81 | 2. Replace `YOUR_KEY` and `YOUR_PAGE_ID` with your actual Notion API key and page ID 82 | 3. Restart Cursor to apply the changes 83 | 84 | #### Method 2: Manual Mode 85 | 86 | 1. Open Cursor and go to Settings 87 | 2. Navigate to the "MCP" or "Model Context Protocol" section 88 | 3. Click "Add Server" or equivalent 89 | 4. Enter the following command in the appropriate field: 90 | 91 | ``` 92 | env NOTION_TOKEN=YOUR_KEY NOTION_PAGE_ID=YOUR_PAGE_ID npx -y notion-mcp-server 93 | ``` 94 | 95 | 5. Replace `YOUR_KEY` and `YOUR_PAGE_ID` with your actual Notion API key and page ID 96 | 6. Save the settings and restart Cursor if necessary 97 | 98 | ### Claude Desktop Integration 99 | 100 | 1. Create or edit the `mcp.json` file in your configuration directory: 101 | 102 | ```json 103 | { 104 | "mcpServers": { 105 | "notion-mcp-server": { 106 | "command": "npx", 107 | "args": ["-y", "notion-mcp-server"], 108 | "env": { 109 | "NOTION_TOKEN": "YOUR_KEY", 110 | "NOTION_PAGE_ID": "YOUR_PAGE_ID" 111 | } 112 | } 113 | } 114 | } 115 | ``` 116 | 117 | 2. Replace `YOUR_KEY` and `YOUR_PAGE_ID` with your actual Notion API key and page ID 118 | 3. Restart Claude Desktop to apply the changes 119 | 120 | ## 🌟 Features 121 | 122 | - **📝 Notion Integration** - Interact with Notion databases, pages, and blocks 123 | - **🔌 Universal MCP Compatibility** - Works with all MCP clients including Cursor, Claude Desktop, Cline, and Zed 124 | - **🔍 Data Retrieval** - Fetch information from Notion pages, blocks, and databases 125 | - **✏️ Content Creation** - Create and update Notion pages and blocks 126 | - **📊 Block Management** - Append, update, and delete blocks within Notion pages 127 | - **💾 Database Operations** - Create, query, and update databases 128 | - **🔄 Batch Operations** - Perform multiple operations in a single request 129 | - **🗑️ Archive & Restore** - Archive and restore Notion pages 130 | - **🔎 Search Functionality** - Search Notion pages and databases by title 131 | - **💬 Comments Management** - Get, create, and reply to comments on pages and discussions 132 | - **👥 User Management** - Retrieve workspace users and user information 133 | 134 | ## 📚 Documentation 135 | 136 | ### Available Tools 137 | 138 | The server provides the following consolidated tools for interacting with Notion: 139 | 140 | #### `notion_pages` 141 | 142 | A comprehensive tool for page operations including: 143 | - Creating new pages with specified content 144 | - Updating page properties 145 | - Archiving pages (moving to trash) 146 | - Restoring previously archived pages 147 | - Searching for pages by title 148 | 149 | Example operations: 150 | ```javascript 151 | { 152 | "payload": { 153 | "action": "create_page", // One of: "create_page", "archive_page", "restore_page", "search_pages", "update_page_properties" 154 | "params": { 155 | // Parameters specific to the chosen action 156 | } 157 | } 158 | } 159 | ``` 160 | 161 | #### `notion_blocks` 162 | 163 | A complete toolkit for block operations including: 164 | - Retrieving block content 165 | - Fetching child blocks 166 | - Appending new blocks to a parent 167 | - Updating existing blocks 168 | - Deleting blocks 169 | - Performing batch operations (append, update, delete, mixed) 170 | 171 | Example operations: 172 | ```javascript 173 | { 174 | "payload": { 175 | "action": "append_block_children", // One of: "append_block_children", "retrieve_block", "retrieve_block_children", "update_block", "delete_block", "batch_append_block_children", "batch_update_blocks", "batch_delete_blocks", "batch_mixed_operations" 176 | "params": { 177 | // Parameters specific to the chosen action 178 | } 179 | } 180 | } 181 | ``` 182 | 183 | #### `notion_database` 184 | 185 | A powerful tool for database interactions including: 186 | - Creating new databases with custom properties 187 | - Querying databases with filters and sorting 188 | - Updating database structure and properties 189 | 190 | Example operations: 191 | ```javascript 192 | { 193 | "payload": { 194 | "action": "create_database", // One of: "create_database", "query_database", "update_database" 195 | "params": { 196 | // Parameters specific to the chosen action 197 | } 198 | } 199 | } 200 | ``` 201 | 202 | #### `notion_comments` 203 | 204 | A tool for managing comments on Notion content: 205 | - Retrieving comments from pages and blocks 206 | - Adding new comments to pages 207 | - Replying to existing discussions 208 | 209 | Example operations: 210 | ```javascript 211 | { 212 | "payload": { 213 | "action": "get_comments", // One of: "get_comments", "add_page_comment", "add_discussion_comment" 214 | "params": { 215 | // Parameters specific to the chosen action 216 | } 217 | } 218 | } 219 | ``` 220 | 221 | #### `notion_users` 222 | 223 | A tool for accessing user information: 224 | - Listing all workspace users 225 | - Getting details about specific users 226 | - Retrieving information about the current bot user 227 | 228 | Example operations: 229 | ```javascript 230 | { 231 | "payload": { 232 | "action": "list_users", // One of: "list_users", "get_user", "get_bot_user" 233 | "params": { 234 | // Parameters specific to the chosen action 235 | } 236 | } 237 | } 238 | ``` 239 | 240 | ### Available Resources 241 | 242 | The server currently does not expose any resources, focusing instead on tool-based operations. 243 | 244 | ## 🛠 Development 245 | 246 | 1. **Clone the Repository** 247 | ``` 248 | git clone https://github.com/awkoy/notion-mcp-server.git 249 | cd notion-mcp-server 250 | ``` 251 | 252 | 2. **Install Dependencies** 253 | ``` 254 | npm install 255 | ``` 256 | 257 | 3. **Set Up Environment Variables** 258 | - Create a `.env` file with: 259 | ``` 260 | NOTION_TOKEN=your_notion_api_key 261 | NOTION_PAGE_ID=your_notion_page_id 262 | ``` 263 | 264 | 4. **Build the Project** 265 | ``` 266 | npm run build 267 | ``` 268 | 269 | 5. **Run the Inspector** 270 | ``` 271 | npm run inspector 272 | ``` 273 | 274 | ## 🔧 Technical Details 275 | 276 | - Built using TypeScript and the MCP SDK (version 1.7.0+) 277 | - Uses the official Notion API client (@notionhq/client v2.3.0+) 278 | - Follows the Model Context Protocol specification 279 | - Implements tools for CRUD operations on Notion pages, blocks, and databases 280 | - Supports efficient batch operations for performance optimization 281 | - Validates input/output with Zod schemas 282 | 283 | ## ❓ Troubleshooting 284 | 285 | - **Common Issues** 286 | - **Authentication Errors**: Ensure your Notion token has the correct permissions and integration is enabled for your pages/databases 287 | - **Page Access Issues**: Make sure your integration has been added to the pages you're attempting to access 288 | - **Rate Limiting**: Notion API has rate limits - use batch operations to optimize requests 289 | 290 | - **Getting Help** 291 | - Create an issue on the [GitHub repository](https://github.com/awkoy/notion-mcp-server/issues) 292 | - Check the [Notion API documentation](https://developers.notion.com/reference/intro) 293 | - Visit the MCP community channels for assistance 294 | 295 | ## 🤝 Contributing 296 | 297 | Contributions are welcome! Please feel free to submit a Pull Request. 298 | 299 | 1. Fork the repository 300 | 2. Create your feature branch (`git checkout -b feature/amazing-feature`) 301 | 3. Commit your changes (`git commit -m 'Add some amazing feature'`) 302 | 4. Push to the branch (`git push origin feature/amazing-feature`) 303 | 5. Open a Pull Request 304 | 305 | ## 📄 License 306 | 307 | This project is licensed under the MIT License - see the LICENSE file for details. 308 | 309 | -------------------------------------------------------------------------------- /src/schema/blocks.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { COLOR_SCHEMA } from "./color.js"; 3 | import { ICON_SCHEMA } from "./icon.js"; 4 | import { RICH_TEXT_ITEM_REQUEST_SCHEMA } from "./rich-text.js"; 5 | import { preprocessJson } from "./preprocess.js"; 6 | import { LANGUAGE_SCHEMA } from "./lang.js"; 7 | import { FILE_SCHEMA } from "./file.js"; 8 | 9 | export const BASE_BLOCK_REQUEST_SCHEMA = z.object({ 10 | type: z.string().describe("Type of block"), 11 | object: z.literal("block").optional().describe("Object type identifier"), 12 | created_time: z 13 | .string() 14 | .optional() 15 | .describe("ISO timestamp of block creation"), 16 | last_edited_time: z 17 | .string() 18 | .optional() 19 | .describe("ISO timestamp of last edit"), 20 | has_children: z 21 | .boolean() 22 | .optional() 23 | .describe("Whether block has child blocks"), 24 | archived: z.boolean().optional().describe("Whether block is archived"), 25 | }); 26 | 27 | export const TEXT_BLOCK_BASE_REQUEST_SCHEMA = z.object({ 28 | rich_text: z 29 | .array(RICH_TEXT_ITEM_REQUEST_SCHEMA) 30 | .describe("Array of rich text content"), 31 | color: COLOR_SCHEMA.optional().describe("Color of the block"), 32 | }); 33 | 34 | export const PARAGRAPH_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 35 | type: z.literal("paragraph").describe("Paragraph block type"), 36 | paragraph: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Paragraph block content"), 37 | }); 38 | 39 | export const HEADING1_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 40 | type: z.literal("heading_1").describe("Heading 1 block type"), 41 | heading_1: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({ 42 | is_toggleable: z 43 | .boolean() 44 | .optional() 45 | .describe("Whether heading can be toggled"), 46 | }).describe("Heading 1 block content"), 47 | }); 48 | 49 | export const HEADING2_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 50 | type: z.literal("heading_2").describe("Heading 2 block type"), 51 | heading_2: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({ 52 | is_toggleable: z 53 | .boolean() 54 | .optional() 55 | .describe("Whether heading can be toggled"), 56 | }).describe("Heading 2 block content"), 57 | }); 58 | 59 | export const HEADING3_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 60 | type: z.literal("heading_3").describe("Heading 3 block type"), 61 | heading_3: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({ 62 | is_toggleable: z 63 | .boolean() 64 | .optional() 65 | .describe("Whether heading can be toggled"), 66 | }).describe("Heading 3 block content"), 67 | }); 68 | 69 | export const QUOTE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 70 | type: z.literal("quote").describe("Quote block type"), 71 | quote: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Quote block content"), 72 | }); 73 | 74 | export const CALLOUT_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 75 | type: z.literal("callout").describe("Callout block type"), 76 | callout: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({ 77 | icon: ICON_SCHEMA.optional().describe("Icon for the callout"), 78 | }).describe("Callout block content"), 79 | }); 80 | 81 | export const TOGGLE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 82 | type: z.literal("toggle").describe("Toggle block type"), 83 | toggle: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe("Toggle block content"), 84 | }); 85 | 86 | export const BULLETED_LIST_ITEM_BLOCK_REQUEST_SCHEMA = 87 | BASE_BLOCK_REQUEST_SCHEMA.extend({ 88 | type: z 89 | .literal("bulleted_list_item") 90 | .describe("Bulleted list item block type"), 91 | bulleted_list_item: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe( 92 | "Bulleted list item block content" 93 | ), 94 | }); 95 | 96 | export const NUMBERED_LIST_ITEM_BLOCK_REQUEST_SCHEMA = 97 | BASE_BLOCK_REQUEST_SCHEMA.extend({ 98 | type: z 99 | .literal("numbered_list_item") 100 | .describe("Numbered list item block type"), 101 | numbered_list_item: TEXT_BLOCK_BASE_REQUEST_SCHEMA.describe( 102 | "Numbered list item block content" 103 | ), 104 | }); 105 | 106 | export const TO_DO_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 107 | type: z.literal("to_do").describe("To-do block type"), 108 | to_do: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({ 109 | checked: z.boolean().optional().describe("Whether the to-do is checked"), 110 | }).describe("To-do block content"), 111 | }); 112 | 113 | export const CODE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 114 | type: z.literal("code").describe("Code block type"), 115 | code: TEXT_BLOCK_BASE_REQUEST_SCHEMA.extend({ 116 | language: LANGUAGE_SCHEMA, 117 | }).describe("Code block content"), 118 | }); 119 | 120 | export const DIVIDER_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 121 | type: z.literal("divider").describe("Divider block type"), 122 | divider: z.object({}).describe("Divider block content"), 123 | }); 124 | 125 | export const IMAGE_BLOCK_REQUEST_SCHEMA = BASE_BLOCK_REQUEST_SCHEMA.extend({ 126 | type: z.literal("image").describe("Image block type"), 127 | image: z 128 | .object({ 129 | ...FILE_SCHEMA.shape, 130 | caption: z 131 | .array(RICH_TEXT_ITEM_REQUEST_SCHEMA) 132 | .optional() 133 | .describe("Image caption"), 134 | }) 135 | .describe("Image block content"), 136 | }); 137 | 138 | export const TEXT_BLOCK_REQUEST_SCHEMA = z.preprocess( 139 | preprocessJson, 140 | z 141 | .discriminatedUnion("type", [ 142 | PARAGRAPH_BLOCK_REQUEST_SCHEMA, 143 | HEADING1_BLOCK_REQUEST_SCHEMA, 144 | HEADING2_BLOCK_REQUEST_SCHEMA, 145 | HEADING3_BLOCK_REQUEST_SCHEMA, 146 | QUOTE_BLOCK_REQUEST_SCHEMA, 147 | CALLOUT_BLOCK_REQUEST_SCHEMA, 148 | TOGGLE_BLOCK_REQUEST_SCHEMA, 149 | BULLETED_LIST_ITEM_BLOCK_REQUEST_SCHEMA, 150 | NUMBERED_LIST_ITEM_BLOCK_REQUEST_SCHEMA, 151 | TO_DO_BLOCK_REQUEST_SCHEMA, 152 | CODE_BLOCK_REQUEST_SCHEMA, 153 | DIVIDER_BLOCK_REQUEST_SCHEMA, 154 | IMAGE_BLOCK_REQUEST_SCHEMA, 155 | ]) 156 | .describe("Union of all possible text block request types") 157 | ); 158 | 159 | export const APPEND_BLOCK_CHILDREN_SCHEMA = { 160 | blockId: z.string().describe("The ID of the block to append children to"), 161 | children: z 162 | .array(TEXT_BLOCK_REQUEST_SCHEMA) 163 | .describe("Array of blocks to append as children"), 164 | }; 165 | 166 | export const RETRIEVE_BLOCK_SCHEMA = { 167 | blockId: z.string().describe("The ID of the block to retrieve"), 168 | }; 169 | 170 | export const RETRIEVE_BLOCK_CHILDREN_SCHEMA = { 171 | blockId: z.string().describe("The ID of the block to retrieve children for"), 172 | start_cursor: z.string().optional().describe("Cursor for pagination"), 173 | page_size: z 174 | .number() 175 | .min(1) 176 | .max(100) 177 | .optional() 178 | .describe("Number of results to return (1-100)"), 179 | }; 180 | 181 | export const UPDATE_BLOCK_SCHEMA = { 182 | blockId: z.string().describe("The ID of the block to update"), 183 | data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"), 184 | }; 185 | 186 | export const DELETE_BLOCK_SCHEMA = { 187 | blockId: z.string().describe("The ID of the block to delete/archive"), 188 | }; 189 | 190 | // Batch operation schemas for multiple blocks 191 | 192 | export const BATCH_APPEND_BLOCK_CHILDREN_SCHEMA = { 193 | operations: z 194 | .array( 195 | z.object({ 196 | blockId: z 197 | .string() 198 | .describe("The ID of the block to append children to"), 199 | children: z 200 | .array(TEXT_BLOCK_REQUEST_SCHEMA) 201 | .describe("Array of blocks to append as children"), 202 | }) 203 | ) 204 | .describe("Array of append operations to perform in a single batch"), 205 | }; 206 | 207 | export const BATCH_UPDATE_BLOCKS_SCHEMA = { 208 | operations: z 209 | .array( 210 | z.object({ 211 | blockId: z.string().describe("The ID of the block to update"), 212 | data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"), 213 | }) 214 | ) 215 | .describe("Array of update operations to perform in a single batch"), 216 | }; 217 | 218 | export const BATCH_DELETE_BLOCKS_SCHEMA = { 219 | blockIds: z 220 | .array(z.string().describe("The ID of a block to delete/archive")) 221 | .describe("Array of block IDs to delete in a single batch"), 222 | }; 223 | 224 | // Schema for multi-operation batches (mixed operations) 225 | export const BATCH_MIXED_OPERATIONS_SCHEMA = { 226 | operations: z 227 | .array( 228 | z.discriminatedUnion("operation", [ 229 | z.object({ 230 | operation: z.literal("append"), 231 | blockId: z 232 | .string() 233 | .describe("The ID of the block to append children to"), 234 | children: z 235 | .array(TEXT_BLOCK_REQUEST_SCHEMA) 236 | .describe("Array of blocks to append as children"), 237 | }), 238 | z.object({ 239 | operation: z.literal("update"), 240 | blockId: z.string().describe("The ID of the block to update"), 241 | data: TEXT_BLOCK_REQUEST_SCHEMA.describe("The block data to update"), 242 | }), 243 | z.object({ 244 | operation: z.literal("delete"), 245 | blockId: z.string().describe("The ID of the block to delete/archive"), 246 | }), 247 | ]) 248 | ) 249 | .describe("Array of mixed operations to perform in a single batch"), 250 | }; 251 | 252 | // Combined schema for all block operations 253 | export const BLOCKS_OPERATION_SCHEMA = { 254 | payload: z 255 | .preprocess( 256 | preprocessJson, 257 | z.discriminatedUnion("action", [ 258 | z.object({ 259 | action: z 260 | .literal("append_block_children") 261 | .describe("Use this action to append children to a block."), 262 | params: z.object(APPEND_BLOCK_CHILDREN_SCHEMA), 263 | }), 264 | z.object({ 265 | action: z 266 | .literal("retrieve_block") 267 | .describe("Use this action to retrieve a block."), 268 | params: z.object(RETRIEVE_BLOCK_SCHEMA), 269 | }), 270 | z.object({ 271 | action: z 272 | .literal("retrieve_block_children") 273 | .describe("Use this action to retrieve children of a block."), 274 | params: z.object(RETRIEVE_BLOCK_CHILDREN_SCHEMA), 275 | }), 276 | z.object({ 277 | action: z 278 | .literal("update_block") 279 | .describe("Use this action to update a block."), 280 | params: z.object(UPDATE_BLOCK_SCHEMA), 281 | }), 282 | z.object({ 283 | action: z 284 | .literal("delete_block") 285 | .describe("Use this action to delete/archive a block."), 286 | params: z.object(DELETE_BLOCK_SCHEMA), 287 | }), 288 | z.object({ 289 | action: z 290 | .literal("batch_append_block_children") 291 | .describe( 292 | "Use this action to perform batch append operations on blocks." 293 | ), 294 | params: z.object(BATCH_APPEND_BLOCK_CHILDREN_SCHEMA), 295 | }), 296 | z.object({ 297 | action: z 298 | .literal("batch_update_blocks") 299 | .describe( 300 | "Use this action to perform batch update operations on blocks." 301 | ), 302 | params: z.object(BATCH_UPDATE_BLOCKS_SCHEMA), 303 | }), 304 | z.object({ 305 | action: z 306 | .literal("batch_delete_blocks") 307 | .describe( 308 | "Use this action to perform batch delete operations on blocks." 309 | ), 310 | params: z.object(BATCH_DELETE_BLOCKS_SCHEMA), 311 | }), 312 | z.object({ 313 | action: z 314 | .literal("batch_mixed_operations") 315 | .describe( 316 | "Use this action to perform batch mixed operations on blocks." 317 | ), 318 | params: z.object(BATCH_MIXED_OPERATIONS_SCHEMA), 319 | }), 320 | ]) 321 | ) 322 | .describe( 323 | "A union of all possible block operations. Each operation has a specific action and corresponding parameters. Use this schema to validate the input for block operations such as appending, retrieving, updating, deleting, and performing batch operations. Available actions include: 'append_block_children', 'retrieve_block', 'retrieve_block_children', 'update_block', 'delete_block', 'batch_append_block_children', 'batch_update_blocks', 'batch_delete_blocks', and 'batch_mixed_operations'. Each operation requires specific parameters as defined in the corresponding schemas." 324 | ), 325 | }; 326 | -------------------------------------------------------------------------------- /src/schema/timezone.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const TIMEZONE_SCHEMA = z.enum([ 4 | "Africa/Abidjan", 5 | "Africa/Accra", 6 | "Africa/Addis_Ababa", 7 | "Africa/Algiers", 8 | "Africa/Asmara", 9 | "Africa/Asmera", 10 | "Africa/Bamako", 11 | "Africa/Bangui", 12 | "Africa/Banjul", 13 | "Africa/Bissau", 14 | "Africa/Blantyre", 15 | "Africa/Brazzaville", 16 | "Africa/Bujumbura", 17 | "Africa/Cairo", 18 | "Africa/Casablanca", 19 | "Africa/Ceuta", 20 | "Africa/Conakry", 21 | "Africa/Dakar", 22 | "Africa/Dar_es_Salaam", 23 | "Africa/Djibouti", 24 | "Africa/Douala", 25 | "Africa/El_Aaiun", 26 | "Africa/Freetown", 27 | "Africa/Gaborone", 28 | "Africa/Harare", 29 | "Africa/Johannesburg", 30 | "Africa/Juba", 31 | "Africa/Kampala", 32 | "Africa/Khartoum", 33 | "Africa/Kigali", 34 | "Africa/Kinshasa", 35 | "Africa/Lagos", 36 | "Africa/Libreville", 37 | "Africa/Lome", 38 | "Africa/Luanda", 39 | "Africa/Lubumbashi", 40 | "Africa/Lusaka", 41 | "Africa/Malabo", 42 | "Africa/Maputo", 43 | "Africa/Maseru", 44 | "Africa/Mbabane", 45 | "Africa/Mogadishu", 46 | "Africa/Monrovia", 47 | "Africa/Nairobi", 48 | "Africa/Ndjamena", 49 | "Africa/Niamey", 50 | "Africa/Nouakchott", 51 | "Africa/Ouagadougou", 52 | "Africa/Porto-Novo", 53 | "Africa/Sao_Tome", 54 | "Africa/Timbuktu", 55 | "Africa/Tripoli", 56 | "Africa/Tunis", 57 | "Africa/Windhoek", 58 | "America/Adak", 59 | "America/Anchorage", 60 | "America/Anguilla", 61 | "America/Antigua", 62 | "America/Araguaina", 63 | "America/Argentina/Buenos_Aires", 64 | "America/Argentina/Catamarca", 65 | "America/Argentina/ComodRivadavia", 66 | "America/Argentina/Cordoba", 67 | "America/Argentina/Jujuy", 68 | "America/Argentina/La_Rioja", 69 | "America/Argentina/Mendoza", 70 | "America/Argentina/Rio_Gallegos", 71 | "America/Argentina/Salta", 72 | "America/Argentina/San_Juan", 73 | "America/Argentina/San_Luis", 74 | "America/Argentina/Tucuman", 75 | "America/Argentina/Ushuaia", 76 | "America/Aruba", 77 | "America/Asuncion", 78 | "America/Atikokan", 79 | "America/Atka", 80 | "America/Bahia_Banderas", 81 | "America/Bahia", 82 | "America/Barbados", 83 | "America/Belem", 84 | "America/Belize", 85 | "America/Blanc-Sablon", 86 | "America/Boa_Vista", 87 | "America/Bogota", 88 | "America/Boise", 89 | "America/Buenos_Aires", 90 | "America/Cambridge_Bay", 91 | "America/Campo_Grande", 92 | "America/Cancun", 93 | "America/Caracas", 94 | "America/Catamarca", 95 | "America/Cayenne", 96 | "America/Cayman", 97 | "America/Chicago", 98 | "America/Chihuahua", 99 | "America/Ciudad_Juarez", 100 | "America/Coral_Harbour", 101 | "America/Cordoba", 102 | "America/Costa_Rica", 103 | "America/Creston", 104 | "America/Cuiaba", 105 | "America/Curacao", 106 | "America/Danmarkshavn", 107 | "America/Dawson_Creek", 108 | "America/Dawson", 109 | "America/Denver", 110 | "America/Detroit", 111 | "America/Dominica", 112 | "America/Edmonton", 113 | "America/Eirunepe", 114 | "America/El_Salvador", 115 | "America/Ensenada", 116 | "America/Fort_Nelson", 117 | "America/Fort_Wayne", 118 | "America/Fortaleza", 119 | "America/Glace_Bay", 120 | "America/Godthab", 121 | "America/Goose_Bay", 122 | "America/Grand_Turk", 123 | "America/Grenada", 124 | "America/Guadeloupe", 125 | "America/Guatemala", 126 | "America/Guayaquil", 127 | "America/Guyana", 128 | "America/Halifax", 129 | "America/Havana", 130 | "America/Hermosillo", 131 | "America/Indiana/Indianapolis", 132 | "America/Indiana/Knox", 133 | "America/Indiana/Marengo", 134 | "America/Indiana/Petersburg", 135 | "America/Indiana/Tell_City", 136 | "America/Indiana/Vevay", 137 | "America/Indiana/Vincennes", 138 | "America/Indiana/Winamac", 139 | "America/Indianapolis", 140 | "America/Inuvik", 141 | "America/Iqaluit", 142 | "America/Jamaica", 143 | "America/Jujuy", 144 | "America/Juneau", 145 | "America/Kentucky/Louisville", 146 | "America/Kentucky/Monticello", 147 | "America/Knox_IN", 148 | "America/Kralendijk", 149 | "America/La_Paz", 150 | "America/Lima", 151 | "America/Los_Angeles", 152 | "America/Louisville", 153 | "America/Lower_Princes", 154 | "America/Maceio", 155 | "America/Managua", 156 | "America/Manaus", 157 | "America/Marigot", 158 | "America/Martinique", 159 | "America/Matamoros", 160 | "America/Mazatlan", 161 | "America/Mendoza", 162 | "America/Menominee", 163 | "America/Merida", 164 | "America/Metlakatla", 165 | "America/Mexico_City", 166 | "America/Miquelon", 167 | "America/Moncton", 168 | "America/Monterrey", 169 | "America/Montevideo", 170 | "America/Montreal", 171 | "America/Montserrat", 172 | "America/Nassau", 173 | "America/New_York", 174 | "America/Nipigon", 175 | "America/Nome", 176 | "America/Noronha", 177 | "America/North_Dakota/Beulah", 178 | "America/North_Dakota/Center", 179 | "America/North_Dakota/New_Salem", 180 | "America/Nuuk", 181 | "America/Ojinaga", 182 | "America/Panama", 183 | "America/Pangnirtung", 184 | "America/Paramaribo", 185 | "America/Phoenix", 186 | "America/Port_of_Spain", 187 | "America/Port-au-Prince", 188 | "America/Porto_Acre", 189 | "America/Porto_Velho", 190 | "America/Puerto_Rico", 191 | "America/Punta_Arenas", 192 | "America/Rainy_River", 193 | "America/Rankin_Inlet", 194 | "America/Recife", 195 | "America/Regina", 196 | "America/Resolute", 197 | "America/Rio_Branco", 198 | "America/Rosario", 199 | "America/Santa_Isabel", 200 | "America/Santarem", 201 | "America/Santiago", 202 | "America/Santo_Domingo", 203 | "America/Sao_Paulo", 204 | "America/Scoresbysund", 205 | "America/Shiprock", 206 | "America/Sitka", 207 | "America/St_Barthelemy", 208 | "America/St_Johns", 209 | "America/St_Kitts", 210 | "America/St_Lucia", 211 | "America/St_Thomas", 212 | "America/St_Vincent", 213 | "America/Swift_Current", 214 | "America/Tegucigalpa", 215 | "America/Thule", 216 | "America/Thunder_Bay", 217 | "America/Tijuana", 218 | "America/Toronto", 219 | "America/Tortola", 220 | "America/Vancouver", 221 | "America/Virgin", 222 | "America/Whitehorse", 223 | "America/Winnipeg", 224 | "America/Yakutat", 225 | "America/Yellowknife", 226 | "Antarctica/Casey", 227 | "Antarctica/Davis", 228 | "Antarctica/DumontDUrville", 229 | "Antarctica/Macquarie", 230 | "Antarctica/Mawson", 231 | "Antarctica/McMurdo", 232 | "Antarctica/Palmer", 233 | "Antarctica/Rothera", 234 | "Antarctica/South_Pole", 235 | "Antarctica/Syowa", 236 | "Antarctica/Troll", 237 | "Antarctica/Vostok", 238 | "Arctic/Longyearbyen", 239 | "Asia/Aden", 240 | "Asia/Almaty", 241 | "Asia/Amman", 242 | "Asia/Anadyr", 243 | "Asia/Aqtau", 244 | "Asia/Aqtobe", 245 | "Asia/Ashgabat", 246 | "Asia/Ashkhabad", 247 | "Asia/Atyrau", 248 | "Asia/Baghdad", 249 | "Asia/Bahrain", 250 | "Asia/Baku", 251 | "Asia/Bangkok", 252 | "Asia/Barnaul", 253 | "Asia/Beirut", 254 | "Asia/Bishkek", 255 | "Asia/Brunei", 256 | "Asia/Calcutta", 257 | "Asia/Chita", 258 | "Asia/Choibalsan", 259 | "Asia/Chongqing", 260 | "Asia/Chungking", 261 | "Asia/Colombo", 262 | "Asia/Dacca", 263 | "Asia/Damascus", 264 | "Asia/Dhaka", 265 | "Asia/Dili", 266 | "Asia/Dubai", 267 | "Asia/Dushanbe", 268 | "Asia/Famagusta", 269 | "Asia/Gaza", 270 | "Asia/Harbin", 271 | "Asia/Hebron", 272 | "Asia/Ho_Chi_Minh", 273 | "Asia/Hong_Kong", 274 | "Asia/Hovd", 275 | "Asia/Irkutsk", 276 | "Asia/Istanbul", 277 | "Asia/Jakarta", 278 | "Asia/Jayapura", 279 | "Asia/Jerusalem", 280 | "Asia/Kabul", 281 | "Asia/Kamchatka", 282 | "Asia/Karachi", 283 | "Asia/Kashgar", 284 | "Asia/Kathmandu", 285 | "Asia/Katmandu", 286 | "Asia/Khandyga", 287 | "Asia/Kolkata", 288 | "Asia/Krasnoyarsk", 289 | "Asia/Kuala_Lumpur", 290 | "Asia/Kuching", 291 | "Asia/Kuwait", 292 | "Asia/Macao", 293 | "Asia/Macau", 294 | "Asia/Magadan", 295 | "Asia/Makassar", 296 | "Asia/Manila", 297 | "Asia/Muscat", 298 | "Asia/Nicosia", 299 | "Asia/Novokuznetsk", 300 | "Asia/Novosibirsk", 301 | "Asia/Omsk", 302 | "Asia/Oral", 303 | "Asia/Phnom_Penh", 304 | "Asia/Pontianak", 305 | "Asia/Pyongyang", 306 | "Asia/Qatar", 307 | "Asia/Qostanay", 308 | "Asia/Qyzylorda", 309 | "Asia/Rangoon", 310 | "Asia/Riyadh", 311 | "Asia/Saigon", 312 | "Asia/Sakhalin", 313 | "Asia/Samarkand", 314 | "Asia/Seoul", 315 | "Asia/Shanghai", 316 | "Asia/Singapore", 317 | "Asia/Srednekolymsk", 318 | "Asia/Taipei", 319 | "Asia/Tashkent", 320 | "Asia/Tbilisi", 321 | "Asia/Tehran", 322 | "Asia/Tel_Aviv", 323 | "Asia/Thimbu", 324 | "Asia/Thimphu", 325 | "Asia/Tokyo", 326 | "Asia/Tomsk", 327 | "Asia/Ujung_Pandang", 328 | "Asia/Ulaanbaatar", 329 | "Asia/Ulan_Bator", 330 | "Asia/Urumqi", 331 | "Asia/Ust-Nera", 332 | "Asia/Vientiane", 333 | "Asia/Vladivostok", 334 | "Asia/Yakutsk", 335 | "Asia/Yangon", 336 | "Asia/Yekaterinburg", 337 | "Asia/Yerevan", 338 | "Atlantic/Azores", 339 | "Atlantic/Bermuda", 340 | "Atlantic/Canary", 341 | "Atlantic/Cape_Verde", 342 | "Atlantic/Faeroe", 343 | "Atlantic/Faroe", 344 | "Atlantic/Jan_Mayen", 345 | "Atlantic/Madeira", 346 | "Atlantic/Reykjavik", 347 | "Atlantic/South_Georgia", 348 | "Atlantic/St_Helena", 349 | "Atlantic/Stanley", 350 | "Australia/ACT", 351 | "Australia/Adelaide", 352 | "Australia/Brisbane", 353 | "Australia/Broken_Hill", 354 | "Australia/Canberra", 355 | "Australia/Currie", 356 | "Australia/Darwin", 357 | "Australia/Eucla", 358 | "Australia/Hobart", 359 | "Australia/LHI", 360 | "Australia/Lindeman", 361 | "Australia/Lord_Howe", 362 | "Australia/Melbourne", 363 | "Australia/North", 364 | "Australia/NSW", 365 | "Australia/Perth", 366 | "Australia/Queensland", 367 | "Australia/South", 368 | "Australia/Sydney", 369 | "Australia/Tasmania", 370 | "Australia/Victoria", 371 | "Australia/West", 372 | "Australia/Yancowinna", 373 | "Brazil/Acre", 374 | "Brazil/DeNoronha", 375 | "Brazil/East", 376 | "Brazil/West", 377 | "Canada/Atlantic", 378 | "Canada/Central", 379 | "Canada/Eastern", 380 | "Canada/Mountain", 381 | "Canada/Newfoundland", 382 | "Canada/Pacific", 383 | "Canada/Saskatchewan", 384 | "Canada/Yukon", 385 | "CET", 386 | "Chile/Continental", 387 | "Chile/EasterIsland", 388 | "CST6CDT", 389 | "Cuba", 390 | "EET", 391 | "Egypt", 392 | "Eire", 393 | "EST", 394 | "EST5EDT", 395 | "Etc/GMT-0", 396 | "Etc/GMT-1", 397 | "Etc/GMT-10", 398 | "Etc/GMT-11", 399 | "Etc/GMT-12", 400 | "Etc/GMT-13", 401 | "Etc/GMT-14", 402 | "Etc/GMT-2", 403 | "Etc/GMT-3", 404 | "Etc/GMT-4", 405 | "Etc/GMT-5", 406 | "Etc/GMT-6", 407 | "Etc/GMT-7", 408 | "Etc/GMT-8", 409 | "Etc/GMT-9", 410 | "Etc/GMT", 411 | "Etc/GMT+0", 412 | "Etc/GMT+1", 413 | "Etc/GMT+10", 414 | "Etc/GMT+11", 415 | "Etc/GMT+12", 416 | "Etc/GMT+2", 417 | "Etc/GMT+3", 418 | "Etc/GMT+4", 419 | "Etc/GMT+5", 420 | "Etc/GMT+6", 421 | "Etc/GMT+7", 422 | "Etc/GMT+8", 423 | "Etc/GMT+9", 424 | "Etc/GMT0", 425 | "Etc/Greenwich", 426 | "Etc/UCT", 427 | "Etc/Universal", 428 | "Etc/UTC", 429 | "Etc/Zulu", 430 | "Europe/Amsterdam", 431 | "Europe/Andorra", 432 | "Europe/Astrakhan", 433 | "Europe/Athens", 434 | "Europe/Belfast", 435 | "Europe/Belgrade", 436 | "Europe/Berlin", 437 | "Europe/Bratislava", 438 | "Europe/Brussels", 439 | "Europe/Bucharest", 440 | "Europe/Budapest", 441 | "Europe/Busingen", 442 | "Europe/Chisinau", 443 | "Europe/Copenhagen", 444 | "Europe/Dublin", 445 | "Europe/Gibraltar", 446 | "Europe/Guernsey", 447 | "Europe/Helsinki", 448 | "Europe/Isle_of_Man", 449 | "Europe/Istanbul", 450 | "Europe/Jersey", 451 | "Europe/Kaliningrad", 452 | "Europe/Kiev", 453 | "Europe/Kirov", 454 | "Europe/Kyiv", 455 | "Europe/Lisbon", 456 | "Europe/Ljubljana", 457 | "Europe/London", 458 | "Europe/Luxembourg", 459 | "Europe/Madrid", 460 | "Europe/Malta", 461 | "Europe/Mariehamn", 462 | "Europe/Minsk", 463 | "Europe/Monaco", 464 | "Europe/Moscow", 465 | "Europe/Nicosia", 466 | "Europe/Oslo", 467 | "Europe/Paris", 468 | "Europe/Podgorica", 469 | "Europe/Prague", 470 | "Europe/Riga", 471 | "Europe/Rome", 472 | "Europe/Samara", 473 | "Europe/San_Marino", 474 | "Europe/Sarajevo", 475 | "Europe/Saratov", 476 | "Europe/Simferopol", 477 | "Europe/Skopje", 478 | "Europe/Sofia", 479 | "Europe/Stockholm", 480 | "Europe/Tallinn", 481 | "Europe/Tirane", 482 | "Europe/Tiraspol", 483 | "Europe/Ulyanovsk", 484 | "Europe/Uzhgorod", 485 | "Europe/Vaduz", 486 | "Europe/Vatican", 487 | "Europe/Vienna", 488 | "Europe/Vilnius", 489 | "Europe/Volgograd", 490 | "Europe/Warsaw", 491 | "Europe/Zagreb", 492 | "Europe/Zaporozhye", 493 | "Europe/Zurich", 494 | "GB-Eire", 495 | "GB", 496 | "GMT-0", 497 | "GMT", 498 | "GMT+0", 499 | "GMT0", 500 | "Greenwich", 501 | "Hongkong", 502 | "HST", 503 | "Iceland", 504 | "Indian/Antananarivo", 505 | "Indian/Chagos", 506 | "Indian/Christmas", 507 | "Indian/Cocos", 508 | "Indian/Comoro", 509 | "Indian/Kerguelen", 510 | "Indian/Mahe", 511 | "Indian/Maldives", 512 | "Indian/Mauritius", 513 | "Indian/Mayotte", 514 | "Indian/Reunion", 515 | "Iran", 516 | "Israel", 517 | "Jamaica", 518 | "Japan", 519 | "Kwajalein", 520 | "Libya", 521 | "MET", 522 | "Mexico/BajaNorte", 523 | "Mexico/BajaSur", 524 | "Mexico/General", 525 | "MST", 526 | "MST7MDT", 527 | "Navajo", 528 | "NZ-CHAT", 529 | "NZ", 530 | "Pacific/Apia", 531 | "Pacific/Auckland", 532 | "Pacific/Bougainville", 533 | "Pacific/Chatham", 534 | "Pacific/Chuuk", 535 | "Pacific/Easter", 536 | "Pacific/Efate", 537 | "Pacific/Enderbury", 538 | "Pacific/Fakaofo", 539 | "Pacific/Fiji", 540 | "Pacific/Funafuti", 541 | "Pacific/Galapagos", 542 | "Pacific/Gambier", 543 | "Pacific/Guadalcanal", 544 | "Pacific/Guam", 545 | "Pacific/Honolulu", 546 | "Pacific/Johnston", 547 | "Pacific/Kanton", 548 | "Pacific/Kiritimati", 549 | "Pacific/Kosrae", 550 | "Pacific/Kwajalein", 551 | "Pacific/Majuro", 552 | "Pacific/Marquesas", 553 | "Pacific/Midway", 554 | "Pacific/Nauru", 555 | "Pacific/Niue", 556 | "Pacific/Norfolk", 557 | "Pacific/Noumea", 558 | "Pacific/Pago_Pago", 559 | "Pacific/Palau", 560 | "Pacific/Pitcairn", 561 | "Pacific/Pohnpei", 562 | "Pacific/Ponape", 563 | "Pacific/Port_Moresby", 564 | "Pacific/Rarotonga", 565 | "Pacific/Saipan", 566 | "Pacific/Samoa", 567 | "Pacific/Tahiti", 568 | "Pacific/Tarawa", 569 | "Pacific/Tongatapu", 570 | "Pacific/Truk", 571 | "Pacific/Wake", 572 | "Pacific/Wallis", 573 | "Pacific/Yap", 574 | "Poland", 575 | "Portugal", 576 | "PRC", 577 | "PST8PDT", 578 | "ROC", 579 | "ROK", 580 | "Singapore", 581 | "Turkey", 582 | "UCT", 583 | "Universal", 584 | "US/Alaska", 585 | "US/Aleutian", 586 | "US/Arizona", 587 | "US/Central", 588 | "US/East-Indiana", 589 | "US/Eastern", 590 | "US/Hawaii", 591 | "US/Indiana-Starke", 592 | "US/Michigan", 593 | "US/Mountain", 594 | "US/Pacific", 595 | "US/Samoa", 596 | "UTC", 597 | "W-SU", 598 | "WET", 599 | "Zulu", 600 | ]); 601 | -------------------------------------------------------------------------------- /src/schema/database.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { ICON_SCHEMA } from "./icon.js"; 3 | import { FILE_SCHEMA } from "./file.js"; 4 | import { PARENT_SCHEMA } from "./page.js"; 5 | import { 6 | RICH_TEXT_ITEM_REQUEST_SCHEMA, 7 | TEXT_CONTENT_REQUEST_SCHEMA, 8 | TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA, 9 | } from "./rich-text.js"; 10 | import { preprocessJson } from "./preprocess.js"; 11 | import { NUMBER_FORMAT } from "./number.js"; 12 | import { getRootPageId } from "../services/notion.js"; 13 | 14 | export const EMPTY_OBJECT_SCHEMA = z.record(z.string(), z.never()).default({}); 15 | export const SELECT_COLOR_SCHEMA = z.enum([ 16 | "default", 17 | "gray", 18 | "brown", 19 | "orange", 20 | "yellow", 21 | "green", 22 | "blue", 23 | "purple", 24 | "pink", 25 | "red", 26 | ]); 27 | 28 | // Title property for database creation 29 | export const TITLE_PROPERTY_SCHEMA = z.object({ 30 | title: z 31 | .array( 32 | z.object({ 33 | text: TEXT_CONTENT_REQUEST_SCHEMA.describe( 34 | "Text content for title segment" 35 | ), 36 | }) 37 | ) 38 | .describe("Array of text segments that make up the title"), 39 | }); 40 | 41 | // Database property schemas 42 | // 1. Title property 43 | export const TITLE_DB_PROPERTY_SCHEMA = z.object({ 44 | type: z.literal("title").describe("Title property type"), 45 | title: EMPTY_OBJECT_SCHEMA.describe( 46 | "There is no additional property configuration." 47 | ), 48 | description: z.string().optional().describe("Property description"), 49 | }); 50 | 51 | // 2. Rich text property 52 | export const RICH_TEXT_DB_PROPERTY_SCHEMA = z.object({ 53 | type: z.literal("rich_text").describe("Rich text property type"), 54 | rich_text: EMPTY_OBJECT_SCHEMA.describe( 55 | "There is no additional property configuration." 56 | ), 57 | description: z.string().optional().describe("Property description"), 58 | }); 59 | 60 | // 3. Number property 61 | 62 | export const NUMBER_DB_PROPERTY_SCHEMA = z.object({ 63 | type: z.literal("number").describe("Number property type"), 64 | number: z 65 | .object({ 66 | format: NUMBER_FORMAT.describe("Number format"), 67 | }) 68 | .describe("Number property configuration"), 69 | description: z.string().optional().describe("Property description"), 70 | }); 71 | 72 | // 4. Select property 73 | export const SELECT_OPTION_SCHEMA = z.object({ 74 | name: z.string().describe("Name of the select option"), 75 | color: SELECT_COLOR_SCHEMA.optional().describe("Color of the select option"), 76 | id: z.string().optional().describe("ID of the select option"), 77 | }); 78 | 79 | export const SELECT_DB_PROPERTY_SCHEMA = z.object({ 80 | type: z.literal("select").describe("Select property type"), 81 | select: z 82 | .object({ 83 | options: z 84 | .array(SELECT_OPTION_SCHEMA) 85 | .optional() 86 | .describe("Select options"), 87 | }) 88 | .describe("Select property configuration"), 89 | description: z.string().optional().describe("Property description"), 90 | }); 91 | 92 | // 5. Multi-select property 93 | export const MULTI_SELECT_DB_PROPERTY_SCHEMA = z.object({ 94 | type: z.literal("multi_select").describe("Multi-select property type"), 95 | multi_select: z 96 | .object({ 97 | options: z 98 | .array(SELECT_OPTION_SCHEMA) 99 | .optional() 100 | .describe("Multi-select options"), 101 | }) 102 | .describe("Multi-select property configuration"), 103 | description: z.string().optional().describe("Property description"), 104 | }); 105 | 106 | // 6. Date property 107 | export const DATE_DB_PROPERTY_SCHEMA = z.object({ 108 | type: z.literal("date").describe("Date property type"), 109 | date: EMPTY_OBJECT_SCHEMA.describe( 110 | "There is no additional property configuration." 111 | ), 112 | description: z.string().optional().describe("Property description"), 113 | }); 114 | 115 | // 7. People property 116 | export const PEOPLE_DB_PROPERTY_SCHEMA = z.object({ 117 | type: z.literal("people").describe("People property type"), 118 | people: EMPTY_OBJECT_SCHEMA.describe( 119 | "There is no additional property configuration." 120 | ), 121 | description: z.string().optional().describe("Property description"), 122 | }); 123 | 124 | // 8. Files property 125 | export const FILES_DB_PROPERTY_SCHEMA = z.object({ 126 | type: z.literal("files").describe("Files property type"), 127 | files: EMPTY_OBJECT_SCHEMA.describe( 128 | "There is no additional property configuration." 129 | ), 130 | description: z.string().optional().describe("Property description"), 131 | }); 132 | 133 | // 9. Checkbox property 134 | export const CHECKBOX_DB_PROPERTY_SCHEMA = z.object({ 135 | type: z.literal("checkbox").describe("Checkbox property type"), 136 | checkbox: EMPTY_OBJECT_SCHEMA.describe( 137 | "There is no additional property configuration." 138 | ), 139 | description: z.string().optional().describe("Property description"), 140 | }); 141 | 142 | // 10. URL property 143 | export const URL_DB_PROPERTY_SCHEMA = z.object({ 144 | type: z.literal("url").describe("URL property type"), 145 | url: EMPTY_OBJECT_SCHEMA.describe( 146 | "There is no additional property configuration." 147 | ), 148 | description: z.string().optional().describe("Property description"), 149 | }); 150 | 151 | // 11. Email property 152 | export const EMAIL_DB_PROPERTY_SCHEMA = z.object({ 153 | type: z.literal("email").describe("Email property type"), 154 | email: EMPTY_OBJECT_SCHEMA.describe( 155 | "There is no additional property configuration." 156 | ), 157 | description: z.string().optional().describe("Property description"), 158 | }); 159 | 160 | // 12. Phone number property 161 | export const PHONE_NUMBER_DB_PROPERTY_SCHEMA = z.object({ 162 | type: z.literal("phone_number").describe("Phone number property type"), 163 | phone_number: EMPTY_OBJECT_SCHEMA.describe( 164 | "There is no additional property configuration." 165 | ), 166 | description: z.string().optional().describe("Property description"), 167 | }); 168 | 169 | // 13. Formula property 170 | export const FORMULA_DB_PROPERTY_SCHEMA = z.object({ 171 | type: z.literal("formula").describe("Formula property type"), 172 | formula: z 173 | .object({ 174 | expression: z.string().describe("Formula expression"), 175 | }) 176 | .describe("Formula property configuration"), 177 | description: z.string().optional().describe("Property description"), 178 | }); 179 | 180 | // 14. Relation property 181 | export const RELATION_DB_PROPERTY_SCHEMA = z.object({ 182 | type: z.literal("relation").describe("Relation property type"), 183 | relation: z 184 | .object({ 185 | database_id: z 186 | .string() 187 | .describe("The ID of the database this relation refers to"), 188 | synced_property_name: z 189 | .string() 190 | .optional() 191 | .describe("Synced property name"), 192 | synced_property_id: z.string().optional().describe("Synced property ID"), 193 | single_property: EMPTY_OBJECT_SCHEMA.describe( 194 | "Whether this is a single property relation" 195 | ), 196 | }) 197 | .describe("Relation property configuration"), 198 | description: z.string().optional().describe("Property description"), 199 | }); 200 | 201 | // 15. Rollup property 202 | export const ROLLUP_FUNCTION = z.enum([ 203 | "average", 204 | "checked", 205 | "count_per_group", 206 | "count", 207 | "count_values", 208 | "date_range", 209 | "earliest_date", 210 | "empty", 211 | "latest_date", 212 | "max", 213 | "median", 214 | "min", 215 | "not_empty", 216 | "percent_checked", 217 | "percent_empty", 218 | "percent_not_empty", 219 | "percent_per_group", 220 | "percent_unchecked", 221 | "range", 222 | "show_original", 223 | "show_unique", 224 | "sum", 225 | "unchecked", 226 | "unique", 227 | ]); 228 | 229 | export const ROLLUP_DB_PROPERTY_SCHEMA = z.object({ 230 | type: z.literal("rollup").describe("Rollup property type"), 231 | rollup: z 232 | .object({ 233 | relation_property_name: z 234 | .string() 235 | .describe("Name of the relation property"), 236 | relation_property_id: z.string().describe("ID of the relation property"), 237 | rollup_property_name: z 238 | .string() 239 | .describe("Name of the property to roll up"), 240 | rollup_property_id: z.string().describe("ID of the property to roll up"), 241 | function: ROLLUP_FUNCTION.describe("Rollup function"), 242 | }) 243 | .describe("Rollup property configuration"), 244 | description: z.string().optional().describe("Property description"), 245 | }); 246 | 247 | // 16. Created time property 248 | export const CREATED_TIME_DB_PROPERTY_SCHEMA = z.object({ 249 | type: z.literal("created_time").describe("Created time property type"), 250 | created_time: EMPTY_OBJECT_SCHEMA.describe( 251 | "There is no additional property configuration." 252 | ), 253 | description: z.string().optional().describe("Property description"), 254 | }); 255 | 256 | // 17. Created by property 257 | export const CREATED_BY_DB_PROPERTY_SCHEMA = z.object({ 258 | type: z.literal("created_by").describe("Created by property type"), 259 | created_by: EMPTY_OBJECT_SCHEMA.describe( 260 | "There is no additional property configuration." 261 | ), 262 | description: z.string().optional().describe("Property description"), 263 | }); 264 | 265 | // 18. Last edited time property 266 | export const LAST_EDITED_TIME_DB_PROPERTY_SCHEMA = z.object({ 267 | type: z 268 | .literal("last_edited_time") 269 | .describe("Last edited time property type"), 270 | last_edited_time: EMPTY_OBJECT_SCHEMA.describe( 271 | "There is no additional property configuration." 272 | ), 273 | description: z.string().optional().describe("Property description"), 274 | }); 275 | 276 | // 19. Last edited by property 277 | export const LAST_EDITED_BY_DB_PROPERTY_SCHEMA = z.object({ 278 | type: z.literal("last_edited_by").describe("Last edited by property type"), 279 | last_edited_by: EMPTY_OBJECT_SCHEMA.describe( 280 | "There is no additional property configuration." 281 | ), 282 | description: z.string().optional().describe("Property description"), 283 | }); 284 | 285 | // Status property 286 | export const STATUS_OPTION_SCHEMA = z.object({ 287 | id: z.string().optional().describe("ID of the status option"), 288 | name: z.string().describe("Name of the status option"), 289 | color: SELECT_COLOR_SCHEMA.describe("Color of the status option"), 290 | }); 291 | 292 | export const STATUS_GROUP_SCHEMA = z.object({ 293 | id: z.string().optional().describe("ID of the status group"), 294 | name: z.string().describe("Name of the status group"), 295 | color: SELECT_COLOR_SCHEMA.describe("Color of the status group"), 296 | option_ids: z.array(z.string()).describe("IDs of options in this group"), 297 | }); 298 | 299 | export const STATUS_DB_PROPERTY_SCHEMA = z.object({ 300 | type: z.literal("status").describe("Status property type"), 301 | status: z 302 | .object({ 303 | options: z.array(STATUS_OPTION_SCHEMA).describe("Status options"), 304 | }) 305 | .describe("Status property configuration"), 306 | description: z.string().optional().describe("Property description"), 307 | }); 308 | 309 | // Combined database property schema 310 | export const DATABASE_PROPERTY_SCHEMA = z.preprocess( 311 | preprocessJson, 312 | z 313 | .discriminatedUnion("type", [ 314 | TITLE_DB_PROPERTY_SCHEMA, 315 | RICH_TEXT_DB_PROPERTY_SCHEMA, 316 | NUMBER_DB_PROPERTY_SCHEMA, 317 | SELECT_DB_PROPERTY_SCHEMA, 318 | MULTI_SELECT_DB_PROPERTY_SCHEMA, 319 | DATE_DB_PROPERTY_SCHEMA, 320 | PEOPLE_DB_PROPERTY_SCHEMA, 321 | FILES_DB_PROPERTY_SCHEMA, 322 | CHECKBOX_DB_PROPERTY_SCHEMA, 323 | URL_DB_PROPERTY_SCHEMA, 324 | EMAIL_DB_PROPERTY_SCHEMA, 325 | PHONE_NUMBER_DB_PROPERTY_SCHEMA, 326 | FORMULA_DB_PROPERTY_SCHEMA, 327 | RELATION_DB_PROPERTY_SCHEMA, 328 | ROLLUP_DB_PROPERTY_SCHEMA, 329 | CREATED_TIME_DB_PROPERTY_SCHEMA, 330 | CREATED_BY_DB_PROPERTY_SCHEMA, 331 | LAST_EDITED_TIME_DB_PROPERTY_SCHEMA, 332 | LAST_EDITED_BY_DB_PROPERTY_SCHEMA, 333 | ]) 334 | .describe("Union of all possible database property types") 335 | ); 336 | 337 | // Create database schema 338 | export const CREATE_DATABASE_SCHEMA = { 339 | parent: PARENT_SCHEMA.optional() 340 | .default({ 341 | type: "page_id", 342 | page_id: getRootPageId(), 343 | }) 344 | .describe( 345 | "Optional parent - if not provided, will use NOTION_PAGE_ID as parent page" 346 | ), 347 | title: z.array(TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA).describe("Database title"), 348 | description: z 349 | .array(TEXT_RICH_TEXT_ITEM_REQUEST_SCHEMA) 350 | .optional() 351 | .describe("Database description"), 352 | properties: z 353 | .record( 354 | z.string().describe("Property name"), 355 | DATABASE_PROPERTY_SCHEMA.describe("Property schema") 356 | ) 357 | .describe("Database properties"), 358 | is_inline: z 359 | .boolean() 360 | .optional() 361 | .default(false) 362 | .describe("Whether database is inline"), 363 | icon: z.preprocess( 364 | preprocessJson, 365 | ICON_SCHEMA.nullable().optional().describe("Optional icon for the database") 366 | ), 367 | cover: z.preprocess( 368 | preprocessJson, 369 | FILE_SCHEMA.nullable() 370 | .optional() 371 | .describe("Optional cover image for the database") 372 | ), 373 | }; 374 | 375 | // Query database schema 376 | export const QUERY_DATABASE_SCHEMA = { 377 | database_id: z.string().describe("The ID of the database to query"), 378 | filter: z 379 | .preprocess(preprocessJson, z.any()) 380 | .optional() 381 | .describe("Filter criteria for the query"), 382 | sorts: z 383 | .array( 384 | z.object({ 385 | property: z.string().optional().describe("Property to sort by"), 386 | timestamp: z 387 | .enum(["created_time", "last_edited_time"]) 388 | .describe("Timestamp to sort by"), 389 | direction: z 390 | .enum(["ascending", "descending"]) 391 | .describe("Sort direction"), 392 | }) 393 | ) 394 | .optional() 395 | .describe("Sort criteria for the query"), 396 | start_cursor: z.string().optional().describe("Cursor for pagination"), 397 | page_size: z 398 | .number() 399 | .min(1) 400 | .max(100) 401 | .optional() 402 | .describe("Number of results to return (1-100)"), 403 | }; 404 | 405 | // Update database schema 406 | export const UPDATE_DATABASE_SCHEMA = { 407 | database_id: z.string().describe("The ID of the database to update"), 408 | title: z 409 | .array(RICH_TEXT_ITEM_REQUEST_SCHEMA) 410 | .optional() 411 | .describe("Updated database title"), 412 | description: z 413 | .array(RICH_TEXT_ITEM_REQUEST_SCHEMA) 414 | .optional() 415 | .describe("Updated database description"), 416 | properties: z 417 | .record( 418 | z.string().describe("Property name"), 419 | DATABASE_PROPERTY_SCHEMA.describe("Property schema") 420 | ) 421 | .describe("Properties of the page"), 422 | is_inline: z.boolean().optional().describe("Whether database is inline"), 423 | icon: z.preprocess( 424 | preprocessJson, 425 | ICON_SCHEMA.nullable().optional().describe("Updated icon for the database") 426 | ), 427 | cover: z.preprocess( 428 | preprocessJson, 429 | FILE_SCHEMA.nullable() 430 | .optional() 431 | .describe("Updated cover image for the database") 432 | ), 433 | }; 434 | 435 | // Combined schema for all database operations 436 | export const DATABASE_OPERATION_SCHEMA = { 437 | payload: z 438 | .preprocess( 439 | preprocessJson, 440 | z.discriminatedUnion("action", [ 441 | z.object({ 442 | action: z 443 | .literal("create_database") 444 | .describe("Use this action to create a new database."), 445 | params: z.object(CREATE_DATABASE_SCHEMA), 446 | }), 447 | z.object({ 448 | action: z 449 | .literal("query_database") 450 | .describe("Use this action to query a database."), 451 | params: z.object(QUERY_DATABASE_SCHEMA), 452 | }), 453 | z.object({ 454 | action: z 455 | .literal("update_database") 456 | .describe("Use this action to update a database."), 457 | params: z.object(UPDATE_DATABASE_SCHEMA), 458 | }), 459 | ]) 460 | ) 461 | .describe( 462 | "A union of all possible database operations. Each operation has a specific action and corresponding parameters. Use this schema to validate the input for database operations such as creating, querying, and updating databases. Available actions include: 'create_database', 'query_database', and 'update_database'. Each operation requires specific parameters as defined in the corresponding schemas." 463 | ), 464 | }; 465 | -------------------------------------------------------------------------------- /src/types/emoji.ts: -------------------------------------------------------------------------------- 1 | export type EmojiRequest = 2 | | "😀" 3 | | "😃" 4 | | "😄" 5 | | "😁" 6 | | "😆" 7 | | "😅" 8 | | "🤣" 9 | | "😂" 10 | | "🙂" 11 | | "🙃" 12 | | "🫠" 13 | | "😉" 14 | | "😊" 15 | | "😇" 16 | | "🥰" 17 | | "😍" 18 | | "🤩" 19 | | "😘" 20 | | "😗" 21 | | "☺️" 22 | | "☺" 23 | | "😚" 24 | | "😙" 25 | | "🥲" 26 | | "😋" 27 | | "😛" 28 | | "😜" 29 | | "🤪" 30 | | "😝" 31 | | "🤑" 32 | | "🤗" 33 | | "🤭" 34 | | "🫢" 35 | | "🫣" 36 | | "🤫" 37 | | "🤔" 38 | | "🫡" 39 | | "🤐" 40 | | "🤨" 41 | | "😐" 42 | | "😑" 43 | | "😶" 44 | | "🫥" 45 | | "😶‍🌫️" 46 | | "😶‍🌫" 47 | | "😏" 48 | | "😒" 49 | | "🙄" 50 | | "😬" 51 | | "😮‍💨" 52 | | "🤥" 53 | | "😌" 54 | | "😔" 55 | | "😪" 56 | | "🤤" 57 | | "😴" 58 | | "😷" 59 | | "🤒" 60 | | "🤕" 61 | | "🤢" 62 | | "🤮" 63 | | "🤧" 64 | | "🥵" 65 | | "🥶" 66 | | "🥴" 67 | | "😵" 68 | | "😵‍💫" 69 | | "🤯" 70 | | "🤠" 71 | | "🥳" 72 | | "🥸" 73 | | "😎" 74 | | "🤓" 75 | | "🧐" 76 | | "😕" 77 | | "🫤" 78 | | "😟" 79 | | "🙁" 80 | | "☹️" 81 | | "☹" 82 | | "😮" 83 | | "😯" 84 | | "😲" 85 | | "😳" 86 | | "🥺" 87 | | "🥹" 88 | | "😦" 89 | | "😧" 90 | | "😨" 91 | | "😰" 92 | | "😥" 93 | | "😢" 94 | | "😭" 95 | | "😱" 96 | | "😖" 97 | | "😣" 98 | | "😞" 99 | | "😓" 100 | | "😩" 101 | | "😫" 102 | | "🥱" 103 | | "😤" 104 | | "😡" 105 | | "😠" 106 | | "🤬" 107 | | "😈" 108 | | "👿" 109 | | "💀" 110 | | "☠️" 111 | | "☠" 112 | | "💩" 113 | | "🤡" 114 | | "👹" 115 | | "👺" 116 | | "👻" 117 | | "👽" 118 | | "👾" 119 | | "🤖" 120 | | "😺" 121 | | "😸" 122 | | "😹" 123 | | "😻" 124 | | "😼" 125 | | "😽" 126 | | "🙀" 127 | | "😿" 128 | | "😾" 129 | | "🙈" 130 | | "🙉" 131 | | "🙊" 132 | | "💋" 133 | | "💌" 134 | | "💘" 135 | | "💝" 136 | | "💖" 137 | | "💗" 138 | | "💓" 139 | | "💞" 140 | | "💕" 141 | | "💟" 142 | | "❣️" 143 | | "❣" 144 | | "💔" 145 | | "❤️‍🔥" 146 | | "❤‍🔥" 147 | | "❤️‍🩹" 148 | | "❤‍🩹" 149 | | "❤️" 150 | | "❤" 151 | | "🧡" 152 | | "💛" 153 | | "💚" 154 | | "💙" 155 | | "💜" 156 | | "🤎" 157 | | "🖤" 158 | | "🤍" 159 | | "💯" 160 | | "💢" 161 | | "💥" 162 | | "💫" 163 | | "💦" 164 | | "💨" 165 | | "🕳️" 166 | | "🕳" 167 | | "💣" 168 | | "💬" 169 | | "👁️‍🗨️" 170 | | "🗨️" 171 | | "🗨" 172 | | "🗯️" 173 | | "🗯" 174 | | "💭" 175 | | "💤" 176 | | "👋🏻" 177 | | "👋🏼" 178 | | "👋🏽" 179 | | "👋🏾" 180 | | "👋🏿" 181 | | "👋" 182 | | "🤚🏻" 183 | | "🤚🏼" 184 | | "🤚🏽" 185 | | "🤚🏾" 186 | | "🤚🏿" 187 | | "🤚" 188 | | "🖐🏻" 189 | | "🖐🏼" 190 | | "🖐🏽" 191 | | "🖐🏾" 192 | | "🖐🏿" 193 | | "🖐️" 194 | | "🖐" 195 | | "✋🏻" 196 | | "✋🏼" 197 | | "✋🏽" 198 | | "✋🏾" 199 | | "✋🏿" 200 | | "✋" 201 | | "🖖🏻" 202 | | "🖖🏼" 203 | | "🖖🏽" 204 | | "🖖🏾" 205 | | "🖖🏿" 206 | | "🖖" 207 | | "🫱🏻" 208 | | "🫱🏼" 209 | | "🫱🏽" 210 | | "🫱🏾" 211 | | "🫱🏿" 212 | | "🫱" 213 | | "🫲🏻" 214 | | "🫲🏼" 215 | | "🫲🏽" 216 | | "🫲🏾" 217 | | "🫲🏿" 218 | | "🫲" 219 | | "🫳🏻" 220 | | "🫳🏼" 221 | | "🫳🏽" 222 | | "🫳🏾" 223 | | "🫳🏿" 224 | | "🫳" 225 | | "🫴🏻" 226 | | "🫴🏼" 227 | | "🫴🏽" 228 | | "🫴🏾" 229 | | "🫴🏿" 230 | | "🫴" 231 | | "👌🏻" 232 | | "👌🏼" 233 | | "👌🏽" 234 | | "👌🏾" 235 | | "👌🏿" 236 | | "👌" 237 | | "🤌🏻" 238 | | "🤌🏼" 239 | | "🤌🏽" 240 | | "🤌🏾" 241 | | "🤌🏿" 242 | | "🤌" 243 | | "🤏🏻" 244 | | "🤏🏼" 245 | | "🤏🏽" 246 | | "🤏🏾" 247 | | "🤏🏿" 248 | | "🤏" 249 | | "✌🏻" 250 | | "✌🏼" 251 | | "✌🏽" 252 | | "✌🏾" 253 | | "✌🏿" 254 | | "✌️" 255 | | "✌" 256 | | "🤞🏻" 257 | | "🤞🏼" 258 | | "🤞🏽" 259 | | "🤞🏾" 260 | | "🤞🏿" 261 | | "🤞" 262 | | "🫰🏻" 263 | | "🫰🏼" 264 | | "🫰🏽" 265 | | "🫰🏾" 266 | | "🫰🏿" 267 | | "🫰" 268 | | "🤟🏻" 269 | | "🤟🏼" 270 | | "🤟🏽" 271 | | "🤟🏾" 272 | | "🤟🏿" 273 | | "🤟" 274 | | "🤘🏻" 275 | | "🤘🏼" 276 | | "🤘🏽" 277 | | "🤘🏾" 278 | | "🤘🏿" 279 | | "🤘" 280 | | "🤙🏻" 281 | | "🤙🏼" 282 | | "🤙🏽" 283 | | "🤙🏾" 284 | | "🤙🏿" 285 | | "🤙" 286 | | "👈🏻" 287 | | "👈🏼" 288 | | "👈🏽" 289 | | "👈🏾" 290 | | "👈🏿" 291 | | "👈" 292 | | "👉🏻" 293 | | "👉🏼" 294 | | "👉🏽" 295 | | "👉🏾" 296 | | "👉🏿" 297 | | "👉" 298 | | "👆🏻" 299 | | "👆🏼" 300 | | "👆🏽" 301 | | "👆🏾" 302 | | "👆🏿" 303 | | "👆" 304 | | "🖕🏻" 305 | | "🖕🏼" 306 | | "🖕🏽" 307 | | "🖕🏾" 308 | | "🖕🏿" 309 | | "🖕" 310 | | "👇🏻" 311 | | "👇🏼" 312 | | "👇🏽" 313 | | "👇🏾" 314 | | "👇🏿" 315 | | "👇" 316 | | "☝🏻" 317 | | "☝🏼" 318 | | "☝🏽" 319 | | "☝🏾" 320 | | "☝🏿" 321 | | "☝️" 322 | | "☝" 323 | | "🫵🏻" 324 | | "🫵🏼" 325 | | "🫵🏽" 326 | | "🫵🏾" 327 | | "🫵🏿" 328 | | "🫵" 329 | | "👍🏻" 330 | | "👍🏼" 331 | | "👍🏽" 332 | | "👍🏾" 333 | | "👍🏿" 334 | | "👍" 335 | | "👎🏻" 336 | | "👎🏼" 337 | | "👎🏽" 338 | | "👎🏾" 339 | | "👎🏿" 340 | | "👎" 341 | | "✊🏻" 342 | | "✊🏼" 343 | | "✊🏽" 344 | | "✊🏾" 345 | | "✊🏿" 346 | | "✊" 347 | | "👊🏻" 348 | | "👊🏼" 349 | | "👊🏽" 350 | | "👊🏾" 351 | | "👊🏿" 352 | | "👊" 353 | | "🤛🏻" 354 | | "🤛🏼" 355 | | "🤛🏽" 356 | | "🤛🏾" 357 | | "🤛🏿" 358 | | "🤛" 359 | | "🤜🏻" 360 | | "🤜🏼" 361 | | "🤜🏽" 362 | | "🤜🏾" 363 | | "🤜🏿" 364 | | "🤜" 365 | | "👏🏻" 366 | | "👏🏼" 367 | | "👏🏽" 368 | | "👏🏾" 369 | | "👏🏿" 370 | | "👏" 371 | | "🙌🏻" 372 | | "🙌🏼" 373 | | "🙌🏽" 374 | | "🙌🏾" 375 | | "🙌🏿" 376 | | "🙌" 377 | | "🫶🏻" 378 | | "🫶🏼" 379 | | "🫶🏽" 380 | | "🫶🏾" 381 | | "🫶🏿" 382 | | "🫶" 383 | | "👐🏻" 384 | | "👐🏼" 385 | | "👐🏽" 386 | | "👐🏾" 387 | | "👐🏿" 388 | | "👐" 389 | | "🤲🏻" 390 | | "🤲🏼" 391 | | "🤲🏽" 392 | | "🤲🏾" 393 | | "🤲🏿" 394 | | "🤲" 395 | | "🤝🏻" 396 | | "🤝🏼" 397 | | "🤝🏽" 398 | | "🤝🏾" 399 | | "🤝🏿" 400 | | "🫱🏻‍🫲🏼" 401 | | "🫱🏻‍🫲🏽" 402 | | "🫱🏻‍🫲🏾" 403 | | "🫱🏻‍🫲🏿" 404 | | "🫱🏼‍🫲🏻" 405 | | "🫱🏼‍🫲🏽" 406 | | "🫱🏼‍🫲🏾" 407 | | "🫱🏼‍🫲🏿" 408 | | "🫱🏽‍🫲🏻" 409 | | "🫱🏽‍🫲🏼" 410 | | "🫱🏽‍🫲🏾" 411 | | "🫱🏽‍🫲🏿" 412 | | "🫱🏾‍🫲🏻" 413 | | "🫱🏾‍🫲🏼" 414 | | "🫱🏾‍🫲🏽" 415 | | "🫱🏾‍🫲🏿" 416 | | "🫱🏿‍🫲🏻" 417 | | "🫱🏿‍🫲🏼" 418 | | "🫱🏿‍🫲🏽" 419 | | "🫱🏿‍🫲🏾" 420 | | "🤝" 421 | | "🙏🏻" 422 | | "🙏🏼" 423 | | "🙏🏽" 424 | | "🙏🏾" 425 | | "🙏🏿" 426 | | "🙏" 427 | | "✍🏻" 428 | | "✍🏼" 429 | | "✍🏽" 430 | | "✍🏾" 431 | | "✍🏿" 432 | | "✍️" 433 | | "✍" 434 | | "💅🏻" 435 | | "💅🏼" 436 | | "💅🏽" 437 | | "💅🏾" 438 | | "💅🏿" 439 | | "💅" 440 | | "🤳🏻" 441 | | "🤳🏼" 442 | | "🤳🏽" 443 | | "🤳🏾" 444 | | "🤳🏿" 445 | | "🤳" 446 | | "💪🏻" 447 | | "💪🏼" 448 | | "💪🏽" 449 | | "💪🏾" 450 | | "💪🏿" 451 | | "💪" 452 | | "🦾" 453 | | "🦿" 454 | | "🦵🏻" 455 | | "🦵🏼" 456 | | "🦵🏽" 457 | | "🦵🏾" 458 | | "🦵🏿" 459 | | "🦵" 460 | | "🦶🏻" 461 | | "🦶🏼" 462 | | "🦶🏽" 463 | | "🦶🏾" 464 | | "🦶🏿" 465 | | "🦶" 466 | | "👂🏻" 467 | | "👂🏼" 468 | | "👂🏽" 469 | | "👂🏾" 470 | | "👂🏿" 471 | | "👂" 472 | | "🦻🏻" 473 | | "🦻🏼" 474 | | "🦻🏽" 475 | | "🦻🏾" 476 | | "🦻🏿" 477 | | "🦻" 478 | | "👃🏻" 479 | | "👃🏼" 480 | | "👃🏽" 481 | | "👃🏾" 482 | | "👃🏿" 483 | | "👃" 484 | | "🧠" 485 | | "🫀" 486 | | "🫁" 487 | | "🦷" 488 | | "🦴" 489 | | "👀" 490 | | "👁️" 491 | | "👁" 492 | | "👅" 493 | | "👄" 494 | | "🫦" 495 | | "👶🏻" 496 | | "👶🏼" 497 | | "👶🏽" 498 | | "👶🏾" 499 | | "👶🏿" 500 | | "👶" 501 | | "🧒🏻" 502 | | "🧒🏼" 503 | | "🧒🏽" 504 | | "🧒🏾" 505 | | "🧒🏿" 506 | | "🧒" 507 | | "👦🏻" 508 | | "👦🏼" 509 | | "👦🏽" 510 | | "👦🏾" 511 | | "👦🏿" 512 | | "👦" 513 | | "👧🏻" 514 | | "👧🏼" 515 | | "👧🏽" 516 | | "👧🏾" 517 | | "👧🏿" 518 | | "👧" 519 | | "🧑🏻" 520 | | "🧑🏼" 521 | | "🧑🏽" 522 | | "🧑🏾" 523 | | "🧑🏿" 524 | | "🧑" 525 | | "👱🏻" 526 | | "👱🏼" 527 | | "👱🏽" 528 | | "👱🏾" 529 | | "👱🏿" 530 | | "👱" 531 | | "👨🏻" 532 | | "👨🏼" 533 | | "👨🏽" 534 | | "👨🏾" 535 | | "👨🏿" 536 | | "👨" 537 | | "🧔🏻" 538 | | "🧔🏼" 539 | | "🧔🏽" 540 | | "🧔🏾" 541 | | "🧔🏿" 542 | | "🧔" 543 | | "🧔🏻‍♂️" 544 | | "🧔🏼‍♂️" 545 | | "🧔🏽‍♂️" 546 | | "🧔🏾‍♂️" 547 | | "🧔🏿‍♂️" 548 | | "🧔‍♂️" 549 | | "🧔‍♂" 550 | | "🧔🏻‍♀️" 551 | | "🧔🏼‍♀️" 552 | | "🧔🏽‍♀️" 553 | | "🧔🏾‍♀️" 554 | | "🧔🏿‍♀️" 555 | | "🧔‍♀️" 556 | | "🧔‍♀" 557 | | "👨🏻‍🦰" 558 | | "👨🏼‍🦰" 559 | | "👨🏽‍🦰" 560 | | "👨🏾‍🦰" 561 | | "👨🏿‍🦰" 562 | | "👨‍🦰" 563 | | "👨🏻‍🦱" 564 | | "👨🏼‍🦱" 565 | | "👨🏽‍🦱" 566 | | "👨🏾‍🦱" 567 | | "👨🏿‍🦱" 568 | | "👨‍🦱" 569 | | "👨🏻‍🦳" 570 | | "👨🏼‍🦳" 571 | | "👨🏽‍🦳" 572 | | "👨🏾‍🦳" 573 | | "👨🏿‍🦳" 574 | | "👨‍🦳" 575 | | "👨🏻‍🦲" 576 | | "👨🏼‍🦲" 577 | | "👨🏽‍🦲" 578 | | "👨🏾‍🦲" 579 | | "👨🏿‍🦲" 580 | | "👨‍🦲" 581 | | "👩🏻" 582 | | "👩🏼" 583 | | "👩🏽" 584 | | "👩🏾" 585 | | "👩🏿" 586 | | "👩" 587 | | "👩🏻‍🦰" 588 | | "👩🏼‍🦰" 589 | | "👩🏽‍🦰" 590 | | "👩🏾‍🦰" 591 | | "👩🏿‍🦰" 592 | | "👩‍🦰" 593 | | "🧑🏻‍🦰" 594 | | "🧑🏼‍🦰" 595 | | "🧑🏽‍🦰" 596 | | "🧑🏾‍🦰" 597 | | "🧑🏿‍🦰" 598 | | "🧑‍🦰" 599 | | "👩🏻‍🦱" 600 | | "👩🏼‍🦱" 601 | | "👩🏽‍🦱" 602 | | "👩🏾‍🦱" 603 | | "👩🏿‍🦱" 604 | | "👩‍🦱" 605 | | "🧑🏻‍🦱" 606 | | "🧑🏼‍🦱" 607 | | "🧑🏽‍🦱" 608 | | "🧑🏾‍🦱" 609 | | "🧑🏿‍🦱" 610 | | "🧑‍🦱" 611 | | "👩🏻‍🦳" 612 | | "👩🏼‍🦳" 613 | | "👩🏽‍🦳" 614 | | "👩🏾‍🦳" 615 | | "👩🏿‍🦳" 616 | | "👩‍🦳" 617 | | "🧑🏻‍🦳" 618 | | "🧑🏼‍🦳" 619 | | "🧑🏽‍🦳" 620 | | "🧑🏾‍🦳" 621 | | "🧑🏿‍🦳" 622 | | "🧑‍🦳" 623 | | "👩🏻‍🦲" 624 | | "👩🏼‍🦲" 625 | | "👩🏽‍🦲" 626 | | "👩🏾‍🦲" 627 | | "👩🏿‍🦲" 628 | | "👩‍🦲" 629 | | "🧑🏻‍🦲" 630 | | "🧑🏼‍🦲" 631 | | "🧑🏽‍🦲" 632 | | "🧑🏾‍🦲" 633 | | "🧑🏿‍🦲" 634 | | "🧑‍🦲" 635 | | "👱🏻‍♀️" 636 | | "👱🏼‍♀️" 637 | | "👱🏽‍♀️" 638 | | "👱🏾‍♀️" 639 | | "👱🏿‍♀️" 640 | | "👱‍♀️" 641 | | "👱‍♀" 642 | | "👱🏻‍♂️" 643 | | "👱🏼‍♂️" 644 | | "👱🏽‍♂️" 645 | | "👱🏾‍♂️" 646 | | "👱🏿‍♂️" 647 | | "👱‍♂️" 648 | | "👱‍♂" 649 | | "🧓🏻" 650 | | "🧓🏼" 651 | | "🧓🏽" 652 | | "🧓🏾" 653 | | "🧓🏿" 654 | | "🧓" 655 | | "👴🏻" 656 | | "👴🏼" 657 | | "👴🏽" 658 | | "👴🏾" 659 | | "👴🏿" 660 | | "👴" 661 | | "👵🏻" 662 | | "👵🏼" 663 | | "👵🏽" 664 | | "👵🏾" 665 | | "👵🏿" 666 | | "👵" 667 | | "🙍🏻" 668 | | "🙍🏼" 669 | | "🙍🏽" 670 | | "🙍🏾" 671 | | "🙍🏿" 672 | | "🙍" 673 | | "🙍🏻‍♂️" 674 | | "🙍🏼‍♂️" 675 | | "🙍🏽‍♂️" 676 | | "🙍🏾‍♂️" 677 | | "🙍🏿‍♂️" 678 | | "🙍‍♂️" 679 | | "🙍‍♂" 680 | | "🙍🏻‍♀️" 681 | | "🙍🏼‍♀️" 682 | | "🙍🏽‍♀️" 683 | | "🙍🏾‍♀️" 684 | | "🙍🏿‍♀️" 685 | | "🙍‍♀️" 686 | | "🙍‍♀" 687 | | "🙎🏻" 688 | | "🙎🏼" 689 | | "🙎🏽" 690 | | "🙎🏾" 691 | | "🙎🏿" 692 | | "🙎" 693 | | "🙎🏻‍♂️" 694 | | "🙎🏼‍♂️" 695 | | "🙎🏽‍♂️" 696 | | "🙎🏾‍♂️" 697 | | "🙎🏿‍♂️" 698 | | "🙎‍♂️" 699 | | "🙎‍♂" 700 | | "🙎🏻‍♀️" 701 | | "🙎🏼‍♀️" 702 | | "🙎🏽‍♀️" 703 | | "🙎🏾‍♀️" 704 | | "🙎🏿‍♀️" 705 | | "🙎‍♀️" 706 | | "🙎‍♀" 707 | | "🙅🏻" 708 | | "🙅🏼" 709 | | "🙅🏽" 710 | | "🙅🏾" 711 | | "🙅🏿" 712 | | "🙅" 713 | | "🙅🏻‍♂️" 714 | | "🙅🏼‍♂️" 715 | | "🙅🏽‍♂️" 716 | | "🙅🏾‍♂️" 717 | | "🙅🏿‍♂️" 718 | | "🙅‍♂️" 719 | | "🙅‍♂" 720 | | "🙅🏻‍♀️" 721 | | "🙅🏼‍♀️" 722 | | "🙅🏽‍♀️" 723 | | "🙅🏾‍♀️" 724 | | "🙅🏿‍♀️" 725 | | "🙅‍♀️" 726 | | "🙅‍♀" 727 | | "🙆🏻" 728 | | "🙆🏼" 729 | | "🙆🏽" 730 | | "🙆🏾" 731 | | "🙆🏿" 732 | | "🙆" 733 | | "🙆🏻‍♂️" 734 | | "🙆🏼‍♂️" 735 | | "🙆🏽‍♂️" 736 | | "🙆🏾‍♂️" 737 | | "🙆🏿‍♂️" 738 | | "🙆‍♂️" 739 | | "🙆‍♂" 740 | | "🙆🏻‍♀️" 741 | | "🙆🏼‍♀️" 742 | | "🙆🏽‍♀️" 743 | | "🙆🏾‍♀️" 744 | | "🙆🏿‍♀️" 745 | | "🙆‍♀️" 746 | | "🙆‍♀" 747 | | "💁🏻" 748 | | "💁🏼" 749 | | "💁🏽" 750 | | "💁🏾" 751 | | "💁🏿" 752 | | "💁" 753 | | "💁🏻‍♂️" 754 | | "💁🏼‍♂️" 755 | | "💁🏽‍♂️" 756 | | "💁🏾‍♂️" 757 | | "💁🏿‍♂️" 758 | | "💁‍♂️" 759 | | "💁‍♂" 760 | | "💁🏻‍♀️" 761 | | "💁🏼‍♀️" 762 | | "💁🏽‍♀️" 763 | | "💁🏾‍♀️" 764 | | "💁🏿‍♀️" 765 | | "💁‍♀️" 766 | | "💁‍♀" 767 | | "🙋🏻" 768 | | "🙋🏼" 769 | | "🙋🏽" 770 | | "🙋🏾" 771 | | "🙋🏿" 772 | | "🙋" 773 | | "🙋🏻‍♂️" 774 | | "🙋🏼‍♂️" 775 | | "🙋🏽‍♂️" 776 | | "🙋🏾‍♂️" 777 | | "🙋🏿‍♂️" 778 | | "🙋‍♂️" 779 | | "🙋‍♂" 780 | | "🙋🏻‍♀️" 781 | | "🙋🏼‍♀️" 782 | | "🙋🏽‍♀️" 783 | | "🙋🏾‍♀️" 784 | | "🙋🏿‍♀️" 785 | | "🙋‍♀️" 786 | | "🙋‍♀" 787 | | "🧏🏻" 788 | | "🧏🏼" 789 | | "🧏🏽" 790 | | "🧏🏾" 791 | | "🧏🏿" 792 | | "🧏" 793 | | "🧏🏻‍♂️" 794 | | "🧏🏼‍♂️" 795 | | "🧏🏽‍♂️" 796 | | "🧏🏾‍♂️" 797 | | "🧏🏿‍♂️" 798 | | "🧏‍♂️" 799 | | "🧏‍♂" 800 | | "🧏🏻‍♀️" 801 | | "🧏🏼‍♀️" 802 | | "🧏🏽‍♀️" 803 | | "🧏🏾‍♀️" 804 | | "🧏🏿‍♀️" 805 | | "🧏‍♀️" 806 | | "🧏‍♀" 807 | | "🙇🏻" 808 | | "🙇🏼" 809 | | "🙇🏽" 810 | | "🙇🏾" 811 | | "🙇🏿" 812 | | "🙇" 813 | | "🙇🏻‍♂️" 814 | | "🙇🏼‍♂️" 815 | | "🙇🏽‍♂️" 816 | | "🙇🏾‍♂️" 817 | | "🙇🏿‍♂️" 818 | | "🙇‍♂️" 819 | | "🙇‍♂" 820 | | "🙇🏻‍♀️" 821 | | "🙇🏼‍♀️" 822 | | "🙇🏽‍♀️" 823 | | "🙇🏾‍♀️" 824 | | "🙇🏿‍♀️" 825 | | "🙇‍♀️" 826 | | "🙇‍♀" 827 | | "🤦🏻" 828 | | "🤦🏼" 829 | | "🤦🏽" 830 | | "🤦🏾" 831 | | "🤦🏿" 832 | | "🤦" 833 | | "🤦🏻‍♂️" 834 | | "🤦🏼‍♂️" 835 | | "🤦🏽‍♂️" 836 | | "🤦🏾‍♂️" 837 | | "🤦🏿‍♂️" 838 | | "🤦‍♂️" 839 | | "🤦‍♂" 840 | | "🤦🏻‍♀️" 841 | | "🤦🏼‍♀️" 842 | | "🤦🏽‍♀️" 843 | | "🤦🏾‍♀️" 844 | | "🤦🏿‍♀️" 845 | | "🤦‍♀️" 846 | | "🤦‍♀" 847 | | "🤷🏻" 848 | | "🤷🏼" 849 | | "🤷🏽" 850 | | "🤷🏾" 851 | | "🤷🏿" 852 | | "🤷" 853 | | "🤷🏻‍♂️" 854 | | "🤷🏼‍♂️" 855 | | "🤷🏽‍♂️" 856 | | "🤷🏾‍♂️" 857 | | "🤷🏿‍♂️" 858 | | "🤷‍♂️" 859 | | "🤷‍♂" 860 | | "🤷🏻‍♀️" 861 | | "🤷🏼‍♀️" 862 | | "🤷🏽‍♀️" 863 | | "🤷🏾‍♀️" 864 | | "🤷🏿‍♀️" 865 | | "🤷‍♀️" 866 | | "🤷‍♀" 867 | | "🧑🏻‍⚕️" 868 | | "🧑🏼‍⚕️" 869 | | "🧑🏽‍⚕️" 870 | | "🧑🏾‍⚕️" 871 | | "🧑🏿‍⚕️" 872 | | "🧑‍⚕️" 873 | | "🧑‍⚕" 874 | | "👨🏻‍⚕️" 875 | | "👨🏼‍⚕️" 876 | | "👨🏽‍⚕️" 877 | | "👨🏾‍⚕️" 878 | | "👨🏿‍⚕️" 879 | | "👨‍⚕️" 880 | | "👨‍⚕" 881 | | "👩🏻‍⚕️" 882 | | "👩🏼‍⚕️" 883 | | "👩🏽‍⚕️" 884 | | "👩🏾‍⚕️" 885 | | "👩🏿‍⚕️" 886 | | "👩‍⚕️" 887 | | "👩‍⚕" 888 | | "🧑🏻‍🎓" 889 | | "🧑🏼‍🎓" 890 | | "🧑🏽‍🎓" 891 | | "🧑🏾‍🎓" 892 | | "🧑🏿‍🎓" 893 | | "🧑‍🎓" 894 | | "👨🏻‍🎓" 895 | | "👨🏼‍🎓" 896 | | "👨🏽‍🎓" 897 | | "👨🏾‍🎓" 898 | | "👨🏿‍🎓" 899 | | "👨‍🎓" 900 | | "👩🏻‍🎓" 901 | | "👩🏼‍🎓" 902 | | "👩🏽‍🎓" 903 | | "👩🏾‍🎓" 904 | | "👩🏿‍🎓" 905 | | "👩‍🎓" 906 | | "🧑🏻‍🏫" 907 | | "🧑🏼‍🏫" 908 | | "🧑🏽‍🏫" 909 | | "🧑🏾‍🏫" 910 | | "🧑🏿‍🏫" 911 | | "🧑‍🏫" 912 | | "👨🏻‍🏫" 913 | | "👨🏼‍🏫" 914 | | "👨🏽‍🏫" 915 | | "👨🏾‍🏫" 916 | | "👨🏿‍🏫" 917 | | "👨‍🏫" 918 | | "👩🏻‍🏫" 919 | | "👩🏼‍🏫" 920 | | "👩🏽‍🏫" 921 | | "👩🏾‍🏫" 922 | | "👩🏿‍🏫" 923 | | "👩‍🏫" 924 | | "🧑🏻‍⚖️" 925 | | "🧑🏼‍⚖️" 926 | | "🧑🏽‍⚖️" 927 | | "🧑🏾‍⚖️" 928 | | "🧑🏿‍⚖️" 929 | | "🧑‍⚖️" 930 | | "🧑‍⚖" 931 | | "👨🏻‍⚖️" 932 | | "👨🏼‍⚖️" 933 | | "👨🏽‍⚖️" 934 | | "👨🏾‍⚖️" 935 | | "👨🏿‍⚖️" 936 | | "👨‍⚖️" 937 | | "👨‍⚖" 938 | | "👩🏻‍⚖️" 939 | | "👩🏼‍⚖️" 940 | | "👩🏽‍⚖️" 941 | | "👩🏾‍⚖️" 942 | | "👩🏿‍⚖️" 943 | | "👩‍⚖️" 944 | | "👩‍⚖" 945 | | "🧑🏻‍🌾" 946 | | "🧑🏼‍🌾" 947 | | "🧑🏽‍🌾" 948 | | "🧑🏾‍🌾" 949 | | "🧑🏿‍🌾" 950 | | "🧑‍🌾" 951 | | "👨🏻‍🌾" 952 | | "👨🏼‍🌾" 953 | | "👨🏽‍🌾" 954 | | "👨🏾‍🌾" 955 | | "👨🏿‍🌾" 956 | | "👨‍🌾" 957 | | "👩🏻‍🌾" 958 | | "👩🏼‍🌾" 959 | | "👩🏽‍🌾" 960 | | "👩🏾‍🌾" 961 | | "👩🏿‍🌾" 962 | | "👩‍🌾" 963 | | "🧑🏻‍🍳" 964 | | "🧑🏼‍🍳" 965 | | "🧑🏽‍🍳" 966 | | "🧑🏾‍🍳" 967 | | "🧑🏿‍🍳" 968 | | "🧑‍🍳" 969 | | "👨🏻‍🍳" 970 | | "👨🏼‍🍳" 971 | | "👨🏽‍🍳" 972 | | "👨🏾‍🍳" 973 | | "👨🏿‍🍳" 974 | | "👨‍🍳" 975 | | "👩🏻‍🍳" 976 | | "👩🏼‍🍳" 977 | | "👩🏽‍🍳" 978 | | "👩🏾‍🍳" 979 | | "👩🏿‍🍳" 980 | | "👩‍🍳" 981 | | "🧑🏻‍🔧" 982 | | "🧑🏼‍🔧" 983 | | "🧑🏽‍🔧" 984 | | "🧑🏾‍🔧" 985 | | "🧑🏿‍🔧" 986 | | "🧑‍🔧" 987 | | "👨🏻‍🔧" 988 | | "👨🏼‍🔧" 989 | | "👨🏽‍🔧" 990 | | "👨🏾‍🔧" 991 | | "👨🏿‍🔧" 992 | | "👨‍🔧" 993 | | "👩🏻‍🔧" 994 | | "👩🏼‍🔧" 995 | | "👩🏽‍🔧" 996 | | "👩🏾‍🔧" 997 | | "👩🏿‍🔧" 998 | | "👩‍🔧" 999 | | "🧑🏻‍🏭" 1000 | | "🧑🏼‍🏭" 1001 | | "🧑🏽‍🏭" 1002 | | "🧑🏾‍🏭" 1003 | | "🧑🏿‍🏭" 1004 | | "🧑‍🏭" 1005 | | "👨🏻‍🏭" 1006 | | "👨🏼‍🏭" 1007 | | "👨🏽‍🏭" 1008 | | "👨🏾‍🏭" 1009 | | "👨🏿‍🏭" 1010 | | "👨‍🏭" 1011 | | "👩🏻‍🏭" 1012 | | "👩🏼‍🏭" 1013 | | "👩🏽‍🏭" 1014 | | "👩🏾‍🏭" 1015 | | "👩🏿‍🏭" 1016 | | "👩‍🏭" 1017 | | "🧑🏻‍💼" 1018 | | "🧑🏼‍💼" 1019 | | "🧑🏽‍💼" 1020 | | "🧑🏾‍💼" 1021 | | "🧑🏿‍💼" 1022 | | "🧑‍💼" 1023 | | "👨🏻‍💼" 1024 | | "👨🏼‍💼" 1025 | | "👨🏽‍💼" 1026 | | "👨🏾‍💼" 1027 | | "👨🏿‍💼" 1028 | | "👨‍💼" 1029 | | "👩🏻‍💼" 1030 | | "👩🏼‍💼" 1031 | | "👩🏽‍💼" 1032 | | "👩🏾‍💼" 1033 | | "👩🏿‍💼" 1034 | | "👩‍💼" 1035 | | "🧑🏻‍🔬" 1036 | | "🧑🏼‍🔬" 1037 | | "🧑🏽‍🔬" 1038 | | "🧑🏾‍🔬" 1039 | | "🧑🏿‍🔬" 1040 | | "🧑‍🔬" 1041 | | "👨🏻‍🔬" 1042 | | "👨🏼‍🔬" 1043 | | "👨🏽‍🔬" 1044 | | "👨🏾‍🔬" 1045 | | "👨🏿‍🔬" 1046 | | "👨‍🔬" 1047 | | "👩🏻‍🔬" 1048 | | "👩🏼‍🔬" 1049 | | "👩🏽‍🔬" 1050 | | "👩🏾‍🔬" 1051 | | "👩🏿‍🔬" 1052 | | "👩‍🔬" 1053 | | "🧑🏻‍💻" 1054 | | "🧑🏼‍💻" 1055 | | "🧑🏽‍💻" 1056 | | "🧑🏾‍💻" 1057 | | "🧑🏿‍💻" 1058 | | "🧑‍💻" 1059 | | "👨🏻‍💻" 1060 | | "👨🏼‍💻" 1061 | | "👨🏽‍💻" 1062 | | "👨🏾‍💻" 1063 | | "👨🏿‍💻" 1064 | | "👨‍💻" 1065 | | "👩🏻‍💻" 1066 | | "👩🏼‍💻" 1067 | | "👩🏽‍💻" 1068 | | "👩🏾‍💻" 1069 | | "👩🏿‍💻" 1070 | | "👩‍💻" 1071 | | "🧑🏻‍🎤" 1072 | | "🧑🏼‍🎤" 1073 | | "🧑🏽‍🎤" 1074 | | "🧑🏾‍🎤" 1075 | | "🧑🏿‍🎤" 1076 | | "🧑‍🎤" 1077 | | "👨🏻‍🎤" 1078 | | "👨🏼‍🎤" 1079 | | "👨🏽‍🎤" 1080 | | "👨🏾‍🎤" 1081 | | "👨🏿‍🎤" 1082 | | "👨‍🎤" 1083 | | "👩🏻‍🎤" 1084 | | "👩🏼‍🎤" 1085 | | "👩🏽‍🎤" 1086 | | "👩🏾‍🎤" 1087 | | "👩🏿‍🎤" 1088 | | "👩‍🎤" 1089 | | "🧑🏻‍🎨" 1090 | | "🧑🏼‍🎨" 1091 | | "🧑🏽‍🎨" 1092 | | "🧑🏾‍🎨" 1093 | | "🧑🏿‍🎨" 1094 | | "🧑‍🎨" 1095 | | "👨🏻‍🎨" 1096 | | "👨🏼‍🎨" 1097 | | "👨🏽‍🎨" 1098 | | "👨🏾‍🎨" 1099 | | "👨🏿‍🎨" 1100 | | "👨‍🎨" 1101 | | "👩🏻‍🎨" 1102 | | "👩🏼‍🎨" 1103 | | "👩🏽‍🎨" 1104 | | "👩🏾‍🎨" 1105 | | "👩🏿‍🎨" 1106 | | "👩‍🎨" 1107 | | "🧑🏻‍✈️" 1108 | | "🧑🏼‍✈️" 1109 | | "🧑🏽‍✈️" 1110 | | "🧑🏾‍✈️" 1111 | | "🧑🏿‍✈️" 1112 | | "🧑‍✈️" 1113 | | "🧑‍✈" 1114 | | "👨🏻‍✈️" 1115 | | "👨🏼‍✈️" 1116 | | "👨🏽‍✈️" 1117 | | "👨🏾‍✈️" 1118 | | "👨🏿‍✈️" 1119 | | "👨‍✈️" 1120 | | "👨‍✈" 1121 | | "👩🏻‍✈️" 1122 | | "👩🏼‍✈️" 1123 | | "👩🏽‍✈️" 1124 | | "👩🏾‍✈️" 1125 | | "👩🏿‍✈️" 1126 | | "👩‍✈️" 1127 | | "👩‍✈" 1128 | | "🧑🏻‍🚀" 1129 | | "🧑🏼‍🚀" 1130 | | "🧑🏽‍🚀" 1131 | | "🧑🏾‍🚀" 1132 | | "🧑🏿‍🚀" 1133 | | "🧑‍🚀" 1134 | | "👨🏻‍🚀" 1135 | | "👨🏼‍🚀" 1136 | | "👨🏽‍🚀" 1137 | | "👨🏾‍🚀" 1138 | | "👨🏿‍🚀" 1139 | | "👨‍🚀" 1140 | | "👩🏻‍🚀" 1141 | | "👩🏼‍🚀" 1142 | | "👩🏽‍🚀" 1143 | | "👩🏾‍🚀" 1144 | | "👩🏿‍🚀" 1145 | | "👩‍🚀" 1146 | | "🧑🏻‍🚒" 1147 | | "🧑🏼‍🚒" 1148 | | "🧑🏽‍🚒" 1149 | | "🧑🏾‍🚒" 1150 | | "🧑🏿‍🚒" 1151 | | "🧑‍🚒" 1152 | | "👨🏻‍🚒" 1153 | | "👨🏼‍🚒" 1154 | | "👨🏽‍🚒" 1155 | | "👨🏾‍🚒" 1156 | | "👨🏿‍🚒" 1157 | | "👨‍🚒" 1158 | | "👩🏻‍🚒" 1159 | | "👩🏼‍🚒" 1160 | | "👩🏽‍🚒" 1161 | | "👩🏾‍🚒" 1162 | | "👩🏿‍🚒" 1163 | | "👩‍🚒" 1164 | | "👮🏻" 1165 | | "👮🏼" 1166 | | "👮🏽" 1167 | | "👮🏾" 1168 | | "👮🏿" 1169 | | "👮" 1170 | | "👮🏻‍♂️" 1171 | | "👮🏼‍♂️" 1172 | | "👮🏽‍♂️" 1173 | | "👮🏾‍♂️" 1174 | | "👮🏿‍♂️" 1175 | | "👮‍♂️" 1176 | | "👮‍♂" 1177 | | "👮🏻‍♀️" 1178 | | "👮🏼‍♀️" 1179 | | "👮🏽‍♀️" 1180 | | "👮🏾‍♀️" 1181 | | "👮🏿‍♀️" 1182 | | "👮‍♀️" 1183 | | "👮‍♀" 1184 | | "🕵🏻" 1185 | | "🕵🏼" 1186 | | "🕵🏽" 1187 | | "🕵🏾" 1188 | | "🕵🏿" 1189 | | "🕵️" 1190 | | "🕵" 1191 | | "🕵🏻‍♂️" 1192 | | "🕵🏼‍♂️" 1193 | | "🕵🏽‍♂️" 1194 | | "🕵🏾‍♂️" 1195 | | "🕵🏿‍♂️" 1196 | | "🕵️‍♂️" 1197 | | "🕵🏻‍♀️" 1198 | | "🕵🏼‍♀️" 1199 | | "🕵🏽‍♀️" 1200 | | "🕵🏾‍♀️" 1201 | | "🕵🏿‍♀️" 1202 | | "🕵️‍♀️" 1203 | | "💂🏻" 1204 | | "💂🏼" 1205 | | "💂🏽" 1206 | | "💂🏾" 1207 | | "💂🏿" 1208 | | "💂" 1209 | | "💂🏻‍♂️" 1210 | | "💂🏼‍♂️" 1211 | | "💂🏽‍♂️" 1212 | | "💂🏾‍♂️" 1213 | | "💂🏿‍♂️" 1214 | | "💂‍♂️" 1215 | | "💂‍♂" 1216 | | "💂🏻‍♀️" 1217 | | "💂🏼‍♀️" 1218 | | "💂🏽‍♀️" 1219 | | "💂🏾‍♀️" 1220 | | "💂🏿‍♀️" 1221 | | "💂‍♀️" 1222 | | "💂‍♀" 1223 | | "🥷🏻" 1224 | | "🥷🏼" 1225 | | "🥷🏽" 1226 | | "🥷🏾" 1227 | | "🥷🏿" 1228 | | "🥷" 1229 | | "👷🏻" 1230 | | "👷🏼" 1231 | | "👷🏽" 1232 | | "👷🏾" 1233 | | "👷🏿" 1234 | | "👷" 1235 | | "👷🏻‍♂️" 1236 | | "👷🏼‍♂️" 1237 | | "👷🏽‍♂️" 1238 | | "👷🏾‍♂️" 1239 | | "👷🏿‍♂️" 1240 | | "👷‍♂️" 1241 | | "👷‍♂" 1242 | | "👷🏻‍♀️" 1243 | | "👷🏼‍♀️" 1244 | | "👷🏽‍♀️" 1245 | | "👷🏾‍♀️" 1246 | | "👷🏿‍♀️" 1247 | | "👷‍♀️" 1248 | | "👷‍♀" 1249 | | "🫅🏻" 1250 | | "🫅🏼" 1251 | | "🫅🏽" 1252 | | "🫅🏾" 1253 | | "🫅🏿" 1254 | | "🫅" 1255 | | "🤴🏻" 1256 | | "🤴🏼" 1257 | | "🤴🏽" 1258 | | "🤴🏾" 1259 | | "🤴🏿" 1260 | | "🤴" 1261 | | "👸🏻" 1262 | | "👸🏼" 1263 | | "👸🏽" 1264 | | "👸🏾" 1265 | | "👸🏿" 1266 | | "👸" 1267 | | "👳🏻" 1268 | | "👳🏼" 1269 | | "👳🏽" 1270 | | "👳🏾" 1271 | | "👳🏿" 1272 | | "👳" 1273 | | "👳🏻‍♂️" 1274 | | "👳🏼‍♂️" 1275 | | "👳🏽‍♂️" 1276 | | "👳🏾‍♂️" 1277 | | "👳🏿‍♂️" 1278 | | "👳‍♂️" 1279 | | "👳‍♂" 1280 | | "👳🏻‍♀️" 1281 | | "👳🏼‍♀️" 1282 | | "👳🏽‍♀️" 1283 | | "👳🏾‍♀️" 1284 | | "👳🏿‍♀️" 1285 | | "👳‍♀️" 1286 | | "👳‍♀" 1287 | | "👲🏻" 1288 | | "👲🏼" 1289 | | "👲🏽" 1290 | | "👲🏾" 1291 | | "👲🏿" 1292 | | "👲" 1293 | | "🧕🏻" 1294 | | "🧕🏼" 1295 | | "🧕🏽" 1296 | | "🧕🏾" 1297 | | "🧕🏿" 1298 | | "🧕" 1299 | | "🤵🏻" 1300 | | "🤵🏼" 1301 | | "🤵🏽" 1302 | | "🤵🏾" 1303 | | "🤵🏿" 1304 | | "🤵" 1305 | | "🤵🏻‍♂️" 1306 | | "🤵🏼‍♂️" 1307 | | "🤵🏽‍♂️" 1308 | | "🤵🏾‍♂️" 1309 | | "🤵🏿‍♂️" 1310 | | "🤵‍♂️" 1311 | | "🤵‍♂" 1312 | | "🤵🏻‍♀️" 1313 | | "🤵🏼‍♀️" 1314 | | "🤵🏽‍♀️" 1315 | | "🤵🏾‍♀️" 1316 | | "🤵🏿‍♀️" 1317 | | "🤵‍♀️" 1318 | | "🤵‍♀" 1319 | | "👰🏻" 1320 | | "👰🏼" 1321 | | "👰🏽" 1322 | | "👰🏾" 1323 | | "👰🏿" 1324 | | "👰" 1325 | | "👰🏻‍♂️" 1326 | | "👰🏼‍♂️" 1327 | | "👰🏽‍♂️" 1328 | | "👰🏾‍♂️" 1329 | | "👰🏿‍♂️" 1330 | | "👰‍♂️" 1331 | | "👰‍♂" 1332 | | "👰🏻‍♀️" 1333 | | "👰🏼‍♀️" 1334 | | "👰🏽‍♀️" 1335 | | "👰🏾‍♀️" 1336 | | "👰🏿‍♀️" 1337 | | "👰‍♀️" 1338 | | "👰‍♀" 1339 | | "🤰🏻" 1340 | | "🤰🏼" 1341 | | "🤰🏽" 1342 | | "🤰🏾" 1343 | | "🤰🏿" 1344 | | "🤰" 1345 | | "🫃🏻" 1346 | | "🫃🏼" 1347 | | "🫃🏽" 1348 | | "🫃🏾" 1349 | | "🫃🏿" 1350 | | "🫃" 1351 | | "🫄🏻" 1352 | | "🫄🏼" 1353 | | "🫄🏽" 1354 | | "🫄🏾" 1355 | | "🫄🏿" 1356 | | "🫄" 1357 | | "🤱🏻" 1358 | | "🤱🏼" 1359 | | "🤱🏽" 1360 | | "🤱🏾" 1361 | | "🤱🏿" 1362 | | "🤱" 1363 | | "👩🏻‍🍼" 1364 | | "👩🏼‍🍼" 1365 | | "👩🏽‍🍼" 1366 | | "👩🏾‍🍼" 1367 | | "👩🏿‍🍼" 1368 | | "👩‍🍼" 1369 | | "👨🏻‍🍼" 1370 | | "👨🏼‍🍼" 1371 | | "👨🏽‍🍼" 1372 | | "👨🏾‍🍼" 1373 | | "👨🏿‍🍼" 1374 | | "👨‍🍼" 1375 | | "🧑🏻‍🍼" 1376 | | "🧑🏼‍🍼" 1377 | | "🧑🏽‍🍼" 1378 | | "🧑🏾‍🍼" 1379 | | "🧑🏿‍🍼" 1380 | | "🧑‍🍼" 1381 | | "👼🏻" 1382 | | "👼🏼" 1383 | | "👼🏽" 1384 | | "👼🏾" 1385 | | "👼🏿" 1386 | | "👼" 1387 | | "🎅🏻" 1388 | | "🎅🏼" 1389 | | "🎅🏽" 1390 | | "🎅🏾" 1391 | | "🎅🏿" 1392 | | "🎅" 1393 | | "🤶🏻" 1394 | | "🤶🏼" 1395 | | "🤶🏽" 1396 | | "🤶🏾" 1397 | | "🤶🏿" 1398 | | "🤶" 1399 | | "🧑🏻‍🎄" 1400 | | "🧑🏼‍🎄" 1401 | | "🧑🏽‍🎄" 1402 | | "🧑🏾‍🎄" 1403 | | "🧑🏿‍🎄" 1404 | | "🧑‍🎄" 1405 | | "🦸🏻" 1406 | | "🦸🏼" 1407 | | "🦸🏽" 1408 | | "🦸🏾" 1409 | | "🦸🏿" 1410 | | "🦸" 1411 | | "🦸🏻‍♂️" 1412 | | "🦸🏼‍♂️" 1413 | | "🦸🏽‍♂️" 1414 | | "🦸🏾‍♂️" 1415 | | "🦸🏿‍♂️" 1416 | | "🦸‍♂️" 1417 | | "🦸‍♂" 1418 | | "🦸🏻‍♀️" 1419 | | "🦸🏼‍♀️" 1420 | | "🦸🏽‍♀️" 1421 | | "🦸🏾‍♀️" 1422 | | "🦸🏿‍♀️" 1423 | | "🦸‍♀️" 1424 | | "🦸‍♀" 1425 | | "🦹🏻" 1426 | | "🦹🏼" 1427 | | "🦹🏽" 1428 | | "🦹🏾" 1429 | | "🦹🏿" 1430 | | "🦹" 1431 | | "🦹🏻‍♂️" 1432 | | "🦹🏼‍♂️" 1433 | | "🦹🏽‍♂️" 1434 | | "🦹🏾‍♂️" 1435 | | "🦹🏿‍♂️" 1436 | | "🦹‍♂️" 1437 | | "🦹‍♂" 1438 | | "🦹🏻‍♀️" 1439 | | "🦹🏼‍♀️" 1440 | | "🦹🏽‍♀️" 1441 | | "🦹🏾‍♀️" 1442 | | "🦹🏿‍♀️" 1443 | | "🦹‍♀️" 1444 | | "🦹‍♀" 1445 | | "🧙🏻" 1446 | | "🧙🏼" 1447 | | "🧙🏽" 1448 | | "🧙🏾" 1449 | | "🧙🏿" 1450 | | "🧙" 1451 | | "🧙🏻‍♂️" 1452 | | "🧙🏼‍♂️" 1453 | | "🧙🏽‍♂️" 1454 | | "🧙🏾‍♂️" 1455 | | "🧙🏿‍♂️" 1456 | | "🧙‍♂️" 1457 | | "🧙‍♂" 1458 | | "🧙🏻‍♀️" 1459 | | "🧙🏼‍♀️" 1460 | | "🧙🏽‍♀️" 1461 | | "🧙🏾‍♀️" 1462 | | "🧙🏿‍♀️" 1463 | | "🧙‍♀️" 1464 | | "🧙‍♀" 1465 | | "🧚🏻" 1466 | | "🧚🏼" 1467 | | "🧚🏽" 1468 | | "🧚🏾" 1469 | | "🧚🏿" 1470 | | "🧚" 1471 | | "🧚🏻‍♂️" 1472 | | "🧚🏼‍♂️" 1473 | | "🧚🏽‍♂️" 1474 | | "🧚🏾‍♂️" 1475 | | "🧚🏿‍♂️" 1476 | | "🧚‍♂️" 1477 | | "🧚‍♂" 1478 | | "🧚🏻‍♀️" 1479 | | "🧚🏼‍♀️" 1480 | | "🧚🏽‍♀️" 1481 | | "🧚🏾‍♀️" 1482 | | "🧚🏿‍♀️" 1483 | | "🧚‍♀️" 1484 | | "🧚‍♀" 1485 | | "🧛🏻" 1486 | | "🧛🏼" 1487 | | "🧛🏽" 1488 | | "🧛🏾" 1489 | | "🧛🏿" 1490 | | "🧛" 1491 | | "🧛🏻‍♂️" 1492 | | "🧛🏼‍♂️" 1493 | | "🧛🏽‍♂️" 1494 | | "🧛🏾‍♂️" 1495 | | "🧛🏿‍♂️" 1496 | | "🧛‍♂️" 1497 | | "🧛‍♂" 1498 | | "🧛🏻‍♀️" 1499 | | "🧛🏼‍♀️" 1500 | | "🧛🏽‍♀️" 1501 | | "🧛🏾‍♀️" 1502 | | "🧛🏿‍♀️" 1503 | | "🧛‍♀️" 1504 | | "🧛‍♀" 1505 | | "🧜🏻" 1506 | | "🧜🏼" 1507 | | "🧜🏽" 1508 | | "🧜🏾" 1509 | | "🧜🏿" 1510 | | "🧜" 1511 | | "🧜🏻‍♂️" 1512 | | "🧜🏼‍♂️" 1513 | | "🧜🏽‍♂️" 1514 | | "🧜🏾‍♂️" 1515 | | "🧜🏿‍♂️" 1516 | | "🧜‍♂️" 1517 | | "🧜‍♂" 1518 | | "🧜🏻‍♀️" 1519 | | "🧜🏼‍♀️" 1520 | | "🧜🏽‍♀️" 1521 | | "🧜🏾‍♀️" 1522 | | "🧜🏿‍♀️" 1523 | | "🧜‍♀️" 1524 | | "🧜‍♀" 1525 | | "🧝🏻" 1526 | | "🧝🏼" 1527 | | "🧝🏽" 1528 | | "🧝🏾" 1529 | | "🧝🏿" 1530 | | "🧝" 1531 | | "🧝🏻‍♂️" 1532 | | "🧝🏼‍♂️" 1533 | | "🧝🏽‍♂️" 1534 | | "🧝🏾‍♂️" 1535 | | "🧝🏿‍♂️" 1536 | | "🧝‍♂️" 1537 | | "🧝‍♂" 1538 | | "🧝🏻‍♀️" 1539 | | "🧝🏼‍♀️" 1540 | | "🧝🏽‍♀️" 1541 | | "🧝🏾‍♀️" 1542 | | "🧝🏿‍♀️" 1543 | | "🧝‍♀️" 1544 | | "🧝‍♀" 1545 | | "🧞" 1546 | | "🧞‍♂️" 1547 | | "🧞‍♂" 1548 | | "🧞‍♀️" 1549 | | "🧞‍♀" 1550 | | "🧟" 1551 | | "🧟‍♂️" 1552 | | "🧟‍♂" 1553 | | "🧟‍♀️" 1554 | | "🧟‍♀" 1555 | | "🧌" 1556 | | "💆🏻" 1557 | | "💆🏼" 1558 | | "💆🏽" 1559 | | "💆🏾" 1560 | | "💆🏿" 1561 | | "💆" 1562 | | "💆🏻‍♂️" 1563 | | "💆🏼‍♂️" 1564 | | "💆🏽‍♂️" 1565 | | "💆🏾‍♂️" 1566 | | "💆🏿‍♂️" 1567 | | "💆‍♂️" 1568 | | "💆‍♂" 1569 | | "💆🏻‍♀️" 1570 | | "💆🏼‍♀️" 1571 | | "💆🏽‍♀️" 1572 | | "💆🏾‍♀️" 1573 | | "💆🏿‍♀️" 1574 | | "💆‍♀️" 1575 | | "💆‍♀" 1576 | | "💇🏻" 1577 | | "💇🏼" 1578 | | "💇🏽" 1579 | | "💇🏾" 1580 | | "💇🏿" 1581 | | "💇" 1582 | | "💇🏻‍♂️" 1583 | | "💇🏼‍♂️" 1584 | | "💇🏽‍♂️" 1585 | | "💇🏾‍♂️" 1586 | | "💇🏿‍♂️" 1587 | | "💇‍♂️" 1588 | | "💇‍♂" 1589 | | "💇🏻‍♀️" 1590 | | "💇🏼‍♀️" 1591 | | "💇🏽‍♀️" 1592 | | "💇🏾‍♀️" 1593 | | "💇🏿‍♀️" 1594 | | "💇‍♀️" 1595 | | "💇‍♀" 1596 | | "🚶🏻" 1597 | | "🚶🏼" 1598 | | "🚶🏽" 1599 | | "🚶🏾" 1600 | | "🚶🏿" 1601 | | "🚶" 1602 | | "🚶🏻‍♂️" 1603 | | "🚶🏼‍♂️" 1604 | | "🚶🏽‍♂️" 1605 | | "🚶🏾‍♂️" 1606 | | "🚶🏿‍♂️" 1607 | | "🚶‍♂️" 1608 | | "🚶‍♂" 1609 | | "🚶🏻‍♀️" 1610 | | "🚶🏼‍♀️" 1611 | | "🚶🏽‍♀️" 1612 | | "🚶🏾‍♀️" 1613 | | "🚶🏿‍♀️" 1614 | | "🚶‍♀️" 1615 | | "🚶‍♀" 1616 | | "🧍🏻" 1617 | | "🧍🏼" 1618 | | "🧍🏽" 1619 | | "🧍🏾" 1620 | | "🧍🏿" 1621 | | "🧍" 1622 | | "🧍🏻‍♂️" 1623 | | "🧍🏼‍♂️" 1624 | | "🧍🏽‍♂️" 1625 | | "🧍🏾‍♂️" 1626 | | "🧍🏿‍♂️" 1627 | | "🧍‍♂️" 1628 | | "🧍‍♂" 1629 | | "🧍🏻‍♀️" 1630 | | "🧍🏼‍♀️" 1631 | | "🧍🏽‍♀️" 1632 | | "🧍🏾‍♀️" 1633 | | "🧍🏿‍♀️" 1634 | | "🧍‍♀️" 1635 | | "🧍‍♀" 1636 | | "🧎🏻" 1637 | | "🧎🏼" 1638 | | "🧎🏽" 1639 | | "🧎🏾" 1640 | | "🧎🏿" 1641 | | "🧎" 1642 | | "🧎🏻‍♂️" 1643 | | "🧎🏼‍♂️" 1644 | | "🧎🏽‍♂️" 1645 | | "🧎🏾‍♂️" 1646 | | "🧎🏿‍♂️" 1647 | | "🧎‍♂️" 1648 | | "🧎‍♂" 1649 | | "🧎🏻‍♀️" 1650 | | "🧎🏼‍♀️" 1651 | | "🧎🏽‍♀️" 1652 | | "🧎🏾‍♀️" 1653 | | "🧎🏿‍♀️" 1654 | | "🧎‍♀️" 1655 | | "🧎‍♀" 1656 | | "🧑🏻‍🦯" 1657 | | "🧑🏼‍🦯" 1658 | | "🧑🏽‍🦯" 1659 | | "🧑🏾‍🦯" 1660 | | "🧑🏿‍🦯" 1661 | | "🧑‍🦯" 1662 | | "👨🏻‍🦯" 1663 | | "👨🏼‍🦯" 1664 | | "👨🏽‍🦯" 1665 | | "👨🏾‍🦯" 1666 | | "👨🏿‍🦯" 1667 | | "👨‍🦯" 1668 | | "👩🏻‍🦯" 1669 | | "👩🏼‍🦯" 1670 | | "👩🏽‍🦯" 1671 | | "👩🏾‍🦯" 1672 | | "👩🏿‍🦯" 1673 | | "👩‍🦯" 1674 | | "🧑🏻‍🦼" 1675 | | "🧑🏼‍🦼" 1676 | | "🧑🏽‍🦼" 1677 | | "🧑🏾‍🦼" 1678 | | "🧑🏿‍🦼" 1679 | | "🧑‍🦼" 1680 | | "👨🏻‍🦼" 1681 | | "👨🏼‍🦼" 1682 | | "👨🏽‍🦼" 1683 | | "👨🏾‍🦼" 1684 | | "👨🏿‍🦼" 1685 | | "👨‍🦼" 1686 | | "👩🏻‍🦼" 1687 | | "👩🏼‍🦼" 1688 | | "👩🏽‍🦼" 1689 | | "👩🏾‍🦼" 1690 | | "👩🏿‍🦼" 1691 | | "👩‍🦼" 1692 | | "🧑🏻‍🦽" 1693 | | "🧑🏼‍🦽" 1694 | | "🧑🏽‍🦽" 1695 | | "🧑🏾‍🦽" 1696 | | "🧑🏿‍🦽" 1697 | | "🧑‍🦽" 1698 | | "👨🏻‍🦽" 1699 | | "👨🏼‍🦽" 1700 | | "👨🏽‍🦽" 1701 | | "👨🏾‍🦽" 1702 | | "👨🏿‍🦽" 1703 | | "👨‍🦽" 1704 | | "👩🏻‍🦽" 1705 | | "👩🏼‍🦽" 1706 | | "👩🏽‍🦽" 1707 | | "👩🏾‍🦽" 1708 | | "👩🏿‍🦽" 1709 | | "👩‍🦽" 1710 | | "🏃🏻" 1711 | | "🏃🏼" 1712 | | "🏃🏽" 1713 | | "🏃🏾" 1714 | | "🏃🏿" 1715 | | "🏃" 1716 | | "🏃🏻‍♂️" 1717 | | "🏃🏼‍♂️" 1718 | | "🏃🏽‍♂️" 1719 | | "🏃🏾‍♂️" 1720 | | "🏃🏿‍♂️" 1721 | | "🏃‍♂️" 1722 | | "🏃‍♂" 1723 | | "🏃🏻‍♀️" 1724 | | "🏃🏼‍♀️" 1725 | | "🏃🏽‍♀️" 1726 | | "🏃🏾‍♀️" 1727 | | "🏃🏿‍♀️" 1728 | | "🏃‍♀️" 1729 | | "🏃‍♀" 1730 | | "💃🏻" 1731 | | "💃🏼" 1732 | | "💃🏽" 1733 | | "💃🏾" 1734 | | "💃🏿" 1735 | | "💃" 1736 | | "🕺🏻" 1737 | | "🕺🏼" 1738 | | "🕺🏽" 1739 | | "🕺🏾" 1740 | | "🕺🏿" 1741 | | "🕺" 1742 | | "🕴🏻" 1743 | | "🕴🏼" 1744 | | "🕴🏽" 1745 | | "🕴🏾" 1746 | | "🕴🏿" 1747 | | "🕴️" 1748 | | "🕴" 1749 | | "👯" 1750 | | "👯‍♂️" 1751 | | "👯‍♂" 1752 | | "👯‍♀️" 1753 | | "👯‍♀" 1754 | | "🧖🏻" 1755 | | "🧖🏼" 1756 | | "🧖🏽" 1757 | | "🧖🏾" 1758 | | "🧖🏿" 1759 | | "🧖" 1760 | | "🧖🏻‍♂️" 1761 | | "🧖🏼‍♂️" 1762 | | "🧖🏽‍♂️" 1763 | | "🧖🏾‍♂️" 1764 | | "🧖🏿‍♂️" 1765 | | "🧖‍♂️" 1766 | | "🧖‍♂" 1767 | | "🧖🏻‍♀️" 1768 | | "🧖🏼‍♀️" 1769 | | "🧖🏽‍♀️" 1770 | | "🧖🏾‍♀️" 1771 | | "🧖🏿‍♀️" 1772 | | "🧖‍♀️" 1773 | | "🧖‍♀" 1774 | | "🧗🏻" 1775 | | "🧗🏼" 1776 | | "🧗🏽" 1777 | | "🧗🏾" 1778 | | "🧗🏿" 1779 | | "🧗" 1780 | | "🧗🏻‍♂️" 1781 | | "🧗🏼‍♂️" 1782 | | "🧗🏽‍♂️" 1783 | | "🧗🏾‍♂️" 1784 | | "🧗🏿‍♂️" 1785 | | "🧗‍♂️" 1786 | | "🧗‍♂" 1787 | | "🧗🏻‍♀️" 1788 | | "🧗🏼‍♀️" 1789 | | "🧗🏽‍♀️" 1790 | | "🧗🏾‍♀️" 1791 | | "🧗🏿‍♀️" 1792 | | "🧗‍♀️" 1793 | | "🧗‍♀" 1794 | | "🤺" 1795 | | "🏇🏻" 1796 | | "🏇🏼" 1797 | | "🏇🏽" 1798 | | "🏇🏾" 1799 | | "🏇🏿" 1800 | | "🏇" 1801 | | "⛷️" 1802 | | "⛷" 1803 | | "🏂🏻" 1804 | | "🏂🏼" 1805 | | "🏂🏽" 1806 | | "🏂🏾" 1807 | | "🏂🏿" 1808 | | "🏂" 1809 | | "🏌🏻" 1810 | | "🏌🏼" 1811 | | "🏌🏽" 1812 | | "🏌🏾" 1813 | | "🏌🏿" 1814 | | "🏌️" 1815 | | "🏌" 1816 | | "🏌🏻‍♂️" 1817 | | "🏌🏼‍♂️" 1818 | | "🏌🏽‍♂️" 1819 | | "🏌🏾‍♂️" 1820 | | "🏌🏿‍♂️" 1821 | | "🏌️‍♂️" 1822 | | "🏌🏻‍♀️" 1823 | | "🏌🏼‍♀️" 1824 | | "🏌🏽‍♀️" 1825 | | "🏌🏾‍♀️" 1826 | | "🏌🏿‍♀️" 1827 | | "🏌️‍♀️" 1828 | | "🏄🏻" 1829 | | "🏄🏼" 1830 | | "🏄🏽" 1831 | | "🏄🏾" 1832 | | "🏄🏿" 1833 | | "🏄" 1834 | | "🏄🏻‍♂️" 1835 | | "🏄🏼‍♂️" 1836 | | "🏄🏽‍♂️" 1837 | | "🏄🏾‍♂️" 1838 | | "🏄🏿‍♂️" 1839 | | "🏄‍♂️" 1840 | | "🏄‍♂" 1841 | | "🏄🏻‍♀️" 1842 | | "🏄🏼‍♀️" 1843 | | "🏄🏽‍♀️" 1844 | | "🏄🏾‍♀️" 1845 | | "🏄🏿‍♀️" 1846 | | "🏄‍♀️" 1847 | | "🏄‍♀" 1848 | | "🚣🏻" 1849 | | "🚣🏼" 1850 | | "🚣🏽" 1851 | | "🚣🏾" 1852 | | "🚣🏿" 1853 | | "🚣" 1854 | | "🚣🏻‍♂️" 1855 | | "🚣🏼‍♂️" 1856 | | "🚣🏽‍♂️" 1857 | | "🚣🏾‍♂️" 1858 | | "🚣🏿‍♂️" 1859 | | "🚣‍♂️" 1860 | | "🚣‍♂" 1861 | | "🚣🏻‍♀️" 1862 | | "🚣🏼‍♀️" 1863 | | "🚣🏽‍♀️" 1864 | | "🚣🏾‍♀️" 1865 | | "🚣🏿‍♀️" 1866 | | "🚣‍♀️" 1867 | | "🚣‍♀" 1868 | | "🏊🏻" 1869 | | "🏊🏼" 1870 | | "🏊🏽" 1871 | | "🏊🏾" 1872 | | "🏊🏿" 1873 | | "🏊" 1874 | | "🏊🏻‍♂️" 1875 | | "🏊🏼‍♂️" 1876 | | "🏊🏽‍♂️" 1877 | | "🏊🏾‍♂️" 1878 | | "🏊🏿‍♂️" 1879 | | "🏊‍♂️" 1880 | | "🏊‍♂" 1881 | | "🏊🏻‍♀️" 1882 | | "🏊🏼‍♀️" 1883 | | "🏊🏽‍♀️" 1884 | | "🏊🏾‍♀️" 1885 | | "🏊🏿‍♀️" 1886 | | "🏊‍♀️" 1887 | | "🏊‍♀" 1888 | | "⛹🏻" 1889 | | "⛹🏼" 1890 | | "⛹🏽" 1891 | | "⛹🏾" 1892 | | "⛹🏿" 1893 | | "⛹️" 1894 | | "⛹" 1895 | | "⛹🏻‍♂️" 1896 | | "⛹🏼‍♂️" 1897 | | "⛹🏽‍♂️" 1898 | | "⛹🏾‍♂️" 1899 | | "⛹🏿‍♂️" 1900 | | "⛹️‍♂️" 1901 | | "⛹🏻‍♀️" 1902 | | "⛹🏼‍♀️" 1903 | | "⛹🏽‍♀️" 1904 | | "⛹🏾‍♀️" 1905 | | "⛹🏿‍♀️" 1906 | | "⛹️‍♀️" 1907 | | "🏋🏻" 1908 | | "🏋🏼" 1909 | | "🏋🏽" 1910 | | "🏋🏾" 1911 | | "🏋🏿" 1912 | | "🏋️" 1913 | | "🏋" 1914 | | "🏋🏻‍♂️" 1915 | | "🏋🏼‍♂️" 1916 | | "🏋🏽‍♂️" 1917 | | "🏋🏾‍♂️" 1918 | | "🏋🏿‍♂️" 1919 | | "🏋️‍♂️" 1920 | | "🏋🏻‍♀️" 1921 | | "🏋🏼‍♀️" 1922 | | "🏋🏽‍♀️" 1923 | | "🏋🏾‍♀️" 1924 | | "🏋🏿‍♀️" 1925 | | "🏋️‍♀️" 1926 | | "🚴🏻" 1927 | | "🚴🏼" 1928 | | "🚴🏽" 1929 | | "🚴🏾" 1930 | | "🚴🏿" 1931 | | "🚴" 1932 | | "🚴🏻‍♂️" 1933 | | "🚴🏼‍♂️" 1934 | | "🚴🏽‍♂️" 1935 | | "🚴🏾‍♂️" 1936 | | "🚴🏿‍♂️" 1937 | | "🚴‍♂️" 1938 | | "🚴‍♂" 1939 | | "🚴🏻‍♀️" 1940 | | "🚴🏼‍♀️" 1941 | | "🚴🏽‍♀️" 1942 | | "🚴🏾‍♀️" 1943 | | "🚴🏿‍♀️" 1944 | | "🚴‍♀️" 1945 | | "🚴‍♀" 1946 | | "🚵🏻" 1947 | | "🚵🏼" 1948 | | "🚵🏽" 1949 | | "🚵🏾" 1950 | | "🚵🏿" 1951 | | "🚵" 1952 | | "🚵🏻‍♂️" 1953 | | "🚵🏼‍♂️" 1954 | | "🚵🏽‍♂️" 1955 | | "🚵🏾‍♂️" 1956 | | "🚵🏿‍♂️" 1957 | | "🚵‍♂️" 1958 | | "🚵‍♂" 1959 | | "🚵🏻‍♀️" 1960 | | "🚵🏼‍♀️" 1961 | | "🚵🏽‍♀️" 1962 | | "🚵🏾‍♀️" 1963 | | "🚵🏿‍♀️" 1964 | | "🚵‍♀️" 1965 | | "🚵‍♀" 1966 | | "🤸🏻" 1967 | | "🤸🏼" 1968 | | "🤸🏽" 1969 | | "🤸🏾" 1970 | | "🤸🏿" 1971 | | "🤸" 1972 | | "🤸🏻‍♂️" 1973 | | "🤸🏼‍♂️" 1974 | | "🤸🏽‍♂️" 1975 | | "🤸🏾‍♂️" 1976 | | "🤸🏿‍♂️" 1977 | | "🤸‍♂️" 1978 | | "🤸‍♂" 1979 | | "🤸🏻‍♀️" 1980 | | "🤸🏼‍♀️" 1981 | | "🤸🏽‍♀️" 1982 | | "🤸🏾‍♀️" 1983 | | "🤸🏿‍♀️" 1984 | | "🤸‍♀️" 1985 | | "🤸‍♀" 1986 | | "🤼" 1987 | | "🤼‍♂️" 1988 | | "🤼‍♂" 1989 | | "🤼‍♀️" 1990 | | "🤼‍♀" 1991 | | "🤽🏻" 1992 | | "🤽🏼" 1993 | | "🤽🏽" 1994 | | "🤽🏾" 1995 | | "🤽🏿" 1996 | | "🤽" 1997 | | "🤽🏻‍♂️" 1998 | | "🤽🏼‍♂️" 1999 | | "🤽🏽‍♂️" 2000 | | "🤽🏾‍♂️" 2001 | | "🤽🏿‍♂️" 2002 | | "🤽‍♂️" 2003 | | "🤽‍♂" 2004 | | "🤽🏻‍♀️" 2005 | | "🤽🏼‍♀️" 2006 | | "🤽🏽‍♀️" 2007 | | "🤽🏾‍♀️" 2008 | | "🤽🏿‍♀️" 2009 | | "🤽‍♀️" 2010 | | "🤽‍♀" 2011 | | "🤾🏻" 2012 | | "🤾🏼" 2013 | | "🤾🏽" 2014 | | "🤾🏾" 2015 | | "🤾🏿" 2016 | | "🤾" 2017 | | "🤾🏻‍♂️" 2018 | | "🤾🏼‍♂️" 2019 | | "🤾🏽‍♂️" 2020 | | "🤾🏾‍♂️" 2021 | | "🤾🏿‍♂️" 2022 | | "🤾‍♂️" 2023 | | "🤾‍♂" 2024 | | "🤾🏻‍♀️" 2025 | | "🤾🏼‍♀️" 2026 | | "🤾🏽‍♀️" 2027 | | "🤾🏾‍♀️" 2028 | | "🤾🏿‍♀️" 2029 | | "🤾‍♀️" 2030 | | "🤾‍♀" 2031 | | "🤹🏻" 2032 | | "🤹🏼" 2033 | | "🤹🏽" 2034 | | "🤹🏾" 2035 | | "🤹🏿" 2036 | | "🤹" 2037 | | "🤹🏻‍♂️" 2038 | | "🤹🏼‍♂️" 2039 | | "🤹🏽‍♂️" 2040 | | "🤹🏾‍♂️" 2041 | | "🤹🏿‍♂️" 2042 | | "🤹‍♂️" 2043 | | "🤹‍♂" 2044 | | "🤹🏻‍♀️" 2045 | | "🤹🏼‍♀️" 2046 | | "🤹🏽‍♀️" 2047 | | "🤹🏾‍♀️" 2048 | | "🤹🏿‍♀️" 2049 | | "🤹‍♀️" 2050 | | "🤹‍♀" 2051 | | "🧘🏻" 2052 | | "🧘🏼" 2053 | | "🧘🏽" 2054 | | "🧘🏾" 2055 | | "🧘🏿" 2056 | | "🧘" 2057 | | "🧘🏻‍♂️" 2058 | | "🧘🏼‍♂️" 2059 | | "🧘🏽‍♂️" 2060 | | "🧘🏾‍♂️" 2061 | | "🧘🏿‍♂️" 2062 | | "🧘‍♂️" 2063 | | "🧘‍♂" 2064 | | "🧘🏻‍♀️" 2065 | | "🧘🏼‍♀️" 2066 | | "🧘🏽‍♀️" 2067 | | "🧘🏾‍♀️" 2068 | | "🧘🏿‍♀️" 2069 | | "🧘‍♀️" 2070 | | "🧘‍♀" 2071 | | "🛀🏻" 2072 | | "🛀🏼" 2073 | | "🛀🏽" 2074 | | "🛀🏾" 2075 | | "🛀🏿" 2076 | | "🛀" 2077 | | "🛌🏻" 2078 | | "🛌🏼" 2079 | | "🛌🏽" 2080 | | "🛌🏾" 2081 | | "🛌🏿" 2082 | | "🛌" 2083 | | "🧑🏻‍🤝‍🧑🏻" 2084 | | "🧑🏻‍🤝‍🧑🏼" 2085 | | "🧑🏻‍🤝‍🧑🏽" 2086 | | "🧑🏻‍🤝‍🧑🏾" 2087 | | "🧑🏻‍🤝‍🧑🏿" 2088 | | "🧑🏼‍🤝‍🧑🏻" 2089 | | "🧑🏼‍🤝‍🧑🏼" 2090 | | "🧑🏼‍🤝‍🧑🏽" 2091 | | "🧑🏼‍🤝‍🧑🏾" 2092 | | "🧑🏼‍🤝‍🧑🏿" 2093 | | "🧑🏽‍🤝‍🧑🏻" 2094 | | "🧑🏽‍🤝‍🧑🏼" 2095 | | "🧑🏽‍🤝‍🧑🏽" 2096 | | "🧑🏽‍🤝‍🧑🏾" 2097 | | "🧑🏽‍🤝‍🧑🏿" 2098 | | "🧑🏾‍🤝‍🧑🏻" 2099 | | "🧑🏾‍🤝‍🧑🏼" 2100 | | "🧑🏾‍🤝‍🧑🏽" 2101 | | "🧑🏾‍🤝‍🧑🏾" 2102 | | "🧑🏾‍🤝‍🧑🏿" 2103 | | "🧑🏿‍🤝‍🧑🏻" 2104 | | "🧑🏿‍🤝‍🧑🏼" 2105 | | "🧑🏿‍🤝‍🧑🏽" 2106 | | "🧑🏿‍🤝‍🧑🏾" 2107 | | "🧑🏿‍🤝‍🧑🏿" 2108 | | "🧑‍🤝‍🧑" 2109 | | "👭" 2110 | | "👫" 2111 | | "👬" 2112 | | "💏" 2113 | | "💑" 2114 | | "👪" 2115 | | "👨‍👩‍👦" 2116 | | "👨‍👩‍👧" 2117 | | "👨‍👩‍👧‍👦" 2118 | | "👨‍👩‍👦‍👦" 2119 | | "👨‍👩‍👧‍👧" 2120 | | "👨‍👨‍👦" 2121 | | "👨‍👨‍👧" 2122 | | "👨‍👨‍👧‍👦" 2123 | | "👨‍👨‍👦‍👦" 2124 | | "👨‍👨‍👧‍👧" 2125 | | "👩‍👩‍👦" 2126 | | "👩‍👩‍👧" 2127 | | "👩‍👩‍👧‍👦" 2128 | | "👩‍👩‍👦‍👦" 2129 | | "👩‍👩‍👧‍👧" 2130 | | "👨‍👦" 2131 | | "👨‍👦‍👦" 2132 | | "👨‍👧" 2133 | | "👨‍👧‍👦" 2134 | | "👨‍👧‍👧" 2135 | | "👩‍👦" 2136 | | "👩‍👦‍👦" 2137 | | "👩‍👧" 2138 | | "👩‍👧‍👦" 2139 | | "👩‍👧‍👧" 2140 | | "🗣️" 2141 | | "🗣" 2142 | | "👤" 2143 | | "👥" 2144 | | "🫂" 2145 | | "👣" 2146 | | "🐵" 2147 | | "🐒" 2148 | | "🦍" 2149 | | "🦧" 2150 | | "🐶" 2151 | | "🐕" 2152 | | "🦮" 2153 | | "🐕‍🦺" 2154 | | "🐩" 2155 | | "🐺" 2156 | | "🦊" 2157 | | "🦝" 2158 | | "🐱" 2159 | | "🐈" 2160 | | "🐈‍⬛" 2161 | | "🦁" 2162 | | "🐯" 2163 | | "🐅" 2164 | | "🐆" 2165 | | "🐴" 2166 | | "🐎" 2167 | | "🦄" 2168 | | "🦓" 2169 | | "🦌" 2170 | | "🦬" 2171 | | "🐮" 2172 | | "🐂" 2173 | | "🐃" 2174 | | "🐄" 2175 | | "🐷" 2176 | | "🐖" 2177 | | "🐗" 2178 | | "🐽" 2179 | | "🐏" 2180 | | "🐑" 2181 | | "🐐" 2182 | | "🐪" 2183 | | "🐫" 2184 | | "🦙" 2185 | | "🦒" 2186 | | "🐘" 2187 | | "🦣" 2188 | | "🦏" 2189 | | "🦛" 2190 | | "🐭" 2191 | | "🐁" 2192 | | "🐀" 2193 | | "🐹" 2194 | | "🐰" 2195 | | "🐇" 2196 | | "🐿️" 2197 | | "🐿" 2198 | | "🦫" 2199 | | "🦔" 2200 | | "🦇" 2201 | | "🐻" 2202 | | "🐻‍❄️" 2203 | | "🐻‍❄" 2204 | | "🐨" 2205 | | "🐼" 2206 | | "🦥" 2207 | | "🦦" 2208 | | "🦨" 2209 | | "🦘" 2210 | | "🦡" 2211 | | "🐾" 2212 | | "🦃" 2213 | | "🐔" 2214 | | "🐓" 2215 | | "🐣" 2216 | | "🐤" 2217 | | "🐥" 2218 | | "🐦" 2219 | | "🐧" 2220 | | "🕊️" 2221 | | "🕊" 2222 | | "🦅" 2223 | | "🦆" 2224 | | "🦢" 2225 | | "🦉" 2226 | | "🦤" 2227 | | "🪶" 2228 | | "🦩" 2229 | | "🦚" 2230 | | "🦜" 2231 | | "🐸" 2232 | | "🐊" 2233 | | "🐢" 2234 | | "🦎" 2235 | | "🐍" 2236 | | "🐲" 2237 | | "🐉" 2238 | | "🦕" 2239 | | "🦖" 2240 | | "🐳" 2241 | | "🐋" 2242 | | "🐬" 2243 | | "🦭" 2244 | | "🐟" 2245 | | "🐠" 2246 | | "🐡" 2247 | | "🦈" 2248 | | "🐙" 2249 | | "🐚" 2250 | | "🪸" 2251 | | "🐌" 2252 | | "🦋" 2253 | | "🐛" 2254 | | "🐜" 2255 | | "🐝" 2256 | | "🪲" 2257 | | "🐞" 2258 | | "🦗" 2259 | | "🪳" 2260 | | "🕷️" 2261 | | "🕷" 2262 | | "🕸️" 2263 | | "🕸" 2264 | | "🦂" 2265 | | "🦟" 2266 | | "🪰" 2267 | | "🪱" 2268 | | "🦠" 2269 | | "💐" 2270 | | "🌸" 2271 | | "💮" 2272 | | "🪷" 2273 | | "🏵️" 2274 | | "🏵" 2275 | | "🌹" 2276 | | "🥀" 2277 | | "🌺" 2278 | | "🌻" 2279 | | "🌼" 2280 | | "🌷" 2281 | | "🌱" 2282 | | "🪴" 2283 | | "🌲" 2284 | | "🌳" 2285 | | "🌴" 2286 | | "🌵" 2287 | | "🌾" 2288 | | "🌿" 2289 | | "☘️" 2290 | | "☘" 2291 | | "🍀" 2292 | | "🍁" 2293 | | "🍂" 2294 | | "🍃" 2295 | | "🪹" 2296 | | "🪺" 2297 | | "🍇" 2298 | | "🍈" 2299 | | "🍉" 2300 | | "🍊" 2301 | | "🍋" 2302 | | "🍌" 2303 | | "🍍" 2304 | | "🥭" 2305 | | "🍎" 2306 | | "🍏" 2307 | | "🍐" 2308 | | "🍑" 2309 | | "🍒" 2310 | | "🍓" 2311 | | "🫐" 2312 | | "🥝" 2313 | | "🍅" 2314 | | "🫒" 2315 | | "🥥" 2316 | | "🥑" 2317 | | "🍆" 2318 | | "🥔" 2319 | | "🥕" 2320 | | "🌽" 2321 | | "🌶️" 2322 | | "🌶" 2323 | | "🫑" 2324 | | "🥒" 2325 | | "🥬" 2326 | | "🥦" 2327 | | "🧄" 2328 | | "🧅" 2329 | | "🍄" 2330 | | "🥜" 2331 | | "🫘" 2332 | | "🌰" 2333 | | "🍞" 2334 | | "🥐" 2335 | | "🥖" 2336 | | "🫓" 2337 | | "🥨" 2338 | | "🥯" 2339 | | "🥞" 2340 | | "🧇" 2341 | | "🧀" 2342 | | "🍖" 2343 | | "🍗" 2344 | | "🥩" 2345 | | "🥓" 2346 | | "🍔" 2347 | | "🍟" 2348 | | "🍕" 2349 | | "🌭" 2350 | | "🥪" 2351 | | "🌮" 2352 | | "🌯" 2353 | | "🫔" 2354 | | "🥙" 2355 | | "🧆" 2356 | | "🥚" 2357 | | "🍳" 2358 | | "🥘" 2359 | | "🍲" 2360 | | "🫕" 2361 | | "🥣" 2362 | | "🥗" 2363 | | "🍿" 2364 | | "🧈" 2365 | | "🧂" 2366 | | "🥫" 2367 | | "🍱" 2368 | | "🍘" 2369 | | "🍙" 2370 | | "🍚" 2371 | | "🍛" 2372 | | "🍜" 2373 | | "🍝" 2374 | | "🍠" 2375 | | "🍢" 2376 | | "🍣" 2377 | | "🍤" 2378 | | "🍥" 2379 | | "🥮" 2380 | | "🍡" 2381 | | "🥟" 2382 | | "🥠" 2383 | | "🥡" 2384 | | "🦀" 2385 | | "🦞" 2386 | | "🦐" 2387 | | "🦑" 2388 | | "🦪" 2389 | | "🍦" 2390 | | "🍧" 2391 | | "🍨" 2392 | | "🍩" 2393 | | "🍪" 2394 | | "🎂" 2395 | | "🍰" 2396 | | "🧁" 2397 | | "🥧" 2398 | | "🍫" 2399 | | "🍬" 2400 | | "🍭" 2401 | | "🍮" 2402 | | "🍯" 2403 | | "🍼" 2404 | | "🥛" 2405 | | "☕" 2406 | | "🫖" 2407 | | "🍵" 2408 | | "🍶" 2409 | | "🍾" 2410 | | "🍷" 2411 | | "🍸" 2412 | | "🍹" 2413 | | "🍺" 2414 | | "🍻" 2415 | | "🥂" 2416 | | "🥃" 2417 | | "🫗" 2418 | | "🥤" 2419 | | "🧋" 2420 | | "🧃" 2421 | | "🧉" 2422 | | "🧊" 2423 | | "🥢" 2424 | | "🍽️" 2425 | | "🍽" 2426 | | "🍴" 2427 | | "🥄" 2428 | | "🔪" 2429 | | "🫙" 2430 | | "🏺" 2431 | | "🌍" 2432 | | "🌎" 2433 | | "🌏" 2434 | | "🌐" 2435 | | "🗺️" 2436 | | "🗺" 2437 | | "🗾" 2438 | | "🧭" 2439 | | "🏔️" 2440 | | "🏔" 2441 | | "⛰️" 2442 | | "⛰" 2443 | | "🌋" 2444 | | "🗻" 2445 | | "🏕️" 2446 | | "🏕" 2447 | | "🏖️" 2448 | | "🏖" 2449 | | "🏜️" 2450 | | "🏜" 2451 | | "🏝️" 2452 | | "🏝" 2453 | | "🏞️" 2454 | | "🏞" 2455 | | "🏟️" 2456 | | "🏟" 2457 | | "🏛️" 2458 | | "🏛" 2459 | | "🏗️" 2460 | | "🏗" 2461 | | "🧱" 2462 | | "🪨" 2463 | | "🪵" 2464 | | "🛖" 2465 | | "🏘️" 2466 | | "🏘" 2467 | | "🏚️" 2468 | | "🏚" 2469 | | "🏠" 2470 | | "🏡" 2471 | | "🏢" 2472 | | "🏣" 2473 | | "🏤" 2474 | | "🏥" 2475 | | "🏦" 2476 | | "🏨" 2477 | | "🏩" 2478 | | "🏪" 2479 | | "🏫" 2480 | | "🏬" 2481 | | "🏭" 2482 | | "🏯" 2483 | | "🏰" 2484 | | "💒" 2485 | | "🗼" 2486 | | "🗽" 2487 | | "⛪" 2488 | | "🕌" 2489 | | "🛕" 2490 | | "🕍" 2491 | | "⛩️" 2492 | | "⛩" 2493 | | "🕋" 2494 | | "⛲" 2495 | | "⛺" 2496 | | "🌁" 2497 | | "🌃" 2498 | | "🏙️" 2499 | | "🏙" 2500 | | "🌄" 2501 | | "🌅" 2502 | | "🌆" 2503 | | "🌇" 2504 | | "🌉" 2505 | | "♨️" 2506 | | "♨" 2507 | | "🎠" 2508 | | "🛝" 2509 | | "🎡" 2510 | | "🎢" 2511 | | "💈" 2512 | | "🎪" 2513 | | "🚂" 2514 | | "🚃" 2515 | | "🚄" 2516 | | "🚅" 2517 | | "🚆" 2518 | | "🚇" 2519 | | "🚈" 2520 | | "🚉" 2521 | | "🚊" 2522 | | "🚝" 2523 | | "🚞" 2524 | | "🚋" 2525 | | "🚌" 2526 | | "🚍" 2527 | | "🚎" 2528 | | "🚐" 2529 | | "🚑" 2530 | | "🚒" 2531 | | "🚓" 2532 | | "🚔" 2533 | | "🚕" 2534 | | "🚖" 2535 | | "🚗" 2536 | | "🚘" 2537 | | "🚙" 2538 | | "🛻" 2539 | | "🚚" 2540 | | "🚛" 2541 | | "🚜" 2542 | | "🏎️" 2543 | | "🏎" 2544 | | "🏍️" 2545 | | "🏍" 2546 | | "🛵" 2547 | | "🦽" 2548 | | "🦼" 2549 | | "🛺" 2550 | | "🚲" 2551 | | "🛴" 2552 | | "🛹" 2553 | | "🛼" 2554 | | "🚏" 2555 | | "🛣️" 2556 | | "🛣" 2557 | | "🛤️" 2558 | | "🛤" 2559 | | "🛢️" 2560 | | "🛢" 2561 | | "⛽" 2562 | | "🛞" 2563 | | "🚨" 2564 | | "🚥" 2565 | | "🚦" 2566 | | "🛑" 2567 | | "🚧" 2568 | | "⚓" 2569 | | "🛟" 2570 | | "⛵" 2571 | | "🛶" 2572 | | "🚤" 2573 | | "🛳️" 2574 | | "🛳" 2575 | | "⛴️" 2576 | | "⛴" 2577 | | "🛥️" 2578 | | "🛥" 2579 | | "🚢" 2580 | | "✈️" 2581 | | "✈" 2582 | | "🛩️" 2583 | | "🛩" 2584 | | "🛫" 2585 | | "🛬" 2586 | | "🪂" 2587 | | "💺" 2588 | | "🚁" 2589 | | "🚟" 2590 | | "🚠" 2591 | | "🚡" 2592 | | "🛰️" 2593 | | "🛰" 2594 | | "🚀" 2595 | | "🛸" 2596 | | "🛎️" 2597 | | "🛎" 2598 | | "🧳" 2599 | | "⌛" 2600 | | "⏳" 2601 | | "⌚" 2602 | | "⏰" 2603 | | "⏱️" 2604 | | "⏱" 2605 | | "⏲️" 2606 | | "⏲" 2607 | | "🕰️" 2608 | | "🕰" 2609 | | "🕛" 2610 | | "🕧" 2611 | | "🕐" 2612 | | "🕜" 2613 | | "🕑" 2614 | | "🕝" 2615 | | "🕒" 2616 | | "🕞" 2617 | | "🕓" 2618 | | "🕟" 2619 | | "🕔" 2620 | | "🕠" 2621 | | "🕕" 2622 | | "🕡" 2623 | | "🕖" 2624 | | "🕢" 2625 | | "🕗" 2626 | | "🕣" 2627 | | "🕘" 2628 | | "🕤" 2629 | | "🕙" 2630 | | "🕥" 2631 | | "🕚" 2632 | | "🕦" 2633 | | "🌑" 2634 | | "🌒" 2635 | | "🌓" 2636 | | "🌔" 2637 | | "🌕" 2638 | | "🌖" 2639 | | "🌗" 2640 | | "🌘" 2641 | | "🌙" 2642 | | "🌚" 2643 | | "🌛" 2644 | | "🌜" 2645 | | "🌡️" 2646 | | "🌡" 2647 | | "☀️" 2648 | | "☀" 2649 | | "🌝" 2650 | | "🌞" 2651 | | "🪐" 2652 | | "⭐" 2653 | | "🌟" 2654 | | "🌠" 2655 | | "🌌" 2656 | | "☁️" 2657 | | "☁" 2658 | | "⛅" 2659 | | "⛈️" 2660 | | "⛈" 2661 | | "🌤️" 2662 | | "🌤" 2663 | | "🌥️" 2664 | | "🌥" 2665 | | "🌦️" 2666 | | "🌦" 2667 | | "🌧️" 2668 | | "🌧" 2669 | | "🌨️" 2670 | | "🌨" 2671 | | "🌩️" 2672 | | "🌩" 2673 | | "🌪️" 2674 | | "🌪" 2675 | | "🌫️" 2676 | | "🌫" 2677 | | "🌬️" 2678 | | "🌬" 2679 | | "🌀" 2680 | | "🌈" 2681 | | "🌂" 2682 | | "☂️" 2683 | | "☂" 2684 | | "☔" 2685 | | "⛱️" 2686 | | "⛱" 2687 | | "⚡" 2688 | | "❄️" 2689 | | "❄" 2690 | | "☃️" 2691 | | "☃" 2692 | | "⛄" 2693 | | "☄️" 2694 | | "☄" 2695 | | "🔥" 2696 | | "💧" 2697 | | "🌊" 2698 | | "🎃" 2699 | | "🎄" 2700 | | "🎆" 2701 | | "🎇" 2702 | | "🧨" 2703 | | "✨" 2704 | | "🎈" 2705 | | "🎉" 2706 | | "🎊" 2707 | | "🎋" 2708 | | "🎍" 2709 | | "🎎" 2710 | | "🎏" 2711 | | "🎐" 2712 | | "🎑" 2713 | | "🧧" 2714 | | "🎀" 2715 | | "🎁" 2716 | | "🎗️" 2717 | | "🎗" 2718 | | "🎟️" 2719 | | "🎟" 2720 | | "🎫" 2721 | | "🎖️" 2722 | | "🎖" 2723 | | "🏆" 2724 | | "🏅" 2725 | | "🥇" 2726 | | "🥈" 2727 | | "🥉" 2728 | | "⚽" 2729 | | "⚾" 2730 | | "🥎" 2731 | | "🏀" 2732 | | "🏐" 2733 | | "🏈" 2734 | | "🏉" 2735 | | "🎾" 2736 | | "🥏" 2737 | | "🎳" 2738 | | "🏏" 2739 | | "🏑" 2740 | | "🏒" 2741 | | "🥍" 2742 | | "🏓" 2743 | | "🏸" 2744 | | "🥊" 2745 | | "🥋" 2746 | | "🥅" 2747 | | "⛳" 2748 | | "⛸️" 2749 | | "⛸" 2750 | | "🎣" 2751 | | "🤿" 2752 | | "🎽" 2753 | | "🎿" 2754 | | "🛷" 2755 | | "🥌" 2756 | | "🎯" 2757 | | "🪀" 2758 | | "🪁" 2759 | | "🎱" 2760 | | "🔮" 2761 | | "🪄" 2762 | | "🧿" 2763 | | "🪬" 2764 | | "🎮" 2765 | | "🕹️" 2766 | | "🕹" 2767 | | "🎰" 2768 | | "🎲" 2769 | | "🧩" 2770 | | "🧸" 2771 | | "🪅" 2772 | | "🪩" 2773 | | "🪆" 2774 | | "♠️" 2775 | | "♠" 2776 | | "♥️" 2777 | | "♥" 2778 | | "♦️" 2779 | | "♦" 2780 | | "♣️" 2781 | | "♣" 2782 | | "♟️" 2783 | | "♟" 2784 | | "🃏" 2785 | | "🀄" 2786 | | "🎴" 2787 | | "🎭" 2788 | | "🖼️" 2789 | | "🖼" 2790 | | "🎨" 2791 | | "🧵" 2792 | | "🪡" 2793 | | "🧶" 2794 | | "🪢" 2795 | | "👓" 2796 | | "🕶️" 2797 | | "🕶" 2798 | | "🥽" 2799 | | "🥼" 2800 | | "🦺" 2801 | | "👔" 2802 | | "👕" 2803 | | "👖" 2804 | | "🧣" 2805 | | "🧤" 2806 | | "🧥" 2807 | | "🧦" 2808 | | "👗" 2809 | | "👘" 2810 | | "🥻" 2811 | | "🩱" 2812 | | "🩲" 2813 | | "🩳" 2814 | | "👙" 2815 | | "👚" 2816 | | "👛" 2817 | | "👜" 2818 | | "👝" 2819 | | "🛍️" 2820 | | "🛍" 2821 | | "🎒" 2822 | | "🩴" 2823 | | "👞" 2824 | | "👟" 2825 | | "🥾" 2826 | | "🥿" 2827 | | "👠" 2828 | | "👡" 2829 | | "🩰" 2830 | | "👢" 2831 | | "👑" 2832 | | "👒" 2833 | | "🎩" 2834 | | "🎓" 2835 | | "🧢" 2836 | | "🪖" 2837 | | "⛑️" 2838 | | "⛑" 2839 | | "📿" 2840 | | "💄" 2841 | | "💍" 2842 | | "💎" 2843 | | "🔇" 2844 | | "🔈" 2845 | | "🔉" 2846 | | "🔊" 2847 | | "📢" 2848 | | "📣" 2849 | | "📯" 2850 | | "🔔" 2851 | | "🔕" 2852 | | "🎼" 2853 | | "🎵" 2854 | | "🎶" 2855 | | "🎙️" 2856 | | "🎙" 2857 | | "🎚️" 2858 | | "🎚" 2859 | | "🎛️" 2860 | | "🎛" 2861 | | "🎤" 2862 | | "🎧" 2863 | | "📻" 2864 | | "🎷" 2865 | | "🪗" 2866 | | "🎸" 2867 | | "🎹" 2868 | | "🎺" 2869 | | "🎻" 2870 | | "🪕" 2871 | | "🥁" 2872 | | "🪘" 2873 | | "📱" 2874 | | "📲" 2875 | | "☎️" 2876 | | "☎" 2877 | | "📞" 2878 | | "📟" 2879 | | "📠" 2880 | | "🔋" 2881 | | "🪫" 2882 | | "🔌" 2883 | | "💻" 2884 | | "🖥️" 2885 | | "🖥" 2886 | | "🖨️" 2887 | | "🖨" 2888 | | "⌨️" 2889 | | "⌨" 2890 | | "🖱️" 2891 | | "🖱" 2892 | | "🖲️" 2893 | | "🖲" 2894 | | "💽" 2895 | | "💾" 2896 | | "💿" 2897 | | "📀" 2898 | | "🧮" 2899 | | "🎥" 2900 | | "🎞️" 2901 | | "🎞" 2902 | | "📽️" 2903 | | "📽" 2904 | | "🎬" 2905 | | "📺" 2906 | | "📷" 2907 | | "📸" 2908 | | "📹" 2909 | | "📼" 2910 | | "🔍" 2911 | | "🔎" 2912 | | "🕯️" 2913 | | "🕯" 2914 | | "💡" 2915 | | "🔦" 2916 | | "🏮" 2917 | | "🪔" 2918 | | "📔" 2919 | | "📕" 2920 | | "📖" 2921 | | "📗" 2922 | | "📘" 2923 | | "📙" 2924 | | "📚" 2925 | | "📓" 2926 | | "📒" 2927 | | "📃" 2928 | | "📜" 2929 | | "📄" 2930 | | "📰" 2931 | | "🗞️" 2932 | | "🗞" 2933 | | "📑" 2934 | | "🔖" 2935 | | "🏷️" 2936 | | "🏷" 2937 | | "💰" 2938 | | "🪙" 2939 | | "💴" 2940 | | "💵" 2941 | | "💶" 2942 | | "💷" 2943 | | "💸" 2944 | | "💳" 2945 | | "🧾" 2946 | | "💹" 2947 | | "✉️" 2948 | | "✉" 2949 | | "📧" 2950 | | "📨" 2951 | | "📩" 2952 | | "📤" 2953 | | "📥" 2954 | | "📦" 2955 | | "📫" 2956 | | "📪" 2957 | | "📬" 2958 | | "📭" 2959 | | "📮" 2960 | | "🗳️" 2961 | | "🗳" 2962 | | "✏️" 2963 | | "✏" 2964 | | "✒️" 2965 | | "✒" 2966 | | "🖋️" 2967 | | "🖋" 2968 | | "🖊️" 2969 | | "🖊" 2970 | | "🖌️" 2971 | | "🖌" 2972 | | "🖍️" 2973 | | "🖍" 2974 | | "📝" 2975 | | "💼" 2976 | | "📁" 2977 | | "📂" 2978 | | "🗂️" 2979 | | "🗂" 2980 | | "📅" 2981 | | "📆" 2982 | | "🗒️" 2983 | | "🗒" 2984 | | "🗓️" 2985 | | "🗓" 2986 | | "📇" 2987 | | "📈" 2988 | | "📉" 2989 | | "📊" 2990 | | "📋" 2991 | | "📌" 2992 | | "📍" 2993 | | "📎" 2994 | | "🖇️" 2995 | | "🖇" 2996 | | "📏" 2997 | | "📐" 2998 | | "✂️" 2999 | | "✂" 3000 | | "🗃️" 3001 | | "🗃" 3002 | | "🗄️" 3003 | | "🗄" 3004 | | "🗑️" 3005 | | "🗑" 3006 | | "🔒" 3007 | | "🔓" 3008 | | "🔏" 3009 | | "🔐" 3010 | | "🔑" 3011 | | "🗝️" 3012 | | "🗝" 3013 | | "🔨" 3014 | | "🪓" 3015 | | "⛏️" 3016 | | "⛏" 3017 | | "⚒️" 3018 | | "⚒" 3019 | | "🛠️" 3020 | | "🛠" 3021 | | "🗡️" 3022 | | "🗡" 3023 | | "⚔️" 3024 | | "⚔" 3025 | | "🔫" 3026 | | "🪃" 3027 | | "🏹" 3028 | | "🛡️" 3029 | | "🛡" 3030 | | "🪚" 3031 | | "🔧" 3032 | | "🪛" 3033 | | "🔩" 3034 | | "⚙️" 3035 | | "⚙" 3036 | | "🗜️" 3037 | | "🗜" 3038 | | "⚖️" 3039 | | "⚖" 3040 | | "🦯" 3041 | | "🔗" 3042 | | "⛓️" 3043 | | "⛓" 3044 | | "🪝" 3045 | | "🧰" 3046 | | "🧲" 3047 | | "🪜" 3048 | | "⚗️" 3049 | | "⚗" 3050 | | "🧪" 3051 | | "🧫" 3052 | | "🧬" 3053 | | "🔬" 3054 | | "🔭" 3055 | | "📡" 3056 | | "💉" 3057 | | "🩸" 3058 | | "💊" 3059 | | "🩹" 3060 | | "🩼" 3061 | | "🩺" 3062 | | "🩻" 3063 | | "🚪" 3064 | | "🛗" 3065 | | "🪞" 3066 | | "🪟" 3067 | | "🛏️" 3068 | | "🛏" 3069 | | "🛋️" 3070 | | "🛋" 3071 | | "🪑" 3072 | | "🚽" 3073 | | "🪠" 3074 | | "🚿" 3075 | | "🛁" 3076 | | "🪤" 3077 | | "🪒" 3078 | | "🧴" 3079 | | "🧷" 3080 | | "🧹" 3081 | | "🧺" 3082 | | "🧻" 3083 | | "🪣" 3084 | | "🧼" 3085 | | "🫧" 3086 | | "🪥" 3087 | | "🧽" 3088 | | "🧯" 3089 | | "🛒" 3090 | | "🚬" 3091 | | "⚰️" 3092 | | "⚰" 3093 | | "🪦" 3094 | | "⚱️" 3095 | | "⚱" 3096 | | "🗿" 3097 | | "🪧" 3098 | | "🪪" 3099 | | "🏧" 3100 | | "🚮" 3101 | | "🚰" 3102 | | "♿" 3103 | | "🚹" 3104 | | "🚺" 3105 | | "🚻" 3106 | | "🚼" 3107 | | "🚾" 3108 | | "🛂" 3109 | | "🛃" 3110 | | "🛄" 3111 | | "🛅" 3112 | | "⚠️" 3113 | | "⚠" 3114 | | "🚸" 3115 | | "⛔" 3116 | | "🚫" 3117 | | "🚳" 3118 | | "🚭" 3119 | | "🚯" 3120 | | "🚱" 3121 | | "🚷" 3122 | | "📵" 3123 | | "🔞" 3124 | | "☢️" 3125 | | "☢" 3126 | | "☣️" 3127 | | "☣" 3128 | | "⬆️" 3129 | | "⬆" 3130 | | "↗️" 3131 | | "↗" 3132 | | "➡️" 3133 | | "➡" 3134 | | "↘️" 3135 | | "↘" 3136 | | "⬇️" 3137 | | "⬇" 3138 | | "↙️" 3139 | | "↙" 3140 | | "⬅️" 3141 | | "⬅" 3142 | | "↖️" 3143 | | "↖" 3144 | | "↕️" 3145 | | "↕" 3146 | | "↔️" 3147 | | "↩️" 3148 | | "↩" 3149 | | "↪️" 3150 | | "↪" 3151 | | "⤴️" 3152 | | "⤴" 3153 | | "⤵️" 3154 | | "⤵" 3155 | | "🔃" 3156 | | "🔄" 3157 | | "🔙" 3158 | | "🔚" 3159 | | "🔛" 3160 | | "🔜" 3161 | | "🔝" 3162 | | "🛐" 3163 | | "⚛️" 3164 | | "⚛" 3165 | | "🕉️" 3166 | | "🕉" 3167 | | "✡️" 3168 | | "✡" 3169 | | "☸️" 3170 | | "☸" 3171 | | "☯️" 3172 | | "☯" 3173 | | "✝️" 3174 | | "✝" 3175 | | "☦️" 3176 | | "☦" 3177 | | "☪️" 3178 | | "☪" 3179 | | "☮️" 3180 | | "☮" 3181 | | "🕎" 3182 | | "🔯" 3183 | | "♈" 3184 | | "♉" 3185 | | "♊" 3186 | | "♋" 3187 | | "♌" 3188 | | "♍" 3189 | | "♎" 3190 | | "♏" 3191 | | "♐" 3192 | | "♑" 3193 | | "♒" 3194 | | "♓" 3195 | | "⛎" 3196 | | "🔀" 3197 | | "🔁" 3198 | | "🔂" 3199 | | "▶️" 3200 | | "⏩" 3201 | | "⏭️" 3202 | | "⏭" 3203 | | "⏯️" 3204 | | "⏯" 3205 | | "◀️" 3206 | | "⏪" 3207 | | "⏮️" 3208 | | "⏮" 3209 | | "🔼" 3210 | | "⏫" 3211 | | "🔽" 3212 | | "⏬" 3213 | | "⏸️" 3214 | | "⏸" 3215 | | "⏹️" 3216 | | "⏹" 3217 | | "⏺️" 3218 | | "⏺" 3219 | | "⏏️" 3220 | | "⏏" 3221 | | "🎦" 3222 | | "🔅" 3223 | | "🔆" 3224 | | "📶" 3225 | | "📳" 3226 | | "📴" 3227 | | "♀️" 3228 | | "♀" 3229 | | "♂️" 3230 | | "♂" 3231 | | "⚧️" 3232 | | "⚧" 3233 | | "✖️" 3234 | | "✖" 3235 | | "➕" 3236 | | "➖" 3237 | | "➗" 3238 | | "🟰" 3239 | | "♾️" 3240 | | "♾" 3241 | | "‼️" 3242 | | "‼" 3243 | | "⁉️" 3244 | | "⁉" 3245 | | "❓" 3246 | | "❔" 3247 | | "❕" 3248 | | "❗" 3249 | | "〰️" 3250 | | "〰" 3251 | | "💱" 3252 | | "💲" 3253 | | "⚕️" 3254 | | "⚕" 3255 | | "♻️" 3256 | | "♻" 3257 | | "⚜️" 3258 | | "⚜" 3259 | | "🔱" 3260 | | "📛" 3261 | | "🔰" 3262 | | "⭕" 3263 | | "✅" 3264 | | "☑️" 3265 | | "☑" 3266 | | "✔️" 3267 | | "✔" 3268 | | "❌" 3269 | | "❎" 3270 | | "➰" 3271 | | "➿" 3272 | | "〽️" 3273 | | "〽" 3274 | | "✳️" 3275 | | "✳" 3276 | | "✴️" 3277 | | "✴" 3278 | | "❇️" 3279 | | "❇" 3280 | | "©️" 3281 | | "©" 3282 | | "®️" 3283 | | "®" 3284 | | "™️" 3285 | | "#️⃣" 3286 | | "#⃣" 3287 | | "*️⃣" 3288 | | "*⃣" 3289 | | "0️⃣" 3290 | | "0⃣" 3291 | | "1️⃣" 3292 | | "1⃣" 3293 | | "2️⃣" 3294 | | "2⃣" 3295 | | "3️⃣" 3296 | | "3⃣" 3297 | | "4️⃣" 3298 | | "4⃣" 3299 | | "5️⃣" 3300 | | "5⃣" 3301 | | "6️⃣" 3302 | | "6⃣" 3303 | | "7️⃣" 3304 | | "7⃣" 3305 | | "8️⃣" 3306 | | "8⃣" 3307 | | "9️⃣" 3308 | | "9⃣" 3309 | | "🔟" 3310 | | "🔠" 3311 | | "🔡" 3312 | | "🔢" 3313 | | "🔣" 3314 | | "🔤" 3315 | | "🅰️" 3316 | | "🅰" 3317 | | "🆎" 3318 | | "🅱️" 3319 | | "🅱" 3320 | | "🆑" 3321 | | "🆒" 3322 | | "🆓" 3323 | | "ℹ️" 3324 | | "ℹ" 3325 | | "🆔" 3326 | | "Ⓜ️" 3327 | | "Ⓜ" 3328 | | "🆕" 3329 | | "🆖" 3330 | | "🅾️" 3331 | | "🅾" 3332 | | "🆗" 3333 | | "🅿️" 3334 | | "🅿" 3335 | | "🆘" 3336 | | "🆙" 3337 | | "🆚" 3338 | | "🈁" 3339 | | "🈂️" 3340 | | "🈂" 3341 | | "🈷️" 3342 | | "🈷" 3343 | | "🈶" 3344 | | "🈯" 3345 | | "🉐" 3346 | | "🈹" 3347 | | "🈚" 3348 | | "🈲" 3349 | | "🉑" 3350 | | "🈸" 3351 | | "🈴" 3352 | | "🈳" 3353 | | "㊗️" 3354 | | "㊗" 3355 | | "㊙️" 3356 | | "㊙" 3357 | | "🈺" 3358 | | "🈵" 3359 | | "🔴" 3360 | | "🟠" 3361 | | "🟡" 3362 | | "🟢" 3363 | | "🔵" 3364 | | "🟣" 3365 | | "🟤" 3366 | | "⚫" 3367 | | "⚪" 3368 | | "🟥" 3369 | | "🟧" 3370 | | "🟨" 3371 | | "🟩" 3372 | | "🟦" 3373 | | "🟪" 3374 | | "🟫" 3375 | | "⬛" 3376 | | "⬜" 3377 | | "◼️" 3378 | | "◼" 3379 | | "◻️" 3380 | | "◻" 3381 | | "◾" 3382 | | "◽" 3383 | | "▪️" 3384 | | "▪" 3385 | | "▫️" 3386 | | "▫" 3387 | | "🔶" 3388 | | "🔷" 3389 | | "🔸" 3390 | | "🔹" 3391 | | "🔺" 3392 | | "🔻" 3393 | | "💠" 3394 | | "🔘" 3395 | | "🔳" 3396 | | "🔲" 3397 | | "🏁" 3398 | | "🚩" 3399 | | "🎌" 3400 | | "🏴" 3401 | | "🏳️" 3402 | | "🏳" 3403 | | "🏳️‍🌈" 3404 | | "🏳‍🌈" 3405 | | "🏳️‍⚧️" 3406 | | "🏴‍☠️" 3407 | | "🏴‍☠" 3408 | | "🇦🇨" 3409 | | "🇦🇩" 3410 | | "🇦🇪" 3411 | | "🇦🇫" 3412 | | "🇦🇬" 3413 | | "🇦🇮" 3414 | | "🇦🇱" 3415 | | "🇦🇲" 3416 | | "🇦🇴" 3417 | | "🇦🇶" 3418 | | "🇦🇷" 3419 | | "🇦🇸" 3420 | | "🇦🇹" 3421 | | "🇦🇺" 3422 | | "🇦🇼" 3423 | | "🇦🇽" 3424 | | "🇦🇿" 3425 | | "🇧🇦" 3426 | | "🇧🇧" 3427 | | "🇧🇩" 3428 | | "🇧🇪" 3429 | | "🇧🇫" 3430 | | "🇧🇬" 3431 | | "🇧🇭" 3432 | | "🇧🇮" 3433 | | "🇧🇯" 3434 | | "🇧🇱" 3435 | | "🇧🇲" 3436 | | "🇧🇳" 3437 | | "🇧🇴" 3438 | | "🇧🇶" 3439 | | "🇧🇷" 3440 | | "🇧🇸" 3441 | | "🇧🇹" 3442 | | "🇧🇻" 3443 | | "🇧🇼" 3444 | | "🇧🇾" 3445 | | "🇧🇿" 3446 | | "🇨🇦" 3447 | | "🇨🇨" 3448 | | "🇨🇩" 3449 | | "🇨🇫" 3450 | | "🇨🇬" 3451 | | "🇨🇭" 3452 | | "🇨🇮" 3453 | | "🇨🇰" 3454 | | "🇨🇱" 3455 | | "🇨🇲" 3456 | | "🇨🇳" 3457 | | "🇨🇴" 3458 | | "🇨🇵" 3459 | | "🇨🇷" 3460 | | "🇨🇺" 3461 | | "🇨🇻" 3462 | | "🇨🇼" 3463 | | "🇨🇽" 3464 | | "🇨🇾" 3465 | | "🇨🇿" 3466 | | "🇩🇪" 3467 | | "🇩🇬" 3468 | | "🇩🇯" 3469 | | "🇩🇰" 3470 | | "🇩🇲" 3471 | | "🇩🇴" 3472 | | "🇩🇿" 3473 | | "🇪🇦" 3474 | | "🇪🇨" 3475 | | "🇪🇪" 3476 | | "🇪🇬" 3477 | | "🇪🇭" 3478 | | "🇪🇷" 3479 | | "🇪🇸" 3480 | | "🇪🇹" 3481 | | "🇪🇺" 3482 | | "🇫🇮" 3483 | | "🇫🇯" 3484 | | "🇫🇰" 3485 | | "🇫🇲" 3486 | | "🇫🇴" 3487 | | "🇫🇷" 3488 | | "🇬🇦" 3489 | | "🇬🇧" 3490 | | "🇬🇩" 3491 | | "🇬🇪" 3492 | | "🇬🇫" 3493 | | "🇬🇬" 3494 | | "🇬🇭" 3495 | | "🇬🇮" 3496 | | "🇬🇱" 3497 | | "🇬🇲" 3498 | | "🇬🇳" 3499 | | "🇬🇵" 3500 | | "🇬🇶" 3501 | | "🇬🇷" 3502 | | "🇬🇸" 3503 | | "🇬🇹" 3504 | | "🇬🇺" 3505 | | "🇬🇼" 3506 | | "🇬🇾" 3507 | | "🇭🇰" 3508 | | "🇭🇲" 3509 | | "🇭🇳" 3510 | | "🇭🇷" 3511 | | "🇭🇹" 3512 | | "🇭🇺" 3513 | | "🇮🇨" 3514 | | "🇮🇩" 3515 | | "🇮🇪" 3516 | | "🇮🇱" 3517 | | "🇮🇲" 3518 | | "🇮🇳" 3519 | | "🇮🇴" 3520 | | "🇮🇶" 3521 | | "🇮🇷" 3522 | | "🇮🇸" 3523 | | "🇮🇹" 3524 | | "🇯🇪" 3525 | | "🇯🇲" 3526 | | "🇯🇴" 3527 | | "🇯🇵" 3528 | | "🇰🇪" 3529 | | "🇰🇬" 3530 | | "🇰🇭" 3531 | | "🇰🇮" 3532 | | "🇰🇲" 3533 | | "🇰🇳" 3534 | | "🇰🇵" 3535 | | "🇰🇷" 3536 | | "🇰🇼" 3537 | | "🇰🇾" 3538 | | "🇰🇿" 3539 | | "🇱🇦" 3540 | | "🇱🇧" 3541 | | "🇱🇨" 3542 | | "🇱🇮" 3543 | | "🇱🇰" 3544 | | "🇱🇷" 3545 | | "🇱🇸" 3546 | | "🇱🇹" 3547 | | "🇱🇺" 3548 | | "🇱🇻" 3549 | | "🇱🇾" 3550 | | "🇲🇦" 3551 | | "🇲🇨" 3552 | | "🇲🇩" 3553 | | "🇲🇪" 3554 | | "🇲🇫" 3555 | | "🇲🇬" 3556 | | "🇲🇭" 3557 | | "🇲🇰" 3558 | | "🇲🇱" 3559 | | "🇲🇲" 3560 | | "🇲🇳" 3561 | | "🇲🇴" 3562 | | "🇲🇵" 3563 | | "🇲🇶" 3564 | | "🇲🇷" 3565 | | "🇲🇸" 3566 | | "🇲🇹" 3567 | | "🇲🇺" 3568 | | "🇲🇻" 3569 | | "🇲🇼" 3570 | | "🇲🇽" 3571 | | "🇲🇾" 3572 | | "🇲🇿" 3573 | | "🇳🇦" 3574 | | "🇳🇨" 3575 | | "🇳🇪" 3576 | | "🇳🇫" 3577 | | "🇳🇬" 3578 | | "🇳🇮" 3579 | | "🇳🇱" 3580 | | "🇳🇴" 3581 | | "🇳🇵" 3582 | | "🇳🇷" 3583 | | "🇳🇺" 3584 | | "🇳🇿" 3585 | | "🇴🇲" 3586 | | "🇵🇦" 3587 | | "🇵🇪" 3588 | | "🇵🇫" 3589 | | "🇵🇬" 3590 | | "🇵🇭" 3591 | | "🇵🇰" 3592 | | "🇵🇱" 3593 | | "🇵🇲" 3594 | | "🇵🇳" 3595 | | "🇵🇷" 3596 | | "🇵🇸" 3597 | | "🇵🇹" 3598 | | "🇵🇼" 3599 | | "🇵🇾" 3600 | | "🇶🇦" 3601 | | "🇷🇪" 3602 | | "🇷🇴" 3603 | | "🇷🇸" 3604 | | "🇷🇺" 3605 | | "🇷🇼" 3606 | | "🇸🇦" 3607 | | "🇸🇧" 3608 | | "🇸🇨" 3609 | | "🇸🇩" 3610 | | "🇸🇪" 3611 | | "🇸🇬" 3612 | | "🇸🇭" 3613 | | "🇸🇮" 3614 | | "🇸🇯" 3615 | | "🇸🇰" 3616 | | "🇸🇱" 3617 | | "🇸🇲" 3618 | | "🇸🇳" 3619 | | "🇸🇴" 3620 | | "🇸🇷" 3621 | | "🇸🇸" 3622 | | "🇸🇹" 3623 | | "🇸🇻" 3624 | | "🇸🇽" 3625 | | "🇸🇾" 3626 | | "🇸🇿" 3627 | | "🇹🇦" 3628 | | "🇹🇨" 3629 | | "🇹🇩" 3630 | | "🇹🇫" 3631 | | "🇹🇬" 3632 | | "🇹🇭" 3633 | | "🇹🇯" 3634 | | "🇹🇰" 3635 | | "🇹🇱" 3636 | | "🇹🇲" 3637 | | "🇹🇳" 3638 | | "🇹🇴" 3639 | | "🇹🇷" 3640 | | "🇹🇹" 3641 | | "🇹🇻" 3642 | | "🇹🇼" 3643 | | "🇹🇿" 3644 | | "🇺🇦" 3645 | | "🇺🇬" 3646 | | "🇺🇲" 3647 | | "🇺🇳" 3648 | | "🇺🇸" 3649 | | "🇺🇾" 3650 | | "🇺🇿" 3651 | | "🇻🇦" 3652 | | "🇻🇨" 3653 | | "🇻🇪" 3654 | | "🇻🇬" 3655 | | "🇻🇮" 3656 | | "🇻🇳" 3657 | | "🇻🇺" 3658 | | "🇼🇫" 3659 | | "🇼🇸" 3660 | | "🇽🇰" 3661 | | "🇾🇪" 3662 | | "🇾🇹" 3663 | | "🇿🇦" 3664 | | "🇿🇲" 3665 | | "🇿🇼" 3666 | | "🏴󠁧󠁢󠁥󠁮󠁧󠁿" 3667 | | "🏴󠁧󠁢󠁳󠁣󠁴󠁿" 3668 | | "🏴󠁧󠁢󠁷󠁬󠁳󠁿"; 3669 | --------------------------------------------------------------------------------