Metadata-Version: 2.4
Name: concinno-skills-crm
Version: 0.1.0
Summary: CRM agent skills (HubSpot + Salesforce) for Concinno — Databricks 2026 enterprise agent top use case.
Project-URL: Homepage, https://github.com/aiking931931/concinno
Project-URL: Issues, https://github.com/aiking931931/concinno/issues
Project-URL: Changelog, https://github.com/aiking931931/concinno/blob/main/projects/concinno-skills-crm/CHANGELOG.md
Author-email: "AI King (Chen-Xuan Wang)" <me@ai-king.dev>
License-Expression: Apache-2.0
Keywords: agent,concinno,crm,hubspot,salesforce,skills
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: concinno>=2.15.1
Requires-Dist: hubspot-api-client>=11.0
Requires-Dist: simple-salesforce>=1.12
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff>=0.7; extra == 'dev'
Description-Content-Type: text/markdown

# concinno-skills-crm

CRM agent skills for [Concinno](https://pypi.org/project/concinno/) —
HubSpot + Salesforce. Databricks *2026 State of AI Agents* places
customer experience (~40 % of enterprise use cases) at the top of the
agent workload pile; CRM is the system-of-record, so it is the first
ecosystem we ship after `concinno-skills-chat` and
`concinno-skills-google`.

## Status

MVP (0.1.0) — four tools, two ecosystems:

| Tool class         | Platform    | Underlying SDK          | SDK licence |
|--------------------|-------------|-------------------------|-------------|
| `HubSpotContact`   | HubSpot     | `hubspot-api-client`    | Apache-2.0  |
| `HubSpotDeal`      | HubSpot     | `hubspot-api-client`    | Apache-2.0  |
| `SalesforceQuery`  | Salesforce  | `simple-salesforce`     | Apache-2.0  |
| `SalesforceUpsert` | Salesforce  | `simple-salesforce`     | Apache-2.0  |

Pipedrive / Zoho / Microsoft Dynamics are deferred to 0.2.x until a
maintained Python SDK with a permissive licence is proven stable.

## Install

```bash
pip install concinno-skills-crm
```

Both SDKs are hard dependencies and pulled in automatically.

## Credentials

Tokens live under well-known keys in the Concinno `CredentialStore`,
which reads (in order):

1. Runtime overrides via `CredentialStore.set(...)`.
2. Env var `CONCINNO_CRED_<UPPER_KEY>`.
3. `~/.concinno/credentials.json`.

### HubSpot

| Key                       | Env var                                  |
|---------------------------|------------------------------------------|
| `hubspot_access_token`    | `CONCINNO_CRED_HUBSPOT_ACCESS_TOKEN`     |

Produce the token via **HubSpot → Settings → Integrations → Private
Apps** (or an OAuth install if you run a marketplace app).

### Salesforce

Two auth flows are supported. Provide **one** set of keys:

**Password + security token flow**:

| Key                           | Env var                                  |
|-------------------------------|------------------------------------------|
| `salesforce_username`         | `CONCINNO_CRED_SALESFORCE_USERNAME`      |
| `salesforce_password`         | `CONCINNO_CRED_SALESFORCE_PASSWORD`      |
| `salesforce_security_token`   | `CONCINNO_CRED_SALESFORCE_SECURITY_TOKEN`|

**Session-id flow** (when you already hold a bearer session):

| Key                          | Env var                                  |
|------------------------------|------------------------------------------|
| `salesforce_session_id`      | `CONCINNO_CRED_SALESFORCE_SESSION_ID`    |
| `salesforce_instance_url`    | `CONCINNO_CRED_SALESFORCE_INSTANCE_URL`  |

Optional for both flows: `salesforce_domain` — set to `"test"` for a
sandbox org, default is `"login"`.

Example `~/.concinno/credentials.json`:

```jsonc
{
  "hubspot_access_token": "pat-na1-...",
  "salesforce_username": "me@example.com",
  "salesforce_password": "secret",
  "salesforce_security_token": "abcdef",
  "salesforce_domain": "login"
}
```

Missing credentials return `{"error": "no <service> credentials — set
via CredentialStore / env ..."}` rather than crashing.

## Usage via Concinno `ToolRegistry`

When the consumer sets `CONCINNO_LOAD_PLUGINS=1`, the default registry
auto-mounts every CRM tool:

```python
import os
os.environ["CONCINNO_LOAD_PLUGINS"] = "1"

from concinno.tools.registry import get_default_registry

reg = get_default_registry()
assert {
    "HubSpotContact",
    "HubSpotDeal",
    "SalesforceQuery",
    "SalesforceUpsert",
} <= set(reg.list_deferred())
```

## Direct Python usage

```python
from concinno_skills_crm import (
    HubSpotContact,
    HubSpotDeal,
    SalesforceQuery,
    SalesforceUpsert,
)

HubSpotContact().call(
    action="create",
    properties={"email": "a@b.com", "firstname": "A"},
)
HubSpotDeal().call(action="list", limit=50)
SalesforceQuery().call(
    action="soql",
    query="SELECT Id, Name FROM Account LIMIT 5",
)
SalesforceUpsert().call(
    action="upsert",
    sobject="Account",
    external_id_field="External_Id__c",
    external_id="X123",
    data={"Name": "Acme"},
)
```

All tools return `{"ok": True, ...}` (or `{"results": [...]}` for
`list`) on success and `{"error": "..."}` on failure — same shape as
every other Concinno built-in tool. No exceptions escape `call()`.

## Concurrency

All four tools set `is_concurrency_safe = False`. CRM clients hold
session + rate-limit state, and serial calls are the sane default for
HubSpot's per-account budget and Salesforce's API limits (typically
15k / 24h / org per free tier).

## Safety notes

- `SalesforceQuery.soql` forwards the query string verbatim but
  warn-logs when a DML verb (`UPDATE` / `DELETE` / `INSERT` /
  `UPSERT` / `MERGE`) leads the string — SFDC's `/query` endpoint will
  reject it, this is purely triage help.
- `list` actions cap the page size at 100 (HubSpot) / 1000 (defensive).
  Larger pulls belong in a 0.2.x `list_all` action with explicit
  pagination.

## License

Apache-2.0.
