Skip to main content
Glama

bnbchain-mcp

Official
by bnb-chain
object.ts10.8 kB
import { existsSync, fstatSync, readFileSync, statSync, writeFileSync } from "fs" import path from "path" import { bytesFromBase64, Long, RedundancyType, VisibilityType } from "@bnb-chain/greenfield-js-sdk" import { ObjectInfo } from "@bnb-chain/greenfield-js-sdk/dist/esm/types/sp/Common" import { NodeAdapterReedSolomon } from "@bnb-chain/reed-solomon/node.adapter" import type { Hex } from "viem" import Logger from "@/utils/logger" import { getMimeType, response, type ApiResponse } from "../util" import { getAccount } from "./account" import { createBucket } from "./bucket" import { getClient } from "./client" import { executeTransaction } from "./common" import { selectSp } from "./sp" // Define file response type interface FileData { bucketName: string objectName: string } /** * Create a file object */ const createFileObject = (filePath: string) => { const stats = statSync(filePath) const fileSize = stats.size const fileType = getMimeType(filePath) || "unknown" return { name: path.basename(filePath), type: fileType, size: fileSize, content: readFileSync(filePath) } } /** * Create a file in Greenfield */ export const createFile = async ( network: "testnet" | "mainnet", { privateKey, filePath, bucketName }: { privateKey: Hex filePath: string bucketName?: string } ): Promise<ApiResponse<FileData>> => { try { // Ensure the file exists const objectName = path.basename(filePath) const fileObj = createFileObject(filePath) if (fileObj.size > 1024 * 1024 * 1024) { return response.fail("File size must be less than 1GB") } const rs = new NodeAdapterReedSolomon() const expectCheckSums = await rs.encodeInSubWorker( Uint8Array.from(fileObj.content) ) Logger.debug( `File info: ${filePath}, size: ${fileObj.size} bytes, type: ${fileObj.type}, expectCheckSums: ${expectCheckSums}` ) // Get or create bucket const bucketNameRes = await createBucket(network, { privateKey, bucketName }) if (bucketNameRes.status === "error") { return response.fail( bucketNameRes.message || "Unknown bucket creation error" ) } const _bucketName = bucketNameRes.data!.bucketName Logger.debug(`Using bucket: ${_bucketName}, for object: ${objectName}`) // Get client and account const client = getClient(network) const account = await getAccount(network, privateKey) // Create object transaction const createObjectTx = await client.object.createObject({ bucketName: _bucketName, objectName: objectName, creator: account.address, visibility: VisibilityType.VISIBILITY_TYPE_PRIVATE, contentType: fileObj.type as string, redundancyType: RedundancyType.REDUNDANCY_EC_TYPE, payloadSize: Long.fromInt(fileObj.content.byteLength), expectChecksums: expectCheckSums.map((x) => bytesFromBase64(x)) }) Logger.debug(`Created object transaction for file: ${objectName}`) // Execute transaction const txResult = await executeTransaction<void>( createObjectTx, account, privateKey, "Create object", `${objectName} in bucket ${_bucketName}` ) if (txResult.status !== "success") { return response.fail(`Failed to create object: ${txResult.message}`) } // Upload file content const uploadRes = await client.object.uploadObject( { bucketName: _bucketName, objectName: objectName, body: fileObj, txnHash: txResult.txHash || "" }, { type: "ECDSA", privateKey: privateKey } ) Logger.debug(`Upload result: ${JSON.stringify(uploadRes)}`) if (uploadRes.code === 0) { return response.success({ bucketName: _bucketName, objectName }) } else { return response.fail(`Create file failed: ${JSON.stringify(uploadRes)}`) } } catch (error) { Logger.error(`Create file operation failed: ${error}`) return response.fail(`Create file operation failed: ${error}`) } } /** * Create folder in Greenfield */ export const createFolder = async ( network: "testnet" | "mainnet", { privateKey, folderName, bucketName }: { privateKey: Hex folderName?: string bucketName?: string } ): Promise<ApiResponse<{ bucketName: string; folderName: string }>> => { try { const _folderName = folderName || "created-by-bnbchain-mcp" // Get or create bucket const bucketNameRes = await createBucket(network, { privateKey, bucketName }) if (bucketNameRes.status === "error") { return response.fail( bucketNameRes.message || "Unknown bucket creation error" ) } const _bucketName = bucketNameRes.data!.bucketName // Get client and account const client = getClient(network) const account = await getAccount(network, privateKey) // Ensure folder name ends with a slash const formattedFolderName = _folderName.endsWith("/") ? _folderName : `${_folderName}/` // Create folder transaction const createObjectTx = await client.object.createFolder({ bucketName: _bucketName, objectName: formattedFolderName, creator: account.address, visibility: VisibilityType.VISIBILITY_TYPE_PRIVATE, redundancyType: RedundancyType.REDUNDANCY_EC_TYPE }) // Execute transaction const txResult = await executeTransaction<void>( createObjectTx, account, privateKey, "Create folder", `${formattedFolderName} in bucket ${_bucketName}` ) // If successful, return folder information if (txResult.status === "success") { return response.success({ bucketName: _bucketName, folderName: formattedFolderName }) } // If transaction failed, return the error return response.fail(txResult.message || "Unknown create folder error") } catch (error) { Logger.error(`Create folder operation failed: ${error}`) return response.fail(`Create folder operation failed: ${error}`) } } export const getObjectInfo = async ( network: "testnet" | "mainnet", { bucketName, objectName }: { bucketName: string objectName: string } ): Promise<ApiResponse<ObjectInfo>> => { try { const client = getClient(network) const res = await client.object.headObject(bucketName, objectName) return response.success(res.objectInfo as {} as ObjectInfo) } catch (error) { Logger.error(`Get object info operation failed: ${error}`) return response.fail(`Get object info operation failed: ${error}`) } } /** * Delete an object in Greenfield */ export const deleteObject = async ( network: "testnet" | "mainnet", { privateKey, bucketName, objectName }: { privateKey: Hex bucketName: string objectName: string } ): Promise<ApiResponse<void>> => { try { const client = getClient(network) const account = await getAccount(network, privateKey) // Check if object exists first try { await client.object.headObject(bucketName, objectName) } catch (error) { Logger.error( `Object ${objectName} does not exist in bucket ${bucketName}` ) return response.fail( `Object ${objectName} does not exist in bucket ${bucketName}` ) } // Create delete transaction const tx = await client.object.deleteObject({ bucketName, objectName, operator: account.address }) const txResult = await executeTransaction<void>( tx, account, privateKey, "Delete object", `${objectName} from bucket ${bucketName}` ) // Simply return the transaction result return txResult } catch (error) { Logger.error(`Delete object operation failed: ${error}`) return response.fail(`Delete object operation failed: ${error}`) } } /** * List objects in a bucket in Greenfield * @param network Greenfield network (testnet or mainnet) * @param bucketName Name of the bucket to list objects from * @returns List of objects with their details */ export const listObjects = async ( network: "testnet" | "mainnet", bucketName: string ): Promise< ApiResponse<{ objects: Array<{ objectName: string; createAt: number }> }> > => { try { const client = getClient(network) // Check if bucket exists first try { await client.bucket.headBucket(bucketName) } catch (error) { Logger.error(`Bucket ${bucketName} does not exist`) return response.fail(`Bucket ${bucketName} does not exist`) } const sp = await selectSp(network) const objectsRes = await client.object.listObjects({ bucketName, endpoint: sp.endpoint }) if (objectsRes.code !== 0) { return response.fail( `Failed to list objects: ${JSON.stringify(objectsRes)}` ) } const res = objectsRes.body?.GfSpListObjectsByBucketNameResponse?.Objects || [] const objects = res.map((it) => ({ objectName: it.ObjectInfo.ObjectName, createAt: it.ObjectInfo.CreateAt })) return response.success({ objects }) } catch (error) { Logger.error(`List objects operation failed: ${error}`) return response.fail(`List objects operation failed: ${error}`) } } export const downloadObject = async ( network: "testnet" | "mainnet", { privateKey, bucketName, objectName, targetPath }: { privateKey: Hex bucketName: string objectName: string targetPath?: string } ): Promise<ApiResponse<{ file: string }>> => { try { let filePath = "" if (!targetPath || !existsSync(targetPath)) { Logger.debug( `Target path ${targetPath} does not exist, using current directory` ) // add tmp prefix to avoid file name conflict filePath = path.join(process.cwd(), "tmp-" + objectName) } else { filePath = path.join(targetPath, objectName) } const client = getClient(network) const res = await client.object.getObject( { bucketName, objectName }, { type: "ECDSA", privateKey: privateKey } ) if (res.code !== 0) { return response.fail(`Failed to download object: ${JSON.stringify(res)}`) } // blob to file const file = res.body as unknown as Blob if (!file) { return response.fail("Object is not a file") } const buffer = await file.arrayBuffer() writeFileSync(filePath, Buffer.from(buffer)) return response.success({ file: filePath }) } catch (error) { Logger.error(`Download object operation failed: ${error}`) return response.fail(`Download object operation failed: ${error}`) } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bnb-chain/bnbchain-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server