@aws/agentcore 1.0.0-preview.10 → 1.0.0-preview.12

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.
Files changed (65) hide show
  1. package/dist/agent-inspector/index.css +1 -1
  2. package/dist/agent-inspector/index.js +83 -78
  3. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +452 -146
  4. package/dist/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +2 -2
  5. package/dist/assets/cdk/bin/cdk.ts +4 -0
  6. package/dist/assets/cdk/lib/cdk-stack.ts +2 -0
  7. package/dist/assets/cdk/package.json +1 -1
  8. package/dist/assets/container/python/Dockerfile +1 -1
  9. package/dist/assets/python/a2a/googleadk/base/main.py +24 -16
  10. package/dist/assets/python/a2a/googleadk/base/pyproject.toml +1 -1
  11. package/dist/assets/python/a2a/langchain_langgraph/base/main.py +24 -16
  12. package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +1 -0
  13. package/dist/assets/python/a2a/strands/base/main.py +23 -17
  14. package/dist/assets/python/agui/googleadk/base/main.py +62 -2
  15. package/dist/assets/python/agui/googleadk/base/pyproject.toml +1 -1
  16. package/dist/assets/python/agui/langchain_langgraph/base/main.py +56 -0
  17. package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +1 -0
  18. package/dist/assets/python/agui/strands/base/main.py +63 -1
  19. package/dist/assets/python/http/autogen/base/main.py +27 -19
  20. package/dist/assets/python/http/googleadk/base/main.py +24 -16
  21. package/dist/assets/python/http/googleadk/base/pyproject.toml +1 -0
  22. package/dist/assets/python/http/langchain_langgraph/base/main.py +24 -16
  23. package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +1 -0
  24. package/dist/assets/python/http/openaiagents/base/main.py +24 -16
  25. package/dist/assets/python/http/strands/base/main.py +23 -17
  26. package/dist/assets/python/mcp/standalone/base/main.py +58 -0
  27. package/dist/cli/index.mjs +707 -634
  28. package/dist/lib/errors/types.d.ts +9 -0
  29. package/dist/lib/errors/types.d.ts.map +1 -1
  30. package/dist/lib/errors/types.js +19 -1
  31. package/dist/lib/errors/types.js.map +1 -1
  32. package/dist/lib/errors/zod.js +1 -1
  33. package/dist/lib/errors/zod.js.map +1 -1
  34. package/dist/lib/packaging/helpers.d.ts.map +1 -1
  35. package/dist/lib/packaging/helpers.js +6 -10
  36. package/dist/lib/packaging/helpers.js.map +1 -1
  37. package/dist/lib/result.d.ts +21 -4
  38. package/dist/lib/result.d.ts.map +1 -1
  39. package/dist/lib/result.js +19 -0
  40. package/dist/lib/result.js.map +1 -1
  41. package/dist/lib/schemas/io/global-config.d.ts +28 -21
  42. package/dist/lib/schemas/io/global-config.d.ts.map +1 -1
  43. package/dist/lib/schemas/io/global-config.js +57 -28
  44. package/dist/lib/schemas/io/global-config.js.map +1 -1
  45. package/dist/lib/utils/index.d.ts +1 -1
  46. package/dist/lib/utils/index.d.ts.map +1 -1
  47. package/dist/lib/utils/index.js +2 -1
  48. package/dist/lib/utils/index.js.map +1 -1
  49. package/dist/lib/utils/zod.d.ts +16 -0
  50. package/dist/lib/utils/zod.d.ts.map +1 -1
  51. package/dist/lib/utils/zod.js +45 -0
  52. package/dist/lib/utils/zod.js.map +1 -1
  53. package/dist/schema/schemas/agent-env.d.ts +50 -4
  54. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  55. package/dist/schema/schemas/agent-env.js +102 -4
  56. package/dist/schema/schemas/agent-env.js.map +1 -1
  57. package/dist/schema/schemas/agentcore-project.d.ts +12 -2
  58. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  59. package/dist/schema/schemas/primitives/harness.d.ts +8 -0
  60. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
  61. package/dist/schema/schemas/primitives/harness.js +23 -0
  62. package/dist/schema/schemas/primitives/harness.js.map +1 -1
  63. package/npm-shrinkwrap.json +281 -366
  64. package/package.json +12 -7
  65. package/scripts/bump-version.ts +1 -1
@@ -1,4 +1,6 @@
1
+ {{#if needsOs}}
1
2
  import os
3
+ {{/if}}
2
4
  from autogen_agentchat.agents import AssistantAgent
3
5
  from autogen_core.tools import FunctionTool
4
6
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
@@ -22,18 +24,21 @@ add_numbers_tool = FunctionTool(
22
24
  # Define a collection of tools used by the model
23
25
  tools = [add_numbers_tool]
24
26
 
25
- {{#if sessionStorageMountPath}}
26
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
27
+ {{#if needsOs}}
28
+ _MOUNT_PATHS = [
29
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
30
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
31
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
32
+ ]
27
33
 
28
34
  def _safe_resolve(path: str) -> str:
29
- """Resolve path safely within the storage boundary."""
30
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
31
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
32
- raise ValueError(f"Path '{path}' is outside the storage boundary")
35
+ resolved = os.path.realpath(path)
36
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
37
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
33
38
  return resolved
34
39
 
35
40
  def file_read(path: str) -> str:
36
- """Read a file from persistent storage. The path is relative to the storage root."""
41
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
37
42
  try:
38
43
  full_path = _safe_resolve(path)
39
44
  with open(full_path) as f:
@@ -44,7 +49,7 @@ def file_read(path: str) -> str:
44
49
  return f"Error reading '{path}': {e.strerror}"
45
50
 
46
51
  def file_write(path: str, content: str) -> str:
47
- """Write content to a file in persistent storage. The path is relative to the storage root."""
52
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
48
53
  try:
49
54
  full_path = _safe_resolve(path)
50
55
  parent = os.path.dirname(full_path)
@@ -58,29 +63,32 @@ def file_write(path: str, content: str) -> str:
58
63
  except OSError as e:
59
64
  return f"Error writing '{path}': {e.strerror}"
60
65
 
61
- def list_files(directory: str = "") -> str:
62
- """List files in persistent storage. The directory is relative to the storage root."""
66
+ def list_files(path: str) -> str:
67
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
63
68
  try:
64
- target = _safe_resolve(directory)
65
- entries = os.listdir(target)
69
+ full_path = _safe_resolve(path)
70
+ entries = os.listdir(full_path)
66
71
  return "\n".join(entries) if entries else "(empty directory)"
67
72
  except ValueError as e:
68
73
  return str(e)
69
74
  except OSError as e:
70
- return f"Error listing '{directory}': {e.strerror}"
75
+ return f"Error listing '{path}': {e.strerror}"
71
76
 
72
77
  tools.extend([
73
- FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
74
- FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
75
- FunctionTool(list_files, description="List files in persistent storage. The directory is relative to the storage root."),
78
+ FunctionTool(file_read, description="Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."),
79
+ FunctionTool(file_write, description="Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."),
80
+ FunctionTool(list_files, description="List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."),
76
81
  ])
77
82
  {{/if}}
78
83
 
79
84
  SYSTEM_MESSAGE = """
80
85
  You are a helpful assistant. Use tools when appropriate.
81
- {{#if sessionStorageMountPath}}
82
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
83
- {{/if}}
86
+ {{#if needsOs}}
87
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
88
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
89
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
90
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
91
+ {{/each}}{{/if}}
84
92
  """
85
93
 
86
94
  @app.entrypoint
@@ -1,4 +1,6 @@
1
+ {{#if needsOs}}
1
2
  import os
3
+ {{/if}}
2
4
  from google.adk.agents import Agent
3
5
  from google.adk.runners import Runner
4
6
  from google.adk.sessions import InMemorySessionService
@@ -29,18 +31,21 @@ def add_numbers(a: int, b: int) -> int:
29
31
  # Define a collection of tools used by the model
30
32
  tools = [add_numbers]
31
33
 
32
- {{#if sessionStorageMountPath}}
33
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
34
+ {{#if needsOs}}
35
+ _MOUNT_PATHS = [
36
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
37
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
38
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
39
+ ]
34
40
 
35
41
  def _safe_resolve(path: str) -> str:
36
- """Resolve path safely within the storage boundary."""
37
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
38
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
39
- raise ValueError(f"Path '{path}' is outside the storage boundary")
42
+ resolved = os.path.realpath(path)
43
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
44
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
40
45
  return resolved
41
46
 
42
47
  def file_read(path: str) -> str:
43
- """Read a file from persistent storage. The path is relative to the storage root."""
48
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
44
49
  try:
45
50
  full_path = _safe_resolve(path)
46
51
  with open(full_path) as f:
@@ -51,7 +56,7 @@ def file_read(path: str) -> str:
51
56
  return f"Error reading '{path}': {e.strerror}"
52
57
 
53
58
  def file_write(path: str, content: str) -> str:
54
- """Write content to a file in persistent storage. The path is relative to the storage root."""
59
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
55
60
  try:
56
61
  full_path = _safe_resolve(path)
57
62
  parent = os.path.dirname(full_path)
@@ -65,25 +70,28 @@ def file_write(path: str, content: str) -> str:
65
70
  except OSError as e:
66
71
  return f"Error writing '{path}': {e.strerror}"
67
72
 
68
- def list_files(directory: str = "") -> str:
69
- """List files in persistent storage. The directory is relative to the storage root."""
73
+ def list_files(path: str) -> str:
74
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
70
75
  try:
71
- target = _safe_resolve(directory)
72
- entries = os.listdir(target)
76
+ full_path = _safe_resolve(path)
77
+ entries = os.listdir(full_path)
73
78
  return "\n".join(entries) if entries else "(empty directory)"
74
79
  except ValueError as e:
75
80
  return str(e)
76
81
  except OSError as e:
77
- return f"Error listing '{directory}': {e.strerror}"
82
+ return f"Error listing '{path}': {e.strerror}"
78
83
 
79
84
  tools.extend([file_read, file_write, list_files])
80
85
  {{/if}}
81
86
 
82
87
  AGENT_INSTRUCTION = """
83
88
  I can answer your questions using the knowledge I have!
84
- {{#if sessionStorageMountPath}}
85
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
86
- {{/if}}
89
+ {{#if needsOs}}
90
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
91
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
92
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
93
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
94
+ {{/each}}{{/if}}
87
95
  """
88
96
 
89
97
  # Get MCP Toolset
@@ -12,6 +12,7 @@ dependencies = [
12
12
  "opentelemetry-distro",
13
13
  "opentelemetry-exporter-otlp",
14
14
  "google-adk >= 1.17.0, < 2.0.0",
15
+ "google-genai >= 1.0.0, < 2.0.0",
15
16
  "bedrock-agentcore >= 1.0.3",
16
17
  "botocore[crt] >= 1.35.0",
17
18
  {{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
@@ -1,4 +1,6 @@
1
+ {{#if needsOs}}
1
2
  import os
3
+ {{/if}}
2
4
  from typing import Any
3
5
 
4
6
  from langchain_core.messages import HumanMessage{{#if hasConfigBundle}}, SystemMessage{{/if}}
@@ -33,9 +35,12 @@ def get_or_create_model():
33
35
 
34
36
  DEFAULT_SYSTEM_PROMPT = """
35
37
  You are a helpful assistant. Use tools when appropriate.
36
- {{#if sessionStorageMountPath}}
37
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
38
- {{/if}}
38
+ {{#if needsOs}}
39
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
40
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
41
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
42
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
43
+ {{/each}}{{/if}}
39
44
  """
40
45
 
41
46
 
@@ -49,19 +54,22 @@ def add_numbers(a: int, b: int) -> int:
49
54
  # Define a collection of tools used by the model
50
55
  tools = [add_numbers]
51
56
 
52
- {{#if sessionStorageMountPath}}
53
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
57
+ {{#if needsOs}}
58
+ _MOUNT_PATHS = [
59
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
60
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
61
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
62
+ ]
54
63
 
55
64
  def _safe_resolve(path: str) -> str:
56
- """Resolve path safely within the storage boundary."""
57
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
58
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
59
- raise ValueError(f"Path '{path}' is outside the storage boundary")
65
+ resolved = os.path.realpath(path)
66
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
67
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
60
68
  return resolved
61
69
 
62
70
  @tool
63
71
  def file_read(path: str) -> str:
64
- """Read a file from persistent storage. The path is relative to the storage root."""
72
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
65
73
  try:
66
74
  full_path = _safe_resolve(path)
67
75
  with open(full_path) as f:
@@ -73,7 +81,7 @@ def file_read(path: str) -> str:
73
81
 
74
82
  @tool
75
83
  def file_write(path: str, content: str) -> str:
76
- """Write content to a file in persistent storage. The path is relative to the storage root."""
84
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
77
85
  try:
78
86
  full_path = _safe_resolve(path)
79
87
  parent = os.path.dirname(full_path)
@@ -88,16 +96,16 @@ def file_write(path: str, content: str) -> str:
88
96
  return f"Error writing '{path}': {e.strerror}"
89
97
 
90
98
  @tool
91
- def list_files(directory: str = "") -> str:
92
- """List files in persistent storage. The directory is relative to the storage root."""
99
+ def list_files(path: str) -> str:
100
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
93
101
  try:
94
- target = _safe_resolve(directory)
95
- entries = os.listdir(target)
102
+ full_path = _safe_resolve(path)
103
+ entries = os.listdir(full_path)
96
104
  return "\n".join(entries) if entries else "(empty directory)"
97
105
  except ValueError as e:
98
106
  return str(e)
99
107
  except OSError as e:
100
- return f"Error listing '{directory}': {e.strerror}"
108
+ return f"Error listing '{path}': {e.strerror}"
101
109
 
102
110
  tools.extend([file_read, file_write, list_files])
103
111
  {{/if}}
@@ -28,6 +28,7 @@ dependencies = [
28
28
  {{/if}}
29
29
  {{#if (eq modelProvider "Gemini")}}
30
30
  "langchain-google-genai >= 3.0.3",
31
+ "google-genai >= 1.0.0, < 2.0.0",
31
32
  {{/if}}
32
33
  {{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
33
34
  {{/if}}{{/if}}
@@ -1,4 +1,6 @@
1
+ {{#if needsOs}}
1
2
  import os
3
+ {{/if}}
2
4
  from agents import Agent, Runner, function_tool
3
5
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
4
6
  from model.load import load_model
@@ -37,19 +39,22 @@ def add_numbers(a: int, b: int) -> int:
37
39
 
38
40
  tools = [add_numbers]
39
41
 
40
- {{#if sessionStorageMountPath}}
41
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
42
+ {{#if needsOs}}
43
+ _MOUNT_PATHS = [
44
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
45
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
46
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
47
+ ]
42
48
 
43
49
  def _safe_resolve(path: str) -> str:
44
- """Resolve path safely within the storage boundary."""
45
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
46
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
47
- raise ValueError(f"Path '{path}' is outside the storage boundary")
50
+ resolved = os.path.realpath(path)
51
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
52
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
48
53
  return resolved
49
54
 
50
55
  @function_tool
51
56
  def file_read(path: str) -> str:
52
- """Read a file from persistent storage. The path is relative to the storage root."""
57
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
53
58
  try:
54
59
  full_path = _safe_resolve(path)
55
60
  with open(full_path) as f:
@@ -61,7 +66,7 @@ def file_read(path: str) -> str:
61
66
 
62
67
  @function_tool
63
68
  def file_write(path: str, content: str) -> str:
64
- """Write content to a file in persistent storage. The path is relative to the storage root."""
69
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
65
70
  try:
66
71
  full_path = _safe_resolve(path)
67
72
  parent = os.path.dirname(full_path)
@@ -76,25 +81,28 @@ def file_write(path: str, content: str) -> str:
76
81
  return f"Error writing '{path}': {e.strerror}"
77
82
 
78
83
  @function_tool
79
- def list_files(directory: str = "") -> str:
80
- """List files in persistent storage. The directory is relative to the storage root."""
84
+ def list_files(path: str) -> str:
85
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
81
86
  try:
82
- target = _safe_resolve(directory)
83
- entries = os.listdir(target)
87
+ full_path = _safe_resolve(path)
88
+ entries = os.listdir(full_path)
84
89
  return "\n".join(entries) if entries else "(empty directory)"
85
90
  except ValueError as e:
86
91
  return str(e)
87
92
  except OSError as e:
88
- return f"Error listing '{directory}': {e.strerror}"
93
+ return f"Error listing '{path}': {e.strerror}"
89
94
 
90
95
  tools.extend([file_read, file_write, list_files])
91
96
  {{/if}}
92
97
 
93
98
  INSTRUCTIONS = """
94
99
  You are a helpful assistant. Use tools when appropriate.
95
- {{#if sessionStorageMountPath}}
96
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
97
- {{/if}}
100
+ {{#if needsOs}}
101
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
102
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
103
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
104
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
105
+ {{/each}}{{/if}}
98
106
  """
99
107
 
100
108
  # Define the agent execution
@@ -15,7 +15,7 @@ from mcp_client.client import get_streamable_http_mcp_client
15
15
  {{#if hasMemory}}
16
16
  from memory.session import get_memory_session_manager
17
17
  {{/if}}
18
- {{#if sessionStorageMountPath}}
18
+ {{#if needsOs}}
19
19
  import os
20
20
  {{/if}}
21
21
 
@@ -31,9 +31,12 @@ mcp_clients = [get_streamable_http_mcp_client()]
31
31
 
32
32
  DEFAULT_SYSTEM_PROMPT = """
33
33
  You are a helpful assistant. Use tools when appropriate.
34
- {{#if sessionStorageMountPath}}
35
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
36
- {{/if}}
34
+ {{#if needsOs}}
35
+ You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
36
+ {{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
37
+ {{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
38
+ {{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
39
+ {{/each}}{{/if}}
37
40
  """
38
41
 
39
42
  {{#if hasConfigBundle}}
@@ -54,19 +57,22 @@ def add_numbers(a: int, b: int) -> int:
54
57
  return a+b
55
58
  tools.append(add_numbers)
56
59
 
57
- {{#if sessionStorageMountPath}}
58
- SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
60
+ {{#if needsOs}}
61
+ _MOUNT_PATHS = [
62
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
63
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
64
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
65
+ ]
59
66
 
60
67
  def _safe_resolve(path: str) -> str:
61
- """Resolve path safely within the storage boundary."""
62
- resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
63
- if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
64
- raise ValueError(f"Path '{path}' is outside the storage boundary")
68
+ resolved = os.path.realpath(path)
69
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
70
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
65
71
  return resolved
66
72
 
67
73
  @tool
68
74
  def file_read(path: str) -> str:
69
- """Read a file from persistent storage. The path is relative to the storage root."""
75
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
70
76
  try:
71
77
  full_path = _safe_resolve(path)
72
78
  with open(full_path) as f:
@@ -78,7 +84,7 @@ def file_read(path: str) -> str:
78
84
 
79
85
  @tool
80
86
  def file_write(path: str, content: str) -> str:
81
- """Write content to a file in persistent storage. The path is relative to the storage root."""
87
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
82
88
  try:
83
89
  full_path = _safe_resolve(path)
84
90
  parent = os.path.dirname(full_path)
@@ -93,16 +99,16 @@ def file_write(path: str, content: str) -> str:
93
99
  return f"Error writing '{path}': {e.strerror}"
94
100
 
95
101
  @tool
96
- def list_files(directory: str = "") -> str:
97
- """List files in persistent storage. The directory is relative to the storage root."""
102
+ def list_files(path: str) -> str:
103
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
98
104
  try:
99
- target = _safe_resolve(directory)
100
- entries = os.listdir(target)
105
+ full_path = _safe_resolve(path)
106
+ entries = os.listdir(full_path)
101
107
  return "\n".join(entries) if entries else "(empty directory)"
102
108
  except ValueError as e:
103
109
  return str(e)
104
110
  except OSError as e:
105
- return f"Error listing '{directory}': {e.strerror}"
111
+ return f"Error listing '{path}': {e.strerror}"
106
112
 
107
113
  tools.extend([file_read, file_write, list_files])
108
114
  {{/if}}
@@ -1,7 +1,65 @@
1
+ {{#if needsOs}}
2
+ import os
3
+ {{/if}}
1
4
  from mcp.server.fastmcp import FastMCP
2
5
 
3
6
  mcp = FastMCP("{{ name }}", host="0.0.0.0", stateless_http=True)
4
7
 
8
+ {{#if needsOs}}
9
+ _MOUNT_PATHS = [
10
+ {{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
11
+ {{#each efsMounts}}"{{mountPath}}",{{/each}}
12
+ {{#each s3Mounts}}"{{mountPath}}",{{/each}}
13
+ ]
14
+
15
+ def _safe_resolve(path: str) -> str:
16
+ resolved = os.path.realpath(path)
17
+ if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
18
+ raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
19
+ return resolved
20
+
21
+ @mcp.tool()
22
+ def file_read(path: str) -> str:
23
+ """Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
24
+ try:
25
+ full_path = _safe_resolve(path)
26
+ with open(full_path) as f:
27
+ return f.read()
28
+ except ValueError as e:
29
+ return str(e)
30
+ except OSError as e:
31
+ return f"Error reading '{path}': {e.strerror}"
32
+
33
+ @mcp.tool()
34
+ def file_write(path: str, content: str) -> str:
35
+ """Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
36
+ try:
37
+ full_path = _safe_resolve(path)
38
+ parent = os.path.dirname(full_path)
39
+ if parent:
40
+ os.makedirs(parent, exist_ok=True)
41
+ with open(full_path, "w") as f:
42
+ f.write(content)
43
+ return f"Written to {path}"
44
+ except ValueError as e:
45
+ return str(e)
46
+ except OSError as e:
47
+ return f"Error writing '{path}': {e.strerror}"
48
+
49
+ @mcp.tool()
50
+ def list_files(path: str) -> str:
51
+ """List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
52
+ try:
53
+ full_path = _safe_resolve(path)
54
+ entries = os.listdir(full_path)
55
+ return "\n".join(entries) if entries else "(empty directory)"
56
+ except ValueError as e:
57
+ return str(e)
58
+ except OSError as e:
59
+ return f"Error listing '{path}': {e.strerror}"
60
+
61
+ {{/if}}
62
+
5
63
 
6
64
  @mcp.tool()
7
65
  def add_numbers(a: int, b: int) -> int: