@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,104 @@
1
+ """
2
+ Per-user policy on a shared agent.
3
+
4
+ One SecureAgent is built once for the service. bind_to_user() returns an agent
5
+ that issues every invocation as that user, so MACAW evaluates alice's and bob's
6
+ policies against the same application resources.
7
+
8
+ service = SecureAgent(model, app_name="pydantic-agent", tools=[...])
9
+ alice_agent = service.bind_to_user(alice_client)
10
+
11
+ The registration and the tools are shared; only the caller identity differs.
12
+
13
+ Prerequisites:
14
+ export OPENAI_API_KEY=sk-...
15
+ MACAW LocalAgent running
16
+ Identity provider running with the demo users (Keycloak or Auth0)
17
+ Workspace policies for user:alice and user:bob covering tool:pydantic-agent/*
18
+
19
+ Run:
20
+ python pydanticai_1d_multiuser_bind.py
21
+ """
22
+
23
+ import os
24
+ import sys
25
+
26
+ from macaw_client import MACAWClient, PermissionDenied, RemoteIdentityProvider
27
+ from pydantic_ai.models.openai import OpenAIChatModel
28
+
29
+ from macaw_adapters.pydantic_ai import SecureAgent
30
+
31
+ APP_NAME = "pydantic-agent"
32
+ GENERATE = f"tool:{APP_NAME}/generate"
33
+
34
+ USERS = [("alice", "Alice123!"), ("bob", "Bob@123!")]
35
+
36
+
37
+ def query_catalog(table: str, limit: int = 10) -> str:
38
+ """Query the data catalog for rows from a table."""
39
+ return f"{limit} rows from {table}"
40
+
41
+
42
+ def export_dataset(table: str) -> str:
43
+ """Export a full dataset to external storage."""
44
+ return f"exported {table}"
45
+
46
+
47
+ def login(username: str, password: str):
48
+ """Authenticate against the identity provider and register a user agent."""
49
+ jwt_token, _ = RemoteIdentityProvider().login(username, password)
50
+ user = MACAWClient(
51
+ user_name=username,
52
+ iam_token=jwt_token,
53
+ agent_type="user",
54
+ app_name=APP_NAME,
55
+ )
56
+ if not user.register():
57
+ raise RuntimeError(f"Failed to register user agent for {username}")
58
+ return user
59
+
60
+
61
+ def main() -> int:
62
+ if not os.environ.get("OPENAI_API_KEY"):
63
+ print("Set OPENAI_API_KEY first:")
64
+ print(" export OPENAI_API_KEY=sk-...")
65
+ return 1
66
+
67
+ # One service agent, shared by every user.
68
+ service = SecureAgent(
69
+ OpenAIChatModel("gpt-4o-mini"),
70
+ app_name=APP_NAME,
71
+ tools=[query_catalog, export_dataset],
72
+ intent_policy={"resources": [GENERATE, f"tool:{APP_NAME}/*"]},
73
+ )
74
+ print(f"Service registered: {service._macaw.server_id}")
75
+
76
+ for username, password in USERS:
77
+ print(f"\n=== {username} ===")
78
+ try:
79
+ user = login(username, password)
80
+ except Exception as e:
81
+ print(f" login failed: {e}")
82
+ print(" Is the identity provider running? See demos/tutorial-1/setup/")
83
+ continue
84
+
85
+ agent = service.bind_to_user(user)
86
+
87
+ for prompt in (
88
+ "Use query_catalog on the customers table with limit 3.",
89
+ "Export the customers table using export_dataset.",
90
+ ):
91
+ try:
92
+ result = agent.run_sync(prompt)
93
+ print(f" {result.output}")
94
+ except PermissionDenied as e:
95
+ print(f" denied by MACAW: {e}")
96
+
97
+ user.unregister()
98
+
99
+ print("\nSame agent, same tools. The difference is whose policy was resolved.")
100
+ return 0
101
+
102
+
103
+ if __name__ == "__main__":
104
+ sys.exit(main())
@@ -0,0 +1,92 @@
1
+ """
2
+ Local tools and an MCP server in one agent.
3
+
4
+ A Pydantic AI agent can mix tools it owns with tools reached over MCP. Both are
5
+ governed, but at different boundaries:
6
+
7
+ local functions registered with MACAW and dispatched by it
8
+ -> tool:pydantic-agent/<name>
9
+
10
+ MCP tools authorized here under the caller's identity, then executed
11
+ by the server, which SecureMCP governs on its own side
12
+ -> tool:pydantic-agent/<name> (this agent)
13
+ -> tool:securemcp-calculator/<name> (the server)
14
+
15
+ The agent-side check is the one that carries the user's identity, because an MCP
16
+ connection has none of its own.
17
+
18
+ This example runs examples/mcp/securemcp_calculator.py as a subprocess over
19
+ stdio, the same way examples/mcp/3a_native_mcp_client.py does.
20
+
21
+ Prerequisites:
22
+ export OPENAI_API_KEY=sk-...
23
+ MACAW LocalAgent running
24
+ Workspace policy allowing tool:pydantic-agent/*
25
+
26
+ Run:
27
+ python pydanticai_1e_mcp_compose.py
28
+ """
29
+
30
+ import asyncio
31
+ import os
32
+ import sys
33
+ from pathlib import Path
34
+
35
+ from pydantic_ai.mcp import MCPToolset, StdioTransport
36
+ from pydantic_ai.models.openai import OpenAIChatModel
37
+
38
+ from macaw_adapters.pydantic_ai import SecureAgent
39
+
40
+ APP_NAME = "pydantic-agent"
41
+ GENERATE = f"tool:{APP_NAME}/generate"
42
+
43
+ CALCULATOR = Path(__file__).resolve().parents[1] / "mcp" / "securemcp_calculator.py"
44
+
45
+
46
+ def describe_result(value: str) -> str:
47
+ """Write a one-line description of a calculation result."""
48
+ return f"result recorded: {value}"
49
+
50
+
51
+ async def main() -> int:
52
+ if not os.environ.get("OPENAI_API_KEY"):
53
+ print("Set OPENAI_API_KEY first:")
54
+ print(" export OPENAI_API_KEY=sk-...")
55
+ return 1
56
+
57
+ if not CALCULATOR.exists():
58
+ print(f"Calculator server not found: {CALCULATOR}")
59
+ return 1
60
+
61
+ # env is passed through so the server subprocess can import macaw_adapters
62
+ # when running from a source checkout rather than an installed package.
63
+ calculator = MCPToolset(
64
+ StdioTransport(
65
+ command=sys.executable,
66
+ args=[str(CALCULATOR), "stdio"],
67
+ env=dict(os.environ),
68
+ )
69
+ )
70
+
71
+ agent = SecureAgent(
72
+ OpenAIChatModel("gpt-4o-mini"),
73
+ app_name=APP_NAME,
74
+ tools=[describe_result], # owned: MACAW dispatches these
75
+ toolsets=[calculator], # MCP: MACAW authorizes, server executes
76
+ intent_policy={"resources": [GENERATE, f"tool:{APP_NAME}/*"]},
77
+ )
78
+ print(f"Registered: {agent._macaw.server_id}")
79
+ print(f"Local tools: describe_result")
80
+ print(f"MCP server : {CALCULATOR.name}")
81
+
82
+ result = await agent.run(
83
+ "Add 17 and 25 using the calculator, then describe the result."
84
+ )
85
+ print(f"\n{result.output}")
86
+ print("\nBoth tools were invoked through MACAW under this agent's identity.")
87
+
88
+ return 0
89
+
90
+
91
+ if __name__ == "__main__":
92
+ sys.exit(asyncio.run(main()))
@@ -0,0 +1,55 @@
1
+ """
2
+ MACAW Adapters - Secure AI Adapters for Enterprise
3
+
4
+ Security adapters for popular AI frameworks including OpenAI, Anthropic,
5
+ LangChain, LiteLLM (100+ providers), and MCP (Model Context Protocol).
6
+
7
+ Usage:
8
+ from macaw_adapters.openai import SecureOpenAI
9
+ from macaw_adapters.anthropic import SecureAnthropic
10
+ from macaw_adapters.langchain.agents import create_react_agent, AgentExecutor
11
+ from macaw_adapters.mcp import SecureMCP
12
+
13
+ # LiteLLM - drop-in replacement supporting 100+ providers
14
+ from macaw_adapters import litellm
15
+ response = litellm.completion(model="groq/llama3-70b", messages=[...])
16
+
17
+ Prerequisites:
18
+ - MACAW Client Library: Download from https://macawsecurity.ai
19
+ - Free Account: Create at https://console.macawsecurity.ai
20
+
21
+ For more information, visit: https://macawsecurity.ai
22
+ """
23
+
24
+ __version__ = "0.9.10.1"
25
+ __author__ = "MACAW Security"
26
+ __license__ = "Apache-2.0"
27
+
28
+ # Lazy imports - only load adapters when explicitly imported
29
+ # This allows using one adapter without installing dependencies for others
30
+ # e.g., `from macaw_adapters.mcp import SecureMCP` works without openai installed
31
+
32
+ __all__ = [
33
+ "openai",
34
+ "anthropic",
35
+ "langchain",
36
+ "litellm",
37
+ "mcp",
38
+ "pydantic_ai",
39
+ "__version__",
40
+ ]
41
+
42
+
43
+ def __getattr__(name):
44
+ """
45
+ Lazy import adapters only when accessed.
46
+
47
+ Imported by full module path: an adapter shares its name with the package it
48
+ wraps (openai, mcp, pydantic_ai, ...), and `from macaw_adapters import <name>`
49
+ falls back to this function for those, which recurses.
50
+ """
51
+ if name in ("openai", "anthropic", "langchain", "litellm", "mcp", "pydantic_ai"):
52
+ import importlib
53
+
54
+ return importlib.import_module(f"macaw_adapters.{name}")
55
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,374 @@
1
+ # SecureAnthropic Reference
2
+
3
+ Drop-in replacement for Anthropic Claude client with MACAW security. Supports three usage paths for different scenarios.
4
+
5
+ ## Installation
6
+
7
+ ```python
8
+ from macaw_adapters.anthropic import SecureAnthropic
9
+ ```
10
+
11
+ Install the package:
12
+ ```bash
13
+ pip install -e /path/to/secureAI
14
+ ```
15
+
16
+ ---
17
+
18
+ ## When to Use Each Path
19
+
20
+ | Scenario | Path | Example |
21
+ |----------|------|---------|
22
+ | Simple app, no user distinction | Direct on service | `client.messages.create()` |
23
+ | Multi-user, per-user policies | bind_to_user | `service.bind_to_user(user_client)` |
24
+ | A2A communication, explicit control | invoke_tool | `user.invoke_tool("tool:xxx/generate", ...)` |
25
+ | CLI tool, single operator | Direct on service | `client.messages.create()` |
26
+ | SaaS with user accounts | bind_to_user | Different users = different permissions |
27
+
28
+ ### Why bind_to_user?
29
+
30
+ Without `bind_to_user`, everyone uses the **service's identity**. With `bind_to_user`, each user's **JWT identity flows through** for policy evaluation.
31
+
32
+ This enables scenarios like:
33
+ - Alice: Claude Haiku only, max 500 tokens
34
+ - Bob: Claude Haiku/Sonnet/Opus, max 2000 tokens
35
+
36
+ Same service, different permissions based on WHO is calling.
37
+
38
+ ---
39
+
40
+ ## Path 1: Direct on Service (Simplest)
41
+
42
+ ```python
43
+ from macaw_adapters.anthropic import SecureAnthropic
44
+
45
+ # Just like using regular Anthropic client
46
+ client = SecureAnthropic(
47
+ app_name="my-app",
48
+ intent_policy={"purpose": "financial analysis"}
49
+ )
50
+
51
+ # Same API as Anthropic
52
+ response = client.messages.create(
53
+ model="claude-3-opus-20240229",
54
+ max_tokens=500,
55
+ messages=[{"role": "user", "content": "What is compound interest?"}]
56
+ )
57
+
58
+ print(response.content[0].text)
59
+ ```
60
+
61
+ **Use when**: Single app, no user distinction, app-level policies.
62
+
63
+ ---
64
+
65
+ ## Path 2: bind_to_user (Multi-user)
66
+
67
+ ```python
68
+ from macaw_adapters.anthropic import SecureAnthropic
69
+ from macaw_client import MACAWClient, RemoteIdentityProvider
70
+
71
+ # 1. Create SINGLE service (shared across all users)
72
+ service = SecureAnthropic(app_name="financial-service")
73
+
74
+ # 2. User login -> JWT -> MACAWClient
75
+ jwt_token, _ = RemoteIdentityProvider().login("alice", "Alice123!")
76
+ user = MACAWClient(
77
+ user_name="alice",
78
+ iam_token=jwt_token,
79
+ agent_type="user",
80
+ app_name="financial-app"
81
+ )
82
+ user.register()
83
+
84
+ # 3. Bind user to service
85
+ user_anthropic = service.bind_to_user(user)
86
+
87
+ # 4. Same API, user's identity flows through for policy evaluation
88
+ response = user_anthropic.messages.create(
89
+ model="claude-3-haiku-20240307",
90
+ max_tokens=500,
91
+ messages=[{"role": "user", "content": "Hello"}]
92
+ )
93
+
94
+ # 5. Cleanup when user session ends
95
+ user_anthropic.unbind()
96
+ ```
97
+
98
+ **Use when**: SaaS app, different users need different permissions.
99
+
100
+ ---
101
+
102
+ ## Path 3: invoke_tool (A2A)
103
+
104
+ ```python
105
+ from macaw_adapters.anthropic import SecureAnthropic
106
+ from macaw_client import MACAWClient, RemoteIdentityProvider
107
+
108
+ # 1. Create Anthropic service
109
+ service = SecureAnthropic(app_name="anthropic-service")
110
+
111
+ # 2. Create user agent
112
+ jwt_token, _ = RemoteIdentityProvider().login("alice", "Alice123!")
113
+ user = MACAWClient(
114
+ user_name="alice",
115
+ iam_token=jwt_token,
116
+ agent_type="user",
117
+ app_name="my-agent"
118
+ )
119
+ user.register()
120
+
121
+ # 3. Explicit invoke_tool - you control everything
122
+ result = user.invoke_tool(
123
+ tool_name="tool:anthropic-service/generate", # MAPL tool name
124
+ parameters={
125
+ "model": "claude-3-haiku-20240307",
126
+ "max_tokens": 500,
127
+ "messages": [{"role": "user", "content": "What is compound interest?"}]
128
+ },
129
+ target_agent=service.server_id # Explicit routing
130
+ )
131
+
132
+ # Result is raw dict
133
+ content = result["content"][0]["text"]
134
+ ```
135
+
136
+ **Use when**: Building agent systems, need explicit control, cross-service communication.
137
+
138
+ ---
139
+
140
+ ## SecureAnthropic Class
141
+
142
+ ```python
143
+ client = SecureAnthropic(
144
+ api_key="sk-ant-...", # Optional: uses ANTHROPIC_API_KEY env var
145
+ app_name="my-app", # Application name for MACAW registration
146
+ intent_policy={...}, # Application-defined security policy (MAPL format)
147
+ jwt_token="...", # Optional: creates user-mode client
148
+ user_name="alice" # Optional: user name for user mode
149
+ )
150
+ ```
151
+
152
+ ### Constructor Parameters
153
+
154
+ | Parameter | Type | Default | Description |
155
+ |-----------|------|---------|-------------|
156
+ | `api_key` | str | env var | Anthropic API key |
157
+ | `app_name` | str | `"secure-anthropic-app"` | Application name for MACAW |
158
+ | `intent_policy` | dict | `{}` | Security policy (MAPL format) |
159
+ | `jwt_token` | str | None | JWT token for user mode |
160
+ | `user_name` | str | None | User name for user mode |
161
+
162
+ ### Properties
163
+
164
+ | Property | Type | Description |
165
+ |----------|------|-------------|
166
+ | `client.server_id` | str | Agent ID after registration |
167
+ | `client.macaw_client` | MACAWClient | MACAW client instance |
168
+ | `client.anthropic_client` | Anthropic | Underlying Anthropic client |
169
+ | `client._mode` | str | "service" or "user" |
170
+
171
+ ### Methods
172
+
173
+ #### bind_to_user(user_client) -> BoundSecureAnthropic
174
+
175
+ Bind service to a user's MACAW client for per-user identity.
176
+
177
+ ```python
178
+ user_anthropic = service.bind_to_user(user_client)
179
+ ```
180
+
181
+ Only valid in service mode. Returns `BoundSecureAnthropic` wrapper.
182
+
183
+ #### register_tool(name, handler) -> SecureAnthropic
184
+
185
+ Register a tool that Claude can call.
186
+
187
+ ```python
188
+ client.register_tool("get_weather", get_weather_fn)
189
+ ```
190
+
191
+ ---
192
+
193
+ ## BoundSecureAnthropic Class
194
+
195
+ Per-user wrapper created via `bind_to_user()`.
196
+
197
+ ```python
198
+ user_anthropic = service.bind_to_user(user_client)
199
+
200
+ # Same API as SecureAnthropic
201
+ response = user_anthropic.messages.create(...)
202
+
203
+ # Check binding status
204
+ if user_anthropic.is_bound:
205
+ ...
206
+
207
+ # Cleanup
208
+ user_anthropic.unbind()
209
+ ```
210
+
211
+ ### Properties
212
+
213
+ | Property | Type | Description |
214
+ |----------|------|-------------|
215
+ | `is_bound` | bool | Whether wrapper is still bound |
216
+ | `service` | SecureAnthropic | The bound service (raises if unbound) |
217
+ | `user_client` | MACAWClient | The bound user client (raises if unbound) |
218
+
219
+ ### Methods
220
+
221
+ #### unbind()
222
+
223
+ Invalidate this binding. All future calls will raise `RuntimeError`.
224
+
225
+ ---
226
+
227
+ ## API Surface
228
+
229
+ SecureAnthropic mirrors the Anthropic API structure:
230
+
231
+ ```python
232
+ # Messages API (primary)
233
+ response = client.messages.create(
234
+ model="claude-3-opus-20240229",
235
+ max_tokens=1024,
236
+ messages=[{"role": "user", "content": "..."}],
237
+ tools=[...] # Optional: auto-discovered
238
+ )
239
+
240
+ # Completions API (legacy)
241
+ response = client.completions.create(
242
+ model="claude-2.1",
243
+ prompt="..."
244
+ )
245
+ ```
246
+
247
+ ---
248
+
249
+ ## MAPL Tool Names
250
+
251
+ SecureAnthropic uses MAPL-compliant tool names:
252
+
253
+ | Operation | Tool Name |
254
+ |-----------|-----------|
255
+ | Messages | `tool:{app_name}/generate` |
256
+ | Completions | `tool:{app_name}/complete` |
257
+
258
+ Example: If `app_name="anthropic-service"`, the generate tool is `tool:anthropic-service/generate`.
259
+
260
+ ---
261
+
262
+ ## Tool Auto-Discovery
263
+
264
+ SecureAnthropic automatically discovers tool implementations from the caller's scope:
265
+
266
+ ```python
267
+ # Define tools in your code
268
+ def get_weather(location: str) -> str:
269
+ return f"Weather in {location}: Sunny, 72F"
270
+
271
+ # Define tool schemas for Claude
272
+ tools = [
273
+ {
274
+ "name": "get_weather", # Must match function name
275
+ "description": "Get weather for a location",
276
+ "input_schema": {
277
+ "type": "object",
278
+ "properties": {
279
+ "location": {"type": "string", "description": "City name"}
280
+ },
281
+ "required": ["location"]
282
+ }
283
+ }
284
+ ]
285
+
286
+ # Tools are auto-discovered and routed through MACAW PEP
287
+ response = client.messages.create(
288
+ model="claude-3-opus-20240229",
289
+ max_tokens=1024,
290
+ messages=[{"role": "user", "content": "What's the weather in Paris?"}],
291
+ tools=tools
292
+ )
293
+ ```
294
+
295
+ ---
296
+
297
+ ## Intent Policy Format (MAPL)
298
+
299
+ ```python
300
+ intent_policy = {
301
+ # Deny access to sensitive resources
302
+ "denied_resources": ["*password*", "*credential*", "*secret*"],
303
+
304
+ # Allow specific resources
305
+ "resources": ["tool:get_weather", "tool:search_files"],
306
+
307
+ # Parameter constraints
308
+ "constraints": {
309
+ "parameters": {
310
+ "tool:search_files": {
311
+ "path": ["*public*", "*reports*"]
312
+ }
313
+ },
314
+ "denied_parameters": {
315
+ "tool:*": {
316
+ "query": ["*password*"]
317
+ }
318
+ }
319
+ },
320
+
321
+ # Application metadata
322
+ "purpose": "weather and file search app"
323
+ }
324
+ ```
325
+
326
+ ---
327
+
328
+ ## Authenticated Prompts
329
+
330
+ SecureAnthropic supports authenticated prompts for cryptographic proof of prompt origin:
331
+
332
+ ```python
333
+ # Tools declare which parameters are prompts
334
+ self.tools = {
335
+ f"tool:{self.app_name}/generate": {
336
+ "handler": self._handle_generate,
337
+ "prompts": ["messages"] # messages param is authenticated
338
+ }
339
+ }
340
+ ```
341
+
342
+ Authenticated prompts are auto-created when `agent.authenticate_prompts` is enabled.
343
+
344
+ ---
345
+
346
+ ## Migration from Anthropic
347
+
348
+ ```python
349
+ # Before
350
+ from anthropic import Anthropic
351
+ client = Anthropic()
352
+
353
+ # After (drop-in replacement)
354
+ from macaw_adapters.anthropic import SecureAnthropic
355
+ client = SecureAnthropic(
356
+ app_name="my-app",
357
+ intent_policy={"denied_resources": ["*sensitive*"]}
358
+ )
359
+
360
+ # Same API - no other code changes needed
361
+ response = client.messages.create(...)
362
+ ```
363
+
364
+ ---
365
+
366
+ ## Key Differences from OpenAI Adapter
367
+
368
+ | Feature | SecureAnthropic | SecureOpenAI |
369
+ |---------|-----------------|--------------|
370
+ | Tool Schema | `input_schema` | `parameters` in `function` |
371
+ | Tool Type | Direct name | `type: "function"` wrapper |
372
+ | Response | `content` blocks | `message.tool_calls` |
373
+ | Continuation | `tool_result` in user message | `tool` role message |
374
+ | Primary API | `messages.create()` | `chat.completions.create()` |
@@ -0,0 +1,22 @@
1
+ """
2
+ MACAW Anthropic Adapter - Secure drop-in replacement for Anthropic client.
3
+
4
+ Usage:
5
+ # Before
6
+ from anthropic import Anthropic
7
+ client = Anthropic()
8
+
9
+ # After (one line change!)
10
+ from macaw_adapters.anthropic import SecureAnthropic
11
+ client = SecureAnthropic()
12
+
13
+ Features:
14
+ - Drop-in replacement for Anthropic client
15
+ - Policy-enforced tool execution
16
+ - Cryptographic audit trail
17
+ - Multi-user identity binding
18
+ """
19
+
20
+ from macaw_adapters.anthropic.secure_anthropic import SecureAnthropic
21
+
22
+ __all__ = ["SecureAnthropic"]