salesforce-agent 0.2.0__tar.gz

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 (45) hide show
  1. salesforce_agent-0.2.0/LICENSE +21 -0
  2. salesforce_agent-0.2.0/MANIFEST.in +4 -0
  3. salesforce_agent-0.2.0/PKG-INFO +215 -0
  4. salesforce_agent-0.2.0/README.md +183 -0
  5. salesforce_agent-0.2.0/pyproject.toml +58 -0
  6. salesforce_agent-0.2.0/requirements.txt +2 -0
  7. salesforce_agent-0.2.0/salesforce_agent/__init__.py +69 -0
  8. salesforce_agent-0.2.0/salesforce_agent/__main__.py +4 -0
  9. salesforce_agent-0.2.0/salesforce_agent/agent_server.py +77 -0
  10. salesforce_agent-0.2.0/salesforce_agent/api/__init__.py +17 -0
  11. salesforce_agent-0.2.0/salesforce_agent/api/api_client_admin.py +58 -0
  12. salesforce_agent-0.2.0/salesforce_agent/api/api_client_base.py +119 -0
  13. salesforce_agent-0.2.0/salesforce_agent/api/api_client_bulk.py +145 -0
  14. salesforce_agent-0.2.0/salesforce_agent/api/api_client_describe.py +40 -0
  15. salesforce_agent-0.2.0/salesforce_agent/api/api_client_query.py +68 -0
  16. salesforce_agent-0.2.0/salesforce_agent/api/api_client_records.py +164 -0
  17. salesforce_agent-0.2.0/salesforce_agent/api_client.py +47 -0
  18. salesforce_agent-0.2.0/salesforce_agent/auth.py +321 -0
  19. salesforce_agent-0.2.0/salesforce_agent/main_agent.json +14 -0
  20. salesforce_agent-0.2.0/salesforce_agent/mcp/__init__.py +19 -0
  21. salesforce_agent-0.2.0/salesforce_agent/mcp/mcp_salesforce.py +259 -0
  22. salesforce_agent-0.2.0/salesforce_agent/mcp_config.json +3 -0
  23. salesforce_agent-0.2.0/salesforce_agent/mcp_server.py +75 -0
  24. salesforce_agent-0.2.0/salesforce_agent/salesforce_input_models.py +161 -0
  25. salesforce_agent-0.2.0/salesforce_agent/salesforce_response_models.py +150 -0
  26. salesforce_agent-0.2.0/salesforce_agent.egg-info/PKG-INFO +215 -0
  27. salesforce_agent-0.2.0/salesforce_agent.egg-info/SOURCES.txt +43 -0
  28. salesforce_agent-0.2.0/salesforce_agent.egg-info/dependency_links.txt +1 -0
  29. salesforce_agent-0.2.0/salesforce_agent.egg-info/entry_points.txt +3 -0
  30. salesforce_agent-0.2.0/salesforce_agent.egg-info/requires.txt +21 -0
  31. salesforce_agent-0.2.0/salesforce_agent.egg-info/top_level.txt +1 -0
  32. salesforce_agent-0.2.0/setup.cfg +4 -0
  33. salesforce_agent-0.2.0/tests/test_admin.py +28 -0
  34. salesforce_agent-0.2.0/tests/test_api_wrapper.py +36 -0
  35. salesforce_agent-0.2.0/tests/test_auth.py +338 -0
  36. salesforce_agent-0.2.0/tests/test_bulk.py +133 -0
  37. salesforce_agent-0.2.0/tests/test_concept_parity.py +37 -0
  38. salesforce_agent-0.2.0/tests/test_describe.py +35 -0
  39. salesforce_agent-0.2.0/tests/test_errors.py +134 -0
  40. salesforce_agent-0.2.0/tests/test_init_dynamics.py +18 -0
  41. salesforce_agent-0.2.0/tests/test_query.py +82 -0
  42. salesforce_agent-0.2.0/tests/test_records.py +155 -0
  43. salesforce_agent-0.2.0/tests/test_salesforce_mcp_validation.py +273 -0
  44. salesforce_agent-0.2.0/tests/test_salesforce_models.py +62 -0
  45. salesforce_agent-0.2.0/tests/test_startup.py +33 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Advanced Agentic Coding
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ include LICENSE
2
+ include README.md
3
+ include requirements.txt
4
+ recursive-include salesforce_agent *.py *.json
@@ -0,0 +1,215 @@
1
+ Metadata-Version: 2.4
2
+ Name: salesforce-agent
3
+ Version: 0.2.0
4
+ Summary: Salesforce REST/SOQL/Bulk API 2.0 connector + MCP Server + A2A Server for Agentic AI!
5
+ Author-email: Audel Rouhi <knucklessg1@gmail.com>
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Environment :: Console
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: <3.15,>=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: agent-utilities>=0.48.0
16
+ Requires-Dist: httpx>=0.27.0
17
+ Provides-Extra: mcp
18
+ Requires-Dist: agent-utilities[mcp]>=0.48.0; extra == "mcp"
19
+ Provides-Extra: agent
20
+ Requires-Dist: agent-utilities[agent,logfire]>=0.48.0; extra == "agent"
21
+ Provides-Extra: jwt
22
+ Requires-Dist: cryptography>=42.0.0; extra == "jwt"
23
+ Provides-Extra: all
24
+ Requires-Dist: salesforce-agent[agent,jwt,logfire,mcp]>=0.2.0; extra == "all"
25
+ Provides-Extra: test
26
+ Requires-Dist: pytest-xdist>=3.6.0; extra == "test"
27
+ Requires-Dist: pytest; extra == "test"
28
+ Requires-Dist: pytest-asyncio; extra == "test"
29
+ Requires-Dist: pytest-cov; extra == "test"
30
+ Requires-Dist: cryptography>=42.0.0; extra == "test"
31
+ Dynamic: license-file
32
+
33
+ # Salesforce Agent
34
+ ## CLI or API | MCP | Agent
35
+
36
+ ![PyPI - Version](https://img.shields.io/pypi/v/salesforce-agent)
37
+ ![MCP Server](https://badge.mcpx.dev?type=server 'MCP Server')
38
+ ![PyPI - Downloads](https://img.shields.io/pypi/dd/salesforce-agent)
39
+ ![GitHub Repo stars](https://img.shields.io/github/stars/Knuckles-Team/salesforce-agent)
40
+ ![PyPI - License](https://img.shields.io/pypi/l/salesforce-agent)
41
+ ![GitHub last commit (by committer)](https://img.shields.io/github/last-commit/Knuckles-Team/salesforce-agent)
42
+ ![PyPI - Wheel](https://img.shields.io/pypi/wheel/salesforce-agent)
43
+
44
+ *Version: 0.2.0*
45
+
46
+ > **Documentation** — Installation, deployment, usage across the API, CLI, and MCP
47
+ > server live on the docs site:
48
+ > <https://knuckles-team.github.io/salesforce-agent/>
49
+
50
+ ## Table of Contents
51
+
52
+ - [Overview](#overview)
53
+ - [Architecture](#architecture)
54
+ - [Installation](#installation)
55
+ - [MCP Tools](#mcp-tools)
56
+ - [Auth Flows](#auth-flows)
57
+ - [Environment Variables](#environment-variables)
58
+ - [Quick Start](#quick-start)
59
+ - [Deployment](#deployment)
60
+ - [Development](#development)
61
+ - [License](#license)
62
+
63
+ ## Overview
64
+
65
+ **The Salesforce connector for the agent-utilities fleet** — an owned thin
66
+ httpx wrapper over the Salesforce REST API exposed as a FastMCP server and an
67
+ A2A agent. REST + SOQL/SOSL + Bulk API 2.0 + metadata describe, with safety
68
+ gates designed for autonomous agents.
69
+
70
+ No `simple-salesforce`: every endpoint is a documented thin call with its
71
+ Salesforce API doc URL cited in the docstring.
72
+
73
+ ## Architecture
74
+
75
+ ```mermaid
76
+ graph TD
77
+ User([User/A2A]) --> Server[A2A Server / salesforce-agent]
78
+ Server --> Agent[Pydantic AI Agent]
79
+ Agent --> MCP[MCP Server / salesforce-mcp]
80
+ MCP --> Client[Api facade / httpx]
81
+ Client --> ExternalAPI([Salesforce REST API])
82
+ ```
83
+
84
+ ## Installation
85
+
86
+ ```bash
87
+ pip install salesforce-agent # core client only
88
+ pip install salesforce-agent[mcp] # + FastMCP server
89
+ pip install salesforce-agent[agent] # + Pydantic AI A2A agent server
90
+ pip install salesforce-agent[jwt] # + cryptography for the JWT bearer flow
91
+ pip install salesforce-agent[all] # everything
92
+ ```
93
+
94
+ Prebuilt Docker image: `knucklessg1/salesforce-agent:latest`.
95
+
96
+ ## MCP Tools
97
+
98
+ Five consolidated, action-routed tools. Each takes `action` and `params_json`.
99
+
100
+ | Tool | Actions | Toggle |
101
+ |------|---------|--------|
102
+ | `salesforce_soql` | `query` (auto-pagination via `nextRecordsUrl`, capped), `query_all` (deleted/archived), `explain`, `search` (SOSL) | `SOQLTOOL` |
103
+ | `salesforce_records` | `get` (field selection), `create`, `update`, `upsert` (external id), `delete`*, `composite` (≤25 subrequests), `collections_create`/`collections_update` (≤200 records), `collections_delete`* | `RECORDSTOOL` |
104
+ | `salesforce_describe` | `global`, `sobject` (fields/relationships/picklists), `record_counts`, `limits` (API usage) | `DESCRIBETOOL` |
105
+ | `salesforce_bulk` | `create_job` (insert/update/upsert/`delete`*/`hardDelete`*), `upload` (CSV), `close`, `abort`, `status`, `list_jobs`, `delete_job`, `results` (successful/failed/unprocessed, size-capped) | `BULKTOOL` |
106
+ | `salesforce_admin` | `user_info`, `org_info`, `list_reports`, `run_report` (sync, capped). Listing/running Flows is **out of scope for v1**. | `ADMINTOOL` |
107
+
108
+ `*` Destructive — blocked unless `SALESFORCE_ALLOW_DESTRUCTIVE=true`.
109
+
110
+ ## Auth Flows
111
+
112
+ | Flow | Credentials | Notes |
113
+ |------|-------------|-------|
114
+ | OAuth2 client-credentials | consumer key + secret + My Domain URL | default server-to-server flow |
115
+ | OAuth2 refresh-token | refresh token + consumer key | instance URL from token response |
116
+ | OAuth2 JWT bearer | consumer key + username + RSA key | `pip install salesforce-agent[jwt]` |
117
+ | Static access token | token + instance URL | testing / externally managed sessions |
118
+
119
+ Sandbox orgs: `SALESFORCE_SANDBOX=true` (`test.salesforce.com`). Tokens are
120
+ cached with expiry tracking and refreshed transparently (plus one retry on
121
+ 401); secrets are redacted from all errors and logs.
122
+
123
+ ## Environment Variables
124
+
125
+ | Variable | Default | Purpose |
126
+ |----------|---------|---------|
127
+ | `SALESFORCE_INSTANCE_URL` | — | My Domain instance URL (required for client-credentials and static tokens) |
128
+ | `SALESFORCE_LOGIN_URL` | derived | Override the OAuth login host |
129
+ | `SALESFORCE_SANDBOX` | `False` | Sandbox org (`test.salesforce.com`) |
130
+ | `SALESFORCE_API_VERSION` | `v62.0` | REST API version |
131
+ | `SALESFORCE_AUTH_FLOW` | auto | `client_credentials` / `refresh_token` / `jwt_bearer` / `access_token` |
132
+ | `SALESFORCE_CLIENT_ID` / `SALESFORCE_CLIENT_SECRET` | — | Connected App consumer key/secret |
133
+ | `SALESFORCE_REFRESH_TOKEN` | — | Refresh-token flow credential |
134
+ | `SALESFORCE_JWT_SUBJECT` / `SALESFORCE_JWT_PRIVATE_KEY[_PATH]` / `SALESFORCE_JWT_AUDIENCE` | — | JWT bearer flow |
135
+ | `SALESFORCE_ACCESS_TOKEN` | — | Static access token (testing) |
136
+ | `SALESFORCE_TOKEN_TTL_SECONDS` | `1800` | Cached-token TTL fallback |
137
+ | `SALESFORCE_SSL_VERIFY` | `True` | TLS verification |
138
+ | `SALESFORCE_TIMEOUT` | `30` | HTTP timeout (seconds) |
139
+ | `SALESFORCE_ALLOW_DESTRUCTIVE` | `False` | Gate for all delete paths |
140
+ | `SALESFORCE_MAX_QUERY_RECORDS` | `2000` | Per-call SOQL pagination cap |
141
+ | `SALESFORCE_BULK_RESULTS_MAX_BYTES` | `5000000` | Bulk result download cap |
142
+ | `SALESFORCE_REPORT_MAX_ROWS` | `2000` | Sync report detail-row note (platform cap) |
143
+ | `HOST` / `PORT` / `TRANSPORT` | `0.0.0.0` / `8000` / `stdio` | MCP server bind + transport |
144
+ | `SOQLTOOL` / `RECORDSTOOL` / `DESCRIBETOOL` / `BULKTOOL` / `ADMINTOOL` | `True` | Per-domain tool toggles |
145
+ | `ENABLE_OTEL` / `OTEL_EXPORTER_OTLP_*` | — | Telemetry (OTEL / Langfuse) |
146
+ | `EUNOMIA_TYPE` / `EUNOMIA_POLICY_FILE` / `EUNOMIA_REMOTE_URL` | `none` | MCP authorization middleware |
147
+ | `AUTH_TYPE` | `none` | MCP server auth mode (Docker) |
148
+
149
+ See `.env.example` for the full annotated list.
150
+
151
+ ## Quick Start
152
+
153
+ ```bash
154
+ pip install salesforce-agent[all]
155
+ cp .env.example .env # fill in one auth flow
156
+ salesforce-mcp # stdio MCP server
157
+ ```
158
+
159
+ ```python
160
+ from salesforce_agent import Api
161
+
162
+ api = Api() # configured from SALESFORCE_* env vars
163
+ rows = api.soql.query("SELECT Id, Name FROM Account", max_records=200)
164
+ api.records.upsert("Account", "External_Id__c", "X-1", {"Name": "Acme"})
165
+ ```
166
+
167
+ Typed tool-input contracts live in
168
+ `salesforce_agent.salesforce_input_models`; typed error envelopes in
169
+ `salesforce_agent.salesforce_response_models`.
170
+
171
+ ## Deployment
172
+
173
+ ```bash
174
+ # MCP server only (port 8000, streamable-http, /health)
175
+ docker compose -f docker/mcp.compose.yml up -d
176
+
177
+ # MCP server + A2A agent server (agent on port 9020, AG-UI web interface)
178
+ docker compose -f docker/agent.compose.yml up -d
179
+ ```
180
+
181
+ The A2A agent server (`salesforce-agent` console script, `agent_server.py`)
182
+ reads `MCP_URL`, `PROVIDER`, and `MODEL_ID` from the environment. See
183
+ [docs/deployment.md](docs/deployment.md) for transports, reverse proxy, and
184
+ DNS guidance.
185
+
186
+ See [docs/](docs/index.md) for the full overview, installation, usage, and
187
+ deployment guides; concept registry in [docs/concepts.md](docs/concepts.md)
188
+ (`CONCEPT:SFDC-1.x`).
189
+
190
+ <!-- BEGIN GENERATED: additional-deployment-options -->
191
+ ### Additional Deployment Options
192
+
193
+ `salesforce-agent` can also run as a **local container** (Docker / Podman / `uv`) or be
194
+ consumed from a **remote deployment**. The
195
+ [Deployment guide](https://knuckles-team.github.io/salesforce-agent/deployment/) has full, copy-paste
196
+ `mcp_config.json` for all four transports — **stdio**, **streamable-http**,
197
+ **local container / uv**, and **remote URL**:
198
+
199
+ - **Local container / uv** — launch the server from `mcp_config.json` via `uvx`,
200
+ `docker run`, or `podman run`, or point at a local streamable-http container by `url`.
201
+ - **Remote URL** — connect to a server deployed behind Caddy at
202
+ `http://salesforce-mcp.arpa/mcp` using the `"url"` key.
203
+ <!-- END GENERATED: additional-deployment-options -->
204
+
205
+ ## Development
206
+
207
+ ```bash
208
+ pip install -e .[all,test]
209
+ pytest # mocked httpx suite — no live org required
210
+ pre-commit run --all-files # must be fully green before committing
211
+ ```
212
+
213
+ ## License
214
+
215
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,183 @@
1
+ # Salesforce Agent
2
+ ## CLI or API | MCP | Agent
3
+
4
+ ![PyPI - Version](https://img.shields.io/pypi/v/salesforce-agent)
5
+ ![MCP Server](https://badge.mcpx.dev?type=server 'MCP Server')
6
+ ![PyPI - Downloads](https://img.shields.io/pypi/dd/salesforce-agent)
7
+ ![GitHub Repo stars](https://img.shields.io/github/stars/Knuckles-Team/salesforce-agent)
8
+ ![PyPI - License](https://img.shields.io/pypi/l/salesforce-agent)
9
+ ![GitHub last commit (by committer)](https://img.shields.io/github/last-commit/Knuckles-Team/salesforce-agent)
10
+ ![PyPI - Wheel](https://img.shields.io/pypi/wheel/salesforce-agent)
11
+
12
+ *Version: 0.2.0*
13
+
14
+ > **Documentation** — Installation, deployment, usage across the API, CLI, and MCP
15
+ > server live on the docs site:
16
+ > <https://knuckles-team.github.io/salesforce-agent/>
17
+
18
+ ## Table of Contents
19
+
20
+ - [Overview](#overview)
21
+ - [Architecture](#architecture)
22
+ - [Installation](#installation)
23
+ - [MCP Tools](#mcp-tools)
24
+ - [Auth Flows](#auth-flows)
25
+ - [Environment Variables](#environment-variables)
26
+ - [Quick Start](#quick-start)
27
+ - [Deployment](#deployment)
28
+ - [Development](#development)
29
+ - [License](#license)
30
+
31
+ ## Overview
32
+
33
+ **The Salesforce connector for the agent-utilities fleet** — an owned thin
34
+ httpx wrapper over the Salesforce REST API exposed as a FastMCP server and an
35
+ A2A agent. REST + SOQL/SOSL + Bulk API 2.0 + metadata describe, with safety
36
+ gates designed for autonomous agents.
37
+
38
+ No `simple-salesforce`: every endpoint is a documented thin call with its
39
+ Salesforce API doc URL cited in the docstring.
40
+
41
+ ## Architecture
42
+
43
+ ```mermaid
44
+ graph TD
45
+ User([User/A2A]) --> Server[A2A Server / salesforce-agent]
46
+ Server --> Agent[Pydantic AI Agent]
47
+ Agent --> MCP[MCP Server / salesforce-mcp]
48
+ MCP --> Client[Api facade / httpx]
49
+ Client --> ExternalAPI([Salesforce REST API])
50
+ ```
51
+
52
+ ## Installation
53
+
54
+ ```bash
55
+ pip install salesforce-agent # core client only
56
+ pip install salesforce-agent[mcp] # + FastMCP server
57
+ pip install salesforce-agent[agent] # + Pydantic AI A2A agent server
58
+ pip install salesforce-agent[jwt] # + cryptography for the JWT bearer flow
59
+ pip install salesforce-agent[all] # everything
60
+ ```
61
+
62
+ Prebuilt Docker image: `knucklessg1/salesforce-agent:latest`.
63
+
64
+ ## MCP Tools
65
+
66
+ Five consolidated, action-routed tools. Each takes `action` and `params_json`.
67
+
68
+ | Tool | Actions | Toggle |
69
+ |------|---------|--------|
70
+ | `salesforce_soql` | `query` (auto-pagination via `nextRecordsUrl`, capped), `query_all` (deleted/archived), `explain`, `search` (SOSL) | `SOQLTOOL` |
71
+ | `salesforce_records` | `get` (field selection), `create`, `update`, `upsert` (external id), `delete`*, `composite` (≤25 subrequests), `collections_create`/`collections_update` (≤200 records), `collections_delete`* | `RECORDSTOOL` |
72
+ | `salesforce_describe` | `global`, `sobject` (fields/relationships/picklists), `record_counts`, `limits` (API usage) | `DESCRIBETOOL` |
73
+ | `salesforce_bulk` | `create_job` (insert/update/upsert/`delete`*/`hardDelete`*), `upload` (CSV), `close`, `abort`, `status`, `list_jobs`, `delete_job`, `results` (successful/failed/unprocessed, size-capped) | `BULKTOOL` |
74
+ | `salesforce_admin` | `user_info`, `org_info`, `list_reports`, `run_report` (sync, capped). Listing/running Flows is **out of scope for v1**. | `ADMINTOOL` |
75
+
76
+ `*` Destructive — blocked unless `SALESFORCE_ALLOW_DESTRUCTIVE=true`.
77
+
78
+ ## Auth Flows
79
+
80
+ | Flow | Credentials | Notes |
81
+ |------|-------------|-------|
82
+ | OAuth2 client-credentials | consumer key + secret + My Domain URL | default server-to-server flow |
83
+ | OAuth2 refresh-token | refresh token + consumer key | instance URL from token response |
84
+ | OAuth2 JWT bearer | consumer key + username + RSA key | `pip install salesforce-agent[jwt]` |
85
+ | Static access token | token + instance URL | testing / externally managed sessions |
86
+
87
+ Sandbox orgs: `SALESFORCE_SANDBOX=true` (`test.salesforce.com`). Tokens are
88
+ cached with expiry tracking and refreshed transparently (plus one retry on
89
+ 401); secrets are redacted from all errors and logs.
90
+
91
+ ## Environment Variables
92
+
93
+ | Variable | Default | Purpose |
94
+ |----------|---------|---------|
95
+ | `SALESFORCE_INSTANCE_URL` | — | My Domain instance URL (required for client-credentials and static tokens) |
96
+ | `SALESFORCE_LOGIN_URL` | derived | Override the OAuth login host |
97
+ | `SALESFORCE_SANDBOX` | `False` | Sandbox org (`test.salesforce.com`) |
98
+ | `SALESFORCE_API_VERSION` | `v62.0` | REST API version |
99
+ | `SALESFORCE_AUTH_FLOW` | auto | `client_credentials` / `refresh_token` / `jwt_bearer` / `access_token` |
100
+ | `SALESFORCE_CLIENT_ID` / `SALESFORCE_CLIENT_SECRET` | — | Connected App consumer key/secret |
101
+ | `SALESFORCE_REFRESH_TOKEN` | — | Refresh-token flow credential |
102
+ | `SALESFORCE_JWT_SUBJECT` / `SALESFORCE_JWT_PRIVATE_KEY[_PATH]` / `SALESFORCE_JWT_AUDIENCE` | — | JWT bearer flow |
103
+ | `SALESFORCE_ACCESS_TOKEN` | — | Static access token (testing) |
104
+ | `SALESFORCE_TOKEN_TTL_SECONDS` | `1800` | Cached-token TTL fallback |
105
+ | `SALESFORCE_SSL_VERIFY` | `True` | TLS verification |
106
+ | `SALESFORCE_TIMEOUT` | `30` | HTTP timeout (seconds) |
107
+ | `SALESFORCE_ALLOW_DESTRUCTIVE` | `False` | Gate for all delete paths |
108
+ | `SALESFORCE_MAX_QUERY_RECORDS` | `2000` | Per-call SOQL pagination cap |
109
+ | `SALESFORCE_BULK_RESULTS_MAX_BYTES` | `5000000` | Bulk result download cap |
110
+ | `SALESFORCE_REPORT_MAX_ROWS` | `2000` | Sync report detail-row note (platform cap) |
111
+ | `HOST` / `PORT` / `TRANSPORT` | `0.0.0.0` / `8000` / `stdio` | MCP server bind + transport |
112
+ | `SOQLTOOL` / `RECORDSTOOL` / `DESCRIBETOOL` / `BULKTOOL` / `ADMINTOOL` | `True` | Per-domain tool toggles |
113
+ | `ENABLE_OTEL` / `OTEL_EXPORTER_OTLP_*` | — | Telemetry (OTEL / Langfuse) |
114
+ | `EUNOMIA_TYPE` / `EUNOMIA_POLICY_FILE` / `EUNOMIA_REMOTE_URL` | `none` | MCP authorization middleware |
115
+ | `AUTH_TYPE` | `none` | MCP server auth mode (Docker) |
116
+
117
+ See `.env.example` for the full annotated list.
118
+
119
+ ## Quick Start
120
+
121
+ ```bash
122
+ pip install salesforce-agent[all]
123
+ cp .env.example .env # fill in one auth flow
124
+ salesforce-mcp # stdio MCP server
125
+ ```
126
+
127
+ ```python
128
+ from salesforce_agent import Api
129
+
130
+ api = Api() # configured from SALESFORCE_* env vars
131
+ rows = api.soql.query("SELECT Id, Name FROM Account", max_records=200)
132
+ api.records.upsert("Account", "External_Id__c", "X-1", {"Name": "Acme"})
133
+ ```
134
+
135
+ Typed tool-input contracts live in
136
+ `salesforce_agent.salesforce_input_models`; typed error envelopes in
137
+ `salesforce_agent.salesforce_response_models`.
138
+
139
+ ## Deployment
140
+
141
+ ```bash
142
+ # MCP server only (port 8000, streamable-http, /health)
143
+ docker compose -f docker/mcp.compose.yml up -d
144
+
145
+ # MCP server + A2A agent server (agent on port 9020, AG-UI web interface)
146
+ docker compose -f docker/agent.compose.yml up -d
147
+ ```
148
+
149
+ The A2A agent server (`salesforce-agent` console script, `agent_server.py`)
150
+ reads `MCP_URL`, `PROVIDER`, and `MODEL_ID` from the environment. See
151
+ [docs/deployment.md](docs/deployment.md) for transports, reverse proxy, and
152
+ DNS guidance.
153
+
154
+ See [docs/](docs/index.md) for the full overview, installation, usage, and
155
+ deployment guides; concept registry in [docs/concepts.md](docs/concepts.md)
156
+ (`CONCEPT:SFDC-1.x`).
157
+
158
+ <!-- BEGIN GENERATED: additional-deployment-options -->
159
+ ### Additional Deployment Options
160
+
161
+ `salesforce-agent` can also run as a **local container** (Docker / Podman / `uv`) or be
162
+ consumed from a **remote deployment**. The
163
+ [Deployment guide](https://knuckles-team.github.io/salesforce-agent/deployment/) has full, copy-paste
164
+ `mcp_config.json` for all four transports — **stdio**, **streamable-http**,
165
+ **local container / uv**, and **remote URL**:
166
+
167
+ - **Local container / uv** — launch the server from `mcp_config.json` via `uvx`,
168
+ `docker run`, or `podman run`, or point at a local streamable-http container by `url`.
169
+ - **Remote URL** — connect to a server deployed behind Caddy at
170
+ `http://salesforce-mcp.arpa/mcp` using the `"url"` key.
171
+ <!-- END GENERATED: additional-deployment-options -->
172
+
173
+ ## Development
174
+
175
+ ```bash
176
+ pip install -e .[all,test]
177
+ pytest # mocked httpx suite — no live org required
178
+ pre-commit run --all-files # must be fully green before committing
179
+ ```
180
+
181
+ ## License
182
+
183
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,58 @@
1
+ [build-system]
2
+ requires = [ "setuptools>=80.9.0", "wheel",]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "salesforce-agent"
7
+ version = "0.2.0"
8
+ description = "Salesforce REST/SOQL/Bulk API 2.0 connector + MCP Server + A2A Server for Agentic AI!"
9
+ readme = "README.md"
10
+ classifiers = [ "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Environment :: Console", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3",]
11
+ requires-python = ">=3.11, <3.15"
12
+ dependencies = [ "agent-utilities>=0.48.0", "httpx>=0.27.0",]
13
+ [[project.authors]]
14
+ name = "Audel Rouhi"
15
+ email = "knucklessg1@gmail.com"
16
+
17
+ [project.license]
18
+ text = "MIT"
19
+
20
+ [project.optional-dependencies]
21
+ mcp = [ "agent-utilities[mcp]>=0.48.0",]
22
+ agent = [ "agent-utilities[agent,logfire]>=0.48.0",]
23
+ jwt = [ "cryptography>=42.0.0",]
24
+ all = [ "salesforce-agent[mcp,agent,jwt,logfire]>=0.2.0",]
25
+ test = [ "pytest-xdist>=3.6.0", "pytest", "pytest-asyncio", "pytest-cov", "cryptography>=42.0.0",]
26
+
27
+ [project.scripts]
28
+ salesforce-mcp = "salesforce_agent.mcp_server:mcp_server"
29
+ salesforce-agent = "salesforce_agent.agent_server:agent_server"
30
+
31
+ [tool.setuptools]
32
+ include-package-data = true
33
+
34
+ [tool.ruff]
35
+ line-length = 88
36
+ target-version = "py310"
37
+
38
+ [tool.mypy]
39
+ python_version = "3.10"
40
+ ignore_missing_imports = true
41
+ check_untyped_defs = true
42
+
43
+ [dependency-groups]
44
+ dev = [ "pytest-timeout>=2.4.0",]
45
+
46
+ [tool.setuptools.package-data]
47
+ salesforce_agent = [ "mcp_config.json", "agent_data/**",]
48
+
49
+ [tool.ruff.lint]
50
+ select = [ "E", "F", "I", "UP", "B",]
51
+ ignore = [ "E402", "E501", "B008",]
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = [ ".",]
55
+ include = [ "salesforce_agent*",]
56
+
57
+ [tool.vulture]
58
+ ignore_names = ["request", "config", "exc_info"]
@@ -0,0 +1,2 @@
1
+ agent-utilities>=0.48.0
2
+ httpx>=0.27.0
@@ -0,0 +1,69 @@
1
+ """CONCEPT:ECO-4.0 Unified ecosystem initialization dynamic check."""
2
+
3
+ import importlib
4
+ import inspect
5
+ from typing import Any
6
+
7
+ __version__ = "0.2.0"
8
+ __all__: list[str] = []
9
+
10
+ CORE_MODULES = [
11
+ "salesforce_agent.salesforce_response_models",
12
+ "salesforce_agent.salesforce_input_models",
13
+ "salesforce_agent.auth",
14
+ "salesforce_agent.api_client",
15
+ ]
16
+ OPTIONAL_MODULES = {
17
+ "salesforce_agent.agent_server": "agent",
18
+ "salesforce_agent.mcp_server": "mcp",
19
+ }
20
+
21
+
22
+ def _expose_members(module):
23
+ for name, obj in inspect.getmembers(module):
24
+ if (inspect.isclass(obj) or inspect.isfunction(obj)) and not name.startswith(
25
+ "_"
26
+ ):
27
+ globals()[name] = obj
28
+ if name not in __all__:
29
+ __all__.append(name)
30
+
31
+
32
+ for module_name in CORE_MODULES:
33
+ module = importlib.import_module(module_name)
34
+ _expose_members(module)
35
+
36
+ _loaded_optional_modules: dict[str, Any] = {}
37
+
38
+
39
+ def _import_module_safely(module_name: str):
40
+ try:
41
+ return importlib.import_module(module_name)
42
+ except ImportError:
43
+ return None
44
+
45
+
46
+ def __getattr__(name: str) -> Any:
47
+ if name == "_MCP_AVAILABLE":
48
+ mcp_key = next((k for k in OPTIONAL_MODULES if "mcp_server" in k), None)
49
+ return _import_module_safely(mcp_key) is not None if mcp_key else False
50
+ if name == "_AGENT_AVAILABLE":
51
+ agent_key = next((k for k in OPTIONAL_MODULES if "agent_server" in k), None)
52
+ return _import_module_safely(agent_key) is not None if agent_key else False
53
+
54
+ for module_name in OPTIONAL_MODULES:
55
+ if module_name not in _loaded_optional_modules:
56
+ module = _import_module_safely(module_name)
57
+ if module is not None:
58
+ _loaded_optional_modules[module_name] = module
59
+ _expose_members(module)
60
+
61
+ module = _loaded_optional_modules.get(module_name)
62
+ if module is not None and hasattr(module, name):
63
+ return getattr(module, name)
64
+
65
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
66
+
67
+
68
+ def __dir__() -> list[str]:
69
+ return sorted(list(globals().keys()) + __all__)
@@ -0,0 +1,4 @@
1
+ from salesforce_agent.agent_server import agent_server
2
+
3
+ if __name__ == "__main__":
4
+ agent_server()
@@ -0,0 +1,77 @@
1
+ """Pydantic AI A2A agent server for the Salesforce connector."""
2
+
3
+ import logging
4
+ import os
5
+ import sys
6
+ import warnings
7
+
8
+ __version__ = "0.2.0"
9
+
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
13
+ )
14
+ logger = logging.getLogger(__name__)
15
+
16
+ DEFAULT_AGENT_NAME = None
17
+ DEFAULT_AGENT_DESCRIPTION = None
18
+ DEFAULT_AGENT_SYSTEM_PROMPT = None
19
+
20
+
21
+ def agent_server():
22
+ """Start graph-based Pydantic AI agent server."""
23
+ from agent_utilities import (
24
+ build_system_prompt_from_workspace,
25
+ create_agent_parser,
26
+ create_agent_server,
27
+ initialize_workspace,
28
+ load_identity,
29
+ )
30
+
31
+ global DEFAULT_AGENT_NAME, DEFAULT_AGENT_DESCRIPTION, DEFAULT_AGENT_SYSTEM_PROMPT
32
+ initialize_workspace()
33
+ meta = load_identity()
34
+ DEFAULT_AGENT_NAME = os.getenv(
35
+ "DEFAULT_AGENT_NAME", meta.get("name", "Salesforce Agent")
36
+ )
37
+ DEFAULT_AGENT_DESCRIPTION = os.getenv(
38
+ "AGENT_DESCRIPTION",
39
+ meta.get("description", "AI agent for Salesforce CRM operations."),
40
+ )
41
+ DEFAULT_AGENT_SYSTEM_PROMPT = os.getenv(
42
+ "AGENT_SYSTEM_PROMPT",
43
+ meta.get("content") or build_system_prompt_from_workspace(),
44
+ )
45
+
46
+ warnings.filterwarnings("ignore", message=".*urllib3.*")
47
+ warnings.filterwarnings("ignore", category=DeprecationWarning, module="fastmcp")
48
+
49
+ print(f"{DEFAULT_AGENT_NAME} v{__version__}", file=sys.stderr)
50
+ parser = create_agent_parser()
51
+ args = parser.parse_args()
52
+
53
+ create_agent_server(
54
+ mcp_url=args.mcp_url,
55
+ mcp_config=args.mcp_config or "mcp_config.json",
56
+ host=args.host,
57
+ port=args.port,
58
+ provider=args.provider,
59
+ model_id=args.model_id,
60
+ router_model=args.model_id,
61
+ agent_model=args.model_id,
62
+ base_url=args.base_url,
63
+ api_key=args.api_key,
64
+ custom_skills_directory=args.custom_skills_directory,
65
+ enable_web_ui=args.web,
66
+ enable_otel=args.otel,
67
+ otel_endpoint=args.otel_endpoint,
68
+ otel_headers=args.otel_headers,
69
+ otel_public_key=args.otel_public_key,
70
+ otel_secret_key=args.otel_secret_key,
71
+ otel_protocol=args.otel_protocol,
72
+ debug=args.debug,
73
+ )
74
+
75
+
76
+ if __name__ == "__main__":
77
+ agent_server()
@@ -0,0 +1,17 @@
1
+ """Salesforce REST API resource clients (owned thin httpx wrappers)."""
2
+
3
+ from salesforce_agent.api.api_client_admin import AdminClient
4
+ from salesforce_agent.api.api_client_base import ApiClientBase
5
+ from salesforce_agent.api.api_client_bulk import BulkClient
6
+ from salesforce_agent.api.api_client_describe import DescribeClient
7
+ from salesforce_agent.api.api_client_query import QueryClient
8
+ from salesforce_agent.api.api_client_records import RecordsClient
9
+
10
+ __all__ = [
11
+ "AdminClient",
12
+ "ApiClientBase",
13
+ "BulkClient",
14
+ "DescribeClient",
15
+ "QueryClient",
16
+ "RecordsClient",
17
+ ]