Skip to main content
Glama

add_docset

Install a new documentation set for API reference by providing a local .docset file or direct download URL, enabling AI assistants to access project-specific API documentation.

Instructions

Install a new documentation set (docset) for API reference. Supports both local .docset files and direct URLs.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
sourceYesPath to local .docset file/directory or URL to download. Examples: "/Downloads/Swift.docset", "https://example.com/React.docset.tgz"

Implementation Reference

  • Tool schema/input definition for 'add_docset' - defines name, description, and inputSchema with a required 'source' property
    {
      name: 'add_docset',
      description: 'Install a new documentation set (docset) for API reference. Supports both local .docset files and direct URLs.',
      inputSchema: {
        type: 'object',
        properties: {
          source: {
            type: 'string',
            description: 'Path to local .docset file/directory or URL to download. Examples: "/Downloads/Swift.docset", "https://example.com/React.docset.tgz"'
          }
        },
        required: ['source']
      }
    },
  • Tool handler for 'add_docset' - extracts 'source' from args, calls this.docsetService.addDocset(source), then registers it in multiDocsetDb, returns success message with docset info
    case 'add_docset':
      const source = args?.source;
      if (!source) {
        throw new Error('source parameter is required');
      }
      
      try {
        const docsetInfo = await this.docsetService.addDocset(source);
        // Add to the database for searching
        this.multiDocsetDb.addDocset(docsetInfo);
        
        return {
          content: [{
            type: 'text',
            text: `✅ Successfully installed docset!\n\n**Name:** ${docsetInfo.name}\n**ID:** ${docsetInfo.id}\n**Path:** ${docsetInfo.path}\n\nThe docset is now available for searching with \`search_documentation\` and exploring with \`explore_api\`.`
          }]
        };
      } catch (error) {
        throw new Error(`Failed to add docset: ${error.message}`);
      }
  • DocsetService.addDocset() - determines if source is URL or local path, generates unique docsetId via MD5 hash, and delegates to addDocsetFromUrl() or addDocsetFromLocal()
    async addDocset(source) {
      // Determine if source is URL or local path
      const isUrl = source.startsWith('http://') || source.startsWith('https://');
      const isLocalPath = await this.isLocalPath(source);
      
      if (!isUrl && !isLocalPath) {
        throw new Error(`Invalid source: ${source}. Must be a valid URL or local file path.`);
      }
    
      // Generate unique ID for the docset
      const docsetId = crypto.createHash('md5').update(source).digest('hex').substring(0, 8);
      
      // Check if already exists
      if (this.docsets.has(docsetId)) {
        throw new Error(`Docset from ${source} is already installed`);
      }
    
      if (isUrl) {
        return await this.addDocsetFromUrl(source, docsetId);
      } else {
        return await this.addDocsetFromLocal(source, docsetId);
      }
    }
  • DocsetService.addDocsetFromLocal() - handles local .docset directories and archive files (.tgz, .tar.gz, .zip), extracts/ copies them, reads Info.plist metadata, and returns docsetInfo
    async addDocsetFromLocal(localPath, docsetId) {
      try {
        const stats = await fs.stat(localPath);
        const extractPath = path.join(this.storagePath, docsetId);
        await fs.ensureDir(extractPath);
    
        let docsetPath;
    
        if (stats.isDirectory() && localPath.endsWith('.docset')) {
          // Direct docset directory - copy it
          docsetPath = path.join(extractPath, path.basename(localPath));
          await fs.copy(localPath, docsetPath);
        } else if (stats.isFile()) {
          // Handle different archive formats
          if (localPath.endsWith('.tgz') || localPath.endsWith('.tar.gz')) {
            // Tar archive - extract it
            await tar.extract({
              file: localPath,
              cwd: extractPath,
              strip: 0
            });
          } else if (localPath.endsWith('.zip')) {
            // ZIP archive - extract it
            const zip = new AdmZip(localPath);
            zip.extractAllTo(extractPath, true);
          } else {
            throw new Error('Local file must be a .tgz, .tar.gz, or .zip archive');
          }
    
          // Find the .docset directory
          const files = await fs.readdir(extractPath);
          const docsetDir = files.find(f => f.endsWith('.docset'));
          
          if (!docsetDir) {
            throw new Error('No .docset directory found in the archive');
          }
    
          docsetPath = path.join(extractPath, docsetDir);
        } else {
          throw new Error('Local path must be a .docset directory or archive file (.tgz, .tar.gz, .zip)');
        }
    
        // Read docset metadata
        const metadata = await this.readDocsetMetadata(docsetPath);
        
        // Get docset size
        const size = await this.getDirectorySize(docsetPath);
    
        // Create docset info
        const docsetInfo = {
          id: docsetId,
          name: metadata.CFBundleName || path.basename(docsetPath).replace('.docset', ''),
          source: localPath,
          path: docsetPath,
          version: metadata.CFBundleVersion,
          platform: metadata.DocSetPlatformFamily,
          downloadedAt: new Date(),
          size
        };
    
        // Save to metadata
        this.docsets.set(docsetId, docsetInfo);
        await this.saveMetadata();
    
        return docsetInfo;
      } catch (error) {
        // Clean up on failure
        try {
          const extractPath = path.join(this.storagePath, docsetId);
          await fs.remove(extractPath);
        } catch (cleanupError) {
          // Ignore cleanup errors
        }
        
        throw error;
      }
    }
  • DocsetService.addDocsetFromUrl() - downloads docset from URL, extracts archive, reads Info.plist metadata, saves to storage, returns docsetInfo with progress tracking
    async addDocsetFromUrl(url, docsetId) {
      const progress = {
        docsetId,
        url,
        totalBytes: 0,
        downloadedBytes: 0,
        percentage: 0,
        status: 'downloading'
      };
      
      this.downloadProgress.set(docsetId, progress);
    
      try {
        // Determine file extension from URL
        const urlLower = url.toLowerCase();
        let fileExt = '.tgz';
        if (urlLower.endsWith('.zip')) {
          fileExt = '.zip';
        } else if (urlLower.endsWith('.tar.gz')) {
          fileExt = '.tar.gz';
        }
    
        // Download the docset
        const downloadPath = path.join(this.storagePath, `${docsetId}${fileExt}`);
        await this.downloadFile(url, downloadPath, docsetId);
    
        // Update progress
        progress.status = 'extracting';
        
        // Extract the docset
        const extractPath = path.join(this.storagePath, docsetId);
        await fs.ensureDir(extractPath);
        
        if (fileExt === '.zip') {
          // Extract ZIP file
          const zip = new AdmZip(downloadPath);
          zip.extractAllTo(extractPath, true);
        } else {
          // Extract tar archive
          await tar.extract({
            file: downloadPath,
            cwd: extractPath,
            strip: 0
          });
        }
    
        // Clean up downloaded file
        await fs.unlink(downloadPath);
    
        // Find the .docset directory
        const files = await fs.readdir(extractPath);
        const docsetDir = files.find(f => f.endsWith('.docset'));
        
        if (!docsetDir) {
          throw new Error('No .docset directory found in the downloaded archive');
        }
    
        const docsetPath = path.join(extractPath, docsetDir);
        
        // Read docset metadata
        const metadata = await this.readDocsetMetadata(docsetPath);
        
        // Get docset size
        const size = await this.getDirectorySize(docsetPath);
    
        // Create docset info
        const docsetInfo = {
          id: docsetId,
          name: metadata.CFBundleName || docsetDir.replace('.docset', ''),
          source: url,
          path: docsetPath,
          version: metadata.CFBundleVersion,
          platform: metadata.DocSetPlatformFamily,
          downloadedAt: new Date(),
          size
        };
    
        // Save to metadata
        this.docsets.set(docsetId, docsetInfo);
        await this.saveMetadata();
    
        // Update progress
        progress.status = 'completed';
        progress.percentage = 100;
    
        return docsetInfo;
      } catch (error) {
        progress.status = 'failed';
        progress.error = error.message;
        
        // Clean up any partial downloads
        try {
          const extractPath = path.join(this.storagePath, docsetId);
          await fs.remove(extractPath);
        } catch (cleanupError) {
          // Ignore cleanup errors
        }
        
        throw error;
      } finally {
        // Clean up progress after a delay
        setTimeout(() => {
          this.downloadProgress.delete(docsetId);
        }, 5000);
      }
    }
Behavior2/5

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

With no annotations provided, the description fully determines behavioral transparency. It indicates a write operation ('Install') but does not disclose side effects (e.g., overwrite behavior, storage location, permissions, or error handling). The lack of detail on what happens after installation reduces 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 two highly concise sentences, front-loading the core purpose and supported sources. Every word adds value with no redundancy.

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

Completeness3/5

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

For a simple one-parameter tool with no annotations or output schema, the description is mostly adequate but lacks details on return behavior (e.g., success confirmation, error messages) and usage context. It covers the basic interface but leaves some gaps.

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?

The schema already covers the single parameter 'source' with 100% description coverage, including examples. The description adds context ('for API reference' and 'local .docset files and direct URLs') but does not significantly add meaning beyond the schema. Baseline 3 is appropriate.

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

Purpose5/5

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

The description clearly states the action 'Install', the resource 'documentation set (docset)', and the purpose 'for API reference'. It also specifies the supported formats (local files and URLs), effectively distinguishing it from sibling tools like 'list_docsets' and 'remove_docset'.

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 (e.g., 'refresh_documentation' or 'search_documentation'). There is no mention of prerequisites, typical use cases, or limitations, leaving the agent without context for selection.

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/afterxleep/doc-bot'

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