@macaw-ai/client 0.0.0-stage → 0.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/LICENSE.md +339 -0
  2. package/bin/install.js +278 -0
  3. package/package.json +37 -4
  4. package/payload/README.md +56 -0
  5. package/payload/secureAI/LICENSE +190 -0
  6. package/payload/secureAI/NOTICE +5 -0
  7. package/payload/secureAI/demos/policy-controls/1_uncontrolled.py +139 -0
  8. package/payload/secureAI/demos/policy-controls/2_with_macaw.py +139 -0
  9. package/payload/secureAI/demos/policy-controls/3_with_user_policies.py +162 -0
  10. package/payload/secureAI/demos/policy-controls/credentials.txt +12 -0
  11. package/payload/secureAI/demos/policy-controls/malicious_report.txt +21 -0
  12. package/payload/secureAI/demos/policy-controls/sales_report.txt +20 -0
  13. package/payload/secureAI/demos/tutorial-1-rbac/README.md +143 -0
  14. package/payload/secureAI/demos/tutorial-1-rbac/app.py +147 -0
  15. package/payload/secureAI/demos/tutorial-1-rbac/authprompts_demo.py +306 -0
  16. package/payload/secureAI/demos/tutorial-1-rbac/config/claims-config.yaml +152 -0
  17. package/payload/secureAI/demos/tutorial-1-rbac/demo_secureopenai_with_policies.py +150 -0
  18. package/payload/secureAI/demos/tutorial-1-rbac/policies/bu_Analytics.json +8 -0
  19. package/payload/secureAI/demos/tutorial-1-rbac/policies/company_FinTech_Corp.json +70 -0
  20. package/payload/secureAI/demos/tutorial-1-rbac/policies/llm-base.json +69 -0
  21. package/payload/secureAI/demos/tutorial-1-rbac/policies/team_Reporting.json +8 -0
  22. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_alice.json +32 -0
  23. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_bob.json +28 -0
  24. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_carol.json +34 -0
  25. package/payload/secureAI/demos/tutorial-1-rbac/requirements.txt +6 -0
  26. package/payload/secureAI/demos/tutorial-1-rbac/setup/auth0_complete_setup.sh +392 -0
  27. package/payload/secureAI/demos/tutorial-1-rbac/setup/keycloak_complete_setup.sh +320 -0
  28. package/payload/secureAI/demos/tutorial-2-intent/Policies/alation-remote-proxy.json +155 -0
  29. package/payload/secureAI/demos/tutorial-2-intent/Policies/bu.json +22 -0
  30. package/payload/secureAI/demos/tutorial-2-intent/Policies/company.json +17 -0
  31. package/payload/secureAI/demos/tutorial-2-intent/Policies/user_alice.json +48 -0
  32. package/payload/secureAI/demos/tutorial-2-intent/Policies/user_bob.json +83 -0
  33. package/payload/secureAI/demos/tutorial-2-intent/README.md +213 -0
  34. package/payload/secureAI/demos/tutorial-2-intent/script.py +188 -0
  35. package/payload/secureAI/demos/tutorial-2-intent/setup/alation_verifier.py +177 -0
  36. package/payload/secureAI/demos/tutorial-2-intent/setup/get_alation_user_token.py +180 -0
  37. package/payload/secureAI/examples/README.md +244 -0
  38. package/payload/secureAI/examples/anthropic/anthropic_1a_dropin_simple.py +103 -0
  39. package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind.py +190 -0
  40. package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind_streaming.py +190 -0
  41. package/payload/secureAI/examples/anthropic/anthropic_1c_a2a_invoke.py +156 -0
  42. package/payload/secureAI/examples/attestations/1a_trade_alice.py +215 -0
  43. package/payload/secureAI/examples/attestations/1a_trade_bob.py +164 -0
  44. package/payload/secureAI/examples/attestations/1b_grant_admin.py +170 -0
  45. package/payload/secureAI/examples/attestations/1b_grant_researcher.py +231 -0
  46. package/payload/secureAI/examples/attestations/README.md +128 -0
  47. package/payload/secureAI/examples/langchain/langchain_1a_dropin_simple.py +199 -0
  48. package/payload/secureAI/examples/langchain/langchain_1b_multiuser.py +262 -0
  49. package/payload/secureAI/examples/langchain/langchain_1c_orchestration.py +332 -0
  50. package/payload/secureAI/examples/langchain/langchain_1d_llm_openai.py +130 -0
  51. package/payload/secureAI/examples/langchain/langchain_1e_llm_anthropic.py +130 -0
  52. package/payload/secureAI/examples/langchain/langchain_1f_memory.py +214 -0
  53. package/payload/secureAI/examples/litellm/litellm_1a_dropin_simple.py +131 -0
  54. package/payload/secureAI/examples/mcp/1a_simple_invocation.py +115 -0
  55. package/payload/secureAI/examples/mcp/1b_discovery_and_resources.py +142 -0
  56. package/payload/secureAI/examples/mcp/1c_logging_client.py +100 -0
  57. package/payload/secureAI/examples/mcp/1d_progress_client.py +100 -0
  58. package/payload/secureAI/examples/mcp/1e_sampling_client.py +150 -0
  59. package/payload/secureAI/examples/mcp/1e_sampling_server.py +90 -0
  60. package/payload/secureAI/examples/mcp/1f_elicitation_client.py +127 -0
  61. package/payload/secureAI/examples/mcp/1f_elicitation_server.py +73 -0
  62. package/payload/secureAI/examples/mcp/1g_roots_client.py +122 -0
  63. package/payload/secureAI/examples/mcp/1g_roots_server.py +85 -0
  64. package/payload/secureAI/examples/mcp/2a_proxy_snowflake.py +212 -0
  65. package/payload/secureAI/examples/mcp/2b_proxy_databricks.py +299 -0
  66. package/payload/secureAI/examples/mcp/2c_proxy_stdio_fetch.py +91 -0
  67. package/payload/secureAI/examples/mcp/2d_proxy_stdio_salesforce.py +119 -0
  68. package/payload/secureAI/examples/mcp/2e_proxy_multiuser.py +136 -0
  69. package/payload/secureAI/examples/mcp/2f_proxy_stdio_databricks.py +144 -0
  70. package/payload/secureAI/examples/mcp/3a_native_mcp_client.py +104 -0
  71. package/payload/secureAI/examples/mcp/3b_native_mcp_proxy.py +114 -0
  72. package/payload/secureAI/examples/mcp/3c_native_mcp_sampling.py +180 -0
  73. package/payload/secureAI/examples/mcp/3d_native_mcp_2026.py +135 -0
  74. package/payload/secureAI/examples/mcp/securemcp_calculator.py +248 -0
  75. package/payload/secureAI/examples/mcp/securemcp_server.py +75 -0
  76. package/payload/secureAI/examples/openai/openai_1a_dropin_simple.py +102 -0
  77. package/payload/secureAI/examples/openai/openai_1b_multiuser_bind.py +193 -0
  78. package/payload/secureAI/examples/openai/openai_1b_multiuser_bind_streaming.py +199 -0
  79. package/payload/secureAI/examples/openai/openai_1c_a2a_invoke.py +156 -0
  80. package/payload/secureAI/examples/openai/openai_1d_invocation_patterns.py +337 -0
  81. package/payload/secureAI/examples/phishing-resistance/1a_login_alice.py +180 -0
  82. package/payload/secureAI/examples/phishing-resistance/1b_trade_alice.py +241 -0
  83. package/payload/secureAI/examples/phishing-resistance/1b_trade_bob.py +164 -0
  84. package/payload/secureAI/examples/phishing-resistance/README.md +124 -0
  85. package/payload/secureAI/examples/phishing-resistance/get_token.py +114 -0
  86. package/payload/secureAI/examples/pydantic_ai/README.md +88 -0
  87. package/payload/secureAI/examples/pydantic_ai/policies/app_pydantic-agent.json +10 -0
  88. package/payload/secureAI/examples/pydantic_ai/pydanticai_1a_dropin_simple.py +90 -0
  89. package/payload/secureAI/examples/pydantic_ai/pydanticai_1b_model_routing.py +85 -0
  90. package/payload/secureAI/examples/pydantic_ai/pydanticai_1c_native_tools.py +83 -0
  91. package/payload/secureAI/examples/pydantic_ai/pydanticai_1d_multiuser_bind.py +104 -0
  92. package/payload/secureAI/examples/pydantic_ai/pydanticai_1e_mcp_compose.py +92 -0
  93. package/payload/secureAI/macaw_adapters/__init__.py +55 -0
  94. package/payload/secureAI/macaw_adapters/anthropic/REFERENCE.md +374 -0
  95. package/payload/secureAI/macaw_adapters/anthropic/__init__.py +22 -0
  96. package/payload/secureAI/macaw_adapters/anthropic/secure_anthropic.py +792 -0
  97. package/payload/secureAI/macaw_adapters/anthropic_ext_tools/__init__.py +25 -0
  98. package/payload/secureAI/macaw_adapters/anthropic_ext_tools/secure_anthropic.py +840 -0
  99. package/payload/secureAI/macaw_adapters/langchain/REFERENCE.md +234 -0
  100. package/payload/secureAI/macaw_adapters/langchain/__init__.py +100 -0
  101. package/payload/secureAI/macaw_adapters/langchain/_utils.py +83 -0
  102. package/payload/secureAI/macaw_adapters/langchain/agents.py +296 -0
  103. package/payload/secureAI/macaw_adapters/langchain/anthropic.py +456 -0
  104. package/payload/secureAI/macaw_adapters/langchain/callbacks.py +306 -0
  105. package/payload/secureAI/macaw_adapters/langchain/memory.py +352 -0
  106. package/payload/secureAI/macaw_adapters/langchain/openai.py +410 -0
  107. package/payload/secureAI/macaw_adapters/langchain/tools.py +174 -0
  108. package/payload/secureAI/macaw_adapters/litellm/README.md +216 -0
  109. package/payload/secureAI/macaw_adapters/litellm/__init__.py +143 -0
  110. package/payload/secureAI/macaw_adapters/litellm/secure_litellm.py +686 -0
  111. package/payload/secureAI/macaw_adapters/mcp/REFERENCE.md +460 -0
  112. package/payload/secureAI/macaw_adapters/mcp/__init__.py +65 -0
  113. package/payload/secureAI/macaw_adapters/mcp/_endpoint.py +193 -0
  114. package/payload/secureAI/macaw_adapters/mcp/_endpoint2.py +225 -0
  115. package/payload/secureAI/macaw_adapters/mcp/_serve.py +25 -0
  116. package/payload/secureAI/macaw_adapters/mcp/client.py +721 -0
  117. package/payload/secureAI/macaw_adapters/mcp/decorators.py +228 -0
  118. package/payload/secureAI/macaw_adapters/mcp/fastmcp_compat.py +353 -0
  119. package/payload/secureAI/macaw_adapters/mcp/mcp.py +785 -0
  120. package/payload/secureAI/macaw_adapters/mcp/proxy.py +626 -0
  121. package/payload/secureAI/macaw_adapters/mcp/server.py +317 -0
  122. package/payload/secureAI/macaw_adapters/mcp/subscriptions.py +450 -0
  123. package/payload/secureAI/macaw_adapters/openai/REFERENCE.md +368 -0
  124. package/payload/secureAI/macaw_adapters/openai/__init__.py +22 -0
  125. package/payload/secureAI/macaw_adapters/openai/secure_openai.py +833 -0
  126. package/payload/secureAI/macaw_adapters/openai_ext_tools/REFERENCE.md +368 -0
  127. package/payload/secureAI/macaw_adapters/openai_ext_tools/__init__.py +25 -0
  128. package/payload/secureAI/macaw_adapters/openai_ext_tools/secure_openai.py +876 -0
  129. package/payload/secureAI/macaw_adapters/pydantic_ai/__init__.py +36 -0
  130. package/payload/secureAI/macaw_adapters/pydantic_ai/_core.py +257 -0
  131. package/payload/secureAI/macaw_adapters/pydantic_ai/agent.py +218 -0
  132. package/payload/secureAI/macaw_adapters/pydantic_ai/model.py +99 -0
  133. package/payload/secureAI/macaw_adapters/pydantic_ai/toolset.py +86 -0
  134. package/payload/secureAI/test_harness_quick.py +296 -0
  135. package/wheels/macaw_adapters-0.9.10.1-py3-none-any.whl +0 -0
  136. package/wheels/macaw_client-0.9.10.1-cp312-cp312-macosx_11_0_arm64.whl +0 -0
  137. package/wheels/macaw_client-0.9.10.1-cp312-cp312-manylinux_2_17_x86_64.whl +0 -0
  138. package/wheels/macaw_client-0.9.10.1-cp314-cp314-macosx_11_0_arm64.whl +0 -0
  139. package/wheels/macaw_client-0.9.10.1-cp314-cp314-manylinux_2_17_x86_64.whl +0 -0
  140. package/wheels/macaw_client-0.9.10.1-cp314-cp314-win_amd64.whl +0 -0
  141. package/wheels/macaw_client-0.9.10.1-cp39-cp39-macosx_11_0_arm64.whl +0 -0
  142. package/README.md +0 -3
@@ -0,0 +1,180 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ get_alation_user_token.py -- one-shot Alation USER token via OAuth authorization_code + PKCE.
4
+
5
+ Runs the interactive (User-Initiated) OAuth flow: opens a browser to Alation's authorize
6
+ endpoint, catches the redirect on a local callback server, and exchanges the code (+ PKCE
7
+ verifier) for an access_token + refresh_token that act AS the logged-in user. Prints an
8
+ `export ALATION_TOKEN=...` line you paste into the proxy env.
9
+
10
+ Prereqs: an Alation "User-Initiated OAuth Client" (Confidential, PKCE) whose Redirect URI is
11
+ EXACTLY the one below (default http://127.0.0.1:18722/callback).
12
+
13
+ Env / config:
14
+ ALATION_BASE_URL (required) e.g. https://macaw.mtse.alationcloud.com
15
+ ALATION_CLIENT_ID (required)
16
+ ALATION_CLIENT_SECRET (required for Confidential clients; omit for Public)
17
+ ALATION_REDIRECT_URI (default http://127.0.0.1:18722/callback -- MUST match the client)
18
+ ALATION_SCOPE (optional, space-delimited)
19
+ ALATION_AUTHORIZE_PATH (default /oauth/v1/authorize)
20
+ ALATION_TOKEN_PATH (default /oauth/v2/token/)
21
+
22
+ Run:
23
+ export ALATION_BASE_URL="https://macaw.mtse.alationcloud.com"
24
+ export ALATION_CLIENT_ID="..."
25
+ export ALATION_CLIENT_SECRET="..."
26
+ python get_alation_user_token.py # opens the browser, then prints the token
27
+ python get_alation_user_token.py --refresh <REFRESH_TOKEN> # renew without logging in again
28
+ """
29
+
30
+ import base64
31
+ import hashlib
32
+ import http.server
33
+ import json
34
+ import os
35
+ import secrets
36
+ import sys
37
+ import threading
38
+ import urllib.error
39
+ import urllib.parse
40
+ import urllib.request
41
+ import webbrowser
42
+
43
+ BASE = os.environ.get("ALATION_BASE_URL", "").rstrip("/")
44
+ CLIENT_ID = os.environ.get("ALATION_CLIENT_ID", "")
45
+ CLIENT_SECRET = os.environ.get("ALATION_CLIENT_SECRET", "")
46
+ REDIRECT = os.environ.get("ALATION_REDIRECT_URI", "http://127.0.0.1:18722/callback")
47
+ SCOPE = os.environ.get("ALATION_SCOPE", "")
48
+ AUTHORIZE_PATH = os.environ.get("ALATION_AUTHORIZE_PATH", "/oauth/v1/authorize")
49
+ # authorization_code exchange is on the v1 family (v2/token is client_credentials-only,
50
+ # it returns unsupported_grant_type). Override with ALATION_TOKEN_PATH if your instance differs.
51
+ TOKEN_PATH = os.environ.get("ALATION_TOKEN_PATH", "/oauth/v1/token")
52
+
53
+
54
+ def _post_token(data: dict) -> dict:
55
+ url = BASE + TOKEN_PATH
56
+ body = urllib.parse.urlencode(data).encode()
57
+ req = urllib.request.Request(
58
+ url, data=body,
59
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
60
+ )
61
+ try:
62
+ with urllib.request.urlopen(req) as r: # noqa: S310 (trusted Alation host)
63
+ return json.load(r)
64
+ except urllib.error.HTTPError as e:
65
+ detail = e.read().decode("utf-8", "replace")
66
+ sys.exit(
67
+ f"\nToken exchange FAILED: POST {url} -> HTTP {e.code}\n"
68
+ f" response: {detail}\n"
69
+ f" hint: if this says unsupported_grant_type / invalid endpoint, set "
70
+ f"ALATION_TOKEN_PATH=/oauth/v1/token (match the v1 authorize) and re-run "
71
+ f"(a NEW browser login -- the auth code is single-use)."
72
+ )
73
+
74
+
75
+ def _print_tokens(tok: dict) -> None:
76
+ at = tok.get("access_token")
77
+ rt = tok.get("refresh_token")
78
+ if not at:
79
+ sys.exit(f"No access_token in response: {tok}")
80
+ print("\n" + "=" * 64)
81
+ print("Alation USER access token (acts as the logged-in user):\n")
82
+ print(f'export ALATION_TOKEN="{at}"')
83
+ if rt:
84
+ print(f'\n# refresh token (renew later): {rt}')
85
+ print("=" * 64)
86
+
87
+
88
+ def refresh(refresh_token: str) -> None:
89
+ data = {"grant_type": "refresh_token", "refresh_token": refresh_token, "client_id": CLIENT_ID}
90
+ if CLIENT_SECRET:
91
+ data["client_secret"] = CLIENT_SECRET
92
+ _print_tokens(_post_token(data))
93
+
94
+
95
+ def login() -> None:
96
+ # PKCE
97
+ verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode()
98
+ challenge = base64.urlsafe_b64encode(
99
+ hashlib.sha256(verifier.encode()).digest()
100
+ ).rstrip(b"=").decode()
101
+ state = secrets.token_urlsafe(16)
102
+
103
+ q = {
104
+ "response_type": "code",
105
+ "client_id": CLIENT_ID,
106
+ "redirect_uri": REDIRECT,
107
+ "code_challenge": challenge,
108
+ "code_challenge_method": "S256",
109
+ "state": state,
110
+ }
111
+ if SCOPE:
112
+ q["scope"] = SCOPE
113
+ authorize_url = f"{BASE}{AUTHORIZE_PATH}?{urllib.parse.urlencode(q)}"
114
+
115
+ caught = {}
116
+ parsed = urllib.parse.urlparse(REDIRECT)
117
+ host, port, path = parsed.hostname, parsed.port or 80, parsed.path
118
+
119
+ class Handler(http.server.BaseHTTPRequestHandler):
120
+ def do_GET(self):
121
+ u = urllib.parse.urlparse(self.path)
122
+ if u.path != path:
123
+ self.send_response(404); self.end_headers(); return
124
+ params = urllib.parse.parse_qs(u.query)
125
+ caught["code"] = (params.get("code") or [None])[0]
126
+ caught["state"] = (params.get("state") or [None])[0]
127
+ caught["error"] = (params.get("error") or [None])[0]
128
+ self.send_response(200)
129
+ self.send_header("Content-Type", "text/html")
130
+ self.end_headers()
131
+ self.wfile.write(b"<h3>Alation login complete - you can close this tab.</h3>")
132
+
133
+ def log_message(self, *_): # silence
134
+ pass
135
+
136
+ srv = http.server.HTTPServer((host, port), Handler)
137
+ threading.Thread(target=srv.handle_request, daemon=True).start()
138
+
139
+ print(f"Opening browser for Alation login...\n {authorize_url}\n", file=sys.stderr)
140
+ try:
141
+ webbrowser.open(authorize_url)
142
+ except Exception:
143
+ pass
144
+ print("If no browser opened, paste the URL above into one.", file=sys.stderr)
145
+
146
+ # wait for the single callback
147
+ import time
148
+ for _ in range(600): # ~5 min
149
+ if caught:
150
+ break
151
+ time.sleep(0.5)
152
+ if not caught:
153
+ sys.exit("Timed out waiting for the OAuth callback.")
154
+ if caught.get("error"):
155
+ sys.exit(f"Authorization error: {caught['error']}")
156
+ if caught.get("state") != state:
157
+ sys.exit("State mismatch (possible CSRF) -- aborting.")
158
+ code = caught.get("code")
159
+ if not code:
160
+ sys.exit("No authorization code received.")
161
+
162
+ data = {
163
+ "grant_type": "authorization_code",
164
+ "code": code,
165
+ "redirect_uri": REDIRECT,
166
+ "client_id": CLIENT_ID,
167
+ "code_verifier": verifier,
168
+ }
169
+ if CLIENT_SECRET:
170
+ data["client_secret"] = CLIENT_SECRET
171
+ _print_tokens(_post_token(data))
172
+
173
+
174
+ if __name__ == "__main__":
175
+ if not BASE or not CLIENT_ID:
176
+ sys.exit("Set ALATION_BASE_URL and ALATION_CLIENT_ID (see the module docstring).")
177
+ if len(sys.argv) == 3 and sys.argv[1] == "--refresh":
178
+ refresh(sys.argv[2])
179
+ else:
180
+ login()
@@ -0,0 +1,244 @@
1
+ # MACAW Adapter Examples
2
+
3
+ These examples demonstrate how MACAW adapters add enterprise security controls to AI applications through **drop-in replacements** for popular frameworks.
4
+
5
+ ## Three Classes of Adapters
6
+
7
+ MACAW provides API-compatible replacements across the AI stack:
8
+
9
+ | Category | Frameworks | What It Secures |
10
+ |----------|------------|-----------------|
11
+ | **LLM APIs** | OpenAI, Anthropic | Direct LLM calls (chat completions, messages) |
12
+ | **Agentic Frameworks** | MCP (Model Context Protocol) | Tool servers, resources, agent-to-agent calls |
13
+ | **Orchestrators** | LangChain | Agent executors, tool chains, multi-agent workflows |
14
+
15
+ The adapters are thin wrappers that route requests through MACAW's security layer. Change one import line - your existing code continues to work unchanged, now with enterprise security built in.
16
+
17
+ ## Why MACAW?
18
+
19
+ Building production AI applications requires more than just API calls. You need:
20
+
21
+ - **Policy-based access controls** - Who can use which models? What token limits apply? Which tools are allowed?
22
+ - **Identity propagation** - User identity must flow through every layer for per-user policies
23
+ - **Audit trail** - Tamper-proof logs of every AI operation for compliance
24
+ - **Observability** - Trace requests through the AI stack layers
25
+
26
+ Without MACAW, implementing these controls requires months of custom development, careful security review, and ongoing maintenance across every integration point.
27
+
28
+ **With MACAW**, you get all of this by changing an import:
29
+
30
+ ```python
31
+ # Without MACAW: Alice can use any model, any tokens, no audit trail
32
+ from openai import OpenAI
33
+ client = OpenAI()
34
+
35
+ # With MACAW: Alice blocked from GPT-4 by policy, full audit trail
36
+ from macaw_adapters.openai import SecureOpenAI
37
+ client = SecureOpenAI(app_name="my-app")
38
+ ```
39
+
40
+ MACAW deploys cryptographically-secured policy enforcement points at the edge using a zero-trust mesh architecture. Every tool endpoint becomes a PEP, enabling deterministic security controls even for non-deterministic AI systems.
41
+
42
+ ## 30-Second Quick Start
43
+
44
+ ```python
45
+ # Install
46
+ # pip install macaw-adapters[openai]
47
+
48
+ from macaw_adapters.openai import SecureOpenAI
49
+
50
+ # Same API as OpenAI - just a different import
51
+ client = SecureOpenAI(app_name="my-app")
52
+
53
+ response = client.chat.completions.create(
54
+ model="gpt-4",
55
+ messages=[{"role": "user", "content": "Hello!"}]
56
+ )
57
+ print(response.choices[0].message.content)
58
+ ```
59
+
60
+ That's it. Your OpenAI calls now flow through MACAW's security layer with policy enforcement and audit logging.
61
+
62
+ ## Examples by Framework
63
+
64
+ ### OpenAI (`openai/`)
65
+
66
+ Drop-in replacement for the OpenAI Python SDK with full API compatibility including streaming.
67
+
68
+ | Example | Description |
69
+ |---------|-------------|
70
+ | `openai_1a_dropin_simple.py` | Basic drop-in replacement with app-level policy |
71
+ | `openai_1b_multiuser_bind.py` | Per-user policies via `bind_to_user()` pattern |
72
+ | `openai_1b_multiuser_bind_streaming.py` | Same as 1b with streaming responses |
73
+ | `openai_1c_a2a_invoke.py` | Agent-to-agent calls via `invoke_tool()` |
74
+ | `openai_1d_invocation_patterns.py` | Compares both invocation patterns side-by-side |
75
+
76
+ **Progression**: Start with 1a for single-user apps. Move to 1b when you need different permissions per user (SaaS). Use 1c for agent orchestration systems. See 1d to understand how both patterns achieve the same security guarantees.
77
+
78
+ ### Anthropic (`anthropic/`)
79
+
80
+ Drop-in replacement for the Anthropic Python SDK with full API compatibility including streaming.
81
+
82
+ | Example | Description |
83
+ |---------|-------------|
84
+ | `anthropic_1a_dropin_simple.py` | Basic drop-in replacement with app-level policy |
85
+ | `anthropic_1b_multiuser_bind.py` | Per-user policies via `bind_to_user()` pattern |
86
+ | `anthropic_1b_multiuser_bind_streaming.py` | Same as 1b with streaming responses |
87
+ | `anthropic_1c_a2a_invoke.py` | Agent-to-agent calls via `invoke_tool()` |
88
+
89
+ **Progression**: Same pattern as OpenAI - the adapters provide consistent security semantics across LLM providers.
90
+
91
+ ### MCP (`mcp/`)
92
+
93
+ Drop-in replacement for MCP (Model Context Protocol) servers and clients. Secures tool invocations, resource access, and agent communication.
94
+
95
+ | Example | Description |
96
+ |---------|-------------|
97
+ | `securemcp_server.py` | Basic SecureMCP server with tools and resources |
98
+ | `securemcp_calculator.py` | Calculator server with context tracking |
99
+ | `1a_simple_invocation.py` | Client invoking tools on a server |
100
+ | `1b_discovery_and_resources.py` | Discovering servers and accessing resources |
101
+ | `1c_logging_client.py` | Client with logging support |
102
+ | `1d_progress_client.py` | Progress tracking for long operations |
103
+ | `1e_sampling_*.py` | LLM sampling (client + server) |
104
+ | `1f_elicitation_*.py` | User input elicitation (client + server) |
105
+ | `1g_roots_*.py` | Filesystem roots (client + server) |
106
+
107
+ **Progression**: Start with the server example to create a SecureMCP tool server. Use 1a-1b to understand client patterns. Examples 1c-1g demonstrate advanced MCP features (logging, progress, sampling, elicitation, roots) all flowing through MACAW's security layer.
108
+
109
+ ### LangChain (`langchain/`)
110
+
111
+ Drop-in replacements for LangChain agent components with MACAW security controls on tool access.
112
+
113
+ | Example | Description |
114
+ |---------|-------------|
115
+ | `langchain_1a_dropin_simple.py` | Basic agent with security policy |
116
+ | `langchain_1b_multiuser.py` | Per-user agents with different tool permissions |
117
+ | `langchain_1c_orchestration.py` | Multi-agent supervisor pattern |
118
+ | `langchain_1d_llm_openai.py` | Securing LLM calls (OpenAI) |
119
+ | `langchain_1e_llm_anthropic.py` | Securing LLM calls (Anthropic) |
120
+ | `langchain_1f_memory.py` | Memory integration with security |
121
+
122
+ **Progression**: Start with 1a to add tool access control to a single agent. Use 1b when different users need different tool permissions. Use 1c for multi-agent systems where each agent has isolated permissions.
123
+
124
+ ### External Attestations (`attestations/`)
125
+
126
+ Human-in-the-loop approval workflows for sensitive operations. Request blocks until a manager/admin approves.
127
+
128
+ | Example | Description |
129
+ |---------|-------------|
130
+ | `1a_trade_alice.py` | Requester: High-value trade blocks for manager approval |
131
+ | `1a_trade_bob.py` | Approver: Manager approves/denies pending requests |
132
+ | `1b_grant_researcher.py` | Requester: First call blocks, subsequent calls instant |
133
+ | `1b_grant_admin.py` | Approver: Admin grants reusable capability |
134
+
135
+ **Progression**: Start with 1a to understand one-time approvals (each trade needs fresh approval). Use 1b for capability grants (approve once, use many times within TTL).
136
+
137
+ **Two-terminal pattern**: Run the requester script first, then run the approver script in another terminal while the requester waits.
138
+
139
+ ### Phishing-Resistance (`phishing-resistance/`)
140
+
141
+ Login-strength gating. A tool runs only if the human logged in with a phishing-resistant method (passkey / hardware key). Decided by the control plane at login from the token's `amr` — no human in the loop.
142
+
143
+ | Example | Description |
144
+ |---------|-------------|
145
+ | `1a_login_alice.py` | Alice alone: a `phishing_resistant`-gated tool. Passkey → allowed, password → denied |
146
+ | `1b_trade_alice.py` | Requester: layers `phishing_resistant` in front of the trade approval |
147
+ | `1b_trade_bob.py` | Approver: manager approves the trade (once the phishing gate is cleared) |
148
+ | `get_token.py` | Signs in to Entra and mints an id_token carrying `amr` (passkey vs password) |
149
+
150
+ **Progression**: Start with 1a for the pure concept (run it twice — passkey vs password). Use 1b to see it composed as a precondition to a human approval (builds on `attestations/`).
151
+
152
+ ## Running the Examples
153
+
154
+ ### Prerequisites
155
+
156
+ 1. **Install the adapters**:
157
+ ```bash
158
+ pip install macaw-adapters[all]
159
+ # Or specific: pip install macaw-adapters[openai,anthropic,langchain,mcp]
160
+ ```
161
+
162
+ 2. **Set API keys** (for LLM examples):
163
+ ```bash
164
+ export OPENAI_API_KEY=sk-...
165
+ export ANTHROPIC_API_KEY=sk-ant-...
166
+ ```
167
+
168
+ 3. **MACAW client configured** (`~/.macaw/config.json`)
169
+
170
+ ### Running
171
+
172
+ ```bash
173
+ # OpenAI drop-in example
174
+ python openai/openai_1a_dropin_simple.py
175
+
176
+ # Anthropic with streaming
177
+ python anthropic/anthropic_1b_multiuser_bind_streaming.py
178
+
179
+ # MCP server (run first)
180
+ python mcp/securemcp_server.py
181
+ # Then in another terminal:
182
+ python mcp/1a_simple_invocation.py
183
+
184
+ # LangChain multi-agent
185
+ python langchain/langchain_1c_orchestration.py
186
+ ```
187
+
188
+ ## Tutorial
189
+
190
+ For a complete end-to-end tutorial with policy setup, identity provider configuration, and a realistic multi-user scenario, see:
191
+
192
+ **[demos/tutorial-1/](../demos/tutorial-1/)** - Role-Based AI Access Control
193
+
194
+ This tutorial demonstrates a FinTech Corp scenario with hierarchical policies:
195
+ - **alice** (Analyst): GPT-3.5 only, 500 token limit
196
+ - **bob** (Manager): GPT-3.5/4, 2000 token limit
197
+ - **carol** (Admin): All models, 4000 token limit
198
+
199
+ Includes setup scripts for Keycloak and Auth0 identity providers, complete MAPL policy files, and three demo applications showing different integration patterns.
200
+
201
+ ## Console Dev Hub
202
+
203
+ Everything in this repository is also available in the MACAW Console's Dev Hub with interactive features:
204
+
205
+ ```
206
+ Console > Dev Hub
207
+ ├── Quick Start
208
+ │ └── Download Client SDK (macOS/Linux/Windows, Python 3.9-3.12) and Adapters
209
+ ├── Tutorials
210
+ │ └── Role-Based Access Control
211
+ │ ├── Multi-User SaaS Patterns
212
+ │ ├── Agent Orchestration
213
+ │ └── Policy Hierarchies
214
+ ├── Examples
215
+ │ ├── OpenAI (drop-in, multi-user, streaming, A2A)
216
+ │ ├── Anthropic (drop-in, multi-user, streaming, A2A)
217
+ │ ├── MCP
218
+ │ │ ├── Simple Invocation
219
+ │ │ ├── Discovery & Resources
220
+ │ │ ├── Logging
221
+ │ │ ├── Progress Tracking
222
+ │ │ ├── Sampling
223
+ │ │ ├── Elicitation
224
+ │ │ └── Roots
225
+ │ └── LangChain
226
+ │ ├── Drop-in Agents
227
+ │ ├── Multi-user Permissions
228
+ │ ├── Agent Orchestration
229
+ │ ├── LLM Wrappers (OpenAI, Anthropic)
230
+ │ └── Memory Integration
231
+ └── Reference
232
+ ├── MACAW Client SDK
233
+ ├── Adapter APIs
234
+ ├── MAPL Policy Language
235
+ └── Claims Mapping
236
+ ```
237
+
238
+ Access at [console.macawsecurity.ai](https://console.macawsecurity.ai) → Dev Hub tab.
239
+
240
+ ## Learn More
241
+
242
+ - **Documentation**: [docs.macawsecurity.ai](https://docs.macawsecurity.ai)
243
+ - **Console**: [console.macawsecurity.ai](https://console.macawsecurity.ai)
244
+ - **GitHub**: [github.com/macawsecurity/secureAI](https://github.com/macawsecurity/secureAI)
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ anthropic_1a_dropin_simple.py - Drop-in replacement for Anthropic client
4
+
5
+ The simplest integration path - just replace your import and it works.
6
+ No Identity Provider needed.
7
+
8
+ Prerequisites:
9
+ - MACAW SDK installed (pip install macaw-client macaw-adapters)
10
+ - ANTHROPIC_API_KEY environment variable
11
+
12
+ Run:
13
+ export ANTHROPIC_API_KEY=sk-ant-...
14
+ python anthropic_1a_dropin_simple.py
15
+ """
16
+
17
+ import os
18
+ import sys
19
+
20
+ # BEFORE: from anthropic import Anthropic
21
+ # AFTER:
22
+ from macaw_adapters.anthropic import SecureAnthropic
23
+
24
+
25
+ def main():
26
+ # Check for API key
27
+ if not os.environ.get("ANTHROPIC_API_KEY"):
28
+ print("Set ANTHROPIC_API_KEY environment variable")
29
+ print(" export ANTHROPIC_API_KEY=sk-ant-...")
30
+ return
31
+
32
+ print("=" * 60)
33
+ print("Example 1a: Drop-in Replacement (Anthropic)")
34
+ print("=" * 60)
35
+ print("\nPath: Direct on service (simplest)")
36
+ print("Use when: Single app, no user distinction, app-level policies")
37
+
38
+ # Create SecureAnthropic - just like regular Anthropic client
39
+ # Add intent_policy to restrict to Haiku only (no Sonnet/Opus)
40
+ client = SecureAnthropic(
41
+ app_name="my-simple-app",
42
+ intent_policy={
43
+ "constraints": {
44
+ "parameters": {
45
+ "tool:*/generate": {
46
+ "model": ["claude-3-haiku-20240307"], # Only Haiku allowed
47
+ "max_tokens": {"max": 200}
48
+ }
49
+ }
50
+ }
51
+ }
52
+ )
53
+
54
+ print(f"\nService registered: {client.server_id}")
55
+ print(f"Mode: {client._mode}")
56
+ print("Policy: Haiku only, max 200 tokens")
57
+
58
+ # Same API as Anthropic - no changes needed
59
+ print("\n--- Test 1: Allowed model (claude-3-haiku) ---")
60
+ response = client.messages.create(
61
+ model="claude-3-haiku-20240307",
62
+ max_tokens=100,
63
+ messages=[
64
+ {"role": "user", "content": "What is compound interest? Answer briefly."}
65
+ ]
66
+ )
67
+
68
+ print(f"Model: {response.model}")
69
+ print(f"Response: {response.content[0].text}")
70
+
71
+ # Policy enforcement - try a blocked model
72
+ print("\n--- Test 2: Blocked model (claude-4.5-opus) ---")
73
+ try:
74
+ response = client.messages.create(
75
+ model="claude-opus-4-5-20251101", # NOT in allowed list!
76
+ max_tokens=100,
77
+ messages=[
78
+ {"role": "user", "content": "What is compound interest?"}
79
+ ]
80
+ )
81
+ print(f"Unexpected success - should have been blocked!")
82
+ print(f"Response: {response.content[0].text[:100]}...")
83
+ except Exception as e:
84
+ print(f"Correctly blocked: {e}")
85
+
86
+ print("\n" + "=" * 60)
87
+ print("Example complete!")
88
+ print("=" * 60)
89
+
90
+
91
+ if __name__ == "__main__":
92
+ try:
93
+ main()
94
+ except Exception as e:
95
+ err = str(e)
96
+ print("\n" + "=" * 60)
97
+ if "ANTHROPIC_API_KEY" in err or "api_key" in err.lower():
98
+ print("ERROR: Anthropic API key not configured")
99
+ print("Fix: export ANTHROPIC_API_KEY=sk-ant-...")
100
+ else:
101
+ print(f"ERROR: {e}")
102
+ print("=" * 60)
103
+ sys.exit(1)