Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion charts/mcp-stack/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ mcpContextForge:
VALIDATION_DANGEROUS_JS_PATTERN: '(?i)(?:^|\s|[\"''`<>=])(javascript:|vbscript:|data:\s*[^,]*[;\s]*(javascript|vbscript)|\bon[a-z]+\s*=|<\s*script\b)' # pattern to detect JavaScript injection
VALIDATION_NAME_PATTERN: '^[a-zA-Z0-9_.\-\s]+$' # pattern for validating names (allows spaces)
VALIDATION_IDENTIFIER_PATTERN: '^[a-zA-Z0-9_\-\.]+$' # pattern for validating IDs (no spaces)
VALIDATION_SAFE_URI_PATTERN: '^[a-zA-Z0-9_\-.:/?=&%]+$' # pattern for safe URI characters
VALIDATION_SAFE_URI_PATTERN: '^[a-zA-Z0-9_\-.:/?=&%{}]+$' # pattern for safe URI characters
VALIDATION_UNSAFE_URI_PATTERN: '[<>"''\\]' # pattern to detect unsafe URI characters
VALIDATION_TOOL_NAME_PATTERN: '^[a-zA-Z][a-zA-Z0-9._-]*$' # MCP tool naming pattern
VALIDATION_TOOL_METHOD_PATTERN: '^[a-zA-Z][a-zA-Z0-9_\./-]*$' # MCP tool method naming pattern
Expand Down
2 changes: 1 addition & 1 deletion docs/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1609,7 +1609,7 @@
"type": "string"
},
"validation_safe_uri_pattern": {
"default": "^[a-zA-Z0-9_\\-.:/?=&%]+$",
"default": "^[a-zA-Z0-9_\\-.:/?=&%{}]+$",
"title": "Validation Safe Uri Pattern",
"type": "string"
},
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1609,7 +1609,7 @@
"type": "string"
},
"validation_safe_uri_pattern": {
"default": "^[a-zA-Z0-9_\\-.:/?=&%]+$",
"default": "^[a-zA-Z0-9_\\-.:/?=&%{}]+$",
"title": "Validation Safe Uri Pattern",
"type": "string"
},
Expand Down
13 changes: 10 additions & 3 deletions mcpgateway/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7566,7 +7566,7 @@ async def admin_add_resource(request: Request, db: Session = Depends(get_db), us
... ("name", "Test Resource"),
... ("description", "A test resource"),
... ("mimeType", "text/plain"),
... ("template", ""),
... ("uri_template", ""),
... ("content", "Sample content"),
... ])
>>> mock_request = MagicMock(spec=Request)
Expand Down Expand Up @@ -7599,15 +7599,22 @@ async def admin_add_resource(request: Request, db: Session = Depends(get_db), us

try:
# Handle template field: convert empty string to None for optional field
template_value = form.get("template")
template = None
template_value = form.get("uri_template")
template = template_value if template_value else None
template_value = form.get("uri_template")
uri_value = form.get("uri")

# Ensure uri_value is a string
if isinstance(uri_value, str) and "{" in uri_value and "}" in uri_value:
template = uri_value

resource = ResourceCreate(
uri=str(form["uri"]),
name=str(form["name"]),
description=str(form.get("description", "")),
mime_type=str(form.get("mimeType", "")),
template=template,
uri_template=template,
content=str(form["content"]),
tags=tags,
visibility=visibility,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""resource_rename_template_to_uri_template

Revision ID: 191a2def08d7
Revises: f3a3a3d901b8
Create Date: 2025-11-17 21:20:05.223248
"""

# Standard
from typing import Sequence, Union

# Third-Party
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision: str = "191a2def08d7"
down_revision: Union[str, Sequence[str], None] = "f3a3a3d901b8"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
conn = op.get_bind()
inspector = sa.inspect(conn)

columns = [c["name"] for c in inspector.get_columns("resources")]

# Only rename if old column exists
if "template" in columns and "uri_template" not in columns:
with op.batch_alter_table("resources") as batch_op:
batch_op.alter_column("template", new_column_name="uri_template")


def downgrade() -> None:
"""Downgrade schema."""
conn = op.get_bind()
inspector = sa.inspect(conn)

columns = [c["name"] for c in inspector.get_columns("resources")]

# Only rename back if current column exists
if "uri_template" in columns and "template" not in columns:
with op.batch_alter_table("resources") as batch_op:
batch_op.alter_column("uri_template", new_column_name="template")
2 changes: 1 addition & 1 deletion mcpgateway/common/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Settings(BaseSettings):
# Character validation patterns
validation_name_pattern: str = r"^[a-zA-Z0-9_.\-\s]+$" # Allow spaces for names
validation_identifier_pattern: str = r"^[a-zA-Z0-9_\-\.]+$" # No spaces for IDs
validation_safe_uri_pattern: str = r"^[a-zA-Z0-9_\-.:/?=&%]+$"
validation_safe_uri_pattern: str = r"^[a-zA-Z0-9_\-.:/?=&%{}]+$"
validation_unsafe_uri_pattern: str = r'[<>"\'\\]'
validation_tool_name_pattern: str = r"^[a-zA-Z][a-zA-Z0-9._-]*$" # MCP tool naming
validation_tool_method_pattern: str = r"^[a-zA-Z][a-zA-Z0-9_\./-]*$"
Expand Down
7 changes: 5 additions & 2 deletions mcpgateway/common/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -715,11 +715,14 @@ class ResourceTemplate(BaseModelWithConfigDict):
Serialized as '_meta' in JSON.
"""

uri_template: str
# ✅ DB field name: uri_template
# ✅ API (JSON) alias:
id: Optional[int] = None
uri_template: str = Field(..., alias="uriTemplate")
name: str
description: Optional[str] = None
mime_type: Optional[str] = None
annotations: Optional[Annotations] = None
annotations: Optional[Dict[str, Any]] = None
meta: Optional[Dict[str, Any]] = Field(None, alias="_meta")


Expand Down
2 changes: 1 addition & 1 deletion mcpgateway/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1303,7 +1303,7 @@ def validate_database(self) -> None:
# Character validation patterns
validation_name_pattern: str = r"^[a-zA-Z0-9_.\-\s]+$" # Allow spaces for names
validation_identifier_pattern: str = r"^[a-zA-Z0-9_\-\.]+$" # No spaces for IDs
validation_safe_uri_pattern: str = r"^[a-zA-Z0-9_\-.:/?=&%]+$"
validation_safe_uri_pattern: str = r"^[a-zA-Z0-9_\-.:/?=&%{}]+$"
validation_unsafe_uri_pattern: str = r'[<>"\'\\]'
validation_tool_name_pattern: str = r"^[a-zA-Z][a-zA-Z0-9._-]*$" # MCP tool naming
validation_tool_method_pattern: str = r"^[a-zA-Z][a-zA-Z0-9_\./-]*$"
Expand Down
2 changes: 1 addition & 1 deletion mcpgateway/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2161,7 +2161,7 @@ class Resource(Base):
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
mime_type: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
size: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
template: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # URI template for parameterized resources
uri_template: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # URI template for parameterized resources
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
is_active: Mapped[bool] = mapped_column(default=True)
Expand Down
2 changes: 1 addition & 1 deletion mcpgateway/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2865,7 +2865,7 @@ async def read_resource(resource_id: str, request: Request, db: Session = Depend

try:
# Call service with context for plugin support
content = await resource_service.read_resource(db, resource_id, request_id=request_id, user=user, server_id=server_id)
content = await resource_service.read_resource(db, resource_id=resource_id, request_id=request_id, user=user, server_id=server_id)
except (ResourceNotFoundError, ResourceError) as exc:
# Translate to FastAPI HTTP error
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(exc)) from exc
Expand Down
5 changes: 3 additions & 2 deletions mcpgateway/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -1501,7 +1501,7 @@ class ResourceCreate(BaseModel):
name: str = Field(..., description="Human-readable resource name")
description: Optional[str] = Field(None, description="Resource description")
mime_type: Optional[str] = Field(None, alias="mimeType", description="Resource MIME type")
template: Optional[str] = Field(None, description="URI template for parameterized resources")
uri_template: Optional[str] = Field(None, description="URI template for parameterized resources")
content: Union[str, bytes] = Field(..., description="Resource content (text or binary)")
tags: Optional[List[str]] = Field(default_factory=list, description="Tags for categorizing the resource")

Expand Down Expand Up @@ -1642,7 +1642,7 @@ class ResourceUpdate(BaseModelWithConfigDict):
name: Optional[str] = Field(None, description="Human-readable resource name")
description: Optional[str] = Field(None, description="Resource description")
mime_type: Optional[str] = Field(None, description="Resource MIME type")
template: Optional[str] = Field(None, description="URI template for parameterized resources")
uri_template: Optional[str] = Field(None, description="URI template for parameterized resources")
content: Optional[Union[str, bytes]] = Field(None, description="Resource content (text or binary)")
tags: Optional[List[str]] = Field(None, description="Tags for categorizing the resource")

Expand Down Expand Up @@ -1776,6 +1776,7 @@ class ResourceRead(BaseModelWithConfigDict):
name: str
description: Optional[str]
mime_type: Optional[str]
uri_template: Optional[str] = Field(None, description="URI template for parameterized resources")
size: Optional[int]
created_at: datetime
updated_at: datetime
Expand Down
Loading
Loading