UNPKG

@nuwa-ai/cap-kit

Version:
301 lines (298 loc) 9.19 kB
// src/index.ts import { RoochClient, Transaction, Args } from "@roochnetwork/rooch-sdk"; import { DidAccountSigner } from "@nuwa-ai/identity-kit"; import * as yaml from "js-yaml"; // src/client.ts import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp"; import { DIDAuth } from "@nuwa-ai/identity-kit"; import { experimental_createMCPClient as createMCPClient } from "ai"; var buildClient = async (mcpUrl, signer) => { const keyId = (await signer.listKeyIds())[0]; const payload = { operation: "mcp-json-rpc", params: { body: {} } }; const signedObject = await DIDAuth.v1.createSignature(payload, signer, keyId); const authHeader = DIDAuth.v1.toAuthorizationHeader(signedObject); const transport = new StreamableHTTPClientTransport( new URL(mcpUrl), { requestInit: { headers: { Authorization: authHeader } } } ); return await createMCPClient({ transport }); }; // src/type.ts import { z } from "zod"; var CapMcpServerConfigSchema = z.object({ url: z.string(), transport: z.enum(["httpStream", "sse"]) }); var CapModelSchema = z.object({ id: z.string(), name: z.string(), slug: z.string(), providerName: z.string(), providerSlug: z.string(), description: z.string(), contextLength: z.number(), pricing: z.object({ input_per_million_tokens: z.number(), output_per_million_tokens: z.number(), request_per_k_requests: z.number(), image_per_k_images: z.number(), web_search_per_k_searches: z.number() }), supported_inputs: z.array(z.string()), supported_parameters: z.array(z.string()) }); var CapPromptSchema = z.object({ value: z.string(), suggestions: z.array(z.string()).optional() }); var CapIDSchema = z.object({ id: z.string(), authorDID: z.string(), idName: z.string() }); var CapCoreSchema = z.object({ prompt: CapPromptSchema, model: CapModelSchema, mcpServers: z.record(z.string(), CapMcpServerConfigSchema) }); var CapThumbnailSchema = z.object({ type: z.enum(["file", "url"]), file: z.string().optional(), url: z.string().optional() }).nullable(); var CapMetadataSchema = z.object({ displayName: z.string(), description: z.string(), tags: z.array(z.string()), submittedAt: z.number(), homepage: z.string().optional(), repository: z.string().optional(), thumbnail: CapThumbnailSchema }); var CapSchema = CapIDSchema.extend({ core: CapCoreSchema, metadata: CapMetadataSchema }); var ResultCapMetadataSchema = z.object({ id: z.string(), cid: z.string(), name: z.string(), version: z.string(), displayName: z.string(), description: z.string(), tags: z.array(z.string()), submittedAt: z.number(), homepage: z.string().optional(), repository: z.string().optional(), thumbnail: CapThumbnailSchema }); // src/index.ts var CapKit = class { constructor(option) { this.roochClient = new RoochClient({ url: option.roochUrl }); this.contractAddress = option.contractAddress; this.mcpUrl = option.mcpUrl; this.signer = option.signer; } async queryCapWithID(id, cid) { const client = await buildClient(this.mcpUrl, this.signer); try { const tools = await client.tools(); const queryWithCID = tools.queryWithID; if (!queryWithCID) { throw new Error("Query with id tool not available on MCP server"); } const result = await queryWithCID.execute({ id, cid }, { toolCallId: "queryWithID", 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; } finally { await client.close(); } } async queryWithName(name, tags, page, size) { const client = await buildClient(this.mcpUrl, this.signer); try { const tools = await client.tools(); const queryWithName = tools.queryWithName; if (!queryWithName) { throw new Error("query tool not available on MCP server"); } const result = await queryWithName.execute({ name, tags, page, pageSize: size }, { toolCallId: "query-cap", 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: page || 0, pageSize: size || 50, items: [] } }; } if (queryResult.code !== 200) { throw new Error(`query failed: ${queryResult.error || "Unknown error"}`); } const transformedItems = queryResult.data.items.map((item) => { const thumbnailType = JSON.parse(item.thumbnail); return { id: item.id, cid: item.cid, name: item.name, version: item.version, displayName: item.display_name, description: item.description, tags: item.tags, submittedAt: item.submitted_at, homepage: item.homepage, repository: item.repository, thumbnail: thumbnailType }; }); return { code: queryResult.code, data: { totalItems: queryResult.data.totalItems, page: queryResult.data.page, pageSize: queryResult.data.pageSize, items: transformedItems } }; } finally { await client.close(); } } async downloadCapWithID(id, format) { const result = await this.queryCapWithID(id); if (result.code === 200) { return this.downloadCapWithCID(result.data.cid, format); } else { throw new Error("Invalid Cap ID"); } } async downloadCapWithCID(cid, format) { const client = await buildClient(this.mcpUrl, this.signer); try { const tools = await client.tools(); const downloadFile = tools.downloadFile; if (!downloadFile) { throw new Error("downloadFile tool not available on MCP server"); } const result = await downloadFile.execute({ cid, dataFormat: format }, { 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"}`); } return yaml.load(downloadResult.data.fileData); } finally { await client.close(); } } 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 cid = await this.uploadToIPFS(cap.id, acpContent, this.signer); const result = await this.registerOnChain(cap.idName, cid, this.signer); if (result.execution_info.status.type !== "executed") { throw new Error("unknown error"); } return cid; } async uploadToIPFS(name, content, signer) { const client = await buildClient(this.mcpUrl, signer); try { const tools = await client.tools(); const uploadTool = tools.uploadFile; if (!uploadTool) { throw new Error("uploadFile tool not available on MCP server"); } const fileData = Buffer.from(content, "utf8").toString("base64"); const fileName = `${name}.cap.yaml`; const result = await uploadTool.execute({ fileName, fileData }, { 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 || !uploadData.ipfsCid) { throw new Error(`Upload failed: ${uploadResult.error || "Unknown error"}`); } return uploadData.ipfsCid; } finally { await client.close(); } } async registerOnChain(name, cid, signer) { 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: 5e8 }); return await this.roochClient.signAndExecuteTransaction({ transaction, signer: chainSigner }); } }; export { CapCoreSchema, CapIDSchema, CapKit, CapMcpServerConfigSchema, CapMetadataSchema, CapModelSchema, CapPromptSchema, CapSchema, CapThumbnailSchema, ResultCapMetadataSchema }; //# sourceMappingURL=index.js.map