# MCP Registry Discovery in TUUI: How It Works and Integrates with the UI

> Discover how TUUI finds MCP servers using the public MCP Registry API and displays results in a Vue card component. Learn about the seamless integration with the UI and gain valuable insights.

- Repository: [AIQL/tuui](https://github.com/ai-ql/tuui)
- Tags: internals
- Published: 2026-02-23

---

**TUUI discovers MCP servers by querying the public MCP Registry API at `registry.modelcontextprotocol.io`, then renders the results through a Vue-based card component wrapped in a dialog interface.**

The **ai-ql/tuui** repository implements a lightweight discovery mechanism that connects to the official MCP Registry to fetch available Model Context Protocol servers. This article explains how the [`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue) component handles API communication, pagination, and UI integration within the TUUI application.

## How TUUI Discovers MCP Servers via the Registry

### Querying the MCP Registry API

The discovery flow begins in [`src/renderer/components/common/RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/src/renderer/components/common/RegistryCard.vue), where the `fetchJson()` function constructs a targeted request to the registry endpoint:

```typescript
// RegistryCard.vue – lines 49-55
const baseUrl = 'https://registry.modelcontextprotocol.io/v0/servers'
const url = new URL(baseUrl)
if (search) url.searchParams.append('search', search)
url.searchParams.append('limit', queryLimit)
url.searchParams.append('version', 'latest')
if (cursor) url.searchParams.append('cursor', cursor)

```

The function appends four key query parameters to the base URL:

- **`search`** – Filters servers by user-entered keywords (optional)
- **`limit`** – Controls pagination size (defaults to `5`)
- **`version=latest`** – Forces the registry to return current server definitions
- **`cursor`** – Pagination token for fetching subsequent result pages

After constructing the URL, the component uses the native `fetch` API to execute the GET request and parse the JSON response:

```typescript
// RegistryCard.vue – lines 56-60
const res = await fetch(url.toString())
if (!res.ok) throw new Error(`HTTP error! Status: ${res.status}`)
return await res.json()

```

### Handling Pagination and State

[`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue) maintains three reactive state variables to manage the discovery workflow:

- **`loadingServers`** – Boolean indicator for UI loading states
- **`queryHistory`** – Map object that caches raw API responses (`McpRegistryType`) keyed by search string
- **`lastQuery`** – Computed property accessing the most recent result set for rendering

When users request additional results, the `getNext()` function extracts `metadata.nextCursor` from the cached response and initiates a follow-up request:

```typescript
// getNext() – lines 33-42
async function getNext() {
  const search = lastQueryString.value
  const nextCursor = lastQuery.value.metadata?.nextCursor
  if (!nextCursor) return

  const json = await fetchJson(search, nextCursor)
  if (json?.servers?.length) {
    queryHistory.value[search].servers.push(...json.servers)
    queryHistory.value[search].metadata = json.metadata
  }
}

```

## Integrating Registry Data into the TUUI Interface

### Rendering Server Cards with RegistryCard.vue

The `RegistryCard` component transforms raw registry data into interactive UI elements. It iterates over `lastQuery.servers` to display each server's metadata, including name, version, description, and repository links.

The helper function `getPackageUrl()` resolves external package URLs based on registry type:

```typescript
// getPackageUrl() – lines 66-84
function getPackageUrl(registry: McpRegistryPackage) {
  switch (registry.registryType) {
    case 'mcpb': return registry.identifier
    case 'npm':
      if (!registry.registryBaseUrl || registry.registryBaseUrl.includes('npmjs.org')) {
        return `https://www.npmjs.com/package/${registry.identifier}`
      }
    // … additional cases for oci, pypi, etc.
    default: return registry.registryBaseUrl ?? undefined
  }
}

```

### Dialog Wrapper in McpRegistryPage.vue

The [`McpRegistryPage.vue`](https://github.com/ai-ql/tuui/blob/main/McpRegistryPage.vue) component located at [`src/renderer/components/pages/McpRegistryPage.vue`](https://github.com/ai-ql/tuui/blob/main/src/renderer/components/pages/McpRegistryPage.vue) provides the container for the discovery interface. It wraps `RegistryCard` inside a Vuetify `v-dialog` component, handling open and close interactions while delegating all data fetching to the card component:

```vue
<!-- McpRegistryPage.vue – lines 34-44 -->
<v-dialog v-model="internalDialog" …>
  <RegistryCard>
    <v-btn … @click="closeDialog"></v-btn>
  </RegistryCard>
</v-dialog>

```

This architecture separates concerns: the page manages dialog state, while `RegistryCard` encapsulates the MCP Registry discovery logic and presentation.

## Type Safety and Registry Types

Type definitions in [`src/renderer/types/registry.ts`](https://github.com/ai-ql/tuui/blob/main/src/renderer/types/registry.ts) ensure type safety across the discovery workflow. The interfaces model the registry API response structure:

- **`McpRegistryPackage`** – Defines package metadata including `registryType`, `identifier`, and `registryBaseUrl`
- **`McpRegistryServer`** – Represents individual server entries with name, version, description, and package references
- **`McpRegistryType`** – Top-level response object containing `servers` array and `metadata` (including `nextCursor` for pagination)

These TypeScript definitions enable IntelliSense throughout [`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue) and prevent runtime errors when accessing nested registry properties.

## Summary

- **TUUI queries** the public MCP Registry at `registry.modelcontextprotocol.io/v0/servers` using the `fetchJson()` function in [`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue)
- **Query parameters** include `search`, `limit`, `version=latest`, and `cursor` for filtering and pagination
- **State management** uses reactive variables (`queryHistory`, `lastQuery`, `loadingServers`) to cache results and track loading states
- **UI integration** occurs through [`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue) for data rendering and [`McpRegistryPage.vue`](https://github.com/ai-ql/tuui/blob/main/McpRegistryPage.vue) as a dialog wrapper
- **Type safety** is enforced via interfaces in [`src/renderer/types/registry.ts`](https://github.com/ai-ql/tuui/blob/main/src/renderer/types/registry.ts) that model the registry API schema

## Frequently Asked Questions

### How does TUUI handle pagination when searching the MCP Registry?

TUUI implements cursor-based pagination through the `getNext()` function in [`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue). After the initial search, the component checks `lastQuery.value.metadata.nextCursor` from the registry response. If a cursor exists, it appends this token to the `cursor` query parameter and fetches the next page, concatenating new results to the existing `queryHistory` array.

### What registry types does TUUI support for package resolution?

According to the `getPackageUrl()` function in [`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue), TUUI handles multiple registry types defined in the `McpRegistryPackage` interface. The switch statement explicitly handles `mcpb` (returning the identifier directly), `npm` (constructing npmjs.org URLs), and falls back to `registryBaseUrl` for other types like OCI or PyPI containers.

### Where are the TypeScript definitions for MCP Registry responses located?

The type definitions reside in [`src/renderer/types/registry.ts`](https://github.com/ai-ql/tuui/blob/main/src/renderer/types/registry.ts). This file exports three primary interfaces: `McpRegistryType` (the root response object containing servers and metadata), `McpRegistryServer` (individual server entries), and `McpRegistryPackage` (package distribution details). These types provide compile-time safety for the JSON payloads returned by `registry.modelcontextprotocol.io`.

### Can users search for specific MCP servers by name in TUUI?

Yes, the discovery interface supports keyword filtering through the `search` query parameter. When users enter text in the search field and press Enter or click the search button, [`RegistryCard.vue`](https://github.com/ai-ql/tuui/blob/main/RegistryCard.vue) calls `getServers(queryString)`, which passes the search term to `fetchJson()`. The registry API filters results server-side and returns matching entries based on the provided keyword.