wowok_agent
Version:
Making It Easy for AI Agents to Communicate, Collaborate, Trade, and Trust.
748 lines (733 loc) β’ 89.9 kB
JavaScript
#!/usr/bin/env node
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { readFileSync } from "fs";
import { resolve } from "path";
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
import { CallService_DataSchema, CallMachine_DataSchema, MachineNode2File_InputSchema, MachineNode2File_OutputWrappedSchema, CallProgress_DataSchema, CallPermission_DataSchema, CallGuard_DataSchema, Guard2File_InputSchema, Guard2File_OutputWrappedSchema, CallArbitration_DataSchema, CallRepository_DataSchema, CallContact_DataSchema, CallTreasury_DataSchema, CallReward_DataSchema, CallAllocation_DataSchema, CallPersonal_DataSchema, CallPayment_DataSchema, CallDemand_DataSchema, CallOrder_DataSchema, CallEnvSchema, SubmissionCallSchema, strictParse, CallOutputSchema, handleCallResult, createServerConfig, createCapabilitiesConfig, createToolMeta, transformSubmission, getEnvConfig, WipGenerationOptionsSchema, WipToHtmlOptionsSchema, TokenDataFilterSchema, LocalInfoFilterSchema, LocalMarkFilterSchema, AccountFilterSchema, TokenTypeSchema, OnchainEventsInputSchema, OnchainEventsResultSchema, ProtocolInfoQuerySchema, ProtocolInfoResultSchema, TableAnswerSchema, TableItem_RepositoryDataSchema, TableItem_PermissionPermSchema, TableItem_EntityRegistrarSchema, TableItem_EntityLinkerSchema, TableItem_RewardRecordSchema, TableItem_DemandPresenterSchema, TableItem_TreasuryHistorySchema, TableItem_MachineNodeSchema, TableItem_ProgressHistorySchema, TableItem_AddressMarkSchema, NameOrAddressSchema, ObjectBaseSchema, AccountOrMark_AddressSchema, AccountOrMark_AddressAISchema, EntrypointSchema, AccountOperationOutputWrappedSchema, LocalMarkOperationOutputWrappedSchema, LocalInfoOperationOutputWrappedSchema, WipOperationOutputSchema, MessengerOperationOutputSchema, AccountOperationSchema, LocalMarkOperationSchema, LocalInfoOperationSchema, parseMachineNodesFromText, formatNodeErrors as formatMachineNodeErrors, MessengerOperationInputSchema, } from "./schema/index.js";
import { CallService, CallMachine, CallProgress, CallPermission, CallGuard, CallArbitration, CallRepository, CallContact, CallTreasury, CallReward, CallAllocation, CallPersonal, CallPayment, CallDemand, CallOrder, gen_passport, guard2file, parseGuardFile, formatGuardParseErrors, machineNode2file, generateNodeComments, generate_wip, verify_wip, sign_wip, wip2html, account_operation, local_mark_operation, local_info_operation, watch_conversations, send_message, send_file, watch_messages, extract_zip_messages, generate_wts, verify_wts, sign_wts, wts2html, proof_message, mark_messages_as_viewed, mark_conversation_as_viewed, query_local_mark_list, query_account_list, query_local_info_list, query_local_token_list, query_account, query_personal, query_objects, query_table, query_tableItem, queryProtocolInfo, query_received, queryTableItem_RepositoryData, queryTableItem_PermissionPerm, queryTableItem_RewardRecord, queryTableItem_DemandPresenter, queryTableItem_TreasuryHistory, queryTableItem_MachineNode, queryTableItem_ProgressHistory, queryTableItem_AddressMark, queryTableItem_EntityRegistrar, queryTableItem_EntityLinker, query_events, blacklist, friendslist, guardlist, settings, AmountType, } from "wowok";
const SERVER_DESCRIPTION = `WoWok MCP Server - Making It Easy for AI Agents to Communicate, Collaborate, Trade, and Trust.
## Token System Overview
### Default Platform Token: WOW
- Token Type: 0x2::wow::WOW
- Precision: 1_000_000_000 (9 decimal places)
- This is the default token for all operations unless specified otherwise
### Multi-Token Support
The platform supports ANY token (e.g., USDT, USDC, custom tokens). To use non-WOW tokens:
1. Use query_toolkit with query_type='token_list' to query available tokens and their precision info
2. Use the token's full type string (e.g., '0x2::usdt::USDT') in token_type parameter
3. Each token has its own precision - always verify before operations
### Token Precision Handling
Different tokens have different precisions. ALWAYS query to verify:
- WOW (default): 1_000_000_000 (9 decimals)
- Other tokens: Use query_toolkit with query_type='token_list' to get actual precision
- Never assume a token's decimal places - always query first
## CRITICAL: Token Amount Handling
### Amount Input Rules
1. WITH currency unit (e.g., '2WOW', '10.5WOW', '100USDT'):
- Auto-converted using token's precision: 2WOW -> 2_000_000_000
- Use for: All on-chain token operations
- MUST match the token_type parameter
2. WITHOUT currency unit (e.g., '2', '100'):
- Used as-is: 2 -> 2
- Use for: Regular numeric parameters (counts, indices, etc.)
### Multi-Token Operation Examples
- Create USDT-priced Service: token_type='0x2::usdt::USDT', amount='100USDT'
- Allocate USDC rewards: token_type='0x2::usdc::USDC', amount='500USDC'
- WOW remains default: omit token_type or use '0x2::wow::WOW'
### Security Warning
Token amounts are HIGHLY SENSITIVE. Always:
- Confirm user's intent before executing transfers
- Verify token type and amount format (with/without unit)
- Query token precision if unsure
- Double-check large amount transactions
- Never assume decimal places - use explicit token unit
## Tool Selection Guide
| User Intent | Correct Tool | Wrong Tool (Avoid) |
|------------|--------------|-------------------|
| Create service listing | onchain_operations (service) | query_toolkit |
| Send coins to address | onchain_operations (payment) | account_operation |
| Check my balance | query_toolkit (account_balance) | onchain_operations |
| Manage local wallet | account_operation | onchain_operations |
| Export Guard for edit | guard2file | query_toolkit |
| Send/Watch encrypted message | messenger_operation | onchain_operations |
| Create workflow template | onchain_operations (machine) | wip_file |
| Store my phone number | local_info_operation | onchain_operations |
| Buy service (create order) | onchain_operations (order) | onchain_operations (service) |
| Manage my orders | onchain_operations (order) | query_toolkit |
| Apply for arbitration | onchain_operations (order) | onchain_operations (arbitration) |
| Create reward pool for marketing | onchain_operations (reward) | onchain_operations (treasury) |
| Claim rewards from pool | onchain_operations (reward) | query_toolkit |
| Create fund allocation plan | onchain_operations (allocation) | onchain_operations (treasury) |
| Execute fund distribution | onchain_operations (allocation) | onchain_operations (payment) |
| Post service demand with reward | onchain_operations (demand) | onchain_operations (service) |
| Submit solution for demand | onchain_operations (demand) | onchain_operations (order) |
| Create team treasury | onchain_operations (treasury) | onchain_operations (allocation) |
| Deposit/Withdraw from treasury | onchain_operations (treasury) | onchain_operations (payment) |
| Create access control rules | onchain_operations (permission) | onchain_operations (guard) |
| Create validation rules (Guard) | onchain_operations (guard) | onchain_operations (permission) |
| Export Guard for editing | guard2file | query_toolkit |
| Export Machine nodes for editing | machineNode2file | query_toolkit |
| Query token list and precision | query_toolkit (token_list) | wowok_buildin_info |
| Watch on-chain events | onchain_events | query_toolkit |
| Generate WIP promise file | wip_file (generate) | messenger_operation |
| Sign WIP file | wip_file (sign) | account_operation (signData) |
| Verify WIP file integrity | wip_file (verify) | query_toolkit |
| Send encrypted file via messenger | messenger_operation (send_file) | wip_file |
| Generate witness timestamp (WTS) | messenger_operation (generate_wts) | wip_file |
| Query protocol constants | wowok_buildin_info | query_toolkit |
| Get documentation URL | documents_and_learn | wowok_buildin_info |
## Local vs On-chain Operations
### LOCAL ONLY (Never touch blockchain):
- account_operation
- local_mark_operation
- local_info_operation
### ON-CHAIN (Blockchain transactions):
- onchain_operations
- messenger_operation (some operations)
- wip_file (sign operation)
### QUERY (Read-only):
- query_toolkit
- onchain_events
- wowok_buildin_info
- documents_and_learn
- guard2file
- machineNode2file
`;
const server = new McpServer(createServerConfig(packageJson, SERVER_DESCRIPTION), createCapabilitiesConfig());
function convertAmountType(type) {
if (typeof type === "number")
return type;
const upperType = type.toUpperCase();
if (upperType === "GUARDU64IDENTIFIER" || upperType === "GUARD_U64_IDENTIFIER") {
return AmountType.GuardU64Identifier;
}
if (upperType === "FIXED") {
return AmountType.Fixed;
}
return AmountType.Fixed;
}
function transformRewardData(data) {
if (!data)
return data;
const transformed = { ...data };
if (transformed.guard_add && Array.isArray(transformed.guard_add)) {
transformed.guard_add = transformed.guard_add.map((guard) => {
if (guard.amount && typeof guard.amount.type === "string") {
return {
...guard,
amount: {
...guard.amount,
type: convertAmountType(guard.amount.type)
}
};
}
return guard;
});
}
return transformed;
}
const OnchainOperationsSchema = z.preprocess((input) => {
if (typeof input === 'object' && input !== null) {
const obj = { ...input };
if (typeof obj.description === 'string' && !obj.operation_type) {
try {
const parsed = JSON.parse(obj.description);
if (parsed && typeof parsed === 'object' && parsed.operation_type) {
return parsed;
}
}
catch { }
}
if (typeof obj.data === 'string') {
try {
obj.data = JSON.parse(obj.data);
}
catch { }
}
if (typeof obj.env === 'string') {
try {
obj.env = JSON.parse(obj.env);
}
catch { }
}
if (typeof obj.submission === 'string') {
try {
obj.submission = JSON.parse(obj.submission);
}
catch { }
}
if (typeof obj.info === 'string') {
try {
obj.info = JSON.parse(obj.info);
}
catch { }
}
return obj;
}
return input;
}, z.discriminatedUnion("operation_type", [
z.object({
operation_type: z.literal("service"),
data: CallService_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("πͺ Service Object: Create and manage product/service listings with transparent promises, bind workflow templates to order processing, set pricing, issue discount coupons to customers, and establish quality standards, etc.."),
z.object({
operation_type: z.literal("machine"),
data: CallMachine_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("βοΈ Machine Object: Design and deploy automated workflow templates (Machines) that define how services are delivered, etc.."),
z.object({
operation_type: z.literal("progress"),
data: CallProgress_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π Progress Object: Track and manage active workflows in real-time."),
z.object({
operation_type: z.literal("repository"),
data: CallRepository_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π¦ Repository Object: Read/write database with consensus field + address as key, strongly-typed data as value."),
z.object({
operation_type: z.literal("arbitration"),
data: CallArbitration_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("βοΈ Arbitration Object: Access a transparent on-chain arbitration system for resolving order conflicts."),
z.object({
operation_type: z.literal("contact"),
data: CallContact_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π¬ Contact Object: Manage on-chain instant messaging contact profiles."),
z.object({
operation_type: z.literal("treasury"),
data: CallTreasury_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π° Treasury Object: Create and manage treasury for team funds with deposit/withdrawal rules, etc.."),
z.object({
operation_type: z.literal("reward"),
data: CallReward_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π Reward Object: Create reward pools and set claim conditions by Guard verification."),
z.object({
operation_type: z.literal("allocation"),
data: CallAllocation_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π€ Allocation Object: Create distribution plans to auto-distribute funds to multiple recipients."),
z.object({
operation_type: z.literal("permission"),
data: CallPermission_DataSchema,
env: CallEnvSchema.optional(),
}).describe("π Permission Object: Define who can perform which operations on WoWok objects. Important Note: If needed, you should first query 'guard instructions' through the 'wowok_buildin_info' tool."),
z.object({
operation_type: z.literal("guard"),
data: CallGuard_DataSchema,
env: CallEnvSchema.optional(),
}).describe("π‘οΈ Guard Object: Create immutable programmable validation rules that return boolean results. Set 'namedNew' to name the new Guard. Use root.type='node' for direct node tree or root.type='file' to load from file. Use 'wowok_buildin_info' tool with query='guard instructions' for all available operations. NOTE for EntityLinker/EntityRegistrar queries: Add system addresses to the Guard table - ENTITY_LINKER_ADDRESS (0xaaa) for EntityLinker, ENTITY_REGISTRAR_ADDRESS (0xaab) for EntityRegistrar."),
z.object({
operation_type: z.literal("personal"),
data: CallPersonal_DataSchema,
env: CallEnvSchema.optional(),
}).describe("π Public Identity Profile: Establish and manage your on-chain public identity. β οΈ CRITICAL: Everything here is PERMANENTLY PUBLIC on the blockchain!"),
z.object({
operation_type: z.literal("payment"),
data: CallPayment_DataSchema,
env: CallEnvSchema.optional(),
}).describe("π° Payment Object: Send instant, irreversible coin transfers to any wallet address."),
z.object({
operation_type: z.literal("demand"),
data: CallDemand_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π― Demand Object: Post service requests with reward pools on-chain."),
z.object({
operation_type: z.literal("order"),
data: CallOrder_DataSchema,
env: CallEnvSchema.optional(),
submission: SubmissionCallSchema.optional(),
}).describe("π¦ Order Object: Manage the order lifecycle, Including operating order arbitration (Arb Object associated with the order), advancing progress (Progress Object associated with the order), extracting order refunds/payments, setting agents, etc."),
z.object({
operation_type: z.literal("gen_passport"),
guard: z.union([z.string(), z.array(z.string())]).describe("Guard object ID(s) to verify and generate passport from. Can be a single guard (string) or multiple guards (array of strings). Supports guard names or addresses."),
info: SubmissionCallSchema.optional().describe("Optional submission data. If not provided, will attempt to get existing submissions from the guard."),
env: CallEnvSchema.optional(),
}).describe("π Generate Verified Passport Object: Create immutable verified credentials after Guard validation passes. Supports verifying multiple guards at once."),
]));
const WipOperationsSchema = z.preprocess((input) => {
if (typeof input === 'object' && input !== null) {
const obj = { ...input };
if (typeof obj.description === 'string' && !obj.type) {
try {
const parsed = JSON.parse(obj.description);
if (parsed && typeof parsed === 'object' && parsed.type) {
return parsed;
}
}
catch { }
}
if (typeof obj.options === 'string') {
try {
obj.options = JSON.parse(obj.options);
}
catch { }
}
return obj;
}
return input;
}, z.discriminatedUnion("type", [
z.object({
type: z.literal("generate"),
options: WipGenerationOptionsSchema.describe("WIP generation options"),
outputPath: z.string().describe("Output file path (.wip file). If file exists, it will be overwritten"),
}).describe("Generate WIP file from markdown text and optional images"),
z.object({
type: z.literal("verify"),
wipFilePath: z.string().describe("WIP file path to verify. Supports: 1) Local file path (e.g., '/path/to/file.wip', 'C:\\Users\\name\\doc.wip'), 2) Network URL (e.g., 'https://example.com/doc.wip', 'http://site.com/file.wip'), 3) Data URL (e.g., 'data:application/json;base64,eyJ3aXAiOi...')"),
hash_equal: z.string().optional().describe("Optional expected hash value. If provided, the function will first verify if the file's hash matches this value. If not matched, returns hash mismatch error."),
requireSignature: z.boolean().optional().describe("Optional flag to require digital signature. If true, verification will fail if WIP file has no signature"),
}).describe("Verify WIP file integrity and signatures"),
z.object({
type: z.literal("sign"),
wipFilePath: z.string().describe("WIP file path to sign. Supports: 1) Local file path (e.g., '/path/to/file.wip'), 2) Network URL (e.g., 'https://example.com/doc.wip'). The file will be loaded, validated, and signed"),
account: z.string().optional().describe("Signing account (account name or address). If not specified, uses default account"),
outputPath: z.string().optional().describe("Output file path. If not specified, adds 'signed_' prefix to original file name (e.g., 'doc.wip' becomes 'signed_doc.wip')"),
}).describe("Sign WIP file with account"),
z.object({
type: z.literal("wip2html"),
wipPath: z.string().describe("WIP file path or directory path. Supports: 1) Single WIP file (e.g., '/path/to/file.wip'), 2) Directory containing .wip files (e.g., '/path/to/wips/'), 3) Network URL (e.g., 'https://example.com/doc.wip'). When directory is provided, all .wip files in the directory will be converted to HTML"),
options: WipToHtmlOptionsSchema.optional().describe("Conversion options"),
}).describe("Convert WIP file to HTML format"),
]));
const OnchainTableDataSchema = z.preprocess((input) => {
if (typeof input === 'object' && input !== null && !Array.isArray(input)) {
const obj = { ...input };
if (typeof obj.description === 'string' && !obj.query_type) {
try {
const parsed = JSON.parse(obj.description);
if (parsed && typeof parsed === 'object' && parsed.query_type) {
return parsed;
}
}
catch { }
}
if (obj.cursor === '')
obj.cursor = null;
if (obj.limit === '')
obj.limit = null;
return obj;
}
return input;
}, z.union([
z.discriminatedUnion("query_type", [
z.object({
query_type: z.literal("onchain_table"),
parent: NameOrAddressSchema.describe("Parent object ID whose dynamic fields table to query"),
cursor: z.union([z.string(), z.null()]).optional().describe("Pagination cursor from previous page's nextCursor"),
limit: z.union([z.number(), z.null()]).optional().describe("Max items per page"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Paginated query of an on-chain object's dynamic fields table β returns items with keys, types, and object IDs. Use to explore all entries (e.g., all orders in a Service, all records in a Repository). Returns: TableAnswer | undefined (items[], nextCursor, hasNextPage)"),
z.object({
query_type: z.literal("onchain_table_item_repository_data"),
parent: NameOrAddressSchema.describe("Parent Repository object ID"),
name: z.string().describe("Name/key of the record to retrieve from the repository"),
entity: z.union([AccountOrMark_AddressSchema, z.number()]).describe("Entity ID or address that owns/identifies the record"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a specific record from a Repository's on-chain key-value database by name and entity. Use to read stored data records. Returns: TableItem_RepositoryData | undefined"),
z.object({
query_type: z.literal("onchain_table_item_permission_perm"),
parent: NameOrAddressSchema.describe("Parent Permission object ID"),
address: z.union([AccountOrMark_AddressSchema, z.string()]).describe("User address or Guard ID whose permissions to check"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a permission entry from a Permission object's perm table β checks what operations a user/guard is allowed to perform. Use to verify access rights. Returns: TableItem_PermissionPerm | undefined"),
z.object({
query_type: z.literal("onchain_table_item_entity_registrar"),
address: z.union([AccountOrMark_AddressSchema, z.string()]).describe("User address to look up in the global EntityRegistrar"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query an entity's registration record from the global EntityRegistrar β verifies if an address is registered on the blockchain. Use to check registration status and details. Returns: TableItem_EntityRegistrar | undefined"),
z.object({
query_type: z.literal("onchain_table_item_entity_linker"),
address: z.union([AccountOrMark_AddressSchema, z.string()]).describe("Entity address whose community votes/endorsements to query"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query community votes/endorsements for an entity from the global EntityLinker β shows how others have voted on this entity. Use to check reputation and community trust. Returns: TableItem_EntityLinker | undefined"),
z.object({
query_type: z.literal("onchain_table_item_reward_record"),
parent: NameOrAddressSchema.describe("Parent Reward object ID"),
address: AccountOrMark_AddressSchema.describe("User address that claimed the reward"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a reward claim record from a Reward object's table β checks if a user has claimed a specific incentive. Use to verify reward distribution status. Returns: TableItem_RewardRecord | undefined"),
]),
z.discriminatedUnion("query_type", [
z.object({
query_type: z.literal("onchain_table_item_demand_presenter"),
parent: NameOrAddressSchema.describe("Parent Demand object ID"),
address: AccountOrMark_AddressSchema.describe("Presenter address that submitted the demand"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a demand submission from a Demand object's table β Demands are service requests submitted by users. Use to check a specific demand's details. Returns: TableItem_DemandPresenter | undefined"),
z.object({
query_type: z.literal("onchain_table_item_treasury_history"),
parent: NameOrAddressSchema.describe("Parent Treasury object ID"),
address: AccountOrMark_AddressSchema.describe("Payment ID whose treasury record to look up"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a payment record from a Treasury's history table by payment ID β Treasury manages funds and payment tracking. Use to look up payment details and status. Returns: TableItem_TreasuryHistory | undefined"),
z.object({
query_type: z.literal("onchain_table_item_machine_node"),
parent: NameOrAddressSchema.describe("Parent Machine object ID"),
key: z.string().describe("Node name (string key) to query in the Machine's workflow definition"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a workflow node definition from a Machine object's table by node name β Machines define workflow templates. Use to inspect a specific node's configuration and logic. Returns: TableItem_MachineNode | undefined"),
z.object({
query_type: z.literal("onchain_table_item_progress_history"),
parent: NameOrAddressSchema.describe("Parent Progress object ID"),
u64: z.union([z.number(), z.string()]).describe("Sequence number (u64) of the progress step to query"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a progress step record from a Progress object's table by sequence number β Progress tracks order/workflow execution. Use to check the status of a specific execution step. Returns: TableItem_ProgressHistory | undefined"),
z.object({
query_type: z.literal("onchain_table_item_address_mark"),
parent: NameOrAddressSchema.describe("Parent AddressMark object ID"),
address: AccountOrMark_AddressSchema.describe("Address whose PUBLIC on-chain name/tags to look up"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a PUBLIC on-chain name/tag mark for an address β unlike local marks, these are published on-chain. Use to look up public labels attached to any address. Returns: TableItem_AddressMark | undefined"),
z.object({
query_type: z.literal("onchain_table_item_generic"),
parent: NameOrAddressSchema.describe("Parent object ID whose dynamic field to query"),
key_type: z.string().describe("Type of the key (e.g., 'address', 'u64', 'string', '0x2::object::ID')"),
key_value: z.any().describe("Value of the key. Must match the key_type format"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query a generic table item from ANY object's dynamic fields β supports arbitrary key types and values for non-WoWok objects. Use for custom objects and general-purpose table lookups. Returns: ObjectBase | undefined"),
]),
]));
const OnchainTableDataResultSchema = z.object({
result: z.union([
z.object({ query_type: z.literal("onchain_table"), result: z.union([TableAnswerSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_repository_data"), result: z.union([TableItem_RepositoryDataSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_permission_perm"), result: z.union([TableItem_PermissionPermSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_entity_registrar"), result: z.union([TableItem_EntityRegistrarSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_entity_linker"), result: z.union([TableItem_EntityLinkerSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_reward_record"), result: z.union([TableItem_RewardRecordSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_demand_presenter"), result: z.union([TableItem_DemandPresenterSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_treasury_history"), result: z.union([TableItem_TreasuryHistorySchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_machine_node"), result: z.union([TableItem_MachineNodeSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_progress_history"), result: z.union([TableItem_ProgressHistorySchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_address_mark"), result: z.union([TableItem_AddressMarkSchema, z.undefined()]) }),
z.object({ query_type: z.literal("onchain_table_item_generic"), result: z.union([ObjectBaseSchema, z.undefined()]) }),
])
});
async function handleOnchainTableData(args) {
const validated = strictParse(OnchainTableDataSchema, args, "onchain_table_data input");
let result;
switch (validated.query_type) {
case "onchain_table": {
const queryResult = await query_table({ parent: validated.parent, cursor: validated.cursor, limit: validated.limit, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table", result: queryResult };
break;
}
case "onchain_table_item_repository_data": {
const queryResult = await queryTableItem_RepositoryData({ parent: validated.parent, name: validated.name, entity: validated.entity, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_repository_data", result: queryResult };
break;
}
case "onchain_table_item_permission_perm": {
const queryResult = await queryTableItem_PermissionPerm({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_permission_perm", result: queryResult };
break;
}
case "onchain_table_item_reward_record": {
const queryResult = await queryTableItem_RewardRecord({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_reward_record", result: queryResult };
break;
}
case "onchain_table_item_demand_presenter": {
const queryResult = await queryTableItem_DemandPresenter({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_demand_presenter", result: queryResult };
break;
}
case "onchain_table_item_treasury_history": {
const queryResult = await queryTableItem_TreasuryHistory({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_treasury_history", result: queryResult };
break;
}
case "onchain_table_item_machine_node": {
const queryResult = await queryTableItem_MachineNode({ parent: validated.parent, key: validated.key, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_machine_node", result: queryResult };
break;
}
case "onchain_table_item_progress_history": {
const queryResult = await queryTableItem_ProgressHistory({ parent: validated.parent, u64: validated.u64, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_progress_history", result: queryResult };
break;
}
case "onchain_table_item_address_mark": {
const queryResult = await queryTableItem_AddressMark({ parent: validated.parent, address: validated.address, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_address_mark", result: queryResult };
break;
}
case "onchain_table_item_entity_registrar": {
const queryResult = await queryTableItem_EntityRegistrar({ address: validated.address, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_entity_registrar", result: queryResult };
break;
}
case "onchain_table_item_entity_linker": {
const queryResult = await queryTableItem_EntityLinker({ address: validated.address, no_cache: validated.no_cache, network: validated.network });
result = { query_type: "onchain_table_item_entity_linker", result: queryResult };
break;
}
case "onchain_table_item_generic": {
const queryResult = await query_tableItem({
parent: validated.parent,
key: { type: validated.key_type, value: validated.key_value },
no_cache: validated.no_cache,
network: validated.network
});
result = { query_type: "onchain_table_item_generic", result: queryResult };
break;
}
default:
throw new Error(`Unknown table query type. Valid query_types are: "onchain_table", "onchain_table_item_repository_data", "onchain_table_item_permission_perm", "onchain_table_item_reward_record", "onchain_table_item_demand_presenter", "onchain_table_item_treasury_history", "onchain_table_item_machine_node", "onchain_table_item_progress_history", "onchain_table_item_address_mark", "onchain_table_item_entity_registrar", "onchain_table_item_entity_linker", "onchain_table_item_generic"`);
}
return {
content: [{ type: "text", text: JSON.stringify({ result }) }],
structuredContent: { result },
};
}
const WatchQueryOperationsSchema = z.preprocess((input) => {
if (typeof input === 'object' && input !== null) {
const obj = { ...input };
if (typeof obj.description === 'string' && !obj.query_type) {
try {
const parsed = JSON.parse(obj.description);
if (parsed && typeof parsed === 'object' && parsed.query_type) {
return parsed;
}
}
catch { }
}
for (const key of ['filter', 'objects', 'env', 'token_type']) {
if (typeof obj[key] === 'string') {
try {
obj[key] = JSON.parse(obj[key]);
}
catch { }
}
}
return obj;
}
return input;
}, z.union([
z.discriminatedUnion("query_type", [
z.object({
query_type: z.literal("local_mark_list"),
filter: LocalMarkFilterSchema.optional().describe("Local mark filter"),
}).describe("Query your LOCAL address book β maps human-readable names to blockchain addresses with optional tags. Use to resolve namesβaddresses or find addresses by tag. Returns: MarkData[] (name, address, tags, timestamps)"),
z.object({
query_type: z.literal("account_list"),
filter: AccountFilterSchema.optional().describe("Account filter"),
}).describe("Query your LOCAL accounts β view all accounts stored on this device (addresses, public keys, messenger status, suspension state). Use to discover available accounts before operations. Returns: AccountData[] (name, address, pubkey, suspended, messenger, timestamps)"),
z.object({
query_type: z.literal("local_info_list"),
filter: LocalInfoFilterSchema.optional().describe("Local info filter"),
}).describe("Query your LOCAL private info β sensitive data like delivery addresses, phone numbers, contacts stored ONLY on this device. Use to retrieve saved contact/delivery details. Returns: InfoData[] (name, default value, contents, timestamps)"),
z.object({
query_type: z.literal("token_list"),
filter: TokenDataFilterSchema.optional(),
}).describe("Query cached token metadata β symbol, decimals, icon URL, description for tokens previously fetched from chain. Use to look up token info without an on-chain query. Returns: TokenTypeInfo[] (type, alias, name, symbol, decimals, iconUrl)"),
z.object({
query_type: z.literal("account_balance"),
name_or_address: NameOrAddressSchema.optional().describe("Account name or address. Use empty string '' for the default account. Defaults to '' if omitted."),
balance: z.boolean().optional().describe("Set to true to query total balance amount for the token type"),
coin: z.object({
cursor: z.union([z.string(), z.null()]).optional().describe("Pagination cursor for listing coin objects"),
limit: z.union([z.number(), z.null()]).optional().describe("Max coin objects per page"),
}).optional().describe("Set to query paginated coin objects instead of balance. Use cursor/limit for pagination."),
token_type: TokenTypeSchema.optional().describe("Token type to query; defaults to 0x2::wow::WOW (platform token)"),
network: EntrypointSchema.optional(),
}).describe("Query an account's coin balance OR paginated coin objects. Use balance=true for total amount, or coin={cursor,limit} to list individual coin objects. Returns: { address, balance? | coin? }"),
]),
z.discriminatedUnion("query_type", [
z.object({
query_type: z.literal("onchain_personal_profile"),
account: NameOrAddressSchema.optional().describe("Account name or ID to query. Use empty string '' for the default account."),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query any user's PUBLIC on-chain profile β social links, reputation (likes/dislikes), personal info records, voting history, referrer. Use to look up a user's public identity and reputation. Returns: ObjectPersonal | undefined"),
z.object({
query_type: z.literal("onchain_objects"),
objects: z.array(NameOrAddressSchema).describe("List of object IDs (names or addresses) to query in batch"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Batch query on-chain WOWOK objects by ID or name β supports Service, Machine, Order, Treasury, Reward, Arb, Personal, Contact, and more. Use to inspect one or more objects in a single call. Returns: { objects: ObjectBase[] }"),
z.object({
query_type: z.literal("onchain_received"),
name_or_address: AccountOrMark_AddressAISchema.describe("Name or address of the object that received payments/items - can be a string (name/address) or full object"),
all_type: z.boolean().optional().describe("Set to true to query all token types received; defaults to the object's Token type '0x2::payment::CoinWrapper<TOKEN>' (Coins wrapper sent via Payment object). If the object has no Token type, the call will fail."),
cursor: z.union([z.string(), z.null()]).optional().describe("Pagination cursor from previous page"),
limit: z.union([z.number(), z.null()]).optional().describe("Max records per page"),
no_cache: z.boolean().optional().describe("Set to true to bypass cache and fetch fresh on-chain data"),
network: EntrypointSchema.optional(),
}).describe("Query objects (Payments, Tokens, NFTs) received by a specific object. Use to track incoming payments or items sent to an on-chain object. Supports pagination and all_type filter. Returns: ReceivedBalance | ReceivedNormal[]"),
]),
]));
async function handleOnchainOperations(args) {
try {
if (typeof args === 'object' && args !== null && args.description && !args.operation_type) {
const hasWrappedParams = typeof args.description === 'string' &&
(args.description.includes('operation_type') || args.description.includes('data'));
if (hasWrappedParams) {
throw new Error("Invalid parameter structure. Parameters should NOT be wrapped in 'description' field.\n" +
"Correct format: { operation_type: '...', data: {...} }\n" +
"Incorrect format: { description: '{ operation_type: ... }' }");
}
}
const validated = strictParse(OnchainOperationsSchema, args, "onchain_operations input");
const env = getEnvConfig(validated.env);
switch (validated.operation_type) {
case "service": {
const callService = new CallService(validated.data);
const result = validated.submission
? await callService.call_with_submission(env, await transformSubmission(validated.submission))
: await callService.call(env);
return handleCallResult(result);
}
case "machine": {
if (validated.data.node && typeof validated.data.node === 'object' && 'json_or_markdown_file' in validated.data.node) {
const filePathStr = validated.data.node.json_or_markdown_file;
let fileContent;
try {
const filePath = resolve(filePathStr);
fileContent = readFileSync(filePath, 'utf-8');
}
catch (e) {
throw new Error(`Failed to read file '${filePathStr}': ${e.message}`);
}
const parseResult = parseMachineNodesFromText(fileContent);
if (!parseResult.success) {
const errorMessages = formatMachineNodeErrors(parseResult.errors);
throw new Error(`Machine node validation failed for file '${filePathStr}':\n\n${errorMessages}`);
}
if (!parseResult.data) {
throw new Error(`No nodes found in file '${filePathStr}'`);
}
validated.data.node = {
op: "set",
nodes: parseResult.data,
bReplace: true
};
}
const callMachine = new CallMachine(validated.data);
const result = validated.submission
? await callMachine.call_with_submission(env, await transformSubmission(validated.submission))
: await callMachine.call(env);
return handleCallResult(result);
}
case "progress": {
const callProgress = new CallProgress(validated.data);
const result = validated.submission
? await callProgress.call_with_submission(env, await transformSubmission(validated.submission))
: await callProgress.call(env);
return handleCallResult(result);
}
case "repository": {
const callRepository = new CallRepository(validated.data);
const result = validated.submission
? await callRepository.call_with_submission(env, await transformSubmission(validated.submission))
: await callRepository.call(env);
return handleCallResult(result);
}
case "arbitration": {
const callArbitration = new CallArbitration(validated.data);
const result = validated.submission
? await callArbitration.call_with_submission(env, await transformSubmission(validated.submission))
: await callArbitration.call(env);
return handleCallResult(result);
}
case "contact": {
const callContact = new CallContact(validated.data);
const result = validated.submission
? await callContact.call_with_submission(env, await transformSubmission(validated.submission))
: await callContact.call(env);
return handleCallResult(result);
}
case "treasury": {
const callTreasury = new CallTreasury(validated.data);
const result = validated.submission
? await callTreasury.call_with_submission(env, await transformSubmission(validated.submission))
: await callTreasury.call(env);
return handleCallResult(result);
}
case "reward": {
const transformedData = transformRewardData(validated.data);
const callReward = new CallReward(transformedData);
const result = validated.submission
? await callReward.call_with_submission(env, await transformSubmission(validated.submission))
: await callReward.call(env);
return handleCallResult(result);
}
case "allocation": {
const callAllocation = new CallAllocation(validated.data);
const result = validated.submission
? await callAllocation.call_with_submission(env, await transformSubmission(validated.submission))
: await callAllocation.call(env);
return handleCallResult(result);
}
case "permission": {
const callPermission = new CallPermission(validated.data);
const result = await callPermission.call(env);
return handleCallResult(result);
}
case "guard": {
const validatedData = validated.data;
if (validatedData.root?.type === "file") {
const filePath = resolve(validatedData.root.file_path);
let fileContent;
try {
fileContent = readFileSync(filePath, 'utf-8');
}
catch (e) {
throw new Error(`Failed to read Guard definition file '${validatedData.root.file_path}': ${e.message}`);
}
const parseResult = parseGuardFile(fileContent, validatedData.root.format);
if (!parseResult.success) {
const errorMessages = formatGuardParseErrors(parseResult.errors);
throw new Error(`Guard validation failed for file '${validatedData.root.file_path}':\n\n${errorMessages}`);
}
const fileData = parseResult.data;
validatedData.root = { type: "node", node: fileData.root };
if (fileData.table !== undefined && validatedData.table === undefined) {
validatedData.table = fileData.table;
}
if (fileData.description !== undefined && validatedData.description === undefined) {
validatedData.description = fileData.description;
}
if (fileData.rely !== undefined && validatedData.rely === undefined) {
validatedData.rely = fileData.rely;
}
if (fileData.namedNew !== undefined && validatedData.namedNew === undefined) {
validatedData.namedNew = fileData.namedNew;
}
}
if (validatedData.root && typeof validatedData.root === 'object') {
const rootObj = validatedData.root;
if (rootObj.type === 'node' && rootObj.node) {
validatedData.root = rootObj.node;
}
}
if (!validatedData.table || validatedData