Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:49:50 +08:00
commit adc4b2be25
147 changed files with 24716 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
# Salesforce OAuth Demo
This example demonstrates how to create MCP tools that interact with Salesforce using the MXCP OAuth authentication system with the `simple_salesforce` library.
## Features Demonstrated
### 1. MXCP OAuth Authentication
- Project-wide Salesforce OAuth configuration
- Automatic token management through MXCP authentication system
- User authentication via standard OAuth 2.0 flow
- Error handling for authentication failures
### 2. Salesforce API Integration
- `list_sobjects` - Retrieve all available Salesforce objects (sObjects) from your org with optional filtering
- `describe_sobject` - Get detailed metadata for a specific Salesforce object, including field information
- `get_sobject` - Retrieve a specific Salesforce record by its ID
- `search` - Search across all searchable Salesforce objects using simple search terms
- `soql` - Execute SOQL (Salesforce Object Query Language) queries
- `sosl` - Execute SOSL (Salesforce Object Search Language) queries for complex searches
- `whoami` - Display information about the current authenticated Salesforce user
- Token-based API access using authenticated user context
## Prerequisites
1. **Salesforce Org**: You need access to a Salesforce org (Developer Edition is fine)
2. **Salesforce Connected App**: Create a Connected App in Salesforce with OAuth settings
3. **Python Dependencies**: The `simple_salesforce` library (automatically managed by MXCP)
## Setup
### 1. Create Salesforce Connected App
1. Log into your Salesforce org
2. Go to **Setup****App Manager****New Connected App**
3. Fill in basic information:
- **Connected App Name**: "MXCP Integration" (or your preferred name)
- **API Name**: Will auto-populate
- **Contact Email**: Your email
4. Enable OAuth Settings:
- **Enable OAuth Settings**: Check this box
- **Callback URL**: This depends on your deployment:
- **Local Development**: `http://localhost:8000/salesforce/callback`
- **Remote/Production**: `https://your-domain.com/salesforce/callback` (replace with your actual server URL)
- **Selected OAuth Scopes**: Add these scopes:
- Access and manage your data (api)
- Perform requests on your behalf at any time (refresh_token, offline_access)
- Access your basic information (id, profile, email, address, phone)
5. Save the Connected App
6. Note down the **Consumer Key** (Client ID) and **Consumer Secret** (Client Secret)
### 2. Configure Environment Variables
Set your Salesforce OAuth credentials:
```bash
export SALESFORCE_CLIENT_ID="your-consumer-key-from-connected-app"
export SALESFORCE_CLIENT_SECRET="your-consumer-secret-from-connected-app"
```
### 3. Configure Callback URL for Your Deployment
The callback URL configuration depends on where your MXCP server will run:
#### Local Development
For local development, the default configuration in `config.yml` uses `http://localhost:8000/salesforce/callback`. This works when:
- You're running MXCP locally on your development machine
- Users authenticate from the same machine where MXCP is running
#### Remote/Production Deployment
For remote servers or production deployments, you need to:
1. **Update config.yml**: Uncomment and modify the production callback URL:
```yaml
redirect_uris:
- "http://localhost:8000/salesforce/callback" # Keep for local dev
- "https://your-domain.com/salesforce/callback" # Add your actual URL
```
2. **Update base_url**: Set the correct base URL in your config:
```yaml
transport:
http:
base_url: https://your-domain.com # Your actual server URL
```
3. **Configure Connected App**: Add the production callback URL to your Salesforce Connected App's callback URLs
**Important**:
- The callback URL must be accessible from the user's browser, not just from your server
- For production deployments, Salesforce requires HTTPS for callback URLs
- You can configure multiple callback URLs in your Connected App to support both local development and production
## Authenticate with Salesforce
When you first run MXCP, you'll need to authenticate with Salesforce:
```bash
# Start the MXCP server with the config file - this will prompt for authentication
MXCP_CONFIG=config.yml mxcp serve
```
The authentication flow will:
1. Open your browser to Salesforce login
2. You'll log in with your Salesforce credentials
3. Authorize the MXCP application
4. Redirect back to complete authentication
## Project Structure
```
salesforce-oauth/
├── mxcp-site.yml # Project metadata
├── config.yml # Server and authentication configuration
├── python/ # Python modules
│ └── salesforce_client.py # Salesforce API implementations
├── tools/ # Tool definitions
│ ├── list_sobjects.yml # List all Salesforce objects
│ ├── describe_sobject.yml # Get object metadata
│ ├── get_sobject.yml # Get record by ID
│ ├── search.yml # Search across objects
│ ├── soql.yml # Execute SOQL queries
│ ├── sosl.yml # Execute SOSL queries
│ └── whoami.yml # Current user information
└── README.md # This file
```
## Key Concepts
1. **MXCP OAuth Integration**: Uses MXCP's built-in Salesforce OAuth provider for secure authentication
2. **User Context**: Access tokens are automatically managed and provided through `user_context()`
3. **Token-based Authentication**: simple_salesforce is initialized with OAuth tokens instead of credentials
4. **Project-wide Configuration**: Authentication is configured at the project level in `mxcp-site.yml`
5. **Error Handling**: Comprehensive error handling for authentication and API failures
6. **API Integration**: Demonstrates calling Salesforce REST API endpoints with proper OAuth tokens
## Example Output
When you run `list_sobjects`, you'll get a response like:
```json
[
"Account",
"Contact",
"Lead",
"Opportunity",
"Case",
"Product2",
"Task",
"Event",
"User",
"CustomObject__c",
...
]
```
## Troubleshooting
### Authentication Errors
- **"No user context available"**: User needs to authenticate first by running `mxcp serve` and completing OAuth flow
- **"No Salesforce access token found"**: Authentication was incomplete or token expired - re-authenticate
- **Connected App Issues**: Verify your `SALESFORCE_CLIENT_ID` and `SALESFORCE_CLIENT_SECRET` are correct
- **Callback URL Mismatch**: Ensure the callback URL in your Connected App matches where your MXCP server is accessible:
- Local development: `http://localhost:8000/salesforce/callback`
- Remote/production: `https://your-domain.com/salesforce/callback`
- **OAuth Scopes**: Verify your Connected App has the required OAuth scopes (api, refresh_token, id, profile, email)
### API Errors
- Verify you have the necessary permissions in Salesforce
- Check that your org is accessible and not in maintenance mode
- Ensure your Connected App is approved and not restricted by IP ranges
### Connected App Setup Issues
- **App Not Found**: Make sure your Connected App is saved and the Consumer Key/Secret are copied correctly
- **Callback URL**: The callback URL must exactly match your MXCP server's accessible address:
- For local development: `http://localhost:8000/salesforce/callback`
- For remote deployment: `https://your-domain.com/salesforce/callback`
- **OAuth Scopes**: Missing scopes will cause authentication to fail - ensure all required scopes are selected
## Next Steps
This example demonstrates a comprehensive set of Salesforce integration tools. You could extend it with additional tools for data manipulation like:
- `create_record` - Create new records in Salesforce objects
- `update_record` - Update existing records
- `delete_record` - Delete records
- `bulk_operations` - Handle bulk data operations for large datasets

View File

@@ -0,0 +1,22 @@
mxcp: 1
transport:
http:
port: 8000
host: 0.0.0.0
# Set base_url to your server's public URL for production
base_url: http://localhost:8000
projects:
salesforce-oauth:
profiles:
default:
# OAuth Authentication Configuration
auth:
provider: salesforce
salesforce:
client_id: "${SALESFORCE_CLIENT_ID}"
client_secret: "${SALESFORCE_CLIENT_SECRET}"
scope: "api refresh_token openid profile email"
callback_path: "/salesforce/callback"
auth_url: "https://login.salesforce.com/services/oauth2/authorize"
token_url: "https://login.salesforce.com/services/oauth2/token"

View File

@@ -0,0 +1,3 @@
mxcp: 1
project: salesforce-oauth
profile: default

View File

@@ -0,0 +1,466 @@
"""
Salesforce MCP tools using simple_salesforce with MXCP OAuth authentication.
"""
import threading
from functools import wraps
from typing import Dict, Any, List, Optional
from mxcp.sdk.auth.context import get_user_context
from simple_salesforce import Salesforce # type: ignore[attr-defined]
from simple_salesforce.exceptions import SalesforceExpiredSession
from mxcp.runtime import on_init, on_shutdown
import logging
# Thread-safe cache for Salesforce clients
_client_cache: Optional[Dict[str, Salesforce]] = None
_cache_lock: Optional[threading.Lock] = None
@on_init
def init_client_cache() -> None:
"""
Initialize the Salesforce client cache.
"""
global _client_cache, _cache_lock
_client_cache = {}
_cache_lock = threading.Lock()
@on_shutdown
def clear_client_cache() -> None:
"""
Clear the Salesforce client cache.
"""
global _client_cache, _cache_lock
_client_cache = None
_cache_lock = None
def _get_cache_key(context: Any) -> Optional[str]:
"""Generate a cache key based on user context."""
if not context:
return None
# Use user ID and instance URL as cache key
user_id = getattr(context, "user_id", None) or getattr(context, "id", None)
# Extract instance URL
instance_url = None
if context.raw_profile and "urls" in context.raw_profile:
urls = context.raw_profile["urls"]
instance_url = urls.get("custom_domain")
if not instance_url:
for url_key in ["rest", "enterprise", "partner"]:
if url_key in urls:
service_url = urls[url_key]
instance_url = service_url.split("/services/")[0]
break
if user_id and instance_url:
return f"{user_id}:{instance_url}"
return None
def with_session_retry(func: Any) -> Any:
"""
Decorator that automatically retries API calls with cache invalidation when sessions expire.
This handles the race condition where a session might expire between cache validation
and the actual API call.
"""
@wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any:
try:
return func(*args, **kwargs)
except SalesforceExpiredSession:
logging.error("Salesforce session expired")
# Session expired during the call - invalidate cache and retry once
context = get_user_context()
cache_key = _get_cache_key(context)
if cache_key and _cache_lock and _client_cache:
with _cache_lock:
# Remove the expired client from cache
_client_cache.pop(cache_key, None)
# Retry the function call - this will get a fresh client
return func(*args, **kwargs)
return wrapper
def _escape_sosl_search_term(search_term: str) -> str:
"""
Escape special characters in SOSL search terms to prevent injection attacks.
SOSL special characters that need escaping: & | ! { } [ ] ( ) ^ ~ * ? : " ' + -
"""
# Escape backslashes first to avoid double-escaping
escaped = search_term.replace("\\", "\\\\")
# Escape SOSL special characters
special_chars = [
"&",
"|",
"!",
"{",
"}",
"[",
"]",
"(",
")",
"^",
"~",
"*",
"?",
":",
'"',
"'",
"+",
"-",
]
for char in special_chars:
escaped = escaped.replace(char, f"\\{char}")
return escaped
def _get_salesforce_client() -> Salesforce:
"""
Create and return an authenticated Salesforce client using OAuth tokens from user_context.
Uses caching to avoid recreating clients unnecessarily. Clients are cached per user
and instance URL combination in a thread-safe manner.
"""
try:
# Get the authenticated user's context
context = get_user_context()
if not context:
raise ValueError("No user context available. User must be authenticated.")
# Generate cache key
cache_key = _get_cache_key(context)
# Try to get cached client first
if cache_key and _cache_lock and _client_cache:
with _cache_lock:
if cache_key in _client_cache:
logging.info("Using cached Salesforce client")
# Return cached client - retry decorator will handle any session expiry
return _client_cache[cache_key]
logging.info("No cached Salesforce client found, creating new one")
# Extract Salesforce OAuth tokens from user context
access_token = context.external_token
# Extract instance URL from user context (this is user/org-specific)
instance_url = None
if context.raw_profile and "urls" in context.raw_profile:
urls = context.raw_profile["urls"]
# Try custom_domain first (this is the full instance URL)
instance_url = urls.get("custom_domain")
if not instance_url:
# Fallback: extract base URL from any service endpoint
for url_key in ["rest", "enterprise", "partner"]:
if url_key in urls:
service_url = urls[url_key]
instance_url = service_url.split("/services/")[0]
break
if not access_token:
raise ValueError(
"No Salesforce access token found in user context. "
"User must authenticate with Salesforce through MXCP."
)
if not instance_url:
raise ValueError(
"No Salesforce instance URL found in user context. "
"Authentication may be incomplete or profile missing URL information."
)
# Initialize Salesforce client with OAuth token
sf = Salesforce(session_id=access_token, instance_url=instance_url)
# Cache the client if we have a valid cache key
if cache_key and _cache_lock and _client_cache:
with _cache_lock:
_client_cache[cache_key] = sf
return sf
except Exception as e:
raise ValueError(f"Failed to authenticate with Salesforce: {str(e)}")
@with_session_retry
def list_sobjects(filter: Optional[str] = None) -> List[str]:
"""
List all available Salesforce objects (sObjects) in the org.
Args:
filter: Optional fuzzy filter to match object names (case-insensitive substring search).
Examples: "account", "__c" for custom objects, "contact", etc.
Returns:
list: List of Salesforce object names as strings
"""
try:
sf = _get_salesforce_client()
# Get all sObjects metadata
describe_result = sf.describe()
if not describe_result or "sobjects" not in describe_result:
raise ValueError("Invalid describe response from Salesforce API")
# Extract just the object names
sobjects = describe_result["sobjects"]
object_names = []
for obj in sobjects:
if not isinstance(obj, dict) or "name" not in obj:
raise ValueError(f"Invalid sobject format: {obj}")
object_names.append(obj["name"])
# Apply fuzzy filter if provided
if filter is not None and filter.strip():
filter_lower = filter.lower()
object_names = [name for name in object_names if filter_lower in name.lower()]
# Sort alphabetically for consistent output
object_names.sort()
return object_names
except Exception as e:
# Return error in a format that can be handled by the caller
raise Exception(f"Error listing Salesforce objects: {str(e)}")
@with_session_retry
def describe_sobject(object_name: str) -> Dict[str, Any]:
"""
Get detailed field information for a specific Salesforce object (sObject).
Args:
object_name: The API name of the Salesforce object to describe
Returns:
dict: Dictionary where each key is a field name and each value contains field metadata
"""
sf = _get_salesforce_client()
# Try to get the object - catch this specifically for "object doesn't exist"
try:
sobject = getattr(sf, object_name)
except AttributeError:
raise Exception(f"Salesforce object '{object_name}' does not exist")
# Let API errors from describe() propagate naturally with their original messages
describe_result = sobject.describe()
if not describe_result or "fields" not in describe_result:
raise ValueError(f"Invalid describe response for object '{object_name}'")
# Process fields into the required format
fields_info = {}
for field in describe_result["fields"]:
if not isinstance(field, dict):
raise ValueError(f"Invalid field format in '{object_name}': {field}")
required_fields = ["name", "type", "label"]
for required_field in required_fields:
if required_field not in field:
raise ValueError(f"Field missing '{required_field}' in '{object_name}': {field}")
field_name = field["name"]
field_info = {"type": field["type"], "label": field["label"]}
# Add referenceTo information for reference fields
if field["type"] == "reference" and field.get("referenceTo"):
field_info["referenceTo"] = field["referenceTo"]
fields_info[field_name] = field_info
return fields_info
@with_session_retry
def get_sobject(object_name: str, record_id: str) -> Dict[str, Any]:
"""
Retrieve a specific Salesforce record by its object type and ID.
Args:
object_name: The API name of the Salesforce object type
record_id: The unique Salesforce ID of the record to retrieve
Returns:
dict: Dictionary containing all fields and values for the specified record
"""
sf = _get_salesforce_client()
# Try to get the object - catch this specifically for "object doesn't exist"
try:
sobject = getattr(sf, object_name)
except AttributeError:
raise Exception(f"Salesforce object '{object_name}' does not exist")
# Let API errors from get() propagate naturally with their original messages
record = sobject.get(record_id)
if not isinstance(record, dict):
raise ValueError(f"Invalid record format returned for {object_name}:{record_id}")
# Remove 'attributes' field for consistency with other functions
clean_record: Dict[str, Any] = {k: v for k, v in record.items() if k != "attributes"}
return clean_record
@with_session_retry
def soql(query: str) -> List[Dict[str, Any]]:
"""
Execute an arbitrary SOQL (Salesforce Object Query Language) query.
Args:
query: The SOQL query to execute
Returns:
list: Array of records returned by the SOQL query
"""
sf = _get_salesforce_client()
# Execute the SOQL query
result = sf.query(query)
if not result or "records" not in result:
raise ValueError("Invalid SOQL query response from Salesforce API")
# Remove 'attributes' field from each record for cleaner output
records = []
for record in result["records"]:
if not isinstance(record, dict):
raise ValueError(f"Invalid record format in SOQL result: {record}")
clean_record = {k: v for k, v in record.items() if k != "attributes"}
records.append(clean_record)
return records
@with_session_retry
def search(search_term: str) -> List[Dict[str, Any]]:
"""
Search for records across all searchable Salesforce objects using a simple search term.
Uses Salesforce's native search to automatically find matches across all objects.
Args:
search_term: The term to search for across Salesforce objects
Returns:
list: Array of matching records from various Salesforce objects
"""
sf = _get_salesforce_client()
# Escape the search term to prevent SOSL injection attacks
escaped_search_term = _escape_sosl_search_term(search_term)
# Use simple SOSL syntax - Salesforce searches all searchable objects automatically
sosl_query = f"FIND {{{escaped_search_term}}}"
# Execute the SOSL search
search_results = sf.search(sosl_query)
if not search_results or "searchRecords" not in search_results:
raise ValueError("Invalid SOSL search response from Salesforce API")
# Flatten results from all objects into a single array
all_records = []
for record in search_results["searchRecords"]:
if not isinstance(record, dict):
raise ValueError(f"Invalid record format in SOSL result: {record}")
if "attributes" not in record or not isinstance(record["attributes"], dict):
raise ValueError(f"Invalid record attributes in SOSL result: {record}")
# Remove 'attributes' field and add object type for context
clean_record = {k: v for k, v in record.items() if k != "attributes"}
clean_record["_ObjectType"] = record["attributes"]["type"]
all_records.append(clean_record)
return all_records
@with_session_retry
def sosl(query: str) -> List[Dict[str, Any]]:
"""
Execute an arbitrary SOSL (Salesforce Object Search Language) query.
Args:
query: The SOSL query to execute
Returns:
list: Array of records returned by the SOSL search query
"""
sf = _get_salesforce_client()
# Execute the SOSL search
search_results = sf.search(query)
if not search_results or "searchRecords" not in search_results:
raise ValueError("Invalid SOSL query response from Salesforce API")
# Flatten results from all objects into a single array
all_records = []
for record in search_results["searchRecords"]:
if not isinstance(record, dict):
raise ValueError(f"Invalid record format in SOSL result: {record}")
if "attributes" not in record or not isinstance(record["attributes"], dict):
raise ValueError(f"Invalid record attributes in SOSL result: {record}")
# Remove 'attributes' field and add object type for context
clean_record = {k: v for k, v in record.items() if k != "attributes"}
clean_record["_ObjectType"] = record["attributes"]["type"]
all_records.append(clean_record)
return all_records
def whoami() -> Dict[str, Any]:
"""
Get basic information about the currently authenticated Salesforce user from the user context.
Returns essential user information from the MXCP authentication context without making API calls.
Returns:
dict: Dictionary containing essential current user information
"""
context = get_user_context()
if not context:
raise ValueError("No user context available. User must be authenticated.")
# Extract instance URL from context
instance_url = None
if context.raw_profile and "urls" in context.raw_profile:
urls = context.raw_profile["urls"]
instance_url = urls.get("custom_domain")
if not instance_url:
# Fallback: extract base URL from any service endpoint
for url_key in ["rest", "enterprise", "partner"]:
if url_key in urls:
service_url = urls[url_key]
instance_url = service_url.split("/services/")[0]
break
# Extract essential user information from raw profile
raw_profile = context.raw_profile or {}
user_info = {
"user_id": raw_profile.get("user_id"),
"email": raw_profile.get("email"),
"name": raw_profile.get("name"),
"preferred_username": raw_profile.get("preferred_username"),
"organization_id": raw_profile.get("organization_id"),
"instanceUrl": instance_url,
}
return user_info

View File

@@ -0,0 +1,34 @@
mxcp: 1
tool:
name: describe_sobject
description: |
Get detailed metadata for a specific Salesforce object, including all field information.
Returns field names, types, labels, and relationship details.
tags: ["salesforce", "metadata", "schema"]
annotations:
title: "Describe Salesforce Object"
readOnlyHint: true
idempotentHint: true
parameters:
- name: object_name
type: string
description: "Name of the Salesforce object to describe"
examples: ["Account", "Contact", "Opportunity", "Lead", "Case"]
return:
type: object
description: "Object metadata with field information"
additionalProperties: true
language: python
source:
file: ../python/salesforce_client.py
tests:
- name: "Describe Account object"
description: "Test describing the standard Account object"
arguments:
- key: object_name
value: "Account"
result_contains:
Name:
type: "string"
Id:
type: "id"

View File

@@ -0,0 +1,37 @@
mxcp: 1
tool:
name: get_sobject
description: |
Retrieve a specific Salesforce record by its ID.
Returns the complete record data with all accessible fields.
tags: ["salesforce", "data", "records"]
annotations:
title: "Get Salesforce Record"
readOnlyHint: true
idempotentHint: true
parameters:
- name: object_name
type: string
description: "Name of the Salesforce object type"
examples: ["Account", "Contact", "Opportunity", "Lead", "Case"]
- name: record_id
type: string
description: "Salesforce record ID (15 or 18 character ID)"
examples: ["001000000000001", "003000000000001AAA"]
return:
type: object
description: "Complete record data"
additionalProperties: true
language: python
source:
file: ../python/salesforce_client.py
tests:
- name: "Get Account record"
description: "Test retrieving an Account record by ID"
arguments:
- key: object_name
value: "Account"
- key: record_id
value: "001000000000001"
result_contains:
Id: "001000000000001"

View File

@@ -0,0 +1,38 @@
mxcp: 1
tool:
name: list_sobjects
description: |
List all available Salesforce objects (sObjects) in the organization.
Optionally filter the list by providing a filter term for fuzzy matching on object names.
tags: ["salesforce", "metadata", "objects"]
annotations:
title: "List Salesforce Objects"
readOnlyHint: true
idempotentHint: true
parameters:
- name: filter
type: string
description: "Optional filter term to match against object names (case-insensitive fuzzy matching)"
default: null
examples: ["Account", "Contact", "Custom"]
return:
type: array
description: "List of sObject names"
items:
type: string
description: "Name of a Salesforce object"
language: python
source:
file: ../python/salesforce_client.py
tests:
- name: "List all objects"
description: "Test listing all available Salesforce objects"
arguments: []
result_contains_item: "Account"
- name: "Filter objects"
description: "Test filtering objects by name"
arguments:
- key: filter
value: "Account"
result_contains_item: "Account"

View File

@@ -0,0 +1,38 @@
mxcp: 1
tool:
name: search
description: |
Search across all searchable Salesforce objects using the native Salesforce search.
This uses the simple SOSL syntax "FIND {search_term}" which automatically searches
all searchable objects and fields.
language: python
source:
file: ../python/salesforce_client.py
parameters:
- name: search_term
type: string
description: "Term to search for across all searchable objects"
examples: ["John", "Acme", "555-1234", "example.com"]
return:
type: array
description: "Search results from all matching objects"
items:
type: object
description: "Search result record"
additionalProperties: true
tags:
- salesforce
- search
- data
annotations:
title: "Search Salesforce Records"
readOnlyHint: true
idempotentHint: true
tests:
- name: "Basic search"
description: "Test searching for a common term"
arguments:
- key: search_term
value: "test"
# Note: Using result type array since search results can be empty or contain records
result: []

View File

@@ -0,0 +1,33 @@
mxcp: 1
tool:
name: soql
description: |
Execute a SOQL (Salesforce Object Query Language) query. Returns query results as an array of records.
For personalized queries (e.g., 'my tasks', 'my opportunities'), use the whoami tool first to get the current user's ID for filtering (e.g., WHERE OwnerId = 'user_id').
tags: ["salesforce", "query", "data"]
annotations:
title: "Execute SOQL Query"
readOnlyHint: true
idempotentHint: true
parameters:
- name: query
type: string
description: "SOQL query string to execute"
examples: ["SELECT Id, Name FROM Account LIMIT 10", "SELECT Id, Email FROM Contact WHERE LastName = 'Smith'"]
return:
type: array
description: "Query results"
items:
type: object
description: "Record data"
additionalProperties: true
language: python
source:
file: ../python/salesforce_client.py
tests:
- name: "Simple Account query"
description: "Test executing a basic SOQL query on Account object"
arguments:
- key: query
value: "SELECT Id, Name FROM Account LIMIT 1"
result_length: 1

View File

@@ -0,0 +1,36 @@
mxcp: 1
tool:
name: sosl
description: |
Execute a raw SOSL (Salesforce Object Search Language) query.
Allows complex search queries with specific object targeting and field selection.
language: python
source:
file: ../python/salesforce_client.py
parameters:
- name: query
type: string
description: "SOSL query string to execute"
examples: ["FIND {test} IN ALL FIELDS RETURNING Account(Id, Name)", "FIND {John} RETURNING Contact(Id, Name, Email)"]
return:
type: array
description: "Search results"
items:
type: object
description: "Search result record"
additionalProperties: true
tags:
- salesforce
- search
- advanced
annotations:
title: "Execute SOSL Query"
readOnlyHint: true
idempotentHint: true
tests:
- name: "Simple SOSL query"
description: "Test executing a basic SOSL search query"
arguments:
- key: query
value: "FIND {test} IN ALL FIELDS RETURNING Account(Id, Name)"
result: []

View File

@@ -0,0 +1,44 @@
mxcp: 1
tool:
name: whoami
title: Current User Information
description: |
Get the current authenticated user's information (user_id, email, name) from OAuth context.
Use this tool before executing personalized SOQL queries to identify the user for filtering records by ownership or assignment.
tags:
- salesforce
- user
- auth
annotations:
readOnlyHint: true
idempotentHint: true
parameters: []
return:
type: object
description: Essential current user information from OAuth context
properties:
user_id:
type: string
description: Salesforce user ID
email:
type: string
description: User's email address
name:
type: string
description: User's full name
preferred_username:
type: string
description: User's preferred username
organization_id:
type: string
description: Salesforce organization ID
instanceUrl:
type: string
description: Salesforce instance URL for the authenticated user
language: python
source:
file: ../python/salesforce_client.py
tests:
- name: whoami_basic
description: Get current user information
arguments: []