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}