@contractspec/bundle.library 2.4.0 → 2.6.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.
@@ -3,9 +3,9 @@ $ bun run prebuild && bun run build:bundle && bun run build:types
3
3
  $ contractspec-bun-build prebuild
4
4
  $ contractspec-bun-build transpile
5
5
  [contractspec-bun-build] transpile target=bun root=src entries=281
6
- Bundled 281 modules in 135ms
6
+ Bundled 281 modules in 125ms
7
7
 
8
- application/index.js 21.29 KB (entry point)
8
+ application/index.js 23.85 KB (entry point)
9
9
  presentation/features/templates/types.js 8 bytes (entry point)
10
10
  presentation/features/organisms/index.js 49.89 KB (entry point)
11
11
  presentation/features/molecules/index.js 18.78 KB (entry point)
@@ -276,21 +276,21 @@ Bundled 281 modules in 135ms
276
276
  components/docs/advanced/AdvancedWorkflowMonitoringPage.js 4.41 KB (entry point)
277
277
  components/docs/architecture/ArchitectureAppConfigPage.js 14.0 KB (entry point)
278
278
  components/docs/architecture/ArchitectureIntegrationBindingPage.js 14.57 KB (entry point)
279
- application/mcp/index.js 21.29 KB (entry point)
280
- application/mcp/cliMcp.js 9.92 KB (entry point)
281
- application/mcp/docsMcp.js 7.57 KB (entry point)
279
+ application/mcp/index.js 23.85 KB (entry point)
280
+ application/mcp/cliMcp.js 12.48 KB (entry point)
281
+ application/mcp/docsMcp.js 10.13 KB (entry point)
282
282
  features/docs/index.js 443 bytes (entry point)
283
283
  features/docs/docs.contracts.js 443 bytes (entry point)
284
- application/mcp/internalMcp.js 8.60 KB (entry point)
284
+ application/mcp/internalMcp.js 11.17 KB (entry point)
285
285
  infrastructure/elysia/logger.js 0.78 KB (entry point)
286
- application/mcp/common.js 1.93 KB (entry point)
286
+ application/mcp/common.js 4.49 KB (entry point)
287
287
  components/docs/DocsIndexPage.js 12.24 KB (entry point)
288
288
  components/docs/advanced/AdvancedMCPPage.js 15.27 KB (entry point)
289
289
 
290
290
  [contractspec-bun-build] transpile target=node root=src entries=281
291
- Bundled 281 modules in 116ms
291
+ Bundled 281 modules in 94ms
292
292
 
293
- application/index.js 21.29 KB (entry point)
293
+ application/index.js 23.85 KB (entry point)
294
294
  presentation/features/templates/types.js 0 KB (entry point)
295
295
  presentation/features/organisms/index.js 49.89 KB (entry point)
296
296
  presentation/features/molecules/index.js 18.77 KB (entry point)
@@ -561,14 +561,14 @@ Bundled 281 modules in 116ms
561
561
  components/docs/advanced/AdvancedWorkflowMonitoringPage.js 4.40 KB (entry point)
562
562
  components/docs/architecture/ArchitectureAppConfigPage.js 14.0 KB (entry point)
563
563
  components/docs/architecture/ArchitectureIntegrationBindingPage.js 14.56 KB (entry point)
564
- application/mcp/index.js 21.29 KB (entry point)
565
- application/mcp/cliMcp.js 9.92 KB (entry point)
566
- application/mcp/docsMcp.js 7.56 KB (entry point)
564
+ application/mcp/index.js 23.85 KB (entry point)
565
+ application/mcp/cliMcp.js 12.49 KB (entry point)
566
+ application/mcp/docsMcp.js 10.13 KB (entry point)
567
567
  features/docs/index.js 435 bytes (entry point)
568
568
  features/docs/docs.contracts.js 435 bytes (entry point)
569
- application/mcp/internalMcp.js 8.60 KB (entry point)
569
+ application/mcp/internalMcp.js 11.16 KB (entry point)
570
570
  infrastructure/elysia/logger.js 0.77 KB (entry point)
571
- application/mcp/common.js 1.92 KB (entry point)
571
+ application/mcp/common.js 4.48 KB (entry point)
572
572
  components/docs/DocsIndexPage.js 12.23 KB (entry point)
573
573
  components/docs/advanced/AdvancedMCPPage.js 15.26 KB (entry point)
574
574
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # @contractspec/bundle.library
2
2
 
3
+ ## 2.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - bae3db1: fix: build issues
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [bae3db1]
12
+ - @contractspec/lib.contracts-integrations@2.6.0
13
+ - @contractspec/lib.contracts-library@2.6.0
14
+ - @contractspec/lib.contracts-runtime-server-graphql@2.6.0
15
+ - @contractspec/lib.contracts-runtime-server-mcp@2.6.0
16
+ - @contractspec/lib.contracts-runtime-server-rest@2.6.0
17
+ - @contractspec/lib.contracts-spec@2.6.0
18
+ - @contractspec/lib.design-system@2.6.0
19
+ - @contractspec/lib.example-shared-ui@2.6.0
20
+ - @contractspec/lib.logger@2.6.0
21
+ - @contractspec/lib.runtime-sandbox@1.6.0
22
+ - @contractspec/lib.schema@2.6.0
23
+ - @contractspec/lib.ui-kit-web@2.6.0
24
+ - @contractspec/lib.ui-link@2.6.0
25
+ - @contractspec/module.examples@2.6.0
26
+
27
+ ## 2.5.0
28
+
29
+ ### Minor Changes
30
+
31
+ - c83c323: feat: major change to content generation
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies [4fa3bd4]
36
+ - Updated dependencies [63eee9b]
37
+ - Updated dependencies [284cbe2]
38
+ - Updated dependencies [c83c323]
39
+ - @contractspec/lib.contracts-spec@2.5.0
40
+ - @contractspec/lib.contracts-integrations@2.5.0
41
+ - @contractspec/lib.contracts-runtime-server-graphql@2.5.0
42
+ - @contractspec/lib.contracts-runtime-server-rest@2.5.0
43
+ - @contractspec/lib.contracts-runtime-server-mcp@2.5.0
44
+ - @contractspec/lib.contracts-library@2.5.0
45
+ - @contractspec/lib.example-shared-ui@2.5.0
46
+ - @contractspec/lib.runtime-sandbox@1.5.0
47
+ - @contractspec/lib.design-system@2.5.0
48
+ - @contractspec/module.examples@2.5.0
49
+ - @contractspec/lib.ui-kit-web@2.5.0
50
+ - @contractspec/lib.ui-link@2.5.0
51
+ - @contractspec/lib.logger@2.5.0
52
+ - @contractspec/lib.schema@2.5.0
53
+
3
54
  ## 2.4.0
4
55
 
5
56
  ### Minor Changes
@@ -2,72 +2,161 @@
2
2
  // src/application/mcp/common.ts
3
3
  import { PresentationRegistry } from "@contractspec/lib.contracts-spec/presentations";
4
4
  import { createMcpServer } from "@contractspec/lib.contracts-runtime-server-mcp/provider-mcp";
5
- import { mcp } from "elysia-mcp";
6
- function createConsoleLikeLogger(logger) {
7
- const isDebug = process.env.CONTRACTSPEC_MCP_DEBUG === "1";
8
- const toMessage = (args) => args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
9
- return {
10
- log: (...args) => {
11
- if (!isDebug)
12
- return;
13
- logger.info(toMessage(args));
14
- },
15
- info: (...args) => {
16
- if (!isDebug)
17
- return;
18
- logger.info(toMessage(args));
19
- },
20
- warn: (...args) => {
21
- logger.warn(toMessage(args));
22
- },
23
- error: (...args) => {
24
- logger.error(toMessage(args));
25
- },
26
- debug: (...args) => {
27
- if (!isDebug)
28
- return;
29
- logger.debug(toMessage(args));
30
- }
31
- };
32
- }
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
7
+ import { Elysia } from "elysia";
8
+ import { randomUUID } from "crypto";
33
9
  var baseCtx = {
34
10
  actor: "anonymous",
35
11
  decide: async () => ({ effect: "allow" })
36
12
  };
37
- function createMcpElysiaHandler({
13
+ function createJsonRpcErrorResponse(status, code, message, data) {
14
+ return new Response(JSON.stringify({
15
+ jsonrpc: "2.0",
16
+ error: {
17
+ code,
18
+ message,
19
+ ...data ? { data } : {}
20
+ },
21
+ id: null
22
+ }), {
23
+ status,
24
+ headers: {
25
+ "content-type": "application/json"
26
+ }
27
+ });
28
+ }
29
+ function createSessionState({
38
30
  logger,
39
- path,
40
31
  serverName,
41
32
  ops,
42
33
  resources,
43
34
  prompts,
44
- presentations
35
+ presentations,
36
+ stateful
45
37
  }) {
46
- logger.info("Setting up MCP handler...");
47
- return mcp({
48
- basePath: path,
49
- logger: createConsoleLikeLogger(logger),
50
- serverInfo: {
51
- name: serverName,
52
- version: "1.0.0"
53
- },
54
- stateless: process.env.CONTRACTSPEC_MCP_STATEFUL !== "1",
55
- enableJsonResponse: true,
38
+ const server = new McpServer({
39
+ name: serverName,
40
+ version: "1.0.0"
41
+ }, {
56
42
  capabilities: {
57
43
  tools: {},
58
44
  resources: {},
59
45
  prompts: {},
60
46
  logging: {}
61
- },
62
- setupServer: (server) => {
63
- logger.info("Setting up MCP server...");
64
- createMcpServer(server, ops, resources, prompts, {
47
+ }
48
+ });
49
+ logger.info("Setting up MCP server...");
50
+ createMcpServer(server, ops, resources, prompts, {
51
+ logger,
52
+ toolCtx: () => baseCtx,
53
+ promptCtx: () => ({ locale: "en" }),
54
+ resourceCtx: () => ({ locale: "en" }),
55
+ presentations: new PresentationRegistry(presentations)
56
+ });
57
+ const transport = new WebStandardStreamableHTTPServerTransport({
58
+ sessionIdGenerator: stateful ? () => randomUUID() : undefined,
59
+ enableJsonResponse: true
60
+ });
61
+ return server.connect(transport).then(() => ({ server, transport }));
62
+ }
63
+ async function closeSessionState(state) {
64
+ await Promise.allSettled([state.transport.close(), state.server.close()]);
65
+ }
66
+ function toErrorMessage(error) {
67
+ return error instanceof Error ? error.stack ?? error.message : String(error);
68
+ }
69
+ function createMcpElysiaHandler({
70
+ logger,
71
+ path,
72
+ serverName,
73
+ ops,
74
+ resources,
75
+ prompts,
76
+ presentations
77
+ }) {
78
+ logger.info("Setting up MCP handler...");
79
+ const isStateful = process.env.CONTRACTSPEC_MCP_STATEFUL === "1";
80
+ const sessions = new Map;
81
+ async function handleStateless(request) {
82
+ const state = await createSessionState({
83
+ logger,
84
+ path,
85
+ serverName,
86
+ ops,
87
+ resources,
88
+ prompts,
89
+ presentations,
90
+ stateful: false
91
+ });
92
+ try {
93
+ return await state.transport.handleRequest(request);
94
+ } finally {
95
+ await closeSessionState(state);
96
+ }
97
+ }
98
+ async function closeSession(sessionId) {
99
+ const state = sessions.get(sessionId);
100
+ if (!state)
101
+ return;
102
+ sessions.delete(sessionId);
103
+ await closeSessionState(state);
104
+ }
105
+ async function handleStateful(request) {
106
+ const requestedSessionId = request.headers.get("mcp-session-id");
107
+ let state;
108
+ let createdState = false;
109
+ if (requestedSessionId) {
110
+ const existing = sessions.get(requestedSessionId);
111
+ if (!existing) {
112
+ return createJsonRpcErrorResponse(404, -32001, "Session not found");
113
+ }
114
+ state = existing;
115
+ } else {
116
+ state = await createSessionState({
65
117
  logger,
66
- toolCtx: () => baseCtx,
67
- promptCtx: () => ({ locale: "en" }),
68
- resourceCtx: () => ({ locale: "en" }),
69
- presentations: new PresentationRegistry(presentations)
118
+ path,
119
+ serverName,
120
+ ops,
121
+ resources,
122
+ prompts,
123
+ presentations,
124
+ stateful: true
125
+ });
126
+ createdState = true;
127
+ }
128
+ try {
129
+ const response = await state.transport.handleRequest(request);
130
+ const activeSessionId = state.transport.sessionId;
131
+ if (activeSessionId && !sessions.has(activeSessionId)) {
132
+ sessions.set(activeSessionId, state);
133
+ }
134
+ if (request.method === "DELETE" && activeSessionId) {
135
+ await closeSession(activeSessionId);
136
+ } else if (!activeSessionId && createdState) {
137
+ await closeSessionState(state);
138
+ }
139
+ return response;
140
+ } catch (error) {
141
+ if (createdState) {
142
+ await closeSessionState(state);
143
+ }
144
+ throw error;
145
+ }
146
+ }
147
+ return new Elysia({ name: `mcp-${serverName}` }).all(path, async ({ request }) => {
148
+ try {
149
+ if (isStateful) {
150
+ return await handleStateful(request);
151
+ }
152
+ return await handleStateless(request);
153
+ } catch (error) {
154
+ logger.error("Error handling MCP request", {
155
+ path,
156
+ method: request.method,
157
+ error: toErrorMessage(error)
70
158
  });
159
+ return createJsonRpcErrorResponse(500, -32000, "Internal error");
71
160
  }
72
161
  });
73
162
  }
@@ -1,8 +1,6 @@
1
1
  export declare function createCliMcpHandler(path?: string): import("elysia").default<"", {
2
2
  decorator: {};
3
- store: {
4
- authInfo: AuthInfo | undefined;
5
- };
3
+ store: {};
6
4
  derive: {};
7
5
  resolve: {};
8
6
  }, {
@@ -16,39 +14,6 @@ export declare function createCliMcpHandler(path?: string): import("elysia").def
16
14
  parser: {};
17
15
  response: {};
18
16
  }, {
19
- [x: string]: {
20
- "*": {
21
- [x: string]: {
22
- body: unknown;
23
- params: {
24
- "*": string;
25
- } & {};
26
- query: unknown;
27
- headers: unknown;
28
- response: {
29
- 200: {} | {
30
- jsonrpc: string;
31
- error: {
32
- code: import("@modelcontextprotocol/sdk/types.js").ErrorCode;
33
- message: string;
34
- data: string;
35
- };
36
- id: null;
37
- };
38
- 422: {
39
- type: "validation";
40
- on: string;
41
- summary?: string;
42
- message?: string;
43
- found?: unknown;
44
- property?: string;
45
- expected?: string;
46
- };
47
- };
48
- };
49
- };
50
- };
51
- } & {
52
17
  [x: string]: {
53
18
  [x: string]: {
54
19
  body: unknown;
@@ -56,15 +21,7 @@ export declare function createCliMcpHandler(path?: string): import("elysia").def
56
21
  query: unknown;
57
22
  headers: unknown;
58
23
  response: {
59
- 200: {} | {
60
- jsonrpc: string;
61
- error: {
62
- code: import("@modelcontextprotocol/sdk/types.js").ErrorCode;
63
- message: string;
64
- data: string;
65
- };
66
- id: null;
67
- };
24
+ 200: Response;
68
25
  };
69
26
  };
70
27
  };
@@ -79,15 +36,5 @@ export declare function createCliMcpHandler(path?: string): import("elysia").def
79
36
  resolve: {};
80
37
  schema: {};
81
38
  standaloneSchema: {};
82
- response: {
83
- 200: {} | {
84
- jsonrpc: string;
85
- error: {
86
- code: import("@modelcontextprotocol/sdk/types.js").ErrorCode;
87
- message: string;
88
- data: string;
89
- };
90
- id: null;
91
- };
92
- };
39
+ response: {};
93
40
  }>;
@@ -2,72 +2,161 @@
2
2
  // src/application/mcp/common.ts
3
3
  import { PresentationRegistry } from "@contractspec/lib.contracts-spec/presentations";
4
4
  import { createMcpServer } from "@contractspec/lib.contracts-runtime-server-mcp/provider-mcp";
5
- import { mcp } from "elysia-mcp";
6
- function createConsoleLikeLogger(logger) {
7
- const isDebug = process.env.CONTRACTSPEC_MCP_DEBUG === "1";
8
- const toMessage = (args) => args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
9
- return {
10
- log: (...args) => {
11
- if (!isDebug)
12
- return;
13
- logger.info(toMessage(args));
14
- },
15
- info: (...args) => {
16
- if (!isDebug)
17
- return;
18
- logger.info(toMessage(args));
19
- },
20
- warn: (...args) => {
21
- logger.warn(toMessage(args));
22
- },
23
- error: (...args) => {
24
- logger.error(toMessage(args));
25
- },
26
- debug: (...args) => {
27
- if (!isDebug)
28
- return;
29
- logger.debug(toMessage(args));
30
- }
31
- };
32
- }
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
7
+ import { Elysia } from "elysia";
8
+ import { randomUUID } from "crypto";
33
9
  var baseCtx = {
34
10
  actor: "anonymous",
35
11
  decide: async () => ({ effect: "allow" })
36
12
  };
37
- function createMcpElysiaHandler({
13
+ function createJsonRpcErrorResponse(status, code, message, data) {
14
+ return new Response(JSON.stringify({
15
+ jsonrpc: "2.0",
16
+ error: {
17
+ code,
18
+ message,
19
+ ...data ? { data } : {}
20
+ },
21
+ id: null
22
+ }), {
23
+ status,
24
+ headers: {
25
+ "content-type": "application/json"
26
+ }
27
+ });
28
+ }
29
+ function createSessionState({
38
30
  logger,
39
- path,
40
31
  serverName,
41
32
  ops,
42
33
  resources,
43
34
  prompts,
44
- presentations
35
+ presentations,
36
+ stateful
45
37
  }) {
46
- logger.info("Setting up MCP handler...");
47
- return mcp({
48
- basePath: path,
49
- logger: createConsoleLikeLogger(logger),
50
- serverInfo: {
51
- name: serverName,
52
- version: "1.0.0"
53
- },
54
- stateless: process.env.CONTRACTSPEC_MCP_STATEFUL !== "1",
55
- enableJsonResponse: true,
38
+ const server = new McpServer({
39
+ name: serverName,
40
+ version: "1.0.0"
41
+ }, {
56
42
  capabilities: {
57
43
  tools: {},
58
44
  resources: {},
59
45
  prompts: {},
60
46
  logging: {}
61
- },
62
- setupServer: (server) => {
63
- logger.info("Setting up MCP server...");
64
- createMcpServer(server, ops, resources, prompts, {
47
+ }
48
+ });
49
+ logger.info("Setting up MCP server...");
50
+ createMcpServer(server, ops, resources, prompts, {
51
+ logger,
52
+ toolCtx: () => baseCtx,
53
+ promptCtx: () => ({ locale: "en" }),
54
+ resourceCtx: () => ({ locale: "en" }),
55
+ presentations: new PresentationRegistry(presentations)
56
+ });
57
+ const transport = new WebStandardStreamableHTTPServerTransport({
58
+ sessionIdGenerator: stateful ? () => randomUUID() : undefined,
59
+ enableJsonResponse: true
60
+ });
61
+ return server.connect(transport).then(() => ({ server, transport }));
62
+ }
63
+ async function closeSessionState(state) {
64
+ await Promise.allSettled([state.transport.close(), state.server.close()]);
65
+ }
66
+ function toErrorMessage(error) {
67
+ return error instanceof Error ? error.stack ?? error.message : String(error);
68
+ }
69
+ function createMcpElysiaHandler({
70
+ logger,
71
+ path,
72
+ serverName,
73
+ ops,
74
+ resources,
75
+ prompts,
76
+ presentations
77
+ }) {
78
+ logger.info("Setting up MCP handler...");
79
+ const isStateful = process.env.CONTRACTSPEC_MCP_STATEFUL === "1";
80
+ const sessions = new Map;
81
+ async function handleStateless(request) {
82
+ const state = await createSessionState({
83
+ logger,
84
+ path,
85
+ serverName,
86
+ ops,
87
+ resources,
88
+ prompts,
89
+ presentations,
90
+ stateful: false
91
+ });
92
+ try {
93
+ return await state.transport.handleRequest(request);
94
+ } finally {
95
+ await closeSessionState(state);
96
+ }
97
+ }
98
+ async function closeSession(sessionId) {
99
+ const state = sessions.get(sessionId);
100
+ if (!state)
101
+ return;
102
+ sessions.delete(sessionId);
103
+ await closeSessionState(state);
104
+ }
105
+ async function handleStateful(request) {
106
+ const requestedSessionId = request.headers.get("mcp-session-id");
107
+ let state;
108
+ let createdState = false;
109
+ if (requestedSessionId) {
110
+ const existing = sessions.get(requestedSessionId);
111
+ if (!existing) {
112
+ return createJsonRpcErrorResponse(404, -32001, "Session not found");
113
+ }
114
+ state = existing;
115
+ } else {
116
+ state = await createSessionState({
65
117
  logger,
66
- toolCtx: () => baseCtx,
67
- promptCtx: () => ({ locale: "en" }),
68
- resourceCtx: () => ({ locale: "en" }),
69
- presentations: new PresentationRegistry(presentations)
118
+ path,
119
+ serverName,
120
+ ops,
121
+ resources,
122
+ prompts,
123
+ presentations,
124
+ stateful: true
125
+ });
126
+ createdState = true;
127
+ }
128
+ try {
129
+ const response = await state.transport.handleRequest(request);
130
+ const activeSessionId = state.transport.sessionId;
131
+ if (activeSessionId && !sessions.has(activeSessionId)) {
132
+ sessions.set(activeSessionId, state);
133
+ }
134
+ if (request.method === "DELETE" && activeSessionId) {
135
+ await closeSession(activeSessionId);
136
+ } else if (!activeSessionId && createdState) {
137
+ await closeSessionState(state);
138
+ }
139
+ return response;
140
+ } catch (error) {
141
+ if (createdState) {
142
+ await closeSessionState(state);
143
+ }
144
+ throw error;
145
+ }
146
+ }
147
+ return new Elysia({ name: `mcp-${serverName}` }).all(path, async ({ request }) => {
148
+ try {
149
+ if (isStateful) {
150
+ return await handleStateful(request);
151
+ }
152
+ return await handleStateless(request);
153
+ } catch (error) {
154
+ logger.error("Error handling MCP request", {
155
+ path,
156
+ method: request.method,
157
+ error: toErrorMessage(error)
70
158
  });
159
+ return createJsonRpcErrorResponse(500, -32000, "Internal error");
71
160
  }
72
161
  });
73
162
  }