Skip to content
Merged
Changes from 2 commits
Commits
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
93 changes: 93 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as line from "@line/bot-sdk";
import { z } from "zod";
import pkg from "../package.json" with { type: "json" };
Copy link
Contributor

Choose a reason for hiding this comment

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

This syntax is not available in Node v20.9 or earlier...
Instead, use ./version.js, so this import is unnecessary.

Related to #61

import fs from "fs";
import { LINE_BOT_MCP_SERVER_VERSION, USER_AGENT } from "./version.js";

const NO_USER_ID_ERROR =
Expand All @@ -40,6 +42,13 @@ const messagingApiClient = new line.messagingApi.MessagingApiClient({
},
});

const lineBlobClient = new line.messagingApi.MessagingApiBlobClient({
channelAccessToken: channelAccessToken,
defaultHeaders: {
"User-Agent": `${pkg.name}/${pkg.version}`,
},
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const lineBlobClient = new line.messagingApi.MessagingApiBlobClient({
channelAccessToken: channelAccessToken,
defaultHeaders: {
"User-Agent": `${pkg.name}/${pkg.version}`,
},
});
const lineBlobClient = new line.messagingApi.MessagingApiBlobClient({
channelAccessToken: channelAccessToken,
defaultHeaders: {
"User-Agent": USER_AGENT,
},
});

Please use ./version.js as shown 🙏

Related to #61


function createErrorResponse(message: string) {
return {
isError: true,
Expand Down Expand Up @@ -229,6 +238,90 @@ server.tool(
},
);

server.tool(
"get_rich_menu_list",
"Get the list of rich menus associated with your LINE Official Account.",
{},
async () => {
try {
const response = await messagingApiClient.getRichMenuList();
return createSuccessResponse(response);
} catch (error) {
return createErrorResponse(
`Failed to broadcast message: ${error.message}`,
);
}
},
);

server.tool(
"delete_rich_menu",
"Delete a rich menu from your LINE Official Account.",
{
richMenuId: z.string().describe("The ID of the rich menu to delete."),
},
async ({ richMenuId }) => {
try {
const response = await messagingApiClient.deleteRichMenu(richMenuId);
return createSuccessResponse(response);
} catch (error) {
return createErrorResponse(
`Failed to delete rich menu: ${error.message}`,
);
}
},
);
Comment on lines +232 to +264
Copy link
Contributor Author

Choose a reason for hiding this comment

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

delete and list

スクリーンショット 2025-05-29 8 26 37


server.tool(
"set_rich_menu_image",
"Update a rich menu associated with your LINE Official Account.",
{
richMenuId: z.string().describe("The ID of the rich menu to update."),
imagePath: z.string().describe("The path of the image to update."),
},
async ({ richMenuId, imagePath }) => {
try {
const imageBuffer = fs.readFileSync(imagePath);
const imageType = "image/png";
const imageBlob = new Blob([imageBuffer], { type: imageType });

const response = await lineBlobClient.setRichMenuImage(
richMenuId,
imageBlob,
);
return createSuccessResponse(response);
} catch (error) {
return createErrorResponse(
`Failed to update rich menu: ${error.message}`,
);
}
},
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

set rich menu image

スクリーンショット 2025-05-29 9 00 37

Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you!
Hmm, I'm not sure about the best practice for passing images to the MCP Server...

I tried verifying whether we could pass images through chat from AI agents like Claude by changing the tool argument from a path to base64, but I ran into character limits. (like this)

With the method you implemented, we need to prepare images in advance within the local mcp-server repository, but maybe this approach will not work with the npx installation method (though I can't think of any better alternatives...).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This command needs to be discussed further. Therefore, I will separate the PR.


server.tool(
"set_rich_menu_default",
"Set a rich menu as the default rich menu.",
{
richMenuId: z
.string()
.describe("The ID of the rich menu to set as default."),
},
async ({ richMenuId }) => {
const response = await messagingApiClient.setDefaultRichMenu(richMenuId);
return createSuccessResponse(response);
},
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

set default

スクリーンショット 2025-05-29 9 02 18


server.tool(
"cancel_rich_menu_default",
"Cancel the default rich menu.",
{},
async () => {
const response = await messagingApiClient.cancelDefaultRichMenu();
return createSuccessResponse(response);
},
);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

スクリーンショット 2025-05-29 9 04 02


async function main() {
if (!process.env.CHANNEL_ACCESS_TOKEN) {
console.error("Please set CHANNEL_ACCESS_TOKEN");
Expand Down