Skip to content

Commit 17d1949

Browse files
Suresh Kumar Moharajanjonpspri
authored andcommitted
fix syntax issue
refactor: split output_length_guard into config/guards/structured modules - Split monolithic output_length_guard.py into config.py, guards.py, structured.py, and plugin.py for better modularity - Extract 5 handler methods from 286-line tool_post_invoke (21 returns) into named methods: _handle_mcp_content_dict, _handle_plain_string, _handle_text_dict, _handle_mcp_list, _handle_string_list - Extract handle_text closure into standalone _handle_text function - Replace unnecessary binary search in _find_token_cut_point with O(1) arithmetic (max_tokens * chars_per_token), remove the function and max_binary_search_iterations config/validator entirely - Fix recursion depth tracking: use explicit depth parameter instead of fragile path.count(".") + path.count("[") estimation - Fix LengthGuardPolicy.ellipsis default mismatch ("..." vs "\u2026") - Fix echo_back missing docstring and incorrect return type - Fix config.yaml max_tokens: 0 -> null for clarity - Remove max_binary_search_iterations from README, manifest, config - Update test imports and remove obsolete _find_token_cut_point tests Signed-off-by: Jonathan Springer <jps@s390x.com>
1 parent 76560b5 commit 17d1949

11 files changed

Lines changed: 1708 additions & 3467 deletions

File tree

.secrets.baseline

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "package-lock.json|Cargo.lock|^.secrets.baseline$|scripts/sign_image.sh|scripts/zap|sonar-project.properties|^/Users/brian/dev/github.ibm.com/contextforge-org/sps-pipeline-config/.secrets.baseline$|^./.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2026-04-02T15:29:25Z",
6+
"generated_at": "2026-04-02T16:16:00Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -366,11 +366,11 @@
366366
},
367367
{
368368
"hashed_secret": "43fc45734b96bcb1b6cef373e949eb3524ae199b",
369+
"is_secret": false,
369370
"is_verified": false,
370371
"line_number": 1485,
371372
"type": "Secret Keyword",
372-
"verified_result": null,
373-
"is_secret": false
373+
"verified_result": null
374374
},
375375
{
376376
"hashed_secret": "9d989e8d27dc9e0ec3389fc855f142c3d40f0c50",
@@ -1094,27 +1094,27 @@
10941094
"docker-compose.with-langfuse.yml": [
10951095
{
10961096
"hashed_secret": "cb58df830a45cc33df1a313e616ecad78cd796c5",
1097+
"is_secret": false,
10971098
"is_verified": false,
10981099
"line_number": 125,
10991100
"type": "Secret Keyword",
1100-
"verified_result": null,
1101-
"is_secret": false
1101+
"verified_result": null
11021102
},
11031103
{
11041104
"hashed_secret": "2e0c522bfe4e7885492862df2e0b987c0ca02623",
1105+
"is_secret": false,
11051106
"is_verified": false,
11061107
"line_number": 148,
11071108
"type": "Secret Keyword",
1108-
"verified_result": null,
1109-
"is_secret": false
1109+
"verified_result": null
11101110
},
11111111
{
11121112
"hashed_secret": "d9d007c8de197b3f36a3a0ba4f13c0f7df175d5a",
1113+
"is_secret": false,
11131114
"is_verified": false,
11141115
"line_number": 312,
11151116
"type": "Secret Keyword",
1116-
"verified_result": null,
1117-
"is_secret": false
1117+
"verified_result": null
11181118
}
11191119
],
11201120
"docker-compose.yml": [
@@ -2638,11 +2638,11 @@
26382638
"docs/docs/manage/observability/langfuse.md": [
26392639
{
26402640
"hashed_secret": "0c6e5ac9cb218c0a666019d0814c5029b374fb17",
2641+
"is_secret": false,
26412642
"is_verified": false,
26422643
"line_number": 221,
26432644
"type": "Secret Keyword",
2644-
"verified_result": null,
2645-
"is_secret": false
2645+
"verified_result": null
26462646
}
26472647
],
26482648
"docs/docs/manage/observability/observability.md": [
@@ -5398,11 +5398,11 @@
53985398
"mcpgateway/alembic/versions/a7f3c9e1b2d4_add_title_to_tools_resources_prompts.py": [
53995399
{
54005400
"hashed_secret": "2e6b7b27cad43ed0908be745231a98936aad5293",
5401+
"is_secret": false,
54015402
"is_verified": false,
54025403
"line_number": 17,
54035404
"type": "Hex High Entropy String",
5404-
"verified_result": null,
5405-
"is_secret": false
5405+
"verified_result": null
54065406
}
54075407
],
54085408
"mcpgateway/alembic/versions/a8f3b2c1d4e5_add_gateway_refresh_fields.py": [
@@ -6574,19 +6574,19 @@
65746574
"plugins/encoded_exfil_detection/README.md": [
65756575
{
65766576
"hashed_secret": "f7f877c24a4ebef314009a2e79314797bae91bb6",
6577+
"is_secret": false,
65776578
"is_verified": false,
65786579
"line_number": 149,
65796580
"type": "Secret Keyword",
6580-
"verified_result": null,
6581-
"is_secret": false
6581+
"verified_result": null
65826582
},
65836583
{
65846584
"hashed_secret": "5f2fe4f03b2314fa3a217f1e1f0ab2e3f5b1b49f",
6585+
"is_secret": false,
65856586
"is_verified": false,
65866587
"line_number": 178,
65876588
"type": "Secret Keyword",
6588-
"verified_result": null,
6589-
"is_secret": false
6589+
"verified_result": null
65906590
}
65916591
],
65926592
"plugins/examples/custom_auth_example/README.md": [
@@ -6836,27 +6836,27 @@
68366836
},
68376837
{
68386838
"hashed_secret": "532fdeccb155dce5b528ba039b9a5d201b817e3b",
6839+
"is_secret": false,
68396840
"is_verified": false,
68406841
"line_number": 932,
68416842
"type": "Secret Keyword",
6842-
"verified_result": null,
6843-
"is_secret": false
6843+
"verified_result": null
68446844
},
68456845
{
68466846
"hashed_secret": "cf743b3a58a4d0f91c1d7f5825c0b1b5f7758174",
6847+
"is_secret": false,
68476848
"is_verified": false,
68486849
"line_number": 969,
68496850
"type": "Base64 High Entropy String",
6850-
"verified_result": null,
6851-
"is_secret": false
6851+
"verified_result": null
68526852
},
68536853
{
68546854
"hashed_secret": "ff1a7ebc241b8eefe9e75e13f20cc22c365ab626",
6855+
"is_secret": false,
68556856
"is_verified": false,
68566857
"line_number": 969,
68576858
"type": "Secret Keyword",
6858-
"verified_result": null,
6859-
"is_secret": false
6859+
"verified_result": null
68606860
}
68616861
],
68626862
"plugins_rust/secrets_detection/benches/secrets_detection.rs": [
@@ -10794,11 +10794,11 @@
1079410794
"tests/unit/mcpgateway/test_observability.py": [
1079510795
{
1079610796
"hashed_secret": "8f1b63868b5d31deb06a0ff740b192aa490e8950",
10797+
"is_secret": false,
1079710798
"is_verified": false,
1079810799
"line_number": 326,
1079910800
"type": "Secret Keyword",
10800-
"verified_result": null,
10801-
"is_secret": false
10801+
"verified_result": null
1080210802
},
1080310803
{
1080410804
"hashed_secret": "1fb844731bf1e5928ae606915b54e21264da4768",
@@ -11076,11 +11076,11 @@
1107611076
"tests/unit/mcpgateway/utils/test_trace_redaction.py": [
1107711077
{
1107811078
"hashed_secret": "36c3eaa0e1e290f41e2810bae8d9502c785e92d9",
11079+
"is_secret": false,
1107911080
"is_verified": false,
1108011081
"line_number": 50,
1108111082
"type": "Secret Keyword",
11082-
"verified_result": null,
11083-
"is_secret": false
11083+
"verified_result": null
1108411084
}
1108511085
],
1108611086
"tests/unit/mcpgateway/utils/test_url_auth.py": [
@@ -11204,43 +11204,43 @@
1120411204
},
1120511205
{
1120611206
"hashed_secret": "cf743b3a58a4d0f91c1d7f5825c0b1b5f7758174",
11207+
"is_secret": false,
1120711208
"is_verified": false,
1120811209
"line_number": 538,
1120911210
"type": "Base64 High Entropy String",
11210-
"verified_result": null,
11211-
"is_secret": false
11211+
"verified_result": null
1121211212
},
1121311213
{
1121411214
"hashed_secret": "8e42b03e460b2cf358ffbcf4da3bc5d14a22c86e",
11215+
"is_secret": false,
1121511216
"is_verified": false,
1121611217
"line_number": 582,
1121711218
"type": "Base64 High Entropy String",
11218-
"verified_result": null,
11219-
"is_secret": false
11219+
"verified_result": null
1122011220
},
1122111221
{
1122211222
"hashed_secret": "2093dd9cf307518cfe1d2fa5a3985d6fec4e995e",
11223+
"is_secret": false,
1122311224
"is_verified": false,
1122411225
"line_number": 595,
1122511226
"type": "Base64 High Entropy String",
11226-
"verified_result": null,
11227-
"is_secret": false
11227+
"verified_result": null
1122811228
},
1122911229
{
1123011230
"hashed_secret": "caa924f200b35ceb6f0e33878faff75203bdccb4",
11231+
"is_secret": false,
1123111232
"is_verified": false,
1123211233
"line_number": 971,
1123311234
"type": "Secret Keyword",
11234-
"verified_result": null,
11235-
"is_secret": false
11235+
"verified_result": null
1123611236
},
1123711237
{
1123811238
"hashed_secret": "f16da2820437f3c703ff5b95c813f310ce8e67a4",
11239+
"is_secret": false,
1123911240
"is_verified": false,
1124011241
"line_number": 1288,
1124111242
"type": "Secret Keyword",
11242-
"verified_result": null,
11243-
"is_secret": false
11243+
"verified_result": null
1124411244
}
1124511245
],
1124611246
"tests/unit/plugins/test_jwt_claims_extraction.py": [
@@ -11294,19 +11294,19 @@
1129411294
"tools_rust/mcp_runtime/src/observability.rs": [
1129511295
{
1129611296
"hashed_secret": "b7dd0ec3dc49487982011219e66db3716b6669c6",
11297+
"is_secret": false,
1129711298
"is_verified": false,
1129811299
"line_number": 596,
1129911300
"type": "Secret Keyword",
11300-
"verified_result": null,
11301-
"is_secret": false
11301+
"verified_result": null
1130211302
},
1130311303
{
1130411304
"hashed_secret": "d2b1620ca7a5314280e595624e8b13c185f8a2e1",
11305+
"is_secret": false,
1130511306
"is_verified": false,
1130611307
"line_number": 785,
1130711308
"type": "Secret Keyword",
11308-
"verified_result": null,
11309-
"is_secret": false
11309+
"verified_result": null
1131011310
}
1131111311
],
1131211312
"tools_rust/mcp_runtime/tests/runtime.rs": [

mcp-servers/python/output_schema_test_server/src/output_schema_test_server/server_fastmcp.py

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
Implements tools with explicit output schemas to verify the complete workflow.
1111
"""
1212

13+
# Standard
1314
import argparse
1415
import logging
1516
import sys
16-
from typing import Any, List, Dict
17+
from typing import Any
1718

19+
# Third-Party
1820
from fastmcp import FastMCP
1921
from pydantic import BaseModel, Field
2022

@@ -79,15 +81,11 @@ async def multiply_numbers(
7981
) -> CalculationResult:
8082
"""Multiply two numbers and return a structured result."""
8183
logger.info(f"Multiplying {a} * {b}")
82-
return CalculationResult(
83-
result=a * b, operation="multiplication", operands=[a, b], success=True
84-
)
84+
return CalculationResult(result=a * b, operation="multiplication", operands=[a, b], success=True)
8585

8686

8787
@mcp.tool(description="Divide two numbers with error handling in output")
88-
async def divide_numbers(
89-
a: float = Field(..., description="Numerator"), b: float = Field(..., description="Denominator")
90-
) -> CalculationResult:
88+
async def divide_numbers(a: float = Field(..., description="Numerator"), b: float = Field(..., description="Denominator")) -> CalculationResult:
9189
"""Divide two numbers with error handling."""
9290
logger.info(f"Dividing {a} / {b}")
9391

@@ -122,6 +120,7 @@ async def validate_email(
122120
email: str = Field(..., description="Email address to validate"),
123121
) -> ValidationResult:
124122
"""Validate an email address and return structured validation result."""
123+
# Standard
125124
import re
126125

127126
email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
@@ -206,24 +205,23 @@ async def echo(message: str = Field(..., description="Message to echo back")) ->
206205
return f"Echo: {message}"
207206

208207

209-
210-
211208
@mcp.tool(description="Inspectable echo tool")
212-
async def echo_back(message: str) -> str|int:
209+
async def echo_back(message: str) -> str:
210+
"""Echo a message back as-is for inspection."""
213211
return f"{message}"
214212

215213

216214
class NestedData(BaseModel):
217215
"""Example of nested data with lists and dictionaries."""
216+
218217
message: str = Field(..., description="A simple string message")
219218
num: str = Field(..., description="A large number as string")
220-
nested_list: List[Any] = Field(..., description="A nested list, can contain strings or lists")
221-
nested_dict: Dict[str, Any] = Field(..., description="A nested dictionary, can contain strings, lists, or dicts")
219+
nested_list: list[Any] = Field(..., description="A nested list, can contain strings or lists")
220+
nested_dict: dict[str, Any] = Field(..., description="A nested dictionary, can contain strings, lists, or dicts")
221+
222222

223223
@mcp.tool(description="Echo nested list and dictionary structure")
224-
async def echo_nested(
225-
data: NestedData
226-
) -> NestedData:
224+
async def echo_nested(data: NestedData) -> NestedData:
227225
"""
228226
Accepts a nested structure and returns it as-is.
229227
Demonstrates nested list and dict support in MCP output schema.
@@ -261,19 +259,15 @@ async def get_server_info() -> dict[str, Any]:
261259

262260
def main() -> None:
263261
"""Main server entry point with transport selection."""
264-
parser = argparse.ArgumentParser(
265-
description="Output Schema Test MCP Server - Tests outputSchema field support"
266-
)
262+
parser = argparse.ArgumentParser(description="Output Schema Test MCP Server - Tests outputSchema field support")
267263
parser.add_argument(
268264
"--transport",
269265
choices=["stdio", "http"],
270266
default="stdio",
271267
help="Transport mode (stdio or http)",
272268
)
273269
parser.add_argument("--host", default="0.0.0.0", help="HTTP host (only for http transport)")
274-
parser.add_argument(
275-
"--port", type=int, default=9100, help="HTTP port (only for http transport)"
276-
)
270+
parser.add_argument("--port", type=int, default=9100, help="HTTP port (only for http transport)")
277271

278272
args = parser.parse_args()
279273

plugins/config.yaml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ plugins:
434434
# Output Length Guard - enforce bounds or truncate tool outputs
435435
- name: "OutputLengthGuardPlugin"
436436
kind: "plugins.output_length_guard.output_length_guard.OutputLengthGuardPlugin"
437-
description: "Guards tool outputs by enforcing min/max length; block or truncate. Supports MCP content arrays, recursive structuredContent processing, numeric string preservation, word-boundary truncation, and token-based budgets with clear mode segregation."
437+
description: "Guards tool outputs by enforcing min/max character/token length; block or truncate strategies"
438438
version: "1.0.0"
439439
author: "ContextForge"
440440
hooks: ["tool_post_invoke"]
@@ -445,22 +445,21 @@ plugins:
445445
config:
446446
# Output limits
447447
min_chars: 0 # minimum character limit (0 disables)
448-
max_chars: 0 # minimum character limit
449-
min_tokens: 0 # minimum allowed tokens (0 disables)
450-
max_tokens: 0 # maximum allowed tokens (null disables)
448+
max_chars: 15000 # maximum character limit (0 or null disables)
449+
min_tokens: 0 # minimum allowed tokens (0 disables)
450+
max_tokens: null # maximum allowed tokens (null disables)
451451
chars_per_token: 4 # characters per token ratio for estimation (1-10)
452452

453453
# Behavior
454454
limit_mode: "character" # "character" or "token" - choose ONE enforcement method
455-
strategy: "truncate" # truncate | block
455+
strategy: "truncate" # truncate | block
456456
ellipsis: ""
457-
word_boundary: false # true = truncate at word boundaries to avoid mid-word cuts
457+
word_boundary: false # true = truncate at word boundaries to avoid mid-word cuts
458458

459459
# Security limits (optional, defaults shown)
460460
max_text_length: 1000000 # 1MB text limit
461461
max_structure_size: 10000 # Maximum items in list/dict
462462
max_recursion_depth: 100 # Maximum nesting depth
463-
max_binary_search_iterations: 20 # Binary search iteration limit
464463

465464
# Summarizer - summarize long content via OpenAI
466465
- name: "Summarizer"

0 commit comments

Comments
 (0)