@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,460 @@
|
|
|
1
|
+
# SecureMCP Reference
|
|
2
|
+
|
|
3
|
+
SecureMCP provides enterprise-grade security for Model Context Protocol through integration with MACAW Control Plane.
|
|
4
|
+
|
|
5
|
+
**Two main components:**
|
|
6
|
+
1. **SecureMCP** — For MCP servers YOU write (FastMCP-compatible decorators)
|
|
7
|
+
2. **SecureMCPProxy** — For external MCP servers you DON'T control (inline gateway)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## SecureMCP (Your MCP Servers)
|
|
12
|
+
|
|
13
|
+
FastMCP-compatible decorator API with MACAW security. All tool invocations are cryptographically signed, policy-enforced, and audit-logged.
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from macaw_adapters.mcp import SecureMCP, Context
|
|
17
|
+
|
|
18
|
+
mcp = SecureMCP("calculator")
|
|
19
|
+
|
|
20
|
+
@mcp.tool(description="Add two numbers")
|
|
21
|
+
def add(a: float, b: float) -> float:
|
|
22
|
+
return a + b
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
mcp.run()
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Constructor
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
SecureMCP(
|
|
32
|
+
name: str, # Server name (required)
|
|
33
|
+
version: str = "1.0.0", # Server version
|
|
34
|
+
intent_policy: dict = None, # MAPL policy declaration
|
|
35
|
+
roots: list[str] = None, # Filesystem paths server can access
|
|
36
|
+
**kwargs # Additional MACAWClient options
|
|
37
|
+
)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Parameter | Type | Default | Description |
|
|
41
|
+
|-----------|------|---------|-------------|
|
|
42
|
+
| `name` | str | required | Server name for MACAW registration |
|
|
43
|
+
| `version` | str | `"1.0.0"` | Server version |
|
|
44
|
+
| `intent_policy` | dict | `None` | MAPL policy (resources, denied_resources, etc.) |
|
|
45
|
+
| `roots` | list | `None` | Filesystem paths this server can access (MCP roots) |
|
|
46
|
+
|
|
47
|
+
### Decorators
|
|
48
|
+
|
|
49
|
+
#### `@mcp.tool(name, description, prompts)`
|
|
50
|
+
|
|
51
|
+
Register a tool that can be invoked by clients.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
@mcp.tool(description="Add two numbers")
|
|
55
|
+
def add(a: float, b: float) -> float:
|
|
56
|
+
return a + b
|
|
57
|
+
|
|
58
|
+
@mcp.tool(description="Calculate with history")
|
|
59
|
+
def calculate(ctx: Context, op: str, a: float, b: float) -> dict:
|
|
60
|
+
# ctx parameter gives access to context vault
|
|
61
|
+
history = ctx.get("calc_history") or []
|
|
62
|
+
result = a + b if op == "add" else a * b
|
|
63
|
+
history.append({"op": op, "result": result})
|
|
64
|
+
ctx.set("calc_history", history)
|
|
65
|
+
return {"result": result}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Parameter | Type | Description |
|
|
69
|
+
|-----------|------|-------------|
|
|
70
|
+
| `name` | str | Tool name (defaults to function name) |
|
|
71
|
+
| `description` | str | Tool description (defaults to docstring) |
|
|
72
|
+
| `prompts` | list | Parameter names to treat as AuthenticatedPrompts |
|
|
73
|
+
|
|
74
|
+
#### `@mcp.resource(uri_pattern, description)`
|
|
75
|
+
|
|
76
|
+
Register a read-only resource.
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
@mcp.resource("calc://history")
|
|
80
|
+
def get_history(ctx: Context) -> dict:
|
|
81
|
+
return {"history": ctx.get("calc_history") or []}
|
|
82
|
+
|
|
83
|
+
@mcp.resource("config://{key}")
|
|
84
|
+
def get_config(ctx: Context, key: str) -> dict:
|
|
85
|
+
return {"key": key, "value": "..."}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
#### `@mcp.prompt(name, description)`
|
|
89
|
+
|
|
90
|
+
Register a prompt template.
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
@mcp.prompt(description="Generate a greeting")
|
|
94
|
+
def greeting(name: str) -> str:
|
|
95
|
+
return f"Hello, {name}! How can I help?"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Lifecycle
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
mcp.run(transport="stdio") # Blocking run (stdio or sse)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Properties
|
|
105
|
+
|
|
106
|
+
| Property | Type | Description |
|
|
107
|
+
|----------|------|-------------|
|
|
108
|
+
| `mcp.name` | str | Server name |
|
|
109
|
+
| `mcp.version` | str | Server version |
|
|
110
|
+
| `mcp.agent_id` | str | MACAW agent ID (after run) |
|
|
111
|
+
| `mcp.roots` | list | Filesystem roots |
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Context
|
|
116
|
+
|
|
117
|
+
Request context passed to tool handlers. Provides access to context vault, progress reporting, sampling, and elicitation.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
@mcp.tool()
|
|
121
|
+
def my_tool(ctx: Context, param: str) -> dict:
|
|
122
|
+
# Context vault (persistent storage)
|
|
123
|
+
ctx.set("key", "value")
|
|
124
|
+
data = ctx.get("key")
|
|
125
|
+
|
|
126
|
+
# Logging
|
|
127
|
+
ctx.info("Processing request")
|
|
128
|
+
ctx.audit("data_access", target="records", outcome="success")
|
|
129
|
+
|
|
130
|
+
return {"result": data}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Methods
|
|
134
|
+
|
|
135
|
+
| Method | Description |
|
|
136
|
+
|--------|-------------|
|
|
137
|
+
| `ctx.get(key)` | Get value from context vault |
|
|
138
|
+
| `ctx.set(key, value)` | Set value in context vault |
|
|
139
|
+
| `await ctx.report_progress(progress, message)` | Report progress (0.0-1.0) |
|
|
140
|
+
| `await ctx.read_resource(uri)` | Read another resource |
|
|
141
|
+
| `await ctx.sample(prompt, ...)` | Request LLM completion from client |
|
|
142
|
+
| `await ctx.elicit(prompt, ...)` | Request user input from client |
|
|
143
|
+
|
|
144
|
+
### Logging Methods
|
|
145
|
+
|
|
146
|
+
| Method | Description |
|
|
147
|
+
|--------|-------------|
|
|
148
|
+
| `ctx.debug(message)` | Log debug message |
|
|
149
|
+
| `ctx.info(message)` | Log info message |
|
|
150
|
+
| `ctx.warning(message)` | Log warning message |
|
|
151
|
+
| `ctx.error(message)` | Log error message |
|
|
152
|
+
| `ctx.audit(action, target, outcome, **metadata)` | Cryptographically signed audit entry |
|
|
153
|
+
|
|
154
|
+
### Sampling (MCP Sampling)
|
|
155
|
+
|
|
156
|
+
Request LLM completion from the calling client:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
@mcp.tool()
|
|
160
|
+
async def analyze(ctx: Context, text: str) -> dict:
|
|
161
|
+
summary = await ctx.sample(
|
|
162
|
+
prompt=f"Summarize: {text}",
|
|
163
|
+
system_prompt="You are a helpful assistant.",
|
|
164
|
+
max_tokens=500
|
|
165
|
+
)
|
|
166
|
+
return {"summary": summary}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Note on `temperature`:** it is unset by default and is not sent unless you pass
|
|
170
|
+
one. The server does not know which model the client will use, and current Claude
|
|
171
|
+
models reject sampling parameters. Pass a temperature only if you know the client's
|
|
172
|
+
model accepts it — and expect the client may ignore it regardless (MCP treats
|
|
173
|
+
sampling parameters as hints).
|
|
174
|
+
|
|
175
|
+
### Elicitation (MCP Elicitation)
|
|
176
|
+
|
|
177
|
+
Request user input during tool execution:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
@mcp.tool()
|
|
181
|
+
async def delete_file(ctx: Context, path: str) -> dict:
|
|
182
|
+
confirmed = await ctx.elicit(
|
|
183
|
+
prompt=f"Delete {path}?",
|
|
184
|
+
input_type="confirm",
|
|
185
|
+
default="no"
|
|
186
|
+
)
|
|
187
|
+
if confirmed:
|
|
188
|
+
# perform deletion
|
|
189
|
+
return {"deleted": path}
|
|
190
|
+
return {"cancelled": True}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## SecureMCPProxy (External MCP Servers)
|
|
196
|
+
|
|
197
|
+
Inline gateway that wraps external MCP servers (Snowflake, Salesforce, Databricks, etc.) with MACAW security.
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
201
|
+
|
|
202
|
+
# HTTP transport (remote MCP servers)
|
|
203
|
+
proxy = SecureMCPProxy(
|
|
204
|
+
app_name="snowflake-mcp",
|
|
205
|
+
upstream_url="http://localhost:9000/mcp"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# stdio transport (local MCP servers via subprocess)
|
|
209
|
+
proxy = SecureMCPProxy(
|
|
210
|
+
app_name="salesforce-dx",
|
|
211
|
+
command=["npx", "@salesforce/mcp", "--orgs", "DEFAULT"],
|
|
212
|
+
env={"HOME": os.environ["HOME"]}
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Use tools
|
|
216
|
+
tools = proxy.list_tools()
|
|
217
|
+
result = proxy.call_tool("run_snowflake_query", {"statement": "SELECT 1"})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Constructor
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
SecureMCPProxy(
|
|
224
|
+
app_name: str, # Application name (required)
|
|
225
|
+
# HTTP transport
|
|
226
|
+
upstream_url: str = None, # URL of upstream MCP server
|
|
227
|
+
upstream_auth: dict = None, # Auth config (see below)
|
|
228
|
+
# stdio transport
|
|
229
|
+
command: list[str] = None, # Command to start MCP server
|
|
230
|
+
env: dict = None, # Environment variables
|
|
231
|
+
# Identity
|
|
232
|
+
iam_token: str = None, # IAM token for user identity
|
|
233
|
+
user_name: str = None, # User name
|
|
234
|
+
intent_policy: dict = None # MAPL intent policy
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
| Parameter | Type | Description |
|
|
239
|
+
|-----------|------|-------------|
|
|
240
|
+
| `app_name` | str | Application name for MACAW registration |
|
|
241
|
+
| `upstream_url` | str | URL for HTTP transport |
|
|
242
|
+
| `upstream_auth` | dict | Auth: `{"type": "bearer", "token": "..."}` or `{"type": "api_key", "api_key": "...", "header_name": "X-API-Key"}` |
|
|
243
|
+
| `command` | list | Command for stdio transport |
|
|
244
|
+
| `env` | dict | Environment variables for stdio subprocess |
|
|
245
|
+
| `iam_token` | str | IAM token for authenticated identity |
|
|
246
|
+
| `user_name` | str | User name (used with local identity) |
|
|
247
|
+
| `intent_policy` | dict | MAPL policy for this proxy |
|
|
248
|
+
|
|
249
|
+
**Note:** Provide either `upstream_url` (HTTP) or `command` (stdio), not both.
|
|
250
|
+
|
|
251
|
+
### Methods
|
|
252
|
+
|
|
253
|
+
#### `call_tool(tool_name, params) -> Any`
|
|
254
|
+
|
|
255
|
+
Call a tool through MACAW security.
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
result = proxy.call_tool("run_snowflake_query", {
|
|
259
|
+
"statement": "SELECT * FROM customers LIMIT 10"
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Flow:
|
|
264
|
+
1. MACAWClient.invoke_tool() checks MAPL policy
|
|
265
|
+
2. Signs the invocation cryptographically
|
|
266
|
+
3. Logs to audit trail
|
|
267
|
+
4. Proxy handler forwards to upstream MCP server
|
|
268
|
+
|
|
269
|
+
#### `list_tools() -> list[dict]`
|
|
270
|
+
|
|
271
|
+
List available tools discovered from upstream.
|
|
272
|
+
|
|
273
|
+
```python
|
|
274
|
+
tools = proxy.list_tools()
|
|
275
|
+
# Returns: [{"name": "run_query", "description": "...", "schema": {...}}]
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### `get_tool_schema(tool_name) -> dict`
|
|
279
|
+
|
|
280
|
+
Get schema for a specific tool.
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
schema = proxy.get_tool_schema("run_snowflake_query")
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### `bind_to_user(user_client) -> BoundMCPProxy`
|
|
287
|
+
|
|
288
|
+
Bind proxy to a specific user's identity for multi-tenant applications.
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from macaw_client import MACAWClient, RemoteIdentityProvider
|
|
292
|
+
|
|
293
|
+
# Authenticate user via Keycloak/Okta
|
|
294
|
+
jwt, _ = RemoteIdentityProvider().login("alice", "Alice123!")
|
|
295
|
+
alice = MACAWClient(user_name="alice", iam_token=jwt, app_name="my-app")
|
|
296
|
+
alice.register()
|
|
297
|
+
|
|
298
|
+
# Bind proxy to Alice's identity
|
|
299
|
+
alice_proxy = proxy.bind_to_user(alice)
|
|
300
|
+
|
|
301
|
+
# Calls now use Alice's identity and policy
|
|
302
|
+
result = alice_proxy.call_tool("run_snowflake_query", {...})
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### `refresh_tools()`
|
|
306
|
+
|
|
307
|
+
Re-discover tools from upstream (if tools may have changed).
|
|
308
|
+
|
|
309
|
+
```python
|
|
310
|
+
proxy.refresh_tools()
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Properties
|
|
314
|
+
|
|
315
|
+
| Property | Type | Description |
|
|
316
|
+
|----------|------|-------------|
|
|
317
|
+
| `proxy.app_name` | str | Application name |
|
|
318
|
+
| `proxy.upstream_url` | str | Upstream URL |
|
|
319
|
+
| `proxy.agent_id` | str | MACAW agent ID |
|
|
320
|
+
| `proxy.server_id` | str | Alias for agent_id |
|
|
321
|
+
| `proxy.tool_schemas` | dict | Discovered tool schemas |
|
|
322
|
+
| `proxy.is_connected` | bool | Connection status |
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## BoundMCPProxy
|
|
327
|
+
|
|
328
|
+
MCP Proxy bound to a specific user's identity. All calls use the user's policy and audit trail.
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
bound_proxy = proxy.bind_to_user(user_client)
|
|
332
|
+
|
|
333
|
+
# Methods (same as SecureMCPProxy)
|
|
334
|
+
result = bound_proxy.call_tool("tool_name", params)
|
|
335
|
+
tools = bound_proxy.list_tools()
|
|
336
|
+
schema = bound_proxy.get_tool_schema("tool_name")
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Examples
|
|
342
|
+
|
|
343
|
+
### SecureMCP Calculator Server
|
|
344
|
+
|
|
345
|
+
```python
|
|
346
|
+
from macaw_adapters.mcp import SecureMCP, Context
|
|
347
|
+
|
|
348
|
+
mcp = SecureMCP("calculator")
|
|
349
|
+
|
|
350
|
+
@mcp.tool(description="Add two numbers")
|
|
351
|
+
def add(a: float, b: float) -> float:
|
|
352
|
+
return a + b
|
|
353
|
+
|
|
354
|
+
@mcp.tool(description="Multiply two numbers")
|
|
355
|
+
def multiply(a: float, b: float) -> float:
|
|
356
|
+
return a * b
|
|
357
|
+
|
|
358
|
+
@mcp.tool(description="Calculate with history")
|
|
359
|
+
def calculate(ctx: Context, op: str, a: float, b: float) -> dict:
|
|
360
|
+
history = ctx.get("calc_history") or []
|
|
361
|
+
result = a + b if op == "add" else a * b
|
|
362
|
+
history.append({"op": op, "a": a, "b": b, "result": result})
|
|
363
|
+
ctx.set("calc_history", history)
|
|
364
|
+
return {"result": result, "history_count": len(history)}
|
|
365
|
+
|
|
366
|
+
@mcp.resource("calc://history")
|
|
367
|
+
def get_history(ctx: Context) -> dict:
|
|
368
|
+
return {"history": ctx.get("calc_history") or []}
|
|
369
|
+
|
|
370
|
+
if __name__ == "__main__":
|
|
371
|
+
mcp.run()
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### SecureMCPProxy with Snowflake
|
|
375
|
+
|
|
376
|
+
```python
|
|
377
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
378
|
+
|
|
379
|
+
proxy = SecureMCPProxy(
|
|
380
|
+
app_name="snowflake-data",
|
|
381
|
+
upstream_url="http://localhost:9000/mcp"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
# List available tools
|
|
385
|
+
for tool in proxy.list_tools():
|
|
386
|
+
print(f" {tool['name']}: {tool['description']}")
|
|
387
|
+
|
|
388
|
+
# Query data
|
|
389
|
+
result = proxy.call_tool("run_snowflake_query", {
|
|
390
|
+
"statement": "SELECT CURRENT_USER(), CURRENT_ROLE()"
|
|
391
|
+
})
|
|
392
|
+
print(result)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Multi-User SecureMCPProxy
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
399
|
+
from macaw_client import MACAWClient, RemoteIdentityProvider
|
|
400
|
+
|
|
401
|
+
# Service creates shared proxy
|
|
402
|
+
proxy = SecureMCPProxy(
|
|
403
|
+
app_name="data-platform",
|
|
404
|
+
upstream_url="http://localhost:9000/mcp"
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
# Authenticate users via Keycloak
|
|
408
|
+
alice_jwt, _ = RemoteIdentityProvider().login("alice", "Alice123!")
|
|
409
|
+
alice = MACAWClient(user_name="alice", iam_token=alice_jwt, app_name="data-platform")
|
|
410
|
+
alice.register()
|
|
411
|
+
|
|
412
|
+
bob_jwt, _ = RemoteIdentityProvider().login("bob", "Bob@123!")
|
|
413
|
+
bob = MACAWClient(user_name="bob", iam_token=bob_jwt, app_name="data-platform")
|
|
414
|
+
bob.register()
|
|
415
|
+
|
|
416
|
+
# Per-user proxies
|
|
417
|
+
alice_proxy = proxy.bind_to_user(alice)
|
|
418
|
+
bob_proxy = proxy.bind_to_user(bob)
|
|
419
|
+
|
|
420
|
+
# Each user's calls use their identity and policy
|
|
421
|
+
alice_result = alice_proxy.call_tool("list_objects", {"object_type": "database"})
|
|
422
|
+
bob_result = bob_proxy.call_tool("run_snowflake_query", {"statement": "SELECT 1"})
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Security Model
|
|
428
|
+
|
|
429
|
+
All security is delegated to MACAW Control Plane:
|
|
430
|
+
|
|
431
|
+
1. **Registration**: Server/proxy registers with LocalAgent on startup
|
|
432
|
+
2. **Tool Invocation**: Requests routed through ToolAgent PEP
|
|
433
|
+
3. **Policy Enforcement**: MAPL policies enforced on every request
|
|
434
|
+
4. **Cryptographic Signing**: All invocations are signed per-request
|
|
435
|
+
5. **Audit Logging**: Security events logged with cryptographic proof
|
|
436
|
+
6. **Identity Propagation**: User identity flows through bind_to_user()
|
|
437
|
+
|
|
438
|
+
**Per-invocation security**: Even with persistent connections (stdio), MACAW creates fresh AuthenticatedContext for each call. This solves the "shared context" problem in standard MCP.
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
## Legacy API
|
|
443
|
+
|
|
444
|
+
The `Server` and `Client` classes are available for backwards compatibility:
|
|
445
|
+
|
|
446
|
+
```python
|
|
447
|
+
from macaw_adapters.mcp import Server, Client
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
For new projects, use `SecureMCP` (your servers) or `SecureMCPProxy` (external servers).
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Installation
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
pip install macaw-adapters[mcp-proxy]
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
For more information: https://macawsecurity.ai
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MACAW MCP Adapter - FastMCP-compatible API with MACAW Security.
|
|
3
|
+
|
|
4
|
+
Two main components:
|
|
5
|
+
1. SecureMCP - For MCP servers YOU write (wrap your tools with MACAW security)
|
|
6
|
+
2. SecureMCPProxy - For MCP servers you DON'T control (inline gateway to external servers)
|
|
7
|
+
|
|
8
|
+
Usage - SecureMCP (your servers):
|
|
9
|
+
from macaw_adapters.mcp import SecureMCP, Context
|
|
10
|
+
|
|
11
|
+
mcp = SecureMCP("calculator")
|
|
12
|
+
|
|
13
|
+
@mcp.tool(description="Add two numbers")
|
|
14
|
+
def add(a: float, b: float) -> float:
|
|
15
|
+
return a + b
|
|
16
|
+
|
|
17
|
+
mcp.run()
|
|
18
|
+
|
|
19
|
+
Usage - SecureMCPProxy (external servers):
|
|
20
|
+
from macaw_adapters.mcp import SecureMCPProxy
|
|
21
|
+
|
|
22
|
+
# Connect to external MCP server (Salesforce, Google, etc.)
|
|
23
|
+
proxy = SecureMCPProxy(
|
|
24
|
+
app_name="salesforce-mcp",
|
|
25
|
+
upstream_url="https://mcp.salesforce.com",
|
|
26
|
+
upstream_auth={"type": "bearer", "token": SF_TOKEN}
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Call tools - MACAW security applied
|
|
30
|
+
result = proxy.call_tool("query_accounts", {"limit": 10})
|
|
31
|
+
|
|
32
|
+
Features:
|
|
33
|
+
- FastMCP-compatible decorator API (SecureMCP)
|
|
34
|
+
- Inline gateway for external MCP servers (SecureMCPProxy)
|
|
35
|
+
- Policy-enforced tool execution
|
|
36
|
+
- Cryptographic audit trail
|
|
37
|
+
- Multi-user identity via bind_to_user()
|
|
38
|
+
|
|
39
|
+
For more information: https://macawsecurity.ai
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
__version__ = "0.9.10.1"
|
|
43
|
+
|
|
44
|
+
# Primary FastMCP-compatible API (your MCP servers)
|
|
45
|
+
from .mcp import SecureMCP, Context
|
|
46
|
+
|
|
47
|
+
# Inline Gateway for external MCP servers
|
|
48
|
+
from .proxy import SecureMCPProxy, BoundMCPProxy
|
|
49
|
+
|
|
50
|
+
# Legacy API (backwards compatibility)
|
|
51
|
+
from .server import Server
|
|
52
|
+
from .client import Client
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
# Your MCP servers
|
|
56
|
+
"SecureMCP",
|
|
57
|
+
"Context",
|
|
58
|
+
# External MCP servers (inline gateway)
|
|
59
|
+
"SecureMCPProxy",
|
|
60
|
+
"BoundMCPProxy",
|
|
61
|
+
# Legacy
|
|
62
|
+
"Server",
|
|
63
|
+
"Client",
|
|
64
|
+
"__version__"
|
|
65
|
+
]
|