Skip to main content
Glama

webdav_range_request

Read specific byte ranges from files on remote WebDAV servers using HTTP partial content requests to download portions of large files without transferring the entire file.

Instructions

Read a specific byte range from a file on a remote WebDAV server (similar to HTTP 206 Partial Content)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pathYes
rangeYesByte range in format "bytes=0-499" (first 500 bytes), "bytes=500-" (from byte 500 to end), or "0-499" (range from start to end)

Implementation Reference

  • MCP tool registration for 'webdav_range_request', including schema validation with Zod and the complete inline handler function that orchestrates the range request.
    server.tool( "webdav_range_request", "Read a specific byte range from a file on a remote WebDAV server (similar to HTTP 206 Partial Content)", { path: z.string().min(1, "Path must not be empty"), range: z.string().describe( 'Byte range in format "bytes=0-499" (first 500 bytes), "bytes=500-" (from byte 500 to end), or "0-499" (range from start to end)', ), }, async ({ path, range }) => { try { // Check if file exists first const exists = await webdavService.exists(path); if (!exists) { return { content: [{ type: "text", text: `Error: File does not exist at ${path}`, }], isError: true, }; } // Check if range requests are supported const supportsRanges = await webdavService.supportsRangeRequests(path); if (!supportsRanges) { return { content: [{ type: "text", text: `Error: Range requests are not supported for this file or server`, }], isError: true, }; } // Perform the range request const result = await webdavService.readFileWithRange(path, range); // Format the response similar to HTTP 206 response const response = [ `=== HTTP 206 Partial Content Simulation ===`, `File: ${path}`, `Content-Range: ${result.contentRange}`, `Accept-Ranges: ${result.acceptRanges ? "bytes" : "none"}`, `Content-Length: ${result.content.length}`, `Total-Size: ${result.totalSize}`, `Range-Request: ${range}`, ``, `=== Content ===`, result.content, ].join("\n"); return { content: [{ type: "text", text: response, }], }; } catch (error) { return { content: [{ type: "text", text: `Error performing range request: ${(error as Error).message}`, }], isError: true, }; } }, );
  • The handler function that implements the tool logic: input validation is implicit via schema, checks preconditions, delegates to WebDAV service, formats output as simulated HTTP partial content response.
    async ({ path, range }) => { try { // Check if file exists first const exists = await webdavService.exists(path); if (!exists) { return { content: [{ type: "text", text: `Error: File does not exist at ${path}`, }], isError: true, }; } // Check if range requests are supported const supportsRanges = await webdavService.supportsRangeRequests(path); if (!supportsRanges) { return { content: [{ type: "text", text: `Error: Range requests are not supported for this file or server`, }], isError: true, }; } // Perform the range request const result = await webdavService.readFileWithRange(path, range); // Format the response similar to HTTP 206 response const response = [ `=== HTTP 206 Partial Content Simulation ===`, `File: ${path}`, `Content-Range: ${result.contentRange}`, `Accept-Ranges: ${result.acceptRanges ? "bytes" : "none"}`, `Content-Length: ${result.content.length}`, `Total-Size: ${result.totalSize}`, `Range-Request: ${range}`, ``, `=== Content ===`, result.content, ].join("\n"); return { content: [{ type: "text", text: response, }], }; } catch (error) { return { content: [{ type: "text", text: `Error performing range request: ${(error as Error).message}`, }], isError: true, }; } },
  • Input schema using Zod for path (required string) and range (string with description of supported formats).
    { path: z.string().min(1, "Path must not be empty"), range: z.string().describe( 'Byte range in format "bytes=0-499" (first 500 bytes), "bytes=500-" (from byte 500 to end), or "0-499" (range from start to end)', ), },
  • Supporting helper method in WebDAVService that handles the low-level range request: parses range string, validates against file size, creates read stream with range, buffers content, computes Content-Range header.
    async readFileWithRange( path: string, range: string, ): Promise<{ content: string; contentRange: string; acceptRanges: boolean; totalSize: number; }> { const fullPath = this.getFullPath(path); logger.debug(`Reading file with range: ${fullPath}`, { range }); try { // Parse the range header const parsedRange = this.parseRangeHeader(range); if (!parsedRange) { throw new Error("Invalid range format"); } // Get file stats first to check total size const stats = await this.stat(fullPath); const totalSize = stats.size || 0; // Validate range against file size if (parsedRange.start >= totalSize) { throw new Error( `Range start (${parsedRange.start}) is beyond file size (${totalSize})`, ); } // Calculate actual end position const end = parsedRange.end === undefined ? totalSize - 1 : Math.min(parsedRange.end, totalSize - 1); // Use createReadStream with range options const stream = this.client.createReadStream(fullPath, { range: { start: parsedRange.start, end: parsedRange.end, }, }); // Convert stream to string const chunks: Buffer[] = []; return new Promise((resolve, reject) => { stream.on("data", (chunk: Buffer) => { chunks.push(chunk); }); stream.on("end", () => { try { const content = Buffer.concat(chunks).toString("utf8"); const contentRange = `bytes ${parsedRange.start}-${end}/${totalSize}`; logger.debug(`Range request completed: ${fullPath}`, { range, contentLength: content.length, totalSize, }); resolve({ content, contentRange, acceptRanges: true, totalSize, }); } catch (error) { reject( new Error( `Failed to process stream content: ${(error as Error).message}`, ), ); } }); stream.on("error", (error) => { logger.error(`Stream error for ${fullPath}:`, error); reject(new Error(`Stream error: ${error.message}`)); }); }); } catch (error) { logger.error(`Error reading file with range ${fullPath}:`, error); throw new Error( `Failed to read file with range: ${(error as Error).message}`, ); } }
  • Helper method that checks if range requests are supported by attempting to stat the file (assumes support if stat succeeds).
    async supportsRangeRequests(path: string = "/"): Promise<boolean> { const fullPath = this.getFullPath(path); logger.debug(`Checking range request support for: ${fullPath}`); try { // For WebDAV servers, we'll assume range requests are supported // if we can successfully read file metadata const stats = await this.stat(fullPath); return true; } catch (error) { logger.debug(`Range request support check failed for ${fullPath}`, error); return false; } }

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/masx200/mcp-webdav-server'

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