ToolHive Studio

repository·main·Indexed 41 days ago

https://github.com/stacklok/toolhive-studio

A desktop UI and management tool for securely deploying, discovering, and managing Model Context Protocol (MCP) servers. ToolHive runs servers in isolated containers and integrates with AI clients such as Cursor and Claude Code.

Tokens
28K
Snippets
89
Records
120
Agent score
51%

What's inside ToolHive

  1. How ToolHive manages MCP servers

    main

    ToolHive provides a secure environment for running MCP servers by utilizing containerization and proxying:

    • Isolation: Each MCP server runs in its own dedicated, secure container.
    • Connectivity: ToolHive exposes an HTTP/SSE proxy that MCP clients (such as GitHub Copilot, Cursor, or Claude Code) connect to.
    • Deployment Modes: While primarily used as a desktop UI, ToolHive is also available as a standalone command-line tool and as a Kubernetes Operator for advanced use cases.
  2. Understand E2E test isolation and limitations

    main

    ToolHive's E2E tests face certain constraints due to the app's architecture and the nature of running real workloads:

    Test Isolation

    Because the app stores state on disk, tests are not perfectly isolated. To mitigate this, the suite creates a dedicated test group and cleans it up using the thv CLI before and after each run. This means:

    • Tests avoid using the default group.
    • Reliable parallel execution on a single machine is not supported.
    • Some state, such as local caches, may persist across runs.

    Key Limitations

    • Platform: E2E runs are optimized for GitHub Actions Linux runners. macOS and Windows runners are significantly slower.
    • Native Dialogs: Features requiring native OS dialogs (e.g., file pickers) are not covered.
    • Client Sync: The client sync feature is intentionally excluded from E2E testing to reduce complexity, as correctness is primarily validated via backend tests.
    • OS Flows: Installation, uninstallation, and auto-update flows are not tested.
  3. How the MSW Auto-Mocker works

    main

    ToolHive uses an MSW (Mock Service Worker) Auto-Mocker to generate API fixtures automatically from the OpenAPI schema (api/openapi.json).

    Core Components

    • Auto-generated fixtures: Created in renderer/src/common/mocks/fixtures/ based on schema-based fake data.
    • Custom handlers: Located in renderer/src/common/mocks/customHandlers/index.ts. These take precedence over auto-generated mocks and should be used for behavior overrides or endpoints without a schema.
    • Mocking implementation: Found in renderer/src/common/mocks/.

    Workflow

    1. Generation: To create a new fixture, run a Vitest test that calls the target endpoint. The mocker will generate a TypeScript file at renderer/src/common/mocks/fixtures/<sanitized-path>/<method>.ts.
    2. Customization: For simple data tweaks (e.g., replacing placeholder text), edit the generated TypeScript file directly. This is preferred over writing custom handlers.
    3. Regeneration: Delete the specific fixture file to force a re-generation on the next request.
    4. Strictness: If a schema is missing or generation fails, the handler responds with a 500 error and does not create a placeholder. Invalid fixtures also result in a 500 response.
  4. Apply custom branding themes to ToolHive

    main

    You can replace the default Stacklok palette with a custom theme by providing a JSON file that conforms to the BrandingConfig schema. This allows you to change the visual appearance of ToolHive without modifying the source code.

    Method 1: Persistent Installation

    To apply a theme permanently, copy your JSON configuration file to the ToolHive user data directory:

    • Linux: ~/.config/ToolHive/branding-0.json
    • macOS: ~/Library/Application Support/ToolHive/branding-0.json

    Method 2: Environment Variable Override

    For testing or temporary use, set the BRANDING_CONFIG_PATH environment variable to the absolute path of your JSON file before launching the application.

    # Linux
    cp branding-examples/test-theme.json ~/.config/ToolHive/branding-0.json
    
    # macOS
    cp branding-examples/test-theme.json "$HOME/Library/Application Support/ToolHive/branding-0.json"
    
    # Override via environment variable
    BRANDING_CONFIG_PATH="$PWD/branding-examples/test-theme.json" pnpm run start
  5. Test deep links with the `toolhive-gui://` protocol

    main

    The application uses the toolhive-gui:// protocol for deep linking. The format is: toolhive-gui://<version>/<intent>?<params>

    Example links:

    • toolhive-gui://v1/open-registry-server-detail?serverName=fetch
    • toolhive-gui://v1/open-registry-server-detail?serverName=time

    Linux Setup: To test deep links on Linux, you must register the protocol handler by building a .deb and installing the .desktop file:

    pnpm run deeplink:registerTestLinuxProtocolHandler

    After registration, you can test links using xdg-open:

    xdg-open "toolhive-gui://v1/open-registry-server-detail?serverName=fetch"
    pnpm run deeplink:registerTestLinuxProtocolHandler
    xdg-open "toolhive-gui://v1/open-registry-server-detail?serverName=fetch"
  6. Install and start ToolHive development environment

    main

    ToolHive is an Electron application built with React, TypeScript, and Vite. It requires pnpm and a Node.js version specified in .nvmrc.

    Prerequisite: Ensure the Docker daemon is running before starting, as it is required by ToolHive.

    To set up your environment:

    1. Install dependencies: pnpm install
    2. Build the application by running the thv binary once: pnpm thv
    3. Start the development server: pnpm run start (this enables hot reload for the renderer process).
    pnpm install
    pnpm thv
    pnpm run start
  7. Run End-to-End (E2E) tests with Playwright

    main

    E2E tests are performed on the packaged Electron application using Playwright.

    • To package the app and run tests from scratch: pnpm run e2e
    • To run tests against an existing package in out/ (faster iteration): pnpm run e2e:prebuilt
    pnpm run e2e
    pnpm run e2e:prebuilt
  8. Run the ToolHive E2E test suite

    main

    ToolHive uses Playwright to run end-to-end tests against the packaged Electron app located in out/. You can run the suite using pnpm with two different approaches depending on whether you have recently changed the application build.

    Use this command to ensure tests are running against the most recent build, avoiding stale artifacts.

    Fast Iteration: Test Prebuilt

    Use this command if you have not changed the app build and want to skip the build step for faster execution. This reuses the existing out/ directory.

    # Recommended: build + tests
    pnpm run e2e
    
    # Fast iteration: tests only (reuse out/)
    pnpm run e2e:prebuilt
  9. Use AutoAPIMock for test-scoped overrides

    main

    Every generated fixture is wrapped in AutoAPIMock<TResponse, TRequest>, providing type-safe ways to modify API responses within a specific test.

    Type-Safe Data Overrides

    Use .override() to modify the response body. You can return a completely new object or use the existing data to derive a new one.

    Full Handler Overrides

    Use .overrideHandler() for complete control over the response, such as simulating network errors or specific HTTP status codes using MSW's HttpResponse.

    Resetting

    Overrides are automatically reset before each test via resetAllAutoAPIMocks() in the Vitest setup.

    import { HttpResponse } from 'msw'
    import { mockedGetApiV1BetaGroups } from '@mocks/fixtures/groups/get'
    
    // Type-safe data override
    mockedGetApiV1BetaGroups.override(() => ({
      groups: [],
    }))
    
    // Modify default data
    mockedGetApiV1BetaGroups.override((data) => ({
      ...data,
      groups: data.groups?.slice(0, 1),
    }))
    
    // Error responses
    mockedGetApiV1BetaGroups.overrideHandler(() =>
      HttpResponse.json({ error: 'Server error' }, { status: 500 })
    )
    
    // Network error
    mockedGetApiV1BetaGroups.overrideHandler(() => HttpResponse.error())
  10. How to use the Developer Certificate of Origin (DCO)

    main

    To contribute to the project, you must affirm that you have the necessary rights to submit your changes and that they can be submitted under the project's open source license (e.g., Apache 2.0).

    When DCO is mandatory, you must include a Signed-off-by trailer at the bottom of your commit message. You can do this manually or automatically using the -s or --signoff flag in Git.

    # Automatically add the Signed-off-by trailer when committing
    git commit -s -m "Your commit message"
  11. Simulate different OS window designs in DevTools

    main

    The application renders different window chrome based on the platform (macOS uses traffic-light buttons; Windows/Linux use custom controls). You can simulate these layouts in the renderer DevTools console using the OsDesign helper.

    Commands:

    • OsDesign.setMac(): Switches to macOS layout (traffic-light padding, no custom controls).
    • OsDesign.setWindows(): Switches to Windows/Linux layout (custom min/max/close buttons).
    • OsDesign.reset(): Restores real platform detection.
    • OsDesign.current(): Logs the currently active variant.

    Note: These commands write to sessionStorage and trigger a page reload. The override is visual only and does not affect logic like file paths or networking.

    // Run these in the DevTools Console
    OsDesign.setMac();
    OsDesign.setWindows();
    OsDesign.reset();
    OsDesign.current();