Skip to content

Commit 49814c1

Browse files
authored
Merge branch 'main' into feature-287/api-path-versioning_1
2 parents f33da1b + c30cbf5 commit 49814c1

File tree

403 files changed

+41859
-3537
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

403 files changed

+41859
-3537
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.5.0
2+
current_version = 0.6.0
33
commit = False
44
tag = False
55
sign-tags = True

.flake8

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
[flake8]
22
max-line-length = 600
3+
extend-ignore =
4+
E203,
5+
W503
36
per-file-ignores =
47
mcpgateway/services/gateway_service.py: DAR401,DAR402
58
mcpgateway/translate.py: DAR401

.github/tools/cleanup-ghcr-versions.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ fi
9292
##############################################################################
9393
ORG="ibm"
9494
PKG="mcp-context-forge"
95-
KEEP_TAGS=( "0.1.0" "v0.1.0" "0.1.1" "v0.1.1" "0.2.0" "v0.2.0" "0.3.0" "v0.3.0" "0.4.0" "v0.4.0" "0.5.0" "v0.5.0" "latest" )
95+
KEEP_TAGS=( "0.1.0" "v0.1.0" "0.1.1" "v0.1.1" "0.2.0" "v0.2.0" "0.3.0" "v0.3.0" "0.4.0" "v0.4.0" "0.5.0" "v0.5.0" "0.6.0" "v0.6.0" "latest" )
9696
PER_PAGE=100
9797

9898
DRY_RUN=${DRY_RUN:-true} # default safe

.github/tools/fix_file_headers.py

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ def show_file_lines(file_path: Path, num_lines: int = 10) -> str:
397397

398398

399399
def process_file(file_path: Path, mode: str, authors: str, show_diff: bool = False, debug: bool = False,
400-
require_shebang: Optional[bool] = None, require_encoding: bool = True) -> Optional[Dict[str, Any]]:
400+
require_shebang: Optional[bool] = None, require_encoding: bool = True) -> Optional[Dict[str, Any]]:
401401
"""Check a single file and optionally fix its header.
402402
403403
Args:
@@ -476,6 +476,8 @@ def process_file(file_path: Path, mode: str, authors: str, show_diff: bool = Fal
476476
location_match = re.search(r"^Location: \./(.*)$", docstring_node, re.MULTILINE)
477477
if not location_match:
478478
issues.append("Missing 'Location' line")
479+
elif location_match.group(1) != relative_path_str:
480+
issues.append(f"Incorrect 'Location' line: expected './{relative_path_str}', found './{location_match.group(1)}'")
479481

480482
if f"Copyright {COPYRIGHT_YEAR}" not in docstring_node:
481483
issues.append("Missing 'Copyright' line")
@@ -489,7 +491,8 @@ def process_file(file_path: Path, mode: str, authors: str, show_diff: bool = Fal
489491
if not issues:
490492
return None
491493

492-
if mode in ["fix-all", "fix", "interactive"]:
494+
# Generate new source code for diff preview or actual fixing
495+
if mode in ["fix-all", "fix", "interactive"] or show_diff:
493496
# Extract the raw docstring from source
494497
if module_body and isinstance(module_body[0], ast.Expr):
495498
docstring_expr_node = module_body[0]
@@ -500,38 +503,68 @@ def process_file(file_path: Path, mode: str, authors: str, show_diff: bool = Fal
500503
quotes = '"""' if raw_docstring.startswith('"""') else "'''"
501504
inner_content = raw_docstring.strip(quotes)
502505

503-
# Extract existing header fields and body
506+
# Extract existing header fields
504507
existing_header_fields = extract_header_info(source_code, inner_content)
505508

506-
# Find where the header ends and body begins
509+
# Split docstring into lines for analysis
507510
docstring_lines = inner_content.strip().splitlines()
508-
header_end_idx = 0
511+
512+
# Separate the docstring into header and content parts
513+
content_lines = []
514+
in_header_section = False
509515

510516
for i, line in enumerate(docstring_lines):
511-
if any(line.strip().startswith(field + ":") for field in HEADER_FIELDS):
512-
header_end_idx = i + 1
513-
elif header_end_idx > 0 and line.strip():
514-
# Found first non-header content
517+
line_stripped = line.strip()
518+
519+
# Check if this line is a header field
520+
is_header_field = (any(line_stripped.startswith(field + ":") for field in HEADER_FIELDS) or
521+
line_stripped.startswith("Copyright"))
522+
523+
if is_header_field:
524+
in_header_section = True
525+
elif in_header_section and not line_stripped:
526+
# Empty line might separate header from content - continue checking
527+
continue
528+
elif in_header_section and line_stripped and not is_header_field:
529+
# Found content after header section - this and everything after is content
530+
content_lines.extend(docstring_lines[i:])
515531
break
516-
517-
# Extract body content
518-
docstring_body_lines = docstring_lines[header_end_idx:]
519-
if docstring_body_lines and not docstring_body_lines[0].strip():
520-
docstring_body_lines = docstring_body_lines[1:]
532+
elif not in_header_section and line_stripped:
533+
# Content before any header section (like module descriptions)
534+
# Look ahead to see if there are headers following
535+
has_headers_following = any(
536+
any(future_line.strip().startswith(field + ":") for field in HEADER_FIELDS) or
537+
future_line.strip().startswith("Copyright")
538+
for future_line in docstring_lines[i+1:]
539+
)
540+
if has_headers_following:
541+
# This is content, headers follow later
542+
content_lines.append(line)
543+
else:
544+
# No headers following, this is regular content
545+
content_lines.extend(docstring_lines[i:])
546+
break
521547

522548
# Build new header
523549
new_header_lines = []
524-
new_header_lines.append(existing_header_fields.get("Location") or f"Location: ./{relative_path_str}")
550+
# Always use correct location path
551+
new_header_lines.append(f"Location: ./{relative_path_str}")
525552
new_header_lines.append(existing_header_fields.get("Copyright") or f"Copyright {COPYRIGHT_YEAR}")
526553
new_header_lines.append(existing_header_fields.get("SPDX-License-Identifier") or f"SPDX-License-Identifier: {LICENSE}")
527-
new_header_lines.append(f"Authors: {authors}")
554+
# Preserve existing Authors field if it exists, otherwise use the provided authors
555+
new_header_lines.append(existing_header_fields.get("Authors") or f"Authors: {authors}")
528556

529-
# Reconstruct docstring
557+
# Reconstruct docstring with preserved content
530558
new_inner_content = "\n".join(new_header_lines)
531-
if docstring_body_lines:
532-
new_inner_content += "\n\n" + "\n".join(docstring_body_lines).strip()
559+
if content_lines:
560+
content_str = "\n".join(content_lines)
561+
new_inner_content += "\n\n" + content_str
562+
563+
# Ensure proper ending with newline before closing quotes
564+
if not new_inner_content.endswith('\n'):
565+
new_inner_content += '\n'
533566

534-
new_docstring = f"{quotes}{new_inner_content.strip()}{quotes}"
567+
new_docstring = f"{quotes}{new_inner_content}{quotes}"
535568

536569
# Prepare source with appropriate headers
537570
header_lines = []
@@ -562,7 +595,8 @@ def process_file(file_path: Path, mode: str, authors: str, show_diff: bool = Fal
562595
# No docstring found
563596
issues.append("No docstring found")
564597

565-
if mode in ["fix-all", "fix", "interactive"]:
598+
# Generate new source code for diff preview or actual fixing
599+
if mode in ["fix-all", "fix", "interactive"] or show_diff:
566600
# Create new header
567601
new_header = get_header_template(
568602
relative_path_str,
@@ -683,15 +717,15 @@ def parse_arguments(argv: Optional[List[str]] = None) -> argparse.Namespace:
683717
# Header configuration options
684718
header_group = parser.add_argument_group("header configuration")
685719
header_group.add_argument("--require-shebang", choices=["always", "never", "auto"], default="auto",
686-
help="Require shebang line: 'always', 'never', or 'auto' (only for executable files). Default: auto")
720+
help="Require shebang line: 'always', 'never', or 'auto' (only for executable files). Default: auto")
687721
header_group.add_argument("--require-encoding", action="store_true", default=True,
688-
help="Require encoding line. Default: True")
722+
help="Require encoding line. Default: True")
689723
header_group.add_argument("--no-encoding", action="store_false", dest="require_encoding",
690-
help="Don't require encoding line.")
724+
help="Don't require encoding line.")
691725
header_group.add_argument("--copyright-year", type=int, default=COPYRIGHT_YEAR,
692-
help=f"Copyright year to use. Default: {COPYRIGHT_YEAR}")
726+
help=f"Copyright year to use. Default: {COPYRIGHT_YEAR}")
693727
header_group.add_argument("--license", type=str, default=LICENSE,
694-
help=f"License identifier to use. Default: {LICENSE}")
728+
help=f"License identifier to use. Default: {LICENSE}")
695729

696730
return parser.parse_args(argv)
697731

@@ -830,7 +864,7 @@ def print_results(issues_found: List[Dict[str, Any]], mode: str, modified_count:
830864
# Show debug info if available
831865
if "debug" in issue_info:
832866
debug = issue_info["debug"]
833-
print(f" Debug info:", file=sys.stderr)
867+
print(" Debug info:", file=sys.stderr)
834868
print(f" Executable: {debug['executable']}", file=sys.stderr)
835869
print(f" Has shebang: {debug['has_shebang']}", file=sys.stderr)
836870
print(f" Has encoding: {debug['has_encoding']}", file=sys.stderr)

.github/workflows/docker-release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
#
55
# This workflow re-tags a Docker image (built by a previous workflow)
66
# when a GitHub Release is published, giving it a semantic version tag
7-
# like `v0.5.0`. It assumes the CI build has already pushed an image
7+
# like `v0.6.0`. It assumes the CI build has already pushed an image
88
# tagged with the commit SHA, and that all checks on that commit passed.
99
#
1010
# ➤ Trigger: Release published (e.g. from GitHub UI or `gh release` CLI)
1111
# ➤ Assumes: Existing image tagged with the commit SHA is available
12-
# ➤ Result: Image re-tagged as `ghcr.io/OWNER/REPO:v0.5.0`
12+
# ➤ Result: Image re-tagged as `ghcr.io/OWNER/REPO:v0.6.0`
1313
#
1414
# ======================================================================
1515

@@ -25,7 +25,7 @@ on:
2525
workflow_dispatch:
2626
inputs:
2727
tag:
28-
description: 'Release tag (e.g., v0.5.0)'
28+
description: 'Release tag (e.g., v0.6.0)'
2929
required: true
3030
type: string
3131

.github/workflows/release-chart.yml.inactive

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name: Release Helm Chart
33
on:
44
release:
5-
types: [published] # tag repo, ex: v0.5.0 to trigger
5+
types: [published] # tag repo, ex: v0.6.0 to trigger
66
permissions:
77
contents: read
88
packages: write

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.claude
12
mcpgateway-export*
23
mutants
34
.mutmut-cache

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# report issues (linters). Modified files will need to be staged again.
1919
# -----------------------------------------------------------------------------
2020

21-
exclude: '(^|/)(\.pre-commit-config\.yaml|normalize_special_characters\.py|test_input_validation\.py)$' # ignore these files
21+
exclude: '(^|/)(\.pre-commit-config\.yaml|normalize_special_characters\.py|test_input_validation\.py)$|.*\.(jinja|j2)$' # ignore these files and jinja templates
2222

2323
repos:
2424
# -----------------------------------------------------------------------------
@@ -93,7 +93,7 @@ repos:
9393
- id: forbid-ai-stock-phrases
9494
name: ❌ Forbid AI Stock Phrases
9595
description: Prevents common AI-generated phrases from being committed.
96-
entry: '(?i)(brevity|source=chatgpt.com|turn0search0|filecite|unchanged|as an ai language model|i am an ai developed by|this response was generated by|i don''t have real-time information|i don''t have access to real-time|i can''t browse the internet|i cannot browse the internet|my knowledge cutoff|my training data|i''m not able to access|i don''t have the ability to)'
96+
entry: '(?i)(brevity|source=chatgpt.com|turn0search0|filecite|as an ai language model|i am an ai developed by|this response was generated by|i don''t have real-time information|i don''t have access to real-time|i can''t browse the internet|i cannot browse the internet|my knowledge cutoff|my training data|i''m not able to access|i don''t have the ability to)'
9797
language: pygrep
9898
types: [text]
9999
exclude: ^\.pre-commit-config\.yaml$
@@ -368,7 +368,7 @@ repos:
368368
description: Verifies test files in tests/ directories start with `test_`.
369369
language: python
370370
files: (^|/)tests/.+\.py$
371-
exclude: ^tests/.*/(pages|helpers|fuzzers|scripts|fixtures)/.*\.py$ # Exclude page object, helper, fuzzer, script, and fixture files
371+
exclude: ^tests/(.*/)?(pages|helpers|fuzzers|scripts|fixtures|migration)/.*\.py$|^tests/migration/.*\.py$ # Exclude page object, helper, fuzzer, script, fixture, and migration files
372372
args: [--pytest-test-first] # `test_.*\.py`
373373

374374
# - repo: https://github.com/pycqa/flake8

0 commit comments

Comments
 (0)