@aws/agentcore 1.0.0-preview.11 → 1.0.0-preview.13
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/dist/agent-inspector/index.css +1 -1
- package/dist/agent-inspector/index.js +83 -78
- package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +860 -158
- package/dist/assets/cdk/bin/cdk.ts +53 -5
- package/dist/assets/cdk/lib/cdk-stack.ts +164 -1
- package/dist/assets/cdk/package.json +1 -1
- package/dist/assets/cdk/test/cdk.test.ts +2 -0
- package/dist/assets/python/a2a/googleadk/base/main.py +24 -16
- package/dist/assets/python/a2a/langchain_langgraph/base/main.py +24 -16
- package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +1 -1
- package/dist/assets/python/a2a/strands/base/main.py +23 -17
- package/dist/assets/python/agui/googleadk/base/main.py +62 -2
- package/dist/assets/python/agui/googleadk/base/pyproject.toml +1 -1
- package/dist/assets/python/agui/langchain_langgraph/base/main.py +56 -0
- package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +1 -1
- package/dist/assets/python/agui/strands/base/main.py +63 -1
- package/dist/assets/python/http/autogen/base/main.py +27 -19
- package/dist/assets/python/http/autogen/base/pyproject.toml +1 -1
- package/dist/assets/python/http/googleadk/base/main.py +24 -16
- package/dist/assets/python/http/langchain_langgraph/base/main.py +24 -16
- package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +1 -1
- package/dist/assets/python/http/openaiagents/base/main.py +24 -16
- package/dist/assets/python/http/openaiagents/base/pyproject.toml +1 -1
- package/dist/assets/python/http/strands/base/main.py +62 -19
- package/dist/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
- package/dist/assets/python/http/strands/capabilities/payments/payments.py +142 -0
- package/dist/assets/python/mcp/standalone/base/main.py +58 -0
- package/dist/cli/index.mjs +772 -660
- package/dist/lib/errors/types.d.ts +3 -0
- package/dist/lib/errors/types.d.ts.map +1 -1
- package/dist/lib/errors/types.js +10 -1
- package/dist/lib/errors/types.js.map +1 -1
- package/dist/lib/errors/zod.js +1 -1
- package/dist/lib/errors/zod.js.map +1 -1
- package/dist/lib/packaging/helpers.d.ts.map +1 -1
- package/dist/lib/packaging/helpers.js +50 -10
- package/dist/lib/packaging/helpers.js.map +1 -1
- package/dist/lib/result.d.ts +6 -4
- package/dist/lib/result.d.ts.map +1 -1
- package/dist/lib/result.js +7 -0
- package/dist/lib/result.js.map +1 -1
- package/dist/lib/utils/env.d.ts +4 -0
- package/dist/lib/utils/env.d.ts.map +1 -1
- package/dist/lib/utils/env.js +18 -0
- package/dist/lib/utils/env.js.map +1 -1
- package/dist/lib/utils/index.d.ts +1 -1
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +2 -1
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/schema/schemas/agent-env.d.ts +50 -4
- package/dist/schema/schemas/agent-env.d.ts.map +1 -1
- package/dist/schema/schemas/agent-env.js +102 -4
- package/dist/schema/schemas/agent-env.js.map +1 -1
- package/dist/schema/schemas/agentcore-project.d.ts +69 -4
- package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
- package/dist/schema/schemas/agentcore-project.js +61 -5
- package/dist/schema/schemas/agentcore-project.js.map +1 -1
- package/dist/schema/schemas/deployed-state.d.ts +97 -0
- package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
- package/dist/schema/schemas/deployed-state.js +24 -4
- package/dist/schema/schemas/deployed-state.js.map +1 -1
- package/dist/schema/schemas/primitives/harness.d.ts +41 -0
- package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
- package/dist/schema/schemas/primitives/harness.js +65 -1
- package/dist/schema/schemas/primitives/harness.js.map +1 -1
- package/dist/schema/schemas/primitives/index.d.ts +4 -2
- package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
- package/dist/schema/schemas/primitives/index.js +14 -2
- package/dist/schema/schemas/primitives/index.js.map +1 -1
- package/dist/schema/schemas/primitives/payment.d.ts +54 -0
- package/dist/schema/schemas/primitives/payment.d.ts.map +1 -0
- package/dist/schema/schemas/primitives/payment.js +79 -0
- package/dist/schema/schemas/primitives/payment.js.map +1 -0
- package/npm-shrinkwrap.json +1027 -706
- package/package.json +13 -7
- package/scripts/run-e2e-local.sh +9 -3
|
@@ -15,9 +15,12 @@ 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
|
|
18
|
+
{{#if needsOs}}
|
|
19
19
|
import os
|
|
20
20
|
{{/if}}
|
|
21
|
+
{{#if hasPayment}}
|
|
22
|
+
from capabilities.payments.payments import create_payments_plugin, PAYMENT_SYSTEM_PROMPT
|
|
23
|
+
{{/if}}
|
|
21
24
|
|
|
22
25
|
app = BedrockAgentCoreApp()
|
|
23
26
|
log = app.logger
|
|
@@ -31,9 +34,12 @@ mcp_clients = [get_streamable_http_mcp_client()]
|
|
|
31
34
|
|
|
32
35
|
DEFAULT_SYSTEM_PROMPT = """
|
|
33
36
|
You are a helpful assistant. Use tools when appropriate.
|
|
34
|
-
{{#if
|
|
35
|
-
You have
|
|
36
|
-
{{
|
|
37
|
+
{{#if needsOs}}
|
|
38
|
+
You have access to the following mounted filesystems. Use file_read, file_write, and list_files with full absolute paths:
|
|
39
|
+
{{#if sessionStorageMountPath}}- {{sessionStorageMountPath}}: ephemeral session storage (lost when session ends)
|
|
40
|
+
{{/if}}{{#each efsMounts}}- {{mountPath}}: EFS persistent storage (persists across sessions and agent restarts)
|
|
41
|
+
{{/each}}{{#each s3Mounts}}- {{mountPath}}: S3 Files persistent storage (durable, backed by S3)
|
|
42
|
+
{{/each}}{{/if}}
|
|
37
43
|
"""
|
|
38
44
|
|
|
39
45
|
{{#if hasConfigBundle}}
|
|
@@ -54,19 +60,22 @@ def add_numbers(a: int, b: int) -> int:
|
|
|
54
60
|
return a+b
|
|
55
61
|
tools.append(add_numbers)
|
|
56
62
|
|
|
57
|
-
{{#if
|
|
58
|
-
|
|
63
|
+
{{#if needsOs}}
|
|
64
|
+
_MOUNT_PATHS = [
|
|
65
|
+
{{#if sessionStorageMountPath}}"{{sessionStorageMountPath}}",{{/if}}
|
|
66
|
+
{{#each efsMounts}}"{{mountPath}}",{{/each}}
|
|
67
|
+
{{#each s3Mounts}}"{{mountPath}}",{{/each}}
|
|
68
|
+
]
|
|
59
69
|
|
|
60
70
|
def _safe_resolve(path: str) -> str:
|
|
61
|
-
|
|
62
|
-
resolved
|
|
63
|
-
|
|
64
|
-
raise ValueError(f"Path '{path}' is outside the storage boundary")
|
|
71
|
+
resolved = os.path.realpath(path)
|
|
72
|
+
if not any(resolved == os.path.realpath(m) or resolved.startswith(os.path.realpath(m) + os.sep) for m in _MOUNT_PATHS):
|
|
73
|
+
raise ValueError(f"Path '{path}' is not within any configured mount ({', '.join(_MOUNT_PATHS)})")
|
|
65
74
|
return resolved
|
|
66
75
|
|
|
67
76
|
@tool
|
|
68
77
|
def file_read(path: str) -> str:
|
|
69
|
-
"""Read a file from
|
|
78
|
+
"""Read a file from a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
|
|
70
79
|
try:
|
|
71
80
|
full_path = _safe_resolve(path)
|
|
72
81
|
with open(full_path) as f:
|
|
@@ -78,7 +87,7 @@ def file_read(path: str) -> str:
|
|
|
78
87
|
|
|
79
88
|
@tool
|
|
80
89
|
def file_write(path: str, content: str) -> str:
|
|
81
|
-
"""Write
|
|
90
|
+
"""Write a file to a mounted filesystem. Use the absolute path (e.g. /mnt/tools/data.txt)."""
|
|
82
91
|
try:
|
|
83
92
|
full_path = _safe_resolve(path)
|
|
84
93
|
parent = os.path.dirname(full_path)
|
|
@@ -93,16 +102,16 @@ def file_write(path: str, content: str) -> str:
|
|
|
93
102
|
return f"Error writing '{path}': {e.strerror}"
|
|
94
103
|
|
|
95
104
|
@tool
|
|
96
|
-
def list_files(
|
|
97
|
-
"""List files in
|
|
105
|
+
def list_files(path: str) -> str:
|
|
106
|
+
"""List files in a mounted filesystem directory. Use the absolute path (e.g. /mnt/tools)."""
|
|
98
107
|
try:
|
|
99
|
-
|
|
100
|
-
entries = os.listdir(
|
|
108
|
+
full_path = _safe_resolve(path)
|
|
109
|
+
entries = os.listdir(full_path)
|
|
101
110
|
return "\n".join(entries) if entries else "(empty directory)"
|
|
102
111
|
except ValueError as e:
|
|
103
112
|
return str(e)
|
|
104
113
|
except OSError as e:
|
|
105
|
-
return f"Error listing '{
|
|
114
|
+
return f"Error listing '{path}': {e.strerror}"
|
|
106
115
|
|
|
107
116
|
tools.extend([file_read, file_write, list_files])
|
|
108
117
|
{{/if}}
|
|
@@ -147,12 +156,12 @@ class ConfigBundleHook(HookProvider):
|
|
|
147
156
|
{{/if}}
|
|
148
157
|
|
|
149
158
|
{{#if hasMemory}}
|
|
159
|
+
{{#unless hasPayment}}
|
|
150
160
|
def agent_factory():
|
|
151
161
|
cache = {}
|
|
152
162
|
def get_or_create_agent(session_id, user_id):
|
|
153
163
|
key = f"{session_id}/{user_id}"
|
|
154
164
|
if key not in cache:
|
|
155
|
-
# Create an agent for the given session_id and user_id
|
|
156
165
|
cache[key] = Agent(
|
|
157
166
|
model=load_model(),
|
|
158
167
|
session_manager=get_memory_session_manager(session_id, user_id),
|
|
@@ -163,6 +172,7 @@ def agent_factory():
|
|
|
163
172
|
return cache[key]
|
|
164
173
|
return get_or_create_agent
|
|
165
174
|
get_or_create_agent = agent_factory()
|
|
175
|
+
{{/unless}}
|
|
166
176
|
{{else}}
|
|
167
177
|
{{#if hasConfigBundle}}
|
|
168
178
|
def create_agent():
|
|
@@ -173,6 +183,7 @@ def create_agent():
|
|
|
173
183
|
hooks=[ConfigBundleHook()],
|
|
174
184
|
)
|
|
175
185
|
{{else}}
|
|
186
|
+
{{#unless hasPayment}}
|
|
176
187
|
_agent = None
|
|
177
188
|
|
|
178
189
|
def get_or_create_agent():
|
|
@@ -181,9 +192,10 @@ def get_or_create_agent():
|
|
|
181
192
|
_agent = Agent(
|
|
182
193
|
model=load_model(),
|
|
183
194
|
system_prompt=DEFAULT_SYSTEM_PROMPT,
|
|
184
|
-
tools=tools
|
|
195
|
+
tools=tools,
|
|
185
196
|
)
|
|
186
197
|
return _agent
|
|
198
|
+
{{/unless}}
|
|
187
199
|
{{/if}}
|
|
188
200
|
{{/if}}
|
|
189
201
|
|
|
@@ -192,16 +204,47 @@ def get_or_create_agent():
|
|
|
192
204
|
async def invoke(payload, context):
|
|
193
205
|
log.info("Invoking Agent.....")
|
|
194
206
|
|
|
207
|
+
{{#if hasPayment}}
|
|
208
|
+
user_id = payload.get("user_id") or getattr(context, "user_id", "default-user")
|
|
209
|
+
instrument_id = payload.get("payment_instrument_id")
|
|
210
|
+
session_id = payload.get("payment_session_id")
|
|
211
|
+
payments_plugin = create_payments_plugin(user_id, instrument_id, session_id)
|
|
212
|
+
plugins = [payments_plugin] if payments_plugin else []
|
|
213
|
+
{{/if}}
|
|
214
|
+
|
|
195
215
|
{{#if hasMemory}}
|
|
216
|
+
{{#if hasPayment}}
|
|
217
|
+
mem_session_id = getattr(context, 'session_id', 'default-session')
|
|
218
|
+
mem_user_id = getattr(context, 'user_id', 'default-user')
|
|
219
|
+
agent = Agent(
|
|
220
|
+
model=load_model(),
|
|
221
|
+
session_manager=get_memory_session_manager(mem_session_id, mem_user_id),
|
|
222
|
+
system_prompt=DEFAULT_SYSTEM_PROMPT + PAYMENT_SYSTEM_PROMPT,
|
|
223
|
+
tools=tools,
|
|
224
|
+
plugins=plugins,{{#if hasConfigBundle}}
|
|
225
|
+
hooks=[ConfigBundleHook()],{{/if}}
|
|
226
|
+
)
|
|
227
|
+
{{else}}
|
|
196
228
|
session_id = getattr(context, 'session_id', 'default-session')
|
|
197
229
|
user_id = getattr(context, 'user_id', 'default-user')
|
|
198
230
|
agent = get_or_create_agent(session_id, user_id)
|
|
231
|
+
{{/if}}
|
|
232
|
+
{{else}}
|
|
233
|
+
{{#if hasPayment}}
|
|
234
|
+
agent = Agent(
|
|
235
|
+
model=load_model(),
|
|
236
|
+
system_prompt=DEFAULT_SYSTEM_PROMPT + PAYMENT_SYSTEM_PROMPT,
|
|
237
|
+
tools=tools,
|
|
238
|
+
plugins=plugins,{{#if hasConfigBundle}}
|
|
239
|
+
hooks=[ConfigBundleHook()],{{/if}}
|
|
240
|
+
)
|
|
199
241
|
{{else}}
|
|
200
242
|
{{#if hasConfigBundle}}
|
|
201
243
|
agent = create_agent()
|
|
202
244
|
{{else}}
|
|
203
245
|
agent = get_or_create_agent()
|
|
204
246
|
{{/if}}
|
|
247
|
+
{{/if}}
|
|
205
248
|
{{/if}}
|
|
206
249
|
|
|
207
250
|
# Execute and format response
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Payment capability -- auto-generated by agentcore CLI.
|
|
2
|
+
|
|
3
|
+
Configures AgentCorePaymentsPlugin for Strands agents.
|
|
4
|
+
Manager config is auto-discovered from AGENTCORE_PAYMENT_*
|
|
5
|
+
environment variables set at deploy time.
|
|
6
|
+
|
|
7
|
+
Uses a per-invocation factory pattern so each request gets its own
|
|
8
|
+
plugin instance with the correct user_id, instrument_id, and session_id.
|
|
9
|
+
This prevents concurrency bugs where one user's payment context
|
|
10
|
+
could leak to another user's request.
|
|
11
|
+
|
|
12
|
+
Uses the official SDK plugin which handles:
|
|
13
|
+
- x402 v1 (body-based) and v2 (header-based) payment detection
|
|
14
|
+
- Automatic 402 response interception and payment processing
|
|
15
|
+
- Retry limiting (max 3 payment retries per tool use)
|
|
16
|
+
- Error management and logging
|
|
17
|
+
"""
|
|
18
|
+
import os
|
|
19
|
+
import logging
|
|
20
|
+
|
|
21
|
+
import boto3
|
|
22
|
+
from bedrock_agentcore.payments.integrations.strands import AgentCorePaymentsPlugin
|
|
23
|
+
from bedrock_agentcore.payments.integrations.config import AgentCorePaymentsPluginConfig
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
PAYMENT_SYSTEM_PROMPT = """
|
|
28
|
+
You have payment capabilities via the x402 protocol:
|
|
29
|
+
- Use http_request to call HTTP endpoints. 402 Payment Required responses are settled automatically by the plugin and the call is retried.
|
|
30
|
+
- Use get_payment_session to check your remaining budget before expensive operations
|
|
31
|
+
- Use get_payment_instrument_balance to check wallet USDC balance
|
|
32
|
+
- Use list_payment_instruments to see available payment instruments
|
|
33
|
+
- If budget is low, inform the user before proceeding with paid requests
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
_manager_arn = None
|
|
37
|
+
_connector_id = None
|
|
38
|
+
_process_payment_role_arn = None
|
|
39
|
+
_name_segment = None
|
|
40
|
+
_region = None
|
|
41
|
+
_auth_mode = None
|
|
42
|
+
_manager_count = 0
|
|
43
|
+
for key, value in os.environ.items():
|
|
44
|
+
if key.startswith("AGENTCORE_PAYMENT_") and key.endswith("_MANAGER_ARN"):
|
|
45
|
+
if _manager_arn is None:
|
|
46
|
+
_manager_arn = value
|
|
47
|
+
_name_segment = key[len("AGENTCORE_PAYMENT_"):-len("_MANAGER_ARN")]
|
|
48
|
+
_manager_count += 1
|
|
49
|
+
if _manager_count > 1:
|
|
50
|
+
logger.warning(
|
|
51
|
+
"Multiple payment managers detected in environment. Using the first one found. "
|
|
52
|
+
"Remove extra AGENTCORE_PAYMENT_*_MANAGER_ARN env vars to eliminate ambiguity."
|
|
53
|
+
)
|
|
54
|
+
_region = os.getenv("AWS_REGION")
|
|
55
|
+
|
|
56
|
+
_prefix = f"AGENTCORE_PAYMENT_{_name_segment}_" if _name_segment else "AGENTCORE_PAYMENT_"
|
|
57
|
+
_auth_mode = os.getenv(f"{_prefix}AUTH_MODE", "sigv4")
|
|
58
|
+
_connector_id = os.getenv(f"{_prefix}CONNECTOR_ID")
|
|
59
|
+
_process_payment_role_arn = os.getenv(f"{_prefix}PROCESS_PAYMENT_ROLE_ARN")
|
|
60
|
+
_auto_payment = os.getenv(f"{_prefix}AUTO_PAYMENT", "true").lower() == "true"
|
|
61
|
+
_allowlist_raw = os.getenv(f"{_prefix}TOOL_ALLOWLIST")
|
|
62
|
+
_allowlist = _allowlist_raw.split(",") if _allowlist_raw else None
|
|
63
|
+
_network_prefs_raw = os.getenv(f"{_prefix}NETWORK_PREFERENCES")
|
|
64
|
+
_network_prefs = _network_prefs_raw.split(",") if _network_prefs_raw else None
|
|
65
|
+
|
|
66
|
+
if not _manager_arn:
|
|
67
|
+
logger.warning("No payment manager config found in environment")
|
|
68
|
+
if not _connector_id:
|
|
69
|
+
logger.warning("No payment connector config found in environment")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _assume_role_session(role_arn):
|
|
73
|
+
"""Assume an IAM role and return a boto3 session with temporary credentials."""
|
|
74
|
+
sts = boto3.client("sts", region_name=_region)
|
|
75
|
+
creds = sts.assume_role(
|
|
76
|
+
RoleArn=role_arn,
|
|
77
|
+
RoleSessionName="agentcore-payment-plugin",
|
|
78
|
+
)["Credentials"]
|
|
79
|
+
return boto3.Session(
|
|
80
|
+
aws_access_key_id=creds["AccessKeyId"],
|
|
81
|
+
aws_secret_access_key=creds["SecretAccessKey"],
|
|
82
|
+
aws_session_token=creds["SessionToken"],
|
|
83
|
+
region_name=_region,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_payments_plugin(user_id, instrument_id=None, session_id=None):
|
|
88
|
+
"""Create a fresh plugin instance per invocation.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
user_id: From invocation context (required for SigV4, derived from JWT for bearer)
|
|
92
|
+
instrument_id: From invocation payload (created by app backend per user)
|
|
93
|
+
session_id: From invocation payload (created by app backend per conversation)
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
AgentCorePaymentsPlugin instance, or None if no manager is configured.
|
|
97
|
+
"""
|
|
98
|
+
if not _manager_arn:
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
config_kwargs = {
|
|
102
|
+
"payment_manager_arn": _manager_arn,
|
|
103
|
+
"region": _region,
|
|
104
|
+
"payment_instrument_id": instrument_id,
|
|
105
|
+
"payment_session_id": session_id,
|
|
106
|
+
"payment_connector_id": _connector_id,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
config_kwargs["auto_payment"] = _auto_payment
|
|
110
|
+
if _allowlist:
|
|
111
|
+
config_kwargs["payment_tool_allowlist"] = _allowlist
|
|
112
|
+
if _network_prefs:
|
|
113
|
+
config_kwargs["network_preferences_config"] = _network_prefs
|
|
114
|
+
|
|
115
|
+
if _process_payment_role_arn:
|
|
116
|
+
# Only pass boto3_session if SDK supports it (added in bedrock-agentcore >= 1.11).
|
|
117
|
+
# Older SDKs use the runtime role's default credentials and can still call ProcessPayment
|
|
118
|
+
# if the runtime role has been granted permission directly.
|
|
119
|
+
import inspect
|
|
120
|
+
if "boto3_session" in inspect.signature(AgentCorePaymentsPluginConfig).parameters:
|
|
121
|
+
config_kwargs["boto3_session"] = _assume_role_session(_process_payment_role_arn)
|
|
122
|
+
else:
|
|
123
|
+
logger.warning(
|
|
124
|
+
"PROCESS_PAYMENT_ROLE_ARN set but bedrock-agentcore SDK does not support boto3_session. "
|
|
125
|
+
"Upgrade to bedrock-agentcore>=1.11 to enable cross-role payment processing."
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if _auth_mode == "bearer":
|
|
129
|
+
bearer_token = os.getenv("AGENTCORE_BEARER_TOKEN")
|
|
130
|
+
if bearer_token:
|
|
131
|
+
config_kwargs["bearer_token"] = bearer_token
|
|
132
|
+
else:
|
|
133
|
+
logger.warning(
|
|
134
|
+
"Bearer auth mode configured but AGENTCORE_BEARER_TOKEN not set. "
|
|
135
|
+
"Falling back to SigV4. Set AGENTCORE_BEARER_TOKEN or pass bearer_token in invoke context."
|
|
136
|
+
)
|
|
137
|
+
config_kwargs["user_id"] = user_id or "default-user"
|
|
138
|
+
else:
|
|
139
|
+
config_kwargs["user_id"] = user_id or "default-user"
|
|
140
|
+
|
|
141
|
+
config = AgentCorePaymentsPluginConfig(**config_kwargs)
|
|
142
|
+
return AgentCorePaymentsPlugin(config=config)
|
|
@@ -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:
|