เครื่องมือ JavaScript แบบ Agentic

ฟีเจอร์บนเว็บ: document.modelContext

Imperative API ใช้ document.modelContext.registerTool() เพื่อกำหนดเครื่องมือ JavaScript แบบเป็นโปรแกรม ซึ่งเหมาะสําหรับแอปพลิเคชันหน้าเว็บเดียว (SPA) ที่ต้องเพิ่มหรือนําเครื่องมือออกตามเส้นทางปัจจุบันหรือสถานะของผู้ใช้

WebMCP ทำงานบนสถาปัตยกรรมผู้ผลิต/ผู้บริโภค ดังนี้

  • หน้าเว็บเป็นผู้สร้างเครื่องมือ: คุณกำหนดและลงทะเบียนเครื่องมือโดยใช้ document.modelContext.registerTool() หน้าเว็บต้องติดตั้งใช้งานเพียงเท่านี้ คุณไม่จำเป็นต้องเรียกใช้ getTools() หรือ executeTool() ในหน้าเว็บเพื่อแสดงเครื่องมือต่อ Agent
  • ตัวแทนเบราว์เซอร์และส่วนขยายเป็นผู้ใช้หลัก: ตัวแทนที่เบราว์เซอร์จัดหาให้หรือผ่านเบราว์เซอร์ ซึ่งรวมถึงตัวแทนในตัวของเบราว์เซอร์, ส่วนขยายเบราว์เซอร์, เครื่องมือสำหรับนักพัฒนาเว็บ และผู้ช่วยระดับระบบ จะค้นพบและเรียกใช้เครื่องมือที่ลงทะเบียนไว้นอกแบนด์ผ่านกลไกแพลตฟอร์มภายใน
  • เอเจนต์ในหน้าเว็บและโปรแกรมทดสอบอัตโนมัติ: getTools() และ executeTool() มีไว้สำหรับผู้ช่วย JavaScript ในหน้าเว็บโดยเฉพาะ (เช่น วิดเจ็ตแชทที่ฝังหรือ <iframe>) และชุดโปรแกรมทดสอบอัตโนมัติที่ต้องค้นหาและเรียกใช้เครื่องมือโดยตรงภายในบริบทของหน้าเว็บ

การลงทะเบียนและวงจร

ระบบจะลงทะเบียนเครื่องมือโดยการส่งออบเจ็กต์คำจำกัดความของเครื่องมือและออบเจ็กต์ตัวเลือกที่ไม่บังคับ (ModelContextRegisterToolOptions) ซึ่งรองรับสมาชิกที่ไม่บังคับ 2 รายการ ได้แก่

  • signal: AbortSignal ที่ใช้เพื่อยกเลิกการลงทะเบียนเครื่องมือเมื่อไม่จำเป็นต้องใช้แล้ว
  • exposedTo: อาร์เรย์ของสตริงต้นทางที่ควบคุมว่าเอกสารใดในโครงสร้างเอกสารที่ได้รับอนุญาตให้ค้นพบและเรียกใช้เครื่องมือข้ามขอบเขตเฟรม

การจัดการวงจรด้วย AbortController

WebMCP ไม่มีเมธอด unregisterTool() หากต้องการยกเลิกการลงทะเบียนเครื่องมือ คุณต้องส่ง AbortSignal ระหว่างการลงทะเบียนและยกเลิกสัญญาณดังกล่าวเมื่อไม่จำเป็นต้องใช้เครื่องมืออีกต่อไป

const controller = new AbortController();

await document.modelContext.registerTool({
  name: "get_user_preferences",
  description: "Retrieves the user's saved preferences.",
  inputSchema: { type: "object", properties: {} },
  execute() {
    const prefs = localStorage.getItem("user_prefs");
    return prefs ? JSON.parse(prefs) : { theme: "light" };
  },
  annotations: { readOnlyHint: true }
}, { signal: controller.signal });

// To unregister the tool (e.g., on component unmount):
controller.abort();

การยกเลิกสัญญาณการลงทะเบียนจะยกเลิกการลงทะเบียนเครื่องมือเท่านั้น แต่จะไม่ยกเลิกการดำเนินการที่ทำงานอยู่แล้ว เครื่องมือที่เริ่มงานที่ใช้เวลานานต้องเป็นไปตามสัญญาณต่อการดำเนินการที่อธิบายไว้ด้านล่างด้วย ไม่เช่นนั้นการยกเลิกการลงทะเบียนจะทำให้คำขอที่ไม่มีเจ้าของและการอัปเดต UI ที่ล้าสมัยยังคงอยู่

การควบคุมการแสดงเครื่องมือแบบข้ามต้นทางด้วย exposedTo

โดยค่าเริ่มต้น เครื่องมือที่ลงทะเบียนจะเป็นแบบส่วนตัวสำหรับเอกสารที่ลงทะเบียนและเฟรมต้นทางเดียวกัน เมื่อแอปพลิเคชันของคุณฝังอยู่ใน <iframe> (หรือฝังวิดเจ็ตของบุคคลที่สาม) และคุณต้องการเฟรมระดับบนหรือระดับเดียวกันเพื่อค้นหาและเรียกใช้เครื่องมือ ให้ระบุต้นทางที่อนุญาตโดยใช้ exposedTo

const controller = new AbortController();

await document.modelContext.registerTool({
  name: "get_cart_items",
  description: "Retrieves items currently in the user's shopping cart.",
  inputSchema: { type: "object", properties: {} },
  async execute(input, { signal }) {
    return await cartService.getItems({ signal });
  },
  annotations: { readOnlyHint: true },
}, {
  signal: controller.signal,
  exposedTo: ["https://checkout.example.com"],
});
  • การแฮนด์เชคแบบเลือกใช้ร่วมกัน: การค้นพบเครื่องมือแบบข้ามต้นทางเป็นการเลือกใช้แบบ 2 ทาง เฟรมของผู้ให้บริการเครื่องมือจะให้สิทธิ์ต้นทางของผู้บริโภคผ่าน exposedTo และเฟรมของผู้บริโภคต้องขอต้นทางของผู้ให้บริการนั้นอย่างชัดแจ้งผ่าน getTools({ fromOrigins: [...] }) หากไม่มี fromOrigins getTools() จะค้นหาเฉพาะเอกสารที่มีต้นทางเดียวกัน
  • การจับคู่ต้นทาง: ต้นทางของเอกสารการจดทะเบียนจะมีสิทธิ์เข้าถึงเสมอ ต้นทางที่ระบุใน exposedTo จะได้รับสิทธิ์ในการค้นหาเครื่องมือผ่าน document.modelContext.getTools({ fromOrigins: [...] }) และเรียกใช้ผ่าน document.modelContext.executeTool()
  • ข้อกำหนดด้านความปลอดภัย: ต้นทางทุกรายการใน exposedTo ต้องเชื่อถือได้ (HTTPS หรือ localhost) การส่งต้นทางที่ไม่ถูกต้องหรือไม่ปลอดภัยจะทำให้ registerTool() ปฏิเสธด้วย SecurityError
  • ขอบเขต: exposedTo ควบคุมการค้นพบข้ามเอกสารในหน้าเว็บในเฟรมต่างๆ ในแผนผังต้นไม้ของเอกสาร

การกำหนดพารามิเตอร์

พารามิเตอร์ (params) จะกำหนดโดยใช้พร็อพเพอร์ตี้ inputSchema ซึ่งต้องเป็นออบเจ็กต์ JSON Schema ที่อธิบายข้อมูลที่มีโครงสร้างที่เครื่องมือคาดหวัง

await document.modelContext.registerTool({
  name: "calculate_area",
  description: "Calculates the area of a rectangle.",
  inputSchema: {
    type: "object",
    properties: {
      width: { type: "number", description: "The width of the rectangle." },
      height: { type: "number", description: "The height of the rectangle." }
    },
    required: ["width", "height"]
  },
  execute(input) {
    // input is { width: 10, height: 20 }
    return input.width * input.height;
  },
  annotations: { readOnlyHint: true }
});

คำอธิบายเครื่องมือและพารามิเตอร์การเขียน

Agent ได้รับ inputSchema ทั้งหมดแล้ว ดังนั้น description จึงต้องระบุเพียงสิ่งที่เครื่องมือทำและเวลาที่ควรใช้ อย่าใส่ชื่อพารามิเตอร์ ประเภท ค่า enum ค่าเริ่มต้น หรือขีดจำกัดซ้ำในคำอธิบาย ให้ประกาศข้อมูลเหล่านี้ในสคีมา (enum, default, minimum/maximum) แทน คำอธิบายพารามิเตอร์เป็นไปตามกฎเดียวกัน กล่าวคือ อธิบายความหมายของค่า ไม่ใช่ประเภทหรือค่าที่อนุญาต

เบราว์เซอร์จะไม่ตรวจสอบอาร์กิวเมนต์กับ inputSchema หรือใช้ค่า default ดังนั้นให้ทำทั้ง 2 อย่างใน execute

const SORT_ORDERS = ["newest", "price_low_to_high"];

await document.modelContext.registerTool({
  name: "search_products",
  // Don't list the sort orders here, even via SORT_ORDERS: `enum` already does.
  description: "Searches the product catalog by keyword. Use when the user wants to find or compare products.",
  inputSchema: {
    type: "object",
    properties: {
      query: { type: "string", description: "Words to search for." },
      sort: { type: "string", enum: SORT_ORDERS, default: "newest", description: "Order of the results." },
    },
    required: ["query"],
  },
  async execute({ query, sort = "newest" }, { signal }) {
    if (typeof query !== "string" || !SORT_ORDERS.includes(sort)) {
      return { error: "Pass a search query and a supported sort order.", sortOrders: SORT_ORDERS };
    }
    return await productService.search({ query, sort }, { signal });
  },
  annotations: { readOnlyHint: true },
});

รูปแบบการดำเนินการ

คอลแบ็ก execute จะได้รับอาร์กิวเมนต์ 2 รายการเสมอ ได้แก่ ออบเจ็กต์อินพุต และออบเจ็กต์ตัวเลือกที่มี AbortSignal ซึ่งก็คือ execute(inputObject, { signal })

กรณีที่ควรใช้ async execute

ใช้ async เมื่อเครื่องมือเกี่ยวข้องกับการดำเนินการที่แสดงผล Promise หรือใช้เวลาในการดำเนินการให้เสร็จสมบูรณ์

  • การเรียกใช้เครือข่าย: การดึงข้อมูลจาก API
  • พื้นที่เก็บข้อมูลแบบอะซิงโครนัส: การเข้าถึง IndexedDB
  • เหตุการณ์ภายนอก: รอให้การเปลี่ยนแปลงสถานะหรือภาพเคลื่อนไหวที่เฉพาะเจาะจงเสร็จสิ้น
async execute(input, { signal }) {
  const response = await fetch(`/api/data/${input.id}`, { signal });
  return await response.json();
}

กรณีที่ควรใช้ execute (พร้อมกัน)

ใช้ฟังก์ชันแบบซิงโครนัสมาตรฐานสำหรับการดำเนินการทันที เครื่องมือแบบซิงโครนัสจะทำงานจนเสร็จก่อนที่จะสังเกตเห็นการยกเลิก จึงสามารถละเว้นสัญญาณได้ ดังนี้

  • ตรรกะล้วนๆ: คณิตศาสตร์ การกรอง หรือการจัดเรียงข้อมูลที่มีอยู่แล้วในหน่วยความจำ
  • สถานะแบบซิงโครนัส: การอ่านจาก localStorage หรือเครื่องมือจัดการสถานะแบบซิงโครนัส
execute(input) {
  return input.items.filter(item => item.active);
}

การจัดการข้อผิดพลาดใน execute

เมื่อ execute แสดงข้อผิดพลาด (หรือแสดงผลสัญญาที่ถูกปฏิเสธ) Agent จะได้รับเฉพาะข้อผิดพลาดในการดำเนินการทั่วไป ระบบจะไม่ส่งต่อข้อความแสดงข้อผิดพลาดไปยังตัวแทน หากเครื่องมือพบข้อผิดพลาดที่ดำเนินการได้หรือกู้คืนได้ (เช่น อินพุตไม่ถูกต้อง รายการหายไป หรือสถานะ HTTP ล้มเหลว) อย่า throw new Error(...) แต่ให้แสดงผลเพย์โหลดข้อผิดพลาดที่มีโครงสร้างผ่านค่าที่แสดงผลปกติของเครื่องมือแทน เพื่อให้ AI Agent ได้รับรายละเอียดการวินิจฉัยและสามารถแก้ไขด้วยตนเองหรือลองอีกครั้งได้ ดังนี้

execute({ itemName }) {
  const item = menuItems.find((entry) => entry.name.toLowerCase() === itemName.toLowerCase());
  if (!item) {
    // Return the error as a value instead of throwing, so the agent receives the message:
    return {
      error: `Menu item "${itemName}" not found.`,
      availableItems: menuItems.map((entry) => entry.name),
    };
  }
  return item;
}

ส่งอีกครั้งเมื่อยกเลิกการดำเนินการแล้วเท่านั้น (signal.aborted คือ true)

กำลังยกเลิกการดำเนินการที่อยู่ระหว่างดำเนินการ

การยกเลิกเริ่มต้นโดยผู้ใช้หรือตัวแทน บังคับ: เครื่องมือใดๆ ที่ส่งคำขอเครือข่ายหรืองานแบบไม่พร้อมกันอื่นๆ ที่ใช้เวลานานต้องเป็นไปตาม signal มิฉะนั้นการดำเนินการที่ยกเลิกจะยังคงใช้ทรัพยากรต่อไปและอาจเขียนข้อมูลที่ล้าสมัยลงใน UI หลังจากที่ Agent ดำเนินการต่อ

  • API ที่ยอมรับสัญญาณ (ผู้อ่าน fetch(), addEventListener(), ReadableStream): ส่ง signal ผ่านโดยตรง
  • API ที่ไม่ (setTimeout, setInterval, ลูปการโพล, ไปป์ไลน์ที่ใช้ await มาก): เรียก signal.throwIfAborted() ระหว่างขั้นตอน และ/หรือปฏิเสธใน signal.addEventListener('abort', ...) ตัวจับเวลาไม่ยอมรับสัญญาณ ดังนั้นคุณต้องล้างตัวจับเวลาด้วยตนเองใน Listener การยกเลิก
await document.modelContext.registerTool({
  name: "fetch_tool",
  description: "Fetch the text content of a URL and show it on the page.",
  inputSchema: {
    type: "object",
    properties: {
      url: { type: "string", description: "The URL to fetch" },
      priority: {
        type: "string",
        enum: ["high", "low", "auto"],
        description: "Relative network priority for the request",
      },
    },
    required: ["url"],
  },
  execute: async ({ url, priority }, { signal }) => {
    const output = document.querySelector("pre");
    output.textContent = "Loading…";
    try {
      // Passing `signal` aborts the network request the moment execution is cancelled.
      const response = await fetch(url, { priority, signal });
      if (!response.ok) {
        output.textContent = "";
        return { error: `Request failed with status ${response.status}` };
      }
      const text = await response.text();
      output.textContent = text;
      return text;
    } catch (err) {
      // Roll back the UI state this execution set, so a cancelled run leaves no trace.
      output.textContent = "";
      if (signal.aborted) {
        // The execution was cancelled: re-throw instead of returning an error value.
        throw err;
      }
      // Return other errors as a value instead of throwing, so the agent receives the message.
      return { error: err instanceof Error ? err.message : String(err) };
    }
  },
  annotations: { readOnlyHint: true }
});

เครื่องมือการแจ้งวิดีโอที่ไม่เหมาะสมที่มีผลตามมา

ตั้งแต่ Chrome 154 เป็นต้นไป annotations จะรองรับ consequentialHint ตั้งค่าเป็น true สำหรับเครื่องมือที่ดำเนินการที่มีความเสี่ยงสูง ย้อนกลับไม่ได้ หรือดำเนินการในชีวิตจริง เช่น การจองการเดินทาง การโอนเงิน การส่งข้อความ หรือการลบข้อมูล ซึ่งจะส่งสัญญาณให้ Agent ขอการยืนยันจากผู้ใช้อย่างชัดเจนก่อนดำเนินการ

await document.modelContext.registerTool({
  name: 'book_flight',
  description: 'Book a flight for the user with confirmed flight details.',
  inputSchema: {
    type: 'object',
    properties: {
      flightId: { type: 'string', description: 'ID of the flight to book' },
      passengers: { type: 'number', description: 'Number of tickets to purchase' },
    },
    required: ['flightId', 'passengers'],
  },
  execute: async ({ flightId, passengers }, { signal }) => {
    // Forward `signal` to the transaction API to honor cancellation:
    return await bookFlightReservation({ flightId, passengers }, { signal });
  },
  annotations: {
    readOnlyHint: false,      // This tool mutates state (it makes a booking).
    consequentialHint: true,  // Spends money and is not trivially reversible.
    untrustedContentHint: false, // Output is first-party data, not third-party content.
  }
});

บังคับ: ถือว่า consequentialHint เป็นคำแนะนำ ไม่ใช่กลไกการบังคับใช้ โดยจะไม่บล็อกการดำเนินการด้วยตัวเอง และเบราว์เซอร์ที่เก่ากว่าคำแนะนำจะไม่แสดงข้อความแจ้งเลย คงการยืนยันในแอปที่การดำเนินการนั้นรับประกันไว้แล้ว

เครื่องมือแก้ไขข้อบกพร่องในการแจ้งว่าไม่เหมาะสม

ตั้งแต่ Chrome 156 เป็นต้นไป annotations จะรองรับ debugging ตั้งค่า annotations.debugging เป็น true สำหรับเครื่องมือที่สร้างขึ้นเพื่อการแก้ไขข้อบกพร่องและเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ ไม่ใช่งานของผู้ใช้ปลายทาง ตัวอย่าง: การดัมพ์สถานะภายในของแอป การเปิด/ปิดแฟล็กฟีเจอร์ หรือการสร้างข้อมูลทดสอบ ซึ่งจะช่วยให้ Agent แยกแยะเครื่องมือสำหรับนักพัฒนาแอปเท่านั้นออกจากเครื่องมือที่ออกแบบมาเพื่อดำเนินการแทนผู้ใช้ได้ โดยค่าเริ่มต้นจะเป็น false เอเจนต์ในหน้าเว็บและโปรแกรมทดสอบอัตโนมัติจะอ่านค่านี้ได้จาก RegisteredTool.annotations.debugging ผ่าน getTools()

await document.modelContext.registerTool({
  name: "dump_app_state",
  description: "Returns a snapshot of the internal application store for debugging.",
  execute() {
    return appStore.getState();
  },
  annotations: {
    readOnlyHint: true, // Only reads state.
    debugging: true,    // Developer tooling, not an end-user action.
  }
});

เอเจนต์และการทดสอบในหน้าเว็บ (getTools และ executeTool)

หน้าเว็บไม่จำเป็นต้องมี getTools() หรือ executeTool() เพื่อแสดงเครื่องมือต่อ AI Agent เพียงเรียกใช้ document.modelContext.registerTool() ก็เพียงพอแล้วสำหรับเอเจนต์เบราว์เซอร์, ส่วนขยายเบราว์เซอร์, เครื่องมือสำหรับนักพัฒนาเว็บ และผู้ช่วยระดับระบบในการค้นพบและเรียกใช้เครื่องมือของคุณนอกแบนด์

เมธอด getTools() และ executeTool() มีไว้สำหรับผู้บริโภคที่ทำงานภายในสภาพแวดล้อม JavaScript โดยเฉพาะ

  1. เอเจนต์ JavaScript ในหน้าเว็บ: ผู้ช่วย AI, วิดเจ็ต Copilot หรืออินเทอร์เฟซแชทที่ฝังอยู่ในหน้าโดยตรง (หรือทำงานภายใน <iframe>) ซึ่งจำเป็นต้องค้นหาเครื่องมือที่หน้าเว็บโดยรอบหรือเฟรมที่เข้าถึงได้ในโครงสร้างเอกสารมีให้ และเรียกใช้เครื่องมือเหล่านั้นในนามของผู้ใช้
  2. การทดสอบและการแก้ไขข้อบกพร่องอัตโนมัติ: ชุดโปรแกรมทดสอบ (เช่น Playwright, Vitest หรือการทดสอบหน่วยในเบราว์เซอร์) ที่ตรวจสอบข้อมูลเมตาของเครื่องมือที่ลงทะเบียนและจำลองการเรียกใช้เครื่องมือของ Agent โดยไม่ต้องใช้ชุดทดสอบ LLM ภายนอกแบบเรียลไทม์

กำลังค้นหาเครื่องมือที่พร้อมใช้งานด้วย getTools()

ตัวแทนในหน้าเว็บจะเรียกใช้ document.modelContext.getTools() เพื่อดึงอาร์เรย์ของออบเจ็กต์ RegisteredTool จากเอกสารปัจจุบันและเฟรมที่เข้าถึงได้ในโครงสร้างเอกสาร

ตั้งแต่ Chrome 155 เป็นต้นไป ระบบจะแสดงผล RegisteredTool.inputSchema เป็นออบเจ็กต์ JavaScript โดยตรง (สำเนาแบบลึกของสคีมาที่ระบุไว้เมื่อลงทะเบียน) การเปลี่ยนแปลงออบเจ็กต์นี้จะไม่เปลี่ยนสคีมาจริงของเครื่องมือที่ลงทะเบียนไว้

// Example: An in-page AI assistant discovering tools available on the page:
const tools = await document.modelContext.getTools();

// In-page agents can listen for tools being registered or unregistered dynamically:
document.modelContext.addEventListener("toolchange", async () => {
  const updatedTools = await document.modelContext.getTools();
  console.log(`Active tools updated: ${updatedTools.map((t) => t.name).join(", ")}`);
});

หากต้องการค้นหาเครื่องมือที่ลงทะเบียนไว้ภายใน iframe ที่เป็นองค์ประกอบสืบทอดแบบข้ามต้นทาง (ซึ่งต้องให้สิทธิ์เข้าถึงอย่างชัดแจ้งผ่าน exposedTo ระหว่างการจดทะเบียน) ให้ส่ง fromOrigins ในพจนานุกรมตัวเลือก ดังนี้

// Query tools registered in the embedded cart frame that exposed them to this checkout origin:
const cartTools = await document.modelContext.getTools({
  fromOrigins: ["https://cart.example.com"],
});

การเรียกใช้เครื่องมือด้วย executeTool()

เอเจนต์ในหน้าเว็บและสคริปต์ทดสอบจะเรียกใช้ document.modelContext.executeTool(tool, inputObject, options) เพื่อเรียกใช้เครื่องมือที่ลงทะเบียนไว้ เครื่องมือจะทำงานในเอกสารที่ลงทะเบียนไว้ และ Promise จะแสดงผลลัพธ์เป็นสตริงที่ได้จาก Callback execute ของเครื่องมือ

ตั้งแต่ Chrome 155 เป็นต้นมา executeTool() จะยอมรับออบเจ็กต์ JavaScript ธรรมดาโดยตรงสำหรับ inputObject ซึ่งตรงกับรูปแบบอินพุตที่ Callback execute ของเครื่องมือได้รับ

// 1. Discover the tool (typically done during agent initialization or test setup):
const tools = await document.modelContext.getTools();
const areaTool = tools.find((t) => t.name === "calculate_area");

if (areaTool) {
  const controller = new AbortController();
  // Example input payload matching the tool's inputSchema:
  const inputObject = { width: 10, height: 20 };
  const options = { signal: controller.signal };

  // 2. Pass input arguments as a JavaScript object, with a JSON string fallback for Chrome <155:
  let resultString;
  try {
    resultString = await document.modelContext.executeTool(areaTool, inputObject, options);
  } catch (err) {
    if (err?.message?.startsWith("Failed to parse input")) {
      resultString = await document.modelContext.executeTool(
        areaTool,
        JSON.stringify(inputObject),
        options
      );
    } else {
      // Re-throw real tool execution or cancellation errors:
      throw err;
    }
  }

  console.log("Execution output string:", resultString);
}

ระบบจะปฏิเสธ Promise ที่แสดงผลในกรณีต่อไปนี้

  • inputObject ไม่ใช่ออบเจ็กต์หรือทำให้เป็นอนุกรมเป็นสตริง JSON ไม่ได้ (เช่น การอ้างอิงแบบวงกลมหรือค่า BigInt) โปรดทราบว่าค่าที่ละเว้นโดยการแปลงเป็นอนุกรม JSON (เช่น ฟังก์ชันและ undefined) จะถูกละเว้นจากออบเจ็กต์ที่เครื่องมือได้รับ แทนที่จะทำให้เกิดการปฏิเสธ
  • การดำเนินการถูกยกเลิกผ่าน options.signal
  • เครื่องมือถูกยกเลิกการลงทะเบียนก่อนการดำเนินการหรือพบข้อผิดพลาดขณะรันไทม์ที่ไม่ได้จัดการ

รูปแบบโรงงานเครื่องมือ

หากต้องการส่งบริบท (เช่น Store หรืออินสแตนซ์แอปพลิเคชัน) ไปยังเครื่องมือ ให้ใช้ฟังก์ชัน Factory

export function createInventoryTool(inventoryManager) {
  return {
    name: "get_inventory",
    description: "Lists items in the inventory.",
    inputSchema: { type: "object", properties: {} },
    execute() {
      return inventoryManager.getItems();
    },
    annotations: { readOnlyHint: true }
  };
}

ประเภท TypeScript

ในโปรเจ็กต์ TypeScript อย่าเขียนการประกาศสำหรับ document.modelContext ด้วยตนเอง ติดตั้งแพ็กเกจ webmcp-types อย่างเป็นทางการ (npm install --save-dev webmcp-types) และเพิ่มลงใน tsconfig.json เนื่องจากระบบไม่ได้เลือกโดยอัตโนมัติ

{
  "compilerOptions": {
    "types": ["webmcp-types"]
  }
}
  • document.modelContext มีประเภทเป็นไม่บังคับ ดังนั้นให้ป้องกันด้วย if (document.modelContext) ก่อนใช้งาน
  • เมื่อใช้ inputSchema ในบรรทัด ระบบจะอนุมานประเภทอินพุต execute จากสคีมา
  • ประเภทที่มีชื่อ เช่น WebMCP.ModelContextTool เป็นแบบส่วนกลาง (ไม่จำเป็นต้องนำเข้า) ซึ่งมีประโยชน์สำหรับการพิมพ์โรงงานเครื่องมือ

การรองรับเฟรมเวิร์ก

บังคับ: หากแอปใช้ React, Angular หรือ Vue ให้ลงทะเบียนเครื่องมือผ่านการผสานรวม WebMCP อย่างเป็นทางการของเฟรมเวิร์กนั้นๆ ตามที่ระบุไว้ด้านล่าง อย่าเรียก document.modelContext.registerTool() จากคอมโพเนนต์หรือเขียน Wrapper วงจรของคุณเอง การผสานรวมแต่ละรายการจะจัดการการตรวจหาฟีเจอร์ การล้าง AbortSignal และเครื่องมือกำหนดขอบเขตให้กับคอมโพเนนต์หรือวงจรของ Injector อยู่แล้ว API เหล่านี้เป็นเวอร์ชันทดลอง ดังนั้นก่อนเขียนโค้ด ให้ค้นหาซิกเนเจอร์ปัจจุบันในประเภทหรือเอกสารของแพ็กเกจ

เฟรมเวิร์ก แพ็กเกจ API เอกสาร
React 18+ use-webmcp-tool useWebMCP(options): การเรียกใช้ 1 ครั้งต่อเครื่องมือ npmjs.com/package/use-webmcp-tool
Angular @angular/core, @angular/forms/signals provideExperimentalWebMcpTools([...]) (ผู้ให้บริการแอปหรือเส้นทาง), declareExperimentalWebMcpTool(...) (บริการภายใน), provideExperimentalWebMcpForms() + ตัวเลือก experimentalWebMcpTool ในแบบฟอร์มสัญญาณ angular.dev/ai/webmcp
Vue @vueuse/core useWebMCP(options) Composable vueuse.org/core/usewebmcp

กฎทั้งหมดในคู่มือนี้จะยังคงมีผลเมื่อคุณใช้การผสานรวมเหล่านี้

  • คงรูปแบบคำจำกัดความของเครื่องมือไว้เหมือนเดิม ได้แก่ name, description, inputSchema, execute(input, { signal }) และ annotations
  • ปฏิบัติตามกฎสำหรับคำอธิบายและคำอธิบายประกอบ
  • ตรวจสอบอินพุตและใช้ค่าเริ่มต้นภายใน execute
  • ส่ง signal ไปยังงานที่ใช้เวลานาน
  • หากต้องการเสนอเครื่องมือเป็นบางครั้ง ให้ส่ง enabled (React, Vue) อย่ารวมฮุกหรือการเรียก Composable ไว้ในเงื่อนไข
  • การผสานรวมอาจรวมผลลัพธ์และข้อผิดพลาด (เช่น เป็น { content, isError }) โปรดตรวจสอบเอกสารของการผสานรวมก่อนที่จะพึ่งพาวิธีที่ข้อผิดพลาดไปถึง Agent

หมายเหตุเกี่ยวกับ API

  • annotations: (ไม่บังคับ) พจนานุกรมสำหรับข้อมูลเมตาของเครื่องมือ
    • readOnlyHint: (ไม่บังคับ) ตั้งค่าเป็น true หากเครื่องมือไม่แก้ไขสถานะใดๆ และอ่านข้อมูลเท่านั้น ซึ่งจะช่วยให้ Agent ตัดสินใจได้ว่าเมื่อใดที่เรียกใช้เครื่องมือได้อย่างปลอดภัย
    • consequentialHint: (ไม่บังคับ, Chrome 154 ขึ้นไป) ตั้งค่าเป็น true สำหรับการดำเนินการที่มีความเสี่ยงสูง ย้อนกลับไม่ได้ หรือการดำเนินการในชีวิตจริง เพื่อให้ Agent ขอการยืนยันจากผู้ใช้ก่อน
    • untrustedContentHint: (ไม่บังคับ) ตั้งค่าเป็น true เมื่อเครื่องมือแสดงผลเนื้อหาที่เว็บไซต์ของคุณไม่ได้ควบคุม เช่น ข้อความที่ผู้ใช้สร้างขึ้นหรือการตอบกลับจาก API ของบุคคลที่สาม
    • debugging: (ไม่บังคับ, Chrome 156 ขึ้นไป) ตั้งค่าเป็น true เมื่อเครื่องมือมีไว้สำหรับการแก้ไขข้อบกพร่องและเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ ไม่ใช่งานของผู้ใช้ปลายทาง ค่าเริ่มต้นคือ false
  • ตัวเลือกการลงทะเบียน
    • signal: (ไม่บังคับ) AbortSignal ที่ใช้เพื่อยกเลิกการลงทะเบียนเครื่องมือเมื่อไม่จำเป็นต้องใช้แล้ว
    • exposedTo: (ไม่บังคับ) อาร์เรย์ของสตริงต้นทางที่ปลอดภัยซึ่งควบคุมว่าเอกสารใดในแผนผังเอกสารที่ได้รับอนุญาตให้ค้นพบและเรียกใช้เครื่องมือข้ามขอบเขตเฟรม
  • ตัวเลือกการค้นพบเครื่องมือ (ModelContextGetToolOptions)
    • fromOrigins: (ไม่บังคับ) อาร์เรย์ของสตริงต้นทางที่ปลอดภัยเพื่อค้นหาในเฟรมที่เข้าถึงได้ การเรียก getTools() โดยไม่มี fromOrigins จะค้นหาเฉพาะเอกสารจากต้นทางเดียวกัน
  • รูปแบบการแสดงผล: ฟังก์ชัน execute สามารถแสดงผลค่าที่แปลงเป็น JSON ได้ (ออบเจ็กต์, อาร์เรย์, สตริง, ตัวเลข, บูลีน) ระบบจะไม่ส่งต่อข้อความของข้อยกเว้นที่ส่งมาจาก execute ไปยัง Agent ดังนั้นให้แสดงผลข้อผิดพลาดที่ดำเนินการได้เป็นเพย์โหลดข้อผิดพลาดที่มีโครงสร้าง (เช่น { error: "..." }) ผ่านค่าที่แสดงผลของเครื่องมือเพื่อให้ Agent อ่านและดำเนินการกับข้อผิดพลาดเหล่านั้นได้
  • บริบทที่ปลอดภัย: WebMCP ต้องใช้ HTTPS ต้นทางทั้งหมดใน exposedTo และ fromOrigins ต้องเชื่อถือได้ด้วย
  • เลิกใช้งาน/นำออก: ระบบไม่รองรับ navigator.modelContext (เลิกใช้งานใน Chromium 150), unregisterTool(), provideContext() และ clearContext() อีกต่อไป

กลยุทธ์สำรอง

ควรใช้ WebMCP Imperative API ร่วมกับการตรวจหาฟีเจอร์เพื่อให้มั่นใจว่าเข้ากันได้กับเบราว์เซอร์ที่ยังไม่รองรับ WebMCP

if ('modelContext' in document) {
  // Register tools
}