Skip to main content
Glama

fetchFromGateway

Retrieve content from IPFS networks using Pinata's gateway by providing a CID, supporting both public and private file access.

Instructions

Fetch content from Public or Private IPFS via Pinata gateway and return it

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
cidYesThe CID of the file to fetch
networkNoWhether the file is on public or private IPFSpublic

Implementation Reference

  • Complete implementation of the fetchFromGateway tool that fetches content from Public or Private IPFS via Pinata gateway. The handler constructs the appropriate URL based on network type (public uses /ipfs/ endpoint, private creates a temporary download link first), fetches the content, and returns it as text or base64 depending on the content type and size.
    server.tool(
      "fetchFromGateway",
      "Fetch content from Public or Private IPFS via Pinata gateway and return it",
      {
        cid: z.string().describe("The CID of the file to fetch"),
        network: z
          .enum(["public", "private"])
          .default("public")
          .describe("Whether the file is on public or private IPFS"),
      },
      async ({ cid, network }) => {
        try {
          if (!GATEWAY_URL) {
            throw new Error("GATEWAY_URL environment variable is not set");
          }
    
          let fileUrl: string;
    
          if (network === "public") {
            fileUrl = `https://${GATEWAY_URL}/ipfs/${cid}`;
          } else {
            const filePath = `https://${GATEWAY_URL}/files/${cid}`;
            const apiUrl = `https://api.pinata.cloud/v3/files/private/download_link`;
            const date = Math.floor(new Date().getTime() / 1000);
            const expires = 600;
    
            const payload = {
              url: filePath,
              expires,
              date,
              method: "GET",
            };
    
            const linkResponse = await fetch(apiUrl, {
              method: "POST",
              headers: getHeaders(),
              body: JSON.stringify(payload),
            });
    
            if (!linkResponse.ok) {
              const errorText = await linkResponse.text();
              throw new Error(
                `Failed to create download link: ${linkResponse.status} ${linkResponse.statusText}. Response: ${errorText}`
              );
            }
    
            const linkData = await linkResponse.json();
            fileUrl = linkData.data;
          }
    
          const response = await fetch(fileUrl);
    
          if (!response.ok) {
            throw new Error(
              `Failed to fetch file: ${response.status} ${response.statusText}`
            );
          }
    
          const contentType =
            response.headers.get("content-type") || "application/octet-stream";
          const arrayBuffer = await response.arrayBuffer();
          const buffer = Buffer.from(arrayBuffer);
    
          let resultText = `✅ Fetched ${buffer.length} bytes from ${network} IPFS (CID: ${cid})\nContent-Type: ${contentType}\n\n`;
    
          // Return text content directly, binary as base64
          if (
            contentType.startsWith("text/") ||
            contentType.includes("json") ||
            contentType.includes("javascript") ||
            contentType.includes("xml")
          ) {
            if (buffer.length < 100000) {
              resultText += `Content:\n${buffer.toString("utf-8")}`;
            } else {
              resultText += `Content too large to display (${buffer.length} bytes). Use a smaller file or save to disk.`;
            }
          } else if (buffer.length < 50000) {
            resultText += `Base64 Content:\n${buffer.toString("base64")}`;
          } else {
            resultText += `Binary content too large to display (${buffer.length} bytes).`;
          }
    
          return {
            content: [{ type: "text", text: resultText }],
          };
        } catch (error) {
          return errorResponse(error);
        }
      }
    );
  • Helper functions for consistent API responses. errorResponse creates a standardized error response with the error message, while successResponse formats successful JSON data responses.
    const errorResponse = (error: unknown) => ({
      content: [
        {
          type: "text" as const,
          text: `Error: ${error instanceof Error ? error.message : String(error)}`,
        },
      ],
      isError: true,
    });
    
    // Helper for consistent success responses
    const successResponse = (data: unknown) => ({
      content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
    });
  • Helper function that generates authorization headers for API requests using the PINATA_JWT environment variable.
    const getHeaders = () => {
      if (!PINATA_JWT) {
        throw new Error("PINATA_JWT environment variable is not set");
      }
      return {
        Authorization: `Bearer ${PINATA_JWT}`,
        "Content-Type": "application/json",
      };
    };
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the tool fetches and returns content, implying a read-only operation, but lacks details on authentication requirements (especially for private IPFS), rate limits, error handling, or what 'return it' entails (e.g., format, size limits). For a tool with potential network and access complexities, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core action ('fetch content') and key details (source, gateway). There is no wasted verbiage or redundancy, making it highly concise and well-structured for quick comprehension.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of IPFS operations and lack of annotations or output schema, the description is incomplete. It doesn't address authentication needs for private networks, error cases (e.g., invalid CID), return formats, or performance considerations. For a tool that interacts with external gateways and handles both public and private data, more contextual information is needed to ensure reliable use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with clear documentation for both parameters (cid and network). The description adds minimal value beyond the schema, mentioning 'Public or Private IPFS' which aligns with the network enum but doesn't elaborate on implications. With high schema coverage, the baseline score of 3 is appropriate as the description doesn't significantly enhance parameter understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('fetch content') and target ('from Public or Private IPFS via Pinata gateway'), with the specific resource being content identified by CID. It distinguishes from siblings like 'getFileById' or 'uploadFile' by focusing on gateway retrieval rather than direct file operations. However, it doesn't explicitly differentiate from similar tools like 'createPrivateDownloadLink' which might also involve content access.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. It doesn't mention prerequisites (e.g., authentication needs for private IPFS), compare with siblings like 'getFileById' or 'createPrivateDownloadLink', or specify scenarios where gateway fetching is preferred over direct methods. Usage is implied by the action but without explicit context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/PinataCloud/pinata-mcp'

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