read_icon
Read an existing .icon bundle to extract its manifest JSON and list of assets. Useful for inspecting icon contents programmatically.
Instructions
Read and inspect an existing .icon bundle. Returns the manifest JSON and list of assets.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| bundle_path | Yes | Path to .icon bundle |
Implementation Reference
- src/server.ts:183-191 (registration)Registration of the 'read_icon' MCP tool with its schema (bundle_path string) and handler binding to inspectBundle.
// ── Tool: read_icon ── server.tool( 'read_icon', 'Read and inspect an existing .icon bundle. Returns the manifest JSON and list of assets.', { bundle_path: z.string().describe('Path to .icon bundle'), }, async (params) => inspectBundle(params), ); - src/lib/ops-bundle.ts:249-270 (handler)The inspectBundle handler function that executes the read_icon tool logic. Reads the bundle via readIconBundle and returns formatted manifest + asset list.
export async function inspectBundle(params: { bundle_path: string }): Promise<McpResult> { try { const { manifest, assets } = await readIconBundle(params.bundle_path); const assetList = Array.from(assets.keys()).map((name) => { const buf = assets.get(name)!; return ` ${name} (${(buf.length / 1024).toFixed(1)} KB)`; }); return { content: [{ type: 'text', text: `Manifest:\n${JSON.stringify(manifest, null, 2)}\n\nAssets:\n${assetList.join('\n') || ' (none)'}`, }], }; } catch (error: unknown) { const msg = error instanceof Error ? error.message : 'Unknown error'; return { content: [{ type: 'text', text: `Error: ${msg}` }], isError: true, }; } } - src/lib/bundle.ts:13-58 (helper)The readIconBundle helper function that reads an .icon bundle from disk: parses icon.json manifest and loads Assets/ directory into a Map of filename to Buffer.
export async function readIconBundle( bundlePath: string, maxAssetBytes: number = DEFAULT_MAX_ASSET_BYTES, ): Promise<{ manifest: IconManifest; assets: Map<string, Buffer>; }> { const manifestPath = path.join(bundlePath, 'icon.json'); const manifestJson = await fs.readFile(manifestPath, 'utf-8'); // Let SyntaxError propagate naturally const parsed: unknown = JSON.parse(manifestJson); if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { throw new Error('icon.json does not contain a valid IconManifest object'); } const manifest = parsed as IconManifest; const assets = new Map<string, Buffer>(); const assetsPath = path.join(bundlePath, 'Assets'); try { const files = await fs.readdir(assetsPath); for (const file of files) { const filePath = path.join(assetsPath, file); const stat = await fs.stat(filePath); if (stat.size > maxAssetBytes) { const sizeMB = (stat.size / (1024 * 1024)).toFixed(1); const limitMB = (maxAssetBytes / (1024 * 1024)).toFixed(1); throw new Error( `Asset "${file}" exceeds maximum size (${sizeMB} MB > ${limitMB} MB)`, ); } const buffer = await fs.readFile(filePath); assets.set(file, buffer); } } catch (err: unknown) { if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'ENOENT') { // Assets dir doesn't exist — valid state, return empty map } else { throw err; } } return { manifest, assets }; } - src/server.ts:187-189 (schema)Input schema for read_icon: single required string parameter 'bundle_path' describing the path to the .icon bundle.
{ bundle_path: z.string().describe('Path to .icon bundle'), },