@decocms/runtime 1.2.0 → 1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/runtime",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "@cloudflare/workers-types": "^4.20250617.0",
11
11
  "@decocms/bindings": "^1.0.7",
12
- "@modelcontextprotocol/sdk": "1.25.1",
12
+ "@modelcontextprotocol/sdk": "1.25.2",
13
13
  "@ai-sdk/provider": "^3.0.0",
14
14
  "hono": "^4.10.7",
15
15
  "jose": "^6.0.11",
@@ -1,5 +1,5 @@
1
1
  import { devServerProxy } from "./dev-server-proxy";
2
- import { resolve, dirname } from "path";
2
+ import { resolve, dirname, join } from "path";
3
3
 
4
4
  export interface AssetServerConfig {
5
5
  /**
@@ -209,9 +209,11 @@ export function createAssetHandler(config: AssetServerConfig = {}) {
209
209
  return null; // Path traversal attempt blocked
210
210
  }
211
211
 
212
+ // Try to serve the index.html file relative to the requested file
213
+ const indexRelativeToFilePath = join(filePath, "index.html");
212
214
  // Try to serve the requested file, fall back to index.html for SPA routing
213
215
  const indexPath = resolve(clientDir, "index.html");
214
- for (const pathToTry of [filePath, indexPath]) {
216
+ for (const pathToTry of [filePath, indexRelativeToFilePath, indexPath]) {
215
217
  try {
216
218
  const file = Bun.file(pathToTry);
217
219
  if (await file.exists()) {
package/src/tools.ts CHANGED
@@ -681,13 +681,28 @@ export const createMCPServer = <
681
681
  : z.object({}).shape,
682
682
  },
683
683
  async (args) => {
684
- let result = await tool.execute({
684
+ const result = await tool.execute({
685
685
  context: args,
686
686
  runtimeContext: createRuntimeContext(),
687
687
  });
688
688
 
689
+ // For streamable tools, the Response is handled at the transport layer
690
+ // Do NOT call result.bytes() - it buffers the entire response in memory
691
+ // causing massive memory leaks (2GB+ Uint8Array accumulation)
689
692
  if (isStreamableTool(tool) && result instanceof Response) {
690
- result = { bytes: await result.bytes() };
693
+ return {
694
+ structuredContent: {
695
+ streamable: true,
696
+ status: result.status,
697
+ statusText: result.statusText,
698
+ },
699
+ content: [
700
+ {
701
+ type: "text",
702
+ text: `Streaming response: ${result.status} ${result.statusText}`,
703
+ },
704
+ ],
705
+ };
691
706
  }
692
707
  return {
693
708
  structuredContent: result as Record<string, unknown>,
@@ -830,7 +845,18 @@ export const createMCPServer = <
830
845
 
831
846
  await server.connect(transport);
832
847
 
833
- return await transport.handleRequest(req);
848
+ try {
849
+ return await transport.handleRequest(req);
850
+ } finally {
851
+ // CRITICAL: Close transport to prevent memory leaks
852
+ // Without this, ReadableStream/WritableStream controllers accumulate
853
+ // causing thousands of stream objects to be retained in memory
854
+ try {
855
+ await transport.close?.();
856
+ } catch {
857
+ // Ignore close errors - transport may already be closed
858
+ }
859
+ }
834
860
  };
835
861
 
836
862
  const callTool: CallTool = async ({ toolCallId, toolCallInput }) => {