Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
26 changes: 24 additions & 2 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,33 @@ LINE公式アカウントとAI Agentを接続するために、LINE Messaging AP
5. **get_profile**
- LINEユーザーの詳細なプロフィール情報を取得する。表示名、プロフィール画像URL、ステータスメッセージ、言語を取得できる。
- **入力:**
- `user_id` (string?): プロフィールを取得したいユーザーのユーザーID。デフォルトはDESTINATION_USER_ID。`user_id`または`DESTINATION_USER_ID`のどちらか一方は必ず設定する必要があります。=======
- `user_id` (string?): プロフィールを取得したいユーザーのユーザーID。デフォルトはDESTINATION_USER_ID。`user_id`または`DESTINATION_USER_ID`のどちらか一方は必ず設定する必要があります。
6. **get_message_quota**
- LINE公式アカウントのメッセージ容量と消費量を取得します。月間メッセージ制限と現在の使用量が表示されます。
- **入力:**
- なし
7. **get_rich_menu_list**
- LINE公式アカウントに登録されているリッチメニューの一覧を取得する。
- **入力:**
- なし
8. **delete_rich_menu**
- LINE公式アカウントからリッチメニューを削除する。
- **入力:**
- `richMenuId` (string): 削除するリッチメニューのID。
9. **set_rich_menu_image**
- LINE公式アカウントのリッチメニューの画像を更新する。
- **入力:**
- `richMenuId` (string): 更新するリッチメニューのID。
- `projectPath` (string): プロジェクトのパス。
- `imagePath` (string): 更新する画像のパス。`@`プレフィックスを使用してプロジェクトルートからの相対パスを指定できます。
10. **set_rich_menu_default**
- リッチメニューをデフォルトとして設定する。
- **入力:**
- `richMenuId` (string): デフォルトとして設定するリッチメニューのID。
11. **cancel_rich_menu_default**
- デフォルトのリッチメニューを解除する。
- **入力:**
- なし

## インストール (npxを使用)

Expand Down Expand Up @@ -132,4 +154,4 @@ Claude DesktopやClaudeなどのAI Agentに次の設定を追加してくださ
}
}
}
```
```
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@
- Get the message quota and consumption of the LINE Official Account. This shows the monthly message limit and current usage.
- **Inputs:**
- None
7. **get_rich_menu_list**
- Get the list of rich menus associated with your LINE Official Account.
- **Inputs:**
- None
8. **delete_rich_menu**
- Delete a rich menu from your LINE Official Account.
- **Inputs:**
- `richMenuId` (string): The ID of the rich menu to delete.
9. **set_rich_menu_image**
- Update a rich menu associated with your LINE Official Account.
- **Inputs:**
- `richMenuId` (string): The ID of the rich menu to update.
- `projectPath` (string): The path of the project.
- `imagePath` (string): The path of the image to update. Use `@` prefix to specify a path relative to the project root.
10. **set_rich_menu_default**
- Set a rich menu as the default rich menu.
- **Inputs:**
- `richMenuId` (string): The ID of the rich menu to set as default.
11. **cancel_rich_menu_default**
- Cancel the default rich menu.
- **Inputs:**
- None

## Installation (Using npx)

Expand Down
97 changes: 97 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,94 @@ 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."),
projectPath: z.string().describe("The path of the project."),
imagePath: z.string().describe("The path of the image to update."),
},
async ({ richMenuId, imagePath, projectPath }) => {
try {
if (imagePath.startsWith("@")) {
imagePath = imagePath.replace("@", `${projectPath}/`);
}
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