Tools API
defineTool()
Section titled “defineTool()”function defineTool< TArgs = unknown, TResult = unknown, const TName extends string = string,>( tool: ToolDefinition<TArgs, TResult, TName>,): ToolDefinition<TArgs, TResult, TName>原樣回傳 definition,同時保留 TypeScript generics 與 literal name type。
const updateCounter = defineTool< { delta: number }, { value: number }>({ name: "update_counter", description: "Update the visible counter by delta.", argsSchema: { type: "object", properties: { delta: { type: "integer" }, }, required: ["delta"], }, handler: ({ delta }) => ({ value: counter.add(delta) }),});defineTool() 本身不做 validation;createSession() 與 pedelec.webmcp() 會 normalize/validate tools。
defineRuntimeModule()
Section titled “defineRuntimeModule()”使用情境與心智模型請見 Runtime Modules;以下保留精確的宣告與準備契約。
Vite 應用使用同一個 authoring shape 描述 local 或 package-backed Runtime module。Minimal declaration 可以給 workspace.run() 使用:
import { defineRuntimeModule } from "@kaoruisaac/pedelec";
const sceneTools = defineRuntimeModule({ name: "scene-tools", entry: "./deno/scene-tools.ts",});
const result = await workspace.run( `import { buildScene } from "scene-tools";\nawait buildScene();`, { runtimeModules: [sceneTools] },);同一個 declaration 若也要給 skills.runtimeModules 使用,可以加入 Agent metadata:
import { defineRuntimeModule } from "@kaoruisaac/pedelec";
const spriteTools = defineRuntimeModule({ name: "sprite-tools", description: "Sprite authoring and preview utilities.", entry: "./agent/sprite-tools.ts", usage: `import { previewActorSource } from "sprite-tools";`, preferStdinExecution: true,});
const skills = { guidance: "Use the module when sprite preview is needed.", tools: [], runtimeModules: [spriteTools],};defineRuntimeModule() 保留 concrete declaration shape,包括 optional metadata 是否真的有提供。pedelecVitePlugin() 會在 vite build 與 vite dev 解析 entry、打包 self-contained ESM runtime,並 roll-up 獨立的 public declaration artifact。createSession() 會自動傳送 prepared artifact,Core 在 session 回傳前建立 immutable、thread-scoped package 與 import map。workspace.run() 使用同一種 prepared artifact format,但會按 Workspace 與 authorized SDK origin cache packages,再為每次 run 只對 requested names 建立 exact import map。
只使用 root SDK、從不 import @kaoruisaac/pedelec/vite 的應用不需要 Vite;Vite peer 是 optional。使用 plugin 的應用必須安裝支援的 Vite 8:
npm install @kaoruisaac/pedelec viteimport { defineConfig } from "vite";import { pedelecVitePlugin } from "@kaoruisaac/pedelec/vite";
export default defineConfig({ plugins: [pedelecVitePlugin()],});entry 必須是靜態字串。相對路徑從宣告所在 source file 解析。Vite string alias 與已安裝 package specifier(含 package.json exports subpath)會走應用程式的 Vite resolver(runtime)與 TypeScript bundler resolution(public types)。Remote URL、computed value、inline source 與 imported module object 都不支援。
Plugin 會在轉換後的 module object 上附加 private、non-enumerable 的 runtime/types artifact,並移除原始 authoring entry path。它不會寫入應用程式的 Pedelec workspace,也不會 mutate 既有 session。
usage 請保持精簡:它是常見情境的 agent hint;需要檢查 enum 或 option shape 時,以 generated declaration bundle 為準。
Agent 會在以 pedelec-ts-runtime 執行的 JavaScript/TypeScript script 中,以 logical name import module,不會以 private filesystem path 執行 module。Host Context 會列出 name、description、usage,以及 workspace-relative 的 index.d.ts pointer,供精確 API discovery;generated index.mjs 是 implementation fallback,不是一般 API reference。
preferStdinExecution 是主要用於短 script 的公開 optional authoring metadata。設為 true 時,Host Context 會以 preferredExecution: "stdin" 表示;Agent 應優先使用全域 runStdinCommand。Host Context 不會暴露 raw authoring flag,共用 contract 也不包含 provider-specific shell syntax。這是 preference 而非限制,file-backed execution 仍然有效;省略或設為 false 時不會出現 preferredExecution,也不會影響 bundling 或 artifact contents。
entry 支援 local relative file、Vite alias,以及已安裝的 bare/package exports specifier。它只在 build-time 使用;Desktop 不會解析它,Sandbox runtime 不能 fetch npm、JSR 或 network。Imported module 使用所屬 Thread 的 mode:預設 Sandbox,或明確授權的 Full Access。
只要 Core 仍知道該 thread,end/resume 會保留同一份 module snapshot。pedelec.resumeSession(sessionId) 會重新 attach 到既有 snapshot;要使用重建後的 artifact,請建立新 session。skills.tools 是分開的 App Tool capability:呼叫走 pedelec-cli --thread-id ... tool-spec 與 tool-call,Runtime Modules 則留在 pedelec-ts-runtime script 內。
ToolDefinition
Section titled “ToolDefinition”type ToolDefinition< TArgs = unknown, TResult = unknown, TName extends string = string,> = { name: TName; description: string; argsSchema: ToolArgsSchema; timeoutMs?: number; handler?: ToolSpecificHandler<TArgs, TResult>;};| Field | Required | 說明 |
|---|---|---|
name |
Yes | Agent-facing name;letter 開頭,只能包含 letter、digit、_、.、-。 |
description |
Yes | Non-empty 行為、side effect 與選擇條件說明。 |
argsSchema |
Yes | Pedelec Tool Args Schema;root 必須 object。 |
timeoutMs |
No | Positive integer runtime wait limit;Core 目前預設 60 秒。 |
handler |
No | SDK 保存在 browser、不送到 Core 的 inline handler。pedelec.webmcp() 必須提供。 |
同一 skills manifest 的 names 必須 unique。
ToolSpecificHandler
Section titled “ToolSpecificHandler”type ToolSpecificHandler<TArgs = unknown, TResult = unknown> = ( args: TArgs, ctx: ToolCallContext,) => TResult | Promise<TResult>;Handler 在 browser page context 執行。Type parameters 只有 compile-time,runtime args 仍要 validate。
Return value 必須符合 bridge JSON data model。
SkillsInput
Section titled “SkillsInput”type AgentModuleDefinition<TName extends string = string> = RuntimeModuleDefinition<TName> & { description: string; usage: string; };
type SkillsInput< TTools extends readonly ToolDefinition[] = readonly ToolDefinition[],> = { guidance: string; tools: TTools; runtimeModules?: readonly AgentModuleDefinition[]; /** @deprecated Use runtimeModules. Do not provide both fields. */ denoModules?: readonly AgentModuleDefinition[];};const tools = [getCurrentPage, updateCounter] as const;
const skills = { guidance: "Read current state before making changes.", tools,} satisfies SkillsInput<typeof tools>;guidance 必須是 string,tools 必須是 array。runtimeModules 是 optional;Session/Agent modules 需要 name、description、entry、usage,且 Agent metadata 必須是 non-empty。Workspace-only 的 workspace.run() declaration 只需要 name、entry 與 prepared Vite artifact。preferStdinExecution 是 optional boolean preference,對 Workspace execution 沒有作用。Session artifacts 會在 createSession() transport;Workspace artifacts 在需要時準備,並在 Core runtime cache 有效期間重用。
ToolNameOf
Section titled “ToolNameOf”type ToolNameOf<TTools extends readonly ToolDefinition[]> = Extract< TTools[number]["name"], string>;從 readonly definitions array 取出 tool name union。
const tools = [readPage, updateCounter] as const;type AppToolName = ToolNameOf<typeof tools>;// "get_current_page" | "update_counter"createSession() 用它收窄 named session.onTool()。
SerializableToolManifest
Section titled “SerializableToolManifest”type SerializableToolManifest = { name: string; description: string; argsSchema: ToolArgsSchema; timeoutMs?: number;};送到 Core 的 tool shape,刻意不包含 handler。
SerializableSkillsManifest
Section titled “SerializableSkillsManifest”type SerializableSkillsManifest = { guidance: string; tools: SerializableToolManifest[]; denoModules?: Array<{ name: string; description: string; usage: string; preferStdinExecution?: boolean; }>;};經 normalize 後送進 bridge 的 skills metadata。Prepared runtime 與 declaration source 會走內部 session setup path 傳送;entry 不會送到 Core。
Inline handler normalization
Section titled “Inline handler normalization”createSession() 期間:
- 檢查 tool entries;
- 透過 JSON serialization clone
argsSchema; - 依 name 將 inline handlers 保存在 browser-side map;
- 將 serializable definitions 送到 Core。
Duplicate tool name reject INVALID_INPUT。
Legacy input shorthand 已不支援,請使用 argsSchema。
Handler resolution
Section titled “Handler resolution”Incoming tool call 依序:
- session named handler;
- original definition inline handler;
- generic fallback;
- SDK-created
TOOL_HANDLER_NOT_FOUNDresult。
Handler exception 變成 TOOL_HANDLER_ERROR result;delivery failure emit SUBMIT_TOOL_RESULT_FAILED。
pedelec.webmcp()
Section titled “pedelec.webmcp()”async webmcp<const TTools extends readonly ToolDefinition[]>( input: WebMcpInput<TTools>,): Promise<WebMcpBinding>當 document.modelContext.registerTool 存在時,把既有 App Tool definitions 註冊成 browser WebMCP tools。這個 method 不會建立 Pedelec Session,也不會呼叫 Extension、Desktop、Provider 或 Core。
type WebMcpSkillsInput< TTools extends readonly ToolDefinition[] = readonly ToolDefinition[],> = { tools: TTools;};
type WebMcpInput< TTools extends readonly ToolDefinition[] = readonly ToolDefinition[],> = { skills: WebMcpSkillsInput<TTools>;};
type WebMcpBinding = { available: boolean; dispose(): void;};skills.tools 是唯一的 skills 欄位。guidance 與 runtimeModules 屬於 Session,不在這個 input。傳給 webmcp() 的每個 tool 都必須有 inline handler;這條 binding 沒有 session.onTool()。
const tools = [getCurrentPage] as const;
const binding = await pedelec.webmcp({ skills: { tools },});
if (binding.available) { // Browser WebMCP client 可以呼叫這些 tools,直到 dispose()。}
binding.dispose();同一份 tools 也可以再傳給 createSession()。
Field mapping
Section titled “Field mapping”| App Tool | WebMCP registration |
|---|---|
name |
name |
description |
description |
normalized argsSchema |
inputSchema |
inline handler |
execute |
timeoutMs 仍只是 Pedelec Session 的等待上限。WebMCP registration 沒有 per-tool timeout,這個 binding 也不另外加 timer。Registration 不包含 title、annotations、exposedTo、handler、guidance 或 runtimeModules。
Name 沿用 Pedelec 規則:letter 開頭,之後只能是 letter、digit、_、.、-。WebMCP 另外要求 name 最多 128 個字元。inputSchema 是與 createSession() 相同的 normalized argsSchema clone。Pedelec 不會自動加入 additionalProperties,也不會改寫 required、enum 或 oneOf。
Availability
Section titled “Availability”webmcp() 先驗證 input,再檢查 document.modelContext.registerTool。無效 tool、重複 name、空白 description、超過 128 字元的 name,或缺少 inline handler,仍會以 INVALID_INPUT reject。缺少 handler 的 message 是 WebMCP tool handler must be a function,details 會帶 toolName。
沒有 window 或 document,或 registerTool 不是 function 時,method resolve:
{ available: false, dispose() {} }不支援的 browser 不會 throw,也不會載入 polyfill,也不使用 navigator.modelContext 作為註冊 API。Pedelec 只在 document.modelContext.registerTool 可用時綁定 tools;否則 available 為 false。
Lifecycle
Section titled “Lifecycle”一次 webmcp() 建立一個 AbortController,並把同一個 signal 傳給每一次 registerTool。dispose() 會 abort 該 signal。它是同步的,可以重複呼叫,不會結束 Session,也不會聯絡 Core。Dispose 一個 binding 不會移除另一次 webmcp() 或其他 page script 註冊的 tools。
如果註冊進行到一半失敗,webmcp() 會 abort 這個共用 signal 並 reject,不會回傳 binding。除非失敗本身已經是 PedelecError,否則 error code 是 WEBMCP_REGISTRATION_FAILED;既有的 PedelecError 會保留。其他 script 已經註冊的同名 tool 以 registerTool() 的結果為準;Pedelec 不會改名,也不會 unregister 別人的 tool。
Handler context
Section titled “Handler context”WebMCP 執行不具備 Pedelec Session 的 ToolCallContext;要跨 Pedelec / WebMCP 共用的 inline handler 應只依賴 args 與一般 application/browser state。
Adapter 只呼叫 handler(args),不會製造 sessionId、provider、turnId 或 toolRequestId,也不會把 WebMCP execution AbortSignal 傳進既有 handler。會讀取 ctx.sessionId 的 handler 仍可用於 Pedelec Session,但它不是可移植的 WebMCP handler。
Tool Args Schema entry types
Section titled “Tool Args Schema entry types”Root alias:
type ToolArgsSchema = ToolArgsObjectSchema;Node union:
type ToolArgsSchemaNode = | ToolArgsStringSchema | ToolArgsNumberSchema | ToolArgsIntegerSchema | ToolArgsBooleanSchema | ToolArgsArraySchema | ToolArgsObjectSchema | ToolArgsOneOfSchema;所有欄位見 Types,使用方式見 Tool Args Schema。
舊 Module API 相容性
Section titled “舊 Module API 相容性”DenoModuleDefinition、defineDenoModule() 與 denoModules 仍可使用,TypeScript 以 @deprecated 提示。請改用 RuntimeModuleDefinition、defineRuntimeModule() 與 runtimeModules。同一個 skills/options 物件只能指定其中一種欄位;即使是空陣列,同時帶兩個欄位也會回 INVALID_INPUT。內部 SerializableSkillsManifest.denoModules wire 欄位維持不變。
import { defineDenoModule, defineRuntimeModule, type DenoModuleDefinition } from "@kaoruisaac/pedelec";const legacy: DenoModuleDefinition = defineDenoModule({ name: "project-tools", entry: "./agent/project-tools.ts" });await workspace.run('import "project-tools";', { denoModules: [legacy] });const modern = defineRuntimeModule({ name: "project-tools", entry: "./agent/project-tools.ts" });await workspace.run('import "project-tools";', { runtimeModules: [modern] });