From 6d00f4a04b2ee53f06ee13075dcd6e7bbbdabd14 Mon Sep 17 00:00:00 2001 From: Windsor Date: Wed, 17 Dec 2025 07:56:54 -0800 Subject: [PATCH] feat: url encoding --- src/dedalus_mcp/dispatch.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/dedalus_mcp/dispatch.py b/src/dedalus_mcp/dispatch.py index 7b7e355..5541919 100644 --- a/src/dedalus_mcp/dispatch.py +++ b/src/dedalus_mcp/dispatch.py @@ -46,6 +46,7 @@ import time from enum import Enum, StrEnum from typing import Any, Callable, Protocol, runtime_checkable +from urllib.parse import quote from pydantic import BaseModel, ConfigDict, Field, field_validator @@ -91,9 +92,22 @@ class HttpRequest(BaseModel): @field_validator("path") @classmethod def validate_path(cls, v: str) -> str: - """Validate path starts with /.""" + """Validate path and URL-encode query string if needed. + + Automatically encodes unsafe characters in query params (spaces, + parentheses, quotes) while preserving REST API structure chars. + This prevents "invalid uri character" errors from downstream. + """ if not v.startswith("/"): raise ValueError("path must start with '/'") + + # Split path and query, encode query params if present + if "?" in v: + path_part, query_part = v.split("?", 1) + # Encode query while keeping structure chars (=&,.*) intact + # This handles PostgREST operators like "NOT IN (...)" safely + encoded_query = quote(query_part, safe="=&,.*-_~:") + return f"{path_part}?{encoded_query}" return v @field_validator("headers") @@ -135,6 +149,7 @@ class DispatchErrorCode(str, Enum): CONNECTION_NOT_FOUND = "connection_not_found" CONNECTION_REVOKED = "connection_revoked" DECRYPTION_FAILED = "decryption_failed" + INVALID_REQUEST = "invalid_request" # Malformed path, invalid URI, bad headers DOWNSTREAM_TIMEOUT = "downstream_timeout" DOWNSTREAM_UNREACHABLE = "downstream_unreachable" DOWNSTREAM_TLS_ERROR = "downstream_tls_error"