Skip to main content
Glama
coucya

mcp-server-requests

by coucya

http_request

Send HTTP requests using GET, POST, PUT, PATCH, or DELETE methods with custom headers and parameters. Returns full response data including status, headers, and body for API integration and web communication tasks.

Instructions

Execute an HTTP request with the specified method

Function/Features:

  • Sends HTTP requests using any standard method (GET, POST, PUT, PATCH, DELETE)

  • Allows custom HTTP headers

  • Returns complete HTTP response including status, headers, and body

Notes:

  • 'data' and 'json' parameters are mutually exclusive - use only one

  • When using 'json', the Content-Type header is automatically set to 'application/json'

  • When using 'data', you may need to set appropriate Content-Type header manually

  • Query parameters are URL-encoded automatically and appended to the URL

  • The response includes the full HTTP response with status line, all headers, and body

Args: url (str): Target URL for the HTTP request. method ('GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'], optional): HTTP method to use. Defaults to "GET". query ({ [string]: string | number }, optional): Query parameters to append to the URL. Values are automatically converted to strings. Example: {'key1': 'value1', 'key2': 2}, becomes "key1=value1&key2=2" appended to the URL. headers ({ [string]: string }, optional): Custom HTTP request headers. data (str, optional): Text data to send in the request body. Cannot be used with 'json'. json (Any JSON, optional): Data to serialize as JSON and send in the request body. Cannot be used with 'data'.

Examples: // GET request (default method) http_request({url: "https://api.example.com/data"})

// GET request with query parameters
http_request({url: "https://api.example.com/search", query: {"q": "test", "limit": 10}})

// POST request with JSON data
http_request({url: "https://api.example.com/users", method: "POST", json: {"name": "John", "age": 30}})

// POST request with raw text data
http_request({url: "https://api.example.com/log", method: "POST", data: "This is a log message"})

// PUT request
http_request({url: "https://api.example.com/users/123", method: "PUT", json: {"name": "John Updated", "age": 31}})

// PATCH request
http_request({url: "https://api.example.com/users/123", method: "PATCH", json: {"age": 31, "email": "new@example.com"}})

// DELETE request
http_request({url: "https://api.example.com/users/123", method: "DELETE"})

// Request with custom headers
http_request({url: "https://api.example.com/secure", method: "POST", headers: {"Authorization": "Bearer token"}, json: {"key": "value"}})

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYes(require) Target URL for the HTTP request
methodYes(optional, Defaults to "GET") HTTP method to use for the request
queryYes
headersYes
dataYes
jsonYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Primary MCP tool handler for 'http_request'. Includes registration via @mcp.tool(), input schema via Annotated types, and execution logic calling mcp_http_request with user-agent and formatting.
    @mcp.tool()
    def http_request(
        url: Annotated[str, "(require) Target URL for the HTTP request"],
        *,
        method: Annotated[Literal['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], "(optional, Defaults to \"GET\") HTTP method to use for the request"] = "GET",
        query: Annotated[Optional[Dict[str, str | int | float]], "(optional) Query parameters to append to the URL"] = None,
        headers: Annotated[Optional[Dict[str, str]], "(optional) Custom HTTP request headers"] = None,
        data: Annotated[Optional[str], "(optional) Text data to send in the request body. Cannot be used with 'json'"] = None,
        json: Annotated[Optional[Any], "(optional) JSON to send in the request body. Cannot be used with 'data'"] = None,
    ) -> str:
        """Execute an HTTP request with the specified method
    
        Function/Features:
        - Sends HTTP requests using any standard method (GET, POST, PUT, PATCH, DELETE)
        - Allows custom HTTP headers
        - Returns **complete** HTTP response including status, headers, and body
    
        Notes:
        - 'data' and 'json' parameters are mutually exclusive - use only one
        - When using 'json', the Content-Type header is automatically set to 'application/json'
        - When using 'data', you may need to set appropriate Content-Type header manually
        - Query parameters are URL-encoded automatically and appended to the URL
        - The response includes the full HTTP response with status line, all headers, and body
    
        Args:
            url (str): Target URL for the HTTP request.
            method ('GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'], optional): HTTP method to use. Defaults to "GET".
            query ({ [string]: string | number }, optional): Query parameters to append to the URL. Values are automatically converted to strings. Example: {'key1': 'value1', 'key2': 2}, becomes "key1=value1&key2=2" appended to the URL.
            headers ({ [string]: string }, optional): Custom HTTP request headers.
            data (str, optional): Text data to send in the request body. Cannot be used with 'json'.
            json (Any JSON, optional): Data to serialize as JSON and send in the request body. Cannot be used with 'data'.
    
        Examples:
            // GET request (default method)
            http_request({url: "https://api.example.com/data"})
    
            // GET request with query parameters
            http_request({url: "https://api.example.com/search", query: {"q": "test", "limit": 10}})
    
            // POST request with JSON data
            http_request({url: "https://api.example.com/users", method: "POST", json: {"name": "John", "age": 30}})
    
            // POST request with raw text data
            http_request({url: "https://api.example.com/log", method: "POST", data: "This is a log message"})
    
            // PUT request
            http_request({url: "https://api.example.com/users/123", method: "PUT", json: {"name": "John Updated", "age": 31}})
    
            // PATCH request
            http_request({url: "https://api.example.com/users/123", method: "PATCH", json: {"age": 31, "email": "new@example.com"}})
    
            // DELETE request
            http_request({url: "https://api.example.com/users/123", method: "DELETE"})
    
            // Request with custom headers
            http_request({url: "https://api.example.com/secure", method: "POST", headers: {"Authorization": "Bearer token"}, json: {"key": "value"}})
        """
        return mcp_http_request(method, url, query=query, data=data, json=json, headers=headers, user_agent=ua, force_user_agnet=ua_force)
  • Intermediate wrapper mcp_http_request: adds User-Agent header, calls core http_request, formats response or error.
    def mcp_http_request(
        method: str,
        url: str,
        *,
        query: Optional[dict] = None,
        data: Optional[str | bytes | bytearray] = None,
        json: Optional[dict] = None,
        headers: Optional[dict] = None,
        user_agent: Optional[str] = None,
        force_user_agnet: Optional[bool] = None,
        format_status: bool = True,
        format_headers: bool = True,
        return_content: Literal['raw', 'basic_clean', 'strict_clean', 'markdown'] = "raw",
    ) -> str:
        hs = {}
    
        if headers:
            hs.update(headers)
    
        if force_user_agnet:
            if user_agent:
                hs["User-Agent"] = user_agent
        else:
            if "User-Agent" not in hs and user_agent:
                hs["User-Agent"] = user_agent
    
        try:
            response = http_request(
                method, url,
                query=query,
                headers=hs,
                data=data,
                json_=json
            )
    
            return format_response_result(
                response,
                format_status=format_status,
                format_headers=format_headers,
                return_content=return_content
            )
        except Exception as e:
            return format_error_result(e)
  • Core low-level http_request implementation using Python's urllib.request. Handles URL/query, data/json body, headers, returns Response object. Includes error cases.
    def http_request(
        method: str,
        url: str,
        *,
        query: Optional[dict] = None,
        data: Optional[Union[str, bytes, bytearray]] = None,
        json_: Optional[dict] = None,
        headers: Optional[dict] = None
    ) -> Response:
        if headers is None:
            headers = {}
    
        if not isinstance(method, str):
            raise ArgumentError(f"http method must be a string, and must be one of {str(HTTP_METHODS)}")
    
        m, method = method, method.upper()
        if method not in HTTP_METHODS:
            raise ArgumentError(f"Invalid HTTP method: {m}, must be one of {str(HTTP_METHODS)}")
    
        if not isinstance(url, str):
            raise ArgumentError("URL must be a string")
    
        if data is not None and json_ is not None:
            raise ArgumentError("Both data and json cannot be provided at the same time")
    
        try:
            if query is not None:
                url = merge_query_to_url(url, query)
        except ArgumentError as e:
            raise e from e
        except Exception as e:
            raise ArgumentError("Failed to splicing URL and query") from e
    
        data_bytes = None
        if data is not None:
            if not isinstance(data, (str, bytes, bytearray)):
                raise ArgumentError("Data must be a string, bytes, or bytearray")
            elif isinstance(data, str):
                data_bytes = data.encode(encoding="utf-8")
            elif isinstance(data, bytearray):
                data_bytes = bytes(data)
            else:
                data_bytes = data
        elif json_ is not None:
            try:
                data_bytes = json.dumps(json_).encode(encoding="utf-8")
            except Exception as e:
                raise ArgumentError("Failed to serialize JSON data") from e
    
        if not url.startswith("http://") and not url.startswith("https://"):
            url = "https://" + url
    
        try:
            url = quote(url, safe=";/?:@&=+$,", encoding="utf-8")
            request = urllib.request.Request(url, method=method, headers=headers, data=data_bytes)
            response: http.client.HTTPResponse = urllib.request.urlopen(request)
    
            version = VERSION_MAP.get(response.version, "HTTP/1.1")
            status_code = response.status
            reason = response.reason
            response_headers = response.getheaders()
            content = response.read()
    
            result = Response(url, version, status_code, reason, response_headers, content)
        except urllib.error.HTTPError as e:
            if e.status is None:
                raise RequestError(f"Failed to send request, unknown error") from e
    
            version = "HTTP/1.1"
            status_code = e.status
            reason = e.reason
            response_headers = e.headers.items()
            content = e.read()
    
            result = Response(url, version, status_code, reason, response_headers, content)
        except urllib.error.URLError as e:
            raise RequestError(f"Failed to send request, {e.reason}") from e
        except Exception as e:
            raise RequestError(f"Failed to send request, {e}") from e
    
        return result
  • Response dataclass schema defining the structure of HTTP response objects used throughout the tool.
    @dataclass
    class Response:
        url: str
        version: str
        status_code: int
        reason: str
        headers: list[tuple[str, str]]
        content: str | bytes | bytearray
    
        _content_type: str | None = None
    
        @property
        def content_type(self) -> str:
            if self._content_type is None:
                for k, v in self.headers:
                    if k.lower() == "content-type":
                        self._content_type = v
    
                if self._content_type is None:
                    self._content_type = "application/octet-stream"
    
            return self._content_type
  • Helper to format the final response string: handles content decoding, HTML cleaning/markdown conversion, assembles status/headers/body.
    def format_response_result(
        response: Response,
        *,
        format_status: bool | None = None,
        format_headers: bool | None = None,
        return_content: Literal["raw", "basic_clean", "strict_clean", "markdown"] = "raw",
    ) -> str:
        http_version = response.version
        status = response.status_code
        reason = response.reason
        headers = response.headers
        content = response.content
        content_type = response.content_type
    
        if not isinstance(content_type, str):
            content_type = 'application/octet-stream'
    
        if content_type.startswith("text/") or content_type.startswith("application/json"):
            try:
                if isinstance(content, (bytes, bytearray)):
                    content = content.decode('utf-8')
                else:
                    content = str(content)
            except UnicodeDecodeError as e:
                err_message = f"response content type is \"{content_type}\", but not utf-8 encoded'"
                raise ResponseError(response, err_message) from e
            except Exception as e:
                err_message = f"response content type is \"{content_type}\", but cannot be converted to a string"
                raise ResponseError(response, err_message) from e
        else:
            err_message = f'response content type is "{content_type}", cannot be converted to a string'
            raise ResponseError(response, err_message)
    
        if content_type.startswith("text/html"):
            if return_content == "raw":
                pass
            elif return_content == "basic_clean":
                content = clean_html(content, allowed_attrs=True)
            elif return_content == "strict_clean":
                content = clean_html(content, allowed_attrs=("id", "src", "href"))
            elif return_content == "markdown":
                content = html_to_markdown(content)
    
        strs = []
    
        if format_status:
            strs.append(f"{http_version} {status} {reason}\r\n")
        if format_headers:
            response_header_str = "\r\n".join(f"{k}: {v}" for k, v in headers)
            strs.append(response_header_str)
        if len(strs) > 0:
            strs.append("\r\n\r\n")
        strs.append(content)
    
        return "\r\n".join(strs)
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes key behaviors: automatic URL-encoding of query parameters, automatic Content-Type setting for JSON, and the structure of the complete HTTP response (status, headers, body). However, it lacks details on error handling, timeouts, or authentication requirements, which are important for a general HTTP tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (Function/Features, Notes, Args, Examples) and front-loads key information. However, it is somewhat lengthy due to extensive examples, which, while helpful, could be more concise. Most sentences earn their place by providing essential guidance, but some redundancy exists in explaining response details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex tool with 6 parameters, low schema coverage, and no annotations, the description does a strong job of covering usage, parameters, and behaviors. The presence of an output schema means return values don't need explanation, but the description still clarifies response structure. Minor gaps remain in error handling and advanced HTTP features, but overall it's nearly complete for effective use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Given the low schema description coverage (33%), the description compensates excellently by adding detailed semantics for all parameters. It explains the purpose of 'url', 'method' with default, 'query' with encoding behavior, 'headers', and the critical distinction between 'data' and 'json' with Content-Type implications. The examples further clarify usage, adding significant value beyond the minimal schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose as 'Execute an HTTP request with the specified method,' which is a specific verb+resource combination. It distinguishes itself from sibling tools 'fetch' and 'fetch_to_file' by emphasizing its general-purpose nature supporting multiple HTTP methods, custom headers, and complete response handling, unlike more specialized fetch tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use specific parameters, such as the mutual exclusivity of 'data' and 'json' and when to set Content-Type headers manually. While it doesn't directly compare to sibling tools, the detailed parameter usage rules serve as clear alternatives within the tool itself, helping the agent choose between different request configurations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/coucya/mcp-server-requests'

If you have feedback or need assistance with the MCP directory API, please join our Discord server