UNPKG

@nuwa-ai/cap-kit

Version:
678 lines (674 loc) 20.2 kB
// src/type.ts import { z } from "zod/v3"; var CapAuthorDIDSchema = z.string().startsWith("did:"); var CapIDNameSchema = z.string().regex(/^[a-z0-9_]+$/, "Name must contain only lowercase letters, numbers, and underscores").min(6, "Name must be at least 6 characters").max(20, "Name must be at most 20 characters"); var CapArtifactSchema = z.object({ srcUrl: z.string().url("Must be a valid URL") }); var CapIDSchema = z.object({ authorDID: CapAuthorDIDSchema, idName: CapIDNameSchema }).transform((data) => ({ ...data, id: `${data.authorDID}:${data.idName}` })); var CapModelSchema = z.object({ customGatewayUrl: z.string().url("Must be a valid URL").optional(), providerId: z.enum([ "openai_chat_completion", "openai_responses", "anthropic", "google", "openrouter", "xai", "groq", "togetherai", "azure", "deepseek", "mistral" ]), modelId: z.string().max(50, "Model ID must be at most 50 characters").refine((id) => id.length > 0, "Model ID is required"), parameters: z.record(z.string(), z.any()).optional(), supportedInputs: z.array(z.enum(["text", "image", "file", "audio"])).min(1).refine((inputs) => inputs.includes("text"), "text input is required"), contextLength: z.number().min(1e3, "Please enter a valid context length") }); var CapPromptSuggestionSchema = z.string().max(100, "Each suggestion must be at most 50 characters"); var CapPromptSchema = z.object({ value: z.string(), suggestions: z.array(CapPromptSuggestionSchema).optional() }); var CapMcpServerSchema = z.string().url("Must be a valid URL"); var CapCoreSchema = z.object({ prompt: CapPromptSchema, model: CapModelSchema, mcpServers: z.record(z.string(), CapMcpServerSchema), artifact: CapArtifactSchema.optional() }); var CapThumbnailSchema = z.string().url("Must be a valid URL").optional(); var CapMetadataSchema = z.object({ displayName: z.string().min(1, "Display name is required").max(50, "Display name too long"), description: z.string().min(10, "Description must be at least 10 characters").max(150, "Description too long"), introduction: z.string().min(10, "Introduction must be at least 10 characters").max(5e3, "Introduction too long"), tags: z.array(z.string()).min(1, "At least one tag is required"), homepage: z.string().url("Must be a valid URL").optional(), repository: z.string().url("Must be a valid URL").optional(), thumbnail: CapThumbnailSchema }); var CapSchema = CapIDSchema.and( z.object({ core: CapCoreSchema, metadata: CapMetadataSchema }) ); var CapStatsSchema = z.object({ capId: z.string(), downloads: z.number(), ratingCount: z.number(), averageRating: z.number(), favorites: z.number(), userRating: z.number().optional() }); var ResultCapMetadataSchema = z.object({ id: z.string(), cid: z.string(), name: z.string(), version: z.string(), displayName: z.string(), description: z.string(), introduction: z.string(), timestamp: z.string(), tags: z.array(z.string()), homepage: z.string().optional(), repository: z.string().optional(), thumbnail: CapThumbnailSchema, stats: CapStatsSchema }); var RatingDistribution = z.object({ rating: z.number(), count: z.number() }); // src/mcp.ts import { RoochClient } from "@roochnetwork/rooch-sdk"; import * as yaml from "js-yaml"; // src/client.ts import { createMcpClient } from "@nuwa-ai/payment-kit"; var buildClient = async (mcpUrl, env) => { return createMcpClient({ baseUrl: mcpUrl, env, maxAmount: BigInt(1e7), debug: false, forceMode: "payment" }); }; // src/mcp.ts var CapKitMcp = class { constructor(option) { this.isInitializing = false; this.roochClient = new RoochClient({ url: option.roochUrl }); this.contractAddress = option.contractAddress; this.mcpUrl = option.mcpUrl; this.env = option.env; } async getTools() { if (!this.mcpTools) { const client = await this.getMcpClient(); this.mcpTools = await client.tools(); } return this.mcpTools; } async getMcpClient() { if (this.mcpClient) { return this.mcpClient; } while (this.isInitializing) { await new Promise((resolve) => setTimeout(resolve, 50)); if (this.mcpClient) { return this.mcpClient; } } this.isInitializing = true; try { const client = await buildClient(this.mcpUrl, this.env); await client.listTools(); this.mcpClient = client; return client; } catch (error) { console.error("Failed to initialize MCP client:", error); throw error; } finally { this.isInitializing = false; } } async mcpClose() { this.mcpClient?.close(); } async queryByID(id) { try { const tools = await this.getTools(); const queryCapByID = tools.queryCapByID; if (!queryCapByID) { throw new Error("Query Cap by id tool not available on MCP server"); } const result = await queryCapByID.execute(id, { toolCallId: "queryCapByID", messages: [] }); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } const queryResult = JSON.parse(result.content[0].text); if (queryResult.code !== 200 && queryResult.code !== 404) { throw new Error(`Query with id failed: ${queryResult.error || "Unknown error"}`); } return queryResult; } catch (e) { throw e; } } async queryByName(name, opt) { try { const tools = await this.getTools(); const queryCapByName = tools.queryCapByName; if (!queryCapByName) { throw new Error("query tool not available on MCP server"); } const result = await queryCapByName.execute( { name, tags: opt?.tags, page: opt?.page, pageSize: opt?.size, sortBy: opt?.sortBy, sortOrder: opt?.sortOrder }, { toolCallId: "query-cap-by-name", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } const queryResult = JSON.parse(result.content[0].text); if (queryResult.code === 404) { return { code: 200, data: { totalItems: 0, page: opt?.page || 0, pageSize: opt?.size || 50, items: [] } }; } if (queryResult.code !== 200) { throw new Error(`query failed: ${queryResult.error || "Unknown error"}`); } return { code: queryResult.code, data: { totalItems: queryResult.data.totalItems, page: queryResult.data.page, pageSize: queryResult.data.pageSize, items: queryResult.data.items } }; } catch (e) { throw e; } } async queryMyFavorite(page, size) { try { const tools = await this.getTools(); const queryMyFavoriteCaps = tools.queryMyFavoriteCap; if (!queryMyFavoriteCaps) { throw new Error("queryMyFavoriteCaps tool not available on MCP server"); } const result = await queryMyFavoriteCaps.execute( { page, pageSize: size }, { toolCallId: "queryMyFavoriteCaps", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } const queryResult = JSON.parse(result.content[0].text); if (queryResult.code !== 200) { throw new Error(`queryMyFavoriteCaps failed: ${queryResult.error || "Unknown error"}`); } return { code: queryResult.code, data: { totalItems: queryResult.data.totalItems, page: queryResult.data.page, pageSize: queryResult.data.pageSize, items: queryResult.data.items } }; } catch (e) { throw e; } } async queryCapStats(capId) { try { const tools = await this.getTools(); const queryCapStats = tools.queryCapStats; if (!queryCapStats) { throw new Error("queryCapStats tool not available on MCP server"); } const result = await queryCapStats.execute( { capId }, { toolCallId: "queryCapStats", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } const queryResult = JSON.parse(result.content[0].text); if (queryResult.code !== 200) { throw new Error(`query cap stats failed: ${queryResult.error || "Unknown error"}`); } return queryResult; } catch (e) { throw e; } } async rateCap(capId, rating) { if (rating < 1 || rating > 5 || !Number.isInteger(rating)) { throw new Error("Rating must be an integer between 1 and 5"); } try { const tools = await this.getTools(); const rateCap = tools.rateCap; if (!rateCap) { throw new Error("rateCap tool not available on MCP server"); } const result = await rateCap.execute( { capId, rating }, { toolCallId: "rateCap", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } return { code: 200, data: true }; } catch (e) { throw e; } } async queryCapRatingDistribution(capId) { try { const tools = await this.getTools(); const queryCapRatingDistribution = tools.queryCapRatingDistribution; if (!queryCapRatingDistribution) { throw new Error("queryCapRatingDistribution tool not available on MCP server"); } const result = await queryCapRatingDistribution.execute( { capId }, { toolCallId: "queryCapRatingDistribution", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } const content = result.content?.[0]?.text; if (content) { try { const resut = JSON.parse(content); return { code: 200, data: resut.data.distribution }; } catch (parseError) { throw new Error("Failed to parse rating distribution response"); } } throw new Error("No data received"); } catch (e) { throw e; } } async install(capId, action) { try { const tools = await this.getTools(); const favoriteCap = tools.favoriteCap; if (!favoriteCap) { throw new Error("favoriteCap tool not available on MCP server"); } const result = await favoriteCap.execute( { capId, action: action === "isInstall" ? "isFavorite" : action }, { toolCallId: "favoriteCap", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } if (action === "isInstall") { const data = JSON.parse(result.content[0].text); return { code: 200, data: data.isFavorite }; } else { return { code: 200, data: true }; } } catch (e) { throw e; } } async updateEnableCap(capId, action) { try { const tools = await this.getTools(); const updateEnableCap = tools.updateEnableCap; if (!updateEnableCap) { throw new Error("updateEnableCap tool not available on MCP server"); } const result = await updateEnableCap.execute( { capId, action }, { toolCallId: "updateEnableCap", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } return { code: 200, data: true }; } catch (e) { throw e; } } // async downloadByID(id: string, format?: "base64" | "utf8"): Promise<Cap> { // const result = await this.queryByID({ id: id }); // // if (result.code === 200) { // return this.downloadByCID(result.data!.cid, format); // } else { // throw new Error("Invalid Cap ID"); // } // } async downloadByID(id) { try { const tools = await this.getTools(); const downloadCap = tools.downloadCap; if (!downloadCap) { throw new Error("downloadCap tool not available on MCP server"); } const result = await downloadCap.execute( { id }, { toolCallId: "download-cap", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } const downloadResult = JSON.parse(result.content[0].text); if (downloadResult.code !== 200) { throw new Error(`Download failed: ${downloadResult.error || "Unknown error"}`); } const data = downloadResult.data.rawData; const utf8 = new TextDecoder().decode(Uint8Array.from(atob(data), (c) => c.charCodeAt(0))); return yaml.load(utf8); } catch (e) { throw e; } } // async registerCap(cap: Cap) { // // len > 6 && len < 20, only contain a-z, A-Z, 0-9, _ // if (!/^[a-zA-Z0-9_]{6,20}$/.test(cap.idName)) { // throw new Error( // "Name must be between 6 and 20 characters and only contain a-z, A-Z, 0-9, _", // ); // } // // // 1. Create ACP (Agent Capability Package) file // const acpContent = yaml.dump(cap); // // // 2. Upload ACP file to IPFS using nuwa-cap-store MCP // const cid = await this.uploadToIPFS(cap.id, acpContent); // // // 3. Call Move contract to register the capability // const result = await this.registerOnChain(cap.idName, cid, this.env.keyManager); // // if (result.execution_info.status.type !== "executed") { // throw new Error("unknown error"); // } // // return cid; // } async registerCap(cap) { if (!/^[a-zA-Z0-9_]{6,20}$/.test(cap.idName)) { throw new Error("Name must be between 6 and 20 characters and only contain a-z, A-Z, 0-9, _"); } const acpContent = yaml.dump(cap); const encoder = new TextEncoder(); const bytes = encoder.encode(acpContent); const rawData = btoa(String.fromCharCode(...bytes)); const tools = await this.getTools(); const uploadCap = tools.uploadCap; const result = await uploadCap.execute( { cap: rawData }, { toolCallId: "upload-cap", messages: [] } ); if (result.isError) { throw new Error(result.content?.[0]?.text || "Unknown error"); } const uploadResult = JSON.parse(result.content[0].text); const uploadData = uploadResult.data; if (uploadResult.code !== 200) { throw new Error(`Upload cap failed: ${uploadResult.error || "Unknown error"}`); } return cap.id; } // private async uploadToIPFS( // name: string, // content: string, // ): Promise<string> { // // try { // // Get tools from MCP server // const tools = await this.getTools() // const uploadCap = tools.uploadCap; // // if (!uploadCap) { // throw new Error("uploadCap tool not available on MCP server"); // } // // // Convert content to base64 (UTF-8 safe) // const encoder = new TextEncoder(); // const bytes = encoder.encode(content); // const fileData = btoa(String.fromCharCode(...bytes)); // const fileName = `${name}.cap.yaml`; // // // Upload file to IPFS // const result = await uploadCap.execute( // { // fileName, // fileData, // }, // { // toolCallId: "upload-cap", // messages: [], // }, // ); // // if (result.isError) { // throw new Error((result.content as any)?.[0]?.text || "Unknown error"); // } // // const uploadResult = JSON.parse((result.content as any)[0].text); // const uploadData = uploadResult.data; // // if (uploadResult.code !== 200 || !uploadData.ipfsCid) { // throw new Error( // `Upload cap failed: ${uploadResult.error || "Unknown error"}`, // ); // } // // return uploadData.ipfsCid; // } catch (e) { // throw e // } // } // // private async registerOnChain( // name: string, // cid: string, // signer: SignerInterface, // ) { // const chainSigner = await DidAccountSigner.create(signer); // const transaction = new Transaction(); // transaction.callFunction({ // target: `${this.contractAddress}::acp_registry::register`, // typeArgs: [], // args: [Args.string(name), Args.string(cid)], // maxGas: 500000000, // }); // // return await this.roochClient.signAndExecuteTransaction({ // transaction, // signer: chainSigner, // }); // } }; // src/restful.ts import * as yaml2 from "js-yaml"; var CapKitRestful = class { constructor(apiUrl) { this.apiUrl = apiUrl; } async queryCap(capId) { const response = await fetch(`${this.apiUrl}/cap/${capId}`); return await response.json(); } async queryCaps(name, tags, page, pageSize, sortBy, sortOrder) { const params = new URLSearchParams(); if (name) params.append("name", name); if (tags && tags.length > 0) { tags.forEach((tag) => params.append("tags", tag)); } if (page !== void 0) params.append("page", page.toString()); if (pageSize !== void 0) params.append("pageSize", pageSize.toString()); if (sortBy) params.append("sortBy", sortBy); if (sortOrder) params.append("sortOrder", sortOrder); const url = params.toString() ? `${this.apiUrl}/caps?${params.toString()}` : `${this.apiUrl}/caps`; const response = await fetch(url); return await response.json(); } async queryUserInstalledCaps(did, page, pageSize) { const params = new URLSearchParams(); if (did) params.append("did", did); if (page !== void 0) params.append("page", page.toString()); if (pageSize !== void 0) params.append("pageSize", pageSize.toString()); const url = `${this.apiUrl}/caps/installed?${params.toString()}`; const response = await fetch(url); return await response.json(); } async downloadCap(capId) { const response = await fetch(`${this.apiUrl}/cap/download/${capId}`); const result = await response.json(); const utf8 = new TextDecoder().decode( Uint8Array.from(atob(result.data.raw_data), (c) => c.charCodeAt(0)) ); return yaml2.load(utf8); } async downloadCaps(capIDs) { if (capIDs.length === 0) { return { successful: {}, failed: {}, summary: { successful: 0, failed: 0, total: 0 } }; } const response = await fetch(`${this.apiUrl}/caps/download`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ids: capIDs }) }); const result = await response.json(); const successful = result.data.successful; const formatSuccessful = {}; for (const [id, value] of Object.entries(successful)) { const utf8 = new TextDecoder().decode( Uint8Array.from(atob(value), (c) => c.charCodeAt(0)) ); const cap = yaml2.load(utf8); formatSuccessful[id] = cap; } return { ...result.data, successful: formatSuccessful }; } }; export { CapArtifactSchema, CapAuthorDIDSchema, CapCoreSchema, CapIDNameSchema, CapIDSchema, CapKitMcp, CapKitRestful, CapMcpServerSchema, CapMetadataSchema, CapModelSchema, CapPromptSchema, CapPromptSuggestionSchema, CapSchema, CapStatsSchema, CapThumbnailSchema, RatingDistribution, ResultCapMetadataSchema }; //# sourceMappingURL=index.js.map