Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b15461a
fix(mcp): תקרה לרשימת המועמדים של ambiguous_section, וטסט שמקבע את חו…
claude Sep 20, 2026
9795eb0
fix(mcp): תקרת גודל לגוף בקשה והגבלת קצב לפי זהות, עם סירוב שנוקב בסי…
claude Sep 20, 2026
30f42c6
fix(parsers): rst_parser בודק את הקלט בכניסה, וברירת המחדל של max_sec…
claude Sep 20, 2026
f8cdf9c
fix(mirror): get_file_at_commit בודק את גודל האובייקט לפני git show, …
claude Sep 20, 2026
be911e0
chore(mcp): ארבעה פריטים קטנים מסקירת #3429 — SUGG-014, SUGG-001, SUG…
claude Sep 20, 2026
09cae32
refactor: שלושה כללים שנכתבו פעמיים חזרו למקום אחד — CR בודד, גבול fr…
claude Sep 21, 2026
4b9a061
chore(mcp): יתרת ממצאי הסקירה על #3428 — שני סירובים בשמם, טבלאות קפו…
claude Sep 21, 2026
60e093c
Merge remote-tracking branch 'origin/claude/gracious-einstein-sevk8p'…
claude Sep 21, 2026
6b71260
Merge remote-tracking branch 'origin/claude/gracious-einstein-sevk8p-…
claude Sep 21, 2026
8ce7055
Merge remote-tracking branch 'origin/claude/gracious-einstein-sevk8p-…
claude Sep 21, 2026
c3346f7
Merge remote-tracking branch 'origin/claude/gracious-einstein-sevk8p-…
claude Sep 21, 2026
3d6d92f
Merge remote-tracking branch 'origin/claude/gracious-einstein-sevk8p-…
claude Sep 21, 2026
7063d0b
Merge remote-tracking branch 'origin/claude/gracious-einstein-sevk8p-…
claude Sep 21, 2026
5c6f30c
Merge remote-tracking branch 'origin/claude/gracious-einstein-sevk8p-…
claude Sep 21, 2026
21a4788
fix(mcp): תקרת הגוף מפסיקה לקרוא גוף אנונימי לפני האימות, ויתרת ממצאי…
claude Sep 21, 2026
7aa6657
Merge origin/main (#3438) into claude/gracious-einstein-sevk8p-review
claude Sep 21, 2026
f2c52fa
fix(normalize): וריאציה-סלקטור מוסר לפי קוד התו ולא לפי צורת הכתיב — …
claude Sep 21, 2026
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
12 changes: 12 additions & 0 deletions docs/environment-variables.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2576,6 +2576,18 @@
- ``300``
- ``120``
- MCP
* - ``MCP_MAX_REQUEST_BYTES``
- תקרת גודל (בתים) לגוף בקשה לשרת ה-MCP, במתודות שנושאות גוף (``POST``/``PUT``/``PATCH``) ולכל הכלים. ברירת המחדל **נגזרת** מ-``MAX_CODE_SIZE`` (``request_bytes_for`` ב-``mcp_server/limits.py``: פי שישה ועוד מעטפת, מעוגל ל-MiB שלם — 1MiB על ברירת המחדל של ``MAX_CODE_SIZE``), והמשתנה הזה רק מעלה אותה בלי דיפלוי; הוא אינו kill switch — ``0`` אינו מכבה, המינימום ``65536`` חל. ``Content-Length`` מעל התקרה נדחה ב-``413`` עם ``{"error": "body_too_large", "max_bytes": ...}`` בלי שנקרא בית; גוף בלי אורך מוצהר נספר עד התקרה תחת דדליין של 30 שניות (``408 body_read_timeout``). ערך שאינו מספר — ברירת המחדל, עם WARNING. ראו "גבולות הבקשה" ב-:doc:`mcp-server`.
- לא
- ``1048576``
- ``2097152``
- MCP
* - ``MCP_RATE_LIMIT_PER_MINUTE``
- כמה קריאות כלים מותרות לזהות אחת (משתמש מאומת) בחלון מתגלגל של דקה, בשרת ה-MCP. קריאה מעבר לזה מחזירה ``{"ok": false, "error": "rate_limited", "limit_per_minute": ..., "retry_after_seconds": ...}`` בלי לתפוס חוט. ‏``0`` מכבה במפורש (WARNING בעלייה); ערך שאינו מספר — ברירת המחדל. ‏``/healthz`` ושאר נתיבי ה-HTTP אינם נספרים. ראו "גבולות הבקשה" ב-:doc:`mcp-server`.
- לא
- ``60``
- ``120``
- MCP
* - ``POSTHOG_PROJECT_TOKEN``
- טוקן הפרויקט ב-PostHog (``phc_...``) עבור מדידת השימוש בשרת ה-MCP. חסר (או חסר ``POSTHOG_HOST``) — בפרודקשן המדידה כבויה והשרת עולה רגיל; בסביבת פיתוח העלייה נכשלת ברעש. ראו :ref:`mcp-analytics`.
- לא
Expand Down
176 changes: 167 additions & 9 deletions docs/mcp-server.rst

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions docs/whats-new.rst

Large diffs are not rendered by default.

31 changes: 30 additions & 1 deletion mcp_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@
)

from .backend import ProductionBackend
from .handlers import max_code_size
from .limits import (
DEFAULT_RATE_LIMIT_PER_MINUTE,
MAX_REQUEST_BYTES_ENV,
MIN_MAX_REQUEST_BYTES,
RATE_LIMIT_ENV,
limit_from_env,
request_bytes_for,
)
from .server import build_app
from .token_store import MCPTokenStore
from .wiring import resolve_mongo
Expand Down Expand Up @@ -116,6 +125,17 @@ def create_app():

backend = ProductionBackend(db_manager=db_manager, mongo_db=mongo)
name = os.getenv("MCP_SERVER_NAME", "CodeKeeper")
# Request limits (#3431) — read here, at the service entry, not at import.
# The body cap is derived from the code-size ceiling the service actually
# runs with (``MAX_CODE_SIZE`` from the config, or the handlers' fallback),
# so raising one can never leave the other behind; ``MCP_MAX_REQUEST_BYTES``
# is the way to raise the cap further without a deploy, not a kill switch.
max_request_bytes = limit_from_env(
MAX_REQUEST_BYTES_ENV, request_bytes_for(max_code_size()), minimum=MIN_MAX_REQUEST_BYTES
)
rate_limit_per_minute = limit_from_env(
RATE_LIMIT_ENV, DEFAULT_RATE_LIMIT_PER_MINUTE, minimum=1, zero_disables=True
)

# Phase D: admin-only repo-browser tools (hidden + gated for non-admins).
from .repo_backend import RepoBackend
Expand Down Expand Up @@ -148,10 +168,19 @@ def create_app():
consent_routes=consent,
repo_backend=repo_backend,
name=name,
max_request_bytes=max_request_bytes,
rate_limit_per_minute=rate_limit_per_minute,
)

# Fallback: PAT-only (Claude Code/Desktop) — runs without OAuth config.
return build_app(backend, MCPTokenStore(mongo), repo_backend=repo_backend, name=name)
return build_app(
backend,
MCPTokenStore(mongo),
repo_backend=repo_backend,
name=name,
max_request_bytes=max_request_bytes,
rate_limit_per_minute=rate_limit_per_minute,
)


app = create_app()
138 changes: 105 additions & 33 deletions mcp_server/docs_handlers.py

Large diffs are not rendered by default.

13 changes: 9 additions & 4 deletions mcp_server/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,8 +461,13 @@ def get_collection_items(
)


def _max_code_size() -> int:
"""The app's per-file size gate (characters, not bytes), with a safe fallback."""
def max_code_size() -> int:
"""The app's per-file size gate (characters, not bytes), with a safe fallback.

Public, because ``mcp_server/app.py`` derives the request-body cap from it
(``limits.request_bytes_for``): one lookup of ``MAX_CODE_SIZE`` serves both
gates, so the two cannot drift apart.
"""
try:
from config import config as _cfg

Expand Down Expand Up @@ -493,7 +498,7 @@ def save_file(

# Reject oversize content (the large-file path is non-versioned; out of scope
# here). Mirror the app's own gate, which counts characters, not bytes.
max_size = _max_code_size()
max_size = max_code_size()
if len(code) > max_size:
return {"ok": False, "error": "code_too_large", "max": max_size}

Expand Down Expand Up @@ -615,7 +620,7 @@ def _resave_edited(
an edit never resets them; the same size gate as ``save_file`` applies to
the resulting body.
"""
max_size = _max_code_size()
max_size = max_code_size()
if len(new_code) > max_size:
return {"ok": False, "error": "code_too_large", "max": max_size}
return backend.save_file(
Expand Down
Loading
Loading