Skip to content

Commit 381809f

Browse files
Add MCP server for Redpanda docs (#117)
* Add MCP server for Redpanda docs * Add rate limit * Remove search tool * Reduce requests per minute * Test help * Update doc * Apply suggestions from code review * Apply suggestions from code review * Update node version * Apply suggestions from code review Co-authored-by: Michele Cyran <[email protected]> --------- Co-authored-by: Michele Cyran <[email protected]>
1 parent 71ce0ca commit 381809f

File tree

5 files changed

+444
-49
lines changed

5 files changed

+444
-49
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ node_modules
77
.bundle
88
# Local Netlify folder
99
.netlify
10+
.env
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
= MCP Remote Server for Redpanda Documentation
2+
:description: Learn how to connect to the Redpanda documentation MCP server in Cursor, VS Code, and other AI tools.
3+
4+
Redpanda provides a remote link:https://modelcontextprotocol.io[Model Context Protocol (MCP) server^] that lets you access documentation directly from your IDE or AI tool, such as Cursor, VS Code, or Claude.
5+
6+
The MCP server is hosted at: `https://docs.redpanda.com/mcp`.
7+
You can add this endpoint to any AI agent that supports MCP.
8+
9+
== Set up
10+
11+
[tabs]
12+
====
13+
Cursor::
14+
+
15+
--
16+
link:cursor://mcp/add?name=redpanda&url=https://docs.redpanda.com/mcp[Automatically add the Redpanda MCP server to Cursor].
17+
18+
Or, manually add it by editing your `.cursor/mcp.json` file, as explained in the https://docs.cursor.com/context/model-context-protocol[Cursor documentation^]:
19+
20+
[source,json]
21+
----
22+
{
23+
"mcpServers": {
24+
"redpanda": {
25+
"url": "https://docs.redpanda.com/mcp"
26+
}
27+
}
28+
}
29+
----
30+
--
31+
VS Code::
32+
+
33+
--
34+
link:https://vscode.dev/redirect/mcp/install?name=redpanda&config=%7B%22type%22%3A%22http%22%2C%22url%22%3A%22https%3A%2F%2Fdocs.redpanda.com%2Fmcp%22%7D[Automatically add the Redpanda MCP server to VS Code].
35+
36+
Or, manually create a `.vscode/mcp.json` file in your workspace:
37+
38+
.`.vscode/mcp.json`
39+
[source,json]
40+
----
41+
{
42+
"servers": {
43+
"redpanda": {
44+
"url": "https://docs.redpanda.com/mcp"
45+
}
46+
}
47+
}
48+
----
49+
50+
To configure globally, run **MCP: Open User Configuration** from the Command Palette and add the same JSON to the `mcp` section.
51+
52+
For more, see the https://code.visualstudio.com/docs/copilot/chat/mcp-servers[VS Code MCP documentation^].
53+
--
54+
Claude Desktop::
55+
+
56+
--
57+
To add Redpanda's MCP server to Claude Desktop:
58+
59+
**On macOS:**
60+
`~/Library/Application Support/Claude/claude_desktop_config.json`
61+
62+
**On Windows:**
63+
`%APPDATA%\Claude\claude_desktop_config.json`
64+
65+
Add this configuration:
66+
67+
[source,json]
68+
----
69+
{
70+
"mcpServers": {
71+
"redpanda": {
72+
"url": "https://docs.redpanda.com/mcp"
73+
}
74+
}
75+
}
76+
----
77+
78+
Restart Claude Desktop for changes to take effect.
79+
--
80+
====
81+
82+
== Other AI tools
83+
84+
Any tool that supports MCP servers can connect using the following URL:
85+
86+
[source,text]
87+
----
88+
https://docs.redpanda.com/mcp
89+
----
90+
91+
Popular tools that support MCP:
92+
93+
* https://github.com/microsoft/vscode-copilot[GitHub Copilot^] (VS Code 1.102+)
94+
* https://www.jetbrains.com/ai/[JetBrains AI Assistant^]
95+
* https://zed.dev/[Zed Editor^]
96+
* Tools built with the https://modelcontextprotocol.io/[MCP SDK^]
97+
98+
== What you can do
99+
100+
Once connected, you can ask context-aware questions from within your editor:
101+
102+
* "How do I configure Redpanda for production?"
103+
* "What are the instructions for running Redpanda in a local kind cluster?"
104+
* "What are the best practices for topic partitioning?"
105+
* "How do I set up Redpanda Connect?"
106+
* "What are Redpanda's security features?"
107+
108+
== Usage limits
109+
110+
To ensure fair use and performance, the server enforces a rate limit of **30 questions per 15-minute window**.
111+
112+
If you exceed the limit, wait a few minutes before retrying.

netlify.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build.environment]
2-
NODE_VERSION = "18"
2+
NODE_VERSION = "20"
33

44
[dev]
55
publish = "docs/"

netlify/edge-functions/mcp.js

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// Redpanda Docs MCP Server on Netlify Edge Functions
2+
// ---------------------------------------------------
3+
// This Edge Function implements an authless MCP (Model Context Protocol) server
4+
// that proxies requests to Kapa AI’s chat and search APIs for Redpanda documentation.
5+
// It uses the official MCP SDK plus the Netlify adapter (modelfetch) to support
6+
// JSON-RPC over HTTP and SSE streaming.
7+
//
8+
// For background and reference implementations, see:
9+
// - Kapa AI blog: Build an MCP Server with Kapa AI
10+
// https://www.kapa.ai/blog/build-an-mcp-server-with-kapa-ai
11+
// - Netlify guide: Writing MCPs on Netlify
12+
// https://developers.netlify.com/guides/write-mcps-on-netlify/
13+
//
14+
// Key challenges on Netlify Edge:
15+
// 1. ESM-only runtime: import via https://esm.sh for all modules (no local npm installs).
16+
// 2. Edge transport: leverage the `streamingHttp` protocol via the `@modelfetch/netlify` adapter, which under the hood uses `StreamableHTTPServerTransport` to handle SSE streams in Edge environments. Adapter docs:
17+
// - Modelfetch npm: https://www.npmjs.com/package/@modelfetch/netlify
18+
// - Modelfetch GitHub: https://github.com/modelcontextprotocol/modelfetch
19+
// 3. Header requirements: MCP expects both application/json and text/event-stream in Accept,
20+
// and requires Content-Type: application/json on incoming JSON-RPC messages.
21+
22+
import { McpServer } from 'https://esm.sh/@modelcontextprotocol/[email protected]/server/mcp.js'
23+
import { z } from 'https://esm.sh/[email protected]'
24+
import handle from "https://esm.sh/@modelfetch/[email protected]";
25+
import rateLimiter from 'https://esm.sh/[email protected]';
26+
27+
const API_BASE = "https://api.kapa.ai";
28+
// Fetch Netlify env vars
29+
const KAPA_API_KEY = Netlify.env.get('KAPA_API_KEY');
30+
const KAPA_PROJECT_ID = Netlify.env.get('KAPA_PROJECT_ID');
31+
const KAPA_INTEGRATION_ID = Netlify.env.get('KAPA_INTEGRATION_ID');
32+
33+
// Initialize MCP Server and register tools
34+
const server = new McpServer({
35+
name: "Redpanda Docs MCP", // Display name visible for inspectors
36+
version: "0.1.0",
37+
});
38+
39+
server.registerTool(
40+
"ask_redpanda_question",
41+
{
42+
title: "Ask Redpanda Question",
43+
description: "Ask a question about Redpanda documentation",
44+
inputSchema: { question: z.string() },
45+
},
46+
async ({ question }) => {
47+
try {
48+
const response = await fetch(
49+
`${API_BASE}/query/v1/projects/${KAPA_PROJECT_ID}/chat/`,
50+
{
51+
method: "POST",
52+
headers: {
53+
"Content-Type": "application/json",
54+
"X-API-KEY": KAPA_API_KEY,
55+
},
56+
body: JSON.stringify({
57+
integration_id: KAPA_INTEGRATION_ID,
58+
query: question,
59+
}),
60+
}
61+
);
62+
// Always handle as JSON (Kapa API returns JSON)
63+
if (!response.ok) {
64+
return {
65+
content: [
66+
{
67+
type: "text",
68+
text: `Redpanda Docs MCP error: ${response.status} - ${response.statusText}`,
69+
},
70+
],
71+
};
72+
}
73+
const chatData = await response.json();
74+
return {
75+
content: [
76+
{
77+
type: "text",
78+
text: (chatData.answer || "No answer received"),
79+
},
80+
],
81+
};
82+
} catch (error) {
83+
console.log(`[ask_redpanda_question] Exception:`, error);
84+
return {
85+
content: [
86+
{
87+
type: "text",
88+
text: `Error: Failed to call kapa.ai API - ${error instanceof Error ? error.message : String(error)}`,
89+
},
90+
],
91+
};
92+
}
93+
}
94+
);
95+
96+
// Wrap the server with the Netlify Edge handler
97+
// ---------------------------------------------
98+
// The `handle` function from `@modelfetch/netlify` does several things:
99+
// 1. Adapts the Edge `fetch` Request/Response to the Node-style HTTP transport
100+
// that the MCP SDK expects (using streamingHttp under the hood).
101+
// 2. Parses incoming JSON-RPC payloads from the request body.
102+
// 3. Routes `initialize`, `tool:discover`, and `tool:invoke` JSON-RPC methods
103+
// to the registered tools on our `server` instance.
104+
// 4. Manages Server-Sent Events (SSE) streaming: it takes ReadableStreams
105+
// returned by streaming tools and writes them as
106+
// text/event-stream chunks back through the Edge Function response.
107+
// 5. Handles error formatting according to JSON-RPC (wrapping exceptions in
108+
// appropriate error objects).
109+
const baseHandler = handle({
110+
server: server,
111+
pre: (app) => {
112+
app.use(
113+
"/mcp",
114+
rateLimiter.rateLimiter({
115+
windowMs: 15 * 60 * 1000, // 15 minutes
116+
limit: 30, // 30 requests per window
117+
keyGenerator: (c) => c.ip,
118+
}),
119+
);
120+
},
121+
});
122+
123+
// Wrapper to handle both browser requests (show docs) and MCP client requests
124+
export default async (request, context) => {
125+
// Check if this is a browser request (not an MCP client)
126+
const userAgent = request.headers.get('user-agent') || '';
127+
const accept = request.headers.get('accept') || '';
128+
const contentType = request.headers.get('content-type') || '';
129+
130+
// Detect browser requests:
131+
// - User-Agent contains browser identifiers
132+
// - Accept header includes text/html
133+
// - NOT a JSON-RPC POST request
134+
const isBrowserRequest = (
135+
request.method === 'GET' &&
136+
(userAgent.includes('Mozilla') || userAgent.includes('Chrome') || userAgent.includes('Safari') || userAgent.includes('Edge')) &&
137+
accept.includes('text/html') &&
138+
!contentType.includes('application/json')
139+
);
140+
141+
// If it's a browser request, redirect to the documentation page
142+
if (isBrowserRequest) {
143+
return new Response(null, {
144+
status: 302,
145+
headers: {
146+
'Location': '/home/mcp-setup', // Redirect to the built docs page
147+
},
148+
});
149+
}
150+
151+
// Otherwise, handle as MCP client request
152+
const patchedHeaders = new Headers(request.headers);
153+
patchedHeaders.set('accept', 'application/json, text/event-stream');
154+
patchedHeaders.set('content-type', 'application/json');
155+
156+
const patchedRequest = new Request(request, { headers: patchedHeaders });
157+
return baseHandler(patchedRequest, context);
158+
};
159+
160+
export const config = { path: "/mcp" };
161+

0 commit comments

Comments
 (0)