How TUUI Implements MCP Bundle (.mcpb) Support Using the @anthropic-ai/mcpb SDK

TUUI implements MCP Bundle support by treating .mcpb files as DXT bundles that are unpacked, validated against Zod schemas, and converted into MCP server configurations using the @anthropic-ai/mcpb SDK's unpackExtension and getMcpConfigForManifest functions.

TUUI (The Universal User Interface) is an Electron-based application that extends its capabilities through Model Context Protocol (MCP) servers. When users import an MCP Bundle with the .mcpb extension, TUUI leverages the official @anthropic-ai/mcpb SDK to safely extract, validate, and initialize the bundled server configuration.

Overview of the MCP Bundle Pipeline

The implementation follows a seven-stage pipeline that bridges the renderer process (UI) with the main process (Node.js backend):

  1. File ingestion via msgFileTransferRequest IPC channel
  2. Asset path generation using Constants.getDxtSource
  3. Bundle unpacking through the SDK's unpackExtension function
  4. Manifest validation with Zod schemas (McpbManifestSchema.safeParse)
  5. Server config extraction via getMcpConfigForManifest
  6. Client initialization in initClients for metadata type 'metadata__mcpb_manifest'
  7. UI exposure through the preload script's window.dxtManifest API

File Ingestion and Asset Management

When a user selects a .mcpb file in the renderer, the binary data transfers to the main process through a dedicated IPC channel.

Receiving the Bundle

In src/main/IPCs.ts, the msgFileTransferRequest handler receives the file buffer and delegates to the DXT utilities:

// src/main/IPCs.ts
ipcMain.on('msgFileTransferRequest',
  async (event, { name, data }: IpcFileTransferRequest) => {
    const buffer = Buffer.from(data)
    const saveOption = Constants.getDxtSource(name)
    const filePath = saveOption.mcpbPath
    const dirPath = saveOption.outputDir
    
    if (!existsSync(dirPath)) mkdirSync(dirPath, { recursive: true })
    writeFileSync(filePath, buffer, { encoding: null })
    
    await unpackDxt(saveOption)   // SDK unpack
    event.reply('msgFileTransferResponse', { 
      name, 
      success: true, 
      path: dirPath 
    })
})

Deterministic Asset Paths

The Constants.getDxtSource function in src/main/utils/Constants.ts generates predictable storage locations for each bundle:

// src/main/utils/Constants.ts
static getDxtSource(
  filename: string,
  requiredExtension: string = '.mcpb'
): { mcpbPath: string; outputDir: string } {
  if (!filename.endsWith(requiredExtension)) {
    throw new Error(`File extension name must be: ${requiredExtension}`)
  }
  const dirName = filename.slice(0, -requiredExtension.length)
  const dirPath = join(this.ASSETS_PATH.mcpb, dirName, '/')
  return {
    mcpbPath: join(dirPath, filename),
    outputDir: dirPath
  }
}

This ensures each .mcpb file unpacks into src/main/assets/mcpb/{bundleName}/, preventing collisions and enabling reliable cleanup.

Unpacking and Validating .mcpb Files

The src/main/mcp/dxt.ts module serves as the primary interface to the @anthropic-ai/mcpb SDK, handling extraction and schema validation.

Bundle Unpacking

The unpackDxt function wraps the SDK's unpackExtension method:

// src/main/mcp/dxt.ts
export async function unpackDxt(dxtUnpackOption: {
  mcpbPath: string
  outputDir: string
}): Promise<boolean> {
  return unpackExtension(dxtUnpackOption)   // SDK function
}

This extracts the zip-like .mcpb archive to the designated output directory.

Manifest Validation

After unpacking, getManifest loads and validates manifest.json against the SDK's Zod schema:

// src/main/mcp/dxt.ts
export function getManifest(inputPath: string): McpDxtErrors | McpbManifestAny {
  const manifestPath = /* resolve to manifest.json … */
  const manifestContent = readFileSync(manifestPath, 'utf-8')
  const manifestData = JSON.parse(manifestContent)
  const result = McpbVersion.McpbManifestSchema.safeParse(manifestData)

  if (result.success) return result.data
  else return { errors: … }               // convert Zod errors
}

Using safeParse ensures invalid manifests fail gracefully with detailed error messages rather than crashing the process.

Extracting MCP Server Configuration

Once validated, the manifest converts into a runtime MCP server configuration through getMcpConfigForDxt:

// src/main/mcp/dxt.ts
export async function getMcpConfigForDxt(
  basePath: string,
  baseManifest: McpbManifestAny,
  userConfig: McpbUserConfigValues
): Promise<McpServerConfig> {
  const logger: Logger = { log, warn, error }
  const mcpConfig = await getMcpConfigForManifest({
    manifest: baseManifest,
    extensionPath: basePath,
    systemDirs: mockSystemDirs,
    userConfig,
    pathSeparator: sep,
    logger
  })
  if (!mcpConfig) throw new Error(logMessages.join('\n'))
  return mcpConfig
}

This function bridges the SDK's abstract configuration model with TUUI's concrete McpServerConfig type, handling path resolution and user-defined overrides.

Initializing the MCP Client

The src/main/mcp/init.ts module integrates DXT bundles into TUUI's standard client initialization flow.

Detecting Bundle Metadata

During initClients, TUUI checks for metadata objects with type 'metadata__mcpb_manifest':

// src/main/mcp/init.ts
if (object.type === 'metadata__mcpb_manifest') {
  const stdioConfig = await getMcpConfigForDxt(
    Constants.getPosixPath(path.join(Constants.ASSETS_PATH.mcpb, name)),
    object.config,
    object.user_config
  )
  return initSingleClient(name, stdioConfig, callback)
}

This treats the extracted bundle configuration identically to standard stdio-based MCP servers, ensuring consistent behavior across all server types.

Exposing Bundle Metadata to the UI

TUUI exposes DXT manifest data to the Vue-based renderer through a carefully isolated preload script.

Preload Script Bridge

In src/preload/index.ts, the traverseManifest function retrieves manifests via IPC and exposes them to the window object:

// src/preload/index.ts
async function traverseManifest(): Promise<DXTAPI> {
  const manifests = await ipcRenderer.invoke('list-manifests')
  return manifests.result || {}
}

const dxt = {
  get: traverseManifest,
  refresh: async () => { /* trigger reload */ }
}

contextBridge.exposeInMainWorld('dxtManifest', dxt)

This creates a secure bridge allowing the UI to read bundle metadata without direct filesystem access.

Renderer Store Access

The Vue store in src/renderer/store/mcp.ts provides a typed interface for components:

// src/renderer/store/mcp.ts
export function getDxtManifest(): DXTAPI | undefined {
  return window.dxtManifest?.get()
}

Components can now call getDxtManifest() to display bundle information or trigger refreshes after new uploads.

Complete Code Example: Loading a .mcpb Bundle from the UI

Here is a complete renderer-side implementation demonstrating the full upload flow:

// Renderer (e.g., a Vue component)
async function uploadMcpb(file: File) {
  const arrayBuffer = await file.arrayBuffer()
  
  // Send binary data to main process
  ipcRenderer.send('msgFileTransferRequest', {
    name: file.name,               // must end with .mcpb
    data: new Uint8Array(arrayBuffer)
  })

  // Listen for the result
  ipcRenderer.once('msgFileTransferResponse', (event, resp) => {
    if (resp.success) {
      console.log('Bundle saved to', resp.path)
      // Refresh the DXT list so UI sees the new manifest
      window.dxtManifest.refresh()
    } else {
      console.error('Upload failed:', resp.reason)
    }
  })
}

This component handles the binary transfer, awaits confirmation from the main process, and updates the UI state to reflect the newly available MCP server configuration.

Summary

TUUI implements MCP Bundle support through a robust, SDK-backed pipeline:

  • Asset Management: Constants.getDxtSource generates deterministic paths in src/main/assets/mcpb/ for each bundle.
  • IPC Transfer: The msgFileTransferRequest channel securely moves binary data from renderer to main process.
  • SDK Integration: unpackExtension extracts bundles while getMcpConfigForManifest converts manifests into runtime configs.
  • Validation: Zod schemas from @anthropic-ai/mcpb ensure manifest integrity before client initialization.
  • Client Boot: initClients treats validated bundles identically to stdio servers via initSingleClient.
  • UI Bridge: The preload script exposes window.dxtManifest for Vue components to access bundle metadata.

Frequently Asked Questions

How does TUUI validate the integrity of an .mcpb file?

TUUI validates .mcpb files using the @anthropic-ai/mcpb SDK's Zod schema validation. After unpacking the bundle with unpackExtension, the getManifest function in src/main/mcp/dxt.ts parses manifest.json and validates it against McpbVersion.McpbManifestSchema.safeParse(). This ensures the manifest conforms to the official MCP Bundle specification before TUUI attempts to initialize the server.

What happens if the .mcpb file contains an invalid manifest?

If validation fails during the getManifest call, the function returns a McpDxtErrors object containing detailed Zod error messages rather than the manifest data. This error propagates through the initialization pipeline, preventing getMcpConfigForDxt from attempting to create a server configuration. The UI can then display specific validation errors to help users diagnose malformed bundles.

Can users configure MCP Bundle servers after installation?

Yes, TUUI supports user configuration overrides through the userConfig parameter in getMcpConfigForDxt. When initializing a bundle-based client in src/main/mcp/init.ts, TUUI passes object.user_config to the SDK's getMcpConfigForManifest function. This allows users to customize environment variables, command-line arguments, and other runtime settings without modifying the bundled manifest itself.

How does the renderer process access bundle metadata without filesystem access?

TUUI implements a secure context bridge in src/preload/index.ts that exposes a controlled API via contextBridge.exposeInMainWorld('dxtManifest', dxt). The traverseManifest function uses ipcRenderer.invoke('list-manifests') to request data from the main process, which then returns serialized manifest data. Vue components in the renderer access this through window.dxtManifest.get() via the helper in src/renderer/store/mcp.ts, maintaining Electron's security model by preventing direct filesystem access from the renderer.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →