minecraft-bedrock-mcp-server 0.1.0
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 +577 -0
- package/dist/bridge/event-route.d.ts +11 -0
- package/dist/bridge/event-route.d.ts.map +1 -0
- package/dist/bridge/event-route.js +28 -0
- package/dist/bridge/event-route.js.map +1 -0
- package/dist/bridge/handshake-route.d.ts +11 -0
- package/dist/bridge/handshake-route.d.ts.map +1 -0
- package/dist/bridge/handshake-route.js +45 -0
- package/dist/bridge/handshake-route.js.map +1 -0
- package/dist/bridge/poll-route.d.ts +11 -0
- package/dist/bridge/poll-route.d.ts.map +1 -0
- package/dist/bridge/poll-route.js +28 -0
- package/dist/bridge/poll-route.js.map +1 -0
- package/dist/bridge/result-route.d.ts +10 -0
- package/dist/bridge/result-route.d.ts.map +1 -0
- package/dist/bridge/result-route.js +23 -0
- package/dist/bridge/result-route.js.map +1 -0
- package/dist/config/config-error.d.ts +12 -0
- package/dist/config/config-error.d.ts.map +1 -0
- package/dist/config/config-error.js +13 -0
- package/dist/config/config-error.js.map +1 -0
- package/dist/config/environment.d.ts +89 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +73 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/tls.d.ts +15 -0
- package/dist/config/tls.d.ts.map +1 -0
- package/dist/config/tls.js +27 -0
- package/dist/config/tls.js.map +1 -0
- package/dist/errors/bridge-error.d.ts +41 -0
- package/dist/errors/bridge-error.d.ts.map +1 -0
- package/dist/errors/bridge-error.js +33 -0
- package/dist/errors/bridge-error.js.map +1 -0
- package/dist/errors/error-codes.d.ts +13 -0
- package/dist/errors/error-codes.d.ts.map +1 -0
- package/dist/errors/error-codes.js +41 -0
- package/dist/errors/error-codes.js.map +1 -0
- package/dist/events/subscription-registry.d.ts +55 -0
- package/dist/events/subscription-registry.d.ts.map +1 -0
- package/dist/events/subscription-registry.js +64 -0
- package/dist/events/subscription-registry.js.map +1 -0
- package/dist/http/app.d.ts +26 -0
- package/dist/http/app.d.ts.map +1 -0
- package/dist/http/app.js +88 -0
- package/dist/http/app.js.map +1 -0
- package/dist/http/authentication.d.ts +11 -0
- package/dist/http/authentication.d.ts.map +1 -0
- package/dist/http/authentication.js +36 -0
- package/dist/http/authentication.js.map +1 -0
- package/dist/http/health-route.d.ts +10 -0
- package/dist/http/health-route.d.ts.map +1 -0
- package/dist/http/health-route.js +18 -0
- package/dist/http/health-route.js.map +1 -0
- package/dist/http/metrics-route.d.ts +8 -0
- package/dist/http/metrics-route.d.ts.map +1 -0
- package/dist/http/metrics-route.js +11 -0
- package/dist/http/metrics-route.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +134 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/mcp-route.d.ts +12 -0
- package/dist/mcp/mcp-route.d.ts.map +1 -0
- package/dist/mcp/mcp-route.js +49 -0
- package/dist/mcp/mcp-route.js.map +1 -0
- package/dist/mcp/mcp-server-factory.d.ts +20 -0
- package/dist/mcp/mcp-server-factory.d.ts.map +1 -0
- package/dist/mcp/mcp-server-factory.js +23 -0
- package/dist/mcp/mcp-server-factory.js.map +1 -0
- package/dist/mcp/session-manager.d.ts +30 -0
- package/dist/mcp/session-manager.d.ts.map +1 -0
- package/dist/mcp/session-manager.js +38 -0
- package/dist/mcp/session-manager.js.map +1 -0
- package/dist/mcp/tool-registry.d.ts +15 -0
- package/dist/mcp/tool-registry.d.ts.map +1 -0
- package/dist/mcp/tool-registry.js +27 -0
- package/dist/mcp/tool-registry.js.map +1 -0
- package/dist/observability/logger.d.ts +16 -0
- package/dist/observability/logger.d.ts.map +1 -0
- package/dist/observability/logger.js +19 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +27 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +72 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/protocol/command.d.ts +92 -0
- package/dist/protocol/command.d.ts.map +1 -0
- package/dist/protocol/command.js +37 -0
- package/dist/protocol/command.js.map +1 -0
- package/dist/protocol/event.d.ts +82 -0
- package/dist/protocol/event.d.ts.map +1 -0
- package/dist/protocol/event.js +37 -0
- package/dist/protocol/event.js.map +1 -0
- package/dist/protocol/handshake.d.ts +150 -0
- package/dist/protocol/handshake.d.ts.map +1 -0
- package/dist/protocol/handshake.js +65 -0
- package/dist/protocol/handshake.js.map +1 -0
- package/dist/protocol/index.d.ts +14 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +14 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/protocol-version.d.ts +18 -0
- package/dist/protocol/protocol-version.d.ts.map +1 -0
- package/dist/protocol/protocol-version.js +28 -0
- package/dist/protocol/protocol-version.js.map +1 -0
- package/dist/protocol/result.d.ts +79 -0
- package/dist/protocol/result.d.ts.map +1 -0
- package/dist/protocol/result.js +35 -0
- package/dist/protocol/result.js.map +1 -0
- package/dist/queue/command-id.d.ts +15 -0
- package/dist/queue/command-id.d.ts.map +1 -0
- package/dist/queue/command-id.js +38 -0
- package/dist/queue/command-id.js.map +1 -0
- package/dist/queue/command-queue.d.ts +90 -0
- package/dist/queue/command-queue.d.ts.map +1 -0
- package/dist/queue/command-queue.js +151 -0
- package/dist/queue/command-queue.js.map +1 -0
- package/dist/queue/command-throttle.d.ts +38 -0
- package/dist/queue/command-throttle.d.ts.map +1 -0
- package/dist/queue/command-throttle.js +26 -0
- package/dist/queue/command-throttle.js.map +1 -0
- package/dist/queue/instrumented-command-queue.d.ts +9 -0
- package/dist/queue/instrumented-command-queue.d.ts.map +1 -0
- package/dist/queue/instrumented-command-queue.js +39 -0
- package/dist/queue/instrumented-command-queue.js.map +1 -0
- package/dist/queue/pending-commands.d.ts +35 -0
- package/dist/queue/pending-commands.d.ts.map +1 -0
- package/dist/queue/pending-commands.js +41 -0
- package/dist/queue/pending-commands.js.map +1 -0
- package/dist/server-info.d.ts +5 -0
- package/dist/server-info.d.ts.map +1 -0
- package/dist/server-info.js +22 -0
- package/dist/server-info.js.map +1 -0
- package/dist/structures/structure-file-store.d.ts +22 -0
- package/dist/structures/structure-file-store.d.ts.map +1 -0
- package/dist/structures/structure-file-store.js +89 -0
- package/dist/structures/structure-file-store.js.map +1 -0
- package/dist/tools/block-tools.d.ts +4 -0
- package/dist/tools/block-tools.d.ts.map +1 -0
- package/dist/tools/block-tools.js +111 -0
- package/dist/tools/block-tools.js.map +1 -0
- package/dist/tools/command-tools.d.ts +4 -0
- package/dist/tools/command-tools.d.ts.map +1 -0
- package/dist/tools/command-tools.js +23 -0
- package/dist/tools/command-tools.js.map +1 -0
- package/dist/tools/common-schemas.d.ts +65 -0
- package/dist/tools/common-schemas.d.ts.map +1 -0
- package/dist/tools/common-schemas.js +32 -0
- package/dist/tools/common-schemas.js.map +1 -0
- package/dist/tools/effect-tools.d.ts +4 -0
- package/dist/tools/effect-tools.d.ts.map +1 -0
- package/dist/tools/effect-tools.js +32 -0
- package/dist/tools/effect-tools.js.map +1 -0
- package/dist/tools/entity-tools.d.ts +4 -0
- package/dist/tools/entity-tools.d.ts.map +1 -0
- package/dist/tools/entity-tools.js +133 -0
- package/dist/tools/entity-tools.js.map +1 -0
- package/dist/tools/event-tools.d.ts +4 -0
- package/dist/tools/event-tools.d.ts.map +1 -0
- package/dist/tools/event-tools.js +84 -0
- package/dist/tools/event-tools.js.map +1 -0
- package/dist/tools/index.d.ts +4 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +30 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/inventory-tools.d.ts +4 -0
- package/dist/tools/inventory-tools.d.ts.map +1 -0
- package/dist/tools/inventory-tools.js +52 -0
- package/dist/tools/inventory-tools.js.map +1 -0
- package/dist/tools/player-tools.d.ts +4 -0
- package/dist/tools/player-tools.d.ts.map +1 -0
- package/dist/tools/player-tools.js +117 -0
- package/dist/tools/player-tools.js.map +1 -0
- package/dist/tools/property-tools.d.ts +4 -0
- package/dist/tools/property-tools.d.ts.map +1 -0
- package/dist/tools/property-tools.js +45 -0
- package/dist/tools/property-tools.js.map +1 -0
- package/dist/tools/scoreboard-tools.d.ts +4 -0
- package/dist/tools/scoreboard-tools.d.ts.map +1 -0
- package/dist/tools/scoreboard-tools.js +68 -0
- package/dist/tools/scoreboard-tools.js.map +1 -0
- package/dist/tools/server-tools.d.ts +4 -0
- package/dist/tools/server-tools.d.ts.map +1 -0
- package/dist/tools/server-tools.js +25 -0
- package/dist/tools/server-tools.js.map +1 -0
- package/dist/tools/structure-file-tools.d.ts +4 -0
- package/dist/tools/structure-file-tools.d.ts.map +1 -0
- package/dist/tools/structure-file-tools.js +57 -0
- package/dist/tools/structure-file-tools.js.map +1 -0
- package/dist/tools/structure-tools.d.ts +4 -0
- package/dist/tools/structure-tools.d.ts.map +1 -0
- package/dist/tools/structure-tools.js +91 -0
- package/dist/tools/structure-tools.js.map +1 -0
- package/dist/tools/tool-definition.d.ts +68 -0
- package/dist/tools/tool-definition.d.ts.map +1 -0
- package/dist/tools/tool-definition.js +40 -0
- package/dist/tools/tool-definition.js.map +1 -0
- package/dist/tools/tool-result.d.ts +17 -0
- package/dist/tools/tool-result.d.ts.map +1 -0
- package/dist/tools/tool-result.js +52 -0
- package/dist/tools/tool-result.js.map +1 -0
- package/dist/tools/world-tools.d.ts +4 -0
- package/dist/tools/world-tools.d.ts.map +1 -0
- package/dist/tools/world-tools.js +97 -0
- package/dist/tools/world-tools.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { newSubscriptionId } from "../queue/command-id.js";
|
|
2
|
+
/** Creates an empty {@link SubscriptionRegistry}. */
|
|
3
|
+
export function createSubscriptionRegistry(options) {
|
|
4
|
+
const states = new Map();
|
|
5
|
+
function snapshot(state) {
|
|
6
|
+
return {
|
|
7
|
+
...state.subscription,
|
|
8
|
+
buffered: state.buffer.length,
|
|
9
|
+
lastSequence: state.nextSequence - 1,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
create(eventType, filter) {
|
|
14
|
+
const subscription = {
|
|
15
|
+
id: newSubscriptionId(),
|
|
16
|
+
eventType,
|
|
17
|
+
filter,
|
|
18
|
+
createdAt: new Date().toISOString(),
|
|
19
|
+
};
|
|
20
|
+
states.set(subscription.id, { subscription, buffer: [], nextSequence: 0 });
|
|
21
|
+
return subscription;
|
|
22
|
+
},
|
|
23
|
+
remove(id) {
|
|
24
|
+
return states.delete(id);
|
|
25
|
+
},
|
|
26
|
+
get(id) {
|
|
27
|
+
return states.get(id)?.subscription;
|
|
28
|
+
},
|
|
29
|
+
list() {
|
|
30
|
+
return [...states.values()].map(snapshot);
|
|
31
|
+
},
|
|
32
|
+
ingest(event) {
|
|
33
|
+
const state = states.get(event.subscription_id);
|
|
34
|
+
if (state === undefined)
|
|
35
|
+
return false;
|
|
36
|
+
state.buffer.push({
|
|
37
|
+
sequence: state.nextSequence,
|
|
38
|
+
event_type: event.event_type,
|
|
39
|
+
occurred_at: event.occurred_at,
|
|
40
|
+
payload: event.payload,
|
|
41
|
+
});
|
|
42
|
+
state.nextSequence += 1;
|
|
43
|
+
if (state.buffer.length > options.bufferSize) {
|
|
44
|
+
state.buffer.shift();
|
|
45
|
+
}
|
|
46
|
+
options.onIngest?.(event.subscription_id);
|
|
47
|
+
return true;
|
|
48
|
+
},
|
|
49
|
+
poll(id, cursor) {
|
|
50
|
+
const state = states.get(id);
|
|
51
|
+
if (state === undefined) {
|
|
52
|
+
return { events: [], cursor: cursor ?? "0" };
|
|
53
|
+
}
|
|
54
|
+
const after = cursor === undefined ? -1 : Number(cursor);
|
|
55
|
+
const events = state.buffer.filter((event) => event.sequence > after);
|
|
56
|
+
const last = events.at(-1);
|
|
57
|
+
return {
|
|
58
|
+
events,
|
|
59
|
+
cursor: String(last === undefined ? Math.max(0, after) : last.sequence),
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=subscription-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscription-registry.js","sourceRoot":"","sources":["../../src/events/subscription-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type FastifyInstance } from "fastify";
|
|
2
|
+
import { type Environment } from "../config/environment.js";
|
|
3
|
+
import type { TlsMaterial } from "../config/tls.js";
|
|
4
|
+
import type { SubscriptionRegistry } from "../events/subscription-registry.js";
|
|
5
|
+
import type { SessionManager } from "../mcp/session-manager.js";
|
|
6
|
+
import type { Logger } from "../observability/logger.js";
|
|
7
|
+
import type { Metrics } from "../observability/metrics.js";
|
|
8
|
+
import type { CommandQueue } from "../queue/command-queue.js";
|
|
9
|
+
/** Dependencies required to assemble the HTTP application. */
|
|
10
|
+
export interface AppDependencies {
|
|
11
|
+
readonly environment: Environment;
|
|
12
|
+
readonly logger: Logger;
|
|
13
|
+
readonly queue: CommandQueue;
|
|
14
|
+
readonly subscriptions: SubscriptionRegistry;
|
|
15
|
+
readonly sessionManager: SessionManager;
|
|
16
|
+
/** Present only when `BRIDGE_METRICS_ENABLED` is set. */
|
|
17
|
+
readonly metrics?: Metrics;
|
|
18
|
+
readonly tls: TlsMaterial | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Assembles the Fastify application: an unauthenticated health probe, the
|
|
22
|
+
* agent-token `/bridge` surface, the client-token `/mcp` surface, and — when
|
|
23
|
+
* enabled — a client-token `/metrics` surface. The caller calls `listen`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createApp(deps: AppDependencies): FastifyInstance;
|
|
26
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/http/app.ts"],"names":[],"mappings":"AAEA,OAAgB,EAA0B,KAAK,eAAe,EAAE,MAAM,SAAS,CAAC;AAOhF,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAQ9D,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,oBAAoB,CAAC;IAC7C,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,yDAAyD;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC;AAgBD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,eAAe,GAAG,eAAe,CA4EhE"}
|
package/dist/http/app.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import Fastify, {} from "fastify";
|
|
3
|
+
import cors from "@fastify/cors";
|
|
4
|
+
import rateLimit from "@fastify/rate-limit";
|
|
5
|
+
import { registerEventRoute } from "../bridge/event-route.js";
|
|
6
|
+
import { registerHandshakeRoute } from "../bridge/handshake-route.js";
|
|
7
|
+
import { registerPollRoute } from "../bridge/poll-route.js";
|
|
8
|
+
import { registerResultRoute } from "../bridge/result-route.js";
|
|
9
|
+
import { corsOrigins } from "../config/environment.js";
|
|
10
|
+
import { registerMcpRoute } from "../mcp/mcp-route.js";
|
|
11
|
+
import { SERVER_VERSION } from "../server-info.js";
|
|
12
|
+
import { bearerAuth } from "./authentication.js";
|
|
13
|
+
import { registerHealthRoute } from "./health-route.js";
|
|
14
|
+
import { registerMetricsRoute } from "./metrics-route.js";
|
|
15
|
+
const CORRELATION_ID_HEADER = "x-correlation-id";
|
|
16
|
+
/** Derives a request correlation id from an inbound header, or mints one. */
|
|
17
|
+
function correlationIdFor(req) {
|
|
18
|
+
const header = req.headers[CORRELATION_ID_HEADER];
|
|
19
|
+
if (typeof header === "string" && header.length > 0)
|
|
20
|
+
return header;
|
|
21
|
+
return randomUUID();
|
|
22
|
+
}
|
|
23
|
+
/** Per-token rate-limit key: a hash of the bearer header, never the raw token. */
|
|
24
|
+
function rateLimitKey(authorization) {
|
|
25
|
+
return createHash("sha256")
|
|
26
|
+
.update(authorization ?? "anonymous")
|
|
27
|
+
.digest("hex");
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Assembles the Fastify application: an unauthenticated health probe, the
|
|
31
|
+
* agent-token `/bridge` surface, the client-token `/mcp` surface, and — when
|
|
32
|
+
* enabled — a client-token `/metrics` surface. The caller calls `listen`.
|
|
33
|
+
*/
|
|
34
|
+
export function createApp(deps) {
|
|
35
|
+
const { environment, logger, queue, subscriptions, sessionManager, metrics, tls } = deps;
|
|
36
|
+
// Pin Fastify's logger generic to FastifyBaseLogger so the assembled
|
|
37
|
+
// instance is the plain FastifyInstance the route helpers accept.
|
|
38
|
+
const fastifyLogger = logger;
|
|
39
|
+
const app = Fastify({
|
|
40
|
+
loggerInstance: fastifyLogger,
|
|
41
|
+
genReqId: correlationIdFor,
|
|
42
|
+
bodyLimit: environment.BRIDGE_MAX_BODY_BYTES,
|
|
43
|
+
trustProxy: environment.BRIDGE_TRUST_PROXY,
|
|
44
|
+
...(tls === null ? {} : { https: { cert: tls.cert, key: tls.key } }),
|
|
45
|
+
});
|
|
46
|
+
if (metrics !== undefined) {
|
|
47
|
+
app.addHook("onResponse", (request, reply, done) => {
|
|
48
|
+
metrics.recordHttpRequest(request.method, reply.statusCode);
|
|
49
|
+
done();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const origins = corsOrigins(environment);
|
|
53
|
+
if (origins !== null) {
|
|
54
|
+
void app.register(cors, { origin: origins });
|
|
55
|
+
}
|
|
56
|
+
// Unauthenticated liveness probe.
|
|
57
|
+
registerHealthRoute(app, queue);
|
|
58
|
+
// Bridge surface — the behavior pack, authenticated with the agent token.
|
|
59
|
+
void app.register((bridge, _opts, done) => {
|
|
60
|
+
bridge.addHook("onRequest", bearerAuth("agent", environment.BRIDGE_AGENT_TOKEN));
|
|
61
|
+
registerPollRoute(bridge, queue, environment.BRIDGE_POLL_TIMEOUT_MS);
|
|
62
|
+
registerResultRoute(bridge, queue);
|
|
63
|
+
registerEventRoute(bridge, subscriptions);
|
|
64
|
+
registerHandshakeRoute(bridge, SERVER_VERSION, environment.BRIDGE_POLL_TIMEOUT_MS, subscriptions);
|
|
65
|
+
done();
|
|
66
|
+
}, { prefix: "/bridge" });
|
|
67
|
+
// MCP surface — clients, authenticated with the client token, rate limited.
|
|
68
|
+
void app.register((mcp, _opts, done) => {
|
|
69
|
+
mcp.addHook("onRequest", bearerAuth("client", environment.BRIDGE_CLIENT_TOKEN));
|
|
70
|
+
void mcp.register(rateLimit, {
|
|
71
|
+
max: environment.BRIDGE_RATE_LIMIT_RPM,
|
|
72
|
+
timeWindow: "1 minute",
|
|
73
|
+
keyGenerator: (request) => rateLimitKey(request.headers.authorization),
|
|
74
|
+
});
|
|
75
|
+
registerMcpRoute(mcp, sessionManager);
|
|
76
|
+
done();
|
|
77
|
+
}, { prefix: "/mcp" });
|
|
78
|
+
// Metrics surface — Prometheus scrape, authenticated with the client token.
|
|
79
|
+
if (metrics !== undefined) {
|
|
80
|
+
void app.register((scope, _opts, done) => {
|
|
81
|
+
scope.addHook("onRequest", bearerAuth("client", environment.BRIDGE_CLIENT_TOKEN));
|
|
82
|
+
registerMetricsRoute(scope, metrics);
|
|
83
|
+
done();
|
|
84
|
+
}, { prefix: "/metrics" });
|
|
85
|
+
}
|
|
86
|
+
return app;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/http/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
+
/** Which bearer token a request scope requires. */
|
|
3
|
+
export type AuthScope = "client" | "agent";
|
|
4
|
+
/**
|
|
5
|
+
* Builds a Fastify `onRequest` hook that enforces a single bearer token.
|
|
6
|
+
*
|
|
7
|
+
* On failure it responds `401` with no body, a `WWW-Authenticate` header, and
|
|
8
|
+
* a warning log line carrying the source IP — never the token itself.
|
|
9
|
+
*/
|
|
10
|
+
export declare function bearerAuth(scope: AuthScope, expectedToken: string): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
11
|
+
//# sourceMappingURL=authentication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authentication.d.ts","sourceRoot":"","sources":["../../src/http/authentication.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE5D,mDAAmD;AACnD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAsB3C;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,IAC7B,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC,CAWhG"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from "node:crypto";
|
|
2
|
+
function digest(value) {
|
|
3
|
+
return createHash("sha256").update(value).digest();
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Constant-time bearer token comparison.
|
|
7
|
+
*
|
|
8
|
+
* Both tokens are reduced to fixed-width SHA-256 digests first, so the
|
|
9
|
+
* comparison time does not leak the provided token's length.
|
|
10
|
+
*/
|
|
11
|
+
function tokensMatch(provided, expected) {
|
|
12
|
+
return timingSafeEqual(digest(provided), digest(expected));
|
|
13
|
+
}
|
|
14
|
+
function extractBearerToken(header) {
|
|
15
|
+
const prefix = "Bearer ";
|
|
16
|
+
if (header === undefined || !header.startsWith(prefix))
|
|
17
|
+
return undefined;
|
|
18
|
+
return header.slice(prefix.length);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Builds a Fastify `onRequest` hook that enforces a single bearer token.
|
|
22
|
+
*
|
|
23
|
+
* On failure it responds `401` with no body, a `WWW-Authenticate` header, and
|
|
24
|
+
* a warning log line carrying the source IP — never the token itself.
|
|
25
|
+
*/
|
|
26
|
+
export function bearerAuth(scope, expectedToken) {
|
|
27
|
+
return async function authenticate(request, reply) {
|
|
28
|
+
const token = extractBearerToken(request.headers.authorization);
|
|
29
|
+
if (token !== undefined && tokensMatch(token, expectedToken)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
request.log.warn({ scope, ip: request.ip, method: request.method, url: request.url }, "rejected unauthenticated request");
|
|
33
|
+
await reply.code(401).header("WWW-Authenticate", `Bearer realm="${scope}"`).send();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=authentication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authentication.js","sourceRoot":"","sources":["../../src/http/authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAM1D,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;AACrD,CAAC;AAED;;;;;GAKG;AACH,SAAS,WAAW,CAAC,QAAgB,EAAE,QAAgB;IACrD,OAAO,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,kBAAkB,CAAC,MAA0B;IACpD,MAAM,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IACzE,OAAO,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAgB,EAAE,aAAqB;IAChE,OAAO,KAAK,UAAU,YAAY,CAAC,OAAuB,EAAE,KAAmB;QAC7E,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAChE,IAAI,KAAK,KAAK,SAAS,IAAI,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,EAAE,CAAC;YAC7D,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,IAAI,CACd,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EACnE,kCAAkC,CACnC,CAAC;QACF,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,kBAAkB,EAAE,iBAAiB,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACrF,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { CommandQueue } from "../queue/command-queue.js";
|
|
3
|
+
/**
|
|
4
|
+
* Registers `GET /healthz` — an unauthenticated liveness probe.
|
|
5
|
+
*
|
|
6
|
+
* Always returns `200` while the process is responsive; behavior-pack
|
|
7
|
+
* connectivity is reported in the body as readiness information.
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerHealthRoute(app: FastifyInstance, queue: CommandQueue): void;
|
|
10
|
+
//# sourceMappingURL=health-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-route.d.ts","sourceRoot":"","sources":["../../src/http/health-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAUnF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers `GET /healthz` — an unauthenticated liveness probe.
|
|
3
|
+
*
|
|
4
|
+
* Always returns `200` while the process is responsive; behavior-pack
|
|
5
|
+
* connectivity is reported in the body as readiness information.
|
|
6
|
+
*/
|
|
7
|
+
export function registerHealthRoute(app, queue) {
|
|
8
|
+
app.get("/healthz", () => {
|
|
9
|
+
const stats = queue.stats();
|
|
10
|
+
return {
|
|
11
|
+
status: "ok",
|
|
12
|
+
bridge_connected: stats.bridgeConnected,
|
|
13
|
+
queue_depth: stats.depth,
|
|
14
|
+
commands_in_flight: stats.inFlight,
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=health-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-route.js","sourceRoot":"","sources":["../../src/http/health-route.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAoB,EAAE,KAAmB;IAC3E,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,EAAE;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;QAC5B,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,gBAAgB,EAAE,KAAK,CAAC,eAAe;YACvC,WAAW,EAAE,KAAK,CAAC,KAAK;YACxB,kBAAkB,EAAE,KAAK,CAAC,QAAQ;SACnC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { Metrics } from "../observability/metrics.js";
|
|
3
|
+
/**
|
|
4
|
+
* Registers `GET /metrics` — the Prometheus scrape endpoint. Mounted only when
|
|
5
|
+
* `BRIDGE_METRICS_ENABLED` is set.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerMetricsRoute(app: FastifyInstance, metrics: Metrics): void;
|
|
8
|
+
//# sourceMappingURL=metrics-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-route.d.ts","sourceRoot":"","sources":["../../src/http/metrics-route.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAKjF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registers `GET /metrics` — the Prometheus scrape endpoint. Mounted only when
|
|
3
|
+
* `BRIDGE_METRICS_ENABLED` is set.
|
|
4
|
+
*/
|
|
5
|
+
export function registerMetricsRoute(app, metrics) {
|
|
6
|
+
app.get("/", async (_request, reply) => {
|
|
7
|
+
const body = await metrics.registry.metrics();
|
|
8
|
+
return reply.header("content-type", metrics.registry.contentType).send(body);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=metrics-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics-route.js","sourceRoot":"","sources":["../../src/http/metrics-route.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAoB,EAAE,OAAgB;IACzE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { ConfigError } from "./config/config-error.js";
|
|
4
|
+
import { loadEnvironment } from "./config/environment.js";
|
|
5
|
+
import { loadTlsMaterial } from "./config/tls.js";
|
|
6
|
+
import { createSubscriptionRegistry } from "./events/subscription-registry.js";
|
|
7
|
+
import { createApp } from "./http/app.js";
|
|
8
|
+
import { createMcpServer } from "./mcp/mcp-server-factory.js";
|
|
9
|
+
import { createSessionManager } from "./mcp/session-manager.js";
|
|
10
|
+
import { createLogger } from "./observability/logger.js";
|
|
11
|
+
import { createMetrics } from "./observability/metrics.js";
|
|
12
|
+
import { createCommandQueue } from "./queue/command-queue.js";
|
|
13
|
+
import { createCommandThrottle } from "./queue/command-throttle.js";
|
|
14
|
+
import { createInstrumentedCommandQueue } from "./queue/instrumented-command-queue.js";
|
|
15
|
+
import { SERVER_NAME, SERVER_VERSION } from "./server-info.js";
|
|
16
|
+
import { createStructureFileStore } from "./structures/structure-file-store.js";
|
|
17
|
+
/** Default per-kind command admission rate, protecting the script watchdog. */
|
|
18
|
+
const DEFAULT_THROTTLE = { ratePerSecond: 20, burst: 40 };
|
|
19
|
+
/** Events retained per subscription before the oldest are dropped. */
|
|
20
|
+
const EVENT_BUFFER_SIZE = 512;
|
|
21
|
+
/** Writes a message to stderr and exits non-zero. */
|
|
22
|
+
function fail(message) {
|
|
23
|
+
process.stderr.write(`${message}\n`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
function describeError(error) {
|
|
27
|
+
if (error instanceof ConfigError)
|
|
28
|
+
return error.message;
|
|
29
|
+
return error instanceof Error ? error.message : String(error);
|
|
30
|
+
}
|
|
31
|
+
function loadConfig() {
|
|
32
|
+
try {
|
|
33
|
+
return loadEnvironment();
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return fail(describeError(error));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function loadTls(environment) {
|
|
40
|
+
try {
|
|
41
|
+
return loadTlsMaterial(environment);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
return fail(describeError(error));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function main() {
|
|
48
|
+
const environment = loadConfig();
|
|
49
|
+
const logger = createLogger({
|
|
50
|
+
level: environment.BRIDGE_LOG_LEVEL,
|
|
51
|
+
pretty: process.stdout.isTTY === true,
|
|
52
|
+
});
|
|
53
|
+
const tls = loadTls(environment);
|
|
54
|
+
if (tls === null) {
|
|
55
|
+
logger.warn("TLS is not configured — the bridge is serving plain HTTP. Bearer tokens and " +
|
|
56
|
+
"world data will cross the network unencrypted. Set BRIDGE_TLS_CERT and " +
|
|
57
|
+
"BRIDGE_TLS_KEY (see the README for the mkcert workflow), or terminate TLS at a " +
|
|
58
|
+
"reverse proxy and bind the server to localhost.");
|
|
59
|
+
}
|
|
60
|
+
const baseQueue = createCommandQueue({
|
|
61
|
+
throttle: createCommandThrottle({ defaultPolicy: DEFAULT_THROTTLE }),
|
|
62
|
+
maxOutstanding: environment.BRIDGE_QUEUE_MAX,
|
|
63
|
+
livenessWindowMs: environment.BRIDGE_POLL_TIMEOUT_MS * 2,
|
|
64
|
+
});
|
|
65
|
+
const subscriptions = createSubscriptionRegistry({ bufferSize: EVENT_BUFFER_SIZE });
|
|
66
|
+
const structureFiles = createStructureFileStore(join(environment.BRIDGE_BEHAVIOR_PACK_PATH, "structures"));
|
|
67
|
+
// When metrics are enabled, the queue is wrapped to record command metrics
|
|
68
|
+
// and the gauges sample the base queue and session manager at scrape time.
|
|
69
|
+
let metrics;
|
|
70
|
+
let queue = baseQueue;
|
|
71
|
+
if (environment.BRIDGE_METRICS_ENABLED) {
|
|
72
|
+
metrics = createMetrics({
|
|
73
|
+
queueDepth: () => baseQueue.stats().depth,
|
|
74
|
+
commandsInFlight: () => baseQueue.stats().inFlight,
|
|
75
|
+
bridgeConnected: () => baseQueue.stats().bridgeConnected,
|
|
76
|
+
mcpSessions: () => sessionManager.count(),
|
|
77
|
+
});
|
|
78
|
+
queue = createInstrumentedCommandQueue(baseQueue, metrics);
|
|
79
|
+
}
|
|
80
|
+
const sessionManager = createSessionManager({
|
|
81
|
+
logger,
|
|
82
|
+
createServer: () => createMcpServer({
|
|
83
|
+
queue,
|
|
84
|
+
subscriptions,
|
|
85
|
+
structureFiles,
|
|
86
|
+
logger,
|
|
87
|
+
commandTimeoutMs: environment.BRIDGE_COMMAND_TIMEOUT_MS,
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
const app = createApp({
|
|
91
|
+
environment,
|
|
92
|
+
logger,
|
|
93
|
+
queue,
|
|
94
|
+
subscriptions,
|
|
95
|
+
sessionManager,
|
|
96
|
+
metrics,
|
|
97
|
+
tls,
|
|
98
|
+
});
|
|
99
|
+
let shuttingDown = false;
|
|
100
|
+
async function shutdown(signal) {
|
|
101
|
+
if (shuttingDown)
|
|
102
|
+
return;
|
|
103
|
+
shuttingDown = true;
|
|
104
|
+
logger.info({ signal }, "shutting down");
|
|
105
|
+
queue.close();
|
|
106
|
+
await sessionManager.closeAll();
|
|
107
|
+
await app.close();
|
|
108
|
+
process.exit(0);
|
|
109
|
+
}
|
|
110
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
111
|
+
process.once(signal, (received) => {
|
|
112
|
+
void shutdown(received);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
await app.listen({ host: environment.BRIDGE_HOST, port: environment.BRIDGE_PORT });
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
logger.error({ err: error }, "failed to start the bridge server");
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
const scheme = tls === null ? "http" : "https";
|
|
123
|
+
logger.info({
|
|
124
|
+
name: SERVER_NAME,
|
|
125
|
+
version: SERVER_VERSION,
|
|
126
|
+
address: `${scheme}://${environment.BRIDGE_HOST}:${environment.BRIDGE_PORT}`,
|
|
127
|
+
metrics: environment.BRIDGE_METRICS_ENABLED,
|
|
128
|
+
}, "bridge server listening");
|
|
129
|
+
}
|
|
130
|
+
void main().catch((error) => {
|
|
131
|
+
process.stderr.write(`fatal: ${describeError(error)}\n`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
});
|
|
134
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FastifyInstance } from "fastify";
|
|
2
|
+
import type { SessionManager } from "./session-manager.js";
|
|
3
|
+
/**
|
|
4
|
+
* Registers the MCP Streamable HTTP endpoint (mounted at `/mcp`).
|
|
5
|
+
*
|
|
6
|
+
* `POST` carries JSON-RPC messages and opens a session on `initialize`; `GET`
|
|
7
|
+
* opens the server-to-client SSE stream; `DELETE` terminates a session. The
|
|
8
|
+
* transport writes responses directly to the raw socket, so each handler
|
|
9
|
+
* hijacks the Fastify reply.
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerMcpRoute(app: FastifyInstance, sessions: SessionManager): void;
|
|
12
|
+
//# sourceMappingURL=mcp-route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-route.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-route.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAY3D;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAmCrF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
function headerValue(value) {
|
|
3
|
+
return typeof value === "string" ? value : undefined;
|
|
4
|
+
}
|
|
5
|
+
function sendMissingSession(reply) {
|
|
6
|
+
return reply
|
|
7
|
+
.code(400)
|
|
8
|
+
.send({ error: { code: "INVALID_INPUT", message: "missing or unknown MCP session" } });
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Registers the MCP Streamable HTTP endpoint (mounted at `/mcp`).
|
|
12
|
+
*
|
|
13
|
+
* `POST` carries JSON-RPC messages and opens a session on `initialize`; `GET`
|
|
14
|
+
* opens the server-to-client SSE stream; `DELETE` terminates a session. The
|
|
15
|
+
* transport writes responses directly to the raw socket, so each handler
|
|
16
|
+
* hijacks the Fastify reply.
|
|
17
|
+
*/
|
|
18
|
+
export function registerMcpRoute(app, sessions) {
|
|
19
|
+
app.post("/", async (request, reply) => {
|
|
20
|
+
const sessionId = headerValue(request.headers["mcp-session-id"]);
|
|
21
|
+
const existing = sessionId === undefined ? undefined : sessions.get(sessionId);
|
|
22
|
+
let transport;
|
|
23
|
+
if (existing !== undefined) {
|
|
24
|
+
transport = existing;
|
|
25
|
+
}
|
|
26
|
+
else if (sessionId === undefined && isInitializeRequest(request.body)) {
|
|
27
|
+
transport = await sessions.create();
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return sendMissingSession(reply);
|
|
31
|
+
}
|
|
32
|
+
reply.hijack();
|
|
33
|
+
await transport.handleRequest(request.raw, reply.raw, request.body);
|
|
34
|
+
return reply;
|
|
35
|
+
});
|
|
36
|
+
const streamHandler = async (request, reply) => {
|
|
37
|
+
const sessionId = headerValue(request.headers["mcp-session-id"]);
|
|
38
|
+
const transport = sessionId === undefined ? undefined : sessions.get(sessionId);
|
|
39
|
+
if (transport === undefined) {
|
|
40
|
+
return sendMissingSession(reply);
|
|
41
|
+
}
|
|
42
|
+
reply.hijack();
|
|
43
|
+
await transport.handleRequest(request.raw, reply.raw);
|
|
44
|
+
return reply;
|
|
45
|
+
};
|
|
46
|
+
app.get("/", streamHandler);
|
|
47
|
+
app.delete("/", streamHandler);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=mcp-route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-route.js","sourceRoot":"","sources":["../../src/mcp/mcp-route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAKzE,SAAS,WAAW,CAAC,KAAoC;IACvD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAmB;IAC7C,OAAO,KAAK;SACT,IAAI,CAAC,GAAG,CAAC;SACT,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,gCAAgC,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAoB,EAAE,QAAwB;IAC7E,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,OAAuB,EAAE,KAAmB,EAAE,EAAE;QACnE,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;QACjE,MAAM,QAAQ,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAE/E,IAAI,SAAwC,CAAC;QAC7C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,SAAS,GAAG,QAAQ,CAAC;QACvB,CAAC;aAAM,IAAI,SAAS,KAAK,SAAS,IAAI,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxE,SAAS,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,MAAM,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACpE,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,KAAK,EACzB,OAAuB,EACvB,KAAmB,EACI,EAAE;QACzB,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;QACjE,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAChF,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QACD,KAAK,CAAC,MAAM,EAAE,CAAC;QACf,MAAM,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC5B,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { SubscriptionRegistry } from "../events/subscription-registry.js";
|
|
3
|
+
import type { Logger } from "../observability/logger.js";
|
|
4
|
+
import type { CommandQueue } from "../queue/command-queue.js";
|
|
5
|
+
import type { StructureFileStore } from "../structures/structure-file-store.js";
|
|
6
|
+
/** Domain services shared by every per-session MCP server. */
|
|
7
|
+
export interface McpServerDependencies {
|
|
8
|
+
readonly queue: CommandQueue;
|
|
9
|
+
readonly subscriptions: SubscriptionRegistry;
|
|
10
|
+
readonly structureFiles: StructureFileStore;
|
|
11
|
+
readonly logger: Logger;
|
|
12
|
+
readonly commandTimeoutMs: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Creates an MCP server for a single client session, with the full tool
|
|
16
|
+
* surface registered. Tool handlers close over the shared domain services; the
|
|
17
|
+
* per-session `McpServer` instances are otherwise cheap and independent.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createMcpServer(deps: McpServerDependencies): McpServer;
|
|
20
|
+
//# sourceMappingURL=mcp-server-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server-factory.d.ts","sourceRoot":"","sources":["../../src/mcp/mcp-server-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAIhF,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,oBAAoB,CAAC;IAC7C,QAAQ,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,qBAAqB,GAAG,SAAS,CAYtE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { SERVER_NAME, SERVER_VERSION } from "../server-info.js";
|
|
3
|
+
import { allTools } from "../tools/index.js";
|
|
4
|
+
import { registerTools } from "./tool-registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Creates an MCP server for a single client session, with the full tool
|
|
7
|
+
* surface registered. Tool handlers close over the shared domain services; the
|
|
8
|
+
* per-session `McpServer` instances are otherwise cheap and independent.
|
|
9
|
+
*/
|
|
10
|
+
export function createMcpServer(deps) {
|
|
11
|
+
const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
|
|
12
|
+
registerTools(server, allTools, (extra) => ({
|
|
13
|
+
queue: deps.queue,
|
|
14
|
+
subscriptions: deps.subscriptions,
|
|
15
|
+
structureFiles: deps.structureFiles,
|
|
16
|
+
logger: deps.logger,
|
|
17
|
+
commandTimeoutMs: deps.commandTimeoutMs,
|
|
18
|
+
correlationId: String(extra.requestId),
|
|
19
|
+
signal: extra.signal,
|
|
20
|
+
}));
|
|
21
|
+
return server;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=mcp-server-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-server-factory.js","sourceRoot":"","sources":["../../src/mcp/mcp-server-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEhE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAWnD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAA2B;IACzD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;IAC7E,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1C,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,cAAc,EAAE,IAAI,CAAC,cAAc;QACnC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC;QACtC,MAAM,EAAE,KAAK,CAAC,MAAM;KACrB,CAAC,CAAC,CAAC;IACJ,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import type { Logger } from "../observability/logger.js";
|
|
4
|
+
/**
|
|
5
|
+
* Tracks live MCP client sessions, each pairing a `StreamableHTTPServerTransport`
|
|
6
|
+
* with its own `McpServer`.
|
|
7
|
+
*/
|
|
8
|
+
export interface SessionManager {
|
|
9
|
+
/** Returns the transport for an established session, if one exists. */
|
|
10
|
+
get(sessionId: string): StreamableHTTPServerTransport | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a transport for a new session and connects a fresh `McpServer` to
|
|
13
|
+
* it. The caller drives the `initialize` request through the returned
|
|
14
|
+
* transport; the session registers itself once the transport assigns an id.
|
|
15
|
+
*/
|
|
16
|
+
create(): Promise<StreamableHTTPServerTransport>;
|
|
17
|
+
/** Number of established sessions. */
|
|
18
|
+
count(): number;
|
|
19
|
+
/** Closes every session — used during graceful shutdown. */
|
|
20
|
+
closeAll(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** Configuration for {@link createSessionManager}. */
|
|
23
|
+
export interface SessionManagerOptions {
|
|
24
|
+
readonly logger: Logger;
|
|
25
|
+
/** Factory for the per-session `McpServer` (with its tools registered). */
|
|
26
|
+
readonly createServer: () => McpServer;
|
|
27
|
+
}
|
|
28
|
+
/** Creates an empty {@link SessionManager}. */
|
|
29
|
+
export declare function createSessionManager(options: SessionManagerOptions): SessionManager;
|
|
30
|
+
//# sourceMappingURL=session-manager.d.ts.map
|