Skip to content

Enhance SeqSearch with date range, signals and pagination#3

Open
matt-richardson wants to merge 9 commits into
willibrandon:mainfrom
matt-richardson:improve-seq-search
Open

Enhance SeqSearch with date range, signals and pagination#3
matt-richardson wants to merge 9 commits into
willibrandon:mainfrom
matt-richardson:improve-seq-search

Conversation

@matt-richardson
Copy link
Copy Markdown
Contributor

Add enhancements to SeqSearch tool:

New parameters:

  • signalId: Filter by saved signal/search
  • fromDateUtc/toDateUtc: Date range filtering (more efficient than @timestamp in filter)
  • afterId: Pagination support for fetching more than 1000 events
  • timeoutSeconds: Configurable timeout (1-300 seconds)

Improvements:

  • Filter normalization: Accepts "*" as alias for "all events" (empty string)
  • Catches SeqApiException for filter syntax errors and provides guidance
  • Date validation
  • Signal validation

Includes tests:

  • Date range filtering
  • Timeout handling
  • Invalid input validation (dates, signals, filter syntax)
  • Pagination support
  • Filter normalization

Add enhancements to SeqSearch tool:

New parameters:
- signalId: Filter by saved signal/search
- fromDateUtc/toDateUtc: Date range filtering (more efficient than @timestamp in filter)
- afterId: Pagination support for fetching more than 1000 events
- timeoutSeconds: Configurable timeout (1-300 seconds)

Improvements:
- Filter normalization: Accepts "*" as alias for "all events" (empty string)
- Catches SeqApiException for filter syntax errors  and provides guidance
- Date validation
- Signal validation

Includes tests:
- Date range filtering
- Timeout handling
- Invalid input validation (dates, signals, filter syntax)
- Pagination support
- Filter normalization
@matt-richardson
Copy link
Copy Markdown
Contributor Author

@willibrandon - I've also merged main into this PR to

Copy link
Copy Markdown
Owner

@willibrandon willibrandon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caught up on main, the feature additions look solid. A few notes on the test side, pick what you want to address.

});

Assert.NotNull(result);
Assert.NotNull(result.Content);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This passes as long as the call returns something non-null, even if the date range was ignored entirely. Most of the other new tests have the same shape (WithTimeout_ReturnsBeforeTimeout, WithVeryShortTimeout_HandlesGracefully, WithAfterId_ReturnsPaginatedResults). To actually exercise the feature, the tests need to seed events with known timestamps or IDs and then assert what came back. The pagination test in particular passes a hardcoded event-test-id that doesn't exist, so it isn't testing pagination, it's testing that an unknown afterId doesn't blow up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Rewrote 3 of them to seed events and assert properly. Deleted both timeout tests - making them meaningful would be pretty invasive


Assert.NotNull(result);
Assert.NotNull(result.Content);
if (result.IsError)
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test passes either way. If the asterisk normalizes to empty and returns events, success. If it doesn't normalize and Seq returns a syntax error, also success (the inner Assert just checks the error message matches one of two strings). The point of normalization is that * should return events, so the test should seed an event first and then assert IsError is false with the seeded event in the content.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - seeds an event, asserts IsError=false and marker in content.

Comment thread src/SeqMcpServer/Mcp/SeqTools.cs Outdated
[McpServerTool, Description("Search Seq events with filters, date ranges, signals, pagination, and optional timeout. For date filtering, use fromDateUtc/toDateUtc parameters instead of @Timestamp in the filter expression. For pagination, use afterId with the last event ID from previous results.")]
public static async Task<List<EventEntity>> SeqSearch(
SeqConnectionFactory fac,
[Required] string filter,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick question on [Required] combined with the README pointing users at "" for all events. Whether [Required] rejects empty string depends on how MCP wires DataAnnotations validation. If it rejects, the documented all-events path is unreachable. If it accepts, the attribute is misleading. Either confirming what actually happens or relaxing the attribute makes the rule explicit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed - dropped the [Required], defaulted filter to "", updated docs.

matt-richardson and others added 7 commits May 23, 2026 07:14
The README documents passing "" to return all events, but the
[Required] attribute on a non-nullable string is ambiguous: depending
on whether the MCP runtime runs DataAnnotations validation, the
documented all-events path is either unreachable or the attribute is
misleading. Removing it and defaulting filter to "" makes the rule
explicit and keeps the documented behavior intact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous test only asserted Content was non-null; it would have
passed even if fromDateUtc/toDateUtc were ignored entirely. Now it
seeds a uniquely-tagged event, queries a bracketing range and asserts
the event comes back, then queries a future range and asserts nothing
matches the marker.

Adds shared JSON helpers (GetInnerText, GetEventCount, GetEventIds)
on the test base for inspecting tool call results.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous test passed whether or not "*" was normalized: the
if (IsError) branch accepted both "Syntax error" and "Invalid filter"
substrings, so a Seq syntax error from a non-normalized "*" would have
counted as success. Now the test seeds a uniquely-tagged event, calls
SeqSearch with filter="*", and asserts no error plus the seeded
marker appearing in the result content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous test passed a hardcoded "event-test-id" that did not exist
in Seq, so it only verified that an unknown afterId did not throw.
Now the test seeds 10 uniquely-tagged events, fetches the first page,
takes the last (oldest) event ID as the cursor, fetches the next page,
and asserts the second page is non-empty and shares no IDs with the
first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The strict pageSize equality on firstIds.Count would flake if Seq's
indexing ever lagged behind the seed loop; the inline note tells a
future debugger what to soften it to. The cursor-direction note warns
that switching to .First() would silently re-include page 1 events,
since afterId is exclusive and Seq returns newest-first.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both tests only asserted that result and result.Content were non-null
when timeoutSeconds was set, which passes whether or not the timeout
logic actually works. Meaningfully testing the CTS + cancellation
plumbing requires a behaviour-injection seam (TimeProvider plus a
mockable event source), which is more invasive than the five-line
feature warrants. The non-timeout SeqSearch tests already exercise
the same code path with timeoutSeconds = null, so deletion doesn't
open a real coverage gap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants