seatalk-mcp-server
Version:
A Model Context Protocol server for SeaTalk Open Platform integration
1,239 lines (1,229 loc) • 39.7 kB
JavaScript
// src/index.ts
import {
CallToolRequestSchema,
ListToolsRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
// src/constants.ts
var SERVER_INFO = {
name: "seatalk-server",
version: "0.1.0"
};
var SERVER_CAPABILITIES = {
capabilities: {
tools: {
get_employee_profile: true,
get_employee_code_with_email: true,
check_employee_existence: true,
get_user_language_preference: true,
get_joined_group_chat_list: true,
get_thread_by_thread_id: true,
get_message_by_message_id: true,
get_chat_history: true,
get_group_info: true,
send_message_to_group_chat: true,
send_message_to_bot_user: true
}
}
};
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// src/tools.ts
function getToolDefinitions() {
return [
{
name: "get_employee_profile",
description: "Get an employee's profile by employee ID",
inputSchema: {
type: "object",
properties: {
employee_code: {
type: "string",
description: "The employee code of the employee"
}
},
required: ["employee_code"]
},
examples: [
{
description: "Get profile for employee with code 'EMP123'",
input: { employee_code: "EMP123" },
output: {
code: 0,
employee: {
employee_code: "EMP123",
name: "John Doe",
company_email: "john.doe@company.com",
department: {
department_code: "DEP001",
department_name: "Engineering"
}
}
}
}
]
},
{
name: "get_employee_code_with_email",
description: "Get an employee's code by email address",
inputSchema: {
type: "object",
properties: {
emails: {
type: "array",
items: {
type: "string",
format: "email"
},
minItems: 1,
maxItems: 500,
description: "List of employee email addresses (between 1 and 500 items)"
}
},
required: ["emails"]
},
examples: [
{
description: "Get employee codes for email addresses",
input: { emails: ["john.doe@company.com", "jane.smith@company.com"] },
output: {
code: 0,
results: [
{
email: "john.doe@company.com",
employee_code: "EMP123",
exists: true
},
{
email: "jane.smith@company.com",
employee_code: "EMP456",
exists: true
}
]
}
}
]
},
{
name: "check_employee_existence",
description: "Verify whether employees exist in the organization via SeaTalk ID",
inputSchema: {
type: "object",
properties: {
id: {
type: "string",
description: "One or more SeaTalk ID(s)"
}
},
required: ["id"]
},
examples: [
{
description: "Check if employee with ID 'ST12345' exists",
input: { id: "ST12345" },
output: {
code: 0,
exists: true
}
}
]
},
{
name: "get_user_language_preference",
description: "Get a user's language preference",
inputSchema: {
type: "object",
properties: {
employee_code: {
type: "string",
description: "The employee code of the user"
}
},
required: ["employee_code"]
},
examples: [
{
description: "Get language preference for employee with code 'EMP123'",
input: { employee_code: "EMP123" },
output: {
code: 0,
language: "en"
}
}
]
},
{
name: "get_joined_group_chat_list",
description: "Obtain group chats the bot joined",
inputSchema: {
type: "object",
properties: {
cursor: {
type: "string",
description: "Cursor for pagination"
},
page_size: {
type: "number",
description: "Number of items included in one response (1-100)"
}
}
},
examples: [
{
description: "Get the first 10 group chats the bot has joined",
input: { page_size: 10 },
output: {
code: 0,
groups: [
{
group_id: "group123",
group_name: "Engineering Team",
member_count: 15
},
{
group_id: "group456",
group_name: "Project Alpha",
member_count: 8
}
],
has_more: true,
next_cursor: "cursor_token_for_next_page"
}
}
]
},
{
name: "get_thread_by_thread_id",
description: "Retrieve all messages within a thread of a group chat",
inputSchema: {
type: "object",
properties: {
group_id: {
type: "string",
description: "The ID of the group chat"
},
thread_id: {
type: "string",
description: "The ID of the thread"
},
cursor: {
type: "string",
description: "Cursor for pagination"
},
page_size: {
type: "number",
description: "Number of messages included in one response (1-100)"
}
},
required: ["group_id", "thread_id"]
},
examples: [
{
description: "Get messages in a thread with ID 'thread123' in group 'group456'",
input: {
group_id: "group456",
thread_id: "thread123",
page_size: 20
},
output: {
code: 0,
messages: [
{
message_id: "msg001",
sender: {
employee_code: "EMP123",
name: "John Doe"
},
tag: "text",
text: {
plain_text: "Hello team!"
},
created_at: 1615456789
}
],
has_more: false
}
}
]
},
{
name: "get_message_by_message_id",
description: "Retrieve a message by its message ID within a group chat thread",
inputSchema: {
type: "object",
properties: {
message_id: {
type: "string",
description: 'The ID of the target message, which can be obtained via the event "New Mentioned Message From Group Chat"'
}
},
required: ["message_id"]
},
examples: [
{
description: "Get message with ID 'msg001'",
input: {
message_id: "msg001"
},
output: {
code: 0,
message_id: "msg001",
sender: {
employee_code: "EMP123",
name: "John Doe"
},
tag: "text",
text: {
plain_text: "Hello team!"
},
created_at: 1615456789
}
}
]
},
{
name: "get_chat_history",
description: "Obtain the group chat history (messages sent within 7 days)",
inputSchema: {
type: "object",
properties: {
group_id: {
type: "string",
description: "The ID of the group chat"
},
cursor: {
type: "string",
description: "Cursor for pagination"
},
page_size: {
type: "number",
description: "Number of messages included in one response (1-100)"
}
},
required: ["group_id"]
},
examples: [
{
description: "Get chat history for group 'group456'",
input: {
group_id: "group456",
page_size: 50
},
output: {
code: 0,
messages: [
{
message_id: "msg001",
sender: {
employee_code: "EMP123",
name: "John Doe"
},
tag: "text",
text: {
plain_text: "Hello team!"
},
created_at: 1615456789
},
{
message_id: "msg002",
sender: {
employee_code: "EMP456",
name: "Jane Smith"
},
tag: "image",
image: {
content: "https://example.com/image.jpg"
},
created_at: 1615456890
}
],
has_more: true,
next_cursor: "next_page_cursor"
}
}
]
},
{
name: "get_group_info",
description: "Get information about a group chat, including member list",
inputSchema: {
type: "object",
properties: {
group_id: {
type: "string",
description: "The ID of the group chat"
},
cursor: {
type: "string",
description: "Pagination cursor for members"
},
page_size: {
type: "number",
description: "Number of members per page (1-100)"
}
},
required: ["group_id"]
},
examples: [
{
description: "Get information about group 'group456'",
input: {
group_id: "group456"
},
output: {
code: 0,
group_info: {
group_id: "group456",
group_name: "Project Alpha",
description: "Group for Project Alpha discussion",
created_at: 1615e6,
owner: {
employee_code: "EMP123",
name: "John Doe"
}
},
members: [
{
employee_code: "EMP123",
name: "John Doe",
is_admin: true
},
{
employee_code: "EMP456",
name: "Jane Smith",
is_admin: false
}
],
has_more: false
}
}
]
},
{
name: "send_message_to_group_chat",
description: "Send a message to a group chat which the bot has been added to",
inputSchema: {
type: "object",
properties: {
group_id: {
type: "string",
description: "The ID of the group chat"
},
message: {
type: "object",
properties: {
tag: {
type: "string",
enum: ["text", "image", "file"],
description: "The type of message to send"
},
text: {
type: "object",
properties: {
content: {
type: "string",
description: "The content of the text message"
},
format: {
type: "string",
enum: ["1", "2"],
description: "1: Formatted text (Markdown), 2: Plain text"
}
}
},
image: {
type: "object",
properties: {
content: {
type: "string",
description: "Base64-encoded image file"
}
}
},
file: {
type: "object",
properties: {
filename: {
type: "string",
description: "The file name with extension"
},
content: {
type: "string",
description: "Base64-encoded file"
}
}
}
},
required: ["tag"]
},
quoted_message_id: {
type: "string",
description: "The ID of the message to quote"
},
thread_id: {
type: "string",
description: "The ID of the thread to send the message to"
}
},
required: ["group_id", "message"]
},
examples: [
{
description: "Send a text message to group 'group456'",
input: {
group_id: "group456",
message: {
tag: "text",
text: {
content: "Hello everyone! This is an announcement.",
format: "1"
}
}
},
output: {
code: 0,
message_id: "msg123"
}
},
{
description: "Send an image to group 'group456'",
input: {
group_id: "group456",
message: {
tag: "image",
image: {
content: "base64_encoded_image_data"
}
}
},
output: {
code: 0,
message_id: "msg124"
}
}
]
},
{
name: "send_message_to_bot_user",
description: "Send a message to a user via the bot",
inputSchema: {
type: "object",
properties: {
employee_code: {
type: "string",
description: "The employee code of the recipient."
},
message: {
type: "object",
properties: {
tag: {
type: "string",
enum: [
"text",
"image",
"file",
"interactive_message",
"markdown"
],
description: "The type of message to send"
},
text: {
type: "object",
properties: {
content: {
type: "string",
description: "The content of the text message (Markdown supported)"
},
format: {
type: "string",
enum: ["1", "2"],
description: "1: Formatted text (Markdown), 2: Plain text"
}
}
},
image: {
type: "object",
properties: {
content: {
type: "string",
description: "Base64-encoded image file (PNG, JPG, GIF), max 5MB after encoding"
}
}
},
file: {
type: "object",
properties: {
filename: {
type: "string",
description: "The file name with extension"
},
content: {
type: "string",
description: "Base64-encoded file, max 5MB after encoding, min 10B"
}
}
},
interactive_message: {
type: "object",
properties: {
elements: {
type: "array",
description: "Array of interactive message card elements",
items: {
oneOf: [
{
type: "object",
properties: {
element_type: {
type: "string",
enum: ["title"]
},
title: {
type: "object",
properties: {
text: {
type: "string",
description: "The title text content"
}
},
required: ["text"]
}
},
required: ["element_type", "title"]
},
{
type: "object",
properties: {
element_type: {
type: "string",
enum: ["description"]
},
description: {
type: "object",
properties: {
format: {
type: "string",
enum: ["1", "2"],
description: "1: Formatted text (Markdown), 2: Plain text"
},
text: {
type: "string",
description: "The description text content"
}
},
required: ["text"]
}
},
required: ["element_type", "description"]
},
{
type: "object",
properties: {
element_type: {
type: "string",
enum: ["button"]
},
button: {
oneOf: [
{
type: "object",
properties: {
button_type: {
type: "string",
enum: ["callback"]
},
text: {
type: "string",
description: "The button text"
},
value: {
type: "string",
description: "The callback value returned when button is clicked"
}
},
required: ["button_type", "text", "value"]
},
{
type: "object",
properties: {
button_type: {
type: "string",
enum: ["redirect"]
},
text: {
type: "string",
description: "The button text"
},
mobile_link: {
type: "object",
properties: {
type: {
type: "string",
enum: ["rn", "web"],
description: 'The type of the mobile link. Can be "rn" or "web". Must be filled if a mobile_link object is provided'
},
path: {
type: "string",
description: `The path of the RN App link, or the full URL of the web app URL, or the full URL of an external website. Must be filled if a mobile_link object is provide. For RN app paths, they must start with "/".`
},
params: {
type: "object",
description: "Additional parameters"
}
},
required: ["type", "path"]
},
desktop_link: {
type: "object",
properties: {
type: {
type: "string",
enum: ["rn", "web"],
description: 'The type of the mobile link. Can be "rn" or "web". Must be filled if a mobile_link object is provided'
},
path: {
type: "string",
description: `The path of the RN App link, or the full URL of the web app URL, or the full URL of an external website. Must be filled if a mobile_link object is provide. For RN app paths, they must start with "/".`
},
params: {
type: "object",
description: "Additional parameters"
}
},
required: ["type", "path"]
}
},
required: [
"button_type",
"text",
"mobile_link",
"desktop_link"
]
}
]
}
},
required: ["element_type", "button"]
}
]
}
}
},
required: ["elements"]
},
markdown: {
type: "object",
properties: {
content: {
type: "string",
description: "The content of the message using Markdown syntax"
}
}
}
},
required: ["tag"]
}
},
required: ["employee_code", "message"]
},
examples: [
{
description: "Send a text message to employee 'EMP123'",
input: {
employee_code: "EMP123",
message: {
tag: "text",
text: {
content: "Hi there! Just checking in.",
format: "1"
}
}
},
output: {
code: 0,
message_id: "msg125"
}
},
{
description: "Send an interactive message card to employee 'EMP123'",
input: {
employee_code: "EMP123",
message: {
tag: "interactive_message",
interactive_message: {
elements: [
{
element_type: "title",
title: {
text: "Task Assignment"
}
},
{
element_type: "description",
description: {
format: 1,
text: "You have been assigned a new task."
}
},
{
element_type: "button",
button: {
button_type: "callback",
text: "Accept",
value: "accept_task"
}
},
{
element_type: "button",
button: {
button_type: "callback",
text: "Decline",
value: "decline_task"
}
}
]
}
}
},
output: {
code: 0,
message_id: "msg126"
}
}
]
}
];
}
// src/errors.ts
var SEATALK_ERROR_CODES = {
// Common errors
0: "Success",
2: "Server error",
5: "Resource not found",
8: "Server error",
100: "App access token is expired or invalid",
101: "API is rejected due to rate limit control",
102: "Request body contains invalid input",
103: "App permission denied",
104: "Bot capability is not turned on",
105: "App is not online",
// Auth-specific errors
1e3: "App Secret is invalid",
2e3: "Single Sign-On Token is expired or invalid",
2001: "User is not an employee of the current company",
2002: "Token belongs to another app",
2003: "Cursor invalid",
2004: "Cursor expired",
// User-specific errors
3e3: "User not found with the current email",
3001: "User not found with the current code",
3002: "User is not a subscriber of the bot",
3003: "User is not signed in to SeaTalk",
3004: "Invalid custom field name",
// Message-specific errors
4e3: "Message type is invalid",
4001: "Message exceeds the maximum length",
4002: "Message sending failed",
4003: "Message cannot be empty",
4004: "Fail to fetch the quoted message due to SeaTalk's internal error",
4005: "The quoted message cannot be found",
4009: "Message cannot be found via the message id provided",
4010: "The thread cannot be found",
4011: "Mention everyone (@all) is not allowed in thread replies",
4012: "No permission to update this message",
// App-specific errors
5e3: "appID mismatch",
5001: "linkID expired",
5002: "App not released yet",
5003: "App link amount has reached the upper limit",
// Group chat errors
7e3: "Group chat not found with the current code",
7001: "Bot is not a member of the group chat"
};
var SeaTalkAPIError = class extends Error {
code;
constructor(code, message) {
const errorMessage = message || SEATALK_ERROR_CODES[code] || "Unknown error";
super(`SeaTalk API Error (${code}): ${errorMessage}`);
this.code = code;
this.name = "SeaTalkAPIError";
}
};
function handleAPIError(response) {
if (response.code !== 0) {
throw new SeaTalkAPIError(response.code, response.message);
}
}
// src/auth.ts
import NodeCache from "node-cache";
import axios from "axios";
import dotenv from "dotenv";
dotenv.config();
var SEATALK_APP_ID = process.env.SEATALK_APP_ID || "your_app_id";
var SEATALK_APP_SECRET = process.env.SEATALK_APP_SECRET || "your_app_secret";
var SEATALK_API_BASE_URL = "https://openapi.seatalk.io";
var tokenCache = new NodeCache({ stdTTL: 7e3 });
async function getAccessToken() {
const cacheKey = "seatalk_access_token";
const cachedToken = tokenCache.get(cacheKey);
if (cachedToken) {
console.error("[Auth] Using cached access token");
return cachedToken;
}
console.error("[Auth] Fetching new access token");
console.error("[Auth] Using APP_ID:", SEATALK_APP_ID ? "Available" : "Not available");
try {
const response = await axios.post(`${SEATALK_API_BASE_URL}/auth/app_access_token`, {
app_id: SEATALK_APP_ID,
app_secret: SEATALK_APP_SECRET
});
if (response.data.code !== 0 || !response.data.app_access_token) {
throw new Error(`Failed to get access token: ${JSON.stringify(response.data)}`);
}
const token = response.data.app_access_token;
tokenCache.set(cacheKey, token);
return token;
} catch (error) {
console.error("[Auth] Error getting access token:", error);
throw new Error("Failed to authenticate with SeaTalk API");
}
}
async function getAuthorizedClient() {
const token = await getAccessToken();
return axios.create({
baseURL: SEATALK_API_BASE_URL,
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
});
}
// src/api.ts
var SeaTalkAPI = class {
/**
* Makes an API call to the SeaTalk API with error handling
*
* @param method HTTP method (get, post, etc.)
* @param endpoint API endpoint path
* @param params Request parameters or body
* @param options Additional options
* @returns API response data
*/
async makeAPICall(method, endpoint, params, options) {
try {
console.error(`[API] ${options.logDescription}:`, JSON.stringify(params, null, 2));
const client = await getAuthorizedClient();
let response;
if (method === "get") {
response = await client.get(endpoint, { params });
} else {
response = await client.post(endpoint, params);
}
console.error("[API] Response from SeaTalk API:", JSON.stringify(response.data));
handleAPIError(response.data);
if (options.extraLogging) {
options.extraLogging(response.data);
}
return response.data;
} catch (error) {
console.error(`[Error] ${options.logDescription} failed:`, error);
if (error instanceof SeaTalkAPIError) {
throw error;
}
if (error instanceof Error) {
throw new Error(`${options.logDescription} failed: ${error.message}`);
}
throw new Error(`${options.logDescription} failed: Unknown error`);
}
}
async getEmployeeProfile(params) {
return this.makeAPICall(
"get",
"/contacts/v2/profile",
{ employee_code: params.employee_code },
{ logDescription: "Getting employee profile" }
);
}
async getEmployeeCodeWithEmail(params) {
if (!params.emails || !Array.isArray(params.emails) || params.emails.length === 0) {
throw new Error("emails parameter must be a non-empty array (between 1 and 500 items)");
}
return this.makeAPICall(
"post",
"/contacts/v2/get_employee_code_with_email",
{ emails: params.emails },
{ logDescription: "Getting employee codes with emails" }
);
}
async checkEmployeeExistence(params) {
return this.makeAPICall(
"get",
"/contacts/v2/check_employees",
{ id: params.id },
{ logDescription: "Checking employee existence" }
);
}
async getUserLanguagePreference(params) {
return this.makeAPICall(
"get",
"/contacts/v2/language_preference",
{ employee_code: params.employee_code },
{ logDescription: "Getting user language preference" }
);
}
async getJoinedGroupChatList(params = {}) {
return this.makeAPICall(
"get",
"/messaging/v2/group_chat/joined",
params,
{ logDescription: "Getting joined group chat list" }
);
}
async getThreadByThreadId(params) {
return this.makeAPICall(
"get",
"/messaging/v2/group_chat/get_thread_by_thread_id",
params,
{ logDescription: "Getting thread by thread ID" }
);
}
/**
* Retrieve a message by its message ID within a group chat
*
* The message can be of different types:
* - text: Contains text content
* - image: Contains an image URL
* - video: Contains a video URL
* - file: Contains a file URL and filename
* - combined_forwarded_chat_history: Contains forwarded messages
*/
async getMessageByMessageId(params) {
return this.makeAPICall(
"get",
"/messaging/v2/get_message_by_message_id",
{ message_id: params.message_id },
{
logDescription: "Getting message by message ID",
extraLogging: (data) => {
var _a, _b, _c, _d, _e;
if (data.tag) {
console.error(`[API] Retrieved message of type: ${data.tag}`);
switch (data.tag) {
case "text":
if ((_a = data.text) == null ? void 0 : _a.plain_text) {
console.error(`[API] Text message content: "${data.text.plain_text.substring(0, 50)}${data.text.plain_text.length > 50 ? "..." : ""}"`);
}
break;
case "image":
if ((_b = data.image) == null ? void 0 : _b.content) {
console.error("[API] Retrieved image URL");
}
break;
case "video":
if ((_c = data.video) == null ? void 0 : _c.content) {
console.error("[API] Retrieved video URL");
}
break;
case "file":
if ((_d = data.file) == null ? void 0 : _d.content) {
console.error(`[API] Retrieved file URL, filename: ${data.file.filename || "unknown"}`);
}
break;
case "combined_forwarded_chat_history":
if ((_e = data.combined_forwarded_chat_history) == null ? void 0 : _e.content) {
console.error(`[API] Retrieved ${data.combined_forwarded_chat_history.content.length} forwarded messages`);
}
break;
default:
console.error(`[API] Unknown message type: ${data.tag}`);
}
}
}
}
);
}
async getChatHistory(params) {
return this.makeAPICall(
"get",
"/messaging/v2/group_chat/history",
params,
{ logDescription: "Getting chat history" }
);
}
async getGroupInfo(params) {
return this.makeAPICall(
"get",
"/messaging/v2/group_chat/info",
params,
{ logDescription: "Getting group info" }
);
}
async sendMessageToGroupChat(params) {
return this.makeAPICall(
"post",
"/messaging/v2/group_chat",
params,
{ logDescription: "Sending message to group chat" }
);
}
/**
* Send a message to a user via the bot
*
* The message can be of different types:
* - text: Text message with optional formatting
* - image: Image message (Base64-encoded)
* - interactive_message: Interactive card
* - file: File attachment (Base64-encoded)
* - markdown: Markdown message (phasing out)
*/
async sendMessageToBotUser(params) {
return this.makeAPICall(
"post",
"/messaging/v2/single_chat",
params,
{
logDescription: "Sending message to bot user",
extraLogging: (data) => {
console.error(`[API] Sending message of type: ${params.message.tag}`);
}
}
);
}
};
// src/handlers.ts
var seatalkAPI = new SeaTalkAPI();
async function handleToolCall(handler, params, toolName) {
try {
console.error(`[Tool] Handling ${toolName} with params:`, JSON.stringify(params, null, 2));
const result = await handler(params);
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
console.error(`[Error] Tool execution failed for ${toolName}:`, error.message);
return {
content: [{
type: "text",
text: `Error: ${error.message}`
}],
isError: true
};
}
}
async function processToolCall(toolName, toolArguments) {
console.error(`[Tool] Processing tool call: ${toolName}`);
if (!toolArguments) {
throw new Error("Missing required arguments");
}
switch (toolName) {
case "get_employee_profile" /* GET_EMPLOYEE_PROFILE */:
return handleToolCall(
seatalkAPI.getEmployeeProfile.bind(seatalkAPI),
toolArguments,
toolName
);
case "get_employee_code_with_email" /* GET_EMPLOYEE_CODE_WITH_EMAIL */:
if (!toolArguments.emails || !Array.isArray(toolArguments.emails) || toolArguments.emails.length === 0) {
throw new Error("emails parameter must be a non-empty array (between 1 and 500 items)");
}
return handleToolCall(
seatalkAPI.getEmployeeCodeWithEmail.bind(seatalkAPI),
toolArguments,
toolName
);
case "check_employee_existence" /* CHECK_EMPLOYEE_EXISTENCE */:
return handleToolCall(
seatalkAPI.checkEmployeeExistence.bind(seatalkAPI),
toolArguments,
toolName
);
case "get_user_language_preference" /* GET_USER_LANGUAGE_PREFERENCE */:
return handleToolCall(
seatalkAPI.getUserLanguagePreference.bind(seatalkAPI),
toolArguments,
toolName
);
case "get_joined_group_chat_list" /* GET_JOINED_GROUP_CHAT_LIST */:
return handleToolCall(
seatalkAPI.getJoinedGroupChatList.bind(seatalkAPI),
toolArguments || {},
toolName
);
case "get_thread_by_thread_id" /* GET_THREAD_BY_THREAD_ID */:
return handleToolCall(
seatalkAPI.getThreadByThreadId.bind(seatalkAPI),
toolArguments,
toolName
);
case "get_message_by_message_id" /* GET_MESSAGE_BY_MESSAGE_ID */:
return handleToolCall(
seatalkAPI.getMessageByMessageId.bind(seatalkAPI),
toolArguments,
toolName
);
case "get_chat_history" /* GET_CHAT_HISTORY */:
return handleToolCall(
seatalkAPI.getChatHistory.bind(seatalkAPI),
toolArguments,
toolName
);
case "get_group_info" /* GET_GROUP_INFO */:
return handleToolCall(
seatalkAPI.getGroupInfo.bind(seatalkAPI),
toolArguments,
toolName
);
case "send_message_to_group_chat" /* SEND_MESSAGE_TO_GROUP_CHAT */:
return handleToolCall(
seatalkAPI.sendMessageToGroupChat.bind(seatalkAPI),
toolArguments,
toolName
);
case "send_message_to_bot_user" /* SEND_MESSAGE_TO_BOT_USER */:
return handleToolCall(
seatalkAPI.sendMessageToBotUser.bind(seatalkAPI),
toolArguments,
toolName
);
default:
throw new Error(`Unknown tool: ${toolName}`);
}
}
// src/index.ts
var server = new Server(
SERVER_INFO,
SERVER_CAPABILITIES
);
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: getToolDefinitions()
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
console.error(`[Tool] Calling tool: ${request.params.name}`);
try {
return await processToolCall(request.params.name, request.params.arguments);
} catch (error) {
console.error(`[Error] Tool execution failed: ${error.message}`);
return {
content: [{
type: "text",
text: `Error: ${error.message}`
}],
isError: true
};
}
});
async function main() {
console.error("[Setup] Initializing SeaTalk MCP server...");
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch((error) => {
console.error("[Error] Server startup failed:", error);
process.exit(1);
});