mirror of
https://github.com/bendtherules/ask262.git
synced 2026-08-18 21:31:46 +00:00
Add mcp http server
This commit is contained in:
@@ -98,18 +98,45 @@ Add to your MCP client configuration:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**OpenCode (`.opencode/mcp.json`):**
|
**OpenCode (`.opencode/mcp.json` or `~/.config/opencode/opencode.json`):**
|
||||||
|
|
||||||
|
*stdio (local process):*
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"servers": {
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"mcp": {
|
||||||
"ask262": {
|
"ask262": {
|
||||||
"command": "bun",
|
"type": "local",
|
||||||
"args": ["run", "src/mcp-server.ts"]
|
"command": ["bun", "run", "src/mcp-server.ts"],
|
||||||
|
"enabled": true,
|
||||||
|
"environment": {
|
||||||
|
"OLLAMA_HOST": "http://localhost:11434"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*http (stateless JSON server):*
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"mcp": {
|
||||||
|
"ask262": {
|
||||||
|
"type": "remote",
|
||||||
|
"url": "http://localhost:3000/mcp",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the HTTP server first:
|
||||||
|
```bash
|
||||||
|
bun run mcp-http # Development
|
||||||
|
ask262-http # After npm install -g
|
||||||
|
```
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
Test the MCP server before configuring your client:
|
Test the MCP server before configuring your client:
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ Add to your MCP client configuration using `bunx` (no installation required):
|
|||||||
```
|
```
|
||||||
|
|
||||||
**OpenCode** (global config `~/.config/opencode/opencode.json` or project config `opencode.json`):
|
**OpenCode** (global config `~/.config/opencode/opencode.json` or project config `opencode.json`):
|
||||||
|
|
||||||
|
*stdio (local process):*
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"$schema": "https://opencode.ai/config.json",
|
"$schema": "https://opencode.ai/config.json",
|
||||||
@@ -63,6 +65,28 @@ Add to your MCP client configuration using `bunx` (no installation required):
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*http (stateless JSON server):*
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"mcp": {
|
||||||
|
"ask262": {
|
||||||
|
"type": "remote",
|
||||||
|
"url": "http://localhost:3000/mcp",
|
||||||
|
"enabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the HTTP server first:
|
||||||
|
```bash
|
||||||
|
bun run mcp-http # Development
|
||||||
|
ask262-http # After npm install -g
|
||||||
|
# Or with custom port:
|
||||||
|
ASK262_PORT=8080 ask262-http
|
||||||
|
```
|
||||||
|
|
||||||
## Available Tools
|
## Available Tools
|
||||||
|
|
||||||
| Tool | Description |
|
| Tool | Description |
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"cheerio": "^1.2.0",
|
"cheerio": "^1.2.0",
|
||||||
"glob": "^13.0.6",
|
"glob": "^13.0.6",
|
||||||
"graphology": "^0.26.0",
|
"graphology": "^0.26.0",
|
||||||
|
"hono": "^4.12.14",
|
||||||
"langchain": "^0.2.0",
|
"langchain": "^0.2.0",
|
||||||
"ora": "^9.3.0",
|
"ora": "^9.3.0",
|
||||||
},
|
},
|
||||||
@@ -270,7 +271,7 @@
|
|||||||
|
|
||||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||||
|
|
||||||
"hono": ["hono@4.12.12", "", {}, "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q=="],
|
"hono": ["hono@4.12.14", "", {}, "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w=="],
|
||||||
|
|
||||||
"htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="],
|
"htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="],
|
||||||
|
|
||||||
@@ -496,6 +497,8 @@
|
|||||||
|
|
||||||
"@langchain/ollama/uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
"@langchain/ollama/uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="],
|
||||||
|
|
||||||
|
"@modelcontextprotocol/sdk/hono": ["hono@4.12.12", "", {}, "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q=="],
|
||||||
|
|
||||||
"apache-arrow/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
|
"apache-arrow/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
|
||||||
|
|
||||||
"chalk-template/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"chalk-template/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|||||||
+4
-1
@@ -9,7 +9,8 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "src/mcp-server.ts",
|
"main": "src/mcp-server.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
"ask262": "src/mcp-server.ts"
|
"ask262": "src/mcp-server.ts",
|
||||||
|
"ask262-http": "src/mcp-server-http.ts"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"type-check": "tsc --noEmit",
|
"type-check": "tsc --noEmit",
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
"test-evaluate-timeout": "bun run src/test/manual/test-evaluate-timeout.ts",
|
"test-evaluate-timeout": "bun run src/test/manual/test-evaluate-timeout.ts",
|
||||||
"test-search-spec-sections": "bun run src/test/manual/test-search-spec-sections.ts",
|
"test-search-spec-sections": "bun run src/test/manual/test-search-spec-sections.ts",
|
||||||
"test-mcp-server": "bun run src/test/manual/test-mcp-server.ts",
|
"test-mcp-server": "bun run src/test/manual/test-mcp-server.ts",
|
||||||
|
"mcp-http": "bun run src/mcp-server-http.ts",
|
||||||
"test": "bun test",
|
"test": "bun test",
|
||||||
"prepublish": "npm run type-check && npm run lint && npm pack --dry-run",
|
"prepublish": "npm run type-check && npm run lint && npm pack --dry-run",
|
||||||
"release": "bash scripts/release.sh --patch"
|
"release": "bash scripts/release.sh --patch"
|
||||||
@@ -57,6 +59,7 @@
|
|||||||
"cheerio": "^1.2.0",
|
"cheerio": "^1.2.0",
|
||||||
"glob": "^13.0.6",
|
"glob": "^13.0.6",
|
||||||
"graphology": "^0.26.0",
|
"graphology": "^0.26.0",
|
||||||
|
"hono": "^4.12.14",
|
||||||
"langchain": "^0.2.0",
|
"langchain": "^0.2.0",
|
||||||
"ora": "^9.3.0"
|
"ora": "^9.3.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,300 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ask262 MCP HTTP Server (Non-streaming)
|
||||||
|
* Provides HTTP-based MCP with normal JSON responses (no SSE).
|
||||||
|
* Uses Hono framework for clean routing and middleware.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { serve } from "@hono/node-server";
|
||||||
|
import * as lancedbSdk from "@lancedb/lancedb";
|
||||||
|
import { OllamaEmbeddings } from "@langchain/ollama";
|
||||||
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
||||||
|
import { Hono } from "hono";
|
||||||
|
import { cors } from "hono/cors";
|
||||||
|
import { z } from "zod";
|
||||||
|
import {
|
||||||
|
createEvaluateInEngine262Tool,
|
||||||
|
createGetSectionContentTool,
|
||||||
|
createSearchSpecSectionsTool,
|
||||||
|
evaluateInputSchema,
|
||||||
|
evaluateOutputSchema,
|
||||||
|
evaluateToolMetadata,
|
||||||
|
evaluateToolName,
|
||||||
|
getSectionInputSchema,
|
||||||
|
getSectionOutputSchema,
|
||||||
|
searchSpecInputSchema,
|
||||||
|
searchSpecOutputSchema,
|
||||||
|
searchSpecToolMetadata,
|
||||||
|
searchSpecToolName,
|
||||||
|
sectionContentToolMetadata,
|
||||||
|
sectionContentToolName,
|
||||||
|
} from "./agent-tools/index.js";
|
||||||
|
import {
|
||||||
|
EMBEDDING_MODEL,
|
||||||
|
STORAGE_DIR as STORAGE_DIR_REL,
|
||||||
|
} from "./constants.js";
|
||||||
|
|
||||||
|
// Resolve storage path relative to this script's directory
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const STORAGE_DIR = path.resolve(__dirname, "..", STORAGE_DIR_REL);
|
||||||
|
|
||||||
|
// Initialize embeddings
|
||||||
|
const embeddings = new OllamaEmbeddings({
|
||||||
|
model: EMBEDDING_MODEL,
|
||||||
|
baseUrl: process.env.OLLAMA_HOST,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Server port (default: 3000)
|
||||||
|
const PORT = Number(process.env.ASK262_PORT) || 3000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory function to create a fresh MCP server instance.
|
||||||
|
* Each HTTP request gets its own isolated server (stateless mode).
|
||||||
|
*/
|
||||||
|
async function createMcpServer() {
|
||||||
|
// Connect to LanceDB
|
||||||
|
const db = await lancedbSdk.connect(STORAGE_DIR);
|
||||||
|
const table = await db.openTable("spec_vectors");
|
||||||
|
|
||||||
|
// Create tool instances
|
||||||
|
const searchSpecTool = createSearchSpecSectionsTool(table, embeddings);
|
||||||
|
const getSectionContentTool = createGetSectionContentTool(table);
|
||||||
|
const evaluateTool = createEvaluateInEngine262Tool();
|
||||||
|
|
||||||
|
// Create MCP server
|
||||||
|
const server = new McpServer(
|
||||||
|
{
|
||||||
|
name: "ask262-server",
|
||||||
|
version: "1.0.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
capabilities: {
|
||||||
|
prompts: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register search spec tool
|
||||||
|
server.registerTool(
|
||||||
|
searchSpecToolName,
|
||||||
|
{
|
||||||
|
description: searchSpecToolMetadata.description,
|
||||||
|
inputSchema: searchSpecInputSchema,
|
||||||
|
outputSchema: searchSpecOutputSchema,
|
||||||
|
annotations: {
|
||||||
|
readOnlyHint: true,
|
||||||
|
idempotentHint: true,
|
||||||
|
openWorldHint: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async ({ query }) => {
|
||||||
|
console.log(`[TOOL] ${searchSpecToolName}: query="${query}"`);
|
||||||
|
const result = await searchSpecTool({ query });
|
||||||
|
console.log(
|
||||||
|
`[TOOL] ${searchSpecToolName}: ${result.results.length} results`,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||||
|
structuredContent: result,
|
||||||
|
isError: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register get section content tool
|
||||||
|
server.registerTool(
|
||||||
|
sectionContentToolName,
|
||||||
|
{
|
||||||
|
description: sectionContentToolMetadata.description,
|
||||||
|
inputSchema: getSectionInputSchema,
|
||||||
|
outputSchema: getSectionOutputSchema,
|
||||||
|
annotations: {
|
||||||
|
readOnlyHint: true,
|
||||||
|
idempotentHint: true,
|
||||||
|
openWorldHint: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async ({ sectionId, recursive }) => {
|
||||||
|
console.log(
|
||||||
|
`[TOOL] ${sectionContentToolName}: sectionId="${sectionId}" recursive=${recursive}`,
|
||||||
|
);
|
||||||
|
const result = await getSectionContentTool({ sectionId, recursive });
|
||||||
|
console.log(
|
||||||
|
`[TOOL] ${sectionContentToolName}: ${result.content.length} chars, ${result.sectionCount} sections`,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
||||||
|
structuredContent: result,
|
||||||
|
isError: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register evaluate in engine262 tool
|
||||||
|
server.registerTool(
|
||||||
|
evaluateToolName,
|
||||||
|
{
|
||||||
|
description: evaluateToolMetadata.description,
|
||||||
|
inputSchema: evaluateInputSchema,
|
||||||
|
outputSchema: evaluateOutputSchema,
|
||||||
|
annotations: {
|
||||||
|
readOnlyHint: true,
|
||||||
|
idempotentHint: true,
|
||||||
|
openWorldHint: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async ({ code }) => {
|
||||||
|
console.log(`[TOOL] ${evaluateToolName}: code length=${code.length}`);
|
||||||
|
const result = await evaluateTool({ code });
|
||||||
|
const isError = result.error !== undefined;
|
||||||
|
if (isError) {
|
||||||
|
console.log(`[TOOL] ${evaluateToolName}: error - ${result.error}`);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`[TOOL] ${evaluateToolName}: ${result.importantSections.length} important, ${result.otherSections.length} other sections`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const text = isError ? result.error : JSON.stringify(result, null, 2);
|
||||||
|
return {
|
||||||
|
content: [{ type: "text", text }],
|
||||||
|
structuredContent: result,
|
||||||
|
isError,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Register prompt for tool orchestration guidance
|
||||||
|
server.registerPrompt(
|
||||||
|
"ask",
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
"Explains JavaScript internals from the ECMAScript specification.",
|
||||||
|
argsSchema: {
|
||||||
|
question: z.string().describe("Question"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async ({ question }) => ({
|
||||||
|
description: "Ask262 orchestration guide",
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: {
|
||||||
|
type: "text",
|
||||||
|
text: question,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
role: "assistant",
|
||||||
|
content: {
|
||||||
|
type: "text",
|
||||||
|
text: `I'll help you explore this using the ECMAScript specification. Let me orchestrate the ask262 tools to find accurate information.
|
||||||
|
|
||||||
|
Available tools:
|
||||||
|
- ask262_search_spec_sections: Vector search to find relevant spec section ids
|
||||||
|
- ask262_get_section_content: Retrieve full text from a spec section id
|
||||||
|
- ask262_evaluate_in_engine262: Execute pure JS and capture which spec section ids are hit. Has 1-second timeout for safety.
|
||||||
|
|
||||||
|
Based on your question: "${question}"
|
||||||
|
|
||||||
|
I'll use one of these orchestration patterns:
|
||||||
|
|
||||||
|
PATTERN 1 - For "What happens when I run this code?" questions:
|
||||||
|
- Use ask262Debug.startImportant() and ask262Debug.stopImportant() in the code to mark only important sections.
|
||||||
|
- STEP 1: ask262_evaluate_in_engine262(code: markedCode)
|
||||||
|
- STEP 2: ask262_get_section_content(sectionId: importantSections[0], recursive: true)
|
||||||
|
- Explain which spec sections were hit and why
|
||||||
|
|
||||||
|
PATTERN 2 - For "How does X work?" questions (e.g., "${question}"):
|
||||||
|
- Flow A: Generate a specific code example and follow Pattern 1
|
||||||
|
- Flow B: If no code example possible, search broadly:
|
||||||
|
* STEP 1: ask262_search_spec_sections(query: relevant keywords from "${question}")
|
||||||
|
* STEP 2: ask262_get_section_content(sectionId: foundSectionId, recursive: true)
|
||||||
|
|
||||||
|
I prefer Pattern 1 when possible as it provides exact spec sections through execution.
|
||||||
|
|
||||||
|
Key principles:
|
||||||
|
- Ignore internal knowledge about Javascript/ECMAScript - rely only on spec sections from tools
|
||||||
|
- Reference specific spec sections by section id (sec-array.prototype.map) or number/name (e.g., "23.1.3.21 Array.prototype.map")
|
||||||
|
- If I can't generate a relevant code example, I'll ask you to provide one`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function main() {
|
||||||
|
// Create Hono app
|
||||||
|
const app = new Hono();
|
||||||
|
|
||||||
|
// Enable CORS for all origins
|
||||||
|
app.use(
|
||||||
|
"*",
|
||||||
|
cors({
|
||||||
|
origin: "*",
|
||||||
|
allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
|
||||||
|
allowHeaders: [
|
||||||
|
"Content-Type",
|
||||||
|
"mcp-session-id",
|
||||||
|
"Last-Event-ID",
|
||||||
|
"mcp-protocol-version",
|
||||||
|
],
|
||||||
|
exposeHeaders: ["mcp-session-id", "mcp-protocol-version"],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Health check endpoint
|
||||||
|
app.get("/health", (c) => c.json({ status: "ok" }));
|
||||||
|
|
||||||
|
// MCP endpoint - handles both GET and POST
|
||||||
|
app.all("/mcp", async (c) => {
|
||||||
|
// Get parsed body from Hono (automatic JSON parsing)
|
||||||
|
let parsedBody: unknown;
|
||||||
|
if (c.req.method === "POST") {
|
||||||
|
try {
|
||||||
|
parsedBody = await c.req.json();
|
||||||
|
} catch {
|
||||||
|
// Invalid JSON - let SDK handle the error
|
||||||
|
parsedBody = await c.req.text();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create fresh server and transport for each request (stateless mode)
|
||||||
|
const server = await createMcpServer();
|
||||||
|
const transport = new WebStandardStreamableHTTPServerTransport({
|
||||||
|
sessionIdGenerator: undefined, // Stateless mode
|
||||||
|
enableJsonResponse: true, // Use JSON responses instead of SSE streaming
|
||||||
|
});
|
||||||
|
await server.connect(transport);
|
||||||
|
|
||||||
|
// Use Web Standard handleRequest method
|
||||||
|
// Hono's c.req.raw is a Web Standard Request
|
||||||
|
const response = await transport.handleRequest(c.req.raw, { parsedBody });
|
||||||
|
|
||||||
|
// Return the Web Standard Response directly
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start the server
|
||||||
|
console.log(`Ask262 MCP HTTP Server running on http://localhost:${PORT}`);
|
||||||
|
console.log(`MCP endpoint: POST http://localhost:${PORT}/mcp`);
|
||||||
|
console.log(`Health check: GET http://localhost:${PORT}/health`);
|
||||||
|
console.log(`Mode: Stateless JSON (non-streaming)`);
|
||||||
|
|
||||||
|
serve({
|
||||||
|
fetch: app.fetch,
|
||||||
|
port: PORT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error("Fatal error:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user