Skip to main content

hedl_mcp/resource_limits/
error.rs

1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License in the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Resource limit error types.
19
20use crate::error::McpError;
21
22/// Resource limit enforcement error.
23///
24/// Represents various types of resource limit violations with actionable
25/// error messages and context.
26#[derive(Debug, thiserror::Error)]
27pub enum ResourceLimitError {
28    /// Request size exceeds configured limit.
29    #[error(
30        "Request size {size} bytes exceeds limit {limit} bytes (exceeded by {exceeded_by} bytes)"
31    )]
32    RequestTooLarge {
33        /// Actual request size in bytes.
34        size: usize,
35        /// Maximum allowed request size in bytes.
36        limit: usize,
37        /// Amount by which the limit was exceeded.
38        exceeded_by: usize,
39    },
40
41    /// String parameter exceeds size limit.
42    #[error("String parameter size {size} bytes exceeds limit {limit} bytes")]
43    StringTooLarge {
44        /// Actual string size in bytes.
45        size: usize,
46        /// Maximum allowed string size in bytes.
47        limit: usize,
48    },
49
50    /// Array element count exceeds limit.
51    #[error("Array has {size} elements, exceeds limit {limit}")]
52    ArrayTooLarge {
53        /// Actual array element count.
54        size: usize,
55        /// Maximum allowed array elements.
56        limit: usize,
57    },
58
59    /// JSON object nesting depth exceeds limit.
60    #[error("JSON depth {depth} exceeds limit {limit}")]
61    JsonTooDeep {
62        /// Actual nesting depth.
63        depth: usize,
64        /// Maximum allowed nesting depth.
65        limit: usize,
66    },
67
68    /// Response size exceeds configured limit.
69    #[error("Response estimated size {estimated_size} bytes exceeds limit {limit} bytes")]
70    ResponseTooLarge {
71        /// Estimated response size in bytes.
72        estimated_size: usize,
73        /// Maximum allowed response size in bytes.
74        limit: usize,
75    },
76
77    /// Result count exceeds configured limit.
78    #[error("Result count {count} exceeds limit {limit}")]
79    TooManyResults {
80        /// Actual result count.
81        count: usize,
82        /// Maximum allowed result count.
83        limit: usize,
84    },
85
86    /// Rate limit exceeded for a specific client.
87    #[error("Rate limit exceeded for client '{client_id}': burst={burst}, rate={rate}/s")]
88    RateLimitExceeded {
89        /// Client identifier that exceeded the limit.
90        client_id: String,
91        /// Maximum burst capacity.
92        burst: usize,
93        /// Refill rate per second.
94        rate: usize,
95    },
96
97    /// Cache memory usage exceeds limit.
98    #[error("Cache memory {current} bytes exceeds limit {limit} bytes, needs {needed} bytes")]
99    CacheMemoryExceeded {
100        /// Current cache memory usage in bytes.
101        current: usize,
102        /// Maximum allowed cache memory in bytes.
103        limit: usize,
104        /// Additional memory needed for the operation.
105        needed: usize,
106    },
107
108    /// Global concurrency limit exceeded.
109    #[error("Global concurrency limit {limit} exceeded, queue timeout")]
110    GlobalConcurrencyExceeded {
111        /// Maximum concurrent requests allowed globally.
112        limit: usize,
113    },
114
115    /// Per-client concurrency limit exceeded.
116    #[error("Client concurrency limit {limit} exceeded for client '{client_id}', queue timeout")]
117    ClientConcurrencyExceeded {
118        /// Client identifier that exceeded the limit.
119        client_id: String,
120        /// Maximum concurrent requests allowed per client.
121        limit: usize,
122    },
123
124    /// Per-tool concurrency limit exceeded.
125    #[error("Tool concurrency limit {limit} exceeded for tool '{tool_name}', queue timeout")]
126    ToolConcurrencyExceeded {
127        /// Name of the tool that exceeded the limit.
128        tool_name: String,
129        /// Maximum concurrent requests allowed per tool.
130        limit: usize,
131    },
132
133    /// Operation execution timeout.
134    #[error("Operation '{tool_name}' exceeded timeout {timeout_ms}ms")]
135    OperationTimeout {
136        /// Name of the tool that timed out.
137        tool_name: String,
138        /// Timeout duration in milliseconds.
139        timeout_ms: u64,
140    },
141
142    /// Semaphore closed unexpectedly.
143    #[error("Semaphore closed unexpectedly")]
144    SemaphoreClosed,
145}
146
147impl ResourceLimitError {
148    /// Get the JSON-RPC error code for this resource limit error.
149    #[must_use]
150    pub fn error_code(&self) -> i32 {
151        match self {
152            Self::RequestTooLarge { .. }
153            | Self::StringTooLarge { .. }
154            | Self::ArrayTooLarge { .. }
155            | Self::JsonTooDeep { .. } => -32006,
156            Self::ResponseTooLarge { .. } | Self::TooManyResults { .. } => -32009,
157            Self::RateLimitExceeded { .. } => -32005,
158            Self::CacheMemoryExceeded { .. } => -32010,
159            Self::GlobalConcurrencyExceeded { .. }
160            | Self::ClientConcurrencyExceeded { .. }
161            | Self::ToolConcurrencyExceeded { .. } => -32007,
162            Self::OperationTimeout { .. } => -32008,
163            Self::SemaphoreClosed => -32011,
164        }
165    }
166}
167
168impl From<ResourceLimitError> for McpError {
169    fn from(err: ResourceLimitError) -> Self {
170        McpError::InvalidRequest(err.to_string())
171    }
172}