@nuwa-ai/cap-kit
Version:
678 lines (674 loc) • 20.2 kB
JavaScript
// 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