feat: add streamable http mcp server
This commit is contained in:
@@ -0,0 +1,30 @@
|
|||||||
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"
|
||||||
|
import { getServer } from "#/mcp/server"
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const req = event.node.req
|
||||||
|
const res = event.node.res
|
||||||
|
const server = getServer()
|
||||||
|
try {
|
||||||
|
const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined })
|
||||||
|
transport.onerror = console.error.bind(console)
|
||||||
|
await server.connect(transport)
|
||||||
|
await transport.handleRequest(req, res, await readBody(event))
|
||||||
|
res.on("close", () => {
|
||||||
|
// console.log("Request closed")
|
||||||
|
transport.close()
|
||||||
|
server.close()
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
return {
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
error: {
|
||||||
|
code: -32603,
|
||||||
|
message: "Internal server error",
|
||||||
|
},
|
||||||
|
id: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import sources from "../../shared/sources.json"
|
||||||
|
|
||||||
|
export const description = Object.entries(sources).filter(([_, source]) => {
|
||||||
|
if (source.redirect) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}).map(([id, source]) => {
|
||||||
|
return source.title ? `${source.name}-${source.title} id is ${id}` : `${source.name} id is ${id}`
|
||||||
|
}).join(";")
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
|
||||||
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"
|
||||||
|
import packageJSON from "../../package.json"
|
||||||
|
import { description } from "./desc.js"
|
||||||
|
|
||||||
|
export function getServer() {
|
||||||
|
const server = new McpServer(
|
||||||
|
{
|
||||||
|
name: "NewsNow",
|
||||||
|
version: packageJSON.version,
|
||||||
|
},
|
||||||
|
{ capabilities: { logging: {} } },
|
||||||
|
)
|
||||||
|
|
||||||
|
server.tool(
|
||||||
|
"get_hotest_latest_news",
|
||||||
|
`get hotest or latest news from source by {id}, return {count: 10} news.`,
|
||||||
|
{
|
||||||
|
id: z.string().describe(`source id. e.g. ${description}`),
|
||||||
|
count: z.any().default(10).describe("count of news to return."),
|
||||||
|
},
|
||||||
|
async ({ count }): Promise<CallToolResult> => {
|
||||||
|
let n = Number(count)
|
||||||
|
if (Number.isNaN(n) || n < 1) {
|
||||||
|
n = 10
|
||||||
|
}
|
||||||
|
await delay(5000)
|
||||||
|
return {
|
||||||
|
content: [{
|
||||||
|
text: "hello",
|
||||||
|
type: "text",
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
|
||||||
|
// const res: SourceResponse = await $fetch(`https://newsnow.busiyi.world/api/s?id=${id}`)
|
||||||
|
// return {
|
||||||
|
// content: res.items.slice(0, count).map((item) => {
|
||||||
|
// return {
|
||||||
|
// text: `[${item.title}](${item.url})`,
|
||||||
|
// type: "text",
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
server.server.onerror = console.error.bind(console)
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ export default defineEventHandler(async (event) => {
|
|||||||
if (!url.pathname.startsWith("/api")) return
|
if (!url.pathname.startsWith("/api")) return
|
||||||
if (["JWT_SECRET", "G_CLIENT_ID", "G_CLIENT_SECRET"].find(k => !process.env[k])) {
|
if (["JWT_SECRET", "G_CLIENT_ID", "G_CLIENT_SECRET"].find(k => !process.env[k])) {
|
||||||
event.context.disabledLogin = true
|
event.context.disabledLogin = true
|
||||||
if (["/api/s", "/api/proxy", "/api/latest"].every(p => !url.pathname.startsWith(p)))
|
if (["/api/s", "/api/proxy", "/api/latest", "/api/mcp"].every(p => !url.pathname.startsWith(p)))
|
||||||
throw createError({ statusCode: 506, message: "Server not configured, disable login" })
|
throw createError({ statusCode: 506, message: "Server not configured, disable login" })
|
||||||
} else {
|
} else {
|
||||||
if (["/api/s", "/api/me"].find(p => url.pathname.startsWith(p))) {
|
if (["/api/s", "/api/me"].find(p => url.pathname.startsWith(p))) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"allowJs": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user