# Manually configure what gets exposed
Source: https://speakeasy.com/blog/gram-vs-fastmcp-cloud

import { Callout, Table } from "@/lib/mdx/components";
import { CalloutCta } from "@/components/callout-cta";
import { GithubIcon } from "@/assets/svg/social/github";

<Callout title="NOTE" type="info">
  This comparison of Gram & FastMCP is based on a snapshot of two developing
  products as of September 2025. If you think we need to update this post,
  please let us know!
</Callout>

<CalloutCta
  icon={<GithubIcon className="size-6" />}
  title="Gram OSS Repository"
  description="Check out Github to see how it works under the hood, contribute improvements, or adapt it for your own use cases. Give us a star!"
  buttonText="View on GitHub"
  buttonHref="https://github.com/speakeasy-api/gram"
/>

Since Anthropic launched the Model Context Protocol in November 2024, the MCP ecosystem has grown quickly. But there's still a gap: building production-grade MCP servers takes too long, and maintaining them as your APIs evolve is tedious manual work. This post breaks down how [Gram](https://getgram.ai) and [FastMCP Cloud](https://fastmcp.cloud) each handle building and deploying MCP servers, and where they fit in your stack. This post compares Gram with FastMCP across two dimensions: building agent tools and deploying MCP servers.

[FastMCP](https://gofastmcp.com/) is an open-source Python framework for building MCP servers, and [FastMCP Cloud](https://fastmcp.cloud/) is the managed hosting platform for deploying FastMCP-based applications.

Gram is the MCP cloud, an open-source platform for building and deploying MCP servers. The platform includes Gram Functions, a TypeScript framework for building MCP servers.

## Quick Comparison

### Framework Comparison

<Table
  title="Building MCP Servers: Gram Functions vs FastMCP"
  columns={[
    { key: "capability", header: "Capability" },
    { key: "gram", header: "Gram Functions" },
    { key: "fastmcp", header: "FastMCP" },
  ]}
  data={[
    {
      capability: "**Open source**",
      gram: "✅ https://github.com/speakeasy-api/gram",
      fastmcp: "✅ https://github.com/jlowin/fastmcp",
    },
    {
      capability: "**Protocol coupling**",
      gram: "Protocol-agnostic (MCP-compatible)",
      fastmcp: "MCP-specific",
    },
    {
      capability: "**Code-based tools**",
      gram: "✅ TypeScript",
      fastmcp: "✅ Python",
    },
    {
      capability: "**API-based tools**",
      gram: "✅",
      fastmcp: "⚠️ bootstrapping only",
    },
    {
      capability: "**No-code tool builder**",
      gram: "✅",
      fastmcp: "❌",
    },
    {
      capability: "**Tool playground**",
      gram: "✅",
      fastmcp: "✅",
    },
    {
      capability: "**Local server development**",
      gram: "✅",
      fastmcp: "✅",
    },
  ]}
  rowKeyField="capability"
/>

### Cloud Platform Comparison

<Table
  title="Deploying MCP Servers: Gram Cloud vs FastMCP Cloud"
  columns={[
    { key: "capability", header: "Capability" },
    { key: "gram", header: "Gram Cloud" },
    { key: "fastmcp", header: "FastMCP Cloud" },
  ]}
  data={[
    {
      capability: "**Architecture**",
      gram: "Serverless",
      fastmcp: "Server-based",
    },
    {
      capability: "**Reusable toolsets**",
      gram: "✅ dynamically build servers",
      fastmcp: "❌ Server-specific",
    },
    {
      capability: "**Tool-use telemetry**",
      gram: "✅",
      fastmcp: "✅",
    },
    {
      capability: "**Server rollbacks**",
      gram: "✅",
      fastmcp: "✅",
    },
    {
      capability: "**OAuth proxy support**",
      gram: "✅",
      fastmcp: "✅",
    },
    {
      capability: "**MCP client framework**",
      gram: "❌",
      fastmcp: "✅",
    },
  ]}
  rowKeyField="capability"
/>

---

## Creating agent tools

**Gram is MCP-compatible, not MCP-specific.** When you define tools with Gram Functions, you're creating agent tools that exist independently of any protocol. These tools can be deployed as MCP servers today, but they're not locked into MCP. If another protocol becomes the standard tomorrow, whether that's code-mode execution, a different agent protocol, or something entirely new, your Gram-defined tools can be wrapped in that protocol without rewriting your core logic.

**FastMCP is MCP-specific.** Tools defined with FastMCP are tightly coupled to the MCP protocol. The framework is designed as a lower-level abstraction that closely mirrors the MCP specification. This gives you direct control over MCP-specific features, but it also locks you into MCP. When the protocol evolves or you need to support a different standard, you're facing a rewrite.

This distinction matters. With [ongoing discussions about MCP's utility and future direction](https://x.com/stevekrouse/status/1986922520298287496), betting your tooling infrastructure on a single protocol is a risk. Protocol-agnostic tooling lets you adapt as the ecosystem evolves without throwing away your work.

### Gram Functions vs FastMCP Framework

Gram Functions (TypeScript) and FastMCP (Python) are both frameworks that abstract the MCP protocol details, but they differ in language, philosophy, and developer experience.

**FastMCP is Python-specific**. You write your MCP server code in Python using FastMCP's decorator-based API. This gives you access to Python's rich ecosystem and is ideal if your team already has Python expertise.

**Gram Functions currently supports TypeScript** (with Python on the roadmap). The TypeScript-first approach caters to teams working in JavaScript/TypeScript ecosystems and offers significantly less boilerplate than traditional MCP SDKs.

**With FastMCP (Python):**

```python filename="my_server.py"
from fastmcp import FastMCP

mcp = FastMCP("My Server")

@mcp.tool()
async def get_order_status(order_id: str) -> dict:
    """Get the status of an order"""
    # Your implementation
    return await fetch_order(order_id)

@mcp.tool()
async def search_products(query: str, category: str = None) -> list:
    """Search for products"""
    params = {"q": query}
    if category:
        params["category"] = category
    return await api_client.get("/products", params=params)
```

**With Gram Functions (TypeScript):**

```typescript filename="server.ts"
import { Gram } from "@gram-ai/functions";
import * as z from "zod";

const g = new Gram()
  .tool({
    name: "get_order_status",
    description: "Get the status of an order",
    inputSchema: { orderId: z.string() },
    async execute(ctx, input) {
      try {
        const response = await fetch(
          `https://api.example.com/orders/${input.orderId}`,
          { signal: ctx.signal },
        );
        return ctx.json(await response.json());
      } catch (error) {
        if (error.name === "AbortError") {
          return ctx.fail("Request was cancelled");
        }
        throw error;
      }
    },
  })
  .tool({
    name: "search_products",
    description: "Search for products",
    inputSchema: {
      query: z.string(),
      category: z.string().optional(),
    },
    async execute(ctx, input) {
      try {
        const params = new URLSearchParams({
          q: input.query,
          ...(input.category && { category: input.category }),
        });
        const response = await fetch(
          `https://api.example.com/products?${params}`,
          { signal: ctx.signal },
        );
        return ctx.json(await response.json());
      } catch (error) {
        if (error.name === "AbortError") {
          return ctx.fail("Request was cancelled");
        }
        throw error;
      }
    },
  });

export const handleToolCall = g.handleToolCall;
```

Both approaches are straightforward. Gram Functions leverages Zod for schema validation, which many TypeScript developers already use. Whereas, FastMCP uses Python type hints for input validation.

### API Proxy with Automatic Sync

One of the biggest differences between the platforms is how they handle API-based tools.

**Gram Cloud provides true API proxy capabilities.** When you upload an OpenAPI specification, Gram generates production-ready tools that proxy requests to your API endpoints. As your API evolves, you upload an updated OpenAPI spec and your MCP tools automatically stay in sync, no code changes required.

![Update OpenAPI spec](/assets/blog/gram-vs-fastmcp-cloud/update-openapi.png)

```mermaid
graph TD
    A[Update API Code] --> B[Regenerate OpenAPI Spec]
    B --> C[Upload to Gram Cloud]
    C --> D[MCP Tools Automatically Updated]
```

**FastMCP Cloud offers OpenAPI integration as a development starting point.** While FastMCP can generate MCP server code from your OpenAPI specification, this is primarily a bootstrapping approach. The generated code requires customization, and then you're on your own to keep your MCP server code synchronized with API changes. Every time your API evolves, you're manually updating tool definitions, testing changes, and redeploying.

```mermaid
graph TD
    A[Existing API] --> B[Generate code stubs from OpenAPI]
    B --> C[Write custom MCP server code]
    C --> D[Configure route mappings]
    D --> E[Set up authentication]
    E --> F[Deploy to FastMCP Cloud]
    F --> G[MCP Server Live]

    H[API Changes] --> I[Update MCP server code]
    I --> J[Test & redeploy]
    J --> K[Manual sync maintenance]
```

### Guided Tool Curation

**Gram Cloud provides a UI for tool curation.** When you upload an API with 50+ endpoints, Gram helps you:

1. Select which endpoints matter for your use cases
2. Organize tools into focused toolsets
3. Enhance descriptions with business context
4. Add examples that help LLMs understand when to use each tool

This guided approach helps you move from a sprawling API to a focused 5-30 tool collection that AI agents can navigate confidently.

![Gram tool curation](/assets/blog/gram-vs-fastmcp-cloud/gram-tool-curation.png)

**FastMCP Cloud manages tool curation in code.** You configure route mappings, filtering, and tool organization in Python code:

```python
from fastmcp import FastMCP
from fastmcp.server.openapi import RouteMap, MCPType


custom_routes = [
    RouteMap(methods=["GET"], pattern=r"^/analytics/.*", mcp_type=MCPType.TOOL),
    RouteMap(pattern=r"^/admin/.*", mcp_type=MCPType.EXCLUDE),
    RouteMap(tags={"internal"}, mcp_type=MCPType.EXCLUDE),
]

mcp = FastMCP.from_openapi(
    openapi_spec=openapi_spec,
    client=client,
    route_maps=custom_routes,
    name="My API Server"
)
```

This code-first approach gives you unlimited flexibility to build sophisticated filtering and routing logic. The downside: you're writing and maintaining curation logic yourself, debugging regex patterns, and iterating through code-test-deploy cycles every time you need to adjust which endpoints are exposed.

---

## Deploying MCP servers

Once you've built your MCP server (or decided not to write code at all), you need to deploy it. The deployment model shapes how much flexibility you have down the road.

### Server Artifact vs Dynamic Composition

Both platforms handle cloud hosting and infrastructure management for you, but they take fundamentally different architectural approaches.

**FastMCP Cloud treats each MCP server as a single deployable artifact.** When you deploy your Python code, it runs as a dedicated application on its own infrastructure. The server process stays running, ready to handle requests. If you want to create a new MCP server with a different combination of tools, you write new code and deploy a separate server artifact. This approach mirrors traditional application deployment and works well for long-running operations.

**Gram treats MCP servers as dynamic compositions of tools.** Rather than deploying code to dedicated machines, you're composing tools into dynamic toolsets. The underlying infrastructure is serverless and shared, scaling to zero when idle (sub-30ms cold starts) and automatically scaling under load. Because servers are just logical groupings rather than physical deployments, you can instantly create new "MCP servers" by composing different combinations of existing tools without writing or deploying any code. This enables the reusable toolsets capability where the same tool can participate in multiple servers simultaneously. The trade-off is that Gram's serverless model isn't suitable for operations that require extended execution time.

### Authentication and Security

Both platforms support OAuth 2.1 with Dynamic Client Registration (DCR) and provide OAuth proxy capabilities for traditional providers like GitHub, Google, and Azure that don't support DCR. But the implementation experience differs significantly.

**Gram Cloud provides managed authentication with UI-based configuration.** Choose between managed authentication (where Gram handles OAuth flows and token management) or passthrough authentication (where users bring their own API credentials). You configure OAuth settings through the UI, and Gram takes care of the infrastructure. The OAuth proxy runs as part of Gram's managed service—no deployment or maintenance required.

![Gram passthrough authentication](/assets/blog/gram-vs-fastmcp-cloud/gram-passthrough-auth.png)

**FastMCP Cloud provides authentication building blocks you assemble in code.** Choose between remote OAuth (for DCR-enabled providers), OAuth/OIDC proxies (for traditional providers), or even build a full OAuth server yourself. You implement authentication by instantiating providers, configuring token verifiers, and setting up client storage backends in your Python code. For production deployments, you'll need to provide JWT signing keys, configure persistent storage (preferably network-accessible like Redis), and handle encryption wrapping for stored tokens. FastMCP's documentation describes the full OAuth server pattern as "an extremely advanced pattern that most users should avoid."

The key difference: Gram treats authentication as a managed service you configure, while FastMCP gives you libraries to build and deploy your own authentication infrastructure.

---

## Conclusion

If you're a Python shop that needs complete control over your MCP infrastructure and you're prepared to build and maintain authentication systems, FastMCP Cloud provides the libraries to construct exactly what you need. The investment is significant—both upfront and ongoing—but you get unlimited flexibility.

Gram takes a different approach to the same problem. The platform is MCP-compatible rather than MCP-specific, so your tools adapt as the agent ecosystem evolves without rewrites. For code-based tools, Gram Functions provides a TypeScript framework with minimal boilerplate. For API-based tools, upload an OpenAPI spec and deploy production-ready MCP servers in minutes with automatic sync as your API evolves. Authentication, deployment, and scaling are handled for you.

The question isn't which is better, it's how deep you want to go on MCP-specifically. FastMCP gives you control and the full scope of the MCP specification at the cost of significant engineering investment. Gram provides a complete platform with a deep focus on creating agent tools quickly and maintaining them easily as your product and AI ecosystem evolve.

Try Gram for free today at [getgram.ai](https://app.getgram.ai).