Skip to content

Commit a174214

Browse files
authored
Merge pull request #24 from alex-feel/alex-feel-dev
Support date filtering in search_context and semantic_search_context
2 parents f1f7064 + 5949a69 commit a174214

12 files changed

Lines changed: 1966 additions & 201 deletions

CLAUDE.md

Lines changed: 32 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -206,60 +206,6 @@ The codebase uses a comprehensive multi-layered testing approach:
206206
- Use `async_db_initialized` for async database operations
207207
- All fixtures create SQLite temporary databases (no PostgreSQL fixtures in test suite)
208208

209-
#### Test Files and Their Purpose:
210-
211-
**Core Tests:**
212-
- **`test_models.py`**: Validates Pydantic data models, field validation, and type conversions
213-
- **`test_database.py`**: Tests SQLite operations, constraints, indexes, and cascade deletes
214-
- **`test_server.py`**: Tests MCP tool handlers with mocked database connections
215-
- **`test_integration.py`**: End-to-end workflows with real database operations
216-
- **`test_real_server.py`**: Tests actual running server via FastMCP client connection
217-
218-
**Parameter and JSON Handling:**
219-
- **`test_parameter_handling.py`**: Validates tool parameter parsing and type coercion
220-
- **`test_json_parameter_handling.py`**: Tests JSON parameter serialization/deserialization
221-
- **`test_json_string_handling.py`**: Validates JSON string handling in tool responses
222-
- **`test_encoding.py`**: Tests character encoding edge cases
223-
224-
**Metadata and Filtering:**
225-
- **`test_metadata_filtering.py`**: Tests advanced metadata filtering with operators
226-
- **`test_metadata_error_handling.py`**: Tests metadata filtering error cases
227-
- **`test_nested_metadata_storage.py`**: Tests nested JSON metadata storage and retrieval
228-
229-
**Feature-Specific Tests:**
230-
- **`test_update_context.py`**: Tests update_context tool functionality
231-
- **`test_deduplication.py`**: Tests context deduplication logic
232-
- **`test_semantic_search_filters.py`**: Tests semantic search with filtering options
233-
- **`test_embedding_service.py`**: Tests embedding generation service
234-
235-
**Repository Tests:**
236-
- **`test_context_repository_extended.py`**: Extended context repository tests
237-
- **`test_image_repository.py`**: Image attachment repository tests
238-
- **`test_statistics_repository.py`**: Statistics repository tests
239-
- **`test_tag_repository.py`**: Tag repository tests
240-
- **`test_embedding_repository.py`**: Embedding repository tests
241-
242-
**Server Tests:**
243-
- **`test_server_edge_cases.py`**: Edge case handling in server tools
244-
- **`test_server_error_paths.py`**: Error path coverage for server
245-
- **`test_server_tools.py`**: Tool implementation tests
246-
- **`test_server_utilities.py`**: Server utility function tests
247-
- **`test_server_json_schema.py`**: JSON schema validation tests
248-
249-
**Error and Validation:**
250-
- **`test_error_formats.py`**: Tests error response formatting consistency
251-
- **`test_error_handling_json.py`**: Tests JSON error handling edge cases
252-
- **`test_json_error_consistency.py`**: Tests consistent error messages across JSON operations
253-
- **`test_validation_errors.py`**: Tests input validation error messages
254-
- **`test_validation_consistency.py`**: Tests validation behavior consistency
255-
256-
**Backend and Infrastructure:**
257-
- **`test_postgresql_backend.py`**: Tests PostgreSQL-specific backend functionality
258-
- **`test_backend_factory.py`**: Tests backend factory and selection logic
259-
- **`test_query_builder_postgresql.py`**: Tests PostgreSQL query builder
260-
- **`test_schema_sync.py`**: Validates SQLite and PostgreSQL schemas are in sync
261-
- **`test_resource_warnings.py`**: Validates proper resource cleanup
262-
263209
#### Test Fixtures (`conftest.py`):
264210
- **`test_settings`**: Creates test-specific AppSettings with temp database
265211
- **`temp_db_path`**: Provides temporary database file path
@@ -571,148 +517,13 @@ All initialization is idempotent - safe to run multiple times.
571517

572518
#### Supabase
573519

574-
**Compatibility:** Supabase is fully compatible via direct PostgreSQL connection - no special code or configuration needed.
575-
576-
Supabase provides TWO connection methods for persistent database connections. Choose based on your network environment.
577-
578-
##### Connection Method 1: Direct Connection (Recommended - Requires IPv6)
579-
580-
**Best for:** VMs, long-running containers, systems with IPv6 support
581-
**Performance:** Lowest latency (~15-20ms)
582-
**Network:** Requires IPv6 (unless you have paid dedicated IPv4 add-on)
583-
584-
**Connection String Format:**
585-
```
586-
postgresql://postgres:[YOUR-PASSWORD]@db.[PROJECT-REF].supabase.co:5432/postgres
587-
```
588-
589-
**How to Get Connection String:**
590-
1. Navigate to: Database → Settings (left sidebar) in Supabase Dashboard
591-
2. Find: "Connect to your project" section
592-
3. Select: "Connection String" tab → "Direct connection" method
593-
4. Copy connection string (you'll need to replace `[YOUR-PASSWORD]` with your actual password)
594-
595-
**Setup Steps:**
596-
```bash
597-
# 1. Get your database password
598-
# IMPORTANT: Database password is NEVER displayed in Supabase Dashboard (security)
599-
# Either: Use the password you set when creating your project
600-
# Or: Click "Reset database password" button (below connection string) to set a new one
601-
# Note: [YOUR-PASSWORD] is a placeholder - replace with your actual password
602-
603-
# 2. Configure environment with Direct Connection
604-
export STORAGE_BACKEND=postgresql
605-
export POSTGRESQL_CONNECTION_STRING=postgresql://postgres:your-actual-password@db.[PROJECT-REF].supabase.co:5432/postgres
606-
export ENABLE_SEMANTIC_SEARCH=true # Optional: only if you need semantic search
607-
608-
# 3. Enable pgvector extension (required for semantic search)
609-
# Option A: Via Supabase Dashboard (easiest)
610-
# - Navigate to: Database → Extensions (left sidebar)
611-
# - Search for "vector"
612-
# - Toggle the "vector" extension to enable (turns green)
613-
# Option B: Via SQL Editor
614-
# - Navigate to: SQL Editor in Supabase Dashboard
615-
# - Run: CREATE EXTENSION IF NOT EXISTS vector;
616-
617-
# 4. Run server (schema auto-initializes)
618-
uv run mcp-context-server
619-
```
620-
621-
##### Connection Method 2: Session Pooler (IPv4 Compatible)
622-
623-
**Best for:** Systems without IPv6 support (Windows, corporate networks with IPv4-only)
624-
**Performance:** Good (~20-30ms, +5-10ms vs Direct Connection via Supavisor proxy)
625-
**Network:** Works with both IPv4 and IPv6 (universal compatibility)
626-
627-
**Connection String Format:**
628-
```
629-
postgresql://postgres.[PROJECT-REF]:[YOUR-PASSWORD]@aws-0-[REGION].pooler.supabase.com:5432/postgres
630-
```
631-
632-
**How to Get Connection String:**
633-
1. Navigate to: Database → Settings (left sidebar) in Supabase Dashboard
634-
2. Find: "Connect to your project" section
635-
3. Select: "Connection String" tab → "Session pooler" method
636-
4. Copy connection string (you'll need to replace `[YOUR-PASSWORD]` with your actual password)
520+
Supabase is fully compatible via PostgreSQL connection - see README.md for detailed setup instructions.
637521

638-
**Important Differences from Direct Connection:**
639-
- **Different hostname**: `aws-0-[REGION].pooler.supabase.com` instead of `db.[PROJECT-REF].supabase.co`
640-
- **Different username format**: `postgres.[PROJECT-REF]` instead of `postgres`
641-
- **Same port**: 5432 (NOT 6543 - Transaction Pooler is different and not for persistent connections)
642-
- **Connection routed through Supavisor**: Adds IPv4 support but slightly higher latency
643-
644-
**Setup Steps:**
645-
```bash
646-
# 1. Get your database password (same as Direct Connection method)
647-
# IMPORTANT: Database password is NEVER displayed in Supabase Dashboard (security)
648-
# Either: Use the password you set when creating your project
649-
# Or: Click "Reset database password" button (below connection string) to set a new one
650-
651-
# 2. Configure environment with Session Pooler connection
652-
export STORAGE_BACKEND=postgresql
653-
export POSTGRESQL_CONNECTION_STRING=postgresql://postgres.[PROJECT-REF]:your-actual-password@aws-0-[REGION].pooler.supabase.com:5432/postgres
654-
export ENABLE_SEMANTIC_SEARCH=true # Optional: only if you need semantic search
655-
656-
# 3. Enable pgvector extension (same as Direct Connection)
657-
# Option A: Via Supabase Dashboard (easiest)
658-
# - Navigate to: Database → Extensions (left sidebar)
659-
# - Search for "vector"
660-
# - Toggle the "vector" extension to enable (turns green)
661-
# Option B: Via SQL Editor
662-
# - Navigate to: SQL Editor in Supabase Dashboard
663-
# - Run: CREATE EXTENSION IF NOT EXISTS vector;
664-
665-
# 4. Run server (schema auto-initializes)
666-
uv run mcp-context-server
667-
```
668-
669-
##### Decision Matrix: Which Connection Method to Use?
670-
671-
| Factor | Direct Connection | Session Pooler |
672-
|--------|------------------|----------------|
673-
| **IPv6 Required?** | Yes (unless paid IPv4 add-on) | No (works with IPv4) |
674-
| **Performance** | Lowest latency (~15-20ms) | Good (+5-10ms via proxy) |
675-
| **Hostname** | `db.[PROJECT-REF].supabase.co` | `aws-0-[REGION].pooler.supabase.com` |
676-
| **Username** | `postgres` | `postgres.[PROJECT-REF]` |
677-
| **Port** | 5432 | 5432 |
678-
| **Best For** | VMs with IPv6, production deployments | Windows, corporate networks, IPv4-only systems |
679-
| **Universal Compatibility** | No (IPv6 only) | Yes (IPv4 and IPv6) |
680-
681-
##### Troubleshooting: "getaddrinfo failed" Error
682-
683-
If you see errors like:
684-
```
685-
OSError: [Errno -3] Temporary failure in name resolution
686-
gaierror: [Errno -3] getaddrinfo failed
687-
```
688-
689-
**Cause:** Your system doesn't support IPv6 and you're using Direct Connection method.
690-
691-
**Solution:** Switch to Session Pooler method (Connection Method 2 above):
692-
1. Get Session Pooler connection string from Supabase Dashboard (Connection String → Session pooler)
693-
2. Update your connection string to use `aws-0-[REGION].pooler.supabase.com` hostname
694-
3. Update username format to `postgres.[PROJECT-REF]`
695-
4. Keep port as 5432
696-
5. Restart server
697-
698-
**Important Connection Details:**
699-
- **Database password is never shown** - you must use your original password or reset it
700-
- **NOT API keys** - API keys (including service_role) are for REST/GraphQL APIs, not direct database connection
701-
- **Port 5432** for both connection methods (Transaction Pooler on port 6543 is different - only for serverless functions)
702-
- **pgvector extension** is available on all Supabase projects - must be manually enabled via Dashboard → Extensions (toggle "vector") or SQL (`CREATE EXTENSION IF NOT EXISTS vector;`) before using semantic search
703-
- **All PostgreSQL features** work identically - JSONB, transactions, metadata filtering
704-
705-
**Why No Special Supabase Support:**
706-
- Supabase IS PostgreSQL - creating separate abstraction would violate DRY and YAGNI principles
707-
- Both connection methods use standard PostgreSQL wire protocol - identical to self-hosted PostgreSQL
708-
- Zero maintenance burden - no Supabase-specific code to maintain
709-
- Future-proof - works regardless of Supabase changes
710-
- Clear semantics - users understand they're using PostgreSQL
711-
712-
**Testing with Supabase:**
713-
- Tests use SQLite temporary databases (no Supabase required for testing)
714-
- Production deployment can use Supabase via standard PostgreSQL configuration (either connection method)
715-
- All PostgreSQL backend features tested with SQLite work identically on Supabase
522+
**Quick Reference:**
523+
- Use `STORAGE_BACKEND=postgresql` with `POSTGRESQL_CONNECTION_STRING`
524+
- Two connection methods: Direct (IPv6 required) or Session Pooler (IPv4 compatible)
525+
- Enable pgvector via Dashboard → Extensions for semantic search
526+
- "getaddrinfo failed" error = switch from Direct to Session Pooler
716527

717528
### StorageBackend Protocol
718529

@@ -813,6 +624,32 @@ Both mypy and pyright are configured:
813624
3. **Return types must be serializable dicts/lists** - use TypedDicts from `app/types.py`
814625
4. **Tool decorators require specific imports**: Use `Annotated` and `Field` from `typing` and `pydantic`
815626

627+
### Adding New MCP Tools
628+
629+
When adding a new tool to the server, follow this pattern:
630+
631+
```python
632+
# In app/server.py
633+
@mcp.tool()
634+
async def my_new_tool(
635+
required_param: Annotated[str, Field(description='Description for MCP clients')],
636+
optional_param: Annotated[int | None, Field(description='Optional parameter')] = None,
637+
ctx: Context | None = None, # ALWAYS last, hidden from MCP clients
638+
) -> MyToolResponse: # Use TypedDict from app/types.py
639+
"""Tool docstring shown to MCP clients."""
640+
repos = _ensure_repositories()
641+
# Use repository methods for database operations
642+
result = await repos.context.some_method(required_param)
643+
return {'success': True, 'data': result}
644+
```
645+
646+
**Checklist for new tools:**
647+
1. Add TypedDict response type to `app/types.py`
648+
2. Add repository methods if database access needed
649+
3. Use `Literal["user", "agent"]` for source parameters
650+
4. Add tests in `tests/test_server.py` or dedicated test file
651+
5. Update server.json if tool adds new environment variables
652+
816653
### Repository Pattern Implementation
817654

818655
1. **All database operations go through repositories** - server.py should never contain SQL

CONTRIBUTING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ uv run pytest tests/test_metadata_error_handling.py -v
5555
# Run semantic search tests
5656
uv run pytest tests/test_semantic_search_filters.py -v
5757

58+
# Run date filtering tests
59+
uv run pytest tests/test_date_filtering.py -v
60+
5861
# Run integration tests only
5962
uv run pytest -m integration
6063

README.md

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ A high-performance Model Context Protocol (MCP) server providing persistent mult
1010
- **Multimodal Context Storage**: Store and retrieve both text and images
1111
- **Thread-Based Scoping**: Agents working on the same task share context through thread IDs
1212
- **Flexible Metadata Filtering**: Store custom structured data with any JSON-serializable fields and filter using 15 powerful operators
13+
- **Date Range Filtering**: Filter context entries by creation timestamp using ISO 8601 format
1314
- **Tag-Based Organization**: Efficient context retrieval with normalized, indexed tags
1415
- **Semantic Search**: Optional vector similarity search for meaning-based retrieval
1516
- **Multiple Database Backends**: Choose between SQLite (default, zero-config) or PostgreSQL (high-concurrency, production-grade)
@@ -501,7 +502,7 @@ The metadata field accepts any JSON-serializable structure, making the server ad
501502

502503
#### search_context
503504

504-
Search context entries with powerful filtering including metadata queries.
505+
Search context entries with powerful filtering including metadata queries and date ranges.
505506

506507
**Parameters:**
507508
- `thread_id` (str, optional): Filter by thread
@@ -510,6 +511,8 @@ Search context entries with powerful filtering including metadata queries.
510511
- `content_type` (str, optional): Filter by type ('text' or 'multimodal')
511512
- `metadata` (dict, optional): Simple metadata filters (key=value equality)
512513
- `metadata_filters` (list, optional): Advanced metadata filters with operators
514+
- `start_date` (str, optional): Filter entries created on or after this date (ISO 8601 format)
515+
- `end_date` (str, optional): Filter entries created on or before this date (ISO 8601 format)
513516
- `limit` (int, optional): Maximum results (default: 50, max: 500)
514517
- `offset` (int, optional): Pagination offset
515518
- `include_images` (bool, optional): Include image data in response
@@ -545,6 +548,28 @@ All string operators support `case_sensitive: true/false` option.
545548

546549
For comprehensive documentation on metadata filtering including real-world use cases, operator examples, nested JSON paths, and performance optimization, see the [Metadata Filtering Guide](docs/metadata-filtering.md).
547550

551+
**Date Filtering:**
552+
553+
Filter entries by creation timestamp using ISO 8601 format:
554+
```python
555+
# Find entries from a specific day
556+
search_context(thread_id="project-123", start_date="2025-11-29", end_date="2025-11-29")
557+
558+
# Find entries from a date range
559+
search_context(thread_id="project-123", start_date="2025-11-01", end_date="2025-11-30")
560+
561+
# Find entries with precise timestamp
562+
search_context(thread_id="project-123", start_date="2025-11-29T10:00:00")
563+
```
564+
565+
Supported ISO 8601 formats:
566+
- Date-only: `2025-11-29`
567+
- DateTime: `2025-11-29T10:00:00`
568+
- UTC (Z suffix): `2025-11-29T10:00:00Z`
569+
- Timezone offset: `2025-11-29T10:00:00+02:00`
570+
571+
**Note:** Date-only `end_date` values automatically expand to end-of-day (`T23:59:59.999999`) for intuitive "entire day" behavior. Naive datetime (without timezone) is interpreted as UTC.
572+
548573
**Returns:** List of matching context entries with optional query statistics
549574

550575
#### get_context_by_ids
@@ -649,6 +674,8 @@ Note: This tool is only available when semantic search is enabled via `ENABLE_SE
649674
- `top_k` (int, optional): Number of results to return (1-100) - defaults to 20
650675
- `thread_id` (str, optional): Filter results to specific thread
651676
- `source` (str, optional): Filter by source type ('user' or 'agent')
677+
- `start_date` (str, optional): Filter entries created on or after this date (ISO 8601 format)
678+
- `end_date` (str, optional): Filter entries created on or before this date (ISO 8601 format)
652679

653680
**Returns:** Dictionary with:
654681
- Query string
@@ -660,6 +687,17 @@ Note: This tool is only available when semantic search is enabled via `ENABLE_SE
660687
- Find related work across different threads based on semantic similarity
661688
- Discover contexts with similar meaning but different wording
662689
- Concept-based retrieval without exact keyword matching
690+
- Find similar content within a specific time period using date filters
691+
692+
**Date Filtering Example:**
693+
```python
694+
# Find similar content from the past week
695+
semantic_search_context(
696+
query="authentication implementation",
697+
start_date="2025-11-22",
698+
end_date="2025-11-29"
699+
)
700+
```
663701

664702
For setup instructions, see the [Semantic Search Guide](docs/semantic-search.md).
665703

0 commit comments

Comments
 (0)