@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,127 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 1f_elicitation_client.py - MCP Elicitation (Server->User Input)
4
+
5
+ Demonstrates client.set_elicitation_handler() for server->user input.
6
+
7
+ The client registers an input handler that prompts the user when
8
+ the server's ctx.elicit() is called.
9
+
10
+ NOTE: This example requires interactive user input and is not suitable
11
+ for automated test harnesses.
12
+
13
+ Prerequisites:
14
+ - MACAW SDK installed (pip install macaw-client macaw-adapters)
15
+ - Elicitation server running (1f_elicitation_server.py)
16
+
17
+ Run:
18
+ # Terminal 1: Start the server
19
+ python 1f_elicitation_server.py
20
+
21
+ # Terminal 2: Run this client (interactive)
22
+ python 1f_elicitation_client.py
23
+ """
24
+
25
+ from macaw_adapters.mcp import Client
26
+
27
+
28
+ def find_elicitation_server(client):
29
+ """Find the most recent elicitation demo server."""
30
+ agents = client.macaw_client.list_agents(agent_type="app")
31
+ matching = [
32
+ a["agent_id"] for a in agents
33
+ if "elicitation-demo" in a.get("agent_id", "")
34
+ and "/tool." not in a.get("agent_id", "")
35
+ ]
36
+ return matching[0] if matching else None # Return first (most recent)
37
+
38
+
39
+ def main():
40
+ print("=" * 50)
41
+ print("Example 1f: Elicitation Demo Client")
42
+ print("=" * 50)
43
+
44
+ client = Client("elicitation-client")
45
+ print(f"Client: {client.client_id}")
46
+
47
+ # Register interactive input handler
48
+ def input_handler(prompt, options, input_type, default, required, **kwargs):
49
+ print()
50
+ if input_type == "confirm":
51
+ response = input(f" {prompt} (y/n) [{default or 'n'}]: ").strip().lower()
52
+ if not response:
53
+ response = (default or "n").lower()
54
+ return response in ("y", "yes")
55
+
56
+ elif input_type == "select" and options:
57
+ print(f" {prompt}")
58
+ for i, opt in enumerate(options):
59
+ print(f" {i+1}. {opt}")
60
+ while True:
61
+ try:
62
+ choice = input(f" Enter number (1-{len(options)}): ").strip()
63
+ idx = int(choice) - 1
64
+ if 0 <= idx < len(options):
65
+ return options[idx]
66
+ except ValueError:
67
+ pass
68
+ print(" Invalid choice, try again")
69
+
70
+ else: # text
71
+ response = input(f" {prompt}: ").strip()
72
+ return response or default
73
+
74
+ client.set_elicitation_handler(input_handler)
75
+ print("Elicitation handler registered (interactive)")
76
+ print()
77
+
78
+ server = find_elicitation_server(client)
79
+ if not server:
80
+ print("\nNo elicitation-demo server found!")
81
+ print("Start it first: python3 1f_elicitation_server.py")
82
+ return
83
+
84
+ print(f"Server: {server}")
85
+ print()
86
+
87
+ # Test create_profile
88
+ print("Testing create_profile():")
89
+ print("-" * 40)
90
+ result = client.macaw_client.invoke_tool(
91
+ "create_profile",
92
+ {},
93
+ target_agent=server
94
+ )
95
+ print(f"\n Result: {result}")
96
+ print()
97
+
98
+ # Test delete_item
99
+ print("Testing delete_item():")
100
+ print("-" * 40)
101
+ result = client.macaw_client.invoke_tool(
102
+ "delete_item",
103
+ {"item_name": "important_file.txt"},
104
+ target_agent=server
105
+ )
106
+ print(f"\n Result: {result}")
107
+
108
+ print()
109
+ print("=" * 50)
110
+ print("Elicitation enables human-in-the-loop workflows")
111
+ print("=" * 50)
112
+
113
+
114
+ if __name__ == "__main__":
115
+ try:
116
+ main()
117
+ except Exception as e:
118
+ err = str(e)
119
+ print("\n" + "=" * 50)
120
+ if "Connection refused" in err or "connect" in err.lower():
121
+ print("ERROR: Cannot connect to MACAW")
122
+ print("Fix: Ensure MACAW is running")
123
+ else:
124
+ print(f"ERROR: {e}")
125
+ print("=" * 50)
126
+ import sys
127
+ sys.exit(1)
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example 1f: MCP Elicitation - SERVER
4
+
5
+ Demonstrates ctx.elicit() - server requesting user input from client.
6
+
7
+ The server tools here use ctx.elicit() to interactively request
8
+ information from the user during execution.
9
+
10
+ Run this server, then run 1f_elicitation_client.py in another terminal.
11
+ """
12
+
13
+ from macaw_adapters.mcp import SecureMCP, Context
14
+
15
+ mcp = SecureMCP("elicitation-demo")
16
+
17
+
18
+ @mcp.tool(description="Create user profile interactively")
19
+ async def create_profile(ctx: Context) -> dict:
20
+ """Create a profile by asking the user questions."""
21
+ ctx.info("Starting profile creation")
22
+
23
+ name = await ctx.elicit(
24
+ prompt="What is your name?",
25
+ input_type="text",
26
+ required=True
27
+ )
28
+
29
+ role = await ctx.elicit(
30
+ prompt="What is your role?",
31
+ options=["Developer", "Designer", "Manager", "Other"],
32
+ input_type="select"
33
+ )
34
+
35
+ ctx.audit(action="profile_created", target="user_profile", outcome="success", name=name)
36
+
37
+ return {"name": name, "role": role, "status": "created"}
38
+
39
+
40
+ @mcp.tool(description="Delete with confirmation")
41
+ async def delete_item(ctx: Context, item_name: str) -> dict:
42
+ """Delete an item with user confirmation."""
43
+ ctx.info(f"Request to delete: {item_name}")
44
+
45
+ confirmed = await ctx.elicit(
46
+ prompt=f"Delete '{item_name}'? This cannot be undone.",
47
+ input_type="confirm",
48
+ default="no"
49
+ )
50
+
51
+ if confirmed:
52
+ ctx.audit(action="delete", target=item_name, outcome="success")
53
+ return {"deleted": item_name, "status": "deleted"}
54
+ else:
55
+ ctx.info("Deletion cancelled")
56
+ return {"deleted": None, "status": "cancelled"}
57
+
58
+
59
+ if __name__ == "__main__":
60
+ print("=" * 50)
61
+ print("Example 1f: Elicitation Demo Server")
62
+ print("=" * 50)
63
+ print()
64
+ print("Tools: create_profile, delete_item")
65
+ print()
66
+ print("These tools use ctx.elicit() to request user input")
67
+ print("during execution (human-in-the-loop).")
68
+ print()
69
+ print("Run client: python3 1f_elicitation_client.py")
70
+ print()
71
+ print("Press Ctrl+C to stop")
72
+ print("=" * 50)
73
+ mcp.run()
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 1g_roots_client.py - MCP Roots via MAPL (Filesystem Boundaries)
4
+
5
+ Tests root boundaries declared by the server.
6
+
7
+ Demonstrates:
8
+ - list_roots() to discover server's allowed paths
9
+ - Access allowed within roots
10
+ - Access denied outside roots
11
+
12
+ Prerequisites:
13
+ - MACAW SDK installed (pip install macaw-client macaw-adapters)
14
+ - Roots server running (1g_roots_server.py)
15
+
16
+ Run:
17
+ # Terminal 1: Start the server
18
+ python 1g_roots_server.py
19
+
20
+ # Terminal 2: Run this client
21
+ python 1g_roots_client.py
22
+ """
23
+
24
+ import asyncio
25
+
26
+ from macaw_adapters.mcp import Client
27
+
28
+ DEMO_DIR = "/tmp/securemcp-roots-demo"
29
+
30
+
31
+ def find_roots_server(client):
32
+ """Find the most recent roots demo server."""
33
+ agents = client.macaw_client.list_agents(agent_type="app")
34
+ matching = [
35
+ a["agent_id"] for a in agents
36
+ if "roots-demo" in a.get("agent_id", "")
37
+ and "/tool." not in a.get("agent_id", "")
38
+ ]
39
+ return matching[0] if matching else None # Return first (most recent)
40
+
41
+
42
+ def main():
43
+ print("=" * 50)
44
+ print("Example 1g: Roots Demo Client")
45
+ print("=" * 50)
46
+
47
+ client = Client("roots-client")
48
+ print(f"Client: {client.client_id}")
49
+
50
+ server = find_roots_server(client)
51
+ if not server:
52
+ print("\nNo roots-demo server found!")
53
+ print("Start it first: python3 1g_roots_server.py")
54
+ return
55
+
56
+ print(f"Server: {server}")
57
+ print()
58
+
59
+ # Discover roots
60
+ print("Discovering server roots (list_roots()):")
61
+ print("-" * 40)
62
+
63
+ async def show_roots():
64
+ roots = await client.list_roots(server_name="roots-demo")
65
+ for r in roots:
66
+ print(f" - {r['path']}")
67
+ return roots
68
+
69
+ asyncio.run(show_roots())
70
+ print()
71
+
72
+ # Test ALLOWED access
73
+ print(f"Testing list_dir('{DEMO_DIR}') - ALLOWED:")
74
+ print("-" * 40)
75
+ result = client.macaw_client.invoke_tool("list_dir", {"path": DEMO_DIR}, target_agent=server)
76
+ print(f" Result: {result}")
77
+ print()
78
+
79
+ print(f"Testing read_file('{DEMO_DIR}/allowed.txt') - ALLOWED:")
80
+ print("-" * 40)
81
+ result = client.macaw_client.invoke_tool("read_file", {"path": f"{DEMO_DIR}/allowed.txt"}, target_agent=server)
82
+ print(f" Result: {result}")
83
+ print()
84
+
85
+ # Test DENIED access (should raise exception or return error)
86
+ print("Testing list_dir('/etc') - DENIED (outside roots):")
87
+ print("-" * 40)
88
+ try:
89
+ result = client.macaw_client.invoke_tool("list_dir", {"path": "/etc"}, target_agent=server)
90
+ print(f" Result: {result}")
91
+ except Exception as e:
92
+ print(f" ✓ Access denied (as expected): {e}")
93
+ print()
94
+
95
+ print("Testing read_file('/etc/passwd') - DENIED:")
96
+ print("-" * 40)
97
+ try:
98
+ result = client.macaw_client.invoke_tool("read_file", {"path": "/etc/passwd"}, target_agent=server)
99
+ print(f" Result: {result}")
100
+ except Exception as e:
101
+ print(f" ✓ Access denied (as expected): {e}")
102
+ print()
103
+
104
+ print("=" * 50)
105
+ print("Roots enforce filesystem boundaries via MAPL")
106
+ print("=" * 50)
107
+
108
+
109
+ if __name__ == "__main__":
110
+ try:
111
+ main()
112
+ except Exception as e:
113
+ err = str(e)
114
+ print("\n" + "=" * 50)
115
+ if "Connection refused" in err or "connect" in err.lower():
116
+ print("ERROR: Cannot connect to MACAW")
117
+ print("Fix: Ensure MACAW is running")
118
+ else:
119
+ print(f"ERROR: {e}")
120
+ print("=" * 50)
121
+ import sys
122
+ sys.exit(1)
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example 1g: MCP Roots via MAPL - SERVER
4
+
5
+ Demonstrates declaring filesystem roots that constrain server access.
6
+
7
+ Roots are declared via SecureMCP(roots=[...]) and translated to
8
+ MAPL resource policies. Tools use ctx.get_roots() to check boundaries.
9
+
10
+ Run this server, then run 1g_roots_client.py in another terminal.
11
+ """
12
+
13
+ import os
14
+ from pathlib import Path
15
+
16
+ from macaw_adapters.mcp import SecureMCP, Context
17
+
18
+ # Setup test directory
19
+ DEMO_DIR = "/tmp/securemcp-roots-demo"
20
+ os.makedirs(DEMO_DIR, exist_ok=True)
21
+ (Path(DEMO_DIR) / "allowed.txt").write_text("This file is in an allowed root.")
22
+
23
+ # Create server with declared roots
24
+ mcp = SecureMCP(
25
+ "roots-demo",
26
+ roots=[DEMO_DIR] # Only this directory is accessible
27
+ )
28
+
29
+
30
+ @mcp.tool(description="List files in directory")
31
+ def list_dir(ctx: Context, path: str) -> dict:
32
+ """List directory contents (respects root boundaries)."""
33
+ ctx.info(f"Listing: {path}")
34
+ roots = ctx.get_roots()
35
+ path = os.path.abspath(path)
36
+
37
+ # Check boundaries
38
+ if not any(path.startswith(os.path.abspath(r)) for r in roots):
39
+ ctx.warning(f"Access denied: {path}")
40
+ ctx.audit(action="list_dir", target=path, outcome="denied")
41
+ return {"error": "Access denied - outside roots", "allowed_roots": roots}
42
+
43
+ if os.path.exists(path):
44
+ entries = os.listdir(path)
45
+ ctx.audit(action="list_dir", target=path, outcome="success")
46
+ return {"path": path, "entries": entries}
47
+ return {"error": "Path not found"}
48
+
49
+
50
+ @mcp.tool(description="Read a file")
51
+ def read_file(ctx: Context, path: str) -> dict:
52
+ """Read file contents (respects root boundaries)."""
53
+ ctx.info(f"Reading: {path}")
54
+ roots = ctx.get_roots()
55
+ path = os.path.abspath(path)
56
+
57
+ if not any(path.startswith(os.path.abspath(r)) for r in roots):
58
+ ctx.audit(action="read_file", target=path, outcome="denied")
59
+ return {"error": "Access denied - outside roots"}
60
+
61
+ if os.path.isfile(path):
62
+ content = Path(path).read_text()
63
+ ctx.audit(action="read_file", target=path, outcome="success")
64
+ return {"path": path, "content": content}
65
+ return {"error": "File not found"}
66
+
67
+
68
+ if __name__ == "__main__":
69
+ print("=" * 50)
70
+ print("Example 1g: Roots Demo Server")
71
+ print("=" * 50)
72
+ print()
73
+ print("Tools: list_dir, read_file")
74
+ print()
75
+ print("Declared Roots (via MAPL):")
76
+ for root in mcp.roots:
77
+ print(f" - {root}")
78
+ print()
79
+ print(f"Test file: {DEMO_DIR}/allowed.txt")
80
+ print()
81
+ print("Run client: python3 1g_roots_client.py")
82
+ print()
83
+ print("Press Ctrl+C to stop")
84
+ print("=" * 50)
85
+ mcp.run()
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Example: SecureMCPProxy with Snowflake MCP Server
4
+
5
+ Connect to Snowflake's MCP server with MACAW security for:
6
+ - Cortex AI (search, analyst, agent)
7
+ - SQL execution
8
+ - Object management
9
+ - Semantic views
10
+
11
+ Prerequisites:
12
+ 1. Snowflake account (free trial: https://signup.snowflake.com)
13
+ 2. Snowflake Labs MCP server running with HTTP transport:
14
+ uvx snowflake-labs-mcp --service-config-file config.yaml --transport streamable-http
15
+ 3. MACAW LocalAgent running
16
+ 4. Install: pip install macaw-adapters[mcp-proxy]
17
+
18
+ Reference: https://github.com/Snowflake-Labs/mcp
19
+
20
+ Usage:
21
+ # Set environment variables
22
+ export SNOWFLAKE_MCP_URL="http://localhost:9000/snowflake-mcp"
23
+ export SNOWFLAKE_ACCOUNT="your-account"
24
+
25
+ python 2b_proxy_snowflake.py
26
+ """
27
+
28
+ import os
29
+ import logging
30
+
31
+ logging.basicConfig(level=logging.INFO)
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ def example_snowflake_proxy():
36
+ """
37
+ Connect to Snowflake MCP server through SecureMCPProxy.
38
+
39
+ Snowflake Labs MCP supports HTTP transport, making it compatible
40
+ with SecureMCPProxy's streamable_http_client.
41
+ """
42
+ from macaw_adapters.mcp import SecureMCPProxy
43
+
44
+ # Snowflake MCP server URL (running with --transport streamable-http)
45
+ upstream_url = os.environ.get(
46
+ "SNOWFLAKE_MCP_URL",
47
+ "http://localhost:9000/mcp"
48
+ )
49
+
50
+ # Create proxy - connects to Snowflake MCP, wraps with MACAW security
51
+ # Note: Snowflake auth is handled by the MCP server itself via env vars
52
+ # (SNOWFLAKE_USER, SNOWFLAKE_PASSWORD, SNOWFLAKE_ACCOUNT, etc.)
53
+ proxy = SecureMCPProxy(
54
+ app_name="snowflake-mcp",
55
+ upstream_url=upstream_url,
56
+ # No upstream_auth needed - Snowflake MCP server handles auth internally
57
+ )
58
+
59
+ print(f"\n{proxy}")
60
+
61
+ # List discovered tools from Snowflake
62
+ print("\nDiscovered Snowflake tools:")
63
+ for tool in proxy.list_tools():
64
+ print(f" - {tool['name']}: {tool.get('description', '')[:60]}...")
65
+
66
+ # Example: Run SQL query
67
+ try:
68
+ result = proxy.call_tool("run_snowflake_query", {
69
+ "statement": "SELECT CURRENT_USER(), CURRENT_ROLE(), CURRENT_DATABASE()"
70
+ })
71
+ print(f"\nSQL result: {result}")
72
+ except ValueError as e:
73
+ print(f"\nSQL not available: {e}")
74
+ except Exception as e:
75
+ print(f"\nSQL error: {e}")
76
+
77
+ # Example: List databases
78
+ try:
79
+ result = proxy.call_tool("list_objects", {
80
+ "object_type": "database"
81
+ })
82
+ # Result is a dict with 'result' key
83
+ content = result.get('result', result) if isinstance(result, dict) else result
84
+ preview = str(content)[:300] if content else "(empty)"
85
+ print(f"\nList databases result: {preview}...")
86
+ except ValueError as e:
87
+ print(f"\nList objects not available: {e}")
88
+ except Exception as e:
89
+ print(f"\nList objects error: {e}")
90
+
91
+
92
+ def example_snowflake_multi_user():
93
+ """
94
+ Multi-user pattern: different users query Snowflake with their own policies.
95
+
96
+ Use case: Data platform where analysts have different access levels.
97
+ """
98
+ from macaw_adapters.mcp import SecureMCPProxy
99
+ from macaw_client import MACAWClient
100
+
101
+ # Service creates shared proxy to Snowflake
102
+ proxy = SecureMCPProxy(
103
+ app_name="snowflake-data-platform",
104
+ upstream_url=os.environ.get("SNOWFLAKE_MCP_URL", "http://localhost:9000/snowflake-mcp"),
105
+ )
106
+
107
+ # Analyst Alice - has access to sales data
108
+ alice = MACAWClient(
109
+ user_name="alice",
110
+ jwt_token=os.environ.get("ALICE_JWT", "alice-jwt"),
111
+ agent_type="user"
112
+ )
113
+ alice.register()
114
+ alice_proxy = proxy.bind_to_user(alice)
115
+
116
+ # Analyst Bob - restricted to aggregate queries only
117
+ bob = MACAWClient(
118
+ user_name="bob",
119
+ jwt_token=os.environ.get("BOB_JWT", "bob-jwt"),
120
+ agent_type="user"
121
+ )
122
+ bob.register()
123
+ bob_proxy = proxy.bind_to_user(bob)
124
+
125
+ print("\nMulti-user Snowflake access:")
126
+ print(f" Alice: {alice_proxy}")
127
+ print(f" Bob: {bob_proxy}")
128
+
129
+ # Alice's query - MACAW policy checked against alice's permissions
130
+ try:
131
+ result = alice_proxy.call_tool("execute_sql", {
132
+ "statement": "SELECT * FROM sales.customers LIMIT 10"
133
+ })
134
+ print(f"\nAlice's query result: {result}")
135
+ except PermissionError as e:
136
+ print(f"\nAlice denied: {e}")
137
+ except Exception as e:
138
+ print(f"\nAlice error: {e}")
139
+
140
+ # Bob's query - MACAW policy checked against bob's permissions
141
+ try:
142
+ result = bob_proxy.call_tool("execute_sql", {
143
+ "statement": "SELECT COUNT(*) FROM sales.customers"
144
+ })
145
+ print(f"\nBob's query result: {result}")
146
+ except PermissionError as e:
147
+ print(f"\nBob denied: {e}")
148
+ except Exception as e:
149
+ print(f"\nBob error: {e}")
150
+
151
+
152
+ def setup_instructions():
153
+ """Print setup instructions for Snowflake MCP server."""
154
+ print("""
155
+ ================================================================================
156
+ Snowflake MCP Server Setup
157
+ ================================================================================
158
+
159
+ 1. Get Snowflake Account (free 30-day trial):
160
+ https://signup.snowflake.com
161
+
162
+ 2. Create config.yaml for Snowflake MCP:
163
+
164
+ # config.yaml
165
+ agent_services: []
166
+
167
+ other_services:
168
+ object_manager: true
169
+ query_manager: true
170
+ semantic_manager: true
171
+
172
+ sql_statement_permissions:
173
+ Select: true
174
+ Create: false
175
+ Drop: false
176
+
177
+ 3. Set Snowflake credentials:
178
+ export SNOWFLAKE_ACCOUNT="your-account"
179
+ export SNOWFLAKE_USER="your-user"
180
+ export SNOWFLAKE_PASSWORD="your-password"
181
+ export SNOWFLAKE_WAREHOUSE="COMPUTE_WH"
182
+ export SNOWFLAKE_DATABASE="your-db"
183
+ export SNOWFLAKE_SCHEMA="PUBLIC"
184
+
185
+ 4. Run Snowflake MCP server with HTTP transport:
186
+ uvx snowflake-labs-mcp --service-config-file config.yaml --transport streamable-http
187
+
188
+ 5. Run this example:
189
+ export SNOWFLAKE_MCP_URL="http://localhost:9000/mcp"
190
+ python 2b_proxy_snowflake.py
191
+
192
+ ================================================================================
193
+ """)
194
+
195
+
196
+ if __name__ == "__main__":
197
+ print("=" * 60)
198
+ print("SecureMCPProxy + Snowflake MCP Server")
199
+ print("=" * 60)
200
+
201
+ setup_instructions()
202
+
203
+ print("\n--- Example: Basic Snowflake Proxy ---")
204
+ try:
205
+ example_snowflake_proxy()
206
+ except ConnectionError as e:
207
+ print(f"\nConnection failed: {e}")
208
+ print("Make sure Snowflake MCP server is running with HTTP transport.")
209
+
210
+ # Uncomment to test multi-user:
211
+ # print("\n--- Example: Multi-User Snowflake Access ---")
212
+ # example_snowflake_multi_user()