poe-code 3.0.432 → 3.0.434
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/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/packages/tiny-http-mcp-server/dist/auth.js +12 -0
- package/packages/tiny-http-mcp-server/dist/express-middleware.js +4 -0
- package/packages/tiny-http-mcp-server/dist/http-server.js +1 -1
- package/packages/tiny-http-mcp-server/dist/http-transport.d.ts +5 -0
- package/packages/tiny-http-mcp-server/dist/http-transport.js +53 -7
package/package.json
CHANGED
|
@@ -219,6 +219,18 @@ export async function authorizeBearerRequest(req, options) {
|
|
|
219
219
|
authorizationServers: options.authorizationServers.map(toUrlString),
|
|
220
220
|
requiredScopes,
|
|
221
221
|
});
|
|
222
|
+
const verifiedScopes = new Set(verifiedToken.scopes);
|
|
223
|
+
if (requiredScopes.some((scope) => !verifiedScopes.has(scope))) {
|
|
224
|
+
return {
|
|
225
|
+
ok: false,
|
|
226
|
+
statusCode: 403,
|
|
227
|
+
challenge: createBearerChallenge(req, {
|
|
228
|
+
error: "insufficient_scope",
|
|
229
|
+
errorDescription: "insufficient scope",
|
|
230
|
+
scope: requiredScopes,
|
|
231
|
+
}, options.protectedResourcePath, options.trustedProxy),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
222
234
|
const auth = toRequestAuthInfo(verifiedToken, toUrlString(options.resource));
|
|
223
235
|
req.auth = auth;
|
|
224
236
|
return {
|
|
@@ -62,6 +62,10 @@ export function createExpressOAuthHandlers(options) {
|
|
|
62
62
|
path,
|
|
63
63
|
}),
|
|
64
64
|
mcpMiddleware: async (req, res, next) => {
|
|
65
|
+
if (req.method === "OPTIONS") {
|
|
66
|
+
await mcpMiddleware(req, res, next);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
65
69
|
const authorization = await authorizeBearerRequest(req, {
|
|
66
70
|
...options.oauth,
|
|
67
71
|
protectedResourcePath: path,
|
|
@@ -106,7 +106,7 @@ export function createHttpServer(options) {
|
|
|
106
106
|
request: {}
|
|
107
107
|
};
|
|
108
108
|
const authorizeHttpRequest = async (req, res, protectedResourcePath) => {
|
|
109
|
-
if (options.oauth === undefined) {
|
|
109
|
+
if (options.oauth === undefined || req.method === "OPTIONS") {
|
|
110
110
|
return true;
|
|
111
111
|
}
|
|
112
112
|
const authenticatedRequest = req;
|
|
@@ -99,12 +99,15 @@ export declare class StreamableHttpTransport {
|
|
|
99
99
|
private readonly trustedProxy;
|
|
100
100
|
private readonly sessionMessages;
|
|
101
101
|
private readonly sseStreams;
|
|
102
|
+
private readonly sseExpiryTimers;
|
|
102
103
|
private readonly sseEventHistory;
|
|
103
104
|
private readonly responseRequestIds;
|
|
104
105
|
private readonly responseOrigins;
|
|
105
106
|
private nextNotificationEventId;
|
|
106
107
|
private nextRequestId;
|
|
107
108
|
private activeToolCalls;
|
|
109
|
+
private closed;
|
|
110
|
+
private sessionExpiryInterval;
|
|
108
111
|
private sseKeepAliveInterval;
|
|
109
112
|
constructor(server: Server, options?: StreamableHttpTransportOptions, runWithRequestContext?: RequestContextRunner);
|
|
110
113
|
handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void>;
|
|
@@ -128,6 +131,8 @@ export declare class StreamableHttpTransport {
|
|
|
128
131
|
private createLocalMessageSession;
|
|
129
132
|
private ensureLocalMessageSession;
|
|
130
133
|
private closeStreamsForSession;
|
|
134
|
+
private scheduleSseExpiry;
|
|
135
|
+
private clearSseExpiryTimer;
|
|
131
136
|
private startSseKeepAlive;
|
|
132
137
|
private stopSseKeepAliveIfIdle;
|
|
133
138
|
private stopSseKeepAlive;
|
|
@@ -39,12 +39,15 @@ export class StreamableHttpTransport {
|
|
|
39
39
|
trustedProxy;
|
|
40
40
|
sessionMessages = new Map();
|
|
41
41
|
sseStreams = new Map();
|
|
42
|
+
sseExpiryTimers = new Map();
|
|
42
43
|
sseEventHistory = new Map();
|
|
43
44
|
responseRequestIds = new WeakMap();
|
|
44
45
|
responseOrigins = new WeakMap();
|
|
45
46
|
nextNotificationEventId = 1;
|
|
46
47
|
nextRequestId = 1;
|
|
47
48
|
activeToolCalls = 0;
|
|
49
|
+
closed = false;
|
|
50
|
+
sessionExpiryInterval;
|
|
48
51
|
sseKeepAliveInterval;
|
|
49
52
|
constructor(server, options = {}, runWithRequestContext = async (_req, callback) => callback()) {
|
|
50
53
|
this.server = server;
|
|
@@ -73,6 +76,17 @@ export class StreamableHttpTransport {
|
|
|
73
76
|
this.requestIdGenerator = options.requestIdGenerator ?? (() => `req-${this.nextRequestId++}`);
|
|
74
77
|
this.observability = options.observability ?? {};
|
|
75
78
|
this.trustedProxy = options.trustedProxy ?? false;
|
|
79
|
+
if (this.sessionTtlMs !== undefined) {
|
|
80
|
+
this.sessionExpiryInterval = setInterval(() => {
|
|
81
|
+
try {
|
|
82
|
+
this.purgeExpiredSessions();
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}, Math.min(this.sessionTtlMs, 60_000));
|
|
88
|
+
this.sessionExpiryInterval.unref();
|
|
89
|
+
}
|
|
76
90
|
}
|
|
77
91
|
async handleRequest(req, res) {
|
|
78
92
|
const startedAt = Date.now();
|
|
@@ -90,12 +104,10 @@ export class StreamableHttpTransport {
|
|
|
90
104
|
sessionId: this.readSessionId(req)
|
|
91
105
|
});
|
|
92
106
|
try {
|
|
93
|
-
this.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
107
|
+
if (this.closed) {
|
|
108
|
+
this.respondWithStatus(res, 503);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
99
111
|
if (!this.acceptsHost(req) || !this.acceptsOrigin(req)) {
|
|
100
112
|
this.respondWithStatus(res, 403);
|
|
101
113
|
return;
|
|
@@ -148,7 +160,16 @@ export class StreamableHttpTransport {
|
|
|
148
160
|
}
|
|
149
161
|
}
|
|
150
162
|
async close() {
|
|
163
|
+
this.closed = true;
|
|
164
|
+
if (this.sessionExpiryInterval !== undefined) {
|
|
165
|
+
clearInterval(this.sessionExpiryInterval);
|
|
166
|
+
this.sessionExpiryInterval = undefined;
|
|
167
|
+
}
|
|
151
168
|
this.stopSseKeepAlive();
|
|
169
|
+
for (const timer of this.sseExpiryTimers.values()) {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
}
|
|
172
|
+
this.sseExpiryTimers.clear();
|
|
152
173
|
for (const sessionId of [...this.sseStreams.keys()]) {
|
|
153
174
|
this.closeStreamsForSession(sessionId);
|
|
154
175
|
}
|
|
@@ -395,6 +416,7 @@ export class StreamableHttpTransport {
|
|
|
395
416
|
streamCount: streams.size
|
|
396
417
|
});
|
|
397
418
|
const cleanup = () => {
|
|
419
|
+
this.clearSseExpiryTimer(res);
|
|
398
420
|
const activeStreams = this.sseStreams.get(sessionId);
|
|
399
421
|
if (activeStreams === undefined) {
|
|
400
422
|
return;
|
|
@@ -415,6 +437,10 @@ export class StreamableHttpTransport {
|
|
|
415
437
|
req.on("close", cleanup);
|
|
416
438
|
res.on("close", cleanup);
|
|
417
439
|
res.on("finish", cleanup);
|
|
440
|
+
const expiresAt = req.auth?.expiresAt;
|
|
441
|
+
if (expiresAt !== undefined) {
|
|
442
|
+
this.scheduleSseExpiry(res, expiresAt);
|
|
443
|
+
}
|
|
418
444
|
res.writeHead(200, this.withSessionHeader(SSE_HEADERS, sessionId, res));
|
|
419
445
|
this.replaySseEvents(req, res, sessionId);
|
|
420
446
|
res.flushHeaders();
|
|
@@ -589,6 +615,7 @@ export class StreamableHttpTransport {
|
|
|
589
615
|
return;
|
|
590
616
|
}
|
|
591
617
|
for (const response of streams) {
|
|
618
|
+
this.clearSseExpiryTimer(response);
|
|
592
619
|
if (!response.writableEnded) {
|
|
593
620
|
response.end();
|
|
594
621
|
}
|
|
@@ -596,6 +623,25 @@ export class StreamableHttpTransport {
|
|
|
596
623
|
this.sseStreams.delete(sessionId);
|
|
597
624
|
this.stopSseKeepAliveIfIdle();
|
|
598
625
|
}
|
|
626
|
+
scheduleSseExpiry(response, expiresAt) {
|
|
627
|
+
const remainingMs = Math.max(0, expiresAt * 1_000 - Date.now());
|
|
628
|
+
const timer = setTimeout(() => {
|
|
629
|
+
this.sseExpiryTimers.delete(response);
|
|
630
|
+
if (!response.writableEnded) {
|
|
631
|
+
response.end();
|
|
632
|
+
}
|
|
633
|
+
}, remainingMs);
|
|
634
|
+
timer.unref();
|
|
635
|
+
this.sseExpiryTimers.set(response, timer);
|
|
636
|
+
}
|
|
637
|
+
clearSseExpiryTimer(response) {
|
|
638
|
+
const timer = this.sseExpiryTimers.get(response);
|
|
639
|
+
if (timer === undefined) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
clearTimeout(timer);
|
|
643
|
+
this.sseExpiryTimers.delete(response);
|
|
644
|
+
}
|
|
599
645
|
startSseKeepAlive() {
|
|
600
646
|
if (this.sseKeepAliveMs === 0 || this.sseKeepAliveInterval !== undefined) {
|
|
601
647
|
return;
|
|
@@ -739,7 +785,7 @@ export class StreamableHttpTransport {
|
|
|
739
785
|
if (host === undefined || host.length === 0) {
|
|
740
786
|
return true;
|
|
741
787
|
}
|
|
742
|
-
return this.allowedHosts.has(this.normalizeHost(
|
|
788
|
+
return this.allowedHosts.has(this.normalizeHost(this.readRequestHost(req)));
|
|
743
789
|
}
|
|
744
790
|
normalizeHost(host) {
|
|
745
791
|
const normalized = host.trim().toLowerCase();
|