Why RESTful Design Still Matters in an MCP World

David Tracey

CTO

Why RESTful Design Still Matters in an MCP World

There is a lot of talk about Model Context Protocol (MCP) servers at the moment, and we believe in the opportunities that MCP provides to build useful and powerful agents that make use of AI. 

We also believe in the benefits of a comprehensive set of APIs based on the RESTful style. This API-first posture is what provided a strong foundation for our hosted MCP server and enables us to extend it further and faster. It’s also why the Bronto MCP server was recently scored higher than competitors.

In this blog, we’ll share an overview of the REST Architectural Style and the guidelines and constraints we adhere to in developing our REST APIs. We’ll also summarise the API-first approach we follow in designing our services. You can check out our REST API docs for details of our APIs.

The Role of REST in Developing Effective MCP Servers

Using a REST API to access a service’s functionality is a common way to build an MCP server, because of all the benefits a REST API provides to a client. We will describe those benefits later in this blog. A 1:1 direct mapping of REST endpoints to MCP tools is, however, usually a bad design, because it may create a lot of low-level MCP tools. 

The MCP server should provide high-level workflow tools so that the model minimizes round trips. Such workflow tools suit a scenario where autonomous LLMs and AI agents select tools dynamically from your MCP server. 

A good appreciation of the REST Architectural Style will help you design the MCP Server’s tools, such as how to avoid the 1:1 mapping above and how to best use the resources defined in REST. Understanding the RESTful architecture style ensures that the foundational REST APIs are optimized to support these higher-level MCP tools efficiently.

REST Goes Beyond Just API Design

REST, stands for Representational State Transfer and is an architectural style for designing networked applications. Many people assume REST is limited to HTTP or is a guide for designing APIs, but it is in fact an architectural style that was introduced in Roy Fielding's dissertation titled “Architectural Styles and the Design of Network-based Software Architectures” in 2000. This architectural style influenced the design of HTTP/1.1, which was co-authored by Fielding. It was not intended to be a universal architectural “silver bullet” and Fielding states explicitly that:

“REST is designed to be efficient for large-grain hypermedia data transfer, optimizing for the common case of the Web, but resulting in an interface that is not optimal for other forms of architectural interaction”. 

An interesting view of the adoption of REST can be found in a later paper published in 2017 “Reflections on the REST Architectural Style and “Principled Design of the Modern Web Architecture” that includes Fielding as one of the authors. This paper recognises that there have been different interpretations of the term REST, but reiterates that:

“REST is not an architecture, but rather an architectural style. It is a set of constraints that, when adhered to, will induce a set of properties; most of those properties are believed to be beneficial for decentralized, network-based applications, while others are the negative trade-offs that can result from any design choice”.

It has been shown that a RESTful style facilitates application development and scalability, as a result of its decoupled nature and the constraints in its interface. An application developer may choose to constrain an architecture in accordance with the REST style and should get an architecture well-suited for building high-volume, high-traffic applications.

Detailing the RESTful Architectural Style

The RESTful Architectural Style uses a Resource as a key abstraction of information that can be represented in a number of representations using the Internet media types. 

It is based on five interface constraints as follows:

  1.  All important resources are identified by a single resource identifier mechanism, usually a Uniform Resource Identifier (URI). This constraint leads to the interface being simple, visible, and reusable.

  2.  Access methods have the same semantics for all resources. For HTTP, this results in a limited set of verbs, such as HEAD, GET, POST, PUT, DELETE, each with well understood semantics. This constraint leads to the interface being visible, scalable, and available (by enabling the use of a layered system and shared caches).

  3. Resources are manipulated through the exchange of representations. This constraint leads to the interface being simple, visible, reusable, cacheable and evolvable using information hiding.

  4. Representations are exchanged via self-descriptive messages. This leads to the interface being visible, scalable, available and evolvable.

  5. Hypertext as the engine of application state (HATEOAS). This leads to the interface being simple, visible, reusable, cacheable, evolvable via loose coupling and adaptable.

Chapter 5 of Fielding’s Thesis also calls out the following constraints from which REST was derived:

  • Uniform Interface: The importance of this constraint can be seen from this quote from Fielding’s thesis “The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components”. This decouples implementation from the service provided, meaning services can evolve independently and making it easier for developers by having uniformity across services, rather than having to understand a new interface for every new service.

  • Client-Server Architecture: This is important as it allows separation of concerns between the client and server allowing each to evolve independently, provided the interface does not change.

  • Statelessness: Statelessness here refers to shared state between a client and server and not resource state. Session state is kept entirely on the client. Each request from client to server must contain all the information required to understand the request and not use any context stored on the server. This allows REST servers to scale horizontally. The absence of server state (i.e. the state of resources) also allows more straightforward load balancing, routing, caching and forwarding.

  • Cacheability: This constraint requires that the data in a response is implicitly or explicitly labeled as cacheable or non-cacheable, so that a client cache can use that cached data to respond to subsequent similar requests.

  • Layered System: This constrains services to only “see” the layer they interact with in an architecture built on hierarchical layers, i.e. the client does not know if it is connected directly to the end server or an intermediary. This limits the overall system complexity. It is useful for wrapping legacy services and for enabling the use of intermediaries, such as load balancers, to improve scalability.

  • Code on Demand (optional): Client functionality can be extended by downloading and executing code. This allows flexibility in terms of updating clients and improves system extensibility, but it is optional as it reduces visibility.

The RESTful Architectural Style has been shown to have the following benefits: 

  • Scalability and Performance: Stateless servers and the support of caching make RESTful systems highly scalable.

  • Flexibility/Interoperability: The uniform interface makes it easier for developers to build and maintain interacting systems, regardless of their implementation details.

  • Evolvability: Client and server applications can evolve separately without breaking compatibility.

  • Visibility: The simplicity of REST makes monitoring and troubleshooting easier.

The RESTful architectural style also includes processing elements that are determined by their roles in an overall application action, i.e. origin server (e.g. Apache), gateway proxy, user agent (e.g. Web browser). 

The Constrained Application Protocol (CoAP) used in the Internet of Things (IoT) is an example of a RESTful approach, showing the RESTful Architectural Style is not just about HTTP.

Adopting an API-First Approach

The API-first approach is a development strategy based on treating APIs as "first-class citizens", where the API abstractions (and resources) are designed as a contract between your service and its clients before the service is implemented in code. This avoids adding an API as an afterthought to a service.

At Bronto, our UI is based on our public REST APIs. We define our abstractions and the API first, with input from stakeholders including UX and PM. We then code the UI to use that API, improving the API by experience gained developing our UI as its client. We make the API public when we are happy with it in terms of stability as we will maintain backwards compatibility once the API is made public.

Breaking Changes and API End of Life 

We design for a tolerant client, which does not break if it sees new fields in a response and so we evolve our API with additive changes to add new resources and or new attributes/objects to resource contents. 

Once made public, this means that the published resources have to be returned from that point on. This allows the API to evolve as existing clients do not have to be updated for new API features, but can take advantage of them when the client itself is updated.

This should mean there is no need for a version to be placed in the URL. Use of the Accept Header is preferred to the version number in the URI as it means that the URI remains well known and encourages the client and server to negotiate over versions of resources.

If an API is to be EOL’ed or a breaking change is to be made, users should be given a defined period of notice about this, e.g. via email and an indication in the response and the old API parts marked as deprecated in the OpenAPI spec. The older version and the newer version should both be available during this transition period.

REST API Guidelines

The benefits of the RESTful Architectural Style also apply to public APIs designed using these constraints, but there is no formal REST API specification. 

In "REST APIs must be hypertext-driven", Fielding provides a set of rules (related to the hypertext HATEOAS constraint above) to be noted before “calling your creation a REST API”. As an example, we use links as part of our Search API to paginate to a page of events to view.

It is important to note that the ‘R‘ in REST stands for ‘Representation‘ and not for ‘Resource‘, where a representation is a machine-readable sequence of bytes and metadata, whereas a resource is a set of entities/values. This distinction is important as it makes Resource the central abstraction for any accessible information and the Resource could have a number of representations. Every resource will have its own resource identifier.

RFC 3986 defines a Uniform Resource Identifier (URI) as a generic naming scheme, which is used to separate the identity of a Resource from what is accepted or returned when it

is accessed. URIs tell a client where a resource can be found and the client then requests a specific representation of that resource from those representations that the requested server can supply, e.g. a web page is a (HTML) representation of a resource. For more on the role of URIs, see “Cool URIs don't change” by Tim Berners-Lee. 

The hypertext driven aspect means that a client of a REST API should:

  • have an entry URI (Uniform Resource Identifier)

  • know the supported media types

  • discover resources available and their URIs

  • access resources via URIs

URI Guidelines

  • Follow the concept of a well-known URI

  • Use only nouns in URIs and no verbs, unless using URI templates as per RFC 6570 where params such as {/?query} may be used to constrain a search. 

    • Verbs should not appear in the URI, based on keeping a Uniform Interface – when using HTTP, the verbs are already defined as POST, GET, PUT, PATCH and DELETE

Resource Design Guidelines

  • Select resources to be exposed in the API according to the nature of the service

    • Expose only the resources appropriate to that service

      • Resist the temptation to expose every persistent resource - that may add redundant functionality clients do not need and make services hard to understand/use

      • Do not expose specific internal application design, as this limits evolvability

    • Avoid tight coupling of the internal and external representations, e.g. don't tie resources to a backend database table 

  • Do not create resources that expose complex behaviour - harder to maintain and may limit scalability

  • Use plurals for Resource names - if there is only ever going to be one instance of that resource then indicate it in the path to that resource (i.e. no id for resource), e.g. /widgets/the-widget 

  • All Resources should have id (as uuid), name (as string) and optionally a description.

  • Attribute names should be snake case

Asynchronous API Guidelines

Long-lived operations (such as processing a complex search) may be sent an initial response and a later response on completion in order to avoid blocking the client. 

For HTTP, we recommend sending an initial Response with a 202 (Accepted) and a body containing an ID for that operation and a URL (say of a temporary Status Resource) which the client queries to check if that operation has completed - 201 should be returned when the search is completed. Note, this URL for the status resource must not be tied to a particular server (e.g. in its session state), as that would break the stateless REST constraint.

For example, in the Bronto Asynchronous Search API, we send an initial Response with a 202 (Accepted) and the location of the status resource for that search returned as a “links”:{} object in the body of the response with a href attribute for the URL of the status resource. The retry-after header will be set to the search timeout and the client can check the status URL until the search has completed successfully with full results (a 201 return code) or receive partial results if the search is still in progress (a 202 return code)

HTTP Verbs and Response Status Code Guidelines

  • GET

    • Must be Idempotent (no side-effects)

    • Must return 200 if retrieved successfully or 204 if request was valid, but no content is returned

  • HEAD 

    • returns the same thing as a GET, without the body, i.e. just the HTTP header information

  • POST

    • Used to create resources

    • Not idempotent

    • Must return 201 if created successfully (or 202 if the resource will be created later as in async operations where it may take time to create the resource)

  • PUT 

    • Used to (completely) replace an object 

    • Idempotent (no side-effects)

    • Should contain all required attributes - returns 400 otherwise

    • Must return 200 if resource replaced successfully

  • PATCH

    • Used to update selected attributes (should be specified as the required attributes)

    • Should contain at least all required attributes , even if only one required, (or all attributes) - returns 400 otherwise

    • Must return 200 if updated successfully

  • DELETE

    • Always returns 204 if successful

Error Status Codes should follow their recommended use in the latest HTTP RFC. In particular:

  • 400 if invalid request, e.g. bad syntax

  • 401 if unauthorized (invalid authentication provided)

  • 403 if forbidden to access resource (authentication valid, but server refuses access)

  • 404 if resource not found

  • 405 if invalid request method for this resource, e.g. PATCH or DELETE not allowed

  • 415 if incorrect request body format

  • 429 if rate-limited

  • 500 if internal server error (usually due to a bug)

  • 503 to indicate server busy, but the query valid and can be retried when system less busy (retry-after header can be used to give a hint for when to retry)

HTTP Specific Guidelines

  • Use retry-after on 429 and x-rate-limit (see Best practices for using the REST API - GitHub Docs )

  • Use links to allow the client to navigate to the next state, e.g. next page in a paginated response 

  • Be consistent with GET on /some_resource without an id. Preferably, this should return a list of the resources under some_resource. If not, it should return 405 if the list of resources under that URI is not to be returned or 200. It should not return 400 unless the syntax of the request is invalid.

  • Provide a default page/resource with appropriate detail when a resource is deprecated when returning 404 in preference to simply returning 404 Not Found with standard web server content.

Building on Bronto’s RESTful Foundation

With these architectural guidelines, it’s easy to build on top of the Bronto platform. We’ve extended functionality to LLM workflows with both a local MCP Server and a hosted MCP server, and enabled teams to build their own version of the Bronto UI with Bronto Vibe, our public Lovable project available for remixing. With an API-first approach, teams can automate workflows and take a headless approach to observability.

If you want to experience the benefits of our API-first approach first-hand, you can schedule a demo or start a free trial of Bronto today.

Share this post

Try Bronto free for 14 days

Centralize your agent and infrastructure telemetry in one platform with sub-second search and 12-month hot retention. No credit card required.