-
Notifications
You must be signed in to change notification settings - Fork 5
feat(mcp): MCP Server Integration Epic (#163) #210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Conversation
* Add MCP optional dependency and entry points - Add fastmcp>=2.3.0,<2.8 as optional dependency under [mcp] extra - Add pleiades-mcp console script entry point - Create pleiades.mcp package with graceful degradation - Configure Pixi mcp environment with pydantic<2.12 pin (fastmcp compatibility) - Add mcp-server task for development Note: pydantic<2.12 is required due to fastmcp/pydantic compatibility issue (see sooperset/mcp-atlassian#721) Closes #167 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix conditional export and NameError in MCP module - Define mcp=None when MCP unavailable to prevent NameError - Only export FastMCP in __all__ when MCP is available - Remove mcp from __all__ since it may be None 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Refactor MCP module with proper architecture and tests BREAKING CHANGES: - fastmcp constraint updated from >=2.3.0,<2.8 to >=2.12.0,<3 (2.12.0+ has pydantic 2.12 compatibility fix) - Removed pydantic<2.12 pin (no longer needed) Architecture improvements: - Refactored to lazy initialization pattern (get_server function) - Added proper TYPE_CHECKING imports for type safety - Added ImportWarning when MCP deps unavailable (aids debugging) - Added comprehensive error messages with install instructions - Added logging integration via pleiades.utils.logger Error handling: - __main__.py now catches ImportError, KeyboardInterrupt, Exception - get_server() raises RuntimeError if server init fails unexpectedly - Clear error messages with pip/pixi install instructions Environment configuration: - Added referencing>=0.35.0,<0.37 to mcp feature (resolves conda conflict) - Added test feature to mcp environment (enables pixi run -e mcp test) Tests: - Added tests/unit/pleiades/mcp/test_init.py with 9 test cases - Tests cover: availability detection, exports, server init, entry points References: - fastmcp pydantic fix: jlowin/fastmcp#1377 - referencing conflict: conda pins 0.37.0, jsonschema-path needs <0.37 Closes #167 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix logger import in MCP server Use loguru_logger instead of logger (correct export name from pleiades.utils.logger module). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix test patterns and remove code issues from review Test fixes: - Replace broken pytest.importorskip() in skipif with module-level HAS_FASTMCP flag (correct pattern) - Add test for check_mcp_available() raising when not installed - Tests now work in BOTH environments: - MCP env: 9 passed, 1 skipped - Default env: 6 passed, 4 skipped Code cleanup (following nova backend pattern): - Remove noisy ImportWarning on every import - Remove self-referential type annotations - Remove unreachable RuntimeError check in get_server() - Simplify server.py by removing TYPE_CHECKING import 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
* Add pleiades.workflows module with high-level APIs (#164) This commit introduces the workflows module providing simple, high-level functions for common PLEIADES operations: - `validate_dataset()`: Check dataset structure and determine workflow type - `analyze_resonance()`: Execute complete resonance analysis workflow - `extract_manifest()`: Parse sMCP manifest files from datasets Key features: - Supports both simplified (pre-existing SAMMY files) and full imaging workflows - Pydantic models for type-safe results (ResonanceResult, ValidationResult) - Follows existing PLEIADES patterns (factory, logging, error handling) - Comprehensive test coverage (37 tests) The full imaging workflow is placeholder - requires imaging processing infrastructure (to be implemented in #172). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix code issues identified by Copilot review Addresses 13 issues identified in PR #195 review: 1. Add encoding='utf-8' to file open in extract_manifest 2. Fix YAML frontmatter parsing to handle --- in body (maxsplit=2) 3. Remove redundant "No raw data directory found" error message 4. Prefer simplified workflow when both types available (full not implemented) 5. Fix docstring: returns ResonanceResult, doesn't raise ValueError 6. Handle empty fit_results list with proper error message 7. Add debug logging for missing broadening parameters 8. Export IsotopeResult model in __init__.py 9. Fix test_full_workflow_missing_open_beam assertion (should be invalid) 10. Add test for manifest with horizontal rules in body 11. Add test for both workflow types available 12. Add test for empty fit_results handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Fix critical bugs in workflows module from code review - Fix chi_squared_results None check before accessing attributes - Fix empty isotopes list parameter validation - Fix 0.0 is falsy bug in chi_squared checks (use 'is not None') - Fix float conversion exception handling (ValueError, TypeError) - Fix severity field to use Literal type - Fix skip_validation logic to return clear error when no files found - Fix broken docstring example with None checks - Add SAMMY output file existence check before parsing - Fix empty YAML frontmatter handling - Add PermissionError handling for sammy_data directory access - Update tests to create mock SAMMY output files 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Add input validation for physical properties and chi-squared - Add positive value validation for MaterialProperties (density, atomic_mass, temperature) - Add input validation for FitQuality.from_chi_squared (reject negative, NaN, infinity) - Add consistent None check for chi_sq.dof - Add empty YAML frontmatter handling in manifest parsing - Add tests for all new validation cases 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
Implements Issue #165: Creates a thin abstraction layer that decouples PLEIADES from specific MCP implementations (like FastMCP). Features: - @mcp_tool decorator registers functions in a global registry - Supports both @mcp_tool and @mcp_tool() syntax - Input validation: tool names must be alphanumeric with _ or - - Registry mutation protection via deep copy - Zero overhead (returns original function unchanged) - Comprehensive documentation (performance, ordering, limitations) Includes 57 tests covering: - Registration, name/description handling - Function preservation, async support - Edge cases (classmethod, staticmethod, varargs, kwargs) - Input validation and security - Registry mutation protection 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <[email protected]>
* feat(mcp): add MCP server module with auto-discovery Implements Issue #166 - MCP server with tool auto-discovery: - discover_tools(): Returns deep copy of registered tools from decorator registry - generate_tool_schema(): Creates JSON schema from function signature/type hints - register_tools(): Registers all discovered tools with FastMCP server - get_server(): Returns singleton FastMCP instance - main(): Entry point for pleiades-mcp command Key features: - Python 3.10+ union syntax support (int | str, int | None) - Handles Union, Optional, list, dict, Any, nested types - O(n) performance documented for schema generation - Graceful error handling with specific exceptions - Unified loguru_logger usage throughout Test coverage: - 30 tests passing (13 skipped when FastMCP not installed) - Edge cases: forward refs, closures, async, generators, unicode - Security: SQL injection patterns, long strings, special characters Closes #166 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(mcp): address code review feedback - Add NoneType constant at module level for efficient type comparisons - Use logger = loguru_logger.bind(name=__name__) pattern (codebase convention) - Replace all loguru_logger calls with bound logger - Use importlib.util.find_spec for cleaner fastmcp detection in tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
* feat(mcp): add MCP tool wrappers for workflow functions
Add tools.py with @mcp_tool decorated wrappers around existing
PLEIADES workflow functions:
- validate_resonance_dataset: Validate dataset structure
- extract_resonance_manifest: Extract YAML frontmatter from manifest
- analyze_resonance: Run SAMMY analysis with configurable backend
Features:
- MCP-compliant string path inputs with JSON-serializable outputs
- Consistent response format: {status: success/error, data/error}
- Pydantic model serialization with mode='json' for datetime handling
- Circular reference protection (max depth 100)
- Security documentation for path traversal considerations
Includes 44 comprehensive tests covering:
- Tool registration and metadata
- Parameter descriptions and types
- Success/error response formats
- JSON serialization edge cases
- Workflow integration with mocking
Closes #168
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(mcp): address review issues in tools and tests
Fixes:
- Correct manifest search order in error message to match workflow
(manifest_intermediate.md, smcp_manifest.md, manifest.md)
- Add defensive comment explaining None check in analyze_resonance
- Fix mock model_dump to accept kwargs (mode='json')
- Remove unused result variables in test_passes_backend_parameter
and test_backend_defaults_to_auto
- Add noqa comment for dynamic import in TestParameterDescriptions
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(tests): use import aliases instead of noqa for module imports
Replace noqa suppression with proper fix: use aliased imports
(`as tools_module`) to distinguish module imports (for side effects
and reload) from specific function imports (`from X import Y`).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(mcp): comprehensive JSON serialization and test improvements
tools.py:
- Handle datetime/date/time via isoformat()
- Handle Decimal via float conversion
- Handle bytes via UTF-8 decode with replacement
- Handle set/frozenset via sorted list conversion
- Handle Enum via .value extraction
- Add fallback for unknown types with warning log
- Update docstring to document all handled types
test_tools.py:
- Remove shebang (not executable)
- Move importlib import to local scope (single use)
- Fix inconsistent mock variable names (mock_v -> mock_validate)
- Add TestJsonSerializationEdgeCases class with 13 tests:
- Circular reference protection
- datetime/date/time handling
- Decimal handling
- bytes handling (valid and invalid UTF-8)
- set/frozenset handling
- Enum/IntEnum handling
- Unknown type fallback
- Nested mixed types
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
---------
Co-authored-by: Claude Opus 4.5 <[email protected]>
Add comprehensive test coverage for MCP functionality: Unit Tests (test_manifest.py - 32 tests): - Manifest file discovery (3 naming variants) - YAML frontmatter parsing - Field validation and defaults - Datetime conversion - Material properties extraction - Error handling Integration Tests (test_mcp_workflow.py - 20 tests): - End-to-end validate/manifest/analyze workflow - Error recovery (permissions, missing files) - JSON serialization verification - Result structure consistency - Docker backend availability check Also fixes TestToolRegistration setup to handle registry clearing by other test classes. Total MCP tests: 201 (182 unit + 19 integration) Total project tests: 1277 Closes #169 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <[email protected]>
…200) * fix(mcp): import tools module in server main() for registration The MCP server was not discovering tools because the tools module was never imported to trigger the @mcp_tool decorator registration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(tests): rename mcp test directories to avoid namespace collision The test directories `tests/unit/pleiades/mcp/` and `tests/integration/mcp/` were colliding with the installed `mcp` Python package, causing pytest to import tests as `mcp.test_server` instead of the full path. This caused `MCP_AVAILABLE` to be False during test execution because the `pleiades.mcp` module was confused with the test package. Renamed to `test_mcp/` following pytest conventions to avoid the collision. Fixes 2 failing tests in TestServerIntegration. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
Ported full imaging workflow from prototype MCP server. The workflow processes raw TIFF imaging data through normalization, format conversion, ENDF retrieval, SAMMY execution, and results parsing. Key changes: - Replaced stub _execute_full_workflow() with ~400 lines of implementation - Added isotope list validation before ENDF retrieval - Added JSON config file validation - Added ENDF parameter file validation - Fixed file extension bug in twenty format conversion - Updated test for full workflow to verify normalization error handling Fixes #201 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
ENDF files use the naming convention 072-Hf-174.B-VIII.0.par (includes
atomic number and library version). Updated validation to use glob
pattern matching instead of expecting simple {isotope}.par naming.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <[email protected]>
NOVA backend for SAMMY execution is disabled because: - nova-galaxy package is unstable - No immediate plan for full NOVA support - Development focus is on MCP integration and 2D resonance fitting Changes: - factory.py: Comment out NovaSammyRunner import, add _NOVA_AVAILABLE flag - test_nova.py: Skip entire module - test_factory.py: Skip NOVA tests, update backend availability assertion - test_config.py: Skip TestNovaSammyConfig class Code preserved for future re-enablement when nova-galaxy stabilizes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
The underlying workflow.analyze_resonance() already supports an isotopes parameter, but the MCP tool wrapper wasn't exposing it. This prevented users from customizing which isotopes to analyze. Now users can specify: isotopes: ['Hf-177', 'Hf-178'] to analyze only specific isotopes instead of all natural isotopes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
* fix(workflows): remove hardcoded demo-specific values (#204) Replace hardcoded Hafnium-specific isotopes and abundances with data-driven lookup from isotopes.info. This enables resonance analysis for any element, not just Hf. Changes: - Add get_isotopes_by_element() and get_natural_composition() to IsotopeManager - Add enrichment support to ManifestData for non-natural abundance samples - Add _get_isotope_composition() helper with proper input validation - Fix hardcoded ORNL facility to read from manifest - Fix mass number calculation to handle weighted averages - Upgrade silent broadening parameter failures from DEBUG to WARNING 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(workflows): address copilot review feedback - Move math and re imports to module level (PEP 8 compliance) - Compile regex pattern at module level for performance - Remove IGNORECASE flag - require proper capitalization for isotope strings - Fix Facility mapping: lansce -> lanl (LANSCE is at LANL) - Remove non-existent Facility.j_parc - default to ornl with warning 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(workflows): address copilot nitpick feedback - Rename test to test_single_natural_isotope_element_100_percent_abundance (clarifies it tests elements with one natural isotope, not user-specified) - Add valid_isotope_count for explicit tracking in mass number calculation - Improve error message: "No valid mass numbers found" vs generic failure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
Add support for explicit isotopes list in manifest to control which isotopes are analyzed. Previously, the isotopes field in manifest YAML was completely ignored, causing all natural isotopes to be included. Changes: - Add isotopes field to ManifestData with format validation - Parse isotopes from YAML frontmatter in extract_manifest - Use manifest isotopes as priority 2 in _get_isotope_composition: 1. User-specified isotopes (highest) 2. Manifest isotopes list (new) 3. Enrichment dict 4. Natural abundance (fallback) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <[email protected]>
* docs(mcp): add MCP module documentation Add comprehensive documentation for the PLEIADES MCP server integration: - README.md: Installation, quick start, tool reference, troubleshooting - manifest-format.md: YAML frontmatter specification with validation rules - integration-pattern.md: Reusable pattern for adding MCP to scientific packages Changes include: - Mermaid diagrams for better web rendering - Clarified decorator approaches (FastMCP built-in vs custom registry) - Documented isotope selection priority hierarchy Closes #170 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: address Copilot review comments - Add timezone Z to ISO-8601 timestamp example (manifest-format.md) - Change URL to next branch instead of feature branch (integration-pattern.md) - Remove nova from backend options in MCP tool description (tools.py) - Use _mcp_registry instead of _tool_registry for accuracy (integration-pattern.md) - Use sys.exit() instead of exit() (integration-pattern.md) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * Update docs/mcp/integration-pattern.md Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]> Co-authored-by: Copilot <[email protected]>
…209) * docs(mcp): add ADR-001 documenting MCP integration pattern decisions Documents the four key architectural decisions for MCP integration: 1. Optional package (not separate package) - prevents drift 2. FastMCP 2.x - future direction of MCP ecosystem 3. Thin wrapper pattern - decouples from specific implementations 4. Physics-centric manifest - enables AI-compilable data descriptions Includes vision alignment section connecting to broader sMCP goals. Closes #171 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(docs): address Copilot review comments - Remove misleading line count claims (~50 lines, ~100 lines) - Use consistent sMCP branding (full_sMCP_manifest) - Replace "Estimated Code Size" table with qualitative "Implementation Scope" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements Model Context Protocol (MCP) server integration for PLEIADES, enabling AI-assisted neutron resonance analysis. The implementation adds:
- High-level
pleiades.workflowsmodule with validation, manifest extraction, and analysis functions pleiades.mcpmodule with FastMCP server and@mcp_tooldecorator- 3 MCP tools exposing workflow functions to AI clients
- Full imaging workflow support (normalization → SAMMY fitting → results)
- Comprehensive test coverage (195 tests) and documentation
Key Architecture:
AI Application (Claude) → MCP Protocol → pleiades.mcp (FastMCP server)
→ pleiades.workflows (orchestration) → pleiades core (SAMMY, processing)
Reviewed changes
Copilot reviewed 30 out of 33 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/pleiades/workflows/test_resonance.py | Comprehensive workflow tests (975 lines) with validation, manifest parsing, analysis workflows |
| tests/unit/pleiades/workflows/test_models.py | Pydantic model validation tests (372 lines) |
| tests/unit/pleiades/test_mcp/test_tools.py | MCP tool wrapper tests (1074 lines) with JSON serialization |
| tests/unit/pleiades/test_mcp/test_server.py | Server auto-discovery and registration tests (911 lines) |
| tests/unit/pleiades/test_mcp/test_decorators.py | Decorator registration tests (956 lines) |
| tests/unit/pleiades/test_mcp/test_manifest.py | Manifest parsing integration tests (722 lines) |
| tests/integration/test_mcp/test_mcp_workflow.py | End-to-end workflow integration tests (281 lines) |
| src/pleiades/workflows/resonance.py | Main workflow orchestration (1266 lines) |
| src/pleiades/workflows/models.py | Pydantic result models (300 lines) |
| src/pleiades/mcp/tools.py | MCP tool wrappers (238 lines) |
| src/pleiades/mcp/server.py | FastMCP server implementation (303 lines) |
| src/pleiades/mcp/decorators.py | @mcp_tool decorator (250 lines) |
| src/pleiades/nuclear/isotopes/manager.py | Added isotope lookup methods (128 lines) |
| pyproject.toml | Added MCP optional dependency and console script |
The PR is well-structured with TDD approach (tests written before implementation), comprehensive error handling, and clear documentation. The code follows established patterns and includes proper input validation throughout.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
|
|
||
| class TestToolRegistration: | ||
| """Test that tools are registered via @mcp_tool decorator.""" | ||
|
|
||
| def setup_method(self): | ||
| """Clear and re-register all tools for consistent test isolation.""" | ||
| import importlib | ||
|
|
||
| import pleiades.mcp.tools as tools_module |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Module 'pleiades.mcp.tools' is imported with both 'import' and 'import from'.
| class TestToolRegistration: | |
| """Test that tools are registered via @mcp_tool decorator.""" | |
| def setup_method(self): | |
| """Clear and re-register all tools for consistent test isolation.""" | |
| import importlib | |
| import pleiades.mcp.tools as tools_module | |
| import pleiades.mcp.tools as tools_module | |
| class TestToolRegistration: | |
| """Test that tools are registered via @mcp_tool decorator.""" | |
| def setup_method(self): | |
| """Clear and re-register all tools for consistent test isolation.""" | |
| import importlib |
| """Clear and re-register all tools for consistent test isolation.""" | ||
| import importlib | ||
|
|
||
| import pleiades.mcp.tools as tools_module |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Module 'pleiades.mcp.tools' is imported with both 'import' and 'import from'.
|
|
||
| # Detect fastmcp availability at module load (correct pattern) | ||
| try: | ||
| import fastmcp # noqa: F401 |
Copilot
AI
Dec 9, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'fastmcp' is not used.
Summary
Complete MCP (Model Context Protocol) server integration for PLEIADES, enabling AI-assisted neutron resonance analysis.
Key Deliverables:
pleiades.workflowsmodule with high-level APIspleiades.mcpmodule with FastMCP server and@mcp_tooldecoratorvalidate_resonance_dataset,extract_resonance_manifest,analyze_resonanceInstallation
Usage
Sub-Issues Closed
pleiades.workflowsmodule@mcp_tooldecoratorpleiades.mcpserver moduleStats
Architecture
Test Plan
pixi run test)Closes #163
🤖 Generated with Claude Code