Skip to content

Commit d2ad93e

Browse files
committed
Update syntax
1 parent 4f6286a commit d2ad93e

File tree

1 file changed

+129
-121
lines changed

1 file changed

+129
-121
lines changed

netlify/edge-functions/mcp-server.ts

Lines changed: 129 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
22
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
3-
import { toReqRes, toFetchResponse } from "fetch-to-node";
3+
import { toFetchResponse, toReqRes } from "fetch-to-node";
44
import z from "zod";
55
import type { Config } from "@netlify/edge-functions";
66

@@ -9,154 +9,162 @@ const projectId = Netlify.env.get("KAPA_PROJECT_ID");
99
const integrationId = Netlify.env.get("KAPA_INTEGRATION_ID");
1010

1111
if (!apiKey || !projectId || !integrationId) {
12-
throw new Error(
13-
"Missing required environment variables: KAPA_API_KEY, KAPA_PROJECT_ID, or KAPA_INTEGRATION_ID"
14-
);
12+
throw new Error(
13+
"Missing required environment variables: KAPA_API_KEY, KAPA_PROJECT_ID, or KAPA_INTEGRATION_ID"
14+
);
1515
}
1616

1717
interface QuestionResponse {
18-
answer: string;
19-
relevant_sources: Array<{
20-
source_url: string;
21-
title: string;
22-
contains_internal_data: boolean;
23-
}>;
18+
answer: string;
19+
relevant_sources: Array<{
20+
source_url: string;
21+
title: string;
22+
contains_internal_data: boolean;
23+
}>;
2424
}
2525

2626
interface SearchResponse {
27-
search_results: Array<{
28-
title: string;
29-
source_url: string;
30-
content: string;
31-
source_type: string;
32-
}>;
27+
search_results: Array<{
28+
title: string;
29+
source_url: string;
30+
content: string;
31+
source_type: string;
32+
}>;
3333
}
3434

3535
function getServer(): McpServer {
36-
const server = new McpServer(
37-
{
38-
name: "Astro Docs server",
39-
version: "1.0.0",
40-
},
41-
{
42-
capabilities: {
43-
logging: {},
44-
},
45-
}
46-
);
47-
server.tool(
48-
"ask_astro_question",
49-
"Ask a technical question about Astro",
50-
{ query: z.string().describe("The question to ask about Astro") },
51-
async ({ query }) => {
52-
if (!query) {
53-
throw new Error("Query is required");
54-
}
55-
const result = await sendKapaRequest<QuestionResponse>("chat", query);
56-
if ("error" in result) {
57-
return formatResponse(result);
58-
}
59-
return formatResponse({
60-
answer: result.answer,
61-
relevant_sources: result.relevant_sources.map(
62-
({ source_url, title }) => ({
63-
source_url,
64-
title,
65-
})
66-
),
67-
});
68-
}
69-
);
36+
const server = new McpServer(
37+
{
38+
name: "Astro Docs server",
39+
version: "1.0.0",
40+
},
41+
{
42+
capabilities: {
43+
logging: {},
44+
},
45+
}
46+
);
47+
server.registerTool(
48+
"ask_astro_question",
49+
{
50+
title: "Ask Astro question",
51+
description: "Ask a technical question about Astro framework",
52+
inputSchema: {
53+
query: z.string().describe("The question to ask about Astro"),
54+
},
55+
},
56+
async ({ query }) => {
57+
if (!query) {
58+
throw new Error("Query is required");
59+
}
60+
const result = await sendKapaRequest<QuestionResponse>("chat", query);
61+
if ("error" in result) {
62+
return formatResponse(result);
63+
}
64+
return formatResponse({
65+
answer: result.answer,
66+
relevant_sources: result.relevant_sources.map(
67+
({ source_url, title }) => ({
68+
source_url,
69+
title,
70+
})
71+
),
72+
});
73+
}
74+
);
7075

71-
server.tool(
72-
"search_astro_docs",
73-
"Search official Astro docs",
74-
{ query: z.string().describe("Search query") },
75-
async ({ query }) => {
76-
if (!query) {
77-
throw new Error("Query is required");
78-
}
79-
const result = await sendKapaRequest<SearchResponse>("search", query);
80-
return formatResponse(result);
81-
}
82-
);
83-
return server;
76+
server.registerTool(
77+
"search_astro_docs",
78+
{
79+
title: "Search Astro Docs",
80+
description: "Search the official Astro framework docs",
81+
inputSchema: { query: z.string().describe("Search query") },
82+
},
83+
async ({ query }) => {
84+
if (!query) {
85+
throw new Error("Query is required");
86+
}
87+
const result = await sendKapaRequest<SearchResponse>("search", query);
88+
return formatResponse(result);
89+
}
90+
);
91+
return server;
8492
}
8593

8694
async function sendKapaRequest<T = unknown>(
87-
action: string,
88-
query: string
95+
action: string,
96+
query: string
8997
): Promise<T | { error: string }> {
90-
const url = `https://api.kapa.ai/query/v1/projects/${projectId}/${action}/`;
98+
const url = `https://api.kapa.ai/query/v1/projects/${projectId}/${action}/`;
9199

92-
const response = await fetch(url, {
93-
method: "POST",
94-
headers: {
95-
"Content-Type": "application/json",
96-
"X-API-KEY": apiKey!,
97-
},
98-
body: JSON.stringify({
99-
query,
100-
integration_id: integrationId,
101-
}),
102-
});
103-
if (!response.ok) {
104-
const errorText = await response.text();
105-
console.error(`Kapa API error: ${errorText}`);
106-
return {
107-
error: "Error: Unable to fetch data from API. Please try again later.",
108-
};
109-
}
110-
return await response.json();
100+
const response = await fetch(url, {
101+
method: "POST",
102+
headers: {
103+
"Content-Type": "application/json",
104+
"X-API-KEY": apiKey!,
105+
},
106+
body: JSON.stringify({
107+
query,
108+
integration_id: integrationId,
109+
}),
110+
});
111+
if (!response.ok) {
112+
const errorText = await response.text();
113+
console.error(`Kapa API error: ${errorText}`);
114+
return {
115+
error: "Error: Unable to fetch data from API. Please try again later.",
116+
};
117+
}
118+
return await response.json();
111119
}
112120

113121
function formatResponse(data: unknown): {
114-
content: Array<{ type: "text"; text: string }>;
122+
content: Array<{ type: "text"; text: string }>;
115123
} {
116-
return {
117-
content: [
118-
{
119-
type: "text",
120-
text: JSON.stringify(data, null, 2),
121-
},
122-
],
123-
};
124+
return {
125+
content: [
126+
{
127+
type: "text",
128+
text: JSON.stringify(data, null, 2),
129+
},
130+
],
131+
};
124132
}
125133

126134
// Netlify Edge Function Handler
127135
export default async function handler(req: Request) {
128-
try {
129-
const { req: nodeReq, res: nodeRes } = toReqRes(req);
130-
const server = getServer();
131-
const transport = new StreamableHTTPServerTransport({
132-
sessionIdGenerator: undefined, // Stateless mode
133-
});
134-
await server.connect(transport);
136+
try {
137+
const { req: nodeReq, res: nodeRes } = toReqRes(req);
138+
const server = getServer();
139+
const transport = new StreamableHTTPServerTransport({
140+
sessionIdGenerator: undefined, // Stateless mode
141+
});
142+
await server.connect(transport);
135143

136-
// Parse request body as JSON
137-
const body = await req.json();
144+
// Parse request body as JSON
145+
const body = await req.json();
138146

139-
// Handle the request through the transport
140-
await transport.handleRequest(nodeReq, nodeRes, body);
147+
// Handle the request through the transport
148+
await transport.handleRequest(nodeReq, nodeRes, body);
141149

142-
// Handle response closing
143-
nodeRes.on("close", () => {
144-
transport.close();
145-
server.close();
146-
});
150+
// Handle response closing
151+
nodeRes.on("close", () => {
152+
transport.close();
153+
server.close();
154+
});
147155

148-
// Convert Node.js ServerResponse back to Web API Response
149-
return toFetchResponse(nodeRes);
150-
} catch (error) {
151-
const errorMessage = error instanceof Error ? error.message : String(error);
152-
console.error("Error in MCP server:", errorMessage);
153-
return Response.json(formatResponse({ error: errorMessage }), {
154-
status: 500,
155-
});
156-
}
156+
// Convert Node.js ServerResponse back to Web API Response
157+
return toFetchResponse(nodeRes);
158+
} catch (error) {
159+
const errorMessage = error instanceof Error ? error.message : String(error);
160+
console.error("Error in MCP server:", errorMessage);
161+
return Response.json(formatResponse({ error: errorMessage }), {
162+
status: 500,
163+
});
164+
}
157165
}
158166

159167
export const config: Config = {
160-
path: "/mcp",
161-
method: "POST",
168+
path: "/mcp",
169+
method: "POST",
162170
};

0 commit comments

Comments
 (0)