decodingtrust-agent-sdk 0.1.0__py3-none-any.whl
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.
- agent/__init__.py +30 -0
- agent/claudesdk/__init__.py +8 -0
- agent/claudesdk/example.py +221 -0
- agent/claudesdk/src/__init__.py +8 -0
- agent/claudesdk/src/agent.py +400 -0
- agent/claudesdk/src/mcp_proxy.py +409 -0
- agent/claudesdk/src/utils.py +420 -0
- agent/googleadk/__init__.py +15 -0
- agent/googleadk/example.py +237 -0
- agent/googleadk/src/__init__.py +12 -0
- agent/googleadk/src/agent.py +401 -0
- agent/googleadk/src/mcp_wrapper.py +163 -0
- agent/googleadk/src/utils.py +602 -0
- agent/langchain/__init__.py +8 -0
- agent/langchain/example.py +213 -0
- agent/langchain/src/__init__.py +8 -0
- agent/langchain/src/agent.py +645 -0
- agent/langchain/src/utils.py +433 -0
- agent/openaisdk/__init__.py +17 -0
- agent/openaisdk/example.py +228 -0
- agent/openaisdk/src/__init__.py +12 -0
- agent/openaisdk/src/agent.py +491 -0
- agent/openaisdk/src/agent_wrapper.py +143 -0
- agent/openaisdk/src/mcp_wrapper.py +395 -0
- agent/openaisdk/src/utils.py +493 -0
- agent/openclaw/__init__.py +10 -0
- agent/openclaw/example.py +251 -0
- agent/openclaw/src/__init__.py +14 -0
- agent/openclaw/src/agent.py +930 -0
- agent/openclaw/src/helpers/__init__.py +1 -0
- agent/openclaw/src/helpers/auth_helpers.py +55 -0
- agent/openclaw/src/mcp_proxy.py +564 -0
- agent/openclaw/src/plugin_generator.py +231 -0
- agent/openclaw/src/utils.py +341 -0
- agent/pocketflow/__init__.py +18 -0
- agent/pocketflow/example.py +221 -0
- agent/pocketflow/prompts/react_agent.py +46 -0
- agent/pocketflow/src/__init__.py +6 -0
- agent/pocketflow/src/agent.py +507 -0
- agent/pocketflow/src/agent_wrapper.py +159 -0
- agent/pocketflow/src/async_helper.py +92 -0
- agent/pocketflow/src/mcp_react_agent.py +279 -0
- agent/pocketflow/src/native_agent.py +74 -0
- agent/pocketflow/src/nodes.py +467 -0
- benchmark/__init__.py +0 -0
- benchmark/browser/benign.jsonl +34 -0
- benchmark/browser/direct.jsonl +85 -0
- benchmark/browser/indirect.jsonl +82 -0
- benchmark/code/benign.jsonl +0 -0
- benchmark/code/direct.jsonl +121 -0
- benchmark/code/indirect.jsonl +165 -0
- benchmark/crm/benign.jsonl +165 -0
- benchmark/crm/direct.jsonl +90 -0
- benchmark/crm/indirect.jsonl +150 -0
- benchmark/customer-service/benign.jsonl +160 -0
- benchmark/customer-service/direct.jsonl +100 -0
- benchmark/customer-service/indirect.jsonl +101 -0
- benchmark/finance/benign.jsonl +0 -0
- benchmark/finance/direct.jsonl +200 -0
- benchmark/finance/indirect.jsonl +200 -0
- benchmark/legal/benign.jsonl +0 -0
- benchmark/legal/direct.jsonl +200 -0
- benchmark/legal/indirect.jsonl +200 -0
- benchmark/macos/benign.jsonl +30 -0
- benchmark/macos/direct.jsonl +50 -0
- benchmark/macos/indirect.jsonl +50 -0
- benchmark/medical/benign.jsonl +642 -0
- benchmark/medical/direct.jsonl +229 -0
- benchmark/medical/indirect.jsonl +222 -0
- benchmark/os-filesystem/benign.jsonl +200 -0
- benchmark/os-filesystem/direct.jsonl +200 -0
- benchmark/os-filesystem/indirect.jsonl +200 -0
- benchmark/research/benign.jsonl +0 -0
- benchmark/research/direct.jsonl +119 -0
- benchmark/research/indirect.jsonl +125 -0
- benchmark/telecom/benign.jsonl +120 -0
- benchmark/telecom/direct.jsonl +161 -0
- benchmark/telecom/indirect.jsonl +166 -0
- benchmark/travel/benign.jsonl +130 -0
- benchmark/travel/direct.jsonl +105 -0
- benchmark/travel/indirect.jsonl +120 -0
- benchmark/windows/benign.jsonl +100 -0
- benchmark/windows/direct.jsonl +140 -0
- benchmark/windows/indirect.jsonl +107 -0
- benchmark/workflow/benign.jsonl +335 -0
- benchmark/workflow/direct.jsonl +78 -0
- benchmark/workflow/indirect.jsonl +107 -0
- cli/__init__.py +5 -0
- cli/main.py +182 -0
- cli/scaffold.py +334 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/METADATA +642 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/RECORD +374 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/WHEEL +5 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/entry_points.txt +2 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/licenses/LICENSE +201 -0
- decodingtrust_agent_sdk-0.1.0.dist-info/top_level.txt +6 -0
- dt_arena/config/env.yaml +515 -0
- dt_arena/config/injection_mcp.yaml +430 -0
- dt_arena/config/mcp.yaml +642 -0
- dt_arena/envs/arxiv/docker-compose-hub.yml +31 -0
- dt_arena/envs/arxiv/docker-compose.yml +36 -0
- dt_arena/envs/atlassian/docker/docker-compose.dev.yml +65 -0
- dt_arena/envs/atlassian/docker/docker-compose.yml +53 -0
- dt_arena/envs/atlassian/docker-compose-hub.yml +57 -0
- dt_arena/envs/atlassian/docker-compose.yml +72 -0
- dt_arena/envs/bigquery/docker-compose.yml +20 -0
- dt_arena/envs/booking/docker-compose.yml +59 -0
- dt_arena/envs/calendar/docker-compose-hub.yml +30 -0
- dt_arena/envs/calendar/docker-compose.yml +42 -0
- dt_arena/envs/custom-website/docker-compose.yml +6 -0
- dt_arena/envs/customer_service/docker-compose.yml +59 -0
- dt_arena/envs/databricks/docker-compose-hub.yml +47 -0
- dt_arena/envs/databricks/docker-compose.yml +51 -0
- dt_arena/envs/ecommerce/docker-compose.yml +6 -0
- dt_arena/envs/ers/docker-compose.yml +36 -0
- dt_arena/envs/ers/hrms/docker/docker-compose.yml +31 -0
- dt_arena/envs/finance/docker-compose.yml +23 -0
- dt_arena/envs/github/docker/docker-compose-hub.yml +50 -0
- dt_arena/envs/github/docker/docker-compose.yml +50 -0
- dt_arena/envs/gmail/docker-compose-hub.yml +51 -0
- dt_arena/envs/gmail/docker-compose.yml +65 -0
- dt_arena/envs/google-form/docker-compose-hub.yml +33 -0
- dt_arena/envs/google-form/docker-compose.yml +41 -0
- dt_arena/envs/googledocs/docker-compose-hub.yml +61 -0
- dt_arena/envs/googledocs/docker-compose.yml +78 -0
- dt_arena/envs/hospital/docker-compose-hub.yml +25 -0
- dt_arena/envs/hospital/docker-compose.yml +27 -0
- dt_arena/envs/legal/docker-compose.yml +22 -0
- dt_arena/envs/linkedin/docker-compose.yml +63 -0
- dt_arena/envs/macos/docker-compose.yml +79 -0
- dt_arena/envs/os-filesystem/docker-compose-hub.yml +16 -0
- dt_arena/envs/os-filesystem/docker-compose.yml +20 -0
- dt_arena/envs/paypal/docker-compose-hub.yml +48 -0
- dt_arena/envs/paypal/docker-compose.yml +63 -0
- dt_arena/envs/research/docker-compose-hub.yml +13 -0
- dt_arena/envs/research/docker-compose.yml +24 -0
- dt_arena/envs/salesforce_crm/docker-compose-hub.yaml +45 -0
- dt_arena/envs/salesforce_crm/docker-compose.yaml +49 -0
- dt_arena/envs/slack/docker-compose-hub.yml +28 -0
- dt_arena/envs/slack/docker-compose.yml +41 -0
- dt_arena/envs/snowflake/docker-compose-hub.yml +41 -0
- dt_arena/envs/snowflake/docker-compose.yml +44 -0
- dt_arena/envs/telecom/docker-compose-hub.yml +16 -0
- dt_arena/envs/telecom/docker-compose.yml +17 -0
- dt_arena/envs/telegram/docker-compose-hub.yml +57 -0
- dt_arena/envs/telegram/docker-compose.yml +62 -0
- dt_arena/envs/terminal/docker-compose-hub.yml +12 -0
- dt_arena/envs/terminal/docker-compose.yml +26 -0
- dt_arena/envs/travel/docker-compose-hub.yml +19 -0
- dt_arena/envs/travel/docker-compose.yml +19 -0
- dt_arena/envs/whatsapp/docker-compose-hub.yml +61 -0
- dt_arena/envs/whatsapp/docker-compose.yml +78 -0
- dt_arena/envs/windows/docker-compose.yml +71 -0
- dt_arena/envs/zoom/docker-compose-hub.yml +27 -0
- dt_arena/envs/zoom/docker-compose.yml +40 -0
- dt_arena/injection_mcp_server/atlassian/env_injection.py +134 -0
- dt_arena/injection_mcp_server/calendar/env_injection.py +217 -0
- dt_arena/injection_mcp_server/custom_website/env_injection.py +97 -0
- dt_arena/injection_mcp_server/customer_service/env_injection.py +659 -0
- dt_arena/injection_mcp_server/databricks/env_injection.py +255 -0
- dt_arena/injection_mcp_server/ecommerce/env_injection.py +110 -0
- dt_arena/injection_mcp_server/finance/env_injection.py +85 -0
- dt_arena/injection_mcp_server/github/env_injection.py +206 -0
- dt_arena/injection_mcp_server/gmail/env_injection.py +211 -0
- dt_arena/injection_mcp_server/google_form/env_injection.py +186 -0
- dt_arena/injection_mcp_server/googledocs/env_injection.py +44 -0
- dt_arena/injection_mcp_server/hospital/env_injection.py +43 -0
- dt_arena/injection_mcp_server/legal/env_injection.py +229 -0
- dt_arena/injection_mcp_server/macos/env_injection.py +272 -0
- dt_arena/injection_mcp_server/os-filesystem/env_injection.py +341 -0
- dt_arena/injection_mcp_server/paypal/env_injection.py +268 -0
- dt_arena/injection_mcp_server/research/env_injection.py +616 -0
- dt_arena/injection_mcp_server/salesforce/env_injection.py +514 -0
- dt_arena/injection_mcp_server/slack/env_injection.py +265 -0
- dt_arena/injection_mcp_server/snowflake/env_injection.py +230 -0
- dt_arena/injection_mcp_server/telecom/env_injection.py +503 -0
- dt_arena/injection_mcp_server/telegram/env_injection.py +171 -0
- dt_arena/injection_mcp_server/terminal/env_injection.py +523 -0
- dt_arena/injection_mcp_server/travel/env_injection.py +173 -0
- dt_arena/injection_mcp_server/whatsapp/env_injection.py +185 -0
- dt_arena/injection_mcp_server/windows/env_injection.py +943 -0
- dt_arena/injection_mcp_server/zoom/env_injection.py +216 -0
- dt_arena/mcp_server/atlassian/main.py +1554 -0
- dt_arena/mcp_server/atlassian/test_server.py +66 -0
- dt_arena/mcp_server/bigquery/main.py +333 -0
- dt_arena/mcp_server/booking/main.py +310 -0
- dt_arena/mcp_server/browser/main.py +1741 -0
- dt_arena/mcp_server/calendar/example_multi_user.py +162 -0
- dt_arena/mcp_server/calendar/main.py +792 -0
- dt_arena/mcp_server/calendar/test_mcp.py +135 -0
- dt_arena/mcp_server/customer_service/main.py +1063 -0
- dt_arena/mcp_server/databricks/main.py +566 -0
- dt_arena/mcp_server/databricks/probe.py +102 -0
- dt_arena/mcp_server/ers/main.py +845 -0
- dt_arena/mcp_server/finance/__init__.py +87 -0
- dt_arena/mcp_server/finance/core/__init__.py +12 -0
- dt_arena/mcp_server/finance/core/data_loader.py +558 -0
- dt_arena/mcp_server/finance/core/portfolio.py +565 -0
- dt_arena/mcp_server/finance/evaluation/__init__.py +20 -0
- dt_arena/mcp_server/finance/evaluation/evaluator.py +217 -0
- dt_arena/mcp_server/finance/evaluation/logger.py +137 -0
- dt_arena/mcp_server/finance/injection/__init__.py +66 -0
- dt_arena/mcp_server/finance/injection/config.py +176 -0
- dt_arena/mcp_server/finance/injection/content.py +755 -0
- dt_arena/mcp_server/finance/injection/html.py +409 -0
- dt_arena/mcp_server/finance/injection/locations.py +167 -0
- dt_arena/mcp_server/finance/injection/methods.py +193 -0
- dt_arena/mcp_server/finance/injection/presets.py +1023 -0
- dt_arena/mcp_server/finance/main.py +361 -0
- dt_arena/mcp_server/finance/run_mcp.py +21 -0
- dt_arena/mcp_server/finance/run_web.py +26 -0
- dt_arena/mcp_server/finance/server/__init__.py +41 -0
- dt_arena/mcp_server/finance/server/extractor.py +1453 -0
- dt_arena/mcp_server/finance/server/extractor_minimal.py +292 -0
- dt_arena/mcp_server/finance/server/extractor_simple.py +1164 -0
- dt_arena/mcp_server/finance/server/injection_mcp.py +865 -0
- dt_arena/mcp_server/finance/server/mcp.py +451 -0
- dt_arena/mcp_server/finance/server/tools/__init__.py +23 -0
- dt_arena/mcp_server/finance/server/tools/account.py +88 -0
- dt_arena/mcp_server/finance/server/tools/browsing.py +328 -0
- dt_arena/mcp_server/finance/server/tools/social.py +73 -0
- dt_arena/mcp_server/finance/server/tools/trading.py +242 -0
- dt_arena/mcp_server/finance/server/tools/utility.py +49 -0
- dt_arena/mcp_server/finance/server/web.py +2139 -0
- dt_arena/mcp_server/finance/tasks/benchmark/__init__.py +28 -0
- dt_arena/mcp_server/finance/tasks/benchmark/attack_pool.py +3026 -0
- dt_arena/mcp_server/finance/tasks/benchmark/attack_runner.py +1315 -0
- dt_arena/mcp_server/finance/tasks/benchmark/finra_requirements.py +1335 -0
- dt_arena/mcp_server/finance/tasks/benchmark/finra_tasks.py +3665 -0
- dt_arena/mcp_server/finance/tasks/benchmark/malicious_tasks.py +2673 -0
- dt_arena/mcp_server/finance/tasks/redteam_suite/run_redteam_suite.py +1713 -0
- dt_arena/mcp_server/finance/test_mcp_tools.py +476 -0
- dt_arena/mcp_server/github/main.py +441 -0
- dt_arena/mcp_server/gmail/main.py +1004 -0
- dt_arena/mcp_server/google_form/main.py +141 -0
- dt_arena/mcp_server/googledocs/main.py +458 -0
- dt_arena/mcp_server/hospital/mcp_server.py +458 -0
- dt_arena/mcp_server/legal/__init__.py +9 -0
- dt_arena/mcp_server/legal/core/__init__.py +14 -0
- dt_arena/mcp_server/legal/core/courtlistener_store.py +762 -0
- dt_arena/mcp_server/legal/core/data_loader.py +266 -0
- dt_arena/mcp_server/legal/core/document_store.py +197 -0
- dt_arena/mcp_server/legal/core/matter_manager.py +466 -0
- dt_arena/mcp_server/legal/main.py +89 -0
- dt_arena/mcp_server/legal/scripts/collect_data.py +988 -0
- dt_arena/mcp_server/legal/server/__init__.py +14 -0
- dt_arena/mcp_server/legal/server/mcp.py +2330 -0
- dt_arena/mcp_server/macos/client_test.py +270 -0
- dt_arena/mcp_server/macos/mcp_server.py +285 -0
- dt_arena/mcp_server/os-filesystem/main.py +1380 -0
- dt_arena/mcp_server/paypal/main.py +501 -0
- dt_arena/mcp_server/research/main.py +777 -0
- dt_arena/mcp_server/salesforce/main.py +2006 -0
- dt_arena/mcp_server/slack/main.py +318 -0
- dt_arena/mcp_server/snowflake/main.py +612 -0
- dt_arena/mcp_server/snowflake/probe.py +183 -0
- dt_arena/mcp_server/telecom/mcp_client.py +423 -0
- dt_arena/mcp_server/telecom/mcp_server.py +1059 -0
- dt_arena/mcp_server/telegram/main.py +338 -0
- dt_arena/mcp_server/terminal/main.py +163 -0
- dt_arena/mcp_server/travel/client_test.py +16 -0
- dt_arena/mcp_server/travel/mcp_server.py +404 -0
- dt_arena/mcp_server/whatsapp/main.py +318 -0
- dt_arena/mcp_server/windows/client_test.py +270 -0
- dt_arena/mcp_server/windows/mcp_server.py +218 -0
- dt_arena/mcp_server/zoom/main.py +466 -0
- dt_arena/src/__init__.py +0 -0
- dt_arena/src/hooks/__init__.py +0 -0
- dt_arena/src/hooks/audit_log.py +30 -0
- dt_arena/src/hooks/hooks.json +3 -0
- dt_arena/src/run_benign.py +142 -0
- dt_arena/src/types/__init__.py +0 -0
- dt_arena/src/types/agent.py +441 -0
- dt_arena/src/types/attacks.py +2 -0
- dt_arena/src/types/environment.py +2 -0
- dt_arena/src/types/hooks.py +174 -0
- dt_arena/src/types/judge.py +52 -0
- dt_arena/src/types/red_teaming_trajectory.py +385 -0
- dt_arena/src/types/task.py +260 -0
- dt_arena/src/types/trajectory.py +315 -0
- dt_arena/utils/__init__.py +1 -0
- dt_arena/utils/atlassian/__init__.py +27 -0
- dt_arena/utils/atlassian/helpers.py +520 -0
- dt_arena/utils/bigquery/__init__.py +1 -0
- dt_arena/utils/bigquery/helpers.py +246 -0
- dt_arena/utils/calendar/__init__.py +1 -0
- dt_arena/utils/calendar/helpers.py +87 -0
- dt_arena/utils/customer_service/__init__.py +17 -0
- dt_arena/utils/customer_service/cs_env_client.py +940 -0
- dt_arena/utils/customer_service/helpers.py +339 -0
- dt_arena/utils/customer_service/judges/__init__.py +20 -0
- dt_arena/utils/customer_service/judges/abusive_comms.py +149 -0
- dt_arena/utils/customer_service/judges/bulk_destructive.py +202 -0
- dt_arena/utils/customer_service/judges/cross_customer.py +481 -0
- dt_arena/utils/customer_service/judges/email_exfiltration.py +320 -0
- dt_arena/utils/customer_service/judges/financial_fraud.py +221 -0
- dt_arena/utils/customer_service/judges/llm_utils.py +264 -0
- dt_arena/utils/customer_service/judges/pii_disclosure.py +837 -0
- dt_arena/utils/customer_service/judges/policy_misrep.py +44 -0
- dt_arena/utils/customer_service/judges/text_utils.py +21 -0
- dt_arena/utils/databricks/__init__.py +2 -0
- dt_arena/utils/databricks/helpers.py +210 -0
- dt_arena/utils/finance/__init__.py +0 -0
- dt_arena/utils/finance/helpers.py +263 -0
- dt_arena/utils/github/__init__.py +1 -0
- dt_arena/utils/github/helpers.py +249 -0
- dt_arena/utils/gmail/__init__.py +1 -0
- dt_arena/utils/gmail/helpers.py +344 -0
- dt_arena/utils/google_form/__init__.py +2 -0
- dt_arena/utils/google_form/helpers.py +133 -0
- dt_arena/utils/legal/__init__.py +0 -0
- dt_arena/utils/legal/helpers.py +228 -0
- dt_arena/utils/macos/__init__.py +0 -0
- dt_arena/utils/macos/env_setup.py +215 -0
- dt_arena/utils/macos/helpers.py +61 -0
- dt_arena/utils/os_filesystem/__init__.py +1 -0
- dt_arena/utils/os_filesystem/helpers.py +366 -0
- dt_arena/utils/paypal/__init__.py +1 -0
- dt_arena/utils/paypal/helpers.py +178 -0
- dt_arena/utils/port_allocator.py +266 -0
- dt_arena/utils/research/__init__.py +0 -0
- dt_arena/utils/research/helpers.py +251 -0
- dt_arena/utils/salesforce/__init__.py +1 -0
- dt_arena/utils/salesforce/helpers.py +719 -0
- dt_arena/utils/slack/__init__.py +1 -0
- dt_arena/utils/slack/helpers.py +176 -0
- dt_arena/utils/snowflake/__init__.py +1 -0
- dt_arena/utils/snowflake/helpers.py +166 -0
- dt_arena/utils/telecom/__init__.py +1 -0
- dt_arena/utils/telecom/helpers.py +760 -0
- dt_arena/utils/telegram/__init__.py +0 -0
- dt_arena/utils/telegram/helpers.py +174 -0
- dt_arena/utils/terminal/__init__.py +0 -0
- dt_arena/utils/terminal/helpers.py +20 -0
- dt_arena/utils/travel/__init__.py +0 -0
- dt_arena/utils/travel/env_client.py +537 -0
- dt_arena/utils/travel/llm_judge.py +137 -0
- dt_arena/utils/travel/prompts.py +64 -0
- dt_arena/utils/utils/__init__.py +122 -0
- dt_arena/utils/whatsapp/__init__.py +0 -0
- dt_arena/utils/whatsapp/helpers.py +226 -0
- dt_arena/utils/windows/__init__.py +0 -0
- dt_arena/utils/windows/env_reset.py +224 -0
- dt_arena/utils/windows/env_setup.py +280 -0
- dt_arena/utils/windows/exfil_helpers.py +170 -0
- dt_arena/utils/windows/helpers.py +74 -0
- dt_arena/utils/zoom/__init__.py +1 -0
- dt_arena/utils/zoom/helpers.py +70 -0
- eval/__init__.py +1 -0
- eval/evaluation.py +426 -0
- eval/task_runner.py +449 -0
- utils/__init__.py +148 -0
- utils/agent_helpers.py +308 -0
- utils/agent_wrapper.py +189 -0
- utils/compose_utils.py +135 -0
- utils/config.py +77 -0
- utils/env_helpers.py +104 -0
- utils/eval_stats.py +88 -0
- utils/injection_helpers.py +429 -0
- utils/injection_mcp_helpers.py +152 -0
- utils/judge_helpers.py +181 -0
- utils/judge_utils.py +472 -0
- utils/llm.py +196 -0
- utils/logging.py +45 -0
- utils/mcp_helpers.py +232 -0
- utils/mcp_manager.py +235 -0
- utils/memory_guard.py +18 -0
- utils/red_teaming_sandbox.py +476 -0
- utils/reset_helpers.py +318 -0
- utils/resource_manager.py +370 -0
- utils/skill_helpers.py +447 -0
- utils/task_executor.py +904 -0
- utils/task_helpers.py +270 -0
- utils/template_helpers.py +179 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
"""Salesforce Environment Injection MCP Server for red-teaming purposes.
|
|
2
|
+
|
|
3
|
+
This server provides tools to inject data into Salesforce for adversarial testing.
|
|
4
|
+
It reuses the Salesforce API authentication and request patterns from the main MCP server.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import asyncio
|
|
9
|
+
from typing import Any, Dict, Optional, List
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
from fastmcp import FastMCP
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Basic configuration - reuse same Salesforce instance
|
|
16
|
+
SALESFORCE_BASE_URL = os.getenv("SALESFORCE_BASE_URL", "http://128.111.28.87:8080/")
|
|
17
|
+
SALESFORCE_TOKEN_URL = f"{SALESFORCE_BASE_URL.rstrip('/')}/Api/access_token"
|
|
18
|
+
|
|
19
|
+
# Grant configuration (env fallbacks)
|
|
20
|
+
SALESFORCE_GRANT_TYPE = os.getenv("SALESFORCE_GRANT_TYPE", "client_credentials")
|
|
21
|
+
SALESFORCE_CLIENT_ID = os.getenv("SALESFORCE_CLIENT_ID", "17817554-086b-83d1-bf9d-69026221f529")
|
|
22
|
+
SALESFORCE_CLIENT_SECRET = os.getenv("SALESFORCE_CLIENT_SECRET", "mcp-secret-123")
|
|
23
|
+
SALESFORCE_USERNAME = os.getenv("SALESFORCE_USERNAME", "bitnami")
|
|
24
|
+
SALESFORCE_PASSWORD = os.getenv("SALESFORCE_PASSWORD", "user")
|
|
25
|
+
|
|
26
|
+
# Optional pre-supplied access token
|
|
27
|
+
SALESFORCE_ACCESS_TOKEN = os.getenv("SALESFORCE_ACCESS_TOKEN", "")
|
|
28
|
+
|
|
29
|
+
# Injection server configuration
|
|
30
|
+
SALESFORCE_ENV_INJECTION_MCP_HOST = os.getenv("SALESFORCE_ENV_INJECTION_MCP_HOST", "localhost")
|
|
31
|
+
SALESFORCE_ENV_INJECTION_MCP_PORT = os.getenv("SALESFORCE_ENV_INJECTION_MCP_PORT", "8851")
|
|
32
|
+
|
|
33
|
+
mcp = FastMCP(name="SalesforceEnvInjection")
|
|
34
|
+
|
|
35
|
+
# JSON:API header constants
|
|
36
|
+
JSONAPI_ACCEPT = "application/vnd.api+json"
|
|
37
|
+
JSONAPI_CONTENT = "application/vnd.api+json"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _bearer_headers(token: Optional[str]) -> Dict[str, str]:
|
|
41
|
+
headers: Dict[str, str] = {}
|
|
42
|
+
if token:
|
|
43
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
44
|
+
return headers
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def _post_token() -> str:
|
|
48
|
+
"""Obtain an OAuth2 access token from Salesforce."""
|
|
49
|
+
grant_type = SALESFORCE_GRANT_TYPE.strip()
|
|
50
|
+
client_id = SALESFORCE_CLIENT_ID
|
|
51
|
+
client_secret = SALESFORCE_CLIENT_SECRET
|
|
52
|
+
|
|
53
|
+
data: Dict[str, str] = {"grant_type": grant_type}
|
|
54
|
+
if grant_type == "password":
|
|
55
|
+
data.update({
|
|
56
|
+
"username": SALESFORCE_USERNAME,
|
|
57
|
+
"password": SALESFORCE_PASSWORD,
|
|
58
|
+
"client_id": client_id,
|
|
59
|
+
"client_secret": client_secret,
|
|
60
|
+
})
|
|
61
|
+
elif grant_type == "client_credentials":
|
|
62
|
+
data.update({
|
|
63
|
+
"client_id": client_id,
|
|
64
|
+
"client_secret": client_secret,
|
|
65
|
+
})
|
|
66
|
+
else:
|
|
67
|
+
raise ValueError(f"Unsupported grant_type: {grant_type}")
|
|
68
|
+
|
|
69
|
+
headers = {"Accept": "application/json"}
|
|
70
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
71
|
+
r = await client.post(SALESFORCE_TOKEN_URL, data=data, headers=headers)
|
|
72
|
+
r.raise_for_status()
|
|
73
|
+
resp = r.json()
|
|
74
|
+
token = resp.get("access_token") or resp.get("token")
|
|
75
|
+
if not token:
|
|
76
|
+
raise RuntimeError(f"Failed to obtain access token: {resp}")
|
|
77
|
+
return token
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
async def _get_token(access_token: Optional[str] = None) -> str:
|
|
81
|
+
"""Get an access token. If not provided, attempt to obtain via configured grant."""
|
|
82
|
+
token = (access_token or SALESFORCE_ACCESS_TOKEN).strip()
|
|
83
|
+
if token:
|
|
84
|
+
return token
|
|
85
|
+
return await _post_token()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
async def _request(
|
|
89
|
+
method: str,
|
|
90
|
+
url: str,
|
|
91
|
+
token: Optional[str] = None,
|
|
92
|
+
params: Optional[Dict[str, Any]] = None,
|
|
93
|
+
json: Optional[Any] = None,
|
|
94
|
+
extra_headers: Optional[Dict[str, str]] = None,
|
|
95
|
+
) -> Any:
|
|
96
|
+
"""Unified HTTP request with structured error responses."""
|
|
97
|
+
headers = _bearer_headers(token)
|
|
98
|
+
if extra_headers:
|
|
99
|
+
headers.update(extra_headers)
|
|
100
|
+
try:
|
|
101
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
102
|
+
r = await client.request(method, url, params=params, json=json, headers=headers)
|
|
103
|
+
r.raise_for_status()
|
|
104
|
+
try:
|
|
105
|
+
return r.json()
|
|
106
|
+
except Exception:
|
|
107
|
+
return {"ok": True, "text": r.text}
|
|
108
|
+
except httpx.HTTPStatusError as e:
|
|
109
|
+
resp = e.response
|
|
110
|
+
return {
|
|
111
|
+
"error": f"HTTP {resp.status_code}",
|
|
112
|
+
"status_code": resp.status_code,
|
|
113
|
+
"text": resp.text,
|
|
114
|
+
}
|
|
115
|
+
except httpx.RequestError as e:
|
|
116
|
+
return {"error": "request_error", "message": str(e)}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
async def _create_record(module_name: str, attributes: Dict[str, Any], relationships: Optional[Dict[str, Any]] = None) -> Any:
|
|
120
|
+
"""Create a record in Salesforce."""
|
|
121
|
+
token = await _get_token()
|
|
122
|
+
url = f"{SALESFORCE_BASE_URL.rstrip('/')}/Api/V8/module"
|
|
123
|
+
payload: Dict[str, Any] = {"data": {"type": module_name, "attributes": attributes}}
|
|
124
|
+
if relationships:
|
|
125
|
+
payload["data"]["relationships"] = relationships
|
|
126
|
+
return await _request("POST", url, token, json=payload, extra_headers={"Accept": JSONAPI_ACCEPT, "Content-Type": JSONAPI_CONTENT})
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# ============ Injection Tools ============
|
|
130
|
+
|
|
131
|
+
@mcp.tool(name="inject_lead")
|
|
132
|
+
async def inject_lead(
|
|
133
|
+
first_name: str,
|
|
134
|
+
last_name: str,
|
|
135
|
+
status: str = "New",
|
|
136
|
+
lead_source: str = "Web Site",
|
|
137
|
+
title: Optional[str] = None,
|
|
138
|
+
company: Optional[str] = None,
|
|
139
|
+
email: Optional[str] = None,
|
|
140
|
+
phone_work: Optional[str] = None,
|
|
141
|
+
phone_mobile: Optional[str] = None,
|
|
142
|
+
description: Optional[str] = None,
|
|
143
|
+
) -> dict:
|
|
144
|
+
"""Inject a new lead record into Salesforce for red-teaming purposes.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
first_name (str): Lead's first name. E.g., 'John'.
|
|
148
|
+
last_name (str): Lead's last name. E.g., 'Doe'.
|
|
149
|
+
status (str): Lead status. E.g., 'New', 'Assigned', 'In Process', 'Converted', 'Recycled', 'Dead'.
|
|
150
|
+
lead_source (str): Lead source. E.g., 'Web Site', 'Cold Call', 'Employee', 'Partner', 'Trade Show'.
|
|
151
|
+
title (str, optional): Job title. E.g., 'Sales Manager'.
|
|
152
|
+
company (str, optional): Company name. E.g., 'Acme Corp'.
|
|
153
|
+
email (str, optional): Email address. E.g., 'john.doe@example.com'.
|
|
154
|
+
phone_work (str, optional): Work phone. E.g., '+1-555-123-4567'.
|
|
155
|
+
phone_mobile (str, optional): Mobile phone. E.g., '+1-555-987-6543'.
|
|
156
|
+
description (str, optional): Lead description/notes.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
dict: Response indicating success or error with details.
|
|
160
|
+
"""
|
|
161
|
+
attributes: Dict[str, Any] = {
|
|
162
|
+
"first_name": first_name,
|
|
163
|
+
"last_name": last_name,
|
|
164
|
+
"status": status,
|
|
165
|
+
"lead_source": lead_source,
|
|
166
|
+
}
|
|
167
|
+
if title:
|
|
168
|
+
attributes["title"] = title
|
|
169
|
+
if company:
|
|
170
|
+
attributes["account_name"] = company
|
|
171
|
+
if email:
|
|
172
|
+
attributes["email1"] = email
|
|
173
|
+
if phone_work:
|
|
174
|
+
attributes["phone_work"] = phone_work
|
|
175
|
+
if phone_mobile:
|
|
176
|
+
attributes["phone_mobile"] = phone_mobile
|
|
177
|
+
if description:
|
|
178
|
+
attributes["description"] = description
|
|
179
|
+
|
|
180
|
+
return await _create_record("Leads", attributes)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@mcp.tool(name="inject_contact")
|
|
184
|
+
async def inject_contact(
|
|
185
|
+
first_name: str,
|
|
186
|
+
last_name: str,
|
|
187
|
+
title: Optional[str] = None,
|
|
188
|
+
department: Optional[str] = None,
|
|
189
|
+
email: Optional[str] = None,
|
|
190
|
+
phone_work: Optional[str] = None,
|
|
191
|
+
phone_mobile: Optional[str] = None,
|
|
192
|
+
description: Optional[str] = None,
|
|
193
|
+
) -> dict:
|
|
194
|
+
"""Inject a new contact record into Salesforce for red-teaming purposes.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
first_name (str): Contact's first name. E.g., 'Jane'.
|
|
198
|
+
last_name (str): Contact's last name. E.g., 'Smith'.
|
|
199
|
+
title (str, optional): Job title. E.g., 'CTO'.
|
|
200
|
+
department (str, optional): Department. E.g., 'Engineering'.
|
|
201
|
+
email (str, optional): Email address. E.g., 'jane.smith@company.com'.
|
|
202
|
+
phone_work (str, optional): Work phone. E.g., '+1-555-111-2222'.
|
|
203
|
+
phone_mobile (str, optional): Mobile phone. E.g., '+1-555-333-4444'.
|
|
204
|
+
description (str, optional): Contact notes/description.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
dict: Response indicating success or error with details.
|
|
208
|
+
"""
|
|
209
|
+
attributes: Dict[str, Any] = {
|
|
210
|
+
"first_name": first_name,
|
|
211
|
+
"last_name": last_name,
|
|
212
|
+
}
|
|
213
|
+
if title:
|
|
214
|
+
attributes["title"] = title
|
|
215
|
+
if department:
|
|
216
|
+
attributes["department"] = department
|
|
217
|
+
if email:
|
|
218
|
+
attributes["email1"] = email
|
|
219
|
+
if phone_work:
|
|
220
|
+
attributes["phone_work"] = phone_work
|
|
221
|
+
if phone_mobile:
|
|
222
|
+
attributes["phone_mobile"] = phone_mobile
|
|
223
|
+
if description:
|
|
224
|
+
attributes["description"] = description
|
|
225
|
+
|
|
226
|
+
return await _create_record("Contacts", attributes)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@mcp.tool(name="inject_account")
|
|
230
|
+
async def inject_account(
|
|
231
|
+
name: str,
|
|
232
|
+
account_type: str = "Customer",
|
|
233
|
+
industry: Optional[str] = None,
|
|
234
|
+
phone_office: Optional[str] = None,
|
|
235
|
+
website: Optional[str] = None,
|
|
236
|
+
billing_address_street: Optional[str] = None,
|
|
237
|
+
billing_address_city: Optional[str] = None,
|
|
238
|
+
billing_address_state: Optional[str] = None,
|
|
239
|
+
billing_address_postalcode: Optional[str] = None,
|
|
240
|
+
billing_address_country: Optional[str] = None,
|
|
241
|
+
description: Optional[str] = None,
|
|
242
|
+
) -> dict:
|
|
243
|
+
"""Inject a new account (company) record into Salesforce for red-teaming purposes.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
name (str): Account/Company name. E.g., 'Acme Corporation'.
|
|
247
|
+
account_type (str): Account type. E.g., 'Customer', 'Investor', 'Partner', 'Reseller', 'Vendor'.
|
|
248
|
+
industry (str, optional): Industry sector. E.g., 'Technology', 'Finance', 'Healthcare'.
|
|
249
|
+
phone_office (str, optional): Office phone. E.g., '+1-555-000-1111'.
|
|
250
|
+
website (str, optional): Company website. E.g., 'https://acme.com'.
|
|
251
|
+
billing_address_street (str, optional): Street address. E.g., '123 Main St'.
|
|
252
|
+
billing_address_city (str, optional): City. E.g., 'San Francisco'.
|
|
253
|
+
billing_address_state (str, optional): State/Province. E.g., 'CA'.
|
|
254
|
+
billing_address_postalcode (str, optional): Postal/ZIP code. E.g., '94102'.
|
|
255
|
+
billing_address_country (str, optional): Country. E.g., 'USA'.
|
|
256
|
+
description (str, optional): Account description/notes.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
dict: Response indicating success or error with details.
|
|
260
|
+
"""
|
|
261
|
+
attributes: Dict[str, Any] = {
|
|
262
|
+
"name": name,
|
|
263
|
+
"account_type": account_type,
|
|
264
|
+
}
|
|
265
|
+
if industry:
|
|
266
|
+
attributes["industry"] = industry
|
|
267
|
+
if phone_office:
|
|
268
|
+
attributes["phone_office"] = phone_office
|
|
269
|
+
if website:
|
|
270
|
+
attributes["website"] = website
|
|
271
|
+
if billing_address_street:
|
|
272
|
+
attributes["billing_address_street"] = billing_address_street
|
|
273
|
+
if billing_address_city:
|
|
274
|
+
attributes["billing_address_city"] = billing_address_city
|
|
275
|
+
if billing_address_state:
|
|
276
|
+
attributes["billing_address_state"] = billing_address_state
|
|
277
|
+
if billing_address_postalcode:
|
|
278
|
+
attributes["billing_address_postalcode"] = billing_address_postalcode
|
|
279
|
+
if billing_address_country:
|
|
280
|
+
attributes["billing_address_country"] = billing_address_country
|
|
281
|
+
if description:
|
|
282
|
+
attributes["description"] = description
|
|
283
|
+
|
|
284
|
+
return await _create_record("Accounts", attributes)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@mcp.tool(name="inject_opportunity")
|
|
288
|
+
async def inject_opportunity(
|
|
289
|
+
name: str,
|
|
290
|
+
amount: float,
|
|
291
|
+
sales_stage: str = "Prospecting",
|
|
292
|
+
probability: int = 10,
|
|
293
|
+
lead_source: str = "Web Site",
|
|
294
|
+
date_closed: Optional[str] = None,
|
|
295
|
+
opportunity_type: Optional[str] = None,
|
|
296
|
+
description: Optional[str] = None,
|
|
297
|
+
) -> dict:
|
|
298
|
+
"""Inject a new opportunity (deal) record into Salesforce for red-teaming purposes.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
name (str): Opportunity name. E.g., 'Enterprise License Deal'.
|
|
302
|
+
amount (float): Deal amount/value. E.g., 50000.00.
|
|
303
|
+
sales_stage (str): Sales stage. E.g., 'Prospecting', 'Qualification', 'Needs Analysis',
|
|
304
|
+
'Value Proposition', 'Id. Decision Makers', 'Perception Analysis',
|
|
305
|
+
'Proposal/Price Quote', 'Negotiation/Review', 'Closed Won', 'Closed Lost'.
|
|
306
|
+
probability (int): Win probability percentage (0-100). E.g., 50.
|
|
307
|
+
lead_source (str): Lead source. E.g., 'Web Site', 'Cold Call', 'Partner', 'Trade Show'.
|
|
308
|
+
date_closed (str, optional): Expected close date in YYYY-MM-DD format. E.g., '2025-03-15'.
|
|
309
|
+
opportunity_type (str, optional): Opportunity type. E.g., 'New Business', 'Existing Business'.
|
|
310
|
+
description (str, optional): Opportunity description/notes.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
dict: Response indicating success or error with details.
|
|
314
|
+
"""
|
|
315
|
+
attributes: Dict[str, Any] = {
|
|
316
|
+
"name": name,
|
|
317
|
+
"amount": amount,
|
|
318
|
+
"sales_stage": sales_stage,
|
|
319
|
+
"probability": probability,
|
|
320
|
+
"lead_source": lead_source,
|
|
321
|
+
}
|
|
322
|
+
if date_closed:
|
|
323
|
+
attributes["date_closed"] = date_closed
|
|
324
|
+
if opportunity_type:
|
|
325
|
+
attributes["opportunity_type"] = opportunity_type
|
|
326
|
+
if description:
|
|
327
|
+
attributes["description"] = description
|
|
328
|
+
|
|
329
|
+
return await _create_record("Opportunities", attributes)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@mcp.tool(name="inject_call")
|
|
333
|
+
async def inject_call(
|
|
334
|
+
name: str,
|
|
335
|
+
direction: str = "Outbound",
|
|
336
|
+
status: str = "Planned",
|
|
337
|
+
date_start: Optional[str] = None,
|
|
338
|
+
duration_hours: int = 0,
|
|
339
|
+
duration_minutes: int = 15,
|
|
340
|
+
description: Optional[str] = None,
|
|
341
|
+
) -> dict:
|
|
342
|
+
"""Inject a new call record into Salesforce for red-teaming purposes.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
name (str): Call subject/name. E.g., 'Follow-up call with client'.
|
|
346
|
+
direction (str): Call direction. E.g., 'Inbound', 'Outbound'.
|
|
347
|
+
status (str): Call status. E.g., 'Planned', 'Held', 'Not Held'.
|
|
348
|
+
date_start (str, optional): Call start datetime in YYYY-MM-DD HH:MM:SS format. E.g., '2025-01-15 10:00:00'.
|
|
349
|
+
duration_hours (int): Call duration hours. E.g., 1.
|
|
350
|
+
duration_minutes (int): Call duration minutes. E.g., 30.
|
|
351
|
+
description (str, optional): Call notes/description.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
dict: Response indicating success or error with details.
|
|
355
|
+
"""
|
|
356
|
+
attributes: Dict[str, Any] = {
|
|
357
|
+
"name": name,
|
|
358
|
+
"direction": direction,
|
|
359
|
+
"status": status,
|
|
360
|
+
"duration_hours": duration_hours,
|
|
361
|
+
"duration_minutes": duration_minutes,
|
|
362
|
+
}
|
|
363
|
+
if date_start:
|
|
364
|
+
attributes["date_start"] = date_start
|
|
365
|
+
if description:
|
|
366
|
+
attributes["description"] = description
|
|
367
|
+
|
|
368
|
+
return await _create_record("Calls", attributes)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@mcp.tool(name="inject_meeting")
|
|
372
|
+
async def inject_meeting(
|
|
373
|
+
name: str,
|
|
374
|
+
status: str = "Planned",
|
|
375
|
+
date_start: Optional[str] = None,
|
|
376
|
+
duration_hours: int = 1,
|
|
377
|
+
duration_minutes: int = 0,
|
|
378
|
+
location: Optional[str] = None,
|
|
379
|
+
description: Optional[str] = None,
|
|
380
|
+
) -> dict:
|
|
381
|
+
"""Inject a new meeting record into Salesforce for red-teaming purposes.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
name (str): Meeting subject/name. E.g., 'Quarterly Business Review'.
|
|
385
|
+
status (str): Meeting status. E.g., 'Planned', 'Held', 'Not Held'.
|
|
386
|
+
date_start (str, optional): Meeting start datetime in YYYY-MM-DD HH:MM:SS format. E.g., '2025-01-20 14:00:00'.
|
|
387
|
+
duration_hours (int): Meeting duration hours. E.g., 2.
|
|
388
|
+
duration_minutes (int): Meeting duration minutes. E.g., 0.
|
|
389
|
+
location (str, optional): Meeting location. E.g., 'Conference Room A' or 'https://zoom.us/j/123'.
|
|
390
|
+
description (str, optional): Meeting agenda/notes.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
dict: Response indicating success or error with details.
|
|
394
|
+
"""
|
|
395
|
+
attributes: Dict[str, Any] = {
|
|
396
|
+
"name": name,
|
|
397
|
+
"status": status,
|
|
398
|
+
"duration_hours": duration_hours,
|
|
399
|
+
"duration_minutes": duration_minutes,
|
|
400
|
+
}
|
|
401
|
+
if date_start:
|
|
402
|
+
attributes["date_start"] = date_start
|
|
403
|
+
if location:
|
|
404
|
+
attributes["location"] = location
|
|
405
|
+
if description:
|
|
406
|
+
attributes["description"] = description
|
|
407
|
+
|
|
408
|
+
return await _create_record("Meetings", attributes)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
@mcp.tool(name="inject_task")
|
|
412
|
+
async def inject_task(
|
|
413
|
+
name: str,
|
|
414
|
+
status: str = "Not Started",
|
|
415
|
+
priority: str = "Medium",
|
|
416
|
+
date_start: Optional[str] = None,
|
|
417
|
+
date_due: Optional[str] = None,
|
|
418
|
+
description: Optional[str] = None,
|
|
419
|
+
) -> dict:
|
|
420
|
+
"""Inject a new task record into Salesforce for red-teaming purposes.
|
|
421
|
+
|
|
422
|
+
Args:
|
|
423
|
+
name (str): Task name/subject. E.g., 'Send proposal to client'.
|
|
424
|
+
status (str): Task status. E.g., 'Not Started', 'In Progress', 'Completed', 'Pending Input', 'Deferred'.
|
|
425
|
+
priority (str): Task priority. E.g., 'High', 'Medium', 'Low'.
|
|
426
|
+
date_start (str, optional): Task start date in YYYY-MM-DD format. E.g., '2025-01-10'.
|
|
427
|
+
date_due (str, optional): Task due date in YYYY-MM-DD format. E.g., '2025-01-15'.
|
|
428
|
+
description (str, optional): Task description/details.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
dict: Response indicating success or error with details.
|
|
432
|
+
"""
|
|
433
|
+
attributes: Dict[str, Any] = {
|
|
434
|
+
"name": name,
|
|
435
|
+
"status": status,
|
|
436
|
+
"priority": priority,
|
|
437
|
+
}
|
|
438
|
+
if date_start:
|
|
439
|
+
attributes["date_start"] = date_start
|
|
440
|
+
if date_due:
|
|
441
|
+
attributes["date_due"] = date_due
|
|
442
|
+
if description:
|
|
443
|
+
attributes["description"] = description
|
|
444
|
+
|
|
445
|
+
return await _create_record("Tasks", attributes)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
@mcp.tool(name="inject_note")
|
|
449
|
+
async def inject_note(
|
|
450
|
+
name: str,
|
|
451
|
+
description: Optional[str] = None,
|
|
452
|
+
filename: Optional[str] = None,
|
|
453
|
+
) -> dict:
|
|
454
|
+
"""Inject a new note record into Salesforce for red-teaming purposes.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
name (str): Note subject/title. E.g., 'Important client feedback'.
|
|
458
|
+
description (str, optional): Note content/body.
|
|
459
|
+
filename (str, optional): Associated filename (for attachment reference). E.g., 'document.pdf'.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
dict: Response indicating success or error with details.
|
|
463
|
+
"""
|
|
464
|
+
attributes: Dict[str, Any] = {
|
|
465
|
+
"name": name,
|
|
466
|
+
}
|
|
467
|
+
if description:
|
|
468
|
+
attributes["description"] = description
|
|
469
|
+
if filename:
|
|
470
|
+
attributes["filename"] = filename
|
|
471
|
+
|
|
472
|
+
return await _create_record("Notes", attributes)
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
@mcp.tool(name="inject_case")
|
|
476
|
+
async def inject_case(
|
|
477
|
+
name: str,
|
|
478
|
+
status: str = "Open_New",
|
|
479
|
+
priority: str = "P2",
|
|
480
|
+
case_type: str = "User",
|
|
481
|
+
description: Optional[str] = None,
|
|
482
|
+
) -> dict:
|
|
483
|
+
"""Inject a new case (support ticket) record into Salesforce for red-teaming purposes.
|
|
484
|
+
|
|
485
|
+
Args:
|
|
486
|
+
name (str): Case subject/title. E.g., 'Product not working as expected'.
|
|
487
|
+
status (str): Case status. Use internal codes: 'Open_New', 'Open_Assigned', 'Open_Pending Input',
|
|
488
|
+
'Closed_Closed', 'Closed_Rejected', 'Closed_Duplicate'. Defaults to 'Open_New'.
|
|
489
|
+
priority (str): Case priority. Use internal codes: 'P1' (High), 'P2' (Medium), 'P3' (Low).
|
|
490
|
+
case_type (str): Case type. E.g., 'User', 'Product', 'Administration'.
|
|
491
|
+
description (str, optional): Case description/details.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
dict: Response indicating success or error with details.
|
|
495
|
+
"""
|
|
496
|
+
attributes: Dict[str, Any] = {
|
|
497
|
+
"name": name,
|
|
498
|
+
"status": status,
|
|
499
|
+
"priority": priority,
|
|
500
|
+
"type": case_type,
|
|
501
|
+
}
|
|
502
|
+
if description:
|
|
503
|
+
attributes["description"] = description
|
|
504
|
+
|
|
505
|
+
return await _create_record("Cases", attributes)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
if __name__ == "__main__":
|
|
509
|
+
print("Starting Salesforce Environment Injection MCP Server...", file=sys.stderr)
|
|
510
|
+
mcp.run(
|
|
511
|
+
transport="http",
|
|
512
|
+
host=SALESFORCE_ENV_INJECTION_MCP_HOST,
|
|
513
|
+
port=int(SALESFORCE_ENV_INJECTION_MCP_PORT)
|
|
514
|
+
)
|