Technical Reference
Tool sources
Sources are the starting point for creating tools that can be utilized by AI agents via MCP servers. A source is any input that describes available functionality:
- OpenAPI documents
- Gram Functions (TypeScript, JavaScript, or Python)
- External MCP servers, added from the catalog, registered by URL as custom remote servers, or connected from a private network as tunneled MCP servers
Sources can be uploaded via the dashboard or using the CLI. Once uploaded, a deployment creates tool definitions. These tools can then be curated into toolsets, which are organized collections tailored to specific use cases or workflows. Each toolset can be served as an MCP server, which can be installed into LLM clients for automated usage.
Gram Functions
Section titled “Gram Functions”Gram Functions are snippets of code that define arbitrary tasks for AI agents to execute via MCP servers. Unlike OpenAPI-based tools that map to existing REST API endpoints, Gram Functions can call multiple APIs, connect to remote databases over TCP/HTTP, and perform complex data transformations with third-party libraries.
Functions are particularly useful for:
- Performing calculations, data transformations, or complex business logic that doesn’t map to a single API endpoint.
- Orchestrating multiple API calls within a single tool to create workflow-based operations.
- Integrating third-party services or databases that don’t have OpenAPI documents.
- Implementing conditional logic, iteration, or other control flow that can’t be expressed in a declarative API specification.
Functions run on a managed runtime. The supported runtimes are Node.js 22
(nodejs:22), Node.js 24 (nodejs:24), and Python 3.12 (python:3.12).
Basic structure
Section titled “Basic structure”At its core, a Gram Function is a zip file containing two files:
manifest.json - A metadata file describing the tools:
{ "version": "0.0.0", "tools": [ { "name": "add", "description": "Add two numbers", "inputSchema": { "type": "object", "properties": { "a": { "type": "number" }, "b": { "type": "number" } }, "required": ["a", "b"] } }, { "name": "square_root", "description": "Calculate the square root of a number", "inputSchema": { "type": "object", "properties": { "a": { "type": "number" } }, "required": ["a"] } } ]}The manifest includes each tool’s name, description, and JSON Schema for input validation. It can also declare required environment variables, tags, and MCP tool annotations such as the read-only and destructive hints.
functions.js - A bundled JavaScript file that exports a handleToolCall function:
export async function handleToolCall({ name, input }) { switch (name) { case "add": return json({ value: input.a + input.b }); case "square_root": return json({ value: Math.sqrt(input.a) }); default: throw new Error(`Unknown tool: ${name}`); }}When this zip file is uploaded, the platform uses the manifest to expose the
tools and invokes handleToolCall to execute them.
Note
The code entrypoint must be named functions with an extension matching the
runtime: functions.js, functions.mjs, functions.ts, or functions.mts
for Node.js, and functions.py for Python.
Bundle size limit
The zipped bundle (containing both the manifest and the code entrypoint) must not exceed 15MB. Keep functions lean by avoiding large dependencies and using tree-shaking when bundling.
Environment variables
Section titled “Environment variables”Environment variables defined in the project can be accessed within functions
using the process.env object. For example, an environment variable named
MY_MCP_MULTIPLIER can be accessed in a function like this:
export async function handleToolCall({ name, input }) { switch (name) { case "multiply": return json({ value: input.a * process.env.MY_MCP_MULTIPLIER }); // other cases... default: throw new Error(`Unknown tool: ${name}`); }}Using the framework
Section titled “Using the framework”While these files can be created manually, the
@gram-ai/functions
TypeScript framework simplifies the entire workflow. The framework handles
manifest generation, bundling, input validation, and packaging automatically.
To create a new function project, use the scaffolding tool:
pnpm create @gram-ai/function@latest --template gramHere’s a basic example using the framework:
import { Gram } from "@gram-ai/functions";import * as z from "zod/mini";
const gram = new Gram().tool({ name: "calculate_discount", description: "Calculate the final price after applying a discount percentage", inputSchema: { originalPrice: z.number().positive(), discountPercent: z.number().min(0).max(100), }, async execute(ctx, input) { const discount = input.originalPrice * (input.discountPercent / 100); const finalPrice = input.originalPrice - discount;
return ctx.json({ originalPrice: input.originalPrice, discount: discount, finalPrice: finalPrice, }); },});
export default gram;The framework automatically generates the manifest and bundles the code at build time.
For more details on creating and deploying Gram Functions, see the Gram Functions guide.
OpenAPI documents
Section titled “OpenAPI documents”OpenAPI documents describe the functionality of REST APIs in a standardized format known as the OpenAPI Specification. These files are widely used to generate API documentation, SDKs, and client libraries. Similarly, the platform leverages OpenAPI documents to generate tools that enable LLMs to interact with REST APIs. OpenAPI-sourced tools are especially useful for:
- Making it easy for end-users to leverage a REST API via AI agents.
- Automating workflows that involve multiple API calls.
- Enhancing LLMs with real-time data and functionality from an API.
Though most users upload these documents for their own REST APIs, tools may be generated using an OpenAPI document for any API. Some examples of public APIs that have OpenAPI documents include:
Note
The platform works best with documents using OpenAPI 3.1.x and its corresponding JSON Schema version. See the section on Limitations of OpenAPI 3.0.x for more details.
Optimizing OpenAPI documents
Section titled “Optimizing OpenAPI documents”Because tools are generated directly from endpoint descriptions in the OpenAPI document, it’s essential that those descriptions are accurate and informative. However, writing descriptions that serve both humans and LLMs can be challenging.
Short descriptions may be readable for humans, but LLMs often require more context to interpret intent and usage correctly. To bridge this gap, the platform supports the x-gram extension in OpenAPI documents, allowing LLM-optimized metadata to be provided specifically for tool generation and usage.
openapi: 3.1.0info: title: E-commerce API version: 1.0.0paths: /products/{merchant_id}/{product_id}: get: summary: Get a product operationId: E-Commerce V1 / product tags: [ecommerce] parameters: - name: merchant_id in: path required: true schema: type: string - name: product_id in: path required: true schema: type: string x-gram: name: get_product summary: "" description: | <context> This endpoint returns details about a product for a given merchant. </context> <prerequisites> - If you are presented with a product or merchant slug then you must first resolve these to their respective IDs. - Given a merchant slug use the `resolve_merchant_id` tool to get the merchant ID. - Given a product slug use the `resolve_product_id` tool to get the product ID. </prerequisites> responseFilterType: jq responses: "200": description: Details about a product content: application/json: schema: $ref: "#/components/schemas/Product"Without the x-gram extension, the generated tool would be named ecommerce_e_commerce_v1_product, and have the description "Get a product by its ID", resulting in a poor quality tool. The x-gram extension allows a tool’s name and description to be customized without altering the original information in the OpenAPI document. The x-speakeasy-mcp extension is also recognized as an alternative and works the same way.
The x-gram extension also supports response filtering through the responseFilterType property, which helps LLMs process API responses more effectively.
Using the x-gram extension is optional. With the tool variations feature, a tool’s name and description can be modified when curating tools into toolsets. However, it might be worth using the x-gram extension to make an OpenAPI document clean, descriptive, and LLM-ready before bringing it into the platform, so the team doesn’t need to fix tool names and descriptions later.
Operation tags
Section titled “Operation tags”Native OpenAPI operation tags are ingested automatically and become the source tags for the generated tool. In the example above, the get_product tool inherits the ecommerce tag from its operation.
These tags drive tag-based tool filtering, which lets MCP clients connect to a focused subset of a server’s tools. A tag set through tool variations overrides the operation tags; see the precedence rules for details.
Limitations of OpenAPI 3.0.x
Section titled “Limitations of OpenAPI 3.0.x”Many LLMs don’t support the JSON Schema version used in OpenAPI 3.0.x documents. When these documents are uploaded, they are transparently upgraded to 3.1.0 using the steps defined in Migrating from OpenAPI 3.0 to 3.1.0. When this happens, line numbers may no longer match the original OpenAPI document. It’s recommended to upgrade OpenAPI documents to 3.1.x for a more streamlined experience.
OpenAPI resources
For more information on how to write, understand, and manage OpenAPI documents, check out the OpenAPI documentation.
Speakeasy also provides a comprehensive OpenAPI editor and CLI that help with editing, saving, and linting OpenAPI documents. Log in at app.speakeasy.com using the same credentials used to access the platform.
External MCP servers
Section titled “External MCP servers”Existing MCP servers can be brought into the platform as sources alongside OpenAPI documents and Gram Functions:
- Catalog servers — third-party MCP servers imported from the catalog, powered by the MCP Registry.
- Custom remote MCP servers — existing remote servers registered by URL. Requests are proxied using the streamable HTTP transport.
- Tunneled MCP servers — MCP servers running inside a private network, connected through an outbound tunnel. See tunneled MCP servers.
Tools from external MCP servers deploy like any other source and can be combined with API tools and Gram Functions in a single toolset.