@line-harness/mcp-server 0.6.7 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -529,9 +529,9 @@ import { z as z5 } from "zod";
529
529
  function registerManageTags(server2) {
530
530
  server2.tool(
531
531
  "manage_tags",
532
- "Create tags, add tags to friends, or remove tags from friends. Supports batch operations on multiple friends.",
532
+ "List, create, or delete tags, and add/remove tags to/from friends. Supports batch operations on multiple friends.",
533
533
  {
534
- action: z5.enum(["create", "add", "remove"]).describe("Action to perform"),
534
+ action: z5.enum(["list", "create", "delete", "add", "remove"]).describe("Action to perform"),
535
535
  tagName: z5.string().optional().describe("Tag name (for 'create' action)"),
536
536
  tagColor: z5.string().optional().describe("Tag color hex code (for 'create' action, e.g. '#FF0000')"),
537
537
  tagId: z5.string().optional().describe("Tag ID (for 'add' or 'remove' actions)"),
@@ -542,6 +542,19 @@ function registerManageTags(server2) {
542
542
  async ({ action, tagName, tagColor, tagId, friendIds }) => {
543
543
  try {
544
544
  const client = getClient();
545
+ if (action === "list") {
546
+ const tags = await client.tags.list();
547
+ return {
548
+ content: [{ type: "text", text: JSON.stringify({ success: true, tags }, null, 2) }]
549
+ };
550
+ }
551
+ if (action === "delete") {
552
+ if (!tagId) throw new Error("tagId is required for delete action");
553
+ await client.tags.delete(tagId);
554
+ return {
555
+ content: [{ type: "text", text: JSON.stringify({ success: true, deleted: tagId }, null, 2) }]
556
+ };
557
+ }
545
558
  if (action === "create") {
546
559
  if (!tagName) throw new Error("tagName is required for create action");
547
560
  const tag = await client.tags.create({
@@ -712,7 +725,7 @@ import { z as z8 } from "zod";
712
725
  function registerCreateRichMenu(server2) {
713
726
  server2.tool(
714
727
  "create_rich_menu",
715
- "Create a LINE rich menu (the persistent menu at the bottom of the chat). Image must be uploaded separately via LINE Developers Console. This creates the menu structure and button areas.",
728
+ "Create a LINE rich menu with optional image upload. Provide imageData (base64) to attach the menu image in one step.",
716
729
  {
717
730
  name: z8.string().describe("Rich menu name"),
718
731
  chatBarText: z8.string().default("\u30E1\u30CB\u30E5\u30FC").describe("Text shown on the chat bar button"),
@@ -724,9 +737,11 @@ function registerCreateRichMenu(server2) {
724
737
  areas: z8.string().describe(
725
738
  "JSON string of menu button areas. Format: [{ bounds: { x, y, width, height }, action: { type: 'uri'|'message'|'postback', uri?, text?, data? } }]"
726
739
  ),
740
+ imageData: z8.string().optional().describe("Base64-encoded image data for the rich menu (PNG or JPEG, 2500x1686 or 2500x843)"),
741
+ imageContentType: z8.enum(["image/png", "image/jpeg"]).default("image/jpeg").describe("Image MIME type"),
727
742
  setAsDefault: z8.boolean().default(false).describe("Set this as the default rich menu for all friends")
728
743
  },
729
- async ({ name, chatBarText, size, selected, areas, setAsDefault }) => {
744
+ async ({ name, chatBarText, size, selected, areas, imageData, imageContentType, setAsDefault }) => {
730
745
  try {
731
746
  const client = getClient();
732
747
  const menu = await client.richMenus.create({
@@ -736,6 +751,9 @@ function registerCreateRichMenu(server2) {
736
751
  selected,
737
752
  areas: JSON.parse(areas)
738
753
  });
754
+ if (imageData) {
755
+ await client.richMenus.uploadImage(menu.richMenuId, imageData, imageContentType);
756
+ }
739
757
  if (setAsDefault) {
740
758
  await client.richMenus.setDefault(menu.richMenuId);
741
759
  }
@@ -747,6 +765,7 @@ function registerCreateRichMenu(server2) {
747
765
  {
748
766
  success: true,
749
767
  richMenuId: menu.richMenuId,
768
+ imageUploaded: !!imageData,
750
769
  isDefault: setAsDefault
751
770
  },
752
771
  null,
@@ -1488,6 +1507,338 @@ function registerUploadImage(server2) {
1488
1507
  );
1489
1508
  }
1490
1509
 
1510
+ // src/tools/manage-friends.ts
1511
+ import { z as z19 } from "zod";
1512
+ function registerManageFriends(server2) {
1513
+ server2.tool(
1514
+ "manage_friends",
1515
+ "\u53CB\u3060\u3061\u306E\u7BA1\u7406\u64CD\u4F5C\u3002count: \u53CB\u3060\u3061\u6570\u53D6\u5F97\u3001set_metadata: \u30E1\u30BF\u30C7\u30FC\u30BF\u66F4\u65B0\u3001set_rich_menu: \u30EA\u30C3\u30C1\u30E1\u30CB\u30E5\u30FC\u5272\u5F53\u3001remove_rich_menu: \u30EA\u30C3\u30C1\u30E1\u30CB\u30E5\u30FC\u89E3\u9664\u3002",
1516
+ {
1517
+ action: z19.enum(["count", "set_metadata", "set_rich_menu", "remove_rich_menu"]).describe("Action to perform"),
1518
+ friendId: z19.string().optional().describe("Friend ID (required for set_metadata, set_rich_menu, remove_rich_menu)"),
1519
+ metadata: z19.string().optional().describe("JSON string of metadata fields to set (for 'set_metadata')"),
1520
+ richMenuId: z19.string().optional().describe("Rich menu ID to assign (for 'set_rich_menu')")
1521
+ },
1522
+ async ({ action, friendId, metadata, richMenuId }) => {
1523
+ try {
1524
+ const client = getClient();
1525
+ if (action === "count") {
1526
+ const count = await client.friends.count();
1527
+ return {
1528
+ content: [{ type: "text", text: JSON.stringify({ success: true, count }, null, 2) }]
1529
+ };
1530
+ }
1531
+ if (!friendId) throw new Error("friendId is required for this action");
1532
+ if (action === "set_metadata") {
1533
+ if (!metadata) throw new Error("metadata (JSON string) is required for set_metadata");
1534
+ const fields = JSON.parse(metadata);
1535
+ const friend = await client.friends.setMetadata(friendId, fields);
1536
+ return {
1537
+ content: [{ type: "text", text: JSON.stringify({ success: true, friend }, null, 2) }]
1538
+ };
1539
+ }
1540
+ if (action === "set_rich_menu") {
1541
+ if (!richMenuId) throw new Error("richMenuId is required for set_rich_menu");
1542
+ await client.friends.setRichMenu(friendId, richMenuId);
1543
+ return {
1544
+ content: [{ type: "text", text: JSON.stringify({ success: true, friendId, richMenuId }, null, 2) }]
1545
+ };
1546
+ }
1547
+ if (action === "remove_rich_menu") {
1548
+ await client.friends.removeRichMenu(friendId);
1549
+ return {
1550
+ content: [{ type: "text", text: JSON.stringify({ success: true, friendId, removed: true }, null, 2) }]
1551
+ };
1552
+ }
1553
+ throw new Error(`Unknown action: ${action}`);
1554
+ } catch (err) {
1555
+ return {
1556
+ content: [{ type: "text", text: JSON.stringify({ success: false, error: String(err) }) }],
1557
+ isError: true
1558
+ };
1559
+ }
1560
+ }
1561
+ );
1562
+ }
1563
+
1564
+ // src/tools/manage-scenarios.ts
1565
+ import { z as z20 } from "zod";
1566
+ function registerManageScenarios(server2) {
1567
+ server2.tool(
1568
+ "manage_scenarios",
1569
+ "\u30B7\u30CA\u30EA\u30AA\u306E\u7BA1\u7406\u64CD\u4F5C\u3002list: \u4E00\u89A7\u3001get: \u8A73\u7D30\uFF08\u30B9\u30C6\u30C3\u30D7\u542B\u3080\uFF09\u3001update: \u66F4\u65B0\u3001delete: \u524A\u9664\u3001add_step: \u30B9\u30C6\u30C3\u30D7\u8FFD\u52A0\u3001update_step: \u30B9\u30C6\u30C3\u30D7\u66F4\u65B0\u3001delete_step: \u30B9\u30C6\u30C3\u30D7\u524A\u9664\u3002",
1570
+ {
1571
+ action: z20.enum(["list", "get", "update", "delete", "add_step", "update_step", "delete_step"]).describe("Action to perform"),
1572
+ scenarioId: z20.string().optional().describe("Scenario ID (required for get, update, delete, add_step, update_step, delete_step)"),
1573
+ stepId: z20.string().optional().describe("Step ID (required for update_step, delete_step)"),
1574
+ name: z20.string().optional().describe("Scenario name (for update)"),
1575
+ description: z20.string().nullable().optional().describe("Scenario description (for update)"),
1576
+ triggerType: z20.enum(["friend_add", "tag_added", "manual"]).optional().describe("Trigger type (for update)"),
1577
+ triggerTagId: z20.string().nullable().optional().describe("Trigger tag ID (for update)"),
1578
+ isActive: z20.boolean().optional().describe("Active status (for update)"),
1579
+ stepOrder: z20.number().optional().describe("Step order number (for add_step, update_step)"),
1580
+ delayMinutes: z20.number().optional().describe("Delay in minutes (for add_step, update_step)"),
1581
+ messageType: z20.enum(["text", "image", "flex"]).optional().describe("Message type (for add_step, update_step)"),
1582
+ messageContent: z20.string().optional().describe("Message content (for add_step, update_step)"),
1583
+ conditionType: z20.string().nullable().optional().describe("Condition type (for add_step, update_step)"),
1584
+ conditionValue: z20.string().nullable().optional().describe("Condition value (for add_step, update_step)"),
1585
+ nextStepOnFalse: z20.number().nullable().optional().describe("Next step on false (for add_step, update_step)"),
1586
+ accountId: z20.string().optional().describe("LINE account ID for list (uses default if omitted)")
1587
+ },
1588
+ async ({ action, scenarioId, stepId, name, description, triggerType, triggerTagId, isActive, stepOrder, delayMinutes, messageType, messageContent, conditionType, conditionValue, nextStepOnFalse, accountId }) => {
1589
+ try {
1590
+ const client = getClient();
1591
+ if (action === "list") {
1592
+ const scenarios = await client.scenarios.list(accountId ? { accountId } : void 0);
1593
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, scenarios }, null, 2) }] };
1594
+ }
1595
+ if (!scenarioId) throw new Error("scenarioId is required for this action");
1596
+ if (action === "get") {
1597
+ const scenario = await client.scenarios.get(scenarioId);
1598
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, scenario }, null, 2) }] };
1599
+ }
1600
+ if (action === "update") {
1601
+ const input = {};
1602
+ if (name !== void 0) input.name = name;
1603
+ if (description !== void 0) input.description = description;
1604
+ if (triggerType !== void 0) input.triggerType = triggerType;
1605
+ if (triggerTagId !== void 0) input.triggerTagId = triggerTagId;
1606
+ if (isActive !== void 0) input.isActive = isActive;
1607
+ const scenario = await client.scenarios.update(scenarioId, input);
1608
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, scenario }, null, 2) }] };
1609
+ }
1610
+ if (action === "delete") {
1611
+ await client.scenarios.delete(scenarioId);
1612
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, deleted: scenarioId }, null, 2) }] };
1613
+ }
1614
+ if (action === "add_step") {
1615
+ if (stepOrder === void 0 || delayMinutes === void 0 || !messageType || !messageContent) {
1616
+ throw new Error("stepOrder, delayMinutes, messageType, messageContent are required for add_step");
1617
+ }
1618
+ const step = await client.scenarios.addStep(scenarioId, {
1619
+ stepOrder,
1620
+ delayMinutes,
1621
+ messageType,
1622
+ messageContent,
1623
+ conditionType: conditionType ?? null,
1624
+ conditionValue: conditionValue ?? null,
1625
+ nextStepOnFalse: nextStepOnFalse ?? null
1626
+ });
1627
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, step }, null, 2) }] };
1628
+ }
1629
+ if (action === "update_step") {
1630
+ if (!stepId) throw new Error("stepId is required for update_step");
1631
+ const input = {};
1632
+ if (stepOrder !== void 0) input.stepOrder = stepOrder;
1633
+ if (delayMinutes !== void 0) input.delayMinutes = delayMinutes;
1634
+ if (messageType !== void 0) input.messageType = messageType;
1635
+ if (messageContent !== void 0) input.messageContent = messageContent;
1636
+ if (conditionType !== void 0) input.conditionType = conditionType;
1637
+ if (conditionValue !== void 0) input.conditionValue = conditionValue;
1638
+ if (nextStepOnFalse !== void 0) input.nextStepOnFalse = nextStepOnFalse;
1639
+ const step = await client.scenarios.updateStep(scenarioId, stepId, input);
1640
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, step }, null, 2) }] };
1641
+ }
1642
+ if (action === "delete_step") {
1643
+ if (!stepId) throw new Error("stepId is required for delete_step");
1644
+ await client.scenarios.deleteStep(scenarioId, stepId);
1645
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, deleted: stepId }, null, 2) }] };
1646
+ }
1647
+ throw new Error(`Unknown action: ${action}`);
1648
+ } catch (err) {
1649
+ return { content: [{ type: "text", text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
1650
+ }
1651
+ }
1652
+ );
1653
+ }
1654
+
1655
+ // src/tools/manage-broadcasts.ts
1656
+ import { z as z21 } from "zod";
1657
+ function registerManageBroadcasts(server2) {
1658
+ server2.tool(
1659
+ "manage_broadcasts",
1660
+ "\u914D\u4FE1\u306E\u7BA1\u7406\u64CD\u4F5C\u3002list: \u4E00\u89A7\u3001get: \u8A73\u7D30\u3001create_draft: \u4E0B\u66F8\u304D\u4F5C\u6210\uFF08\u9001\u4FE1\u3057\u306A\u3044\uFF09\u3001update: \u66F4\u65B0\u3001send: \u9001\u4FE1\u3001send_to_segment: \u30BB\u30B0\u30E1\u30F3\u30C8\u914D\u4FE1\u3002",
1661
+ {
1662
+ action: z21.enum(["list", "get", "create_draft", "update", "send", "send_to_segment"]).describe("Action to perform"),
1663
+ broadcastId: z21.string().optional().describe("Broadcast ID (required for get, update, send, send_to_segment)"),
1664
+ title: z21.string().optional().describe("Broadcast title (for create_draft, update)"),
1665
+ messageType: z21.enum(["text", "image", "flex"]).optional().describe("Message type (for create_draft, update)"),
1666
+ messageContent: z21.string().optional().describe("Message content (for create_draft, update)"),
1667
+ targetType: z21.enum(["all", "tag"]).optional().describe("Target type (for create_draft, update)"),
1668
+ targetTagId: z21.string().nullable().optional().describe("Target tag ID (for create_draft, update)"),
1669
+ scheduledAt: z21.string().nullable().optional().describe("ISO 8601 datetime to schedule (for create_draft, update)"),
1670
+ segmentConditions: z21.string().optional().describe("JSON string of segment conditions: {operator: 'AND'|'OR', rules: [{type, value}]} (for send_to_segment)"),
1671
+ accountId: z21.string().optional().describe("LINE account ID (uses default if omitted)")
1672
+ },
1673
+ async ({ action, broadcastId, title, messageType, messageContent, targetType, targetTagId, scheduledAt, segmentConditions, accountId }) => {
1674
+ try {
1675
+ const client = getClient();
1676
+ if (action === "list") {
1677
+ const broadcasts = await client.broadcasts.list(accountId ? { accountId } : void 0);
1678
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, broadcasts }, null, 2) }] };
1679
+ }
1680
+ if (action === "create_draft") {
1681
+ if (!title || !messageType || !messageContent) {
1682
+ throw new Error("title, messageType, messageContent are required for create_draft");
1683
+ }
1684
+ const input = { title, messageType, messageContent, targetType: targetType ?? "all" };
1685
+ if (targetTagId) input.targetTagId = targetTagId;
1686
+ if (scheduledAt) input.scheduledAt = scheduledAt;
1687
+ if (accountId) input.lineAccountId = accountId;
1688
+ const broadcast = await client.broadcasts.create(input);
1689
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
1690
+ }
1691
+ if (!broadcastId) throw new Error("broadcastId is required for this action");
1692
+ if (action === "get") {
1693
+ const broadcast = await client.broadcasts.get(broadcastId);
1694
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
1695
+ }
1696
+ if (action === "update") {
1697
+ const input = {};
1698
+ if (title !== void 0) input.title = title;
1699
+ if (messageType !== void 0) input.messageType = messageType;
1700
+ if (messageContent !== void 0) input.messageContent = messageContent;
1701
+ if (targetType !== void 0) input.targetType = targetType;
1702
+ if (targetTagId !== void 0) input.targetTagId = targetTagId;
1703
+ if (scheduledAt !== void 0) input.scheduledAt = scheduledAt;
1704
+ const broadcast = await client.broadcasts.update(broadcastId, input);
1705
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
1706
+ }
1707
+ if (action === "send") {
1708
+ const broadcast = await client.broadcasts.send(broadcastId);
1709
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
1710
+ }
1711
+ if (action === "send_to_segment") {
1712
+ if (!segmentConditions) throw new Error("segmentConditions (JSON string) is required for send_to_segment");
1713
+ const conditions = JSON.parse(segmentConditions);
1714
+ const broadcast = await client.broadcasts.sendToSegment(broadcastId, conditions);
1715
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
1716
+ }
1717
+ throw new Error(`Unknown action: ${action}`);
1718
+ } catch (err) {
1719
+ return { content: [{ type: "text", text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
1720
+ }
1721
+ }
1722
+ );
1723
+ }
1724
+
1725
+ // src/tools/manage-rich-menus.ts
1726
+ import { z as z22 } from "zod";
1727
+ function registerManageRichMenus(server2) {
1728
+ server2.tool(
1729
+ "manage_rich_menus",
1730
+ "\u30EA\u30C3\u30C1\u30E1\u30CB\u30E5\u30FC\u306E\u7BA1\u7406\u64CD\u4F5C\u3002list: \u4E00\u89A7\u53D6\u5F97\u3001delete: \u524A\u9664\u3001set_default: \u30C7\u30D5\u30A9\u30EB\u30C8\u8A2D\u5B9A\u3002\u4F5C\u6210\u306F create_rich_menu \u30C4\u30FC\u30EB\u3092\u4F7F\u7528\u3002",
1731
+ {
1732
+ action: z22.enum(["list", "delete", "set_default"]).describe("Action to perform"),
1733
+ richMenuId: z22.string().optional().describe("Rich menu ID (required for delete, set_default)")
1734
+ },
1735
+ async ({ action, richMenuId }) => {
1736
+ try {
1737
+ const client = getClient();
1738
+ if (action === "list") {
1739
+ const menus = await client.richMenus.list();
1740
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, richMenus: menus }, null, 2) }] };
1741
+ }
1742
+ if (!richMenuId) throw new Error("richMenuId is required for this action");
1743
+ if (action === "delete") {
1744
+ await client.richMenus.delete(richMenuId);
1745
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, deleted: richMenuId }, null, 2) }] };
1746
+ }
1747
+ if (action === "set_default") {
1748
+ await client.richMenus.setDefault(richMenuId);
1749
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, defaultRichMenuId: richMenuId }, null, 2) }] };
1750
+ }
1751
+ throw new Error(`Unknown action: ${action}`);
1752
+ } catch (err) {
1753
+ return { content: [{ type: "text", text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
1754
+ }
1755
+ }
1756
+ );
1757
+ }
1758
+
1759
+ // src/tools/manage-forms.ts
1760
+ import { z as z23 } from "zod";
1761
+ function registerManageForms(server2) {
1762
+ server2.tool(
1763
+ "manage_forms",
1764
+ "\u30D5\u30A9\u30FC\u30E0\u306E\u7BA1\u7406\u64CD\u4F5C\u3002list: \u4E00\u89A7\u3001get: \u8A73\u7D30\u3001update: \u66F4\u65B0\u3001delete: \u524A\u9664\u3002\u4F5C\u6210\u306F create_form \u30C4\u30FC\u30EB\u3092\u4F7F\u7528\u3002",
1765
+ {
1766
+ action: z23.enum(["list", "get", "update", "delete"]).describe("Action to perform"),
1767
+ formId: z23.string().optional().describe("Form ID (required for get, update, delete)"),
1768
+ name: z23.string().optional().describe("Form name (for update)"),
1769
+ description: z23.string().nullable().optional().describe("Form description (for update)"),
1770
+ fields: z23.string().optional().describe("JSON string of form fields array (for update)"),
1771
+ onSubmitTagId: z23.string().nullable().optional().describe("Tag to add on submit (for update)"),
1772
+ onSubmitScenarioId: z23.string().nullable().optional().describe("Scenario to enroll on submit (for update)"),
1773
+ saveToMetadata: z23.boolean().optional().describe("Save responses to friend metadata (for update)"),
1774
+ isActive: z23.boolean().optional().describe("Active status (for update)")
1775
+ },
1776
+ async ({ action, formId, name, description, fields, onSubmitTagId, onSubmitScenarioId, saveToMetadata, isActive }) => {
1777
+ try {
1778
+ const client = getClient();
1779
+ if (action === "list") {
1780
+ const forms = await client.forms.list();
1781
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, forms }, null, 2) }] };
1782
+ }
1783
+ if (!formId) throw new Error("formId is required for this action");
1784
+ if (action === "get") {
1785
+ const form = await client.forms.get(formId);
1786
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, form }, null, 2) }] };
1787
+ }
1788
+ if (action === "update") {
1789
+ const input = {};
1790
+ if (name !== void 0) input.name = name;
1791
+ if (description !== void 0) input.description = description;
1792
+ if (fields !== void 0) input.fields = JSON.parse(fields);
1793
+ if (onSubmitTagId !== void 0) input.onSubmitTagId = onSubmitTagId;
1794
+ if (onSubmitScenarioId !== void 0) input.onSubmitScenarioId = onSubmitScenarioId;
1795
+ if (saveToMetadata !== void 0) input.saveToMetadata = saveToMetadata;
1796
+ if (isActive !== void 0) input.isActive = isActive;
1797
+ const form = await client.forms.update(formId, input);
1798
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, form }, null, 2) }] };
1799
+ }
1800
+ if (action === "delete") {
1801
+ await client.forms.delete(formId);
1802
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, deleted: formId }, null, 2) }] };
1803
+ }
1804
+ throw new Error(`Unknown action: ${action}`);
1805
+ } catch (err) {
1806
+ return { content: [{ type: "text", text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
1807
+ }
1808
+ }
1809
+ );
1810
+ }
1811
+
1812
+ // src/tools/manage-tracked-links.ts
1813
+ import { z as z24 } from "zod";
1814
+ function registerManageTrackedLinks(server2) {
1815
+ server2.tool(
1816
+ "manage_tracked_links",
1817
+ "\u30C8\u30E9\u30C3\u30AD\u30F3\u30B0\u30EA\u30F3\u30AF\u306E\u7BA1\u7406\u64CD\u4F5C\u3002list: \u4E00\u89A7\u3001delete: \u524A\u9664\u3002\u4F5C\u6210\u306F create_tracked_link \u30C4\u30FC\u30EB\u3092\u4F7F\u7528\u3002",
1818
+ {
1819
+ action: z24.enum(["list", "delete"]).describe("Action to perform"),
1820
+ linkId: z24.string().optional().describe("Tracked link ID (required for delete)")
1821
+ },
1822
+ async ({ action, linkId }) => {
1823
+ try {
1824
+ const client = getClient();
1825
+ if (action === "list") {
1826
+ const links = await client.trackedLinks.list();
1827
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, trackedLinks: links }, null, 2) }] };
1828
+ }
1829
+ if (action === "delete") {
1830
+ if (!linkId) throw new Error("linkId is required for delete");
1831
+ await client.trackedLinks.delete(linkId);
1832
+ return { content: [{ type: "text", text: JSON.stringify({ success: true, deleted: linkId }, null, 2) }] };
1833
+ }
1834
+ throw new Error(`Unknown action: ${action}`);
1835
+ } catch (err) {
1836
+ return { content: [{ type: "text", text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
1837
+ }
1838
+ }
1839
+ );
1840
+ }
1841
+
1491
1842
  // src/tools/index.ts
1492
1843
  function registerAllTools(server2) {
1493
1844
  registerSendMessage(server2);
@@ -1508,6 +1859,12 @@ function registerAllTools(server2) {
1508
1859
  registerGetConversionLogs(server2);
1509
1860
  registerManageStaff(server2);
1510
1861
  registerUploadImage(server2);
1862
+ registerManageFriends(server2);
1863
+ registerManageScenarios(server2);
1864
+ registerManageBroadcasts(server2);
1865
+ registerManageRichMenus(server2);
1866
+ registerManageForms(server2);
1867
+ registerManageTrackedLinks(server2);
1511
1868
  }
1512
1869
 
1513
1870
  // src/resources/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@line-harness/mcp-server",
3
- "version": "0.6.7",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "line-harness-mcp": "./dist/index.js"
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "@modelcontextprotocol/sdk": "^1.12.1",
11
11
  "zod": "^3.24.4",
12
- "@line-harness/sdk": "0.2.4"
12
+ "@line-harness/sdk": "0.2.5"
13
13
  },
14
14
  "devDependencies": {
15
15
  "tsup": "^8.4.0",
@@ -5,7 +5,7 @@ import { getClient } from "../client.js";
5
5
  export function registerCreateRichMenu(server: McpServer): void {
6
6
  server.tool(
7
7
  "create_rich_menu",
8
- "Create a LINE rich menu (the persistent menu at the bottom of the chat). Image must be uploaded separately via LINE Developers Console. This creates the menu structure and button areas.",
8
+ "Create a LINE rich menu with optional image upload. Provide imageData (base64) to attach the menu image in one step.",
9
9
  {
10
10
  name: z.string().describe("Rich menu name"),
11
11
  chatBarText: z
@@ -34,12 +34,20 @@ export function registerCreateRichMenu(server: McpServer): void {
34
34
  .describe(
35
35
  "JSON string of menu button areas. Format: [{ bounds: { x, y, width, height }, action: { type: 'uri'|'message'|'postback', uri?, text?, data? } }]",
36
36
  ),
37
+ imageData: z
38
+ .string()
39
+ .optional()
40
+ .describe("Base64-encoded image data for the rich menu (PNG or JPEG, 2500x1686 or 2500x843)"),
41
+ imageContentType: z
42
+ .enum(["image/png", "image/jpeg"])
43
+ .default("image/jpeg")
44
+ .describe("Image MIME type"),
37
45
  setAsDefault: z
38
46
  .boolean()
39
47
  .default(false)
40
48
  .describe("Set this as the default rich menu for all friends"),
41
49
  },
42
- async ({ name, chatBarText, size, selected, areas, setAsDefault }) => {
50
+ async ({ name, chatBarText, size, selected, areas, imageData, imageContentType, setAsDefault }) => {
43
51
  try {
44
52
  const client = getClient();
45
53
  const menu = await client.richMenus.create({
@@ -50,6 +58,10 @@ export function registerCreateRichMenu(server: McpServer): void {
50
58
  areas: JSON.parse(areas),
51
59
  });
52
60
 
61
+ if (imageData) {
62
+ await client.richMenus.uploadImage(menu.richMenuId, imageData, imageContentType);
63
+ }
64
+
53
65
  if (setAsDefault) {
54
66
  await client.richMenus.setDefault(menu.richMenuId);
55
67
  }
@@ -62,6 +74,7 @@ export function registerCreateRichMenu(server: McpServer): void {
62
74
  {
63
75
  success: true,
64
76
  richMenuId: menu.richMenuId,
77
+ imageUploaded: !!imageData,
65
78
  isDefault: setAsDefault,
66
79
  },
67
80
  null,
@@ -17,6 +17,12 @@ import { registerManageAdPlatforms } from "./manage-ad-platforms.js";
17
17
  import { registerGetConversionLogs } from "./get-conversion-logs.js";
18
18
  import { registerManageStaff } from "./manage-staff.js";
19
19
  import { registerUploadImage } from "./upload-image.js";
20
+ import { registerManageFriends } from "./manage-friends.js";
21
+ import { registerManageScenarios } from "./manage-scenarios.js";
22
+ import { registerManageBroadcasts } from "./manage-broadcasts.js";
23
+ import { registerManageRichMenus } from "./manage-rich-menus.js";
24
+ import { registerManageForms } from "./manage-forms.js";
25
+ import { registerManageTrackedLinks } from "./manage-tracked-links.js";
20
26
 
21
27
  export function registerAllTools(server: McpServer): void {
22
28
  registerSendMessage(server);
@@ -37,4 +43,10 @@ export function registerAllTools(server: McpServer): void {
37
43
  registerGetConversionLogs(server);
38
44
  registerManageStaff(server);
39
45
  registerUploadImage(server);
46
+ registerManageFriends(server);
47
+ registerManageScenarios(server);
48
+ registerManageBroadcasts(server);
49
+ registerManageRichMenus(server);
50
+ registerManageForms(server);
51
+ registerManageTrackedLinks(server);
40
52
  }
@@ -0,0 +1,81 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { getClient } from "../client.js";
4
+
5
+ export function registerManageBroadcasts(server: McpServer): void {
6
+ server.tool(
7
+ "manage_broadcasts",
8
+ "配信の管理操作。list: 一覧、get: 詳細、create_draft: 下書き作成(送信しない)、update: 更新、send: 送信、send_to_segment: セグメント配信。",
9
+ {
10
+ action: z
11
+ .enum(["list", "get", "create_draft", "update", "send", "send_to_segment"])
12
+ .describe("Action to perform"),
13
+ broadcastId: z.string().optional().describe("Broadcast ID (required for get, update, send, send_to_segment)"),
14
+ title: z.string().optional().describe("Broadcast title (for create_draft, update)"),
15
+ messageType: z.enum(["text", "image", "flex"]).optional().describe("Message type (for create_draft, update)"),
16
+ messageContent: z.string().optional().describe("Message content (for create_draft, update)"),
17
+ targetType: z.enum(["all", "tag"]).optional().describe("Target type (for create_draft, update)"),
18
+ targetTagId: z.string().nullable().optional().describe("Target tag ID (for create_draft, update)"),
19
+ scheduledAt: z.string().nullable().optional().describe("ISO 8601 datetime to schedule (for create_draft, update)"),
20
+ segmentConditions: z.string().optional().describe("JSON string of segment conditions: {operator: 'AND'|'OR', rules: [{type, value}]} (for send_to_segment)"),
21
+ accountId: z.string().optional().describe("LINE account ID (uses default if omitted)"),
22
+ },
23
+ async ({ action, broadcastId, title, messageType, messageContent, targetType, targetTagId, scheduledAt, segmentConditions, accountId }) => {
24
+ try {
25
+ const client = getClient();
26
+
27
+ if (action === "list") {
28
+ const broadcasts = await client.broadcasts.list(accountId ? { accountId } : undefined);
29
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, broadcasts }, null, 2) }] };
30
+ }
31
+
32
+ if (action === "create_draft") {
33
+ if (!title || !messageType || !messageContent) {
34
+ throw new Error("title, messageType, messageContent are required for create_draft");
35
+ }
36
+ const input: Record<string, unknown> = { title, messageType, messageContent, targetType: targetType ?? "all" };
37
+ if (targetTagId) input.targetTagId = targetTagId;
38
+ if (scheduledAt) input.scheduledAt = scheduledAt;
39
+ if (accountId) input.lineAccountId = accountId;
40
+ const broadcast = await client.broadcasts.create(input as never);
41
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
42
+ }
43
+
44
+ if (!broadcastId) throw new Error("broadcastId is required for this action");
45
+
46
+ if (action === "get") {
47
+ const broadcast = await client.broadcasts.get(broadcastId);
48
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
49
+ }
50
+
51
+ if (action === "update") {
52
+ const input: Record<string, unknown> = {};
53
+ if (title !== undefined) input.title = title;
54
+ if (messageType !== undefined) input.messageType = messageType;
55
+ if (messageContent !== undefined) input.messageContent = messageContent;
56
+ if (targetType !== undefined) input.targetType = targetType;
57
+ if (targetTagId !== undefined) input.targetTagId = targetTagId;
58
+ if (scheduledAt !== undefined) input.scheduledAt = scheduledAt;
59
+ const broadcast = await client.broadcasts.update(broadcastId, input);
60
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
61
+ }
62
+
63
+ if (action === "send") {
64
+ const broadcast = await client.broadcasts.send(broadcastId);
65
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
66
+ }
67
+
68
+ if (action === "send_to_segment") {
69
+ if (!segmentConditions) throw new Error("segmentConditions (JSON string) is required for send_to_segment");
70
+ const conditions = JSON.parse(segmentConditions);
71
+ const broadcast = await client.broadcasts.sendToSegment(broadcastId, conditions);
72
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, broadcast }, null, 2) }] };
73
+ }
74
+
75
+ throw new Error(`Unknown action: ${action}`);
76
+ } catch (err) {
77
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
78
+ }
79
+ },
80
+ );
81
+ }
@@ -0,0 +1,54 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { getClient } from "../client.js";
4
+
5
+ export function registerManageForms(server: McpServer): void {
6
+ server.tool(
7
+ "manage_forms",
8
+ "フォームの管理操作。list: 一覧、get: 詳細、update: 更新、delete: 削除。作成は create_form ツールを使用。",
9
+ {
10
+ action: z.enum(["list", "get", "update", "delete"]).describe("Action to perform"),
11
+ formId: z.string().optional().describe("Form ID (required for get, update, delete)"),
12
+ name: z.string().optional().describe("Form name (for update)"),
13
+ description: z.string().nullable().optional().describe("Form description (for update)"),
14
+ fields: z.string().optional().describe("JSON string of form fields array (for update)"),
15
+ onSubmitTagId: z.string().nullable().optional().describe("Tag to add on submit (for update)"),
16
+ onSubmitScenarioId: z.string().nullable().optional().describe("Scenario to enroll on submit (for update)"),
17
+ saveToMetadata: z.boolean().optional().describe("Save responses to friend metadata (for update)"),
18
+ isActive: z.boolean().optional().describe("Active status (for update)"),
19
+ },
20
+ async ({ action, formId, name, description, fields, onSubmitTagId, onSubmitScenarioId, saveToMetadata, isActive }) => {
21
+ try {
22
+ const client = getClient();
23
+ if (action === "list") {
24
+ const forms = await client.forms.list();
25
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, forms }, null, 2) }] };
26
+ }
27
+ if (!formId) throw new Error("formId is required for this action");
28
+ if (action === "get") {
29
+ const form = await client.forms.get(formId);
30
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, form }, null, 2) }] };
31
+ }
32
+ if (action === "update") {
33
+ const input: Record<string, unknown> = {};
34
+ if (name !== undefined) input.name = name;
35
+ if (description !== undefined) input.description = description;
36
+ if (fields !== undefined) input.fields = JSON.parse(fields);
37
+ if (onSubmitTagId !== undefined) input.onSubmitTagId = onSubmitTagId;
38
+ if (onSubmitScenarioId !== undefined) input.onSubmitScenarioId = onSubmitScenarioId;
39
+ if (saveToMetadata !== undefined) input.saveToMetadata = saveToMetadata;
40
+ if (isActive !== undefined) input.isActive = isActive;
41
+ const form = await client.forms.update(formId, input);
42
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, form }, null, 2) }] };
43
+ }
44
+ if (action === "delete") {
45
+ await client.forms.delete(formId);
46
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, deleted: formId }, null, 2) }] };
47
+ }
48
+ throw new Error(`Unknown action: ${action}`);
49
+ } catch (err) {
50
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
51
+ }
52
+ },
53
+ );
54
+ }
@@ -0,0 +1,72 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { getClient } from "../client.js";
4
+
5
+ export function registerManageFriends(server: McpServer): void {
6
+ server.tool(
7
+ "manage_friends",
8
+ "友だちの管理操作。count: 友だち数取得、set_metadata: メタデータ更新、set_rich_menu: リッチメニュー割当、remove_rich_menu: リッチメニュー解除。",
9
+ {
10
+ action: z
11
+ .enum(["count", "set_metadata", "set_rich_menu", "remove_rich_menu"])
12
+ .describe("Action to perform"),
13
+ friendId: z
14
+ .string()
15
+ .optional()
16
+ .describe("Friend ID (required for set_metadata, set_rich_menu, remove_rich_menu)"),
17
+ metadata: z
18
+ .string()
19
+ .optional()
20
+ .describe("JSON string of metadata fields to set (for 'set_metadata')"),
21
+ richMenuId: z
22
+ .string()
23
+ .optional()
24
+ .describe("Rich menu ID to assign (for 'set_rich_menu')"),
25
+ },
26
+ async ({ action, friendId, metadata, richMenuId }) => {
27
+ try {
28
+ const client = getClient();
29
+
30
+ if (action === "count") {
31
+ const count = await client.friends.count();
32
+ return {
33
+ content: [{ type: "text" as const, text: JSON.stringify({ success: true, count }, null, 2) }],
34
+ };
35
+ }
36
+
37
+ if (!friendId) throw new Error("friendId is required for this action");
38
+
39
+ if (action === "set_metadata") {
40
+ if (!metadata) throw new Error("metadata (JSON string) is required for set_metadata");
41
+ const fields = JSON.parse(metadata) as Record<string, unknown>;
42
+ const friend = await client.friends.setMetadata(friendId, fields);
43
+ return {
44
+ content: [{ type: "text" as const, text: JSON.stringify({ success: true, friend }, null, 2) }],
45
+ };
46
+ }
47
+
48
+ if (action === "set_rich_menu") {
49
+ if (!richMenuId) throw new Error("richMenuId is required for set_rich_menu");
50
+ await client.friends.setRichMenu(friendId, richMenuId);
51
+ return {
52
+ content: [{ type: "text" as const, text: JSON.stringify({ success: true, friendId, richMenuId }, null, 2) }],
53
+ };
54
+ }
55
+
56
+ if (action === "remove_rich_menu") {
57
+ await client.friends.removeRichMenu(friendId);
58
+ return {
59
+ content: [{ type: "text" as const, text: JSON.stringify({ success: true, friendId, removed: true }, null, 2) }],
60
+ };
61
+ }
62
+
63
+ throw new Error(`Unknown action: ${action}`);
64
+ } catch (err) {
65
+ return {
66
+ content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: String(err) }) }],
67
+ isError: true,
68
+ };
69
+ }
70
+ },
71
+ );
72
+ }
@@ -0,0 +1,35 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { getClient } from "../client.js";
4
+
5
+ export function registerManageRichMenus(server: McpServer): void {
6
+ server.tool(
7
+ "manage_rich_menus",
8
+ "リッチメニューの管理操作。list: 一覧取得、delete: 削除、set_default: デフォルト設定。作成は create_rich_menu ツールを使用。",
9
+ {
10
+ action: z.enum(["list", "delete", "set_default"]).describe("Action to perform"),
11
+ richMenuId: z.string().optional().describe("Rich menu ID (required for delete, set_default)"),
12
+ },
13
+ async ({ action, richMenuId }) => {
14
+ try {
15
+ const client = getClient();
16
+ if (action === "list") {
17
+ const menus = await client.richMenus.list();
18
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, richMenus: menus }, null, 2) }] };
19
+ }
20
+ if (!richMenuId) throw new Error("richMenuId is required for this action");
21
+ if (action === "delete") {
22
+ await client.richMenus.delete(richMenuId);
23
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, deleted: richMenuId }, null, 2) }] };
24
+ }
25
+ if (action === "set_default") {
26
+ await client.richMenus.setDefault(richMenuId);
27
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, defaultRichMenuId: richMenuId }, null, 2) }] };
28
+ }
29
+ throw new Error(`Unknown action: ${action}`);
30
+ } catch (err) {
31
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
32
+ }
33
+ },
34
+ );
35
+ }
@@ -0,0 +1,98 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { getClient } from "../client.js";
4
+
5
+ export function registerManageScenarios(server: McpServer): void {
6
+ server.tool(
7
+ "manage_scenarios",
8
+ "シナリオの管理操作。list: 一覧、get: 詳細(ステップ含む)、update: 更新、delete: 削除、add_step: ステップ追加、update_step: ステップ更新、delete_step: ステップ削除。",
9
+ {
10
+ action: z
11
+ .enum(["list", "get", "update", "delete", "add_step", "update_step", "delete_step"])
12
+ .describe("Action to perform"),
13
+ scenarioId: z.string().optional().describe("Scenario ID (required for get, update, delete, add_step, update_step, delete_step)"),
14
+ stepId: z.string().optional().describe("Step ID (required for update_step, delete_step)"),
15
+ name: z.string().optional().describe("Scenario name (for update)"),
16
+ description: z.string().nullable().optional().describe("Scenario description (for update)"),
17
+ triggerType: z.enum(["friend_add", "tag_added", "manual"]).optional().describe("Trigger type (for update)"),
18
+ triggerTagId: z.string().nullable().optional().describe("Trigger tag ID (for update)"),
19
+ isActive: z.boolean().optional().describe("Active status (for update)"),
20
+ stepOrder: z.number().optional().describe("Step order number (for add_step, update_step)"),
21
+ delayMinutes: z.number().optional().describe("Delay in minutes (for add_step, update_step)"),
22
+ messageType: z.enum(["text", "image", "flex"]).optional().describe("Message type (for add_step, update_step)"),
23
+ messageContent: z.string().optional().describe("Message content (for add_step, update_step)"),
24
+ conditionType: z.string().nullable().optional().describe("Condition type (for add_step, update_step)"),
25
+ conditionValue: z.string().nullable().optional().describe("Condition value (for add_step, update_step)"),
26
+ nextStepOnFalse: z.number().nullable().optional().describe("Next step on false (for add_step, update_step)"),
27
+ accountId: z.string().optional().describe("LINE account ID for list (uses default if omitted)"),
28
+ },
29
+ async ({ action, scenarioId, stepId, name, description, triggerType, triggerTagId, isActive, stepOrder, delayMinutes, messageType, messageContent, conditionType, conditionValue, nextStepOnFalse, accountId }) => {
30
+ try {
31
+ const client = getClient();
32
+
33
+ if (action === "list") {
34
+ const scenarios = await client.scenarios.list(accountId ? { accountId } : undefined);
35
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, scenarios }, null, 2) }] };
36
+ }
37
+
38
+ if (!scenarioId) throw new Error("scenarioId is required for this action");
39
+
40
+ if (action === "get") {
41
+ const scenario = await client.scenarios.get(scenarioId);
42
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, scenario }, null, 2) }] };
43
+ }
44
+
45
+ if (action === "update") {
46
+ const input: Record<string, unknown> = {};
47
+ if (name !== undefined) input.name = name;
48
+ if (description !== undefined) input.description = description;
49
+ if (triggerType !== undefined) input.triggerType = triggerType;
50
+ if (triggerTagId !== undefined) input.triggerTagId = triggerTagId;
51
+ if (isActive !== undefined) input.isActive = isActive;
52
+ const scenario = await client.scenarios.update(scenarioId, input);
53
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, scenario }, null, 2) }] };
54
+ }
55
+
56
+ if (action === "delete") {
57
+ await client.scenarios.delete(scenarioId);
58
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, deleted: scenarioId }, null, 2) }] };
59
+ }
60
+
61
+ if (action === "add_step") {
62
+ if (stepOrder === undefined || delayMinutes === undefined || !messageType || !messageContent) {
63
+ throw new Error("stepOrder, delayMinutes, messageType, messageContent are required for add_step");
64
+ }
65
+ const step = await client.scenarios.addStep(scenarioId, {
66
+ stepOrder, delayMinutes, messageType, messageContent,
67
+ conditionType: conditionType ?? null, conditionValue: conditionValue ?? null, nextStepOnFalse: nextStepOnFalse ?? null,
68
+ });
69
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, step }, null, 2) }] };
70
+ }
71
+
72
+ if (action === "update_step") {
73
+ if (!stepId) throw new Error("stepId is required for update_step");
74
+ const input: Record<string, unknown> = {};
75
+ if (stepOrder !== undefined) input.stepOrder = stepOrder;
76
+ if (delayMinutes !== undefined) input.delayMinutes = delayMinutes;
77
+ if (messageType !== undefined) input.messageType = messageType;
78
+ if (messageContent !== undefined) input.messageContent = messageContent;
79
+ if (conditionType !== undefined) input.conditionType = conditionType;
80
+ if (conditionValue !== undefined) input.conditionValue = conditionValue;
81
+ if (nextStepOnFalse !== undefined) input.nextStepOnFalse = nextStepOnFalse;
82
+ const step = await client.scenarios.updateStep(scenarioId, stepId, input);
83
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, step }, null, 2) }] };
84
+ }
85
+
86
+ if (action === "delete_step") {
87
+ if (!stepId) throw new Error("stepId is required for delete_step");
88
+ await client.scenarios.deleteStep(scenarioId, stepId);
89
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, deleted: stepId }, null, 2) }] };
90
+ }
91
+
92
+ throw new Error(`Unknown action: ${action}`);
93
+ } catch (err) {
94
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
95
+ }
96
+ },
97
+ );
98
+ }
@@ -5,9 +5,9 @@ import { getClient } from "../client.js";
5
5
  export function registerManageTags(server: McpServer): void {
6
6
  server.tool(
7
7
  "manage_tags",
8
- "Create tags, add tags to friends, or remove tags from friends. Supports batch operations on multiple friends.",
8
+ "List, create, or delete tags, and add/remove tags to/from friends. Supports batch operations on multiple friends.",
9
9
  {
10
- action: z.enum(["create", "add", "remove"]).describe("Action to perform"),
10
+ action: z.enum(["list", "create", "delete", "add", "remove"]).describe("Action to perform"),
11
11
  tagName: z
12
12
  .string()
13
13
  .optional()
@@ -31,6 +31,21 @@ export function registerManageTags(server: McpServer): void {
31
31
  try {
32
32
  const client = getClient();
33
33
 
34
+ if (action === "list") {
35
+ const tags = await client.tags.list();
36
+ return {
37
+ content: [{ type: "text" as const, text: JSON.stringify({ success: true, tags }, null, 2) }],
38
+ };
39
+ }
40
+
41
+ if (action === "delete") {
42
+ if (!tagId) throw new Error("tagId is required for delete action");
43
+ await client.tags.delete(tagId);
44
+ return {
45
+ content: [{ type: "text" as const, text: JSON.stringify({ success: true, deleted: tagId }, null, 2) }],
46
+ };
47
+ }
48
+
34
49
  if (action === "create") {
35
50
  if (!tagName) throw new Error("tagName is required for create action");
36
51
  const tag = await client.tags.create({
@@ -0,0 +1,31 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { z } from "zod";
3
+ import { getClient } from "../client.js";
4
+
5
+ export function registerManageTrackedLinks(server: McpServer): void {
6
+ server.tool(
7
+ "manage_tracked_links",
8
+ "トラッキングリンクの管理操作。list: 一覧、delete: 削除。作成は create_tracked_link ツールを使用。",
9
+ {
10
+ action: z.enum(["list", "delete"]).describe("Action to perform"),
11
+ linkId: z.string().optional().describe("Tracked link ID (required for delete)"),
12
+ },
13
+ async ({ action, linkId }) => {
14
+ try {
15
+ const client = getClient();
16
+ if (action === "list") {
17
+ const links = await client.trackedLinks.list();
18
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, trackedLinks: links }, null, 2) }] };
19
+ }
20
+ if (action === "delete") {
21
+ if (!linkId) throw new Error("linkId is required for delete");
22
+ await client.trackedLinks.delete(linkId);
23
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, deleted: linkId }, null, 2) }] };
24
+ }
25
+ throw new Error(`Unknown action: ${action}`);
26
+ } catch (err) {
27
+ return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, error: String(err) }) }], isError: true };
28
+ }
29
+ },
30
+ );
31
+ }