# Best Practices for Extending Chat-MCP with New MCP Capabilities

> Learn best practices for extending Chat-MCP with new MCP capabilities. Define data contracts, register schemas, and update configuration to easily add features.

- Repository: [AIQL/chat-mcp](https://github.com/ai-ql/chat-mcp)
- Tags: best-practices
- Published: 2026-02-23

---

**To extend Chat-MCP with new MCP capabilities, define the data contract in [`src/main/types.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/types.ts), register the capability schema in [`src/main/main.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/main.ts), and update the configuration to expose the feature through the automatic preload bridge.**

Chat-MCP is an Electron-based client for the Model Context Protocol (MCP) that coordinates between multiple MCP servers through a strict three-layer architecture. Understanding how to properly extend this application with new MCP capabilities requires following a schema-first design that maintains type safety across the main and renderer processes. This guide walks through the exact implementation patterns found in the ai-ql/chat-mcp repository.

## Understanding the Three-Layer Architecture

Chat-MCP’s extension model relies on three coordinated layers that handle configuration, inter-process communication (IPC), and renderer exposure.

### Configuration and Client Initialization

The bootstrap process begins in [`src/main/main.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/main.ts), where the application reads [`config.json`](https://github.com/ai-ql/chat-mcp/blob/main/config.json) to identify available MCP servers. For each entry, the code instantiates a `Client` and extracts advertised capabilities via `client.getServerCapabilities()` (lines 44-78). This discovery phase determines which features the application will expose to the user interface.

### IPC Registration Layer

For every capability reported by the server—such as `tools`, `prompts`, or `resources`—the main process constructs IPC channels following the pattern `<server>-<type>/<action>`. These channels wire to `manageRequests` using validation schemas that strictly define request and response shapes (lines 57-71 and 73-80). This layer ensures that all communication adheres to type-safe contracts before reaching the MCP server.

### Renderer Bridge

The preload script at [`src/preload/preload.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/preload/preload.ts) discovers initialized servers and builds a JavaScript object where keys represent server names and leaf objects contain async methods forwarding calls to the IPC channels (lines 30-60). This object exposes to the renderer as `window.mcpServers` through Electron's `contextBridge` (lines 62-65), creating a clean API surface that remains sandboxed from the main process.

## Step-by-Step Guide to Extending MCP Capabilities

Adding a new capability requires modifications across the type system, IPC registration, and configuration files.

### Step 1: Define the Data Contract in types.ts

Add a JSON schema or Zod schema to [`src/main/types.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/types.ts) describing the request and response payloads for your new capability. The `manageRequests` function uses this for runtime validation, and the client SDK uses it to generate correct TypeScript types.

```typescript
// src/main/types.ts
import { z } from "zod";

export const ReportAnalyticsResultSchema = z.object({
  success: z.boolean(),
  message: z.string().optional(),
});

export const StatsAnalyticsResultSchema = z.object({
  totalEvents: z.number(),
  activeUsers: z.number(),
});

```

### Step 2: Extend the IPC Map in main.ts

In [`src/main/main.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/main.ts), locate the `capabilitySchemas` object. Add a new top-level key for your capability (e.g., `analytics`) and map its actions to the schemas created in step one (lines 57-71). The `registerIpcHandlers` loop automatically creates IPC handlers for each action without additional wiring.

```typescript
// src/main/main.ts
const capabilitySchemas = {
  // existing capabilities...
  analytics: {
    report: ReportAnalyticsResultSchema,
    stats: StatsAnalyticsResultSchema,
  },
};

```

### Step 3: Update the Server Configuration

Insert a new entry under `mcpServers` in [`src/main/config.json`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/config.json) (or the user-provided config file) that includes the new capability name. The bootstrap code reads this file and includes the new capability when building the features list.

```json
{
  "mcpServers": {
    "myMcp": {
      "command": "node ./my-mcp-server.js",
      "analytics": {}
    }
  }
}

```

### Step 4: Access from the Renderer Process

No code changes are required in the preload script. The [`preload.ts`](https://github.com/ai-ql/chat-mcp/blob/main/preload.ts) file automatically discovers the new capability name and creates method stubs via `api[name][capability] = createAPIMethods(...)`. The renderer can immediately call `window.mcpServers.<server>.<capability>.<action>()` without additional boilerplate.

```typescript
// Renderer process
await window.mcpServers.myMcp.analytics.report({ event: "login", userId: "123" });
const stats = await window.mcpServers.myMcp.analytics.stats();
console.log(stats);

```

### Step 5: Add Custom Client Methods (Optional)

If the underlying MCP server requires a custom client method, extend [`src/main/client.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/client.ts) to expose a typed wrapper that calls the appropriate schema-validated method.

```typescript
// src/main/client.ts
export async function sendAnalyticsReport(client: Client, payload: any) {
  return await client.invoke("analytics/report", payload);
}

```

## Key Architectural Considerations

When extending Chat-MCP with new capabilities, adhere to these architectural constraints to maintain system integrity:

- **Naming Convention**: All IPC channels follow `<server>-<type>/<action>`. Maintain this pattern so that the `registerIpcHandlers` loop can process them automatically without manual registration code.

- **Schema-First Design**: All validation lives in the schema objects defined in [`src/main/types.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/types.ts). Adding a capability without a corresponding schema causes runtime errors when `manageRequests` attempts to validate incoming requests.

- **Lazy Exposure**: The preload script builds the API only after `listClients()` resolves. This ensures that any new server reporting additional capabilities reflects instantly in the renderer without requiring an application reload.

- **Separation of Concerns**: The renderer never communicates directly with MCP processes. All traffic routes through IPC handlers, keeping the UI sandboxed and preventing unauthorized access to system resources.

## Summary

- Define data contracts in [`src/main/types.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/types.ts) using Zod or JSON schemas to ensure type safety.
- Register capabilities in [`src/main/main.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/main.ts) by extending the `capabilitySchemas` object to enable automatic IPC handler creation.
- Update [`src/main/config.json`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/config.json) to advertise new server capabilities under the `mcpServers` key.
- The preload bridge exposes new capabilities automatically via `window.mcpServers` without modifying [`src/preload/preload.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/preload/preload.ts).
- Follow the `<server>-<type>/<action>` naming convention for IPC channels to maintain compatibility with the registration loop.

## Frequently Asked Questions

### Where should I define validation schemas for new MCP capabilities?

Define all validation schemas in [`src/main/types.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/types.ts). The `manageRequests` function relies on these schemas for runtime validation, and they provide compile-time type safety for both the server implementation and UI components. Using Zod or JSON schemas here ensures that invalid requests never reach your MCP server.

### Do I need to modify the preload script when adding new capabilities?

No. The [`src/preload/preload.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/preload/preload.ts) script dynamically discovers capabilities from initialized clients through `listClients()` and automatically creates method stubs using `createAPIMethods`. As long as you register the capability in [`src/main/main.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/main.ts) and [`src/main/config.json`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/config.json), the bridge exposes it to the renderer as `window.mcpServers.<server>.<capability>`.

### What naming convention should I use for IPC channels?

Follow the pattern `<server>-<type>/<action>` (e.g., `myServer-analytics/report` or `myServer-tools/list`). This convention is hardcoded into the `registerIpcHandlers` loop in [`src/main/main.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/main.ts). Deviating from this pattern will result in channels that the automatic registration logic cannot detect or wire to validation schemas.

### How does Chat-MCP ensure type safety when extending capabilities?

Chat-MCP uses a schema-first architecture where [`src/main/types.ts`](https://github.com/ai-ql/chat-mcp/blob/main/src/main/types.ts) defines Zod or JSON schemas for all capabilities. The `manageRequests` function validates all IPC traffic against these schemas before forwarding to the MCP client. This prevents type mismatches between the MCP server and the Electron client while providing autocomplete and type checking in the renderer process.