-
Notifications
You must be signed in to change notification settings - Fork 0
feat(mcp): customize MCP outputs #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cd62f83
c349699
a521cb8
a4321bf
ffaa6d6
697d742
6044c34
44137d6
22d7f29
98c6489
2c84441
a9a736f
f2a0dfb
3598172
303d229
8cf89a9
b898651
fa4b40c
f074434
a6a8511
9571c7a
8aa5a39
186ee9c
ec7805c
b717966
7047f4b
832f3be
31e0341
02c0825
fb27a06
ac7e17a
bc840c5
d1a5970
fee94ba
7b4c119
3501ff9
f50310b
80ad195
1434ac7
540c168
e404d7e
80f8804
b161bf3
d7c35d2
0de85a3
25cb90e
e657efe
914badc
e12ab16
2e7863c
9bdbe85
8536daf
b389a69
58374e2
7d31069
67738fb
0cc02b5
78e895d
73cc71b
c850f90
941a4c2
2843870
0bf5aff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
}; |
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); | ||
}; |
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, | ||
}; | ||
} |
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); | ||
}; |
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')); | ||
}; | ||
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')); | ||
}; |
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); | ||
}; |
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)); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
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); | ||
}; |
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); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Deep Linking and Import Errors in HandlerThe new |
There was a problem hiding this comment.
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
inhandlers/index.ts
isn't updated, meaning the new handler logic forlist_messages
is never used. Forset_chat_reminder
,archive_chat
, andclear_chat_reminder
, the original handlers are still active, which may pass an incorrect type toasTextContentResult
if the API now returns aResponse
object directly.Additional Locations (3)
packages/mcp-server/src/tools/chats/reminders/clear-chat-reminder.ts#L37-L38
packages/mcp-server/src/tools/chats/archive-chat.ts#L39-L40
packages/mcp-server/src/tools/chats/reminders/set-chat-reminder.ts#L49-L50