Skip to main content
Glama

WeChat Mini Program Dev MCP

by yfmeii
element.ts15.1 kB
import { UserError } from "fastmcp"; import { z } from "zod"; import type { WeappAutomatorManager } from "../weappClient.js"; import { AnyTool, ToolContext, connectionContainerSchema, formatJson, resolveElement, toSerializableValue, toTextResult, waitOnPage, } from "./common.js"; const tapElementParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), waitMs: z.coerce.number().int().nonnegative().optional(), }); const inputTextParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), value: z.union([z.string(), z.coerce.number()]), }); const callElementMethodParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), method: z.string().trim().min(1), args: z.array(z.unknown()).optional(), }); const getElementDataParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), path: z.string().trim().min(1).optional(), }); const setElementDataParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), data: z.record(z.unknown()), }); const getInnerElementParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), targetSelector: z.string().trim().min(1), }); const getInnerElementsParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), targetSelector: z.string().trim().min(1), }); const getElementSizeParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), }); const getElementWxmlParameters = connectionContainerSchema.extend({ selector: z.string().trim().min(1), innerSelector: z.string().trim().min(1).optional(), outer: z.boolean().optional().default(false), }); export function createElementTools( manager: WeappAutomatorManager ): AnyTool[] { return [ createTapElementTool(manager), createInputTextTool(manager), createCallElementMethodTool(manager), createGetElementDataTool(manager), createSetElementDataTool(manager), createGetInnerElementTool(manager), createGetInnerElementsTool(manager), createGetElementSizeTool(manager), createGetElementWxmlTool(manager), ]; } function createTapElementTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_tap", description: "通过 CSS 选择器模拟点击 WXML 元素。如需点击自定义组件内部的元素,请使用 innerSelector 参数:selector 设为组件 ID 选择器(如 #my-component),innerSelector 设为组件内部元素的选择器。", parameters: tapElementParameters, execute: async (rawArgs, context: ToolContext) => { const args = tapElementParameters.parse(rawArgs ?? {}); const waitMs = args.waitMs; return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); await element.tap(); await waitOnPage(page, waitMs); return toTextResult( `已点击元素 "${args.selector}"${args.innerSelector ? ` -> "${args.innerSelector}"` : ""}${waitMs ? ` 并等待 ${waitMs}ms` : ""}。` ); } ); }, }; } function createInputTextTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_input", description: "向指定元素输入文本。如需向自定义组件内部的元素输入,请使用 innerSelector 参数:selector 设为组件 ID 选择器(如 #my-component),innerSelector 设为组件内部元素的选择器。", parameters: inputTextParameters, execute: async (rawArgs, context: ToolContext) => { const args = inputTextParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); await element.input(args.value); return toTextResult( `已向元素 "${args.selector}"${args.innerSelector ? ` -> "${args.innerSelector}"` : ""} 输入值 "${args.value}"。` ); } ); }, }; } function createCallElementMethodTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_callMethod", description: "调用组件实例指定方法,仅自定义组件可以使用。需要 automator 0.6.0 和基础库 2.9.0 及以上版本。使用 ID 选择器(如 #my-component)定位自定义组件。", parameters: callElementMethodParameters, execute: async (rawArgs, context: ToolContext) => { const args = callElementMethodParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); const callArgs = args.args ?? []; const result = await element.callMethod(args.method, ...callArgs); return toTextResult( formatJson({ selector: args.selector, innerSelector: args.innerSelector ?? null, method: args.method, arguments: callArgs, result: toSerializableValue(result), }) ); } ); }, }; } function createGetElementDataTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_getData", description: "获取组件实例渲染数据,仅自定义组件可以使用。需要 automator 0.6.0 和基础库 2.9.0 及以上版本。使用 ID 选择器(如 #my-component)定位自定义组件。", parameters: getElementDataParameters, execute: async (rawArgs, context: ToolContext) => { const args = getElementDataParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); const data = await element.data(args.path); return toTextResult( formatJson({ selector: args.selector, innerSelector: args.innerSelector ?? null, path: args.path ?? null, data: toSerializableValue(data), }) ); } ); }, }; } function createSetElementDataTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_setData", description: "设置组件实例渲染数据,仅自定义组件可以使用。需要 automator 0.6.0 和基础库 2.9.0 及以上版本。使用 ID 选择器(如 #my-component)定位自定义组件。", parameters: setElementDataParameters, execute: async (rawArgs, context: ToolContext) => { const args = setElementDataParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); await element.setData(args.data); const dataKeys = Object.keys(args.data ?? {}); return toTextResult( `已更新组件数据键: ${dataKeys.length ? dataKeys.join(", ") : "(无)"}。` ); } ); }, }; } function createGetInnerElementTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_getInnerElement", description: "在元素范围内获取元素,相当于 element.$(selector)。重要:操作自定义组件内部元素时,必须先通过 ID 选择器(如 #my-component)定位自定义组件,然后使用此工具获取组件内部的元素。", parameters: getInnerElementParameters, execute: async (rawArgs, context: ToolContext) => { const args = getInnerElementParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); if (typeof element.$ !== "function") { throw new UserError( `元素 "${args.selector}" 不支持查询内部元素。` ); } const innerElement = await element.$(args.targetSelector); if (!innerElement) { throw new UserError( `在元素 "${args.selector}" 内未找到选择器 "${args.targetSelector}" 对应的元素。` ); } const tagName = innerElement.tagName || null; const text = typeof innerElement.text === "function" ? await innerElement.text().catch(() => null) : null; const outerWxml = typeof innerElement.outerWxml === "function" ? await innerElement.outerWxml().catch(() => null) : null; return toTextResult( formatJson({ parentSelector: args.selector, parentInnerSelector: args.innerSelector ?? null, targetSelector: args.targetSelector, tagName: toSerializableValue(tagName), text: toSerializableValue(text), outerWxml: toSerializableValue(outerWxml), }) ); } ); }, }; } function createGetInnerElementsTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_getInnerElements", description: "在元素范围内获取元素数组,相当于 element.$$(selector)。重要:操作自定义组件内部元素时,必须先通过 ID 选择器(如 #my-component)定位自定义组件,然后使用此工具获取组件内部的元素数组。", parameters: getInnerElementsParameters, execute: async (rawArgs, context: ToolContext) => { const args = getInnerElementsParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); if (typeof element.$$ !== "function") { throw new UserError( `元素 "${args.selector}" 不支持查询内部元素数组。` ); } const innerElements = await element.$$(args.targetSelector); if (!Array.isArray(innerElements)) { throw new UserError( `在元素 "${args.selector}" 内查询选择器 "${args.targetSelector}" 失败。` ); } const elementsInfo = await Promise.all( innerElements.map(async (el, index) => { const tagName = el.tagName || null; const text = typeof el.text === "function" ? await el.text().catch(() => null) : null; return { index, tagName: toSerializableValue(tagName), text: toSerializableValue(text), }; }) ); return toTextResult( formatJson({ parentSelector: args.selector, parentInnerSelector: args.innerSelector ?? null, targetSelector: args.targetSelector, count: innerElements.length, elements: elementsInfo, }) ); } ); }, }; } function createGetElementSizeTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_getSize", description: "获取元素大小(宽度和高度)。如需获取自定义组件内部元素的大小,请使用 innerSelector 参数:selector 设为组件 ID 选择器(如 #my-component),innerSelector 设为组件内部元素的选择器。", parameters: getElementSizeParameters, execute: async (rawArgs, context: ToolContext) => { const args = getElementSizeParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); if (typeof element.size !== "function") { throw new UserError( `元素 "${args.selector}" 不支持获取大小。` ); } const size = await element.size(); return toTextResult( formatJson({ selector: args.selector, innerSelector: args.innerSelector ?? null, width: toSerializableValue(size.width), height: toSerializableValue(size.height), }) ); } ); }, }; } function createGetElementWxmlTool(manager: WeappAutomatorManager): AnyTool { return { name: "element_getWxml", description: "获取元素 WXML。默认获取内部 WXML(element.wxml()),设置 outer 为 true 可获取包含元素本身的 WXML(element.outerWxml())。如需获取自定义组件内部元素的 WXML,请使用 innerSelector 参数:selector 设为组件 ID 选择器(如 #my-component),innerSelector 设为组件内部元素的选择器。", parameters: getElementWxmlParameters, execute: async (rawArgs, context: ToolContext) => { const args = getElementWxmlParameters.parse(rawArgs ?? {}); return manager.withPage( context.log, { overrides: args.connection }, async (page) => { const element = await resolveElement( page, args.selector, args.innerSelector ); const methodName = args.outer ? "outerWxml" : "wxml"; if (typeof element[methodName] !== "function") { throw new UserError( `元素 "${args.selector}" 不支持获取 ${methodName}。` ); } const wxml = await element[methodName](); return toTextResult( formatJson({ selector: args.selector, innerSelector: args.innerSelector ?? null, type: args.outer ? "outerWxml" : "wxml", wxml: toSerializableValue(wxml), }) ); } ); }, }; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/yfmeii/weapp-dev-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server