@sashido/mcp-server 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +201 -2
- package/dist/access.d.ts +47 -0
- package/dist/access.d.ts.map +1 -0
- package/dist/access.js +156 -0
- package/dist/access.js.map +1 -0
- package/dist/advice/format.d.ts +22 -0
- package/dist/advice/format.d.ts.map +1 -0
- package/dist/advice/format.js +70 -0
- package/dist/advice/format.js.map +1 -0
- package/dist/advice/schemaChecks.d.ts +17 -0
- package/dist/advice/schemaChecks.d.ts.map +1 -0
- package/dist/advice/schemaChecks.js +64 -0
- package/dist/advice/schemaChecks.js.map +1 -0
- package/dist/audit.d.ts +32 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +41 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth.d.ts +17 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +72 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin/login.d.ts +3 -0
- package/dist/bin/login.d.ts.map +1 -0
- package/dist/bin/login.js +16 -0
- package/dist/bin/login.js.map +1 -0
- package/dist/bin/mcp-server.d.ts +3 -0
- package/dist/bin/mcp-server.d.ts.map +1 -0
- package/dist/bin/mcp-server.js +27 -0
- package/dist/bin/mcp-server.js.map +1 -0
- package/dist/client/parse.d.ts +219 -0
- package/dist/client/parse.d.ts.map +1 -0
- package/dist/client/parse.js +414 -0
- package/dist/client/parse.js.map +1 -0
- package/dist/client/sashido.d.ts +213 -0
- package/dist/client/sashido.d.ts.map +1 -0
- package/dist/client/sashido.js +289 -0
- package/dist/client/sashido.js.map +1 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +70 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +33 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +132 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +60 -0
- package/dist/server.js.map +1 -0
- package/dist/session/redis-client.d.ts +23 -0
- package/dist/session/redis-client.d.ts.map +1 -0
- package/dist/session/redis-client.js +136 -0
- package/dist/session/redis-client.js.map +1 -0
- package/dist/session/session-manager.d.ts +6 -0
- package/dist/session/session-manager.d.ts.map +1 -0
- package/dist/session/session-manager.js +68 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/sse-pubsub.d.ts +23 -0
- package/dist/session/sse-pubsub.d.ts.map +1 -0
- package/dist/session/sse-pubsub.js +121 -0
- package/dist/session/sse-pubsub.js.map +1 -0
- package/dist/session/types.d.ts +43 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/tools/advisor/index.d.ts +3 -0
- package/dist/tools/advisor/index.d.ts.map +1 -0
- package/dist/tools/advisor/index.js +149 -0
- package/dist/tools/advisor/index.js.map +1 -0
- package/dist/tools/apps/index.d.ts +3 -0
- package/dist/tools/apps/index.d.ts.map +1 -0
- package/dist/tools/apps/index.js +113 -0
- package/dist/tools/apps/index.js.map +1 -0
- package/dist/tools/audit/index.d.ts +6 -0
- package/dist/tools/audit/index.d.ts.map +1 -0
- package/dist/tools/audit/index.js +69 -0
- package/dist/tools/audit/index.js.map +1 -0
- package/dist/tools/builds/index.d.ts +3 -0
- package/dist/tools/builds/index.d.ts.map +1 -0
- package/dist/tools/builds/index.js +85 -0
- package/dist/tools/builds/index.js.map +1 -0
- package/dist/tools/classes/index.d.ts +3 -0
- package/dist/tools/classes/index.d.ts.map +1 -0
- package/dist/tools/classes/index.js +223 -0
- package/dist/tools/classes/index.js.map +1 -0
- package/dist/tools/cloud-functions/index.d.ts +3 -0
- package/dist/tools/cloud-functions/index.d.ts.map +1 -0
- package/dist/tools/cloud-functions/index.js +154 -0
- package/dist/tools/cloud-functions/index.js.map +1 -0
- package/dist/tools/deployments/index.d.ts +3 -0
- package/dist/tools/deployments/index.d.ts.map +1 -0
- package/dist/tools/deployments/index.js +124 -0
- package/dist/tools/deployments/index.js.map +1 -0
- package/dist/tools/engines/index.d.ts +3 -0
- package/dist/tools/engines/index.d.ts.map +1 -0
- package/dist/tools/engines/index.js +68 -0
- package/dist/tools/engines/index.js.map +1 -0
- package/dist/tools/env/index.d.ts +3 -0
- package/dist/tools/env/index.d.ts.map +1 -0
- package/dist/tools/env/index.js +116 -0
- package/dist/tools/env/index.js.map +1 -0
- package/dist/tools/files/index.d.ts +3 -0
- package/dist/tools/files/index.d.ts.map +1 -0
- package/dist/tools/files/index.js +132 -0
- package/dist/tools/files/index.js.map +1 -0
- package/dist/tools/health/index.d.ts +3 -0
- package/dist/tools/health/index.d.ts.map +1 -0
- package/dist/tools/health/index.js +47 -0
- package/dist/tools/health/index.js.map +1 -0
- package/dist/tools/infrastructure/deployments.d.ts +23 -0
- package/dist/tools/infrastructure/deployments.d.ts.map +1 -0
- package/dist/tools/infrastructure/deployments.js +76 -0
- package/dist/tools/infrastructure/deployments.js.map +1 -0
- package/dist/tools/infrastructure/index.d.ts +9 -0
- package/dist/tools/infrastructure/index.d.ts.map +1 -0
- package/dist/tools/infrastructure/index.js +164 -0
- package/dist/tools/infrastructure/index.js.map +1 -0
- package/dist/tools/jobs/index.d.ts +3 -0
- package/dist/tools/jobs/index.d.ts.map +1 -0
- package/dist/tools/jobs/index.js +338 -0
- package/dist/tools/jobs/index.js.map +1 -0
- package/dist/tools/logs/index.d.ts +3 -0
- package/dist/tools/logs/index.d.ts.map +1 -0
- package/dist/tools/logs/index.js +133 -0
- package/dist/tools/logs/index.js.map +1 -0
- package/dist/tools/objects/index.d.ts +3 -0
- package/dist/tools/objects/index.d.ts.map +1 -0
- package/dist/tools/objects/index.js +185 -0
- package/dist/tools/objects/index.js.map +1 -0
- package/dist/tools/push/index.d.ts +3 -0
- package/dist/tools/push/index.d.ts.map +1 -0
- package/dist/tools/push/index.js +434 -0
- package/dist/tools/push/index.js.map +1 -0
- package/dist/tools/types/index.d.ts +3 -0
- package/dist/tools/types/index.d.ts.map +1 -0
- package/dist/tools/types/index.js +45 -0
- package/dist/tools/types/index.js.map +1 -0
- package/dist/tools/webhooks/index.d.ts +3 -0
- package/dist/tools/webhooks/index.d.ts.map +1 -0
- package/dist/tools/webhooks/index.js +443 -0
- package/dist/tools/webhooks/index.js.map +1 -0
- package/dist/transports/http.d.ts +4 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +241 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/stdio.d.ts +7 -0
- package/dist/transports/stdio.d.ts.map +1 -0
- package/dist/transports/stdio.js +29 -0
- package/dist/transports/stdio.js.map +1 -0
- package/dist/typegen/dart.d.ts +3 -0
- package/dist/typegen/dart.d.ts.map +1 -0
- package/dist/typegen/dart.js +84 -0
- package/dist/typegen/dart.js.map +1 -0
- package/dist/typegen/kotlin.d.ts +3 -0
- package/dist/typegen/kotlin.d.ts.map +1 -0
- package/dist/typegen/kotlin.js +84 -0
- package/dist/typegen/kotlin.js.map +1 -0
- package/dist/typegen/model.d.ts +37 -0
- package/dist/typegen/model.d.ts.map +1 -0
- package/dist/typegen/model.js +82 -0
- package/dist/typegen/model.js.map +1 -0
- package/dist/typegen/swift.d.ts +3 -0
- package/dist/typegen/swift.d.ts.map +1 -0
- package/dist/typegen/swift.js +105 -0
- package/dist/typegen/swift.js.map +1 -0
- package/dist/typegen/typescript.d.ts +3 -0
- package/dist/typegen/typescript.d.ts.map +1 -0
- package/dist/typegen/typescript.js +73 -0
- package/dist/typegen/typescript.js.map +1 -0
- package/dist/upgrade/rules.d.ts +27 -0
- package/dist/upgrade/rules.d.ts.map +1 -0
- package/dist/upgrade/rules.js +133 -0
- package/dist/upgrade/rules.js.map +1 -0
- package/dist/upgrade/scan.d.ts +18 -0
- package/dist/upgrade/scan.d.ts.map +1 -0
- package/dist/upgrade/scan.js +38 -0
- package/dist/upgrade/scan.js.map +1 -0
- package/dist/upgrade/versions.d.ts +8 -0
- package/dist/upgrade/versions.d.ts.map +1 -0
- package/dist/upgrade/versions.js +25 -0
- package/dist/upgrade/versions.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +68 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAc,MAAM,cAAc,CAAC;AAoBnE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AAEvC,2EAA2E;AAC3E,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,KAAK,IAAI,CAmBtF,CAAC;AAEF;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,WAAW,EAAE,UAAU,GAAE,MAAM,EAAO,GAAG,SAAS,CAYnF;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { appConfig } from './config.js';
|
|
3
|
+
import { toolServer } from './context.js';
|
|
4
|
+
import { registerAppTools } from './tools/apps/index.js';
|
|
5
|
+
import { registerBuildTools } from './tools/builds/index.js';
|
|
6
|
+
import { registerDeploymentTools } from './tools/deployments/index.js';
|
|
7
|
+
import { registerEnvTools } from './tools/env/index.js';
|
|
8
|
+
import { registerEngineTools } from './tools/engines/index.js';
|
|
9
|
+
import { registerHealthTools } from './tools/health/index.js';
|
|
10
|
+
import { registerJobTools } from './tools/jobs/index.js';
|
|
11
|
+
import { registerLogTools } from './tools/logs/index.js';
|
|
12
|
+
import { registerClassTools } from './tools/classes/index.js';
|
|
13
|
+
import { registerObjectTools } from './tools/objects/index.js';
|
|
14
|
+
import { registerFileTools } from './tools/files/index.js';
|
|
15
|
+
import { registerCloudFunctionTools } from './tools/cloud-functions/index.js';
|
|
16
|
+
import { registerPushTools } from './tools/push/index.js';
|
|
17
|
+
import { registerWebhookTools } from './tools/webhooks/index.js';
|
|
18
|
+
import { registerInfrastructureTools } from './tools/infrastructure/index.js';
|
|
19
|
+
import { registerAuditTools } from './tools/audit/index.js';
|
|
20
|
+
import { registerTypeTools } from './tools/types/index.js';
|
|
21
|
+
import { registerAdvisorTools } from './tools/advisor/index.js';
|
|
22
|
+
import { SERVER_NAME, SERVER_VERSION } from './version.js';
|
|
23
|
+
export { SERVER_NAME, SERVER_VERSION };
|
|
24
|
+
/** Tool groups (the `features` option), each the folder under src/tools */
|
|
25
|
+
export const TOOL_GROUPS = {
|
|
26
|
+
apps: registerAppTools,
|
|
27
|
+
builds: registerBuildTools,
|
|
28
|
+
deployments: registerDeploymentTools,
|
|
29
|
+
infrastructure: registerInfrastructureTools,
|
|
30
|
+
audit: registerAuditTools,
|
|
31
|
+
types: registerTypeTools,
|
|
32
|
+
advisor: registerAdvisorTools,
|
|
33
|
+
env: registerEnvTools,
|
|
34
|
+
engines: registerEngineTools,
|
|
35
|
+
health: registerHealthTools,
|
|
36
|
+
classes: registerClassTools,
|
|
37
|
+
objects: registerObjectTools,
|
|
38
|
+
files: registerFileTools,
|
|
39
|
+
'cloud-functions': registerCloudFunctionTools,
|
|
40
|
+
push: registerPushTools,
|
|
41
|
+
jobs: registerJobTools,
|
|
42
|
+
logs: registerLogTools,
|
|
43
|
+
webhooks: registerWebhookTools,
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* An MCP server for one access token: only the tools its scopes, read-only
|
|
47
|
+
* mode, app limit and feature groups allow are registered.
|
|
48
|
+
*/
|
|
49
|
+
export function createServer(ctx, registered = []) {
|
|
50
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
51
|
+
for (const [group, register] of Object.entries(TOOL_GROUPS)) {
|
|
52
|
+
register(toolServer(server, group, ctx.access, registered, ctx), ctx);
|
|
53
|
+
}
|
|
54
|
+
if (appConfig.logLevel === 'debug') {
|
|
55
|
+
console.error(`[MCP] Server created with ${registered.length} tools`);
|
|
56
|
+
}
|
|
57
|
+
return server;
|
|
58
|
+
}
|
|
59
|
+
export { McpServer };
|
|
60
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { RedisClientType } from "redis";
|
|
2
|
+
/**
|
|
3
|
+
* Get or create the Redis client singleton.
|
|
4
|
+
* Returns null if REDIS_URL is not configured or connection fails.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getRedisClient(): Promise<RedisClientType | null>;
|
|
7
|
+
/**
|
|
8
|
+
* Check if Redis is configured
|
|
9
|
+
*/
|
|
10
|
+
export declare function isRedisConfigured(): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Check if Redis is connected and healthy
|
|
13
|
+
*/
|
|
14
|
+
export declare function isRedisConnected(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Ping Redis to check health
|
|
17
|
+
*/
|
|
18
|
+
export declare function pingRedis(): Promise<boolean>;
|
|
19
|
+
/**
|
|
20
|
+
* Gracefully close Redis connection
|
|
21
|
+
*/
|
|
22
|
+
export declare function closeRedisConnection(): Promise<void>;
|
|
23
|
+
//# sourceMappingURL=redis-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-client.d.ts","sourceRoot":"","sources":["../../src/session/redis-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,eAAe,EAAE,MAAM,OAAO,CAAC;AAUtD;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAoBtE;AA4ED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CASlD;AAED;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAc1D"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { createClient } from "redis";
|
|
2
|
+
import { appConfig } from "../config.js";
|
|
3
|
+
let redisClient = null;
|
|
4
|
+
let connectionPromise = null;
|
|
5
|
+
let isConnecting = false;
|
|
6
|
+
let connectionAttempts = 0;
|
|
7
|
+
const MAX_RETRY_ATTEMPTS = 5;
|
|
8
|
+
const RETRY_DELAY_MS = 2000;
|
|
9
|
+
/**
|
|
10
|
+
* Get or create the Redis client singleton.
|
|
11
|
+
* Returns null if REDIS_URL is not configured or connection fails.
|
|
12
|
+
*/
|
|
13
|
+
export async function getRedisClient() {
|
|
14
|
+
if (!appConfig.redis.url) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
// Return existing healthy connection
|
|
18
|
+
if (redisClient?.isOpen) {
|
|
19
|
+
return redisClient;
|
|
20
|
+
}
|
|
21
|
+
// If already connecting, wait for that attempt
|
|
22
|
+
if (connectionPromise) {
|
|
23
|
+
return connectionPromise;
|
|
24
|
+
}
|
|
25
|
+
// Start new connection attempt
|
|
26
|
+
connectionPromise = connectRedis();
|
|
27
|
+
const result = await connectionPromise;
|
|
28
|
+
connectionPromise = null;
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
async function connectRedis() {
|
|
32
|
+
if (isConnecting)
|
|
33
|
+
return null;
|
|
34
|
+
isConnecting = true;
|
|
35
|
+
try {
|
|
36
|
+
const client = createClient({
|
|
37
|
+
url: appConfig.redis.url,
|
|
38
|
+
password: appConfig.redis.password,
|
|
39
|
+
database: appConfig.redis.db,
|
|
40
|
+
socket: {
|
|
41
|
+
connectTimeout: 5000,
|
|
42
|
+
reconnectStrategy: (retries) => {
|
|
43
|
+
if (retries >= MAX_RETRY_ATTEMPTS) {
|
|
44
|
+
console.error(`[Redis] Max reconnection attempts (${MAX_RETRY_ATTEMPTS}) reached`);
|
|
45
|
+
return new Error("Max reconnection attempts reached");
|
|
46
|
+
}
|
|
47
|
+
const delay = Math.min(retries * 500, 3000);
|
|
48
|
+
console.error(`[Redis] Reconnecting in ${delay}ms (attempt ${retries + 1}/${MAX_RETRY_ATTEMPTS})`);
|
|
49
|
+
return delay;
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
client.on("error", (err) => {
|
|
54
|
+
console.error("[Redis] Error:", err.message);
|
|
55
|
+
});
|
|
56
|
+
client.on("reconnecting", () => {
|
|
57
|
+
console.error("[Redis] Reconnecting...");
|
|
58
|
+
});
|
|
59
|
+
client.on("ready", () => {
|
|
60
|
+
connectionAttempts = 0;
|
|
61
|
+
console.error("[Redis] Connection ready");
|
|
62
|
+
});
|
|
63
|
+
client.on("end", () => {
|
|
64
|
+
console.error("[Redis] Connection ended");
|
|
65
|
+
redisClient = null;
|
|
66
|
+
});
|
|
67
|
+
await client.connect();
|
|
68
|
+
console.error("[Redis] Connected to", appConfig.redis.url);
|
|
69
|
+
redisClient = client;
|
|
70
|
+
connectionAttempts = 0;
|
|
71
|
+
return redisClient;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
connectionAttempts++;
|
|
75
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
76
|
+
console.error(`[Redis] Connection failed (attempt ${connectionAttempts}): ${message}`);
|
|
77
|
+
if (connectionAttempts < MAX_RETRY_ATTEMPTS) {
|
|
78
|
+
console.error(`[Redis] Will retry in ${RETRY_DELAY_MS}ms...`);
|
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
80
|
+
isConnecting = false;
|
|
81
|
+
return connectRedis();
|
|
82
|
+
}
|
|
83
|
+
console.error("[Redis] Giving up after max retries, falling back to in-memory");
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
isConnecting = false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if Redis is configured
|
|
92
|
+
*/
|
|
93
|
+
export function isRedisConfigured() {
|
|
94
|
+
return !!appConfig.redis.url;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check if Redis is connected and healthy
|
|
98
|
+
*/
|
|
99
|
+
export function isRedisConnected() {
|
|
100
|
+
return redisClient?.isOpen ?? false;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Ping Redis to check health
|
|
104
|
+
*/
|
|
105
|
+
export async function pingRedis() {
|
|
106
|
+
try {
|
|
107
|
+
const client = await getRedisClient();
|
|
108
|
+
if (!client)
|
|
109
|
+
return false;
|
|
110
|
+
const result = await client.ping();
|
|
111
|
+
return result === "PONG";
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Gracefully close Redis connection
|
|
119
|
+
*/
|
|
120
|
+
export async function closeRedisConnection() {
|
|
121
|
+
if (redisClient?.isOpen) {
|
|
122
|
+
try {
|
|
123
|
+
await redisClient.quit();
|
|
124
|
+
console.error("[Redis] Connection closed gracefully");
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
console.error("[Redis] Error closing connection:", err);
|
|
128
|
+
// Force disconnect if graceful quit fails
|
|
129
|
+
redisClient.disconnect();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
redisClient = null;
|
|
133
|
+
connectionPromise = null;
|
|
134
|
+
connectionAttempts = 0;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=redis-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-client.js","sourceRoot":"","sources":["../../src/session/redis-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAmB,MAAM,OAAO,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,IAAI,WAAW,GAA2B,IAAI,CAAC;AAC/C,IAAI,iBAAiB,GAA2C,IAAI,CAAC;AACrE,IAAI,YAAY,GAAG,KAAK,CAAC;AACzB,IAAI,kBAAkB,GAAG,CAAC,CAAC;AAC3B,MAAM,kBAAkB,GAAG,CAAC,CAAC;AAC7B,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qCAAqC;IACrC,IAAI,WAAW,EAAE,MAAM,EAAE,CAAC;QACxB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,+CAA+C;IAC/C,IAAI,iBAAiB,EAAE,CAAC;QACtB,OAAO,iBAAiB,CAAC;IAC3B,CAAC;IAED,+BAA+B;IAC/B,iBAAiB,GAAG,YAAY,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC;IACvC,iBAAiB,GAAG,IAAI,CAAC;IACzB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,YAAY;IACzB,IAAI,YAAY;QAAE,OAAO,IAAI,CAAC;IAC9B,YAAY,GAAG,IAAI,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC;YAC1B,GAAG,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG;YACxB,QAAQ,EAAE,SAAS,CAAC,KAAK,CAAC,QAAQ;YAClC,QAAQ,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE;YAC5B,MAAM,EAAE;gBACN,cAAc,EAAE,IAAI;gBACpB,iBAAiB,EAAE,CAAC,OAAO,EAAE,EAAE;oBAC7B,IAAI,OAAO,IAAI,kBAAkB,EAAE,CAAC;wBAClC,OAAO,CAAC,KAAK,CACX,sCAAsC,kBAAkB,WAAW,CACpE,CAAC;wBACF,OAAO,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;oBACxD,CAAC;oBACD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC;oBAC5C,OAAO,CAAC,KAAK,CACX,2BAA2B,KAAK,eAAe,OAAO,GAAG,CAAC,IAAI,kBAAkB,GAAG,CACpF,CAAC;oBACF,OAAO,KAAK,CAAC;gBACf,CAAC;aACF;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,GAAG,EAAE;YAC7B,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,kBAAkB,GAAG,CAAC,CAAC;YACvB,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACpB,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YAC1C,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAE3D,WAAW,GAAG,MAAyB,CAAC;QACxC,kBAAkB,GAAG,CAAC,CAAC;QACvB,OAAO,WAAW,CAAC;IACrB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,kBAAkB,EAAE,CAAC;QACrB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACrE,OAAO,CAAC,KAAK,CACX,sCAAsC,kBAAkB,MAAM,OAAO,EAAE,CACxE,CAAC;QAEF,IAAI,kBAAkB,GAAG,kBAAkB,EAAE,CAAC;YAC5C,OAAO,CAAC,KAAK,CAAC,yBAAyB,cAAc,OAAO,CAAC,CAAC;YAC9D,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC,CAAC;YACpE,YAAY,GAAG,KAAK,CAAC;YACrB,OAAO,YAAY,EAAE,CAAC;QACxB,CAAC;QAED,OAAO,CAAC,KAAK,CACX,gEAAgE,CACjE,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;YAAS,CAAC;QACT,YAAY,GAAG,KAAK,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,WAAW,EAAE,MAAM,IAAI,KAAK,CAAC;AACtC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS;IAC7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;QACtC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACnC,OAAO,MAAM,KAAK,MAAM,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB;IACxC,IAAI,WAAW,EAAE,MAAM,EAAE,CAAC;QACxB,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,GAAG,CAAC,CAAC;YACxD,0CAA0C;YAC1C,WAAW,CAAC,UAAU,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,WAAW,GAAG,IAAI,CAAC;IACnB,iBAAiB,GAAG,IAAI,CAAC;IACzB,kBAAkB,GAAG,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SessionMetadata } from "./types.js";
|
|
2
|
+
export declare function registerSession(sessionId: string, tokenHash: string): Promise<void>;
|
|
3
|
+
/** The session if it exists, refreshing its expiry; null otherwise. */
|
|
4
|
+
export declare function touchSession(sessionId: string): Promise<SessionMetadata | null>;
|
|
5
|
+
export declare function deleteSession(sessionId: string): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../../src/session/session-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAqBlD,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,uEAAuE;AACvE,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAyBjC;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASpE"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { getRedisClient, isRedisConfigured } from "./redis-client.js";
|
|
2
|
+
import { appConfig } from "../config.js";
|
|
3
|
+
// SSE sessions only: Streamable HTTP is stateless. A session remembers the
|
|
4
|
+
// hash of the token that opened it, so messages posted to it must come with
|
|
5
|
+
// the same token, on whichever instance they arrive.
|
|
6
|
+
const KEY_PREFIX = "mcp:";
|
|
7
|
+
const ttlMs = () => appConfig.redis.sessionTtlSeconds * 1000;
|
|
8
|
+
const inMemorySessions = new Map();
|
|
9
|
+
// in-memory sessions expire like the Redis ones
|
|
10
|
+
setInterval(() => {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
inMemorySessions.forEach((session, id) => {
|
|
13
|
+
if (now - session.lastAccessedAt > ttlMs()) {
|
|
14
|
+
inMemorySessions.delete(id);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}, 60 * 1000).unref();
|
|
18
|
+
export async function registerSession(sessionId, tokenHash) {
|
|
19
|
+
const metadata = {
|
|
20
|
+
sessionId,
|
|
21
|
+
createdAt: Date.now(),
|
|
22
|
+
lastAccessedAt: Date.now(),
|
|
23
|
+
transportType: "sse",
|
|
24
|
+
tokenHash,
|
|
25
|
+
};
|
|
26
|
+
if (isRedisConfigured()) {
|
|
27
|
+
const redis = await getRedisClient();
|
|
28
|
+
if (redis) {
|
|
29
|
+
await redis.setEx(`${KEY_PREFIX}session:${sessionId}`, appConfig.redis.sessionTtlSeconds, JSON.stringify(metadata));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
inMemorySessions.set(sessionId, metadata);
|
|
34
|
+
}
|
|
35
|
+
/** The session if it exists, refreshing its expiry; null otherwise. */
|
|
36
|
+
export async function touchSession(sessionId) {
|
|
37
|
+
if (isRedisConfigured()) {
|
|
38
|
+
const redis = await getRedisClient();
|
|
39
|
+
if (redis) {
|
|
40
|
+
const key = `${KEY_PREFIX}session:${sessionId}`;
|
|
41
|
+
const data = await redis.get(key);
|
|
42
|
+
if (!data)
|
|
43
|
+
return null;
|
|
44
|
+
const metadata = JSON.parse(data);
|
|
45
|
+
metadata.lastAccessedAt = Date.now();
|
|
46
|
+
await redis.setEx(key, appConfig.redis.sessionTtlSeconds, JSON.stringify(metadata));
|
|
47
|
+
return metadata;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const session = inMemorySessions.get(sessionId);
|
|
51
|
+
if (!session || Date.now() - session.lastAccessedAt > ttlMs()) {
|
|
52
|
+
inMemorySessions.delete(sessionId);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
session.lastAccessedAt = Date.now();
|
|
56
|
+
return session;
|
|
57
|
+
}
|
|
58
|
+
export async function deleteSession(sessionId) {
|
|
59
|
+
if (isRedisConfigured()) {
|
|
60
|
+
const redis = await getRedisClient();
|
|
61
|
+
if (redis) {
|
|
62
|
+
await redis.del(`${KEY_PREFIX}session:${sessionId}`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
inMemorySessions.delete(sessionId);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../src/session/session-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
type MessageHandler = (message: string) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Subscribe to messages for a session
|
|
4
|
+
*/
|
|
5
|
+
export declare function subscribeToSession(sessionId: string, handler: MessageHandler): Promise<void>;
|
|
6
|
+
/**
|
|
7
|
+
* Unsubscribe from a session
|
|
8
|
+
*/
|
|
9
|
+
export declare function unsubscribeFromSession(sessionId: string): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Publish a message to a session (can be on any server)
|
|
12
|
+
*/
|
|
13
|
+
export declare function publishToSession(sessionId: string, message: string): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Check if a session has a local subscriber (SSE connection on this server)
|
|
16
|
+
*/
|
|
17
|
+
export declare function hasLocalSubscriber(sessionId: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Close pub/sub connections
|
|
20
|
+
*/
|
|
21
|
+
export declare function closePubSub(): Promise<void>;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=sse-pubsub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-pubsub.d.ts","sourceRoot":"","sources":["../../src/session/sse-pubsub.ts"],"names":[],"mappings":"AAIA,KAAK,cAAc,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAyDhD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7E;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC,CAqBlB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAYjD"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { createClient } from "redis";
|
|
2
|
+
import { appConfig } from "../config.js";
|
|
3
|
+
import { isRedisConfigured } from "./redis-client.js";
|
|
4
|
+
// Separate client for pub/sub (required by Redis - can't use same connection)
|
|
5
|
+
let subscriberClient = null;
|
|
6
|
+
let publisherClient = null;
|
|
7
|
+
// Local subscriptions: sessionId -> handler
|
|
8
|
+
const localSubscriptions = new Map();
|
|
9
|
+
// In-memory fallback for non-Redis mode
|
|
10
|
+
const inMemoryQueues = new Map();
|
|
11
|
+
const CHANNEL_PREFIX = "mcp:sse:";
|
|
12
|
+
/**
|
|
13
|
+
* Initialize pub/sub clients
|
|
14
|
+
*/
|
|
15
|
+
async function ensureClients() {
|
|
16
|
+
if (!isRedisConfigured() || !appConfig.redis.url) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (subscriberClient?.isOpen && publisherClient?.isOpen) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const config = {
|
|
24
|
+
url: appConfig.redis.url,
|
|
25
|
+
password: appConfig.redis.password,
|
|
26
|
+
database: appConfig.redis.db,
|
|
27
|
+
};
|
|
28
|
+
if (!subscriberClient?.isOpen) {
|
|
29
|
+
subscriberClient = createClient(config);
|
|
30
|
+
subscriberClient.on("error", (err) => {
|
|
31
|
+
console.error("[SSE PubSub] Subscriber error:", err.message);
|
|
32
|
+
});
|
|
33
|
+
await subscriberClient.connect();
|
|
34
|
+
}
|
|
35
|
+
if (!publisherClient?.isOpen) {
|
|
36
|
+
publisherClient = createClient(config);
|
|
37
|
+
publisherClient.on("error", (err) => {
|
|
38
|
+
console.error("[SSE PubSub] Publisher error:", err.message);
|
|
39
|
+
});
|
|
40
|
+
await publisherClient.connect();
|
|
41
|
+
}
|
|
42
|
+
console.error("[SSE PubSub] Connected");
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
console.error("[SSE PubSub] Connection failed:", err);
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to messages for a session
|
|
52
|
+
*/
|
|
53
|
+
export async function subscribeToSession(sessionId, handler) {
|
|
54
|
+
localSubscriptions.set(sessionId, handler);
|
|
55
|
+
const redisAvailable = await ensureClients();
|
|
56
|
+
if (redisAvailable && subscriberClient) {
|
|
57
|
+
const channel = `${CHANNEL_PREFIX}${sessionId}`;
|
|
58
|
+
await subscriberClient.subscribe(channel, (message) => {
|
|
59
|
+
const localHandler = localSubscriptions.get(sessionId);
|
|
60
|
+
if (localHandler) {
|
|
61
|
+
localHandler(message);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Unsubscribe from a session
|
|
68
|
+
*/
|
|
69
|
+
export async function unsubscribeFromSession(sessionId) {
|
|
70
|
+
localSubscriptions.delete(sessionId);
|
|
71
|
+
inMemoryQueues.delete(sessionId);
|
|
72
|
+
if (subscriberClient?.isOpen) {
|
|
73
|
+
const channel = `${CHANNEL_PREFIX}${sessionId}`;
|
|
74
|
+
await subscriberClient.unsubscribe(channel).catch(() => { });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Publish a message to a session (can be on any server)
|
|
79
|
+
*/
|
|
80
|
+
export async function publishToSession(sessionId, message) {
|
|
81
|
+
// Try local first (same server optimization)
|
|
82
|
+
const localHandler = localSubscriptions.get(sessionId);
|
|
83
|
+
if (localHandler) {
|
|
84
|
+
localHandler(message);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
// Try Redis pub/sub
|
|
88
|
+
const redisAvailable = await ensureClients();
|
|
89
|
+
if (redisAvailable && publisherClient) {
|
|
90
|
+
const channel = `${CHANNEL_PREFIX}${sessionId}`;
|
|
91
|
+
const receivers = await publisherClient.publish(channel, message);
|
|
92
|
+
return receivers > 0;
|
|
93
|
+
}
|
|
94
|
+
// Fallback: queue in memory (only works for same server)
|
|
95
|
+
const queue = inMemoryQueues.get(sessionId) ?? [];
|
|
96
|
+
queue.push(message);
|
|
97
|
+
inMemoryQueues.set(sessionId, queue);
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if a session has a local subscriber (SSE connection on this server)
|
|
102
|
+
*/
|
|
103
|
+
export function hasLocalSubscriber(sessionId) {
|
|
104
|
+
return localSubscriptions.has(sessionId);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Close pub/sub connections
|
|
108
|
+
*/
|
|
109
|
+
export async function closePubSub() {
|
|
110
|
+
localSubscriptions.clear();
|
|
111
|
+
inMemoryQueues.clear();
|
|
112
|
+
if (subscriberClient?.isOpen) {
|
|
113
|
+
await subscriberClient.quit().catch(() => { });
|
|
114
|
+
subscriberClient = null;
|
|
115
|
+
}
|
|
116
|
+
if (publisherClient?.isOpen) {
|
|
117
|
+
await publisherClient.quit().catch(() => { });
|
|
118
|
+
publisherClient = null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=sse-pubsub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-pubsub.js","sourceRoot":"","sources":["../../src/session/sse-pubsub.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
export type StreamId = string;
|
|
3
|
+
export type EventId = string;
|
|
4
|
+
/**
|
|
5
|
+
* Stored event structure in Redis
|
|
6
|
+
*/
|
|
7
|
+
export interface StoredEvent {
|
|
8
|
+
eventId: EventId;
|
|
9
|
+
streamId: StreamId;
|
|
10
|
+
message: JSONRPCMessage;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Session metadata stored in Redis
|
|
15
|
+
*/
|
|
16
|
+
export interface SessionMetadata {
|
|
17
|
+
sessionId: string;
|
|
18
|
+
createdAt: number;
|
|
19
|
+
lastAccessedAt: number;
|
|
20
|
+
transportType: "sse";
|
|
21
|
+
/** sha256 of the access token that opened the session */
|
|
22
|
+
tokenHash: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* EventStore interface from MCP SDK (re-exported for convenience)
|
|
26
|
+
*/
|
|
27
|
+
export interface EventStore {
|
|
28
|
+
/**
|
|
29
|
+
* Stores an event for later retrieval
|
|
30
|
+
*/
|
|
31
|
+
storeEvent(streamId: StreamId, message: JSONRPCMessage): Promise<EventId>;
|
|
32
|
+
/**
|
|
33
|
+
* Get the stream ID associated with a given event ID
|
|
34
|
+
*/
|
|
35
|
+
getStreamIdForEventId?(eventId: EventId): Promise<StreamId | undefined>;
|
|
36
|
+
/**
|
|
37
|
+
* Replay events after a given event ID
|
|
38
|
+
*/
|
|
39
|
+
replayEventsAfter(lastEventId: EventId, callbacks: {
|
|
40
|
+
send: (eventId: EventId, message: JSONRPCMessage) => Promise<void>;
|
|
41
|
+
}): Promise<StreamId>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAEzE,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAC9B,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,KAAK,CAAC;IACrB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAE1E;;OAEG;IACH,qBAAqB,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAExE;;OAEG;IACH,iBAAiB,CACf,WAAW,EAAE,OAAO,EACpB,SAAS,EAAE;QACT,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACpE,GACA,OAAO,CAAC,QAAQ,CAAC,CAAC;CACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/advisor/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAmD3D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,GAAG,IAAI,CA+H/E"}
|