Skip to main content
Glama
julien-nc

C411 MCP Server

by julien-nc

download_c411_torrent

Download .torrent files from c411.org using infoHash and save them to a specified directory for torrent management.

Instructions

Download a .torrent file from c411.org by its infoHash and save it to disk.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
infoHashYesThe 40-character hex infoHash of the torrent
outputDirNoDirectory where the .torrent file should be saved. Defaults to /tmp.

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
errorNo
successYes
filenameNo
savedPathNo

Implementation Reference

  • The `downloadTorrent` method in `C411Client` handles the actual logic for downloading a .torrent file from the c411 API and saving it to the specified output directory.
    async downloadTorrent(infoHash: string, outputDir = '/tmp'): Promise<DownloadResult> {
      if (!infoHash || !/^[a-fA-F0-9]{40}$/.test(infoHash)) {
        return { success: false, error: 'Invalid infoHash. Must be a 40-character hex string.' };
      }
    
      try {
        return await this.withAuthentication<DownloadResult>(async () => {
          const response = await this.client.get<ArrayBuffer>(`/api/torrents/${infoHash}/download`, {
            responseType: 'arraybuffer',
            headers: {
              'Accept': 'application/x-bittorrent, application/octet-stream, */*',
              'Referer': `${this.baseUrl}/torrents/${infoHash}`,
            },
          });
    
          const contentType = getContentType(response.headers as Record<string, unknown>);
          const responseUrl = getResponseUrl(response.request);
    
          if (isMaintenanceResponse(response.status, response.data, contentType)) {
            throw new MaintenanceError();
          }
    
          if (isAuthenticationFailureResponse(response.status, response.data, contentType, responseUrl)) {
            return { type: 'reauth' };
          }
    
          if (response.status === 404) {
            return { type: 'success', value: { success: false, error: 'Torrent not found.' } };
          }
    
          if (response.status >= 400) {
            const errorMessage = getErrorMessageFromResponse(
              response.data,
              contentType
            ) || `HTTP ${response.status}`;
            return { type: 'success', value: { success: false, error: `Download failed - ${errorMessage}` } };
          }
    
          const buffer = Buffer.isBuffer(response.data)
            ? response.data
            : Buffer.from(response.data);
    
          if (!this.isValidTorrentFile(buffer)) {
            const errorMessage = getErrorMessageFromResponse(response.data, contentType);
            const responseDescription = errorMessage
              ? `Unexpected download response: ${errorMessage}`
              : `Unexpected download response with content type ${contentType ?? 'unknown'}`;
    
            return {
              type: 'success',
              value: {
                success: false,
                error: `${responseDescription}. The payload does not look like a valid .torrent file.`,
              },
            };
          }
    
          const contentDisposition = typeof response.headers['content-disposition'] === 'string'
            ? response.headers['content-disposition']
            : undefined;
          let filename = `${infoHash}.torrent`;
          if (contentDisposition) {
            const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
            if (match && match[1]) {
              filename = match[1].replace(/['"]/g, '').trim();
            }
          }
    
          const safeFilename = path.basename(filename) || `${infoHash}.torrent`;
          const resolvedOutputDir = path.resolve(outputDir);
          const savedPath = path.join(resolvedOutputDir, safeFilename);
    
          await mkdir(resolvedOutputDir, { recursive: true });
          await writeFile(savedPath, buffer);
    
          return {
            type: 'success',
            value: {
              success: true,
              filename: safeFilename,
              savedPath,
            },
          };
        }, 'Unable to authenticate. Check C411_USERNAME and C411_PASSWORD environment variables.');
      } catch (error) {
        const message = getSafeErrorMessage(error, this.requestTimeoutMs);
        console.error(`Error downloading torrent: ${message}`);
        return { success: false, error: message };
      }
    }
  • Registration of the 'download_c411_torrent' tool, which maps the MCP tool request to the `client.downloadTorrent` implementation.
    server.registerTool('download_c411_torrent', {
      description: 'Download a .torrent file from c411.org by its infoHash and save it to disk.',
      inputSchema: downloadToolSchema,
      outputSchema: downloadToolOutputSchema,
    }, async (args) => {
      const result = await client.downloadTorrent(args.infoHash, args.outputDir ?? '/tmp');
      return result.success
        ? textWithStructuredContent(result.savedPath || `Saved ${result.filename}`, {
          success: true,
          filename: result.filename || `${args.infoHash}.torrent`,
          savedPath: result.savedPath || `Saved ${result.filename}`,
        })
        : errorContent(result.error || 'Download failed', {
          success: false,
          ...(result.filename ? { filename: result.filename } : {}),
          ...(result.savedPath ? { savedPath: result.savedPath } : {}),
          error: result.error || 'Download failed',
        });
    });

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/julien-nc/mcp-server-c411'

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