README.md•14.9 kB
# Temporal Nexus MCP Example
A comprehensive sample application demonstrating how to bridge [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) tools with [Temporal's Nexus RPC framework](https://docs.temporal.io/nexus).
This calculator provides various mathematical operations through MCP, backed by the reliability and durability of Temporal workflows.
Uses a [forked version](https://github.com/steveandroulakis/nexus-mcp-python) of [@bergundy](https://github.com/bergundy)'s nexus-mcp-python, a lightweight way of adding MCP capability to Temporal Nexus Operations.
[](https://drive.google.com/file/d/1oZ_fXj14stUpOaWlao1dJfAzHoODWH3H/view?usp=sharing)
[Watch the 4 minute demo](https://drive.google.com/file/d/1oZ_fXj14stUpOaWlao1dJfAzHoODWH3H/view?usp=sharing) to understand how interaction works.
[](https://www.python.org/downloads/)
[](https://modelcontextprotocol.io/)
[](https://temporal.io/)
## Features
### Calculator Operations
- **Expression Evaluation**: Safely evaluate mathematical expressions using AST parsing
- **Basic Arithmetic**: Add, subtract, multiply, divide operations
- **Advanced Operations**: Power calculations, list summation
- **Built-in Functions**: Support for abs, round, max, min, sum
### MCP Integration
- **Automatic Tool Discovery**: All calculator operations exposed as MCP tools
- **Type-Safe**: Full Pydantic model validation
- **Error Handling**: Comprehensive error handling with meaningful messages
### Temporal Benefits
- **Reliability**: Operations backed by Temporal's fault-tolerant execution
- **Durability**: Long-running calculations survive process restarts
- **Observability**: Full visibility through Temporal UI with workflow and activity executions
- **Scalability**: Distribute across multiple workers
- **Monitoring**: Each calculator operation creates visible workflow executions for easy debugging
## Quick Start
### Prerequisites
- Python 3.13+
- Running Temporal server (local dev server or Temporal Cloud)
- [Temporal CLI](https://docs.temporal.io/cli) installed
### Installation
1. **Clone and set up the project:**
```bash
cd nexus-mcp-calculator
uv sync --dev
```
2. **Set up Temporal infrastructure:**
```bash
./scripts/setup_temporal.sh
```
This creates:
- Handler namespace (`my-handler-namespace`) - where the calculator worker runs
- Caller namespace (`my-caller-namespace`) - where the MCP gateway runs
- Nexus endpoint (`mcp-gateway`) - bridges the two namespaces
### Running the Application
The application consists of two main components that need to run simultaneously:
#### 1. Start the Calculator Worker
```bash
uv run python -m nexus_mcp_calculator.worker
```
This starts the Temporal worker that handles the actual calculator operations.
#### 2. Start the MCP Server
```bash
uv run python -m nexus_mcp_calculator.mcp_server
```
This starts the MCP server that bridges MCP clients to the Temporal worker.
## Usage Examples
### Using from a Temporal Workflow
The calculator can be called from within Temporal workflows:
```bash
uv run python -m nexus_mcp_calculator.demo_workflow
```
This demonstrates:
- Tool discovery through MCP
- Calling calculator operations from workflows
- Handling results and errors
- Integration with Temporal's execution model
### Using with MCP Clients
#### Claude Desktop Configuration
Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"nexus-calculator": {
"command": "uv",
"args": ["run", "python", "-m", "nexus_mcp_calculator.mcp_server"],
"cwd": "/path/to/nexus-mcp-calculator"
}
}
}
```
#### Goose Extension Configuration
Add this command: `uv run --directory /path/to/sample python -m nexus_mcp_calculator.mcp_server`
#### Available Tools
Once connected, you'll have access to these calculator tools:
- `calculate` - Evaluate mathematical expressions
- `add` - Add two numbers
- `subtract` - Subtract two numbers
- `multiply` - Multiply two numbers
- `divide` - Divide two numbers
- `power` - Raise to a power
- `sum_list` - Sum a list of numbers
### Example MCP Calls
```python
# Expression evaluation
calculate({"expression": "2 + 3 * 4"}) # Returns: {"result": 14.0, "expression": "2 + 3 * 4"}
# Basic arithmetic
add({"a": 10, "b": 5}) # Returns: {"result": 15.0, "operation": "10.0 + 5.0 = 15.0"}
# List operations
sum_list({"numbers": [1, 2, 3, 4, 5]}) # Returns: {"result": 15.0, "operation": "sum([1, 2, 3, 4, 5]) = 15.0"}
# Advanced expressions
calculate({"expression": "abs(-10) + round(3.14159, 2)"}) # Returns: {"result": 13.14, ...}
```
## Architecture
```
┌─────────────────┐ MCP Protocol ┌──────────────────┐
│ MCP Client │◄──────────────────►│ MCP Server │
│ (Claude Desktop)│ │ (mcp_server.py) │
└─────────────────┘ └──────────────────┘
│
│ Temporal Client
▼
┌─────────────────────────────────────────────────────────────────┐
│ Temporal Cluster │
│ │
│ ┌─────────────────┐ Nexus RPC ┌────────────────────────────┐│
│ │ Caller Namespace│◄─────────────►│ Handler Namespace ││
│ │ │ │ ││
│ │ ┌─────────────┐│ │ ┌──────────────────────┐ ││
│ │ │ MCP Gateway ││ │ │ Calculator Worker │ ││
│ │ │ Workflow ││ │ │ │ ││
│ │ └─────────────┘│ │ │ ┌──────────────────┐ │ ││
│ └─────────────────┘ │ │ │ Service Handlers │ │ ││
│ │ │ └──────────────────┘ │ ││
│ │ └──────────────────────┘ ││
│ └────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
```
## Configuration
### Command Line Options
#### Worker (`worker.py`)
```bash
uv run python -m nexus_mcp_calculator.worker --help
Options:
--temporal-host TEXT Temporal server host (default: localhost:7233)
--namespace TEXT Temporal namespace (default: my-handler-namespace)
--task-queue TEXT Task queue name (default: mcp)
```
#### MCP Server (`mcp_server.py`)
```bash
uv run python -m nexus_mcp_calculator.mcp_server --help
Options:
--temporal-host TEXT Temporal server host (default: localhost:7233)
--namespace TEXT Temporal namespace (default: my-caller-namespace)
--endpoint TEXT Nexus endpoint name (default: mcp-gateway)
```
#### Demo Workflow (`demo_workflow.py`)
```bash
uv run python -m nexus_mcp_calculator.demo_workflow --help
Options:
--temporal-host TEXT Temporal server host (default: localhost:7233)
--namespace TEXT Temporal namespace (default: my-caller-namespace)
--task-queue TEXT Task queue name (default: calculator-demo)
--endpoint TEXT Nexus endpoint name (default: mcp-gateway)
```
### Setup Scripts
#### Setup Infrastructure
```bash
./scripts/setup_temporal.sh --help
Options:
--handler-namespace NAME Handler namespace (default: my-handler-namespace)
--caller-namespace NAME Caller namespace (default: my-caller-namespace)
--endpoint-name NAME Nexus endpoint name (default: mcp-gateway)
--task-queue NAME Task queue name (default: mcp)
```
#### Cleanup Infrastructure
```bash
./scripts/cleanup_temporal.sh --help
Options:
--handler-namespace NAME Handler namespace (default: my-handler-namespace)
--caller-namespace NAME Caller namespace (default: my-caller-namespace)
--endpoint-name NAME Nexus endpoint name (default: mcp-gateway)
```
## Development
### Project Structure
```
nexus-mcp-calculator/
├── nexus_mcp_calculator/ # Main package
│ ├── __init__.py # Package initialization
│ ├── service.py # Nexus service definitions
│ ├── service_handler.py # MCP-enabled service handlers (workflow operations)
│ ├── workflows.py # Temporal workflows for calculator operations
│ ├── activities.py # Temporal activities for actual calculations
│ ├── worker.py # Temporal worker (registers workflows & activities)
│ ├── mcp_server.py # MCP server/gateway
│ └── demo_workflow.py # Demonstration workflow
├── scripts/ # Setup and utility scripts
│ ├── setup_temporal.sh # Infrastructure setup
│ └── cleanup_temporal.sh # Infrastructure cleanup
├── pyproject.toml # Project configuration
├── README.md # This file
└── CLAUDE.md # Development documentation
```
### Code Quality
```bash
# Format code
uv run ruff format
# Lint code
uv run ruff check
# Type checking
uv run mypy nexus_mcp_calculator/
# Run tests
uv run pytest
```
### Adding New Operations
1. **Define request/response models in `service.py`:**
```python
class NewOperationRequest(BaseModel):
value: float = Field(..., description="Input value")
class NewOperationResponse(BaseModel):
result: float = Field(..., description="Result")
```
2. **Add operation to service:**
```python
@nexusrpc.service(name="Calculator")
class CalculatorService:
new_operation: nexusrpc.Operation[NewOperationRequest, NewOperationResponse]
```
3. **Create activity in `activities.py`:**
```python
@activity.defn
async def new_operation_activity(input: NewOperationRequest) -> NewOperationResponse:
activity.logger.info(f"New operation activity called with: {input.value}")
result = process_value(input.value)
return NewOperationResponse(result=result)
```
4. **Create workflow in `workflows.py`:**
```python
@workflow.defn
class NewOperationWorkflow:
@workflow.run
async def run(self, input: NewOperationRequest) -> NewOperationResponse:
return await workflow.execute_activity(
new_operation_activity,
input,
start_to_close_timeout=workflow.timedelta(seconds=30),
)
```
5. **Add workflow_run_operation to `service_handler.py`:**
```python
@nexus.workflow_run_operation
async def new_operation(
self,
ctx: nexus.WorkflowRunOperationContext,
input: NewOperationRequest
) -> nexus.WorkflowHandle[NewOperationResponse]:
return await ctx.start_workflow(
NewOperationWorkflow.run,
input,
id=f"new_operation-{uuid.uuid4()}",
)
```
6. **Register in `worker.py`:**
```python
# Add to workflows list
NewOperationWorkflow,
# Add to activities list
new_operation_activity,
```
The operation will be automatically discovered and exposed as an MCP tool, with full workflow and activity visibility in the Temporal UI.
### Excluding Operations from MCP
Use the `@exclude` decorator to prevent operations from being exposed as MCP tools:
```python
from nexusmcp import exclude
@exclude
@nexusrpc.handler.sync_operation
async def internal_operation(self, _ctx, input):
# This won't be available to MCP clients
pass
```
## Troubleshooting
### Common Issues
1. **"Namespace not found" errors**
- Ensure you've run `./scripts/setup_temporal.sh`
- Check that your Temporal server is running
2. **"Endpoint not found" errors**
- Verify the Nexus endpoint was created successfully
- Check endpoint name matches between worker and MCP server
3. **MCP connection issues**
- Ensure both worker and MCP server are running
- Check that the worker has started and registered handlers
- Verify namespace and endpoint configuration
4. **Tool discovery fails**
- Check worker logs for service registration
- Verify MCP server can connect to Temporal
- Ensure endpoint is routing to correct task queue
### Viewing Temporal UI
Access the Temporal Web UI at http://localhost:8233 to:
- **Monitor workflow executions**: Every calculator operation now creates a visible workflow
- **Track activity executions**: See the actual calculation activities within workflows
- **View task queues and workers**: Monitor worker health and task processing
- **Debug failed operations**: Full stack traces and execution history
- **Inspect Nexus endpoints**: Monitor cross-namespace communication
- **Observe operation patterns**: See timing, success rates, and performance metrics
### Logging
Enable debug logging:
```python
import logging
logging.basicConfig(level=logging.DEBUG)
```
## Security Considerations
### Expression Evaluation
The calculator uses AST parsing for safe expression evaluation:
- Only allows mathematical operators and functions
- Prevents arbitrary code execution
- Validates input before processing
### Allowed Operations
- Arithmetic: `+`, `-`, `*`, `/`, `%`, `**`
- Functions: `abs`, `round`, `max`, `min`, `sum`
- Constants: Numbers only
### Blocked Operations
- Variable assignment
- Function definitions
- Import statements
- File system access
- Network operations
## License
This project is licensed under the MIT License. See the LICENSE file for details.