Skip to content

Conversation

4geru
Copy link
Contributor

@4geru 4geru commented Jun 8, 2025

Hello Maintainers! 👋

Thank you for maintaining this excellent LINE Bot MCP Server project. I'm excited to contribute a new feature that adds dynamic rich menu creation capabilities. I hope this enhancement will be valuable for the community.

Background

The LINE Bot MCP Server needed functionality to create rich menus dynamically. Previously, users had to manually create rich menu images and configure them individually. There was a need for a flexible, text-based approach to generate rich menus programmatically.

Todo

1. Implemented Rich Menu Creation Feature

  • Added new create_rich_menu tool
  • Implemented automatic rich menu generation that accepts chat bar text and action arrays

2. Built Template System

  • Created 6 rich menu templates (Markdown format) supporting 1-6 items
  • Placed templates in richmenu-templates/ directory
  • Added example images in richmenu-examples/ for reference

3. Implemented Image Generation Pipeline

  • Used Marp to convert Markdown templates to HTML
  • Used Puppeteer to convert HTML to PNG images with specified dimensions (1600x910px)
  • Uploaded generated images to LINE Messaging API

4. Defined LINE Bot Action Schema

  • Created src/common/schema/actionSchema.ts with schemas for 9 action types (postback, message, uri, datetimepicker, camera, cameraRoll, location, richmenuswitch, clipboard)
  • Implemented type-safe validation using Zod

5. Added Dependencies

  • @marp-team/marp-core: For Markdown to slide generation
  • @marp-team/marp-cli: Marp command-line tools

Verified

Functionality

  • All templates (1-6 items) work correctly
  • Placeholders (item01 ' ~ ' item06) in Markdown templates are properly replaced with action labels
  • Generated images comply with LINE specifications (1600x910px)
  • Complete workflow from rich menu creation, image upload, to default setting works

Error Handling

  • Proper error responses for invalid action counts (< 1 or > 6)
  • Error catching at each stage: Marp conversion, Puppeteer execution, LINE API calls
  • Proper cleanup of temporary files

Technical Aspects

  • Proper management and deletion of temporary files (HTML, PNG)
  • Proper cleanup of Puppeteer browser instances
  • Correct usage of both LINE Messaging API Client and Blob Client

Result

This implementation enables automatic generation of visually appealing rich menus using only text-based configuration, significantly improving the developer experience for LINE Bot creators.

Usage

https://x.com/_4geru/status/1933793286285898071
https://x.com/_4geru/status/1933890824808632560

@eucyt
Copy link
Contributor

eucyt commented Jun 9, 2025

@4geru
(just in case)
We refactored dir struction to avoid conflict. So check this development flow for detail please 🙇
https://github.com/line/line-bot-mcp-server/blob/ee1196bc33c1d9ca40c75e67fa044734a8d8602b/CONTRIBUTING.md#add-a-new-tool

@4geru
Copy link
Contributor Author

4geru commented Jun 10, 2025

@eucyt Hello Euchi-san. I updated to separate the tools file.
Also, I can generate and upload the rich menu.

However, it cannot open in my app. LINE app stands up many times, and restarts...
Could you support me...? 🙇 🙇

スクリーンショット 2025-06-11 7 58 20

@4geru
Copy link
Contributor Author

4geru commented Jun 10, 2025

Memo

prompt

LINE MCP を使って リッチメニューをなので作成してください
[1] カメラのアクションを設定してください
[2] テンプレートは7です
[3] ボタンは「ポッチっとな」です

result

{
  "richMenuId": "richmenu-6ba77c74ccaf8a1153045f627cc972c3",
  "setImageResponse": {},
  "richMenuImagePath": "/tmp/slide-07-1749597029244.png",
  "params": {
    "name": "ポッチっとな",
    "chatBarText": "ポッチっとな",
    "selected": false,
    "size": {
      "width": 1600,
      "height": 910
    },
    "areas": [
      {
        "bounds": {
          "x": 0,
          "y": 0,
          "width": 1600,
          "height": 900
        },
        "action": {
          "type": "camera",
          "label": "ポッチっとな"
        }
      }
    ]
  }
}

@4geru
Copy link
Contributor Author

4geru commented Jun 11, 2025

I checked image requirements. 👌
https://developers.line.biz/ja/reference/messaging-api/#upload-rich-menu-image-requirements
スクリーンショット 2025-06-11 13 46 16

@4geru 4geru changed the title 4geru/generate rich menu create rich menu operation Jun 15, 2025
@4geru
Copy link
Contributor Author

4geru commented Jun 15, 2025

@eucyt Sorry for the wait. We have improved and verified the functionality, so please leave a review.

@mokuzon mokuzon requested a review from eucyt June 17, 2025 05:30
@4geru 4geru force-pushed the 4geru/generate-rich-menu branch from b77e1cd to 0b6bd3d Compare June 19, 2025 14:26
@4geru
Copy link
Contributor Author

4geru commented Jun 19, 2025

@eucyt Sorry, I committed a file...
0b6bd3d

@eucyt
Copy link
Contributor

eucyt commented Jun 30, 2025

@4geru san
Sorry to late...
I'll review this in this week 🙇

@eucyt
Copy link
Contributor

eucyt commented Jul 3, 2025

@eucyt Hello Euchi-san. I updated to separate the tools file. Also, I can generate and upload the rich menu.

However, it cannot open in my app. LINE app stands up many times, and restarts... Could you support me...? 🙇 🙇

スクリーンショット 2025-06-11 7 58 20

Is this issue still occurring? If it is still happening, could you please provide the steps to reproduce it, specifically what is happening, and what the expected behavior should be? 🙏

Copy link
Contributor

@eucyt eucyt left a comment

Choose a reason for hiding this comment

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

This is awesome! Thank you for implementing this wonderful tool. This greatly expands the possibilities of line-bot-mcp-server.

Copy link
Contributor

Choose a reason for hiding this comment

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

I was unable to run this tool with Docker on my local. I encountered the following error. Is it possible to make it compatible with Docker? (I'm also concerned about whether screenshots will work. However, if this tool could be used with Docker, it would be quite convenient 😄 )

{"error":{"errno":-2,"code":"ENOENT","syscall":"open","path":"/app/richmenu-templetes/templete-02.md"},"createRichMenuResponse":{"richMenuId":"richmenu-828cebe92610f4649aab4b0a8ea05d17"},"setImageResponse":null,"setDefaultResponse":null}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm going to try with Docker. I tried from dist/index.js.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tested to work well with the Docker.
font issue was too difficult, so I'd taken the time.

@4geru
Copy link
Contributor Author

4geru commented Jul 3, 2025

This issue was not solved :). Some accounts are dead. 😃 However, this tool is updated.

#121 (comment)

@eucyt
Copy link
Contributor

eucyt commented Jul 4, 2025

This issue was not solved :). Some accounts are dead. 😃 However, this tool is updated.

#121 (comment)

@4geru san
I don't fully understand the issue you're consulting about, so could you first tell me about the problem that's occurring?

Are you saying that the LINE client application in smartphone keeps crashing? In other words, is this directly unrelated to the line-bot-mcp-server or LINE Official Account? I would appreciate if you could explain the steps that lead to this malfunction.

@4geru
Copy link
Contributor Author

4geru commented Sep 6, 2025

Are you saying that the LINE client application in smartphone keeps crashing?

Yes, LINE client application in smartphone was clashed. However, the currently version is not crashed.

@4geru 4geru force-pushed the 4geru/generate-rich-menu branch from 9ad75b5 to fd89189 Compare September 6, 2025 07:59
size: 16:9
---
<style>
section {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd like to separate the common css part. However, it was a difficult. therefore, I'll work the next issue.

Prioritize release over perfection. 🙇 🙇

Comment on lines +50 to +55
const lineBlobClient = new line.messagingApi.MessagingApiBlobClient({
channelAccessToken: channelAccessToken,
defaultHeaders: {
"User-Agent": USER_AGENT,
},
});
Copy link
Contributor Author

Choose a reason for hiding this comment

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

the code is the same with BolbClient.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tested to work well with the Docker.
font issue was too difficult, so I'd taken the time.

import { actionSchema } from "../common/schema/actionSchema.js";
import { promises as fsp } from "fs";

const RICHMENU_HEIGHT = 910;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

10px is for the margin.

@4geru
Copy link
Contributor Author

4geru commented Sep 7, 2025

@eucyt Could you review this PR again?

@4geru 4geru requested a review from eucyt September 7, 2025 07:51
Copy link
Contributor

@eucyt eucyt left a comment

Choose a reason for hiding this comment

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

@4geru
Sorry for the delay.

It’s mostly LGTM!
I was able to confirm it works with Docker and Node 🎉
I’ve left a few comments on some minor points, so please address those and resolve the conflicts.

Comment on lines +249 to +257
try {
await fsp.copyFile(richMenuImagePath, outputPath);
console.log(`Rich menu image saved to: ${outputPath}`);
} catch (error) {
console.warn(`Failed to save image to output directory: ${error}`);
}

// 5. Delete the temporary HTML file
await fsp.unlink(tempHtmlPath);
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
try {
await fsp.copyFile(richMenuImagePath, outputPath);
console.log(`Rich menu image saved to: ${outputPath}`);
} catch (error) {
console.warn(`Failed to save image to output directory: ${error}`);
}
// 5. Delete the temporary HTML file
await fsp.unlink(tempHtmlPath);
try {
await fsp.copyFile(richMenuImagePath, outputPath);
console.log(`Rich menu image saved to: ${outputPath}`);
} catch (error) {
console.warn(`Failed to save image to output directory: ${error}`);
} finally {
// 5. Delete the temporary HTML file
await fsp.unlink(tempHtmlPath);
}

How about adding finally block and unlinking there?

label: z.string().optional(),
data: z.string(),
displayText: z.string().optional(),
inputOption: z.enum(["closeRichMenu", "openRichMenu"]).optional(),
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
inputOption: z.enum(["closeRichMenu", "openRichMenu"]).optional(),
inputOption: z
.enum(["closeRichMenu", "openRichMenu", "openKeyboard", "openVoice"])
.optional(),

Comment on lines +46 to +54
const cameraActionSchema = z.object({
type: z.literal("camera"),
label: z.string().optional(),
});
// 6. Camera roll action
const cameraRollActionSchema = z.object({
type: z.literal("cameraRoll"),
label: z.string().optional(),
});
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 cameraActionSchema = z.object({
type: z.literal("camera"),
label: z.string().optional(),
});
// 6. Camera roll action
const cameraRollActionSchema = z.object({
type: z.literal("cameraRoll"),
label: z.string().optional(),
});
const cameraActionSchema = z.object({
type: z.literal("camera"),
label: z.string(),
});
// 6. Camera roll action
const cameraRollActionSchema = z.object({
type: z.literal("cameraRoll"),
label: z.string(),
});

Comment on lines +184 to +189
html, body {
margin: 0;
padding: 0;
height: ${RICHMENU_HEIGHT}px;
overflow: hidden;
}
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
html, body {
margin: 0;
padding: 0;
height: ${RICHMENU_HEIGHT}px;
overflow: hidden;
}
html, body {
margin: 0;
padding: 0;
height: ${RICHMENU_HEIGHT}px;
overflow: hidden;
}

just removing space at end of line 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants