asrai-mcp 0.5.0 → 0.5.1
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/package.json +9 -2
- package/src/sse-server.js +45 -16
package/package.json
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "asrai-mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Asrai crypto analysis MCP server — pay-per-use via x402 on Base. Zero install: just npx.",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"crypto",
|
|
8
|
+
"asrai",
|
|
9
|
+
"x402",
|
|
10
|
+
"trading",
|
|
11
|
+
"signals"
|
|
12
|
+
],
|
|
6
13
|
"homepage": "https://asrai.me/agents",
|
|
7
14
|
"repository": {
|
|
8
15
|
"type": "git",
|
package/src/sse-server.js
CHANGED
|
@@ -23,6 +23,7 @@ import { randomBytes, randomUUID } from "node:crypto";
|
|
|
23
23
|
import { privateKeyToAccount } from "viem/accounts";
|
|
24
24
|
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
25
25
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
26
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
26
27
|
import { createServer } from "./server.js";
|
|
27
28
|
import { connectionStorage } from "./tools.js";
|
|
28
29
|
|
|
@@ -30,6 +31,7 @@ const app = express();
|
|
|
30
31
|
app.use(express.json());
|
|
31
32
|
|
|
32
33
|
// Track active SSE transports by session ID (for POST /messages routing)
|
|
34
|
+
// Stores { transport, key } so the key is available when POST /messages arrives
|
|
33
35
|
const sseTransports = {};
|
|
34
36
|
|
|
35
37
|
// Track active streamable HTTP transports by session ID
|
|
@@ -59,7 +61,7 @@ app.get("/sse", async (req, res) => {
|
|
|
59
61
|
if (!key) return;
|
|
60
62
|
|
|
61
63
|
const transport = new SSEServerTransport("/messages", res);
|
|
62
|
-
sseTransports[transport.sessionId] = transport;
|
|
64
|
+
sseTransports[transport.sessionId] = { transport, key };
|
|
63
65
|
|
|
64
66
|
res.on("close", () => {
|
|
65
67
|
delete sseTransports[transport.sessionId];
|
|
@@ -73,9 +75,11 @@ app.get("/sse", async (req, res) => {
|
|
|
73
75
|
|
|
74
76
|
app.post("/messages", async (req, res) => {
|
|
75
77
|
const sessionId = req.query.sessionId;
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
await
|
|
78
|
+
const session = sseTransports[sessionId];
|
|
79
|
+
if (session) {
|
|
80
|
+
await connectionStorage.run({ key: session.key, spend: 0 }, async () => {
|
|
81
|
+
await session.transport.handlePostMessage(req, res);
|
|
82
|
+
});
|
|
79
83
|
} else {
|
|
80
84
|
res.status(400).send("No active session found.");
|
|
81
85
|
}
|
|
@@ -83,10 +87,19 @@ app.post("/messages", async (req, res) => {
|
|
|
83
87
|
|
|
84
88
|
// ── HTTP Streamable transport (recommended) ───────────────────────────────────
|
|
85
89
|
|
|
86
|
-
|
|
90
|
+
// POST /mcp — initialize new session or handle existing session message
|
|
91
|
+
app.post("/mcp", async (req, res) => {
|
|
87
92
|
const sessionId = req.headers["mcp-session-id"];
|
|
88
93
|
|
|
89
|
-
if (
|
|
94
|
+
if (sessionId) {
|
|
95
|
+
// Existing session — route to stored transport
|
|
96
|
+
const session = streamableTransports[sessionId];
|
|
97
|
+
if (!session) return res.status(404).json({ jsonrpc: "2.0", error: { code: -32000, message: "Session not found" }, id: null });
|
|
98
|
+
|
|
99
|
+
await connectionStorage.run({ key: session.key, spend: 0 }, async () => {
|
|
100
|
+
await session.transport.handleRequest(req, res, req.body);
|
|
101
|
+
});
|
|
102
|
+
} else if (isInitializeRequest(req.body)) {
|
|
90
103
|
// New session — extract and validate key
|
|
91
104
|
const key = extractKey(req, res, "/mcp");
|
|
92
105
|
if (!key) return;
|
|
@@ -98,26 +111,42 @@ app.all("/mcp", async (req, res) => {
|
|
|
98
111
|
},
|
|
99
112
|
});
|
|
100
113
|
|
|
101
|
-
|
|
114
|
+
transport.onclose = () => {
|
|
102
115
|
if (transport.sessionId) delete streamableTransports[transport.sessionId];
|
|
103
|
-
}
|
|
116
|
+
};
|
|
104
117
|
|
|
105
118
|
await connectionStorage.run({ key, spend: 0 }, async () => {
|
|
106
119
|
const server = createServer();
|
|
107
120
|
await server.connect(transport);
|
|
108
|
-
await transport.handleRequest(req, res);
|
|
121
|
+
await transport.handleRequest(req, res, req.body);
|
|
109
122
|
});
|
|
110
123
|
} else {
|
|
111
|
-
|
|
112
|
-
const session = streamableTransports[sessionId];
|
|
113
|
-
if (!session) return res.status(404).send("Session not found.");
|
|
114
|
-
|
|
115
|
-
await connectionStorage.run({ key: session.key, spend: 0 }, async () => {
|
|
116
|
-
await session.transport.handleRequest(req, res);
|
|
117
|
-
});
|
|
124
|
+
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "No valid session ID" }, id: null });
|
|
118
125
|
}
|
|
119
126
|
});
|
|
120
127
|
|
|
128
|
+
// GET /mcp — SSE stream for server-initiated messages (existing sessions only)
|
|
129
|
+
app.get("/mcp", async (req, res) => {
|
|
130
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
131
|
+
const session = streamableTransports[sessionId];
|
|
132
|
+
if (!session) return res.status(400).send("Invalid or missing session ID");
|
|
133
|
+
|
|
134
|
+
await connectionStorage.run({ key: session.key, spend: 0 }, async () => {
|
|
135
|
+
await session.transport.handleRequest(req, res);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// DELETE /mcp — session termination
|
|
140
|
+
app.delete("/mcp", async (req, res) => {
|
|
141
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
142
|
+
const session = streamableTransports[sessionId];
|
|
143
|
+
if (!session) return res.status(400).send("Invalid or missing session ID");
|
|
144
|
+
|
|
145
|
+
await connectionStorage.run({ key: session.key, spend: 0 }, async () => {
|
|
146
|
+
await session.transport.handleRequest(req, res, req.body);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
121
150
|
// ── Utility routes ────────────────────────────────────────────────────────────
|
|
122
151
|
|
|
123
152
|
app.post("/generate-wallet", (_req, res) => {
|