burn-mcp-server 2.0.6 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -2
- package/api/health.mjs +7 -0
- package/api/mcp.js +36075 -0
- package/dist/http.mjs +1131 -0
- package/dist/index.js +1021 -1009
- package/package.json +7 -3
- package/public/index.html +47 -0
- package/server.json +16 -2
- package/src/http-dev.ts +56 -0
- package/src/http.ts +62 -0
- package/src/index.ts +28 -1624
- package/src/lib/auth-stdio.ts +26 -0
- package/src/lib/auth.ts +71 -0
- package/src/setup.ts +1529 -0
- package/src/vercel-handler.ts +20 -0
- package/src/vercel-node-handler.ts +76 -0
- package/tsconfig.json +10 -6
- package/vercel.json +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "burn-mcp-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.7",
|
|
4
4
|
"mcpName": "io.github.Fisher521/burn-mcp-server",
|
|
5
5
|
"description": "Burn MCP — give your AI agent a reading workflow. 26 tools to search, triage, burn, vault, and analyze your saved articles. Works with Claude, Cursor, Windsurf.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,9 +8,13 @@
|
|
|
8
8
|
"burn-mcp": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "esbuild src/index.ts --bundle
|
|
11
|
+
"build": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --format=cjs --packages=external",
|
|
12
|
+
"build:http": "esbuild src/http-dev.ts --bundle --platform=node --target=node18 --outfile=dist/http.mjs --format=esm --packages=external",
|
|
13
|
+
"build:vercel": "esbuild src/vercel-node-handler.ts --bundle --platform=node --target=node20 --outfile=api/mcp.js --format=cjs",
|
|
14
|
+
"build:all": "npm run build && npm run build:http && npm run build:vercel",
|
|
12
15
|
"typecheck": "node --max-old-space-size=4096 node_modules/typescript/bin/tsc --noEmit",
|
|
13
|
-
"start": "node dist/index.js"
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"dev:http": "npm run build:http && node dist/http.mjs"
|
|
14
18
|
},
|
|
15
19
|
"keywords": [
|
|
16
20
|
"mcp",
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Burn MCP Server</title>
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<style>
|
|
8
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 640px; margin: 80px auto; padding: 0 20px; line-height: 1.6; color: #1a1a1a; }
|
|
9
|
+
h1 { font-size: 28px; margin-bottom: 8px; }
|
|
10
|
+
.tag { display: inline-block; background: #ff6a00; color: white; padding: 2px 10px; border-radius: 10px; font-size: 12px; margin-right: 6px; }
|
|
11
|
+
pre { background: #f5f5f5; padding: 16px; border-radius: 8px; overflow-x: auto; font-size: 13px; }
|
|
12
|
+
a { color: #ff6a00; text-decoration: none; }
|
|
13
|
+
a:hover { text-decoration: underline; }
|
|
14
|
+
code { background: #f5f5f5; padding: 2px 6px; border-radius: 4px; font-size: 13px; }
|
|
15
|
+
</style>
|
|
16
|
+
</head>
|
|
17
|
+
<body>
|
|
18
|
+
<h1>Burn MCP Server</h1>
|
|
19
|
+
<p><span class="tag">MCP</span> <span class="tag">Streamable HTTP</span> Read less, absorb more.</p>
|
|
20
|
+
<p>This is the remote HTTP endpoint for <a href="https://burn451.cloud">Burn</a>'s Model Context Protocol server. 26 tools to let Claude / Cursor / Windsurf search, triage, and analyze your saved reading.</p>
|
|
21
|
+
|
|
22
|
+
<h2>Connect</h2>
|
|
23
|
+
<p>Paste this into your MCP client (Claude Desktop, Cursor, Windsurf, etc.):</p>
|
|
24
|
+
<pre>{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"burn": {
|
|
27
|
+
"url": "https://mcp.burn451.cloud/mcp",
|
|
28
|
+
"headers": {
|
|
29
|
+
"Authorization": "Bearer <your-token>"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}</pre>
|
|
34
|
+
|
|
35
|
+
<p>Get your token: <a href="https://burn451.cloud">burn451.cloud</a> → Settings → MCP Server → Copy Access Token</p>
|
|
36
|
+
|
|
37
|
+
<h2>Links</h2>
|
|
38
|
+
<ul>
|
|
39
|
+
<li><a href="https://github.com/Fisher521/burn-mcp-server">Source code on GitHub</a></li>
|
|
40
|
+
<li><a href="https://www.npmjs.com/package/burn-mcp-server">npm package (stdio mode)</a></li>
|
|
41
|
+
<li><a href="https://burn451.cloud">Burn web app</a></li>
|
|
42
|
+
<li><a href="https://x.com/hawking520">Built by @hawking520</a></li>
|
|
43
|
+
</ul>
|
|
44
|
+
|
|
45
|
+
<p style="color: #888; font-size: 13px; margin-top: 40px;">MIT licensed. Part of an open ecosystem of AI-era reading tools.</p>
|
|
46
|
+
</body>
|
|
47
|
+
</html>
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
9
|
"websiteUrl": "https://burn451.cloud",
|
|
10
|
-
"version": "2.0.
|
|
10
|
+
"version": "2.0.7",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "burn-mcp-server",
|
|
15
|
-
"version": "2.0.
|
|
15
|
+
"version": "2.0.7",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
|
@@ -26,5 +26,19 @@
|
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
}
|
|
29
|
+
],
|
|
30
|
+
"remotes": [
|
|
31
|
+
{
|
|
32
|
+
"type": "streamable-http",
|
|
33
|
+
"url": "https://burn-mcp-server.vercel.app/mcp",
|
|
34
|
+
"headers": [
|
|
35
|
+
{
|
|
36
|
+
"name": "Authorization",
|
|
37
|
+
"description": "Bearer <BURN_MCP_TOKEN> — get your token at https://burn451.cloud → Settings → MCP Server",
|
|
38
|
+
"isRequired": true,
|
|
39
|
+
"isSecret": true
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
29
43
|
]
|
|
30
44
|
}
|
package/src/http-dev.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Burn MCP — standalone Node HTTP dev server for local testing.
|
|
2
|
+
// For production deploy, see api/mcp.ts (Vercel Edge) or src/http.ts (the handler).
|
|
3
|
+
|
|
4
|
+
import { createServer } from 'node:http'
|
|
5
|
+
import { handleMcpRequest } from './http.js'
|
|
6
|
+
|
|
7
|
+
const PORT = Number(process.env.PORT) || 3001
|
|
8
|
+
|
|
9
|
+
const server = createServer(async (nodeReq, nodeRes) => {
|
|
10
|
+
try {
|
|
11
|
+
const url = new URL(nodeReq.url || '/', `http://${nodeReq.headers.host || 'localhost'}`)
|
|
12
|
+
const headers = new Headers()
|
|
13
|
+
for (const [k, v] of Object.entries(nodeReq.headers)) {
|
|
14
|
+
if (typeof v === 'string') headers.set(k, v)
|
|
15
|
+
}
|
|
16
|
+
const body = ['GET', 'HEAD'].includes(nodeReq.method || 'GET')
|
|
17
|
+
? null
|
|
18
|
+
: await new Promise<Buffer>((resolve, reject) => {
|
|
19
|
+
const chunks: Buffer[] = []
|
|
20
|
+
nodeReq.on('data', c => chunks.push(c))
|
|
21
|
+
nodeReq.on('end', () => resolve(Buffer.concat(chunks)))
|
|
22
|
+
nodeReq.on('error', reject)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const webReq = new Request(url, {
|
|
26
|
+
method: nodeReq.method,
|
|
27
|
+
headers,
|
|
28
|
+
body: body && body.length > 0 ? body : null,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const webRes = await handleMcpRequest(webReq)
|
|
32
|
+
|
|
33
|
+
nodeRes.statusCode = webRes.status
|
|
34
|
+
webRes.headers.forEach((v, k) => nodeRes.setHeader(k, v))
|
|
35
|
+
if (webRes.body) {
|
|
36
|
+
const reader = webRes.body.getReader()
|
|
37
|
+
while (true) {
|
|
38
|
+
const { done, value } = await reader.read()
|
|
39
|
+
if (done) break
|
|
40
|
+
nodeRes.write(value)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
nodeRes.end()
|
|
44
|
+
} catch (e: any) {
|
|
45
|
+
nodeRes.statusCode = 500
|
|
46
|
+
nodeRes.setHeader('content-type', 'application/json')
|
|
47
|
+
nodeRes.end(JSON.stringify({ error: 'Internal error', detail: String(e?.message || e) }))
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
server.listen(PORT, () => {
|
|
52
|
+
console.log(`Burn MCP HTTP server listening on http://localhost:${PORT}`)
|
|
53
|
+
console.log(`Test: curl -H "Authorization: Bearer <TOKEN>" -X POST http://localhost:${PORT}/mcp \\`)
|
|
54
|
+
console.log(` -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \\`)
|
|
55
|
+
console.log(` -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'`)
|
|
56
|
+
})
|
package/src/http.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Burn MCP — HTTP transport entry (Web Standard fetch API)
|
|
2
|
+
//
|
|
3
|
+
// Works on Vercel Edge / Node runtime, Cloudflare Workers, Deno, Bun.
|
|
4
|
+
// Users connect by adding ONE URL to Claude/Cursor/Windsurf MCP settings instead of installing npx.
|
|
5
|
+
//
|
|
6
|
+
// Auth: each request carries `Authorization: Bearer <BURN_MCP_TOKEN>` header.
|
|
7
|
+
// Stateless: fresh Supabase client per request (short in-memory session cache for performance).
|
|
8
|
+
|
|
9
|
+
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js'
|
|
10
|
+
import { createClient } from '@supabase/supabase-js'
|
|
11
|
+
import { getOrExchangeSession, applySession } from './lib/auth.js'
|
|
12
|
+
import { createBurnServer } from './setup.js'
|
|
13
|
+
|
|
14
|
+
const SUPABASE_URL = process.env.BURN_SUPABASE_URL || 'https://juqtxylquemiuvvmgbej.supabase.co'
|
|
15
|
+
const SUPABASE_ANON_KEY = process.env.BURN_SUPABASE_ANON_KEY || 'sb_publishable_reVgmmCC6ndIo6jFRMM2LQ_wujj5FrO'
|
|
16
|
+
|
|
17
|
+
/** Main handler — turn an incoming HTTP Request into a JSON-RPC MCP Response. */
|
|
18
|
+
export async function handleMcpRequest(req: Request): Promise<Response> {
|
|
19
|
+
// --- Auth: require Bearer token ---
|
|
20
|
+
const authHeader = req.headers.get('authorization') || req.headers.get('Authorization') || ''
|
|
21
|
+
const match = authHeader.match(/^Bearer\s+(.+)$/i)
|
|
22
|
+
const token = match?.[1]?.trim()
|
|
23
|
+
|
|
24
|
+
if (!token) {
|
|
25
|
+
return new Response(JSON.stringify({
|
|
26
|
+
error: 'Missing Authorization header',
|
|
27
|
+
hint: 'Add `Authorization: Bearer <BURN_MCP_TOKEN>` header. Get your token at https://burn451.cloud → Settings → MCP Server.',
|
|
28
|
+
}), { status: 401, headers: { 'content-type': 'application/json' } })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// --- Exchange token → Supabase session (cached in memory 5 min per token) ---
|
|
32
|
+
let session
|
|
33
|
+
try {
|
|
34
|
+
session = await getOrExchangeSession(token)
|
|
35
|
+
} catch (e: any) {
|
|
36
|
+
return new Response(JSON.stringify({
|
|
37
|
+
error: 'Invalid or expired Burn MCP token',
|
|
38
|
+
detail: String(e?.message || e),
|
|
39
|
+
}), { status: 401, headers: { 'content-type': 'application/json' } })
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// --- Build per-request Supabase client with JWT in headers (faster than setSession, Edge-safe) ---
|
|
43
|
+
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
|
|
44
|
+
auth: { persistSession: false, autoRefreshToken: false, detectSessionInUrl: false },
|
|
45
|
+
global: {
|
|
46
|
+
headers: { Authorization: `Bearer ${session.access_token}` },
|
|
47
|
+
},
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const server = createBurnServer(supabase, { rateLimitPerMin: 60 })
|
|
51
|
+
|
|
52
|
+
// --- Streamable HTTP transport (stateless mode — no session reuse across requests) ---
|
|
53
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
54
|
+
sessionIdGenerator: undefined, // stateless mode — MCP SDK treats each request as a new conversation
|
|
55
|
+
enableJsonResponse: true,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
await server.connect(transport)
|
|
59
|
+
return transport.handleRequest(req)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Standalone Node dev server is in src/http-dev.ts (npm run dev:http)
|