Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
cd62f83
chore: do not install brew dependencies in ./scripts/bootstrap by def…
stainless-app[bot] Sep 20, 2025
c349699
feat(mcp): enable experimental docs search tool
stainless-app[bot] Sep 23, 2025
a521cb8
feat(mcp): add option for including docs tools
stainless-app[bot] Sep 24, 2025
a4321bf
perf: faster formatting
stainless-app[bot] Sep 26, 2025
ffaa6d6
chore(internal): remove deprecated `compilerOptions.baseUrl` from tsc…
stainless-app[bot] Sep 26, 2025
697d742
chore(internal): fix incremental formatting in some cases
stainless-app[bot] Sep 27, 2025
6044c34
chore(mcp): allow pointing `docs_search` tool at other URLs
stainless-app[bot] Sep 27, 2025
44137d6
chore(internal): codegen related update
stainless-app[bot] Sep 27, 2025
22d7f29
chore(internal): ignore .eslintcache
stainless-app[bot] Sep 27, 2025
98c6489
fix(mcp): fix cli argument parsing logic
stainless-app[bot] Sep 30, 2025
2c84441
fix(mcp): resolve a linting issue in server code
stainless-app[bot] Sep 30, 2025
a9a736f
chore: update lockfile
stainless-app[bot] Sep 30, 2025
f2a0dfb
chore(internal): remove .eslintcache
stainless-app[bot] Oct 2, 2025
3598172
chore(internal): codegen related update
stainless-app[bot] Oct 2, 2025
303d229
release: 0.1.6
stainless-app[bot] Oct 2, 2025
8cf89a9
chore(jsdoc): fix @link annotations to refer only to parts of the pac…
stainless-app[bot] Oct 4, 2025
b898651
wip
batuhan Oct 5, 2025
fa4b40c
feat(mcp-server): convert get-accounts handler to proper format
batuhan Oct 5, 2025
f074434
feat(mcp-server): convert list-chats handler to proper format
batuhan Oct 5, 2025
a6a8511
feat(mcp-server): convert get-chat handler to proper format
batuhan Oct 5, 2025
9571c7a
feat(mcp-server): convert search-chats handler to proper format
batuhan Oct 5, 2025
8aa5a39
feat(mcp-server): convert archive-chat handler to proper format
batuhan Oct 5, 2025
186ee9c
feat(mcp-server): convert list-messages handler to proper format
batuhan Oct 5, 2025
ec7805c
feat(mcp-server): convert remaining handlers to proper format
batuhan Oct 5, 2025
b717966
Add date-fns dependency to mcp-server
batuhan Oct 6, 2025
7047f4b
Refactor handlers to use single quotes and add semicolons
batuhan Oct 6, 2025
832f3be
Add handler overrides and mapEndpoint to server
batuhan Oct 7, 2025
31e0341
chore(internal): use npm pack for build uploads
stainless-app[bot] Oct 7, 2025
02c0825
Merge branch 'next' into batuhan/mcp
batuhan Oct 7, 2025
fb27a06
chore(mcp-server): run formatter
batuhan Oct 7, 2025
ac7e17a
Refactor date utilities to use date-fns
batuhan Oct 7, 2025
bc840c5
Remove chat archive and reminder handlers, refactor output
batuhan Oct 7, 2025
d1a5970
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
fee94ba
chore: configure new SDK language
stainless-app[bot] Oct 7, 2025
7b4c119
codegen metadata
stainless-app[bot] Oct 7, 2025
3501ff9
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
f50310b
chore: configure new SDK language
stainless-app[bot] Oct 7, 2025
80ad195
codegen metadata
stainless-app[bot] Oct 7, 2025
1434ac7
codegen metadata
stainless-app[bot] Oct 7, 2025
540c168
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
e404d7e
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
80f8804
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
b161bf3
Export HTTP handler functions in http.ts
batuhan Oct 7, 2025
d7c35d2
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
0de85a3
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
25cb90e
Merge branch 'next' into batuhan/mcp
batuhan Oct 7, 2025
e657efe
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
914badc
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
e12ab16
feat(api): manual updates
stainless-app[bot] Oct 7, 2025
2e7863c
codegen metadata
stainless-app[bot] Oct 7, 2025
9bdbe85
Merge branch 'next' into batuhan/mcp
batuhan Oct 7, 2025
8536daf
w
batuhan Oct 7, 2025
b389a69
Refactor handlers to use asMarkdownContentResult
batuhan Oct 7, 2025
58374e2
Update index.ts
batuhan Oct 12, 2025
7d31069
Merge main into batuhan/mcp
batuhan Oct 16, 2025
67738fb
Fix custom MCP handlers to match merged API changes from main
batuhan Oct 16, 2025
0cc02b5
Update focus-app.ts
batuhan Oct 16, 2025
78e895d
Update yarn.lock
batuhan Oct 16, 2025
73cc71b
add date-fns
batuhan Oct 16, 2025
c850f90
w
batuhan Oct 16, 2025
941a4c2
fixes
batuhan Oct 16, 2025
2843870
Update search-messages.ts
batuhan Oct 16, 2025
0bf5aff
fixes
batuhan Oct 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/mcp-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@modelcontextprotocol/sdk": "^1.11.5",
"@valtown/deno-http-worker": "^0.0.21",
"cors": "^2.8.5",
"date-fns": "^4.1.0",
"express": "^5.1.0",
"jq-web": "https://github.com/stainless-api/jq-web/releases/download/v0.8.6/jq-web.tar.gz",
"qs": "^6.14.0",
Expand Down
28 changes: 28 additions & 0 deletions packages/mcp-server/src/handlers/get-accounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { HandlerFunction } from '../tools/types';
import { asMarkdownContentResult } from './utils';

export const handler: HandlerFunction = async (client) => {
const accounts = await client.accounts.list();
if (!accounts || accounts.length === 0)
throw new Error('No accounts found. This should never happen, please contact [email protected].');
const lines: string[] = [];
lines.push('# Accounts');
for (const acc of accounts) {
if (!acc.user) {
lines.push(`\n## ${acc.network}`);
lines.push(`**Account ID**: \`${acc.accountID}\``);
lines.push('**User**: Unknown');
continue;
}

const name = acc.user.fullName || acc.user.username || acc.user.id;
lines.push(`\n## ${acc.network}`);
lines.push(`**Account ID**: \`${acc.accountID}\``);
lines.push(`**User**: ${name}`);
if (acc.user.email) lines.push(`**Email**: ${acc.user.email}`);
if (acc.user.phoneNumber) lines.push(`**Phone**: ${acc.user.phoneNumber}`);
}
lines.push('\n# Using this information\n');
lines.push('- Pass accountIDs to narrow chat/message queries when known.');
return asMarkdownContentResult(lines);
};
18 changes: 18 additions & 0 deletions packages/mcp-server/src/handlers/get-chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { HandlerFunction } from '../tools/types';
import { asMarkdownContentResult, formatChatToMarkdown } from './utils';

export const handler: HandlerFunction = async (client, args) => {
const { chatID, ...queryParams } = args as any;
const chat = await client.chats.retrieve(chatID, queryParams);

const lines: string[] = [];
if (!chat) {
lines.push('Chat not found.');
return asMarkdownContentResult(lines);
}
lines.push(formatChatToMarkdown(chat, undefined));
lines.push('\n# Using this information\n');
lines.push('- Use search_messages to find specific content in this chat.');
lines.push('- Link the "open" link to the user to allow them to view the chat in Beeper Desktop.');
return asMarkdownContentResult(lines);
};
29 changes: 29 additions & 0 deletions packages/mcp-server/src/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type { Endpoint, HandlerFunction } from '../tools';

import { handler as get_accounts } from './get-accounts';
import { handler as open_in_app } from './open-app';
import { handler as search } from './search';
import { handler as get_chat } from './get-chat';
import { handler as search_chats } from './search-chats';
import { handler as search_messages } from './search-messages';
import { handler as send_message } from './send-message';

const HANDLER_OVERRIDES: Record<string, HandlerFunction> = {
get_accounts,
open_in_app,
focus_app: open_in_app,
search,
get_chat,
search_chats,
search_messages,
send_message,
};

export function mapEndpoint(endpoint: Endpoint): Endpoint {
const handler = HANDLER_OVERRIDES[endpoint.tool.name];
if (!handler) return endpoint;
return {
...endpoint,
handler,
};
}
47 changes: 47 additions & 0 deletions packages/mcp-server/src/handlers/list-chats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { HandlerFunction } from '../tools/types';
import { asMarkdownContentResult, formatChatToMarkdown } from './utils';

export const handler: HandlerFunction = async (client, args) => {
const body = args as any;
const output = await client.chats.search(body);

const lines: string[] = [];
lines.push('# Chats');

const items = output.items || [];
const hasMore = !!output.hasMore;

if (hasMore) {
lines.push(`\nShowing ${items.length} chats (more available)`);
if (output.oldestCursor) {
lines.push(`Next page (older): cursor='${output.oldestCursor}', direction='before'`);
}
if (output.newestCursor) {
lines.push(`Previous page (newer): cursor='${output.newestCursor}', direction='after'`);
}
} else if (items.length > 0) {
lines.push(`\nShowing ${items.length} chat${items.length === 1 ? '' : 's'}`);
}

if (items.length === 0) {
lines.push('\nNo chats found.');
} else {
for (const chatWithPreview of items) {
lines.push(formatChatToMarkdown(chatWithPreview, undefined));
const preview = (chatWithPreview as any).preview;
if (preview) {
lines.push(`**Last message**: ${preview.text || '(no text)'}`);
if (preview.senderName) {
lines.push(`**From**: ${preview.senderName}`);
}
lines.push(`**Timestamp**: ${preview.timestamp}`);
}
}
}
lines.push('\n# Using this information\n');
lines.push(
'- Pass the "chatID" to get_chat or search_messages for details about a chat, or send_message to send a message to a chat.',
);
lines.push('- Link the "open" link to the user to allow them to view the chat in Beeper Desktop.');
return asMarkdownContentResult(lines);
};
33 changes: 33 additions & 0 deletions packages/mcp-server/src/handlers/list-messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { asTextContentResult, type HandlerFunction } from '../tools/types';
import { mapMessagesToText } from './utils';

export const handler: HandlerFunction = async (client, args) => {
const body = args as any;
const output = await client.messages.search(body);

const lines: string[] = [];
lines.push('# Messages');

const items = (output as any).items || [];
const hasMore = !!(output as any).hasMore;

if (hasMore) {
lines.push(`\nShowing ${items.length} messages (more available)`);
if ((output as any).oldestCursor) {
lines.push(`Next page (older): cursor='${(output as any).oldestCursor}', direction='before'`);
}
if ((output as any).newestCursor) {
lines.push(`Previous page (newer): cursor='${(output as any).newestCursor}', direction='after'`);
}
} else if (items.length > 0) {
lines.push(`\nShowing ${items.length} message${items.length === 1 ? '' : 's'}`);
}

if (items.length === 0) {
lines.push('\nNo messages found.');
} else {
lines.push(mapMessagesToText(output as any, body, undefined));
}

return asTextContentResult(lines.join('\n'));
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Handler Overrides Not Updated

The HANDLER_OVERRIDES in handlers/index.ts isn't updated, meaning the new handler logic for list_messages is never used. For set_chat_reminder, archive_chat, and clear_chat_reminder, the original handlers are still active, which may pass an incorrect type to asTextContentResult if the API now returns a Response object directly.

Additional Locations (3)

Fix in Cursor Fix in Web

20 changes: 20 additions & 0 deletions packages/mcp-server/src/handlers/open-app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { asTextContentResult, type HandlerFunction } from '../tools/types';

export const handler: HandlerFunction = async (client, args) => {
const body = args as any;
const output = await client.focus(body);

const lines: string[] = [];
if (output.success) {
lines.push('Beeper was opened.');
if (body?.chatID) {
const chatRef = String(body.chatID);
lines.push(`Focused chat: ${chatRef}`);
}
if (body?.draftText) lines.push(`Draft text populated: ${body.draftText}`);
if (body?.draftAttachmentPath) lines.push(`Draft attachment populated: ${body.draftAttachmentPath}`);
} else {
lines.push('Failed to open Beeper.');
}
return asTextContentResult(lines.join('\n'));
};
39 changes: 39 additions & 0 deletions packages/mcp-server/src/handlers/search-chats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import type { HandlerFunction } from '../tools/types';
import { asMarkdownContentResult, formatChatToMarkdown } from './utils';

export const handler: HandlerFunction = async (client, args) => {
const body = args as any;
const output = await client.chats.search(body);

const lines: string[] = [];
lines.push('# Chats');

const items = output.items || [];
const hasMore = !!output.hasMore;

if (hasMore) {
lines.push(`\nFound ${items.length}+ chats (showing ${items.length})`);
if (output.oldestCursor) {
lines.push(`Next page (older): cursor='${output.oldestCursor}', direction='before'`);
}
if (output.newestCursor) {
lines.push(`Previous page (newer): cursor='${output.newestCursor}', direction='after'`);
}
} else if (items.length > 0) {
lines.push(`\nFound ${items.length} chat${items.length === 1 ? '' : 's'}`);
}

if (items.length === 0) {
lines.push('\nNo chats found.');
} else {
for (const chat of items) {
lines.push(formatChatToMarkdown(chat, undefined));
}
}
lines.push('\n# Using this information\n');
lines.push(
'- Pass the "chatID" to get_chat or search_messages for details about a chat, or send_message to send a message to a chat.',
);
lines.push('- Link the "open" link to the user to allow them to view the chat in Beeper Desktop.');
return asMarkdownContentResult(lines);
};
8 changes: 8 additions & 0 deletions packages/mcp-server/src/handlers/search-messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { HandlerFunction } from '../tools/types';
import { mapMessagesToText, asMarkdownContentResult } from './utils';

export const handler: HandlerFunction = async (client, args) => {
const body = args as any;
const output = await client.messages.search(body);
return asMarkdownContentResult(mapMessagesToText(output, body, undefined));
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Type Safety Violation in args Parameter

The args parameter is cast to any without validation, bypassing TypeScript's type safety. This could lead to runtime errors if client.messages.search receives unexpected input.

Fix in Cursor Fix in Web

24 changes: 24 additions & 0 deletions packages/mcp-server/src/handlers/search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { HandlerFunction } from '../tools/types';
import { asMarkdownContentResult, formatChatToMarkdown, mapMessagesToText } from './utils';

export const handler: HandlerFunction = async (client, args) => {
const body = args as any;
const output = await client.search(body);

const lines: string[] = [];
if (body?.query) lines.push(`Query: "${body.query}"`);
const results = output?.results;
if (results?.chats?.length) {
lines.push('\n# Chats');
for (const chat of results.chats) lines.push(formatChatToMarkdown(chat, undefined));
}
if (results?.in_groups?.length) {
lines.push('\n# In Groups');
for (const chat of results.in_groups) lines.push(formatChatToMarkdown(chat, undefined));
}
if (results?.messages?.items?.length) {
lines.push('\n# Messages');
lines.push(mapMessagesToText(results.messages as any, body, undefined));
}
return asMarkdownContentResult(lines);
};
17 changes: 17 additions & 0 deletions packages/mcp-server/src/handlers/send-message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { HandlerFunction } from '../tools/types';
import { asMarkdownContentResult, createOpenLink } from './utils';

export const handler: HandlerFunction = async (client, args) => {
const body = args as any;
const output = await client.messages.send(body);

const lines: string[] = [];
if (output.pendingMessageID) {
const deeplink = createOpenLink('', body?.chatID ?? '');
if (deeplink) lines.push(`**Open the chat in Beeper**: ${deeplink}`);
} else {
lines.push('Failed to send message.');
}

return asMarkdownContentResult(lines);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Deep Linking and Import Errors in Handler

The new send-message.ts handler has two issues. It generates malformed deep links because createOpenLink uses an empty base URL, preventing the "Open chat" functionality. Additionally, the handler may fail to import at runtime, possibly due to a naming mismatch or an integration problem with the handlers index.

Fix in Cursor Fix in Web

Loading