iris-security-mcp 0.2.5__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.
Files changed (27) hide show
  1. iris_security_mcp-0.2.5/PKG-INFO +32 -0
  2. iris_security_mcp-0.2.5/README.md +9 -0
  3. iris_security_mcp-0.2.5/iris_mcp/__init__.py +3 -0
  4. iris_security_mcp-0.2.5/iris_mcp/prompts/__init__.py +1 -0
  5. iris_security_mcp-0.2.5/iris_mcp/prompts/system.py +36 -0
  6. iris_security_mcp-0.2.5/iris_mcp/resources/__init__.py +1 -0
  7. iris_security_mcp-0.2.5/iris_mcp/resources/frameworks.py +88 -0
  8. iris_security_mcp-0.2.5/iris_mcp/server.py +237 -0
  9. iris_security_mcp-0.2.5/iris_mcp/tools/__init__.py +1 -0
  10. iris_security_mcp-0.2.5/iris_mcp/tools/_common.py +64 -0
  11. iris_security_mcp-0.2.5/iris_mcp/tools/compliance.py +354 -0
  12. iris_security_mcp-0.2.5/iris_mcp/tools/cost.py +142 -0
  13. iris_security_mcp-0.2.5/iris_mcp/tools/discovery.py +214 -0
  14. iris_security_mcp-0.2.5/iris_mcp/tools/evidence.py +156 -0
  15. iris_security_mcp-0.2.5/iris_mcp/tools/governance.py +170 -0
  16. iris_security_mcp-0.2.5/iris_mcp/tools/hitl.py +189 -0
  17. iris_security_mcp-0.2.5/iris_mcp/tools/monitoring.py +213 -0
  18. iris_security_mcp-0.2.5/iris_mcp/tools/regulatory.py +83 -0
  19. iris_security_mcp-0.2.5/iris_security_mcp.egg-info/PKG-INFO +32 -0
  20. iris_security_mcp-0.2.5/iris_security_mcp.egg-info/SOURCES.txt +25 -0
  21. iris_security_mcp-0.2.5/iris_security_mcp.egg-info/dependency_links.txt +1 -0
  22. iris_security_mcp-0.2.5/iris_security_mcp.egg-info/entry_points.txt +2 -0
  23. iris_security_mcp-0.2.5/iris_security_mcp.egg-info/requires.txt +4 -0
  24. iris_security_mcp-0.2.5/iris_security_mcp.egg-info/top_level.txt +1 -0
  25. iris_security_mcp-0.2.5/pyproject.toml +45 -0
  26. iris_security_mcp-0.2.5/setup.cfg +4 -0
  27. iris_security_mcp-0.2.5/tests/test_mcp_server.py +124 -0
@@ -0,0 +1,32 @@
1
+ Metadata-Version: 2.4
2
+ Name: iris-security-mcp
3
+ Version: 0.2.5
4
+ Summary: IRIS MCP Server — connect Claude to AI agent governance
5
+ Author-email: IRIS Platform <sdk@iris.ai>
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/gimartinb/iris-sdk
8
+ Project-URL: Repository, https://github.com/gimartinb/iris-sdk
9
+ Project-URL: Documentation, https://gimartinb.github.io/iris-sdk/docs/mcp/
10
+ Keywords: ai-governance,mcp,claude,compliance,cursor
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: Apache Software License
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: iris-security-sdk>=0.2.5
20
+ Requires-Dist: iris-security-cli>=0.2.5
21
+ Requires-Dist: mcp>=1.0.0
22
+ Requires-Dist: anyio>=4.0
23
+
24
+ # iris-security-mcp
25
+
26
+ IRIS MCP Server — connect Claude Desktop or Cursor to AI agent governance.
27
+
28
+ ```bash
29
+ pip install iris-security-mcp
30
+ ```
31
+
32
+ See [docs/mcp/README.md](../../docs/mcp/README.md) for setup and configuration.
@@ -0,0 +1,9 @@
1
+ # iris-security-mcp
2
+
3
+ IRIS MCP Server — connect Claude Desktop or Cursor to AI agent governance.
4
+
5
+ ```bash
6
+ pip install iris-security-mcp
7
+ ```
8
+
9
+ See [docs/mcp/README.md](../../docs/mcp/README.md) for setup and configuration.
@@ -0,0 +1,3 @@
1
+ """IRIS MCP Server — Claude-accessible AI agent governance."""
2
+
3
+ __version__ = "0.2.5"
@@ -0,0 +1 @@
1
+ """IRIS MCP prompt templates."""
@@ -0,0 +1,36 @@
1
+ """System prompts for IRIS MCP."""
2
+
3
+ IRIS_SYSTEM_PROMPT = """
4
+ You have access to IRIS — an AI agent governance platform.
5
+ IRIS helps developers declare what their AI agents are allowed
6
+ to do and enforces those policies at runtime.
7
+
8
+ When a developer asks about AI compliance, regulations, or
9
+ governing their agents, use IRIS tools to give them accurate,
10
+ real-time answers based on their actual codebase and agents.
11
+
12
+ Key IRIS concepts:
13
+ - AgentPassport: the agent's identity and compliance declaration
14
+ - Cedar policy: formally verified policy compiled from plain English
15
+ - Evidence Vault: tamper-evident audit trail of every decision
16
+ - HITL: human-in-the-loop approval for sensitive actions
17
+
18
+ Available frameworks (free):
19
+ - colorado-ai-act: Colorado SB 26-189 (effective Jan 1, 2027)
20
+ - ccpa-admt: California ADMT regulations (effective Jan 1, 2026)
21
+ - colorado-chatbot: Colorado HB 1263 (effective Jan 1, 2027)
22
+ - colorado-health-ai: Colorado HB 1139 (effective Jan 1, 2027)
23
+ - colorado-mental-health: Colorado HB 1195 (effective Aug 12, 2026)
24
+ - nyc-ll144: NYC Local Law 144 — AI in hiring (active now)
25
+ - illinois-ai-video: Illinois AI Video Interview Act (active now)
26
+
27
+ Available frameworks (Pro):
28
+ - nist-ai-rmf, fedramp-moderate, hipaa, soc2, gdpr, eu-ai-act,
29
+ china-pipl, hr-ai
30
+
31
+ When a developer asks which regulations apply to their agent,
32
+ call iris_framework_suggest with what you know about their agent.
33
+
34
+ When a Pro feature is needed but license is not active, explain
35
+ what the feature does and how to activate: iris license activate
36
+ """.strip()
@@ -0,0 +1 @@
1
+ """IRIS MCP resources."""
@@ -0,0 +1,88 @@
1
+ """Compliance framework documentation as MCP resources."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from mcp.types import Resource
6
+
7
+ from iris_core.compliance.framework_check import load_bundle_data
8
+ from iris_core.compliance.registry import _BUNDLE_LOADERS, _is_paid_bundle
9
+
10
+ _FRAMEWORK_NAMES = {
11
+ "colorado-ai-act": "Colorado AI Act (SB 26-189)",
12
+ "colorado-chatbot": "Colorado Chatbot Act (HB 1263)",
13
+ "colorado-health-ai": "Colorado Health AI (HB 1139)",
14
+ "colorado-mental-health-ai": "Colorado Mental Health AI (HB 1195)",
15
+ "ccpa-admt": "California CCPA/ADMT",
16
+ "china-pipl": "China PIPL",
17
+ "illinois-ai-video": "Illinois AI Video Interview Act",
18
+ "nyc-ll144": "NYC Local Law 144 — AEDTs",
19
+ "hipaa": "HIPAA",
20
+ "soc2": "SOC 2",
21
+ "gdpr": "GDPR",
22
+ "eu-ai-act": "EU AI Act",
23
+ "nist-ai-rmf": "NIST AI RMF",
24
+ "fedramp": "FedRAMP Moderate",
25
+ }
26
+
27
+
28
+ def _load_bundle_metadata(bundle_id: str) -> dict:
29
+ """Load bundle docs without Pro entitlement gating (discovery/docs only)."""
30
+ return load_bundle_data(bundle_id)
31
+
32
+
33
+ def _framework_markdown(bundle_id: str, rules: dict) -> str:
34
+ name = rules.get("full_name") or _FRAMEWORK_NAMES.get(bundle_id, bundle_id)
35
+ tier = "Pro" if _is_paid_bundle(bundle_id) else "Free"
36
+ lines = [
37
+ f"# {name}",
38
+ "",
39
+ f"**Bundle ID:** `{bundle_id}`",
40
+ f"**Tier:** {tier}",
41
+ f"**Jurisdiction:** {rules.get('jurisdiction', 'See bundle')}",
42
+ f"**Effective date:** {rules.get('effective_date', 'See bundle')}",
43
+ "",
44
+ "## IRIS coverage",
45
+ "",
46
+ ]
47
+ if rules.get("warning"):
48
+ lines.extend([f"> {rules['warning']}", ""])
49
+ lines.append("## Rules")
50
+ lines.append("")
51
+ for rule in rules.get("rules", []):
52
+ lines.append(f"### {rule.get('rule_id')} — {rule.get('name')}")
53
+ lines.append(f"- **Severity:** {rule.get('severity')}")
54
+ lines.append(f"- {rule.get('description', '')}")
55
+ if rule.get("how_iris_satisfies"):
56
+ lines.append(f"- **IRIS control:** {rule['how_iris_satisfies']}")
57
+ lines.append("")
58
+ return "\n".join(lines)
59
+
60
+
61
+ def build_framework_resources() -> list[Resource]:
62
+ resources: list[Resource] = []
63
+ for bundle_id in sorted(_BUNDLE_LOADERS):
64
+ rules = _load_bundle_metadata(bundle_id)
65
+ name = rules.get("full_name") or _FRAMEWORK_NAMES.get(bundle_id, bundle_id)
66
+ resources.append(
67
+ Resource(
68
+ uri=f"iris://frameworks/{bundle_id}",
69
+ name=name,
70
+ description="Rules, effective dates, and IRIS coverage",
71
+ mimeType="text/markdown",
72
+ )
73
+ )
74
+ return resources
75
+
76
+
77
+ FRAMEWORK_RESOURCES = build_framework_resources()
78
+ _FRAMEWORK_TEXT: dict[str, str] = {}
79
+
80
+
81
+ def get_framework_text(uri: str) -> str | None:
82
+ if not _FRAMEWORK_TEXT:
83
+ for bundle_id in _BUNDLE_LOADERS:
84
+ rules = _load_bundle_metadata(bundle_id)
85
+ _FRAMEWORK_TEXT[f"iris://frameworks/{bundle_id}"] = _framework_markdown(
86
+ bundle_id, rules
87
+ )
88
+ return _FRAMEWORK_TEXT.get(uri)
@@ -0,0 +1,237 @@
1
+ """IRIS MCP Server — connect Claude Desktop and Cursor to AI governance."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ import sys
8
+
9
+ from mcp.server import Server
10
+ from mcp.server.stdio import stdio_server
11
+ from mcp.types import GetPromptResult, Prompt, PromptMessage, Resource, TextContent, TextResourceContents
12
+
13
+ from iris_core.entitlements import Entitlements, Feature
14
+ from iris_mcp import __version__
15
+ from iris_mcp.prompts.system import IRIS_SYSTEM_PROMPT
16
+ from iris_mcp.resources.frameworks import FRAMEWORK_RESOURCES, get_framework_text
17
+ from iris_mcp.tools import (
18
+ compliance,
19
+ cost,
20
+ discovery,
21
+ evidence,
22
+ governance,
23
+ hitl,
24
+ monitoring,
25
+ regulatory,
26
+ )
27
+
28
+ app = Server("iris-governance")
29
+
30
+
31
+ def collect_tools(*, include_pro: bool | None = None) -> list:
32
+ if include_pro is None:
33
+ include_pro = Entitlements().has(Feature.BUNDLE_HIPAA)
34
+
35
+ tools = []
36
+ tools.extend(discovery.get_tools())
37
+ tools.extend(compliance.get_free_tools())
38
+ tools.extend(governance.get_tools())
39
+ tools.extend(monitoring.get_free_tools())
40
+ tools.extend(evidence.get_free_tools())
41
+ tools.extend(regulatory.get_tools())
42
+
43
+ if include_pro:
44
+ tools.extend(compliance.get_pro_tools())
45
+ tools.extend(evidence.get_pro_tools())
46
+ tools.extend(hitl.get_tools())
47
+ tools.extend(cost.get_tools())
48
+ tools.extend(monitoring.get_pro_tools())
49
+
50
+ return tools
51
+
52
+
53
+ @app.list_tools()
54
+ async def list_tools() -> list:
55
+ """Return all available IRIS tools."""
56
+ return collect_tools()
57
+
58
+
59
+ @app.call_tool()
60
+ async def call_tool(name: str, arguments: dict | None) -> list[TextContent]:
61
+ """Route tool calls to the appropriate handler."""
62
+ arguments = arguments or {}
63
+ router = {
64
+ "iris_scan_discover": discovery.scan_discover,
65
+ "iris_scan_govern": discovery.scan_govern,
66
+ "iris_list_agents": discovery.list_agents,
67
+ "iris_compliance_check": compliance.check,
68
+ "iris_framework_suggest": compliance.framework_suggest,
69
+ "iris_regulatory_check": regulatory.check,
70
+ "iris_compliance_assess": compliance.assess,
71
+ "iris_certify": compliance.certify,
72
+ "iris_policy_catalog": compliance.catalog,
73
+ "iris_declare": governance.declare,
74
+ "iris_compile_policy": governance.compile,
75
+ "iris_preview_policy": governance.preview,
76
+ "iris_status": monitoring.status,
77
+ "iris_witness_recent": monitoring.witness_recent,
78
+ "iris_sentinel_status": monitoring.sentinel_status,
79
+ "iris_drift_check": monitoring.drift_check,
80
+ "iris_evidence_summary": evidence.summary,
81
+ "iris_evidence_report": evidence.report,
82
+ "iris_evidence_export": evidence.export,
83
+ "iris_hitl_list": hitl.list_reviews,
84
+ "iris_hitl_approve": hitl.approve,
85
+ "iris_hitl_reject": hitl.reject,
86
+ "iris_hitl_rules": hitl.show_rules,
87
+ "iris_cost_report": cost.report,
88
+ "iris_cost_summary": cost.summary,
89
+ "iris_cost_optimize": cost.optimize,
90
+ }
91
+
92
+ handler = router.get(name)
93
+ if not handler:
94
+ return [TextContent(type="text", text=f"Unknown tool: {name}")]
95
+
96
+ return await handler(arguments)
97
+
98
+
99
+ @app.list_resources()
100
+ async def list_resources() -> list[Resource]:
101
+ """Return framework documentation as readable resources."""
102
+ return FRAMEWORK_RESOURCES
103
+
104
+
105
+ @app.read_resource()
106
+ async def read_resource(uri: str) -> str:
107
+ text = get_framework_text(str(uri))
108
+ if text is None:
109
+ raise ValueError(f"Unknown resource: {uri}")
110
+ return text
111
+
112
+
113
+ @app.list_prompts()
114
+ async def list_prompts() -> list[Prompt]:
115
+ """Return pre-built prompts for common IRIS tasks."""
116
+ return [
117
+ Prompt(
118
+ name="govern_new_agent",
119
+ description="Walk through governing a new AI agent from scratch",
120
+ arguments=[
121
+ {"name": "agent_name", "description": "Name of the agent", "required": True},
122
+ {"name": "domain", "description": "What the agent does", "required": True},
123
+ ],
124
+ ),
125
+ Prompt(
126
+ name="compliance_review",
127
+ description="Run a full compliance review for an agent",
128
+ arguments=[
129
+ {"name": "agent_name", "description": "Agent to review", "required": True},
130
+ ],
131
+ ),
132
+ Prompt(
133
+ name="explain_framework",
134
+ description="Explain a compliance framework in plain English",
135
+ arguments=[
136
+ {"name": "framework", "description": "Framework ID", "required": True},
137
+ ],
138
+ ),
139
+ ]
140
+
141
+
142
+ @app.get_prompt()
143
+ async def get_prompt(name: str, arguments: dict[str, str] | None) -> GetPromptResult:
144
+ arguments = arguments or {}
145
+ if name == "govern_new_agent":
146
+ agent = arguments.get("agent_name", "my-agent")
147
+ domain = arguments.get("domain", "AI assistant")
148
+ text = (
149
+ f"Help me govern a new AI agent named {agent}.\n"
150
+ f"It does: {domain}\n\n"
151
+ f"Use iris_declare, then iris_compile_policy, then iris_compliance_check."
152
+ )
153
+ elif name == "compliance_review":
154
+ agent = arguments.get("agent_name", "my-agent")
155
+ text = (
156
+ f"Run a full compliance review for agent {agent}.\n"
157
+ "Use iris_status, iris_framework_suggest, iris_compliance_check, "
158
+ "and iris_evidence_summary."
159
+ )
160
+ elif name == "explain_framework":
161
+ framework = arguments.get("framework", "colorado-ai-act")
162
+ text = (
163
+ f"Read iris://frameworks/{framework} and explain it in plain English "
164
+ "for a developer building AI agents."
165
+ )
166
+ else:
167
+ text = IRIS_SYSTEM_PROMPT
168
+
169
+ return GetPromptResult(
170
+ description=name,
171
+ messages=[PromptMessage(role="user", content=TextContent(type="text", text=text))],
172
+ )
173
+
174
+
175
+ async def run_server() -> None:
176
+ async with stdio_server() as streams:
177
+ await app.run(
178
+ streams[0],
179
+ streams[1],
180
+ app.create_initialization_options(
181
+ instructions=IRIS_SYSTEM_PROMPT,
182
+ ),
183
+ )
184
+
185
+
186
+ def cli_main() -> None:
187
+ parser = argparse.ArgumentParser(description="IRIS MCP Server")
188
+ parser.add_argument("--version", action="version", version=f"iris-mcp {__version__}")
189
+ parser.add_argument(
190
+ "--list-tools",
191
+ action="store_true",
192
+ help="List available MCP tools and exit",
193
+ )
194
+ parser.add_argument(
195
+ "--cursor-mode",
196
+ action="store_true",
197
+ help="Cursor IDE mode (stdio transport; reserved for future options)",
198
+ )
199
+ args = parser.parse_args()
200
+
201
+ if args.list_tools:
202
+ tools = collect_tools()
203
+ for tool in tools:
204
+ tier = "pro" if tool.name in {
205
+ "iris_compliance_assess",
206
+ "iris_certify",
207
+ "iris_policy_catalog",
208
+ "iris_sentinel_status",
209
+ "iris_drift_check",
210
+ "iris_evidence_report",
211
+ "iris_evidence_export",
212
+ "iris_hitl_list",
213
+ "iris_hitl_approve",
214
+ "iris_hitl_reject",
215
+ "iris_hitl_rules",
216
+ "iris_cost_report",
217
+ "iris_cost_summary",
218
+ "iris_cost_optimize",
219
+ } else "free"
220
+ print(f"{tool.name}\t{tier}\t{tool.description}")
221
+ return
222
+
223
+ if args.cursor_mode:
224
+ pass
225
+
226
+ try:
227
+ asyncio.run(run_server())
228
+ except KeyboardInterrupt:
229
+ sys.exit(0)
230
+
231
+
232
+ def main() -> None:
233
+ cli_main()
234
+
235
+
236
+ if __name__ == "__main__":
237
+ main()
@@ -0,0 +1 @@
1
+ """IRIS MCP tool modules."""
@@ -0,0 +1,64 @@
1
+ """Shared helpers for IRIS MCP tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from mcp.types import TextContent
10
+
11
+ from iris_core.entitlements import Entitlements, Feature
12
+
13
+
14
+ def text_response(text: str) -> list[TextContent]:
15
+ return [TextContent(type="text", text=text)]
16
+
17
+
18
+ def scan_directory(arguments: dict[str, Any]) -> Path:
19
+ directory = arguments.get("directory")
20
+ if directory:
21
+ return Path(directory).expanduser().resolve()
22
+ return Path.cwd()
23
+
24
+
25
+ def governance_dir(arguments: dict[str, Any] | None = None) -> Path:
26
+ arguments = arguments or {}
27
+ if arguments.get("governance_dir"):
28
+ return Path(arguments["governance_dir"]).expanduser().resolve()
29
+
30
+ env_dir = os.environ.get("IRIS_GOVERNANCE_DIR")
31
+ if env_dir:
32
+ path = Path(env_dir).expanduser().resolve()
33
+ if path.name == "agents":
34
+ return path
35
+ agents = path / "agents"
36
+ if agents.exists():
37
+ return agents
38
+ return path
39
+
40
+ return Path.cwd() / "governance" / "agents"
41
+
42
+
43
+ def has_pro() -> bool:
44
+ return Entitlements().has(Feature.BUNDLE_HIPAA)
45
+
46
+
47
+ def pro_gate(feature: Feature, upgrade_message: str) -> str | None:
48
+ if Entitlements().has(feature):
49
+ return None
50
+ return upgrade_message
51
+
52
+
53
+ def format_table(headers: list[str], rows: list[list[str]]) -> str:
54
+ if not rows:
55
+ return "No results."
56
+ widths = [len(h) for h in headers]
57
+ for row in rows:
58
+ for idx, cell in enumerate(row):
59
+ widths[idx] = max(widths[idx], len(cell))
60
+ sep = " ".join(h.ljust(widths[i]) for i, h in enumerate(headers))
61
+ lines = [sep, "-" * len(sep)]
62
+ for row in rows:
63
+ lines.append(" ".join(row[i].ljust(widths[i]) for i in range(len(headers))))
64
+ return "\n".join(lines)