@macaw-ai/client 0.0.0-stage → 0.9.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +339 -0
- package/bin/install.js +422 -0
- package/package.json +37 -4
- package/payload/README.md +56 -0
- package/payload/secureAI/LICENSE +190 -0
- package/payload/secureAI/NOTICE +5 -0
- package/payload/secureAI/demos/policy-controls/1_uncontrolled.py +139 -0
- package/payload/secureAI/demos/policy-controls/2_with_macaw.py +139 -0
- package/payload/secureAI/demos/policy-controls/3_with_user_policies.py +162 -0
- package/payload/secureAI/demos/policy-controls/credentials.txt +12 -0
- package/payload/secureAI/demos/policy-controls/malicious_report.txt +21 -0
- package/payload/secureAI/demos/policy-controls/sales_report.txt +20 -0
- package/payload/secureAI/demos/tutorial-1-rbac/README.md +143 -0
- package/payload/secureAI/demos/tutorial-1-rbac/app.py +147 -0
- package/payload/secureAI/demos/tutorial-1-rbac/authprompts_demo.py +306 -0
- package/payload/secureAI/demos/tutorial-1-rbac/config/claims-config.yaml +152 -0
- package/payload/secureAI/demos/tutorial-1-rbac/demo_secureopenai_with_policies.py +150 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/bu_Analytics.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/company_FinTech_Corp.json +70 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/llm-base.json +69 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/team_Reporting.json +8 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_alice.json +32 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_bob.json +28 -0
- package/payload/secureAI/demos/tutorial-1-rbac/policies/user_carol.json +34 -0
- package/payload/secureAI/demos/tutorial-1-rbac/requirements.txt +6 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/auth0_complete_setup.sh +392 -0
- package/payload/secureAI/demos/tutorial-1-rbac/setup/keycloak_complete_setup.sh +320 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/alation-remote-proxy.json +155 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/bu.json +22 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/company.json +17 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_alice.json +48 -0
- package/payload/secureAI/demos/tutorial-2-intent/Policies/user_bob.json +83 -0
- package/payload/secureAI/demos/tutorial-2-intent/README.md +213 -0
- package/payload/secureAI/demos/tutorial-2-intent/script.py +188 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/alation_verifier.py +177 -0
- package/payload/secureAI/demos/tutorial-2-intent/setup/get_alation_user_token.py +180 -0
- package/payload/secureAI/examples/README.md +244 -0
- package/payload/secureAI/examples/anthropic/anthropic_1a_dropin_simple.py +103 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind_streaming.py +190 -0
- package/payload/secureAI/examples/anthropic/anthropic_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/attestations/1a_trade_alice.py +215 -0
- package/payload/secureAI/examples/attestations/1a_trade_bob.py +164 -0
- package/payload/secureAI/examples/attestations/1b_grant_admin.py +170 -0
- package/payload/secureAI/examples/attestations/1b_grant_researcher.py +231 -0
- package/payload/secureAI/examples/attestations/README.md +128 -0
- package/payload/secureAI/examples/langchain/langchain_1a_dropin_simple.py +199 -0
- package/payload/secureAI/examples/langchain/langchain_1b_multiuser.py +262 -0
- package/payload/secureAI/examples/langchain/langchain_1c_orchestration.py +332 -0
- package/payload/secureAI/examples/langchain/langchain_1d_llm_openai.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1e_llm_anthropic.py +130 -0
- package/payload/secureAI/examples/langchain/langchain_1f_memory.py +214 -0
- package/payload/secureAI/examples/litellm/litellm_1a_dropin_simple.py +131 -0
- package/payload/secureAI/examples/mcp/1a_simple_invocation.py +115 -0
- package/payload/secureAI/examples/mcp/1b_discovery_and_resources.py +142 -0
- package/payload/secureAI/examples/mcp/1c_logging_client.py +100 -0
- package/payload/secureAI/examples/mcp/1d_progress_client.py +100 -0
- package/payload/secureAI/examples/mcp/1e_sampling_client.py +150 -0
- package/payload/secureAI/examples/mcp/1e_sampling_server.py +90 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_client.py +127 -0
- package/payload/secureAI/examples/mcp/1f_elicitation_server.py +73 -0
- package/payload/secureAI/examples/mcp/1g_roots_client.py +122 -0
- package/payload/secureAI/examples/mcp/1g_roots_server.py +85 -0
- package/payload/secureAI/examples/mcp/2a_proxy_snowflake.py +212 -0
- package/payload/secureAI/examples/mcp/2b_proxy_databricks.py +299 -0
- package/payload/secureAI/examples/mcp/2c_proxy_stdio_fetch.py +91 -0
- package/payload/secureAI/examples/mcp/2d_proxy_stdio_salesforce.py +119 -0
- package/payload/secureAI/examples/mcp/2e_proxy_multiuser.py +136 -0
- package/payload/secureAI/examples/mcp/2f_proxy_stdio_databricks.py +144 -0
- package/payload/secureAI/examples/mcp/3a_native_mcp_client.py +104 -0
- package/payload/secureAI/examples/mcp/3b_native_mcp_proxy.py +114 -0
- package/payload/secureAI/examples/mcp/3c_native_mcp_sampling.py +180 -0
- package/payload/secureAI/examples/mcp/3d_native_mcp_2026.py +135 -0
- package/payload/secureAI/examples/mcp/securemcp_calculator.py +248 -0
- package/payload/secureAI/examples/mcp/securemcp_server.py +75 -0
- package/payload/secureAI/examples/openai/openai_1a_dropin_simple.py +102 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind.py +193 -0
- package/payload/secureAI/examples/openai/openai_1b_multiuser_bind_streaming.py +199 -0
- package/payload/secureAI/examples/openai/openai_1c_a2a_invoke.py +156 -0
- package/payload/secureAI/examples/openai/openai_1d_invocation_patterns.py +337 -0
- package/payload/secureAI/examples/phishing-resistance/1a_login_alice.py +180 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_alice.py +241 -0
- package/payload/secureAI/examples/phishing-resistance/1b_trade_bob.py +164 -0
- package/payload/secureAI/examples/phishing-resistance/README.md +124 -0
- package/payload/secureAI/examples/phishing-resistance/get_token.py +114 -0
- package/payload/secureAI/examples/pydantic_ai/README.md +88 -0
- package/payload/secureAI/examples/pydantic_ai/policies/app_pydantic-agent.json +10 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1a_dropin_simple.py +90 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1b_model_routing.py +85 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1c_native_tools.py +83 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1d_multiuser_bind.py +104 -0
- package/payload/secureAI/examples/pydantic_ai/pydanticai_1e_mcp_compose.py +92 -0
- package/payload/secureAI/macaw_adapters/__init__.py +55 -0
- package/payload/secureAI/macaw_adapters/anthropic/REFERENCE.md +374 -0
- package/payload/secureAI/macaw_adapters/anthropic/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/anthropic/secure_anthropic.py +792 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/anthropic_ext_tools/secure_anthropic.py +840 -0
- package/payload/secureAI/macaw_adapters/langchain/REFERENCE.md +234 -0
- package/payload/secureAI/macaw_adapters/langchain/__init__.py +100 -0
- package/payload/secureAI/macaw_adapters/langchain/_utils.py +83 -0
- package/payload/secureAI/macaw_adapters/langchain/agents.py +296 -0
- package/payload/secureAI/macaw_adapters/langchain/anthropic.py +456 -0
- package/payload/secureAI/macaw_adapters/langchain/callbacks.py +306 -0
- package/payload/secureAI/macaw_adapters/langchain/memory.py +352 -0
- package/payload/secureAI/macaw_adapters/langchain/openai.py +410 -0
- package/payload/secureAI/macaw_adapters/langchain/tools.py +174 -0
- package/payload/secureAI/macaw_adapters/litellm/README.md +216 -0
- package/payload/secureAI/macaw_adapters/litellm/__init__.py +143 -0
- package/payload/secureAI/macaw_adapters/litellm/secure_litellm.py +686 -0
- package/payload/secureAI/macaw_adapters/mcp/REFERENCE.md +460 -0
- package/payload/secureAI/macaw_adapters/mcp/__init__.py +65 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint.py +193 -0
- package/payload/secureAI/macaw_adapters/mcp/_endpoint2.py +225 -0
- package/payload/secureAI/macaw_adapters/mcp/_serve.py +25 -0
- package/payload/secureAI/macaw_adapters/mcp/client.py +721 -0
- package/payload/secureAI/macaw_adapters/mcp/decorators.py +228 -0
- package/payload/secureAI/macaw_adapters/mcp/fastmcp_compat.py +353 -0
- package/payload/secureAI/macaw_adapters/mcp/mcp.py +785 -0
- package/payload/secureAI/macaw_adapters/mcp/proxy.py +626 -0
- package/payload/secureAI/macaw_adapters/mcp/server.py +317 -0
- package/payload/secureAI/macaw_adapters/mcp/subscriptions.py +450 -0
- package/payload/secureAI/macaw_adapters/openai/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai/__init__.py +22 -0
- package/payload/secureAI/macaw_adapters/openai/secure_openai.py +833 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/REFERENCE.md +368 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/__init__.py +25 -0
- package/payload/secureAI/macaw_adapters/openai_ext_tools/secure_openai.py +876 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/__init__.py +36 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/_core.py +257 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/agent.py +218 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/model.py +99 -0
- package/payload/secureAI/macaw_adapters/pydantic_ai/toolset.py +86 -0
- package/payload/secureAI/test_harness_quick.py +296 -0
- package/wheels/macaw_adapters-0.9.10.1-py3-none-any.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp312-cp312-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-macosx_11_0_arm64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-manylinux_2_17_x86_64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp314-cp314-win_amd64.whl +0 -0
- package/wheels/macaw_client-0.9.10.1-cp39-cp39-macosx_11_0_arm64.whl +0 -0
- package/README.md +0 -3
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example: SecureMCPProxy with Databricks MCP Server
|
|
4
|
+
|
|
5
|
+
Connect to Databricks MCP servers with MACAW security for:
|
|
6
|
+
- Vector Search (RAG queries)
|
|
7
|
+
- Genie Space (natural language data analysis)
|
|
8
|
+
- SQL execution
|
|
9
|
+
- Unity Catalog functions
|
|
10
|
+
|
|
11
|
+
Databricks MCP Options:
|
|
12
|
+
1. Databricks Managed MCP Servers - HTTP-based, Unity Catalog integrated
|
|
13
|
+
2. Community server (databricks-mcp-server PyPI) - stdio-based
|
|
14
|
+
|
|
15
|
+
This example focuses on Databricks Managed MCP Servers (HTTP).
|
|
16
|
+
|
|
17
|
+
Prerequisites:
|
|
18
|
+
1. Databricks workspace (community edition is free)
|
|
19
|
+
2. MCP servers enabled in workspace
|
|
20
|
+
3. MACAW LocalAgent running
|
|
21
|
+
4. Install: pip install macaw-adapters[mcp-proxy]
|
|
22
|
+
|
|
23
|
+
References:
|
|
24
|
+
- Managed MCP: https://docs.databricks.com/aws/en/generative-ai/mcp/managed-mcp
|
|
25
|
+
- Unity Catalog: https://docs.databricks.com/en/data-governance/unity-catalog/
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
export DATABRICKS_HOST="https://your-workspace.databricks.com"
|
|
29
|
+
export DATABRICKS_TOKEN="dapi..."
|
|
30
|
+
python 2d_proxy_databricks.py
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
import os
|
|
34
|
+
import logging
|
|
35
|
+
|
|
36
|
+
logging.basicConfig(level=logging.INFO)
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def example_databricks_sql_proxy():
|
|
41
|
+
"""
|
|
42
|
+
Connect to Databricks SQL MCP server through SecureMCPProxy.
|
|
43
|
+
|
|
44
|
+
Databricks Managed MCP servers use OAuth/PAT authentication
|
|
45
|
+
and expose SQL, Vector Search, Genie, and UC Functions.
|
|
46
|
+
"""
|
|
47
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
48
|
+
|
|
49
|
+
# Databricks workspace URL and token
|
|
50
|
+
workspace_host = os.environ.get(
|
|
51
|
+
"DATABRICKS_HOST",
|
|
52
|
+
"https://your-workspace.databricks.com"
|
|
53
|
+
)
|
|
54
|
+
token = os.environ.get("DATABRICKS_TOKEN", "")
|
|
55
|
+
|
|
56
|
+
# Databricks SQL MCP endpoint
|
|
57
|
+
# Pattern: https://<workspace>/api/2.0/mcp/sql
|
|
58
|
+
sql_mcp_url = f"{workspace_host}/api/2.0/mcp/sql"
|
|
59
|
+
|
|
60
|
+
# Create proxy with Databricks PAT authentication
|
|
61
|
+
proxy = SecureMCPProxy(
|
|
62
|
+
app_name="databricks-sql",
|
|
63
|
+
upstream_url=sql_mcp_url,
|
|
64
|
+
upstream_auth={
|
|
65
|
+
"type": "bearer",
|
|
66
|
+
"token": token
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
print(f"\n{proxy}")
|
|
71
|
+
|
|
72
|
+
# List discovered tools
|
|
73
|
+
print("\nDiscovered Databricks SQL tools:")
|
|
74
|
+
for tool in proxy.list_tools():
|
|
75
|
+
print(f" - {tool['name']}: {tool.get('description', '')[:60]}...")
|
|
76
|
+
|
|
77
|
+
# Example: Execute SQL query
|
|
78
|
+
try:
|
|
79
|
+
result = proxy.call_tool("execute_sql", {
|
|
80
|
+
"query": "SELECT current_user(), current_catalog(), current_schema()"
|
|
81
|
+
})
|
|
82
|
+
print(f"\nSQL result: {result}")
|
|
83
|
+
except ValueError as e:
|
|
84
|
+
print(f"\nSQL tool not available: {e}")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print(f"\nSQL error: {e}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def example_databricks_vector_search():
|
|
90
|
+
"""
|
|
91
|
+
Connect to Databricks Vector Search MCP server.
|
|
92
|
+
|
|
93
|
+
Use case: RAG applications querying indexed documents.
|
|
94
|
+
"""
|
|
95
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
96
|
+
|
|
97
|
+
workspace_host = os.environ.get("DATABRICKS_HOST", "https://your-workspace.databricks.com")
|
|
98
|
+
token = os.environ.get("DATABRICKS_TOKEN", "")
|
|
99
|
+
|
|
100
|
+
# Vector Search MCP endpoint
|
|
101
|
+
# Pattern: https://<workspace>/api/2.0/mcp/vector-search/{catalog}/{schema}/{index}
|
|
102
|
+
catalog = os.environ.get("DATABRICKS_CATALOG", "main")
|
|
103
|
+
schema = os.environ.get("DATABRICKS_SCHEMA", "default")
|
|
104
|
+
index_name = os.environ.get("DATABRICKS_VS_INDEX", "documents_index")
|
|
105
|
+
|
|
106
|
+
vs_mcp_url = f"{workspace_host}/api/2.0/mcp/vector-search/{catalog}/{schema}/{index_name}"
|
|
107
|
+
|
|
108
|
+
proxy = SecureMCPProxy(
|
|
109
|
+
app_name="databricks-vector-search",
|
|
110
|
+
upstream_url=vs_mcp_url,
|
|
111
|
+
upstream_auth={"type": "bearer", "token": token}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
print(f"\n{proxy}")
|
|
115
|
+
|
|
116
|
+
# Example: Search documents
|
|
117
|
+
try:
|
|
118
|
+
result = proxy.call_tool("search", {
|
|
119
|
+
"query": "How do I configure Unity Catalog?",
|
|
120
|
+
"num_results": 5
|
|
121
|
+
})
|
|
122
|
+
print(f"\nVector search result: {result}")
|
|
123
|
+
except ValueError as e:
|
|
124
|
+
print(f"\nSearch tool not available: {e}")
|
|
125
|
+
except Exception as e:
|
|
126
|
+
print(f"\nSearch error: {e}")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def example_databricks_genie():
|
|
130
|
+
"""
|
|
131
|
+
Connect to Databricks Genie Space MCP server.
|
|
132
|
+
|
|
133
|
+
Use case: Natural language queries over structured data.
|
|
134
|
+
"""
|
|
135
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
136
|
+
|
|
137
|
+
workspace_host = os.environ.get("DATABRICKS_HOST", "https://your-workspace.databricks.com")
|
|
138
|
+
token = os.environ.get("DATABRICKS_TOKEN", "")
|
|
139
|
+
|
|
140
|
+
# Genie Space MCP endpoint
|
|
141
|
+
# Pattern: https://<workspace>/api/2.0/mcp/genie/{genie_space_id}
|
|
142
|
+
genie_space_id = os.environ.get("DATABRICKS_GENIE_SPACE", "your-genie-space-id")
|
|
143
|
+
|
|
144
|
+
genie_mcp_url = f"{workspace_host}/api/2.0/mcp/genie/{genie_space_id}"
|
|
145
|
+
|
|
146
|
+
proxy = SecureMCPProxy(
|
|
147
|
+
app_name="databricks-genie",
|
|
148
|
+
upstream_url=genie_mcp_url,
|
|
149
|
+
upstream_auth={"type": "bearer", "token": token}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
print(f"\n{proxy}")
|
|
153
|
+
|
|
154
|
+
# Example: Ask Genie a question
|
|
155
|
+
try:
|
|
156
|
+
result = proxy.call_tool("ask", {
|
|
157
|
+
"question": "What were the top 5 products by revenue last quarter?"
|
|
158
|
+
})
|
|
159
|
+
print(f"\nGenie result: {result}")
|
|
160
|
+
except ValueError as e:
|
|
161
|
+
print(f"\nGenie tool not available: {e}")
|
|
162
|
+
except Exception as e:
|
|
163
|
+
print(f"\nGenie error: {e}")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def example_databricks_multi_user():
|
|
167
|
+
"""
|
|
168
|
+
Multi-user pattern: data analysts with different Unity Catalog permissions.
|
|
169
|
+
|
|
170
|
+
Use case: Data platform where teams have different catalog access.
|
|
171
|
+
"""
|
|
172
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
173
|
+
from macaw_client import MACAWClient
|
|
174
|
+
|
|
175
|
+
workspace_host = os.environ.get("DATABRICKS_HOST", "https://your-workspace.databricks.com")
|
|
176
|
+
token = os.environ.get("DATABRICKS_TOKEN", "")
|
|
177
|
+
|
|
178
|
+
# Service creates shared proxy
|
|
179
|
+
proxy = SecureMCPProxy(
|
|
180
|
+
app_name="databricks-data-platform",
|
|
181
|
+
upstream_url=f"{workspace_host}/api/2.0/mcp/sql",
|
|
182
|
+
upstream_auth={"type": "bearer", "token": token}
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Data Engineer - full access
|
|
186
|
+
engineer = MACAWClient(
|
|
187
|
+
user_name="data-engineer-sam",
|
|
188
|
+
jwt_token=os.environ.get("SAM_JWT", "sam-jwt"),
|
|
189
|
+
agent_type="user"
|
|
190
|
+
)
|
|
191
|
+
engineer.register()
|
|
192
|
+
engineer_proxy = proxy.bind_to_user(engineer)
|
|
193
|
+
|
|
194
|
+
# Analyst - read-only access
|
|
195
|
+
analyst = MACAWClient(
|
|
196
|
+
user_name="analyst-kim",
|
|
197
|
+
jwt_token=os.environ.get("KIM_JWT", "kim-jwt"),
|
|
198
|
+
agent_type="user"
|
|
199
|
+
)
|
|
200
|
+
analyst.register()
|
|
201
|
+
analyst_proxy = proxy.bind_to_user(analyst)
|
|
202
|
+
|
|
203
|
+
print("\nMulti-user Databricks access:")
|
|
204
|
+
print(f" Engineer (Sam): {engineer_proxy}")
|
|
205
|
+
print(f" Analyst (Kim): {analyst_proxy}")
|
|
206
|
+
|
|
207
|
+
# Sam can create tables
|
|
208
|
+
try:
|
|
209
|
+
result = engineer_proxy.call_tool("execute_sql", {
|
|
210
|
+
"statement": "CREATE TABLE IF NOT EXISTS analytics.metrics (id INT, value DOUBLE)"
|
|
211
|
+
})
|
|
212
|
+
print(f"\nSam's CREATE result: {result}")
|
|
213
|
+
except PermissionError as e:
|
|
214
|
+
print(f"\nSam denied: {e}")
|
|
215
|
+
except Exception as e:
|
|
216
|
+
print(f"\nSam error: {e}")
|
|
217
|
+
|
|
218
|
+
# Kim can only query
|
|
219
|
+
try:
|
|
220
|
+
result = analyst_proxy.call_tool("execute_sql", {
|
|
221
|
+
"statement": "SELECT * FROM analytics.metrics LIMIT 10"
|
|
222
|
+
})
|
|
223
|
+
print(f"\nKim's SELECT result: {result}")
|
|
224
|
+
except PermissionError as e:
|
|
225
|
+
print(f"\nKim denied: {e}")
|
|
226
|
+
except Exception as e:
|
|
227
|
+
print(f"\nKim error: {e}")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def setup_instructions():
|
|
231
|
+
"""Print setup instructions for Databricks MCP."""
|
|
232
|
+
print("""
|
|
233
|
+
================================================================================
|
|
234
|
+
Databricks Managed MCP Server Setup
|
|
235
|
+
================================================================================
|
|
236
|
+
|
|
237
|
+
1. Get Databricks workspace:
|
|
238
|
+
- Enterprise: Contact Databricks
|
|
239
|
+
- Community Edition (free): https://community.cloud.databricks.com
|
|
240
|
+
|
|
241
|
+
2. Enable MCP in your workspace:
|
|
242
|
+
- Go to: Workspace > Agents > MCP Servers
|
|
243
|
+
- View available MCP endpoints
|
|
244
|
+
|
|
245
|
+
3. Create Personal Access Token (PAT):
|
|
246
|
+
- User Settings > Developer > Access Tokens
|
|
247
|
+
- Generate new token with appropriate permissions
|
|
248
|
+
|
|
249
|
+
4. Set environment variables:
|
|
250
|
+
export DATABRICKS_HOST="https://your-workspace.databricks.com"
|
|
251
|
+
export DATABRICKS_TOKEN="dapi..."
|
|
252
|
+
|
|
253
|
+
5. MCP Endpoint Patterns:
|
|
254
|
+
- SQL: {host}/api/2.0/mcp/sql
|
|
255
|
+
- Vector Search: {host}/api/2.0/mcp/vector-search/{catalog}/{schema}/{index}
|
|
256
|
+
- Genie: {host}/api/2.0/mcp/genie/{genie_space_id}
|
|
257
|
+
- UC Functions: {host}/api/2.0/mcp/functions/{catalog}/{schema}/{function}
|
|
258
|
+
|
|
259
|
+
6. Run this example:
|
|
260
|
+
python 2d_proxy_databricks.py
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
ALTERNATIVE: Community MCP Server (stdio-based)
|
|
264
|
+
-----------------------------------------------
|
|
265
|
+
|
|
266
|
+
For local development with stdio transport:
|
|
267
|
+
|
|
268
|
+
1. Install: pip install databricks-mcp-server
|
|
269
|
+
2. Run: uvx databricks-mcp-server@latest
|
|
270
|
+
|
|
271
|
+
Note: stdio servers require an HTTP bridge (mcp-proxy) for SecureMCPProxy.
|
|
272
|
+
|
|
273
|
+
================================================================================
|
|
274
|
+
""")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
if __name__ == "__main__":
|
|
278
|
+
print("=" * 60)
|
|
279
|
+
print("SecureMCPProxy + Databricks MCP Server")
|
|
280
|
+
print("=" * 60)
|
|
281
|
+
|
|
282
|
+
setup_instructions()
|
|
283
|
+
|
|
284
|
+
print("\n--- Example: Databricks SQL Proxy ---")
|
|
285
|
+
try:
|
|
286
|
+
example_databricks_sql_proxy()
|
|
287
|
+
except ConnectionError as e:
|
|
288
|
+
print(f"\nConnection failed: {e}")
|
|
289
|
+
print("Make sure Databricks workspace is accessible and MCP is enabled.")
|
|
290
|
+
|
|
291
|
+
# Uncomment to test other examples:
|
|
292
|
+
# print("\n--- Example: Vector Search ---")
|
|
293
|
+
# example_databricks_vector_search()
|
|
294
|
+
|
|
295
|
+
# print("\n--- Example: Genie Space ---")
|
|
296
|
+
# example_databricks_genie()
|
|
297
|
+
|
|
298
|
+
# print("\n--- Example: Multi-User Access ---")
|
|
299
|
+
# example_databricks_multi_user()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example: SecureMCPProxy with mcp-server-fetch (stdio transport)
|
|
4
|
+
|
|
5
|
+
Demonstrates SecureMCPProxy connecting to a standard MCP server via stdio.
|
|
6
|
+
This validates the stdio transport with a simple, credential-free MCP server.
|
|
7
|
+
|
|
8
|
+
Security Model:
|
|
9
|
+
- MACAW creates AuthenticatedContext for EACH invocation
|
|
10
|
+
- Even with persistent subprocess, each call gets fresh identity/policy/signature
|
|
11
|
+
- Solves the "shared context" problem in standard MCP
|
|
12
|
+
|
|
13
|
+
Prerequisites:
|
|
14
|
+
pip install mcp-server-fetch
|
|
15
|
+
pip install macaw-adapters[mcp-proxy]
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
python 2e_proxy_stdio_fetch.py
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import sys
|
|
22
|
+
import logging
|
|
23
|
+
|
|
24
|
+
logging.basicConfig(level=logging.INFO)
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main():
|
|
29
|
+
"""Test SecureMCPProxy with mcp-server-fetch."""
|
|
30
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
31
|
+
|
|
32
|
+
print("=" * 60)
|
|
33
|
+
print("SecureMCPProxy + mcp-server-fetch (stdio)")
|
|
34
|
+
print("=" * 60)
|
|
35
|
+
|
|
36
|
+
# Step 1: Check mcp-server-fetch is installed
|
|
37
|
+
print("\n1. Checking mcp-server-fetch installation...")
|
|
38
|
+
try:
|
|
39
|
+
import mcp_server_fetch
|
|
40
|
+
print(" OK: mcp-server-fetch is installed")
|
|
41
|
+
except ImportError:
|
|
42
|
+
print(" ERROR: mcp-server-fetch not installed")
|
|
43
|
+
print(" Fix: pip install mcp-server-fetch")
|
|
44
|
+
return 1
|
|
45
|
+
|
|
46
|
+
# Step 2: Create proxy with stdio transport
|
|
47
|
+
print("\n2. Creating SecureMCPProxy with stdio transport...")
|
|
48
|
+
try:
|
|
49
|
+
proxy = SecureMCPProxy(
|
|
50
|
+
app_name="fetch-stdio-test",
|
|
51
|
+
command=["python", "-m", "mcp_server_fetch"],
|
|
52
|
+
)
|
|
53
|
+
print(f" OK: {proxy}")
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print(f" ERROR: {e}")
|
|
56
|
+
return 1
|
|
57
|
+
|
|
58
|
+
# Step 3: Discover tools
|
|
59
|
+
print("\n3. Discovering tools...")
|
|
60
|
+
tools = proxy.list_tools()
|
|
61
|
+
print(f" Found {len(tools)} tools:")
|
|
62
|
+
for tool in tools:
|
|
63
|
+
desc = tool.get('description', '')[:50]
|
|
64
|
+
print(f" - {tool['name']}: {desc}...")
|
|
65
|
+
|
|
66
|
+
if not tools:
|
|
67
|
+
print(" ERROR: No tools discovered")
|
|
68
|
+
return 1
|
|
69
|
+
|
|
70
|
+
# Step 4: Call fetch tool
|
|
71
|
+
print("\n4. Calling fetch tool (https://httpbin.org/html)...")
|
|
72
|
+
try:
|
|
73
|
+
result = proxy.call_tool("fetch", {"url": "https://httpbin.org/html"})
|
|
74
|
+
if result:
|
|
75
|
+
content = result.get('result', result) if isinstance(result, dict) else result
|
|
76
|
+
preview = str(content)[:200].replace('\n', ' ')
|
|
77
|
+
print(f" OK: {preview}...")
|
|
78
|
+
else:
|
|
79
|
+
print(" OK: (empty result)")
|
|
80
|
+
except Exception as e:
|
|
81
|
+
print(f" ERROR: {e}")
|
|
82
|
+
return 1
|
|
83
|
+
|
|
84
|
+
print("\n" + "=" * 60)
|
|
85
|
+
print("SUCCESS: SecureMCPProxy stdio transport works!")
|
|
86
|
+
print("=" * 60)
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
sys.exit(main())
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example: SecureMCPProxy with Salesforce DX MCP (stdio transport)
|
|
4
|
+
|
|
5
|
+
Demonstrates SecureMCPProxy connecting to Salesforce DX CLI via stdio.
|
|
6
|
+
Salesforce DX MCP provides tools for org management, deployment, and queries.
|
|
7
|
+
|
|
8
|
+
Security Model:
|
|
9
|
+
- MACAW creates AuthenticatedContext for EACH invocation
|
|
10
|
+
- Even with persistent subprocess, each call gets fresh identity/policy/signature
|
|
11
|
+
- Salesforce operations are policy-enforced and audited
|
|
12
|
+
|
|
13
|
+
Prerequisites:
|
|
14
|
+
1. Salesforce account (free Developer Edition: https://developer.salesforce.com/signup)
|
|
15
|
+
2. Salesforce CLI: npm install -g @salesforce/cli
|
|
16
|
+
3. Authenticated org: sf org login web
|
|
17
|
+
4. MACAW: pip install macaw-adapters[mcp-proxy]
|
|
18
|
+
|
|
19
|
+
NOTE: This example requires a Salesforce account. Skip if you don't have one -
|
|
20
|
+
the stdio pattern is validated by 2e_proxy_stdio_fetch.py.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
python 2f_proxy_stdio_salesforce.py
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
import os
|
|
27
|
+
import sys
|
|
28
|
+
import shutil
|
|
29
|
+
import logging
|
|
30
|
+
|
|
31
|
+
logging.basicConfig(level=logging.INFO)
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main():
|
|
36
|
+
"""Test SecureMCPProxy with Salesforce DX MCP."""
|
|
37
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
38
|
+
|
|
39
|
+
print("=" * 60)
|
|
40
|
+
print("SecureMCPProxy + Salesforce DX MCP (stdio)")
|
|
41
|
+
print("=" * 60)
|
|
42
|
+
|
|
43
|
+
# Step 1: Check npx is available
|
|
44
|
+
print("\n1. Checking prerequisites...")
|
|
45
|
+
if not shutil.which("npx"):
|
|
46
|
+
print(" ERROR: npx not found")
|
|
47
|
+
print(" Fix: Install Node.js (https://nodejs.org)")
|
|
48
|
+
return 1
|
|
49
|
+
print(" OK: npx found")
|
|
50
|
+
|
|
51
|
+
# Check if sf CLI is available
|
|
52
|
+
if not shutil.which("sf"):
|
|
53
|
+
print(" WARNING: sf CLI not found")
|
|
54
|
+
print(" You may need to install: npm install -g @salesforce/cli")
|
|
55
|
+
|
|
56
|
+
# Step 2: Create proxy with stdio transport
|
|
57
|
+
print("\n2. Creating SecureMCPProxy with Salesforce DX...")
|
|
58
|
+
try:
|
|
59
|
+
proxy = SecureMCPProxy(
|
|
60
|
+
app_name="salesforce-dx",
|
|
61
|
+
command=["npx", "@salesforce/mcp", "--orgs", "DEFAULT", "--dynamic-tools"],
|
|
62
|
+
env={
|
|
63
|
+
"HOME": os.environ.get("HOME", ""),
|
|
64
|
+
"PATH": os.environ.get("PATH", ""),
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
print(f" OK: {proxy}")
|
|
68
|
+
except ConnectionError as e:
|
|
69
|
+
print(f" ERROR: Failed to connect: {e}")
|
|
70
|
+
print("\n Troubleshooting:")
|
|
71
|
+
print(" 1. Install @salesforce/mcp: npm install -g @salesforce/mcp")
|
|
72
|
+
print(" 2. Login to an org: sf org login web")
|
|
73
|
+
print(" 3. Set default org: sf config set target-org=<alias>")
|
|
74
|
+
return 1
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print(f" ERROR: {e}")
|
|
77
|
+
return 1
|
|
78
|
+
|
|
79
|
+
# Step 3: Discover tools
|
|
80
|
+
print("\n3. Discovering Salesforce DX tools...")
|
|
81
|
+
tools = proxy.list_tools()
|
|
82
|
+
print(f" Found {len(tools)} tools:")
|
|
83
|
+
for tool in tools[:10]: # Show first 10
|
|
84
|
+
print(f" - {tool['name']}")
|
|
85
|
+
if len(tools) > 10:
|
|
86
|
+
print(f" ... and {len(tools) - 10} more")
|
|
87
|
+
|
|
88
|
+
if not tools:
|
|
89
|
+
print(" ERROR: No tools discovered")
|
|
90
|
+
return 1
|
|
91
|
+
|
|
92
|
+
# Step 4: Test tool calls
|
|
93
|
+
print("\n4. Testing tool calls...")
|
|
94
|
+
|
|
95
|
+
# Get current username (requires directory - use current dir)
|
|
96
|
+
try:
|
|
97
|
+
result = proxy.call_tool("get_username", {"directory": os.getcwd()})
|
|
98
|
+
content = result.get('result', result) if isinstance(result, dict) else result
|
|
99
|
+
print(f" get_username: {content}")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
print(f" get_username error: {e}")
|
|
102
|
+
|
|
103
|
+
# List available tools (dynamic)
|
|
104
|
+
try:
|
|
105
|
+
result = proxy.call_tool("list_tools", {})
|
|
106
|
+
content = result.get('result', result) if isinstance(result, dict) else result
|
|
107
|
+
preview = str(content)[:300] if content else "(empty)"
|
|
108
|
+
print(f" list_tools: {preview}...")
|
|
109
|
+
except Exception as e:
|
|
110
|
+
print(f" list_tools error: {e}")
|
|
111
|
+
|
|
112
|
+
print("\n" + "=" * 60)
|
|
113
|
+
print("SUCCESS: SecureMCPProxy + Salesforce DX works!")
|
|
114
|
+
print("=" * 60)
|
|
115
|
+
return 0
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
if __name__ == "__main__":
|
|
119
|
+
sys.exit(main())
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Example: SecureMCPProxy Multi-User Pattern
|
|
4
|
+
|
|
5
|
+
Demonstrates bind_to_user() for multi-tenant SaaS applications.
|
|
6
|
+
The service maintains the upstream MCP connection, but each request
|
|
7
|
+
runs with a specific user's identity and policy.
|
|
8
|
+
|
|
9
|
+
Security Model:
|
|
10
|
+
- Service creates ONE proxy to the upstream MCP server
|
|
11
|
+
- Each user gets a BoundMCPProxy via bind_to_user()
|
|
12
|
+
- User's calls are: evaluated against their policy, signed with their identity, logged under their audit trail
|
|
13
|
+
- Even with shared subprocess, MACAW provides per-user isolation
|
|
14
|
+
|
|
15
|
+
Prerequisites:
|
|
16
|
+
1. pip install mcp-server-fetch macaw-adapters[mcp-proxy]
|
|
17
|
+
2. Identity Provider configured (Console -> Settings -> Identity Bridge)
|
|
18
|
+
3. Test users: alice/Alice123!, bob/Bob@123!
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
python 2g_proxy_multiuser.py
|
|
22
|
+
|
|
23
|
+
No IdP configured? Use simpler examples first:
|
|
24
|
+
python 2e_proxy_stdio_fetch.py
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import os
|
|
28
|
+
import sys
|
|
29
|
+
import logging
|
|
30
|
+
|
|
31
|
+
logging.basicConfig(level=logging.INFO)
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main():
|
|
36
|
+
"""Test multi-user SecureMCPProxy pattern."""
|
|
37
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
38
|
+
from macaw_client import MACAWClient, RemoteIdentityProvider
|
|
39
|
+
|
|
40
|
+
print("=" * 60)
|
|
41
|
+
print("SecureMCPProxy Multi-User Pattern")
|
|
42
|
+
print("=" * 60)
|
|
43
|
+
|
|
44
|
+
# Step 1: Service creates shared proxy
|
|
45
|
+
print("\n1. Creating shared proxy (service-level)...")
|
|
46
|
+
try:
|
|
47
|
+
import mcp_server_fetch
|
|
48
|
+
except ImportError:
|
|
49
|
+
print(" ERROR: mcp-server-fetch not installed")
|
|
50
|
+
print(" Fix: pip install mcp-server-fetch")
|
|
51
|
+
return 1
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
proxy = SecureMCPProxy(
|
|
55
|
+
app_name="shared-fetch-proxy",
|
|
56
|
+
command=["python", "-m", "mcp_server_fetch"],
|
|
57
|
+
)
|
|
58
|
+
print(f" OK: {proxy}")
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f" ERROR: {e}")
|
|
61
|
+
return 1
|
|
62
|
+
|
|
63
|
+
# Step 2: Authenticate users
|
|
64
|
+
print("\n2. Authenticating users...")
|
|
65
|
+
|
|
66
|
+
users = {}
|
|
67
|
+
for username, password in [("alice", "Alice123!"), ("bob", "Bob@123!")]:
|
|
68
|
+
try:
|
|
69
|
+
print(f" Authenticating {username}...")
|
|
70
|
+
jwt_token, _ = RemoteIdentityProvider().login(username, password)
|
|
71
|
+
|
|
72
|
+
user = MACAWClient(
|
|
73
|
+
user_name=username,
|
|
74
|
+
iam_token=jwt_token,
|
|
75
|
+
agent_type="user",
|
|
76
|
+
app_name="mcp-multiuser-test"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if not user.register():
|
|
80
|
+
print(f" ERROR: Failed to register {username}")
|
|
81
|
+
continue
|
|
82
|
+
|
|
83
|
+
users[username] = user
|
|
84
|
+
print(f" OK: {username} -> {user.agent_id}")
|
|
85
|
+
|
|
86
|
+
except Exception as e:
|
|
87
|
+
print(f" ERROR: {username} authentication failed: {e}")
|
|
88
|
+
print(" Make sure IdP is configured in Console -> Settings -> Identity Bridge")
|
|
89
|
+
return 1
|
|
90
|
+
|
|
91
|
+
if len(users) < 2:
|
|
92
|
+
print("\n ERROR: Could not authenticate both users")
|
|
93
|
+
return 1
|
|
94
|
+
|
|
95
|
+
# Step 3: Create per-user bound proxies
|
|
96
|
+
print("\n3. Creating per-user proxies via bind_to_user()...")
|
|
97
|
+
alice_proxy = proxy.bind_to_user(users["alice"])
|
|
98
|
+
bob_proxy = proxy.bind_to_user(users["bob"])
|
|
99
|
+
print(f" Alice: {alice_proxy}")
|
|
100
|
+
print(f" Bob: {bob_proxy}")
|
|
101
|
+
|
|
102
|
+
# Step 4: Test user-specific calls
|
|
103
|
+
print("\n4. Testing user-specific tool calls...")
|
|
104
|
+
|
|
105
|
+
test_urls = [
|
|
106
|
+
("alice", alice_proxy, "https://alice.example.com"),
|
|
107
|
+
("bob", bob_proxy, "https://bob.example.com"),
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
for username, user_proxy, url in test_urls:
|
|
111
|
+
print(f"\n {username.upper()}'s call to {url}:")
|
|
112
|
+
try:
|
|
113
|
+
result = user_proxy.call_tool("fetch", {"url": url})
|
|
114
|
+
preview = str(result)[:80].replace('\n', ' ')
|
|
115
|
+
print(f" OK: {preview}...")
|
|
116
|
+
print(f" (Policy: {username}'s policy applied)")
|
|
117
|
+
print(f" (Signed: {username}'s identity)")
|
|
118
|
+
print(f" (Audit: logged under {username})")
|
|
119
|
+
except PermissionError as e:
|
|
120
|
+
print(f" BLOCKED by policy: {e}")
|
|
121
|
+
except Exception as e:
|
|
122
|
+
print(f" ERROR: {e}")
|
|
123
|
+
|
|
124
|
+
print("\n" + "=" * 60)
|
|
125
|
+
print("SUCCESS: Multi-user SecureMCPProxy works!")
|
|
126
|
+
print("=" * 60)
|
|
127
|
+
print("\nKey points:")
|
|
128
|
+
print(" - Same subprocess, different identities")
|
|
129
|
+
print(" - Each call has fresh AuthenticatedContext")
|
|
130
|
+
print(" - Policy enforcement per user")
|
|
131
|
+
print(" - Audit trail tracks who did what")
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
sys.exit(main())
|