# How to Integrate Custom MCP Tools and Services with OpenDerisk

> Learn to integrate custom MCP tools and services with OpenDerisk using the addMCP API and startMCP. Expose your tools to AI agents via the Connectors Modal.

- Repository: [derisk-ai/openderisk](https://github.com/derisk-ai/openderisk)
- Tags: how-to-guide
- Published: 2026-02-28

---

**Integrating custom MCP tools with OpenDerisk requires registering your service via the `addMCP` API, starting the connection with `startMCP`, and selecting the active service in the Connectors Modal to expose its tools to AI agents.**

OpenDerisk ships with a **Modular Connector Platform (MCP)** that bridges external services into agent conversations. The `derisk-ai/openderisk` codebase provides HTTP endpoints and React components to manage **HTTP/SSE** or **STDIO**-based tools. This guide explains how to integrate custom MCP tools and services with OpenDerisk using the core API helpers in [`web/src/client/api/request.ts`](https://github.com/derisk-ai/openderisk/blob/main/web/src/client/api/request.ts) and the provided UI modals.

## MCP Architecture Overview

### MCP Service Model

An MCP entry in OpenDerisk is a structured record defining how the platform connects to your external tool. The required fields include:

- **`name`**: Display name shown in the UI (e.g., `"Risk Search API"`)
- **`type`**: Either `"http"` for SSE/REST endpoints or `"stdio"` for local binaries
- **`sse_url`**: The streaming endpoint URL (required for HTTP type)
- **`sse_headers`**: Optional JSON object for authentication headers
- **`token`**: Optional query parameter secret for simple auth

When you submit the form in [`CreatMcpModel.tsx`](https://github.com/derisk-ai/openderisk/blob/main/CreatMcpModel.tsx), the component validates these fields, parses the headers JSON (lines 70‑78), and POSTs the payload to `/api/v1/serve/mcp/create` via the `addMCP` function.

### Backend API Endpoints

All MCP lifecycle operations route through `/api/v1/serve/mcp/*` and are wrapped by `apiInterceptors` in [`request.ts`](https://github.com/derisk-ai/openderisk/blob/main/request.ts):

- **`addMCP`** (POST `/api/v1/serve/mcp/create`): Registers a new MCP definition (lines 447‑450)
- **`getMCPList`** (GET `/api/v1/serve/mcp/query`): Retrieves paginated MCP records (lines 442‑445)
- **`startMCP`** (POST `/api/v1/serve/mcp/start`): Initiates the connection/stream (lines 452‑455)
- **`offlineMCP`** (POST `/api/v1/serve/mcp/offline`): Terminates the service (lines 557‑560)
- **`deleteMCP`** (POST `/api/v1/serve/mcp/delete`): Removes the registry entry (lines 667‑670)
- **`mcpToolList`** (POST `/api/v1/serve/mcp/tool/list`): Discovers available tools (lines 673‑676)
- **`mcpToolRun`** (POST `/api/v1/serve/mcp/tool/run`): Executes a specific tool with arguments (lines 662‑665)

### Frontend UI Flow

The integration flow spans three main components in `web/src/app/mcp/` and `web/src/components/chat/`:

1. **Creation**: [`CreatMcpModel.tsx`](https://github.com/derisk-ai/openderisk/blob/main/CreatMcpModel.tsx) handles the create/edit modal, converting form data into the JSON payload for `addMCP`
2. **Management**: [`page.tsx`](https://github.com/derisk-ai/openderisk/blob/main/page.tsx) (McpPage) displays the grid of registered MCPs and wires the **Start**, **Stop**, and **Delete** buttons to `startMCP`, `offlineMCP`, and `deleteMCP`
3. **Selection**: [`connectors-modal.tsx`](https://github.com/derisk-ai/openderisk/blob/main/connectors-modal.tsx) fetches the running MCP list via `getMCPList` and propagates selected codes back to the chat component via `onMcpsChange`

## Step-by-Step Integration Guide

### Step 1: Build an SSE Service

Create a streaming endpoint that OpenDerisk can consume. Below is a minimal Python Flask example:

```python

# search_server.py

import json
import time
from flask import Flask, Response, request

app = Flask(__name__)

def format_sse(data: dict) -> str:
    return f"data: {json.dumps(data)}\n\n"

@app.route("/sse", methods=["GET"])
def search_stream():
    query = request.args.get("q", "")
    def event_generator():
        for i in range(1, 4):
            payload = {"tool": "search", "result": f"Item {i} for '{query}'"}
            yield format_sse(payload)
            time.sleep(0.3)
    return Response(event_generator(), mimetype="text/event-stream")

if __name__ == "__main__":
    app.run(port=5000)

```

Run this server to expose `http://localhost:5000/sse`.

### Step 2: Register the MCP Definition

Navigate to the **MCP** section in the OpenDerisk UI and click **Create**. Fill in the modal fields:

| Field | Value |
|-------|-------|
| **Name** | `Local Search` |
| **Type** | `http` |
| **SSE URL** | `http://localhost:5000/sse` |
| **Headers** | `{ "Authorization": "Bearer token123" }` |

When you click **Create**, [`CreatMcpModel.tsx`](https://github.com/derisk-ai/openderisk/blob/main/CreatMcpModel.tsx) executes:

```typescript
// Inside handleOk (lines 68-82)
form.validateFields().then(values => {
  if (values.sse_headers) {
    values.sse_headers = JSON.parse(values.sse_headers);
  }
  runAddMCP(values); // Calls apiInterceptors(addMCP(payload))
});

```

### Step 3: Start the Service

In the MCP dashboard ([`McpPage.tsx`](https://github.com/derisk-ai/openderisk/blob/main/McpPage.tsx)), locate your new entry and click **Start**. This triggers:

```typescript
// McpPage.tsx (lines 55-59)
const { run: runStartMCP } = useRequest(
  async params => await apiInterceptors(startMCP(params)),
  { manual: true }
);

```

The backend opens the SSE connection and marks the service as `available`.

### Step 4: Select Tools in Chat

Open a conversation and click the **Connectors** button (gear icon). The [`ConnectorsModal.tsx`](https://github.com/derisk-ai/openderisk/blob/main/ConnectorsModal.tsx) component fetches active MCPs:

```typescript
// connectors-modal.tsx (lines 84-87)
const { data: mcpList } = useRequest(async () => {
  const [, res] = await apiInterceptors(
    getMCPList({ filter: '' }, { page: "1", page_size: "100" })
  );
  return res?.items || [];
});

```

Tick the checkbox for your **Local Search** MCP. When the agent invokes a tool, the backend POSTs to `/api/v1/serve/mcp/tool/run` with:

```json
{
  "mcp_code": "local_search",
  "tool_name": "search",
  "args": { "q": "financial risk data" }
}

```

The result streams back into the conversation context automatically.

## Key Implementation Files

| File | Purpose |
|------|---------|
| [`web/src/client/api/request.ts`](https://github.com/derisk-ai/openderisk/blob/main/web/src/client/api/request.ts) | Centralized API helpers: `addMCP`, `startMCP`, `mcpToolRun`, etc. |
| [`web/src/app/mcp/CreatMcpModel.tsx`](https://github.com/derisk-ai/openderisk/blob/main/web/src/app/mcp/CreatMcpModel.tsx) | Modal for creating/editing MCP definitions |
| [`web/src/app/mcp/page.tsx`](https://github.com/derisk-ai/openderisk/blob/main/web/src/app/mcp/page.tsx) | Dashboard for listing, starting, and deleting MCPs |
| [`web/src/components/chat/connectors-modal.tsx`](https://github.com/derisk-ai/openderisk/blob/main/web/src/components/chat/connectors-modal.tsx) | Selector for attaching MCPs to conversations |
| [`web/src/components/chat/resource-modal.tsx`](https://github.com/derisk-ai/openderisk/blob/main/web/src/components/chat/resource-modal.tsx) | Alternative resource picker for MCP servers |

## Summary

- **MCP services** in OpenDerisk are HTTP/SSE endpoints or STDIO binaries defined by JSON records containing `name`, `type`, `sse_url`, and optional headers
- **Registration** occurs via `addMCP` (POST `/api/v1/serve/mcp/create`), implemented in [`CreatMcpModel.tsx`](https://github.com/derisk-ai/openderisk/blob/main/CreatMcpModel.tsx)
- **Lifecycle management** uses `startMCP` and `offlineMCP` to control connections, called from [`McpPage.tsx`](https://github.com/derisk-ai/openderisk/blob/main/McpPage.tsx)
- **Tool execution** flows through `mcpToolRun` when agents invoke methods from selected MCPs listed in [`connectors-modal.tsx`](https://github.com/derisk-ai/openderisk/blob/main/connectors-modal.tsx)
- **End-to-end workflow**: Build SSE service → Register via UI/API → Start connection → Select in Connectors Modal → Agent invokes tools automatically

## Frequently Asked Questions

### What is the Modular Connector Platform (MCP) in OpenDerisk?

The **Modular Connector Platform (MCP)** is OpenDerisk's extensibility layer that allows external tools—whether HTTP/SSE APIs or local command-line binaries—to integrate with AI agent workflows. MCP services register their endpoints and tool schemas with the platform, enabling agents to discover and invoke custom functionality during conversations.

### How do I authenticate my MCP service with OpenDerisk?

Authentication is handled via the **`sse_headers`** field when registering your MCP. In [`CreatMcpModel.tsx`](https://github.com/derisk-ai/openderisk/blob/main/CreatMcpModel.tsx), you can input a JSON object containing headers like `{ "Authorization": "Bearer YOUR_TOKEN" }`. These headers are parsed and sent with every request to your SSE endpoint. Alternatively, use the **`token`** field to pass a simple query parameter if your service requires URL-based authentication.

### Can I use local binaries instead of HTTP endpoints?

Yes. Set the **`type`** field to `"stdio"` instead of `"http"` when creating your MCP definition. This instructs OpenDerisk to spawn your tool as a local subprocess and communicate over standard input/output rather than HTTP. The UI in [`CreatMcpModel.tsx`](https://github.com/derisk-ai/openderisk/blob/main/CreatMcpModel.tsx) supports toggling between HTTP/SSE and STDIO modes, though STDIO implementations must adhere to the JSON-RPC protocol expected by the backend provider.

### How do I debug MCP tool calls that fail to execute?

First, verify the MCP is started (check the status indicator in [`McpPage.tsx`](https://github.com/derisk-ai/openderisk/blob/main/McpPage.tsx)). Next, inspect the browser network tab for calls to `/api/v1/serve/mcp/tool/run`—the response will contain error details from your service. Ensure your SSE endpoint returns properly formatted `data: ...` lines with valid JSON payloads. You can also test the endpoint independently using `curl` to confirm it streams events correctly before registering it with OpenDerisk.