get_package_details
Retrieve detailed information about Typst packages including description, authors, categories, repository links, import code, and version history.
Instructions
Get detailed information about a specific Typst package, including its description, authors, categories, repository link, import code, and version history.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| packageName | Yes | The exact name of the package (e.g., "cetz", "polylux", "fletcher") |
Implementation Reference
- src/scraper.ts:113-199 (handler)Core handler function that scrapes the Typst Universe package page using Cheerio to extract details like version, description, authors, categories, repository, import code, and version history.export async function getPackageDetails(packageName: string): Promise<PackageDetails | null> { const packageUrl = `${UNIVERSE_URL}/package/${packageName}`; try { const html = await fetchPage(packageUrl); const $ = cheerio.load(html); // Extract package name and version from the header const headerText = $('h1').first().text().trim(); // Get the description from meta tag or first paragraph const metaDescription = $('meta[name="description"]').attr('content') || $('meta[property="og:description"]').attr('content') || ''; // Extract the full description from the main content const mainContent = $('main').text().trim(); // Find version in the page const versionMatch = mainContent.match(/(\d+\.\d+\.\d+)/); const version = versionMatch ? versionMatch[1] : 'unknown'; // Extract authors const authors: string[] = []; $('a[href*="author"]').each((_, el) => { const authorName = $(el).text().trim(); if (authorName && !authors.includes(authorName)) { authors.push(authorName); } }); // Extract categories const categories: string[] = []; $('a[href*="category="]').each((_, el) => { const category = $(el).text().trim(); if (category && !categories.includes(category)) { categories.push(category); } }); // Extract repository link let repository: string | undefined; const githubLink = $('a[href*="github.com"]').first().attr('href'); if (githubLink && !githubLink.includes('typst/packages')) { repository = githubLink; } // Extract homepage let homepage: string | undefined; $('a').each((_, el) => { const href = $(el).attr('href'); const text = $(el).text().toLowerCase(); if (href && (text.includes('.io') || text.includes('.com') || text.includes('homepage')) && !href.includes('github.com') && !href.includes('typst.app')) { homepage = href; } }); // Build import code const importCode = `#import "@preview/${packageName}:${version}"`; // Extract version history const versionHistory: string[] = []; $(`a[href^="/universe/package/${packageName}/"]`).each((_, el) => { const versionText = $(el).text().trim(); if (versionText.match(/^\d+\.\d+\.\d+$/)) { versionHistory.push(versionText); } }); return { name: packageName, version, description: metaDescription, fullDescription: mainContent.substring(0, 2000), // Limit full description length authors, categories, repository, homepage, importCode, versionHistory, url: packageUrl, }; } catch (error) { console.error(`Failed to fetch package details for ${packageName}:`, error); return null; } }
- src/index.ts:90-92 (schema)Zod schema used to validate the input arguments (packageName) for the get_package_details tool.const GetPackageDetailsSchema = z.object({ packageName: z.string().min(1, 'Package name is required'), });
- src/index.ts:50-63 (registration)Tool registration in the MCP server's TOOLS array, defining name, description, and JSON input schema.{ name: 'get_package_details', description: 'Get detailed information about a specific Typst package, including its description, authors, categories, repository link, import code, and version history.', inputSchema: { type: 'object', properties: { packageName: { type: 'string', description: 'The exact name of the package (e.g., "cetz", "polylux", "fletcher")', }, }, required: ['packageName'], }, },
- src/index.ts:204-228 (handler)Dispatch handler in the main CallToolRequestSchema handler that validates arguments, calls getPackageDetails, handles errors, and formats the response using formatPackageDetails.case 'get_package_details': { const validatedArgs = GetPackageDetailsSchema.parse(args); const details = await getPackageDetails(validatedArgs.packageName); if (!details) { return { content: [ { type: 'text', text: `Package "${validatedArgs.packageName}" not found. Please check the package name and try again.`, }, ], isError: true, }; } return { content: [ { type: 'text', text: formatPackageDetails(details), }, ], }; }
- src/scraper.ts:13-25 (schema)TypeScript interface defining the structure of the PackageDetails return type from getPackageDetails.export interface PackageDetails { name: string; version: string; description: string; fullDescription: string; authors: string[]; categories: string[]; repository?: string; homepage?: string; importCode: string; versionHistory: string[]; url: string; }