
Product Updates
Model Context Protocol: TypeScript SDKs for the Agentic AI ecosystem

Georges Haidar
February 18, 2025

It’s no longer enough for businesses to make their services available to developers. A great development experience also hinges on the ability for AI to access and integrate with available APIs. That’s why starting today, every TypeScript SDK generated by Speakeasy now bundles a runnable Model Context Protocol (MCP) (opens in a new tab) server enabling you to expose your API to the growing landscape of AI agents.
What is MCP?
Model Context Protocol (opens in a new tab) (MCP) is an open source protocol developed by Anthropic for defining tools which connect AI agents to any 3rd party software system that has useful context. Since its announcement (opens in a new tab) in November 2024, we’ve been seeing an increasing number of AI platforms adopting it. Through MCP servers, LLMs can visit websites, read files from your laptop, pull in messages from Slack and much more.
For those familiar, the language server protocol that Microsoft released with Visual Studio Code had such a profound impact on how developers write code that it’s been adopted in many other popular editors. Now, MCP is positioned to do the same for the agentic AI ecosystem.
A type-safe MCP server in every SDK
├── tools│ ├── accountDelete.ts│ ├── accountExportData.ts│ ├── accountsGetInviteCodes.ts│ ├── actorGetSuggestions.ts│ └── ...├── build.mts├── mcp-server.ts├── resources.ts├── server.ts├── shared.ts└── tools.ts
The generated MCP server acts as a thin wrapper around the existing TypeScript SDK, orchestrating API calls and formatting the results on behalf of the AI agent. The TypeScript SDK’s generated Zod schemas are passed down to the MCP server to give the agent an accurate picture of the request format.
For each method in the SDK, the MCP server will have a generated tool. The tool represents a discrete action that the AI agent can take. For example, the bluesky-ts
SDK has a getFeed
method that enables agents to fetch a user’s feed from Bluesky. The MCP server will generate a getFeed
tool that looks like this:
import { linksCreate } from "../../funcs/linksCreate.js";import * as operations from "../../models/operations/index.js";import { formatResult, ToolDefinition } from "../tools.js";const args = {request: operations.CreateLinkRequestBody$inboundSchema.optional(),};export const tool$linksCreate: ToolDefinition<typeof args> = {name: "links_create",description: `Create a new linkCreate a new link for the authenticated workspace.`,args,tool: async (client, args, ctx) => {const [result, apiCall] = await linksCreate(client,args.request,{ fetchOptions: { signal: ctx.signal } },).$inspect();if (!result.ok) {return {content: [{ type: "text", text: result.error.message }],isError: true,};}const value = result.value;return formatResult(value, apiCall);},};
Customizing tools with OpenAPI extensions
We’ve added an x-speakeasy-mcp
OpenAPI extension that will let you annotate your operations with custom tool names, descriptions and scopes. Being able to customize a tool’s description in particular is going to be essential to provide an LLM with the right context on what a tool does and when to call it.
Scopes
Scopes are a small concept we’re introducing that allows you to tag the generated tools and in turn allow users to control which of them are initialized when they start an MCP server. For example, it’s possible to tag the “read” and “write” operations in your OpenAPI document like so:
paths:/todos:get:x-speakeasy-mcp:scope: [read]operationId: listTodos# ...post:x-speakeasy-mcp:scope: [write]operationId: createTodo# .../todos/{id}:delete:x-speakeasy-mcp:scope: [write, destructive]operationId: deleteTodo# ...
The server will now expose the --scope
CLI flag:
USAGEmcp start [--scope destructive|read|write] [--transport stdio|sse] [--port value] [--log-level debug|warning|info|error] [--server-url value] [--server-index value] [--api-host example.com|localhost:3000]mcp start --helpRun the Model Context Protocol serverFLAGS[--scope] Mount tools/resources that match given scope (repeatable flag) [destructive|read|write][--transport] The transport to use for communicating with the server [stdio|sse, default = stdio][--log-level] The log level to use for the server [debug|warning|info|error, default = info][--port] The port to use when the SSE transport is enabled [default = 2718][--server-url] Overrides the default server URL used by the SDK[--server-index] Selects a predefined server used by the SDK[--api-host] Sets the apiHost variable for url substitution [example.com|localhost:3000]-h [--help] Print help information and exit
And it can be launched like so:
{"mcpServers": {"Todos": {"command": "npx","args": ["-y", "--", "todos", "mcp", "start", "--scope", "read"]"env": {"TODOS_API_TOKEN": "..."}}}}
Note the
--scope read
argument above
Now only the tools that represent read operations will be running. This adds a layer of safety if you want to prevent an LLM from accidentally modifying or deleting data while exploring.
Still an SDK at heart
While the MCP server and CLI are going to be the common entrypoint for many users, we’re still focused on shipping best-in-class TypeScript SDKs. Every tool we generate is a self-contained module that developers can import and compose into their own MCP servers or integrate into other frameworks. Here’s a short example of using the resolve-handle
tool from our Bluesky SDK and merge the result with data from your own project:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { BlueskyCore } from "@speakeasy-sdks/bluesky/core.js";import { tool$atprotoIdentityResolveHandle } from "@speakeasy-sdks/bluesky/mcp-server/tools/atprotoIdentityResolveHandle.js";import { fetchProfileFields } from "./bsky.js";import * as z from "zod";const bluesky = new BlueskyCore();const mcp = new McpServer({ name: "Mashup MCP", version: "0.0.0" });const {tool: resolveDID/* name, args (zod schema), description are also available here */} = tool$atprotoIdentityResolveHandle;const schema = {handle: z.string(),fields: z.array(z.enum(["name", "bio", "avatar", "banner"])),};mcp.tool("bluesky-profile","A tool that summarizes a Bluesky user's profile.",schema,async (args, extra) => {const res = await resolveDID(bluesky,{ request: { handle: args.handle } },extra);res.content.push({type: "text",text: JSON.stringify(await fetchProfileFields(args.handle)),});return res;});
Get started with MCP
If you are an existing Speakeasy customer generating TypeScript, you will already have a pull request ready to review that merge that adds MCP support to your SDK (and of course a way to opt out). For those that are new to Speakeasy, follow the SDK generation getting started guide, and check out the docs page that talks about enabling this feature and customizing your tools.
What’s next?
The MCP ecosystem is nascent and we plan to do our part in growing it. The next big step is the planned support for remote servers. Despite not being released into the protocol yet, we’re already seeing momentum from companies like Cloudflare (opens in a new tab) getting ready to support it.
There’s no doubt that remote MCP servers will be a major improvement to the developer experience that will help to broaden their appeal to users. We’re watching the project closely and will be adding support in the coming weeks.
Stay tuned!