@exaudeus/workrail 1.16.1 → 1.17.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.
@@ -598,8 +598,8 @@
598
598
  "bytes": 67
599
599
  },
600
600
  "mcp-server.js": {
601
- "sha256": "7c431887c5601a5e49d17da6f8d687ef45d36d5c5d1e5b91696a6f9dcf90f014",
602
- "bytes": 476
601
+ "sha256": "bf97a81d9bc5bb40d10ec8a06c73b322320e0a0afdaa88f19730efe781ace7cf",
602
+ "bytes": 1135
603
603
  },
604
604
  "mcp/error-mapper.d.ts": {
605
605
  "sha256": "c6e9db65c565063726442b73a4a0cf1a1c07d54c778a85cf0122c4c04ea6a5a9",
@@ -842,12 +842,12 @@
842
842
  "bytes": 12115
843
843
  },
844
844
  "mcp/server.d.ts": {
845
- "sha256": "bec49b8d07e189a53db7100d2f0e1e84ffe03150f04e1b06908ee3282982b4a2",
846
- "bytes": 168
845
+ "sha256": "782a9a50797cac9c5f30e79556f809351d7eb176a16d7c603c09a5133cc25303",
846
+ "bytes": 882
847
847
  },
848
848
  "mcp/server.js": {
849
- "sha256": "4ca61965fdf522e5cc839e26220491638f03053a12f23c4eaf000b54b50ab7ad",
850
- "bytes": 12934
849
+ "sha256": "5086b482d2eb3c42caac637282a5361b4db6e5d14aaeeb7c5ffdf75d211af0d8",
850
+ "bytes": 13422
851
851
  },
852
852
  "mcp/tool-description-provider.d.ts": {
853
853
  "sha256": "1d46abc3112e11b68e57197e846f5708293ec9b2281fa71a9124ee2aad71e41b",
@@ -881,6 +881,38 @@
881
881
  "sha256": "aa364f3e613fc61a39fc7336a723231da06852d92ec7840a95327aa370e42e1c",
882
882
  "bytes": 8360
883
883
  },
884
+ "mcp/transports/http-entry.d.ts": {
885
+ "sha256": "35d313b120dcf38643de9462559163581b89943fe432706986252e8b698b9507",
886
+ "bytes": 70
887
+ },
888
+ "mcp/transports/http-entry.js": {
889
+ "sha256": "3ecf3a39d4eb2d4b844e83296cf04238acfc7c91e9eca0e10c1e8a0fbae71721",
890
+ "bytes": 4279
891
+ },
892
+ "mcp/transports/http-listener.d.ts": {
893
+ "sha256": "5c512d31f001bd65eaaca5421ca8f80e614896fdfe803773aa1f6c893257f20e",
894
+ "bytes": 316
895
+ },
896
+ "mcp/transports/http-listener.js": {
897
+ "sha256": "9e055636c281b5fe09623aa017d2e773462ddbe65e42c0938ed897cac606bb62",
898
+ "bytes": 2555
899
+ },
900
+ "mcp/transports/stdio-entry.d.ts": {
901
+ "sha256": "4ced3c9e00ef67555781dea74315290eea8a9dbffd38155bc00c3fb07b0c1794",
902
+ "bytes": 59
903
+ },
904
+ "mcp/transports/stdio-entry.js": {
905
+ "sha256": "a1f75205084e632b78e1ec28fb82498f04ecdb66fa4a9d9678d885268b0ed885",
906
+ "bytes": 4509
907
+ },
908
+ "mcp/transports/transport-mode.d.ts": {
909
+ "sha256": "1c59128ab0174bd2a113fff17521e6339ca367f2b8980c2f2c164ec393c10518",
910
+ "bytes": 206
911
+ },
912
+ "mcp/transports/transport-mode.js": {
913
+ "sha256": "6627303319e87e047caf99d910626a331742089b90db180f6031aa9a1afc98eb",
914
+ "bytes": 747
915
+ },
884
916
  "mcp/types.d.ts": {
885
917
  "sha256": "2ef7f9c1bffbe365b06469dfad43b45c4ae3b5292dd269b33423bf1c8c9c7c43",
886
918
  "bytes": 4355
@@ -1,3 +1,21 @@
1
1
  import type { ToolContext } from './types.js';
2
+ import { WorkspaceRootsManager } from './workspace-roots-manager.js';
3
+ import { type ToolAnnotations } from './tool-factory.js';
4
+ import type { WrappedToolHandler } from './types/workflow-tool-edition.js';
5
+ interface Tool {
6
+ name: string;
7
+ description: string;
8
+ inputSchema: Record<string, unknown>;
9
+ annotations?: ToolAnnotations;
10
+ }
2
11
  export declare function createToolContext(): Promise<ToolContext>;
12
+ export interface ComposedServer {
13
+ readonly server: import('@modelcontextprotocol/sdk/server/index.js').Server;
14
+ readonly ctx: ToolContext;
15
+ readonly rootsManager: WorkspaceRootsManager;
16
+ readonly tools: readonly Tool[];
17
+ readonly handlers: Record<string, WrappedToolHandler>;
18
+ }
19
+ export declare function composeServer(): Promise<ComposedServer>;
3
20
  export declare function startServer(): Promise<void>;
21
+ export {};
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createToolContext = createToolContext;
37
+ exports.composeServer = composeServer;
37
38
  exports.startServer = startServer;
38
39
  const zod_to_json_schema_js_1 = require("./zod-to-json-schema.js");
39
40
  const container_js_1 = require("../di/container.js");
@@ -134,7 +135,7 @@ function toMcpTool(tool) {
134
135
  annotations: tool.annotations,
135
136
  };
136
137
  }
137
- async function startServer() {
138
+ async function composeServer() {
138
139
  await (0, container_js_1.bootstrap)({ runtimeMode: { kind: 'production' } });
139
140
  const ctx = await createToolContext();
140
141
  if (ctx.v2 && ctx.httpServer && ctx.v2.dataDir && ctx.v2.directoryListing) {
@@ -204,6 +205,12 @@ async function startServer() {
204
205
  : ctx;
205
206
  return handler(args ?? {}, requestCtx);
206
207
  });
208
+ return { server, ctx, rootsManager, tools, handlers };
209
+ }
210
+ async function startServer() {
211
+ const { server, ctx, rootsManager } = await composeServer();
212
+ const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
213
+ const { RootsListChangedNotificationSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
207
214
  server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
208
215
  try {
209
216
  const result = await server.listRoots();
@@ -0,0 +1 @@
1
+ export declare function startHttpServer(port: number): Promise<void>;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.startHttpServer = startHttpServer;
40
+ const server_js_1 = require("../server.js");
41
+ const http_listener_js_1 = require("./http-listener.js");
42
+ const container_js_1 = require("../../di/container.js");
43
+ const tokens_js_1 = require("../../di/tokens.js");
44
+ const crypto = __importStar(require("crypto"));
45
+ const express_1 = __importDefault(require("express"));
46
+ async function startHttpServer(port) {
47
+ const { server, ctx } = await (0, server_js_1.composeServer)();
48
+ const listener = (0, http_listener_js_1.createHttpListener)(port);
49
+ const { StreamableHTTPServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/streamableHttp.js')));
50
+ const transport = new StreamableHTTPServerTransport({
51
+ sessionIdGenerator: () => crypto.randomUUID(),
52
+ enableJsonResponse: true,
53
+ });
54
+ listener.app.use(express_1.default.json());
55
+ listener.app.post('/mcp', (req, res) => transport.handleRequest(req, res, req.body));
56
+ listener.app.get('/mcp', (req, res) => transport.handleRequest(req, res));
57
+ listener.app.delete('/mcp', (req, res) => transport.handleRequest(req, res));
58
+ await listener.start();
59
+ await server.connect(transport);
60
+ const boundPort = listener.getBoundPort();
61
+ console.error('[Transport] WorkRail MCP Server running on HTTP');
62
+ console.error(`[Transport] MCP endpoint: http://localhost:${boundPort}/mcp`);
63
+ const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
64
+ const processSignals = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessSignals);
65
+ const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
66
+ processSignals.on('SIGINT', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGINT' }));
67
+ processSignals.on('SIGTERM', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGTERM' }));
68
+ processSignals.on('SIGHUP', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' }));
69
+ let shutdownStarted = false;
70
+ shutdownEvents.onShutdown((event) => {
71
+ if (shutdownStarted)
72
+ return;
73
+ shutdownStarted = true;
74
+ void (async () => {
75
+ try {
76
+ console.error(`[Shutdown] Requested by ${event.signal}. Stopping services...`);
77
+ await listener.stop();
78
+ await ctx.httpServer?.stop();
79
+ terminator.terminate({ kind: 'success' });
80
+ }
81
+ catch (err) {
82
+ console.error('[Shutdown] Error while stopping services:', err);
83
+ terminator.terminate({ kind: 'failure' });
84
+ }
85
+ })();
86
+ });
87
+ }
@@ -0,0 +1,9 @@
1
+ import { type Application } from 'express';
2
+ export interface HttpListener {
3
+ readonly app: Application;
4
+ readonly requestedPort: number;
5
+ getBoundPort(): number | null;
6
+ start(): Promise<void>;
7
+ stop(): Promise<void>;
8
+ }
9
+ export declare function createHttpListener(requestedPort: number): HttpListener;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createHttpListener = createHttpListener;
7
+ const express_1 = __importDefault(require("express"));
8
+ const http_1 = require("http");
9
+ function createHttpListener(requestedPort) {
10
+ const app = (0, express_1.default)();
11
+ let state = { kind: 'not_started' };
12
+ return {
13
+ app,
14
+ requestedPort,
15
+ getBoundPort() {
16
+ return state.kind === 'running' ? state.boundPort : null;
17
+ },
18
+ async start() {
19
+ if (state.kind === 'running') {
20
+ throw new Error('[HttpListener] Already started');
21
+ }
22
+ if (state.kind === 'stopped') {
23
+ throw new Error('[HttpListener] Cannot restart a stopped listener');
24
+ }
25
+ return new Promise((resolve, reject) => {
26
+ const server = (0, http_1.createServer)(app);
27
+ server.on('error', (err) => {
28
+ if (err.code === 'EADDRINUSE') {
29
+ reject(new Error(`[HttpListener] Port ${requestedPort} is already in use. ` +
30
+ `Set WORKRAIL_HTTP_PORT to a different port or stop the conflicting process.`));
31
+ }
32
+ else {
33
+ reject(err);
34
+ }
35
+ });
36
+ server.listen(requestedPort, () => {
37
+ const addr = server.address();
38
+ const boundPort = addr && typeof addr === 'object' ? addr.port : requestedPort;
39
+ state = { kind: 'running', server, boundPort };
40
+ console.error(`[HttpListener] MCP HTTP transport listening on port ${boundPort}`);
41
+ resolve();
42
+ });
43
+ });
44
+ },
45
+ async stop() {
46
+ if (state.kind !== 'running') {
47
+ return;
48
+ }
49
+ const serverToClose = state.server;
50
+ return new Promise((resolve, reject) => {
51
+ serverToClose.close((err) => {
52
+ if (err) {
53
+ reject(err);
54
+ }
55
+ else {
56
+ state = { kind: 'stopped' };
57
+ console.error(`[HttpListener] MCP HTTP transport stopped`);
58
+ resolve();
59
+ }
60
+ });
61
+ });
62
+ },
63
+ };
64
+ }
@@ -0,0 +1 @@
1
+ export declare function startStdioServer(): Promise<void>;
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.startStdioServer = startStdioServer;
37
+ const server_js_1 = require("../server.js");
38
+ const container_js_1 = require("../../di/container.js");
39
+ const tokens_js_1 = require("../../di/tokens.js");
40
+ async function startStdioServer() {
41
+ const { server, ctx, rootsManager } = await (0, server_js_1.composeServer)();
42
+ const { StdioServerTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/server/stdio.js')));
43
+ const { RootsListChangedNotificationSchema, } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
44
+ server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
45
+ try {
46
+ const result = await server.listRoots();
47
+ rootsManager.updateRootUris(result.roots.map((r) => r.uri));
48
+ console.error(`[Roots] Updated workspace roots: ${result.roots.map((r) => r.uri).join(', ') || '(none)'}`);
49
+ }
50
+ catch {
51
+ console.error('[Roots] Failed to fetch updated roots after change notification');
52
+ }
53
+ });
54
+ const transport = new StdioServerTransport();
55
+ await server.connect(transport);
56
+ try {
57
+ const result = await server.listRoots();
58
+ rootsManager.updateRootUris(result.roots.map((r) => r.uri));
59
+ console.error(`[Roots] Initial workspace roots: ${result.roots.map((r) => r.uri).join(', ') || '(none)'}`);
60
+ }
61
+ catch {
62
+ console.error('[Roots] Client does not support roots/list; workspace context will use server CWD fallback');
63
+ }
64
+ console.error('[Transport] WorkRail MCP Server running on stdio');
65
+ const shutdownEvents = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ShutdownEvents);
66
+ const processSignals = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessSignals);
67
+ const terminator = container_js_1.container.resolve(tokens_js_1.DI.Runtime.ProcessTerminator);
68
+ processSignals.on('SIGINT', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGINT' }));
69
+ processSignals.on('SIGTERM', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGTERM' }));
70
+ processSignals.on('SIGHUP', () => shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' }));
71
+ process.stdin.on('end', () => {
72
+ console.error('[MCP] stdin closed, initiating shutdown');
73
+ shutdownEvents.emit({ kind: 'shutdown_requested', signal: 'SIGHUP' });
74
+ });
75
+ let shutdownStarted = false;
76
+ shutdownEvents.onShutdown((event) => {
77
+ if (shutdownStarted)
78
+ return;
79
+ shutdownStarted = true;
80
+ void (async () => {
81
+ try {
82
+ console.error(`[Shutdown] Requested by ${event.signal}. Stopping services...`);
83
+ await ctx.httpServer?.stop();
84
+ terminator.terminate({ kind: 'success' });
85
+ }
86
+ catch (err) {
87
+ console.error('[Shutdown] Error while stopping services:', err);
88
+ terminator.terminate({ kind: 'failure' });
89
+ }
90
+ })();
91
+ });
92
+ }
@@ -0,0 +1,7 @@
1
+ export type TransportMode = {
2
+ readonly kind: 'stdio';
3
+ } | {
4
+ readonly kind: 'http';
5
+ readonly port: number;
6
+ };
7
+ export declare function resolveTransportMode(env: NodeJS.ProcessEnv): TransportMode;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveTransportMode = resolveTransportMode;
4
+ function resolveTransportMode(env) {
5
+ const transport = env.WORKRAIL_TRANSPORT ?? 'stdio';
6
+ if (transport === 'http') {
7
+ const portStr = env.WORKRAIL_HTTP_PORT ?? '3100';
8
+ const port = parseInt(portStr, 10);
9
+ if (isNaN(port) || port <= 0 || port > 65535) {
10
+ throw new Error(`Invalid WORKRAIL_HTTP_PORT: "${portStr}". Must be a number between 1-65535.`);
11
+ }
12
+ return { kind: 'http', port };
13
+ }
14
+ if (transport !== 'stdio') {
15
+ throw new Error(`Invalid WORKRAIL_TRANSPORT: "${transport}". Must be 'stdio' or 'http'.`);
16
+ }
17
+ return { kind: 'stdio' };
18
+ }
@@ -2,10 +2,26 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.startServer = void 0;
5
+ const transport_mode_js_1 = require("./mcp/transports/transport-mode.js");
6
+ const stdio_entry_js_1 = require("./mcp/transports/stdio-entry.js");
7
+ const http_entry_js_1 = require("./mcp/transports/http-entry.js");
8
+ const assert_never_js_1 = require("./runtime/assert-never.js");
5
9
  var server_js_1 = require("./mcp/server.js");
6
10
  Object.defineProperty(exports, "startServer", { enumerable: true, get: function () { return server_js_1.startServer; } });
7
- const server_js_2 = require("./mcp/server.js");
8
- (0, server_js_2.startServer)().catch((error) => {
9
- console.error('Fatal error running server:', error);
10
- process.exit(1);
11
- });
11
+ const mode = (0, transport_mode_js_1.resolveTransportMode)(process.env);
12
+ switch (mode.kind) {
13
+ case 'stdio':
14
+ (0, stdio_entry_js_1.startStdioServer)().catch((error) => {
15
+ console.error('[stdio] Fatal error:', error);
16
+ process.exit(1);
17
+ });
18
+ break;
19
+ case 'http':
20
+ (0, http_entry_js_1.startHttpServer)(mode.port).catch((error) => {
21
+ console.error('[http] Fatal error:', error);
22
+ process.exit(1);
23
+ });
24
+ break;
25
+ default:
26
+ (0, assert_never_js_1.assertNever)(mode);
27
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exaudeus/workrail",
3
- "version": "1.16.1",
3
+ "version": "1.17.0",
4
4
  "description": "Step-by-step workflow enforcement for AI agents via MCP",
5
5
  "license": "MIT",
6
6
  "repository": {