Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions falcon_mcp/common/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""
Centralized constants for Falcon MCP.
"""

# Common error codes and their meanings
ERROR_CODE_DESCRIPTIONS = {
403: "Permission denied. The API credentials don't have the required access.",
401: "Authentication failed. The API credentials are invalid or expired.",
404: "Resource not found. The requested resource does not exist.",
429: "Rate limit exceeded. Too many requests in a short period.",
500: "Server error. An unexpected error occurred on the server.",
503: "Service unavailable. The service is temporarily unavailable.",
}


class SearchLimits:
"""Constants for search and pagination limits."""
DEFAULT = 10
DEFAULT_DISCOVER = 100
MAX_DETECTIONS = 9999
MAX_HOSTS = 5000
MAX_INCIDENTS = 500
MAX_CLOUD_SCORE = 2500
MAX_DISCOVER_APPS = 1000
MAX_INTEL = 5000
MAX_SPOTLIGHT = 5000
MAX_IDP_RESULTS = 200
MAX_IDP_RELATIONSHIPS = 3
DEFAULT_IDP_RELATIONSHIPS = 2


class GraphQLDefaults:
"""Default values for GraphQL query parameters."""
FIRST_INCIDENTS = 10
FIRST_ENTITIES_BATCH = 50
DEFAULT_TIMELINE_LIMIT = 50
DEFAULT_RELATIONSHIP_LIMIT = 50


class ServerDefaults:
"""Server-level configuration constants."""
DEFAULT_HOST = "127.0.0.1"
DEFAULT_PORT = 8000
DEFAULT_TRANSPORT = "stdio"
CORE_TOOLS_COUNT = 3


class TransportTypes:
"""Transport protocol identifiers."""
STDIO = "stdio"
SSE = "sse"
STREAMABLE_HTTP = "streamable-http"
11 changes: 1 addition & 10 deletions falcon_mcp/common/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,11 @@
from typing import Any, Dict, Optional

from .api_scopes import get_required_scopes
from .constants import ERROR_CODE_DESCRIPTIONS
from .logging import get_logger

logger = get_logger(__name__)

# Common error codes and their meanings
ERROR_CODE_DESCRIPTIONS = {
403: "Permission denied. The API credentials don't have the required access.",
401: "Authentication failed. The API credentials are invalid or expired.",
404: "Resource not found. The requested resource does not exist.",
429: "Rate limit exceeded. Too many requests in a short period.",
500: "Server error. An unexpected error occurred on the server.",
503: "Service unavailable. The service is temporarily unavailable.",
}


class FalconError(Exception):
"""Base exception for all Falcon MCP server errors."""
Expand Down
9 changes: 5 additions & 4 deletions falcon_mcp/modules/cloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from falcon_mcp.common.logging import get_logger
from falcon_mcp.common.utils import prepare_api_parameters
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.cloud import (
IMAGES_VULNERABILITIES_FQL_DOCUMENTATION,
KUBERNETES_CONTAINERS_FQL_DOCUMENTATION,
Expand Down Expand Up @@ -89,9 +90,9 @@ def search_kubernetes_containers(
examples={"cloud:'AWS'", "cluster_name:'prod'"},
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=9999,
le=SearchLimits.MAX_DETECTIONS,
description="The maximum number of containers to return in this response (default: 10; max: 9999). Use with the offset parameter to manage pagination of results.",
),
offset: int | None = Field(
Expand Down Expand Up @@ -184,9 +185,9 @@ def search_images_vulnerabilities(
examples={"cve_id:*'*2025*'", "cvss_score:>5"},
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=9999,
le=SearchLimits.MAX_DETECTIONS,
description="The maximum number of containers to return in this response (default: 10; max: 9999). Use with the offset parameter to manage pagination of results.",
),
offset: int | None = Field(
Expand Down
5 changes: 3 additions & 2 deletions falcon_mcp/modules/detections.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from falcon_mcp.common.logging import get_logger
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.detections import SEARCH_DETECTIONS_FQL_DOCUMENTATION

logger = get_logger(__name__)
Expand Down Expand Up @@ -66,9 +67,9 @@ def search_detections(
examples={"agent_id:'77d11725xxxxxxxxxxxxxxxxxxxxc48ca19'", "status:'new'"},
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=9999,
le=SearchLimits.MAX_DETECTIONS,
description="The maximum number of detections to return in this response (default: 10; max: 9999). Use with the offset parameter to manage pagination of results.",
),
offset: int | None = Field(
Expand Down
9 changes: 5 additions & 4 deletions falcon_mcp/modules/discover.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from falcon_mcp.common.logging import get_logger
from falcon_mcp.common.utils import prepare_api_parameters
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.discover import (
SEARCH_APPLICATIONS_FQL_DOCUMENTATION,
SEARCH_UNMANAGED_ASSETS_FQL_DOCUMENTATION,
Expand Down Expand Up @@ -96,9 +97,9 @@ def search_applications(
examples={"browser_extension", "host_info", "install_usage"},
),
limit: int = Field(
default=100,
default=SearchLimits.DEFAULT_DISCOVER,
ge=1,
le=1000,
le=SearchLimits.MAX_DISCOVER_APPS,
description="Maximum number of items to return: 1-1000. Default is 100.",
),
sort: str | None = Field(
Expand Down Expand Up @@ -153,9 +154,9 @@ def search_unmanaged_assets(
examples={"platform_name:'Windows'", "criticality:'Critical'"},
),
limit: int = Field(
default=100,
default=SearchLimits.DEFAULT_DISCOVER,
ge=1,
le=5000,
le=SearchLimits.MAX_HOSTS,
description="Maximum number of items to return: 1-5000. Default is 100.",
),
offset: int | None = Field(
Expand Down
5 changes: 3 additions & 2 deletions falcon_mcp/modules/hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from falcon_mcp.common.logging import get_logger
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.hosts import SEARCH_HOSTS_FQL_DOCUMENTATION

logger = get_logger(__name__)
Expand Down Expand Up @@ -66,9 +67,9 @@ def search_hosts(
examples={"platform_name:'Windows'", "hostname:'PC*'"},
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=5000,
le=SearchLimits.MAX_HOSTS,
description="The maximum records to return. [1-5000]",
),
offset: int | None = Field(
Expand Down
15 changes: 8 additions & 7 deletions falcon_mcp/modules/idp.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from falcon_mcp.common.logging import get_logger
from falcon_mcp.common.utils import sanitize_input
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import GraphQLDefaults, SearchLimits

logger = get_logger(__name__)

Expand Down Expand Up @@ -83,16 +84,16 @@ def investigate_entity(
),
# Relationship Parameters (when relationship_analysis is included)
relationship_depth: int = Field(
default=2,
default=SearchLimits.DEFAULT_IDP_RELATIONSHIPS,
ge=1,
le=3,
le=SearchLimits.MAX_IDP_RELATIONSHIPS,
description="Depth of relationship analysis (1-3 levels)",
),
# General Parameters
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=200,
le=SearchLimits.MAX_IDP_RESULTS,
description="Maximum number of results to return",
),
include_associations: bool = Field(
Expand Down Expand Up @@ -378,7 +379,7 @@ def _build_entity_details_query(

if include_incidents:
fields.append("""
openIncidents(first: 10) {
openIncidents(first: {GraphQLDefaults.FIRST_INCIDENTS}) {{
nodes {
type
startTime
Expand Down Expand Up @@ -430,7 +431,7 @@ def _build_entity_details_query(

return f"""
query {{
entities(entityIds: {entity_ids_json}, first: 50) {{
entities(entityIds: {entity_ids_json}, first: {GraphQLDefaults.FIRST_ENTITIES_BATCH}) {{
nodes {{
{fields_string}
}}
Expand Down Expand Up @@ -795,7 +796,7 @@ def _build_risk_assessment_query(

return f"""
query {{
entities(entityIds: {entity_ids_json}, first: 50) {{
entities(entityIds: {entity_ids_json}, first: {GraphQLDefaults.FIRST_ENTITIES_BATCH}) {{
nodes {{
entityId
primaryDisplayName
Expand Down
13 changes: 7 additions & 6 deletions falcon_mcp/modules/incidents.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pydantic import AnyUrl, Field

from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.incidents import (
CROWD_SCORE_FQL_DOCUMENTATION,
SEARCH_BEHAVIORS_FQL_DOCUMENTATION,
Expand Down Expand Up @@ -105,9 +106,9 @@ def show_crowd_score(
description="FQL Syntax formatted string used to limit the results. IMPORTANT: use the `falcon://incidents/crowd-score/fql-guide` resource when building this filter parameter.",
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=2500,
le=SearchLimits.MAX_CLOUD_SCORE,
description="Maximum number of records to return. (Max: 2500)",
),
offset: int | None = Field(
Expand Down Expand Up @@ -171,9 +172,9 @@ def search_incidents(
description="FQL Syntax formatted string used to limit the results. IMPORTANT: use the `falcon://incidents/search/fql-guide` resource when building this filter parameter.",
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=500,
le=SearchLimits.MAX_INCIDENTS,
description="Maximum number of records to return. (Max: 500)",
),
offset: int | None = Field(
Expand Down Expand Up @@ -234,9 +235,9 @@ def search_behaviors(
description="FQL Syntax formatted string used to limit the results. IMPORTANT: use the `falcon://incidents/behaviors/fql-guide` resource when building this filter parameter.",
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=500,
le=SearchLimits.MAX_INCIDENTS,
description="Maximum number of records to return. (Max: 500)",
),
offset: int | None = Field(
Expand Down
13 changes: 7 additions & 6 deletions falcon_mcp/modules/intel.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from falcon_mcp.common.logging import get_logger
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.intel import (
QUERY_ACTOR_ENTITIES_FQL_DOCUMENTATION,
QUERY_INDICATOR_ENTITIES_FQL_DOCUMENTATION,
Expand Down Expand Up @@ -96,9 +97,9 @@ def query_actor_entities(
description="FQL query expression that should be used to limit the results. IMPORTANT: use the `falcon://intel/actors/fql-guide` resource when building this filter parameter.",
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=5000,
le=SearchLimits.MAX_INTEL,
description="Maximum number of records to return. Max 5000",
examples={10, 20, 100},
),
Expand Down Expand Up @@ -147,9 +148,9 @@ def query_indicator_entities(
description="FQL query expression that should be used to limit the results. IMPORTANT: use the `falcon://intel/indicators/fql-guide` resource when building this filter parameter.",
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=5000,
le=SearchLimits.MAX_INTEL,
description="Maximum number of records to return. (Max: 5000)",
),
offset: int | None = Field(
Expand Down Expand Up @@ -205,9 +206,9 @@ def query_report_entities(
description="FQL query expression that should be used to limit the results. IMPORTANT: use the `falcon://intel/reports/fql-guide` resource when building this filter parameter.",
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=5000,
le=SearchLimits.MAX_INTEL,
description="Maximum number of records to return. (Max: 5000)",
),
offset: int | None = Field(
Expand Down
3 changes: 2 additions & 1 deletion falcon_mcp/modules/serverless.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from falcon_mcp.common.logging import get_logger
from falcon_mcp.common.utils import prepare_api_parameters
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.serverless import SERVERLESS_VULNERABILITIES_FQL_DOCUMENTATION

logger = get_logger(__name__)
Expand Down Expand Up @@ -61,7 +62,7 @@ def search_serverless_vulnerabilities(
examples={"cloud_provider:'aws'", "severity:'HIGH'"},
),
limit: int | None = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
description="The upper-bound on the number of records to retrieve. (Default: 10)",
),
Expand Down
5 changes: 3 additions & 2 deletions falcon_mcp/modules/spotlight.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from falcon_mcp.common.logging import get_logger
from falcon_mcp.modules.base import BaseModule
from falcon_mcp.common.constants import SearchLimits
from falcon_mcp.resources.spotlight import SEARCH_VULNERABILITIES_FQL_DOCUMENTATION

logger = get_logger(__name__)
Expand Down Expand Up @@ -60,9 +61,9 @@ def search_vulnerabilities(
examples={"status:'open'", "cve.severity:'HIGH'"},
),
limit: int = Field(
default=10,
default=SearchLimits.DEFAULT,
ge=1,
le=5000,
le=SearchLimits.MAX_SPOTLIGHT,
description="Maximum number of results to return. (Max: 5000, Default: 10)",
),
offset: int | None = Field(
Expand Down
Loading