crucible-mcp 1.0.0__tar.gz → 1.1.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.
Files changed (85) hide show
  1. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/PKG-INFO +6 -3
  2. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/README.md +5 -2
  3. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/pyproject.toml +1 -1
  4. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/compliance.py +9 -5
  5. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/server.py +72 -1
  6. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible_mcp.egg-info/PKG-INFO +6 -3
  7. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/setup.cfg +0 -0
  8. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/__init__.py +0 -0
  9. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/cli.py +0 -0
  10. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/domain/__init__.py +0 -0
  11. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/domain/detection.py +0 -0
  12. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/__init__.py +0 -0
  13. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/assertions.py +0 -0
  14. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/budget.py +0 -0
  15. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/bundled/error-handling.yaml +0 -0
  16. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/bundled/security.yaml +0 -0
  17. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/bundled/smart-contract.yaml +0 -0
  18. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/models.py +0 -0
  19. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/enforcement/patterns.py +0 -0
  20. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/errors.py +0 -0
  21. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/hooks/__init__.py +0 -0
  22. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/hooks/claudecode.py +0 -0
  23. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/hooks/precommit.py +0 -0
  24. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/__init__.py +0 -0
  25. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/loader.py +0 -0
  26. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/API_DESIGN.md +0 -0
  27. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/COMMITS.md +0 -0
  28. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/DATABASE.md +0 -0
  29. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/DOCUMENTATION.md +0 -0
  30. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/ERROR_HANDLING.md +0 -0
  31. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/FP.md +0 -0
  32. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/GITIGNORE.md +0 -0
  33. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/OBSERVABILITY.md +0 -0
  34. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/PRECOMMIT.md +0 -0
  35. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/SECURITY.md +0 -0
  36. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/SMART_CONTRACT.md +0 -0
  37. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/SYSTEM_DESIGN.md +0 -0
  38. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/TESTING.md +0 -0
  39. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/knowledge/principles/TYPE_SAFETY.md +0 -0
  40. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/models.py +0 -0
  41. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/review/__init__.py +0 -0
  42. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/review/core.py +0 -0
  43. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/__init__.py +0 -0
  44. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/accessibility-engineer/SKILL.md +0 -0
  45. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/backend-engineer/SKILL.md +0 -0
  46. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/customer-success/SKILL.md +0 -0
  47. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/data-engineer/SKILL.md +0 -0
  48. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/devops-engineer/SKILL.md +0 -0
  49. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/fde-engineer/SKILL.md +0 -0
  50. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/formal-verification/SKILL.md +0 -0
  51. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/gas-optimizer/SKILL.md +0 -0
  52. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/incident-responder/SKILL.md +0 -0
  53. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/loader.py +0 -0
  54. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/mev-researcher/SKILL.md +0 -0
  55. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/mobile-engineer/SKILL.md +0 -0
  56. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/performance-engineer/SKILL.md +0 -0
  57. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/product-engineer/SKILL.md +0 -0
  58. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/protocol-architect/SKILL.md +0 -0
  59. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/security-engineer/SKILL.md +0 -0
  60. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/tech-lead/SKILL.md +0 -0
  61. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/uiux-engineer/SKILL.md +0 -0
  62. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/skills/web3-engineer/SKILL.md +0 -0
  63. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/synthesis/__init__.py +0 -0
  64. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/tools/__init__.py +0 -0
  65. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/tools/delegation.py +0 -0
  66. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible/tools/git.py +0 -0
  67. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible_mcp.egg-info/SOURCES.txt +0 -0
  68. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible_mcp.egg-info/dependency_links.txt +0 -0
  69. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible_mcp.egg-info/entry_points.txt +0 -0
  70. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible_mcp.egg-info/requires.txt +0 -0
  71. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/src/crucible_mcp.egg-info/top_level.txt +0 -0
  72. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_cli.py +0 -0
  73. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_compliance.py +0 -0
  74. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_detection.py +0 -0
  75. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_enforcement.py +0 -0
  76. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_full_review.py +0 -0
  77. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_git.py +0 -0
  78. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_hooks_cli.py +0 -0
  79. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_integration.py +0 -0
  80. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_knowledge.py +0 -0
  81. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_precommit.py +0 -0
  82. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_server.py +0 -0
  83. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_skills.py +0 -0
  84. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_skills_loader.py +0 -0
  85. {crucible_mcp-1.0.0 → crucible_mcp-1.1.0}/tests/test_tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crucible-mcp
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Code review MCP server for Claude. Not affiliated with Atlassian.
5
5
  Author: be.nvy
6
6
  License-Expression: MIT
@@ -109,13 +109,16 @@ Add to Claude Code (`.mcp.json`):
109
109
 
110
110
  | Tool | Purpose |
111
111
  |------|---------|
112
+ | `get_assertions()` | **Session start:** Load enforced patterns into context |
113
+ | `get_principles(topic)` | **Session start:** Load engineering knowledge by topic |
114
+ | `load_knowledge(files)` | **Session start:** Load specific knowledge files |
112
115
  | `review(path)` | Full review: analysis + skills + knowledge + assertions |
113
116
  | `review(mode='staged')` | Review git changes with enforcement |
114
- | `load_knowledge(files)` | Load specific knowledge files |
115
- | `get_principles(topic)` | Load engineering knowledge by topic |
116
117
  | `delegate_*` | Direct tool access (semgrep, ruff, slither, bandit) |
117
118
  | `check_tools()` | Show installed analysis tools |
118
119
 
120
+ **Tip:** Call `get_assertions()` at the start of a session so Claude knows what patterns to avoid *before* writing code.
121
+
119
122
  ## CLI
120
123
 
121
124
  ```bash
@@ -91,13 +91,16 @@ Add to Claude Code (`.mcp.json`):
91
91
 
92
92
  | Tool | Purpose |
93
93
  |------|---------|
94
+ | `get_assertions()` | **Session start:** Load enforced patterns into context |
95
+ | `get_principles(topic)` | **Session start:** Load engineering knowledge by topic |
96
+ | `load_knowledge(files)` | **Session start:** Load specific knowledge files |
94
97
  | `review(path)` | Full review: analysis + skills + knowledge + assertions |
95
98
  | `review(mode='staged')` | Review git changes with enforcement |
96
- | `load_knowledge(files)` | Load specific knowledge files |
97
- | `get_principles(topic)` | Load engineering knowledge by topic |
98
99
  | `delegate_*` | Direct tool access (semgrep, ruff, slither, bandit) |
99
100
  | `check_tools()` | Show installed analysis tools |
100
101
 
102
+ **Tip:** Call `get_assertions()` at the start of a session so Claude knows what patterns to avoid *before* writing code.
103
+
101
104
  ## CLI
102
105
 
103
106
  ```bash
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "crucible-mcp"
3
- version = "1.0.0"
3
+ version = "1.1.0"
4
4
  description = "Code review MCP server for Claude. Not affiliated with Atlassian."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -6,6 +6,7 @@ Supports Sonnet (default) and Opus (for high-stakes assertions).
6
6
 
7
7
  import json
8
8
  import os
9
+ import sys
9
10
  from typing import Any
10
11
 
11
12
  from crucible.enforcement.budget import (
@@ -79,8 +80,8 @@ def _load_api_key_from_config() -> str | None:
79
80
  key = data.get("anthropic_api_key") or data.get("ANTHROPIC_API_KEY")
80
81
  if key:
81
82
  return key
82
- except Exception:
83
- pass # Ignore malformed config files
83
+ except Exception as e:
84
+ print(f"Warning: failed to read {config_path}: {e}", file=sys.stderr)
84
85
 
85
86
  return None
86
87
 
@@ -294,27 +295,30 @@ def run_single_assertion(
294
295
  )
295
296
 
296
297
  except ImportError as e:
298
+ print(f"Warning: LLM assertion '{assertion.id}' skipped: {e}", file=sys.stderr)
297
299
  return LLMAssertionResult(
298
300
  assertion_id=assertion.id,
299
- passed=True, # Don't fail on missing dependency
301
+ passed=True, # Graceful degradation - don't fail on missing dependency
300
302
  findings=(),
301
303
  tokens_used=0,
302
304
  model_used=model_name,
303
305
  error=str(e),
304
306
  )
305
307
  except ValueError as e:
308
+ print(f"Warning: LLM assertion '{assertion.id}' skipped: {e}", file=sys.stderr)
306
309
  return LLMAssertionResult(
307
310
  assertion_id=assertion.id,
308
- passed=True, # Don't fail on missing API key
311
+ passed=True, # Graceful degradation - don't fail on missing API key
309
312
  findings=(),
310
313
  tokens_used=0,
311
314
  model_used=model_name,
312
315
  error=str(e),
313
316
  )
314
317
  except Exception as e:
318
+ print(f"Warning: LLM assertion '{assertion.id}' failed: {e}", file=sys.stderr)
315
319
  return LLMAssertionResult(
316
320
  assertion_id=assertion.id,
317
- passed=True, # Don't fail on API errors
321
+ passed=True, # Graceful degradation - don't fail on API errors
318
322
  findings=(),
319
323
  tokens_used=0,
320
324
  model_used=model_name,
@@ -7,6 +7,7 @@ from mcp.server import Server
7
7
  from mcp.server.stdio import stdio_server
8
8
  from mcp.types import TextContent, Tool
9
9
 
10
+ from crucible.enforcement.assertions import load_assertions
10
11
  from crucible.knowledge.loader import (
11
12
  get_custom_knowledge_files,
12
13
  load_all_knowledge,
@@ -314,6 +315,20 @@ async def list_tools() -> list[Tool]:
314
315
  },
315
316
  },
316
317
  ),
318
+ Tool(
319
+ name="get_assertions",
320
+ description="Load active enforcement assertions for this project. Call at session start to understand what code patterns are enforced. Returns all pattern and LLM assertions that will be checked during reviews.",
321
+ inputSchema={
322
+ "type": "object",
323
+ "properties": {
324
+ "include_compliance": {
325
+ "type": "boolean",
326
+ "description": "Include LLM compliance assertion details (default: true)",
327
+ "default": True,
328
+ },
329
+ },
330
+ },
331
+ ),
317
332
  ]
318
333
 
319
334
 
@@ -715,6 +730,61 @@ def _handle_load_knowledge(arguments: dict[str, Any]) -> list[TextContent]:
715
730
  return [TextContent(type="text", text="\n".join(output_parts))]
716
731
 
717
732
 
733
+ def _handle_get_assertions(arguments: dict[str, Any]) -> list[TextContent]:
734
+ """Handle get_assertions tool - load active enforcement rules."""
735
+ include_compliance = arguments.get("include_compliance", True)
736
+
737
+ assertions, load_errors = load_assertions()
738
+
739
+ if not assertions and not load_errors:
740
+ return [TextContent(type="text", text="No assertions found. Add assertion files to .crucible/assertions/ or use bundled assertions.")]
741
+
742
+ parts: list[str] = ["# Active Enforcement Assertions\n"]
743
+ parts.append("These patterns are enforced during code review. Avoid these in your code.\n")
744
+
745
+ if load_errors:
746
+ parts.append("## Load Errors\n")
747
+ for error in load_errors:
748
+ parts.append(f"- {error}")
749
+ parts.append("")
750
+
751
+ # Group by source file / category
752
+ pattern_assertions = [a for a in assertions if a.type.value == "pattern"]
753
+ llm_assertions = [a for a in assertions if a.type.value == "llm"]
754
+
755
+ if pattern_assertions:
756
+ parts.append("## Pattern Assertions (fast, always run)\n")
757
+ parts.append("| ID | Message | Severity | Languages |")
758
+ parts.append("|---|---|---|---|")
759
+ for a in pattern_assertions:
760
+ langs = ", ".join(a.languages) if a.languages else "all"
761
+ parts.append(f"| `{a.id}` | {a.message} | {a.severity} | {langs} |")
762
+ parts.append("")
763
+
764
+ if llm_assertions and include_compliance:
765
+ parts.append("## LLM Compliance Assertions (semantic, budget-controlled)\n")
766
+ parts.append("| ID | Message | Severity | Model |")
767
+ parts.append("|---|---|---|---|")
768
+ for a in llm_assertions:
769
+ model = a.model or "sonnet"
770
+ parts.append(f"| `{a.id}` | {a.message} | {a.severity} | {model} |")
771
+ parts.append("")
772
+
773
+ # Show compliance requirements for LLM assertions
774
+ parts.append("### Compliance Requirements\n")
775
+ for a in llm_assertions:
776
+ parts.append(f"**{a.id}:**")
777
+ if a.compliance:
778
+ parts.append(f"```\n{a.compliance.strip()}\n```")
779
+ parts.append("")
780
+
781
+ # Summary
782
+ parts.append("---\n")
783
+ parts.append(f"**Total:** {len(pattern_assertions)} pattern + {len(llm_assertions)} LLM assertions")
784
+
785
+ return [TextContent(type="text", text="\n".join(parts))]
786
+
787
+
718
788
  def _handle_delegate_semgrep(arguments: dict[str, Any]) -> list[TextContent]:
719
789
  """Handle delegate_semgrep tool."""
720
790
  path = arguments.get("path", "")
@@ -1241,7 +1311,8 @@ async def call_tool(name: str, arguments: dict[str, Any]) -> list[TextContent]:
1241
1311
  "quick_review": _handle_quick_review,
1242
1312
  "full_review": _handle_full_review,
1243
1313
  "review_changes": _handle_review_changes,
1244
- # Other tools
1314
+ # Context injection tools (call at session start)
1315
+ "get_assertions": _handle_get_assertions,
1245
1316
  "get_principles": _handle_get_principles,
1246
1317
  "load_knowledge": _handle_load_knowledge,
1247
1318
  "delegate_semgrep": _handle_delegate_semgrep,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crucible-mcp
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: Code review MCP server for Claude. Not affiliated with Atlassian.
5
5
  Author: be.nvy
6
6
  License-Expression: MIT
@@ -109,13 +109,16 @@ Add to Claude Code (`.mcp.json`):
109
109
 
110
110
  | Tool | Purpose |
111
111
  |------|---------|
112
+ | `get_assertions()` | **Session start:** Load enforced patterns into context |
113
+ | `get_principles(topic)` | **Session start:** Load engineering knowledge by topic |
114
+ | `load_knowledge(files)` | **Session start:** Load specific knowledge files |
112
115
  | `review(path)` | Full review: analysis + skills + knowledge + assertions |
113
116
  | `review(mode='staged')` | Review git changes with enforcement |
114
- | `load_knowledge(files)` | Load specific knowledge files |
115
- | `get_principles(topic)` | Load engineering knowledge by topic |
116
117
  | `delegate_*` | Direct tool access (semgrep, ruff, slither, bandit) |
117
118
  | `check_tools()` | Show installed analysis tools |
118
119
 
120
+ **Tip:** Call `get_assertions()` at the start of a session so Claude knows what patterns to avoid *before* writing code.
121
+
119
122
  ## CLI
120
123
 
121
124
  ```bash
File without changes