From 346e5b83c87d3a1451b2d6f43bb50d20df5eb765 Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Sat, 13 Dec 2025 15:05:28 -0500 Subject: [PATCH 1/2] MCP - generate_txt_file --- lib/artist/createKnowledgeBase.ts | 6 +- lib/arweave/uploadTextToArweave.ts | 13 ++++ lib/mcp/tools/files/index.ts | 13 ++++ .../registerCreateKnowledgeBaseTool.ts | 1 + .../files/registerGenerateTxtFileTool.ts | 60 +++++++++++++++++ lib/mcp/tools/index.ts | 12 ++-- lib/txt/generateAndStoreTxtFile.ts | 67 +++++++++++++++++++ 7 files changed, 162 insertions(+), 10 deletions(-) create mode 100644 lib/arweave/uploadTextToArweave.ts create mode 100644 lib/mcp/tools/files/index.ts rename lib/mcp/tools/{ => files}/registerCreateKnowledgeBaseTool.ts (99%) create mode 100644 lib/mcp/tools/files/registerGenerateTxtFileTool.ts create mode 100644 lib/txt/generateAndStoreTxtFile.ts diff --git a/lib/artist/createKnowledgeBase.ts b/lib/artist/createKnowledgeBase.ts index 145614dcf..54e29f3af 100644 --- a/lib/artist/createKnowledgeBase.ts +++ b/lib/artist/createKnowledgeBase.ts @@ -1,4 +1,4 @@ -import { uploadToArweave } from "@/lib/arweave/uploadToArweave"; +import { uploadTextToArweave } from "@/lib/arweave/uploadTextToArweave"; import { arweaveGatewayUrl } from "@/lib/arweave/arweaveGatewayUrl"; import { selectAccountInfo } from "@/lib/supabase/account_info/selectAccountInfo"; import { updateAccountInfo } from "@/lib/supabase/account_info/updateAccountInfo"; @@ -22,9 +22,7 @@ export async function createKnowledgeBase( } // Upload text to Arweave - const textBuffer = Buffer.from(knowledgeBaseText, "utf-8"); - const transaction = await uploadToArweave(textBuffer, "text/plain"); - const arweaveUrl = `ar://${transaction.id}`; + const arweaveUrl = await uploadTextToArweave(knowledgeBaseText); const fetchableUrl = arweaveGatewayUrl(arweaveUrl); // Generate a name from the first line or a default name diff --git a/lib/arweave/uploadTextToArweave.ts b/lib/arweave/uploadTextToArweave.ts new file mode 100644 index 000000000..291549a4d --- /dev/null +++ b/lib/arweave/uploadTextToArweave.ts @@ -0,0 +1,13 @@ +import { uploadToArweave } from "./uploadToArweave"; + +/** + * Uploads text content to Arweave as a text/plain file. + * + * @param text - The text content to upload + * @returns The Arweave URL (ar://...) + */ +export async function uploadTextToArweave(text: string): Promise { + const textBuffer = Buffer.from(text, "utf-8"); + const transaction = await uploadToArweave(textBuffer, "text/plain"); + return `ar://${transaction.id}`; +} diff --git a/lib/mcp/tools/files/index.ts b/lib/mcp/tools/files/index.ts new file mode 100644 index 000000000..56669b752 --- /dev/null +++ b/lib/mcp/tools/files/index.ts @@ -0,0 +1,13 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { registerCreateKnowledgeBaseTool } from "./registerCreateKnowledgeBaseTool"; +import { registerGenerateTxtFileTool } from "./registerGenerateTxtFileTool"; + +/** + * Registers all file-related MCP tools on the server. + * + * @param server - The MCP server instance to register tools on. + */ +export const registerAllFileTools = (server: McpServer): void => { + registerCreateKnowledgeBaseTool(server); + registerGenerateTxtFileTool(server); +}; diff --git a/lib/mcp/tools/registerCreateKnowledgeBaseTool.ts b/lib/mcp/tools/files/registerCreateKnowledgeBaseTool.ts similarity index 99% rename from lib/mcp/tools/registerCreateKnowledgeBaseTool.ts rename to lib/mcp/tools/files/registerCreateKnowledgeBaseTool.ts index cfe182852..b086155b3 100644 --- a/lib/mcp/tools/registerCreateKnowledgeBaseTool.ts +++ b/lib/mcp/tools/files/registerCreateKnowledgeBaseTool.ts @@ -52,3 +52,4 @@ export function registerCreateKnowledgeBaseTool(server: McpServer): void { }, ); } + diff --git a/lib/mcp/tools/files/registerGenerateTxtFileTool.ts b/lib/mcp/tools/files/registerGenerateTxtFileTool.ts new file mode 100644 index 000000000..2c2eeb7d6 --- /dev/null +++ b/lib/mcp/tools/files/registerGenerateTxtFileTool.ts @@ -0,0 +1,60 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { generateAndStoreTxtFile } from "@/lib/txt/generateAndStoreTxtFile"; +import { getFetchableUrl } from "@/lib/arweave/getFetchableUrl"; +import { getToolResultSuccess } from "@/lib/mcp/getToolResultSuccess"; +import { getToolResultError } from "@/lib/mcp/getToolResultError"; + +const generateTxtFileSchema = z.object({ + contents: z.string().min(1, "Contents are required").describe("The contents of the TXT file"), +}); + +type GenerateTxtFileArgs = z.infer; + +/** + * Registers the "generate_txt_file" tool on the MCP server. + * Creates a downloadable TXT file from provided contents and stores it on Arweave. + * + * @param server - The MCP server instance to register the tool on. + */ +export function registerGenerateTxtFileTool(server: McpServer): void { + server.registerTool( + "generate_txt_file", + { + description: + "Create a downloadable TXT file from provided contents. The file will be stored onchain with Arweave and metadata will be created.", + inputSchema: generateTxtFileSchema, + }, + async (args: GenerateTxtFileArgs) => { + try { + const result = await generateAndStoreTxtFile(args.contents); + + const arweaveUrl = getFetchableUrl(result.arweave || null); + + const response = { + success: true, + arweaveUrl, + metadataArweave: result.metadataArweave ? getFetchableUrl(result.metadataArweave) : null, + message: "TXT file successfully generated and stored on Arweave.", + }; + + return getToolResultSuccess(response); + } catch (error) { + console.error("Error generating TXT file:", error); + + // Format helpful error messages based on common issues + let errorMessage = error instanceof Error ? error.message : "An unexpected error occurred"; + + if (errorMessage.includes("API key")) { + errorMessage = "API key is missing or invalid. Please check your environment variables."; + } else if (errorMessage.includes("content policy")) { + errorMessage = "Your contents may violate content policy. Please try different contents."; + } else if (errorMessage.includes("rate limit")) { + errorMessage = "Rate limit exceeded. Please try again later."; + } + + return getToolResultError(`Failed to generate TXT file. ${errorMessage}`); + } + }, + ); +} diff --git a/lib/mcp/tools/index.ts b/lib/mcp/tools/index.ts index d39463de9..a56ac2a6f 100644 --- a/lib/mcp/tools/index.ts +++ b/lib/mcp/tools/index.ts @@ -9,7 +9,7 @@ import { registerContactTeamTool } from "./registerContactTeamTool"; import { registerUpdateAccountInfoTool } from "./registerUpdateAccountInfoTool"; import { registerAllArtistSocialsTools } from "./artistSocials"; import { registerSearchWebTool } from "./registerSearchWebTool"; -import { registerCreateKnowledgeBaseTool } from "./registerCreateKnowledgeBaseTool"; +import { registerAllFileTools } from "./files"; /** * Registers all MCP tools on the server. @@ -19,14 +19,14 @@ import { registerCreateKnowledgeBaseTool } from "./registerCreateKnowledgeBaseTo */ export const registerAllTools = (server: McpServer): void => { registerAllArtistSocialsTools(server); - registerGetLocalTimeTool(server); - registerAllTaskTools(server); - registerAllImageTools(server); registerAllCatalogTools(server); + registerAllFileTools(server); + registerAllImageTools(server); registerAllSora2Tools(server); registerAllSpotifyTools(server); + registerAllTaskTools(server); registerContactTeamTool(server); - registerUpdateAccountInfoTool(server); + registerGetLocalTimeTool(server); registerSearchWebTool(server); - registerCreateKnowledgeBaseTool(server); + registerUpdateAccountInfoTool(server); }; diff --git a/lib/txt/generateAndStoreTxtFile.ts b/lib/txt/generateAndStoreTxtFile.ts new file mode 100644 index 000000000..c5b35cd88 --- /dev/null +++ b/lib/txt/generateAndStoreTxtFile.ts @@ -0,0 +1,67 @@ +import { uploadTextToArweave } from "@/lib/arweave/uploadTextToArweave"; +import uploadJsonToArweave from "@/lib/arweave/uploadJsonToArweave"; + +export interface GeneratedTxtResponse { + txt: { + base64Data: string; + mimeType: string; + }; + arweave?: string | null; + metadataArweave?: string | null; +} + +/** + * Generates and stores a TXT file by uploading it to Arweave. + * Creates both the text file and metadata JSON on Arweave. + * + * @param contents - The text contents to store + * @returns The generated TXT response with Arweave URLs + */ +export async function generateAndStoreTxtFile(contents: string): Promise { + if (!contents) { + throw new Error("Contents are required"); + } + + const mimeType = "text/plain"; + + // Upload the TXT file to Arweave + let txtFile: string | null = null; + try { + txtFile = await uploadTextToArweave(contents); + } catch (arweaveError) { + console.error("Error uploading TXT to Arweave:", arweaveError); + // Continue even if Arweave upload fails + } + + const image = "ar://EXwe2peizXKxjUMop6W-JPflC5sWyeQR1y0JiRDwUB0"; + + // Upload metadata JSON to Arweave + let metadataArweave: string | null = null; + try { + const transaction = await uploadJsonToArweave({ + image, + animation_url: txtFile || undefined, + content: { + mime: mimeType, + uri: txtFile || "", + }, + description: contents, + name: contents.substring(0, 100), // Limit name length + }); + metadataArweave = `ar://${transaction.id}`; + } catch (metadataError) { + console.error("Error uploading metadata to Arweave:", metadataError); + } + + // Encode contents to base64 for response + const base64Data = Buffer.from(contents, "utf-8").toString("base64"); + + return { + txt: { + base64Data, + mimeType, + }, + arweave: txtFile, + metadataArweave, + }; +} From 9eda4500f755fdcb932f09bea1e450a9389d5e5d Mon Sep 17 00:00:00 2001 From: Sweets Sweetman Date: Sat, 13 Dec 2025 15:11:18 -0500 Subject: [PATCH 2/2] refactor the generate function location --- lib/{txt => files}/generateAndStoreTxtFile.ts | 1 + lib/mcp/tools/files/registerGenerateTxtFileTool.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) rename lib/{txt => files}/generateAndStoreTxtFile.ts (99%) diff --git a/lib/txt/generateAndStoreTxtFile.ts b/lib/files/generateAndStoreTxtFile.ts similarity index 99% rename from lib/txt/generateAndStoreTxtFile.ts rename to lib/files/generateAndStoreTxtFile.ts index c5b35cd88..331505dd0 100644 --- a/lib/txt/generateAndStoreTxtFile.ts +++ b/lib/files/generateAndStoreTxtFile.ts @@ -65,3 +65,4 @@ export async function generateAndStoreTxtFile(contents: string): Promise