mastyf-ai 4.2.0__tar.gz

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.
@@ -0,0 +1,75 @@
1
+ node_modules/
2
+ build/
3
+ .envrc
4
+ *.tgz
5
+ package.json.prepack-backup
6
+ .packages.prepack-backup.json
7
+ .venv-charts/
8
+ dist/
9
+ *.db
10
+ .env
11
+ .DS_Store
12
+ *.log
13
+ package-lock.json
14
+ coverage/
15
+ test-results/
16
+ /reports/
17
+ docs/tutorials/videos/.promo-build/
18
+ docs/tutorials/videos/mastyf-ai-promo.mp4
19
+ apps/cloud/public/tutorials/mastyf-ai-promo.mp4
20
+ docs/tutorials/videos/*.mp4
21
+ apps/cloud/public/tutorials/*.mp4
22
+ .turbo/
23
+ .threat-state.json
24
+ .ai-learning.json
25
+ default-policy-auto-generated.yaml
26
+ scenarios/dogfood/output/
27
+ scenarios/dogfood/sandbox/
28
+ :memory:
29
+ test-report.html
30
+ test-report*.html
31
+ corpus-eval-report.json
32
+ /benchmark-report.json
33
+ assets/ml/prompt-injection/onnx/*.onnx
34
+ .venv/
35
+ .package.prepack-backup.json
36
+ scripts/.agent-proxy-config.json
37
+ # Runtime auto-threat-research fixtures beyond committed corpus (adv-425+)
38
+ /adversarial-harness/fixtures/custom-attacks/adv-42[5-9].json
39
+ /adversarial-harness/fixtures/custom-attacks/adv-43[0-9].json
40
+ /adversarial-harness/fixtures/custom-attacks/adv-[5-9][0-9][0-9].json
41
+ # Accidental tsc output in package src trees
42
+ packages/server/src/**/*.js
43
+ packages/server/src/**/*.d.ts
44
+ packages/server/src/**/*.js.map
45
+ packages/server/src/**/*.d.ts.map
46
+ scenarios/real-life/output/
47
+ reports/tenants/
48
+ reports/.mastyf-ai-home/
49
+ *.db-shm
50
+ *.db-wal
51
+ *.db.pid
52
+ reports/provenance-test.tar.gz
53
+ reports/insurance-test/
54
+ reports/home/tenants/default/dashboard-access.jsonl
55
+ .vercel
56
+ .env*.local
57
+ *.tsbuildinfo
58
+ apps/proxy-core/proxy-core
59
+ benchmarks/results/
60
+ sca/*.md
61
+ sca/*.png
62
+ sca/ai-learning-metrics.json
63
+ deploy/dashboard.html
64
+ deploy/dashboard-spa/out/
65
+ deploy/dashboard-spa/.next/
66
+ .next/
67
+ # Generated at runtime by scenarios/tests — must never be committed (leaks local absolute paths)
68
+ scenarios/real-life/proxy-filesystem-config.json
69
+ tests/e2e/test-config.json
70
+ tests/e2e/adversarial-test-config.json
71
+ # Orphaned demo/report exports should live under gitignored reports/, never under app/data
72
+ deploy/dashboard-spa/app/data/
73
+ # Runtime logs
74
+ data/tenants/default/logs/*.jsonl
75
+ mastyf-ai-docker.tar
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: mastyf-ai
3
+ Version: 4.2.0
4
+ Summary: Mastyf AI — perimeter security proxy for MCP-based AI agents. Intercept, audit, and block dangerous tool calls.
5
+ Project-URL: Homepage, https://mastyf.ai
6
+ Project-URL: Repository, https://github.com/mastyf-ai/mastyf.ai
7
+ Project-URL: Documentation, https://docs.mastyf.ai
8
+ Author-email: Mastyf AI <dev@mastyf.ai>
9
+ License: AGPL-3.0
10
+ Keywords: ai,llm,mastyf,mcp,prompt-injection,proxy,security
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Security
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: httpx>=0.27.0
22
+ Requires-Dist: pydantic>=2.0.0
23
+ Provides-Extra: langchain
24
+ Requires-Dist: langchain-core>=0.3.0; extra == 'langchain'
25
+ Provides-Extra: openai-agents
26
+ Requires-Dist: openai>=1.0.0; extra == 'openai-agents'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Mastyf AI — Python SDK
30
+
31
+ Perimeter security proxy for MCP-based AI agents. Evaluates every tool call through the Mastyf policy engine before execution.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install mastyf-ai
37
+
38
+ # With framework integrations:
39
+ pip install mastyf-ai[openai-agents] # OpenAI Agents SDK
40
+ pip install mastyf-ai[langchain] # LangChain
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from mastyf_ai import MastyfProxy
47
+
48
+ proxy = MastyfProxy("http://localhost:4000")
49
+
50
+ # Check health
51
+ print(proxy.health()) # {'status': 'ready'}
52
+
53
+ # Test a tool call against policy
54
+ decision = proxy.test_policy("read_file", {"path": "/etc/passwd"})
55
+ print(f"{decision.action}: {decision.reason}")
56
+ # Output: block: Path not in allowed list
57
+
58
+ # Scan a package
59
+ from mastyf_ai import TrustScanner
60
+ scanner = TrustScanner()
61
+ result = scanner.scan("@modelcontextprotocol/server-filesystem")
62
+ print(f"{result['trustGrade']} ({result['trustScore']}/100)")
63
+ ```
64
+
65
+ ## Framework Integration
66
+
67
+ ### OpenAI Agents SDK
68
+
69
+ ```python
70
+ from mastyf_ai import MastyfOpenAIGuard
71
+
72
+ guard = MastyfOpenAIGuard("http://localhost:4000")
73
+
74
+ @guard.wrap_tool
75
+ async def read_file(path: str) -> str:
76
+ with open(path) as f:
77
+ return f.read()
78
+ ```
79
+
80
+ ### LangChain
81
+
82
+ ```python
83
+ from mastyf_ai import MastyfLangChainGuard
84
+
85
+ guard = MastyfLangChainGuard()
86
+ safe_tool = guard.wrap_tool(my_langchain_tool)
87
+ ```
88
+
89
+ ## CLI
90
+
91
+ ```bash
92
+ mastyf health
93
+ mastyf test read_file --args '{"path":"/etc/passwd"}'
94
+ mastyf scan @modelcontextprotocol/server-filesystem
95
+ mastyf hooks
96
+ mastyf audit
97
+ ```
98
+
99
+ ## API Reference
100
+
101
+ See [docs.mastyf.ai](https://docs.mastyf.ai) for full API documentation.
@@ -0,0 +1,73 @@
1
+ # Mastyf AI — Python SDK
2
+
3
+ Perimeter security proxy for MCP-based AI agents. Evaluates every tool call through the Mastyf policy engine before execution.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install mastyf-ai
9
+
10
+ # With framework integrations:
11
+ pip install mastyf-ai[openai-agents] # OpenAI Agents SDK
12
+ pip install mastyf-ai[langchain] # LangChain
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ from mastyf_ai import MastyfProxy
19
+
20
+ proxy = MastyfProxy("http://localhost:4000")
21
+
22
+ # Check health
23
+ print(proxy.health()) # {'status': 'ready'}
24
+
25
+ # Test a tool call against policy
26
+ decision = proxy.test_policy("read_file", {"path": "/etc/passwd"})
27
+ print(f"{decision.action}: {decision.reason}")
28
+ # Output: block: Path not in allowed list
29
+
30
+ # Scan a package
31
+ from mastyf_ai import TrustScanner
32
+ scanner = TrustScanner()
33
+ result = scanner.scan("@modelcontextprotocol/server-filesystem")
34
+ print(f"{result['trustGrade']} ({result['trustScore']}/100)")
35
+ ```
36
+
37
+ ## Framework Integration
38
+
39
+ ### OpenAI Agents SDK
40
+
41
+ ```python
42
+ from mastyf_ai import MastyfOpenAIGuard
43
+
44
+ guard = MastyfOpenAIGuard("http://localhost:4000")
45
+
46
+ @guard.wrap_tool
47
+ async def read_file(path: str) -> str:
48
+ with open(path) as f:
49
+ return f.read()
50
+ ```
51
+
52
+ ### LangChain
53
+
54
+ ```python
55
+ from mastyf_ai import MastyfLangChainGuard
56
+
57
+ guard = MastyfLangChainGuard()
58
+ safe_tool = guard.wrap_tool(my_langchain_tool)
59
+ ```
60
+
61
+ ## CLI
62
+
63
+ ```bash
64
+ mastyf health
65
+ mastyf test read_file --args '{"path":"/etc/passwd"}'
66
+ mastyf scan @modelcontextprotocol/server-filesystem
67
+ mastyf hooks
68
+ mastyf audit
69
+ ```
70
+
71
+ ## API Reference
72
+
73
+ See [docs.mastyf.ai](https://docs.mastyf.ai) for full API documentation.
@@ -0,0 +1,19 @@
1
+ """
2
+ Mastyf AI — Python SDK for MCP security proxy.
3
+
4
+ Usage:
5
+ from mastyf_ai import MastyfProxy
6
+
7
+ proxy = MastyfProxy("http://localhost:4000")
8
+ proxy.start()
9
+
10
+ # The proxy is now protecting all MCP tool calls
11
+ """
12
+
13
+ from .client import MastyfProxy, MastyfConfig
14
+ from .middleware.openai_agents import MastyfOpenAIGuard
15
+ from .middleware.langchain import MastyfLangChainGuard
16
+ from .scanner import TrustScanner
17
+
18
+ __version__ = "4.2.0"
19
+ __all__ = ["MastyfProxy", "MastyfConfig", "MastyfOpenAIGuard", "MastyfLangChainGuard", "TrustScanner"]
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python3
2
+ """Mastyf AI CLI — Python entry point."""
3
+
4
+ import argparse
5
+ import json
6
+ import sys
7
+ from .client import MastyfProxy, MastyfConfig
8
+
9
+
10
+ def main():
11
+ parser = argparse.ArgumentParser(description="Mastyf AI — MCP security proxy client")
12
+ parser.add_argument("--proxy-url", default="http://localhost:4000", help="Proxy URL")
13
+ parser.add_argument("--api-key", help="API key for authentication")
14
+
15
+ sub = parser.add_subparsers(dest="command")
16
+
17
+ health = sub.add_parser("health", help="Check proxy health")
18
+ test = sub.add_parser("test", help="Test a tool call against policy")
19
+ test.add_argument("tool", help="Tool name")
20
+ test.add_argument("--args", default="{}", help="JSON arguments")
21
+ scan = sub.add_parser("scan", help="Scan an MCP package for trust score")
22
+ scan.add_argument("package", help="Package name")
23
+ hooks = sub.add_parser("hooks", help="List registered hooks")
24
+ audit = sub.add_parser("audit", help="Verify audit chain")
25
+
26
+ args = parser.parse_args()
27
+
28
+ if not args.command:
29
+ parser.print_help()
30
+ return
31
+
32
+ proxy = MastyfProxy(MastyfConfig(proxy_url=args.proxy_url, api_key=args.api_key))
33
+
34
+ try:
35
+ if args.command == "health":
36
+ print(json.dumps(proxy.health(), indent=2))
37
+ elif args.command == "test":
38
+ tool_args = json.loads(args.args)
39
+ decision = proxy.test_policy(args.tool, tool_args)
40
+ print(f"Action: {decision.action}\nRule: {decision.rule}\nReason: {decision.reason}")
41
+ elif args.command == "scan":
42
+ from .scanner import TrustScanner
43
+ scanner = TrustScanner(args.proxy_url)
44
+ result = scanner.scan(args.package)
45
+ print(f"Package: {result.get('packageName')}")
46
+ print(f"Score: {result.get('trustScore')}/100")
47
+ print(f"Grade: {result.get('trustGrade')}")
48
+ print(f"Dimensions: {json.dumps(result.get('dimensions', {}), indent=2)}")
49
+ elif args.command == "hooks":
50
+ for h in proxy.get_hooks():
51
+ print(f" {h['name']} ({h['type']}, enabled={h['enabled']})")
52
+ elif args.command == "audit":
53
+ chain = proxy.get_audit_chain()
54
+ print(f"Audit entries: {chain.get('entries', 0)}")
55
+ print(f"Integrity: {'OK' if chain.get('ok') else 'BROKEN (' + str(chain.get('breaks', 0)) + ' breaks)'}")
56
+ except Exception as e:
57
+ print(f"Error: {e}", file=sys.stderr)
58
+ sys.exit(1)
59
+ finally:
60
+ proxy.close()
@@ -0,0 +1,130 @@
1
+ """Mastyf proxy client — connects to a running Mastyf AI proxy instance."""
2
+
3
+ import httpx
4
+ from dataclasses import dataclass, field
5
+ from typing import Optional, Dict, Any, List
6
+
7
+
8
+ @dataclass
9
+ class MastyfConfig:
10
+ proxy_url: str = "http://localhost:4000"
11
+ api_key: Optional[str] = None
12
+ tenant_id: str = "default"
13
+ timeout: float = 10.0
14
+
15
+
16
+ @dataclass
17
+ class PolicyDecision:
18
+ action: str # "pass", "block", "flag"
19
+ rule: str
20
+ reason: str
21
+
22
+
23
+ class MastyfProxy:
24
+ """Client for a running Mastyf AI proxy instance."""
25
+
26
+ def __init__(self, config: MastyfConfig | str = "http://localhost:4000"):
27
+ if isinstance(config, str):
28
+ config = MastyfConfig(proxy_url=config)
29
+ self.config = config
30
+ self._client = httpx.Client(
31
+ base_url=config.proxy_url.rstrip("/"),
32
+ timeout=config.timeout,
33
+ headers=self._build_headers(),
34
+ )
35
+
36
+ def _build_headers(self) -> Dict[str, str]:
37
+ headers = {"Content-Type": "application/json"}
38
+ if self.config.api_key:
39
+ headers["Authorization"] = f"Bearer {self.config.api_key}"
40
+ headers["X-Tenant-Id"] = self.config.tenant_id
41
+ return headers
42
+
43
+ def health(self) -> Dict[str, Any]:
44
+ """Check proxy health."""
45
+ r = self._client.get("/health")
46
+ r.raise_for_status()
47
+ return r.json()
48
+
49
+ def test_policy(self, tool: str, args: Dict[str, Any], server: str = "default") -> PolicyDecision:
50
+ """Test a tool call against the current policy without executing it."""
51
+ r = self._client.post("/api/policy/test", json={
52
+ "tool": tool,
53
+ "server": server,
54
+ "args": args,
55
+ })
56
+ r.raise_for_status()
57
+ data = r.json()
58
+ return PolicyDecision(
59
+ action=data.get("action", "pass"),
60
+ rule=data.get("rule", "unknown"),
61
+ reason=data.get("reason", ""),
62
+ )
63
+
64
+ def evaluate_tool_call(self, tool: str, args: Dict[str, Any], server: str = "default") -> PolicyDecision:
65
+ """Evaluate a tool call through the full policy engine and hooks."""
66
+ r = self._client.post(f"{self.config.proxy_url.rstrip('/')}/mcp", json={
67
+ "jsonrpc": "2.0",
68
+ "id": "python-sdk",
69
+ "method": "tools/call",
70
+ "params": {"name": tool, "arguments": args},
71
+ })
72
+ data = r.json()
73
+ if "error" in data:
74
+ err = data["error"]
75
+ return PolicyDecision(
76
+ action="block",
77
+ rule=err.get("data", {}).get("rule", "unknown"),
78
+ reason=err.get("message", ""),
79
+ )
80
+ return PolicyDecision(action="pass", rule="default", reason="")
81
+
82
+ def get_hooks(self) -> List[Dict[str, Any]]:
83
+ """List all registered hooks."""
84
+ r = self._client.get("/api/hooks")
85
+ r.raise_for_status()
86
+ return r.json().get("hooks", [])
87
+
88
+ def register_hook(self, name: str, code: str, hook_type: str = "before", priority: int = 50) -> bool:
89
+ """Register a custom hook."""
90
+ r = self._client.post("/api/hooks", json={
91
+ "name": name, "code": code, "type": hook_type, "priority": priority,
92
+ })
93
+ return r.status_code == 201
94
+
95
+ def store_credential(self, token: str, provider: str = "api", credential_type: str = "bearer_token") -> str:
96
+ """Store an API token in the encrypted credential broker."""
97
+ r = self._client.post("/api/credentials", json={
98
+ "token": token,
99
+ "providerName": provider,
100
+ "providerId": provider,
101
+ "credentialType": credential_type,
102
+ })
103
+ r.raise_for_status()
104
+ return r.json().get("id", "")
105
+
106
+ def get_audit_chain(self) -> Dict[str, Any]:
107
+ """Verify audit hash chain integrity."""
108
+ r = self._client.get("/api/audit/verify")
109
+ r.raise_for_status()
110
+ return r.json()
111
+
112
+ def get_learning_suggestions(self) -> List[Dict[str, Any]]:
113
+ """Get whitelist suggestions from learning mode."""
114
+ r = self._client.get("/api/learning/suggestions")
115
+ r.raise_for_status()
116
+ return r.json().get("suggestions", [])
117
+
118
+ def close(self):
119
+ self._client.close()
120
+
121
+
122
+ def run_eval(proxy: MastyfProxy, payloads: List[Dict[str, Any]]) -> Dict[str, Any]:
123
+ """Run evaluation payloads against the proxy policy."""
124
+ r = httpx.post(
125
+ f"{proxy.config.proxy_url.rstrip('/')}/api/eval/run",
126
+ json={"payloads": payloads},
127
+ timeout=30.0,
128
+ )
129
+ r.raise_for_status()
130
+ return r.json()
@@ -0,0 +1 @@
1
+ """Mastyf AI middleware for Python agent frameworks."""
@@ -0,0 +1,45 @@
1
+ """LangChain integration for Mastyf AI security proxy."""
2
+
3
+ from typing import Optional, Dict, Any, Callable
4
+ from ..client import MastyfProxy
5
+
6
+
7
+ class MastyfLangChainGuard:
8
+ """Middleware for LangChain that evaluates tool calls through Mastyf before execution."""
9
+
10
+ def __init__(self, proxy_url: str = "http://localhost:4000", api_key: Optional[str] = None):
11
+ self._proxy = MastyfProxy(proxy_url)
12
+
13
+ def wrap_tool(self, tool: Any) -> Any:
14
+ """Wrap a LangChain tool with Mastyf policy evaluation."""
15
+ tool_name = getattr(tool, "name", tool.__class__.__name__)
16
+ original_invoke = tool._run if hasattr(tool, "_run") else tool.invoke
17
+
18
+ def safe_invoke(*args: Any, **kwargs: Any) -> Any:
19
+ args_dict = kwargs or {}
20
+ if args and len(args) > 0 and isinstance(args[0], str):
21
+ args_dict = {"input": args[0]}
22
+ decision = self._proxy.test_policy(tool_name, args_dict)
23
+ if decision.action == "block":
24
+ raise PermissionError(f"Mastyf blocked '{tool_name}': {decision.reason}")
25
+ return original_invoke(*args, **kwargs)
26
+
27
+ if hasattr(tool, "_run"):
28
+ tool._run = safe_invoke
29
+ else:
30
+ tool.invoke = safe_invoke
31
+ return tool
32
+
33
+ def create_callback(self) -> Callable:
34
+ """Create a callback for LangChain agent runs."""
35
+ def on_tool_start(tool_name: str, input_str: str) -> None:
36
+ import json
37
+ try:
38
+ args = json.loads(input_str)
39
+ except Exception:
40
+ args = {"input": input_str}
41
+ decision = self._proxy.test_policy(tool_name, args)
42
+ if decision.action == "block":
43
+ raise PermissionError(f"Mastyf blocked '{tool_name}': {decision.reason}")
44
+
45
+ return on_tool_start
@@ -0,0 +1,33 @@
1
+ """OpenAI Agents SDK integration for Mastyf AI security proxy."""
2
+
3
+ from typing import Optional, Dict, Any
4
+ from ..client import MastyfProxy, PolicyDecision
5
+
6
+
7
+ class MastyfOpenAIGuard:
8
+ """Guard middleware for OpenAI Agents SDK that evaluates all tool calls through Mastyf."""
9
+
10
+ def __init__(self, proxy_url: str = "http://localhost:4000", api_key: Optional[str] = None):
11
+ self._proxy = MastyfProxy(proxy_url) if isinstance(proxy_url, str) else proxy_url
12
+ self._proxy.config.api_key = api_key
13
+
14
+ async def before_tool_call(self, tool_name: str, args: Dict[str, Any]) -> Dict[str, Any]:
15
+ """Called before every tool execution. Returns {'allowed': bool, 'reason': str}."""
16
+ decision = self._proxy.test_policy(tool_name, args)
17
+ return {
18
+ "allowed": decision.action != "block",
19
+ "reason": decision.reason if decision.action == "block" else "",
20
+ }
21
+
22
+ def wrap_tool(self, tool_fn, tool_name: str):
23
+ """Wrap a tool function with policy evaluation."""
24
+ original = tool_fn
25
+
26
+ async def wrapped(**kwargs):
27
+ decision = self._proxy.test_policy(tool_name, kwargs)
28
+ if decision.action == "block":
29
+ raise PermissionError(f"Mastyf blocked '{tool_name}': {decision.reason}")
30
+ return await original(**kwargs)
31
+
32
+ wrapped.__name__ = tool_name
33
+ return wrapped
@@ -0,0 +1,33 @@
1
+ """MCP package trust scanner — scan npm packages for security grades."""
2
+
3
+ from typing import Dict, Any, Optional
4
+ from ..client import MastyfProxy
5
+
6
+
7
+ class TrustScanner:
8
+ """Scan MCP server packages for trust scores."""
9
+
10
+ def __init__(self, proxy_url: str = "http://localhost:4000"):
11
+ self._proxy = MastyfProxy(proxy_url)
12
+
13
+ def scan(self, package_name: str) -> Dict[str, Any]:
14
+ """Scan a package and return its trust score."""
15
+ import httpx
16
+ r = httpx.post(
17
+ f"{self._proxy.config.proxy_url.rstrip('/')}/api/registry/scan",
18
+ json={"packageName": package_name},
19
+ timeout=15.0,
20
+ )
21
+ r.raise_for_status()
22
+ data = r.json()
23
+ return data.get("result", {})
24
+
25
+ def score(self, package_name: str) -> Optional[int]:
26
+ """Return just the numeric trust score (0-100)."""
27
+ result = self.scan(package_name)
28
+ return result.get("trustScore")
29
+
30
+ def grade(self, package_name: str) -> Optional[str]:
31
+ """Return just the trust grade (A+ through F)."""
32
+ result = self.scan(package_name)
33
+ return result.get("trustGrade")
@@ -0,0 +1,42 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mastyf-ai"
7
+ version = "4.2.0"
8
+ description = "Mastyf AI — perimeter security proxy for MCP-based AI agents. Intercept, audit, and block dangerous tool calls."
9
+ readme = "README.md"
10
+ license = {text = "AGPL-3.0"}
11
+ requires-python = ">=3.10"
12
+ authors = [
13
+ {name = "Mastyf AI", email = "dev@mastyf.ai"},
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: GNU Affero General Public License v3",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Security",
24
+ "Topic :: Software Development :: Libraries :: Python Modules",
25
+ ]
26
+ dependencies = [
27
+ "httpx>=0.27.0",
28
+ "pydantic>=2.0.0",
29
+ ]
30
+ keywords = ["mcp", "security", "ai", "llm", "proxy", "prompt-injection", "mastyf"]
31
+
32
+ [project.optional-dependencies]
33
+ openai-agents = ["openai>=1.0.0"]
34
+ langchain = ["langchain-core>=0.3.0"]
35
+
36
+ [project.urls]
37
+ Homepage = "https://mastyf.ai"
38
+ Repository = "https://github.com/mastyf-ai/mastyf.ai"
39
+ Documentation = "https://docs.mastyf.ai"
40
+
41
+ [project.scripts]
42
+ mastyf = "mastyf_ai.cli:main"