Skip to content

v3.1.0: remote MCP, tool improvements, response store, release pipeline#3

Merged
willwebster5 merged 4 commits intomasterfrom
feat/v3.1.0
Apr 10, 2026
Merged

v3.1.0: remote MCP, tool improvements, response store, release pipeline#3
willwebster5 merged 4 commits intomasterfrom
feat/v3.1.0

Conversation

@willwebster5
Copy link
Copy Markdown
Owner

Summary

Four feature batches landing as v3.1.0. Implemented in dependency order: release pipeline → remote MCP foundation → tool improvements → response store. All 95 existing tests pass; lint and format clean.

1. Release Pipeline (43abb46)

Tag-driven release.yml workflow. Push a v* tag to lint, test, validate that SERVER_VERSION matches the tag, build & push a Docker image to GHCR (versioned + latest), and publish a GitHub Release with auto-generated notes.

2. Remote MCP Server (8b5e24d) — SERVER_VERSION → 3.1.0

Per-client HTTP authentication so the server can run remotely and credential-less. stdio mode is unchanged.

  • FalconClient.deferred() for credential-less startup; auth_object guarded against deferred misuse
  • BaseModule._get_auth() / _service() resolve auth via a _session_client ContextVar (per-request isolation)
  • New common/session_auth.py middleware extracts X-Falcon-Client-Id/Secret/Base-Url, OAuths, caches sessions (25 min TTL, LRU max 100), sets the ContextVar
  • New common/health.py ASGI wrapper for /health (no auth)
  • auth_middleware now also gates WebSocket scopes
  • 10 modules refactored from __init__ service creation to per-call self._service(ServiceClass)
  • Dockerfile (python:3.12-slim, non-root, port 8000) + .dockerignore

3. MCP Tool Improvements (7f0247f)

Cuts SOC agent friction observed during live triage:

  • get_alerts: offset (native API pagination), q (server-side free-text search), pattern_name reimplemented as server-side FQL wildcard, max_results cap raised to 1000, summary_mode for compact rendering, next_offset in response
  • alert_analysis: summary_mode with per-event-type field projection (NGSIEM/endpoint/cloud)
  • ngsiem_query: fields parameter for server-side | select([...]) projection (skipped if query already has select()/table())
  • FQL guides: full operator table (~, !~, *, ~*, ~*!), now timestamp keyword, corrected name field docs

4. Response Store (576f006)

Keeps the SOC triage agent inside the MCP tool loop when responses exceed the 20KB threshold. Structured tool output is stored as Python dicts before text formatting and queried via a new MCP tool — no Bash/grep over temp files.

  • Root-level ResponseStore singleton (FIFO ring buffer, 50 entries)
  • get_stored_response tool: query by ref_id with record_index, record_key (scans common ID fields), fields (dot-path projection), search, max_results
  • list_stored_responses tool: summary of all stored references
  • format_text_response() opt-in via new structured_data and metadata kwargs; tools that don't pass them keep the existing temp-file fallback
  • alert_analysis, get_alerts, and ngsiem_query always store structured data (works on small responses too, not just truncation)

Test plan

  • All 95 existing tests pass after refactor (pytest tests/)
  • ruff check . clean
  • ruff format --check . clean
  • Manual: stdio mode regression — start server, list tools, run get_alerts/alert_analysis/ngsiem_query
  • Manual: HTTP mode — start streamable-http, hit /health, verify 401 without X-Falcon-Client-* headers, verify success with valid headers
  • Manual: pagination — get_alerts(max_results=50, offset=0) then offset=50, verify next_offset and total_available consistency
  • Manual: response store — trigger a truncated alert_analysis, query the returned ref_id via get_stored_response
  • Manual: tag-driven release — push a test v3.1.0-rc1 tag in a fork to verify the release workflow end-to-end before cutting v3.1.0

Out of scope

  • Rate limiting / concurrency limits across HTTP clients
  • Bearer token auth (clients pre-authenticate, pass short-lived token)
  • TTL eviction or disk persistence for ResponseStore
  • ECR push from the release workflow

🤖 Generated with Claude Code

willwebster5 and others added 4 commits April 10, 2026 09:55
Push a v* tag to trigger lint/test/release pipeline. Validates
SERVER_VERSION in client.py matches the tag, builds and pushes a
Docker image to GHCR (versioned + latest), and creates a GitHub
Release with auto-generated notes from commits since the last tag.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ackaging

Add per-client authentication for HTTP transports so the MCP server
can run remotely and credential-less. stdio mode is unchanged.

Foundation:
- FalconClient.deferred() — credential-less instance for HTTP mode
- auth_object property guarded against deferred misuse
- BaseModule._get_auth() / _service() — ContextVar-based per-request
  auth resolution with FalconPy services created on demand
- _session_client ContextVar (canonical: modules/base.py)
- SERVER_VERSION → 3.1.0

HTTP middleware stack (innermost → outermost):
- session_auth_middleware: extracts X-Falcon-Client-* headers,
  authenticates via OAuth, caches sessions (25-min TTL, LRU max 100),
  sets ContextVar per request, returns 401 on missing/bad creds
- auth_middleware: now also gates WebSocket scopes
- with_health_check: outermost /health endpoint, no auth required

Module refactor (8 of 10): hosts, response, spotlight, correlation,
case_management, cloud_registration, cloud_security, cao_hunting now
construct FalconPy services per-call via self._service(ServiceClass)
instead of __init__. Supports the deferred FalconClient and per-request
auth isolation. Existing test fixtures updated to mock _service.

Server startup:
- stdio: resolves credentials and authenticates eagerly (unchanged)
- HTTP: deferred FalconClient, per-client auth via headers

Container:
- Dockerfile (python:3.12-slim, non-root mcp user, port 8000)
- .dockerignore for clean build context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… docs

Reduce SOC agent friction observed during live triage by adding
per-tool parameters that shrink response payloads server-side and
enable bulk paging through large alert queues.

get_alerts:
- offset (int, default 0): native pagination via query_alerts_v2
- q (str): free-text search across all alert metadata, server-side
- pattern_name: switched from client-side substring filter to
  server-side FQL wildcard (name:~*'*...*'). Composes with offset,
  removes the 4x over-fetch hack
- max_results cap raised from 200 → 1000
- summary_mode (bool): compact key-fields rendering only
- next_offset added to response for caller-side pagination

alert_analysis:
- summary_mode (bool): compact alert metadata + top 5 events with
  per-event-type field projection (NGSIEM/endpoint/cloud)

ngsiem_query:
- fields (str, comma-separated): server-side projection via
  appended `| select([...])`, skipped if query already has
  select() or table()
- field_projection echoed in response

FQL guides:
- ALERT_FQL: full operator table (~, !~, *, ~*, ~*!), `now`
  timestamp keyword, name field correctly documented as filterable
- HOST_FQL: wildcard operators section
- Removes the now-incorrect "name is not a valid FQL field" note

Module refactor: alerts.py and ngsiem.py both moved to the
self._service() pattern as part of the v3.1.0 remote-MCP work
shipped in the previous commit; bundled here because the file
diffs interleave with the new feature parameters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep the SOC triage agent inside the MCP tool loop when responses
exceed the 20KB threshold. Structured tool output is now stored as
Python dicts before text formatting, queryable via a new MCP tool
instead of falling out to Bash/grep over a temp file.

ResponseStore (root-level, peer to utils.py):
- Singleton classmethod store with FIFO ring buffer (50 entries)
- StoredResponse dataclass: ref_id, tool_name, timestamp, data,
  metadata, record_count
- record_count derived generically from top-level list values
- _reset() for test isolation

modules/response_store.py — two new MCP tools:
- get_stored_response: query by ref_id with record_index, record_key,
  fields (dot-path projection), search (case-insensitive), max_results
- list_stored_responses: summary of all stored references
- record_key lookup scans common ID fields (composite_id, @id,
  detection_id, user.name, ComputerName, source.ip, ...)

utils.format_text_response now accepts optional structured_data and
metadata kwargs:
- When provided, the raw dict is stored and a ref_id is included in
  responses (footer for inline, full guidance for truncated)
- Truncation notice surfaces a context line from metadata
- Tools that don't pass structured_data keep the existing temp-file
  fallback unchanged

Opt-in tools wired in the previous commit (alerts.alert_analysis,
alerts.get_alerts, ngsiem.ngsiem_query) — they always store
structured data so field-level extraction works on small responses
too, not just on truncation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@willwebster5 willwebster5 merged commit 8d866b2 into master Apr 10, 2026
3 checks passed
willwebster5 added a commit that referenced this pull request Apr 22, 2026
Read-only module mapping 1:1 to falconpy's ThreatGraph service
collection (5 tools) plus one lazily-cached edge-types resource.

Thin mapping preferred over composition; spec open question #1
(process-tree helper) deferred to v1.1 pending real agent traces.
Spec open question #3 (edge-type drift) resolved via live cache.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
willwebster5 added a commit that referenced this pull request Apr 22, 2026
Read-only module mapping 1:1 to falconpy's ThreatGraph service
collection (5 tools) plus one lazily-cached edge-types resource.

Thin mapping preferred over composition; spec open question #1
(process-tree helper) deferred to v1.1 pending real agent traces.
Spec open question #3 (edge-type drift) resolved via live cache.

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.

1 participant