Generate a Typed API Client

Generate typed clients and an MCP server from your OpenAPI spec.

Introduction

30 Second Summary

A tiny change to a service can leave the code that calls it out of date. The mismatch often stays hidden until a field disappears or comes back in a different shape.

In this project, you will write an OpenAPI specification for a small API that you control. That single file becomes a typed TypeScript client, a Python client, plus an MCP server.

What You'll Build

You will watch your editor autocomplete valid response fields in a scratch script before the generated client makes a successful API call.

By the end of this project, you'll have:

  • A passing validation result that shows your API description is ready for generation before any client code appears.
  • A typed TypeScript client where your editor autocompletes response fields before a successful endpoint call.
  • Proof that one specification can power multiple targets through a working Python call plus an MCP server that lets a coding agent use your API.
  • Secret Mission: An optional challenge to push your client generation skills further.

Are there any prerequisites?

This project assumes you have an API that you control. The TypeScript path needs Node with a package manager, while the final MCP test needs a coding agent.

Before We Start

Typed client generation depends on a local toolchain that can build each generated target. A missing runtime can stop the workflow before your API specification reaches the generator.

In this step, you'll prepare a workspace for your OpenAPI files. You'll install the open-source Speakeasy generation suite for the work ahead.

In this step, get ready to:
  • Prepare the local toolchain.
  • Install the generation tools.
  • Create a workspace for your API.
Check the required runtimes

Node.js powers the generated TypeScript client. npm manages its packages.

Python runs the second client later in the project. Git lets you keep the specification under version control.

  • Press Cmd+Space on macOS or the Windows key on Windows to open system search.
  • Type Terminal on macOS or PowerShell on Windows.
  • Press Enter to open the terminal.

macOS

  • Check the local toolchain by running these commands:
node --version
npm --version
python3 --version
git --version

What Does This Check Show?

  • The first line reports your installed Node.js version.
  • The second line confirms that npm is available.
  • The third line reports your installed Python version.
  • The final line confirms that Git is available.

Windows

  • Check the local toolchain by running these commands:
node --version
npm --version
py --version
git --version

What Does This Check Show?

  • The first line reports your installed Node.js version.
  • The second line confirms that npm is available.
  • The third line reports your installed Python version.
  • The final line confirms that Git is available.

Linux

  • Check the local toolchain by running these commands:
node --version
npm --version
python3 --version
git --version

What Does This Check Show?

  • The first line reports your installed Node.js version.
  • The second line confirms that npm is available.
  • The third line reports your installed Python version.
  • The final line confirms that Git is available.

Your target is Node.js version 20 or higher. Your Python version must be 3.10 or higher.

✔️ All required versions are ready

Your local runtimes are ready. Keep the terminal open for the Speakeasy installation.

ⓧ I see an older version

An older runtime can reject generated syntax or dependencies. Update each outdated runtime before continuing.

ⓧ A command is missing

A missing command means its tool is absent from your system path. Install only the tools that your terminal could not find.

Still Seeing an Old or Missing Runtime?

A terminal opened before installation can keep an outdated system path. Close every terminal window before opening a fresh one.

If the problem remains, help me diagnose my local toolchain.

Install the editor and generator

A code editor with TypeScript language support shows generated types as you work. Visual Studio Code includes TypeScript IntelliSense by default.

  • Visit the official Visual Studio Code download page.
  • Download the installer for your operating system.
  • Run the installer using its default options.
  • Press Cmd+Space on macOS or the Windows key on Windows to open system search.
  • Type Visual Studio Code into the search bar.
  • Press Enter to open the editor.

You now have an editor that can autocomplete the generated TypeScript types later in the project.

  • Return to the terminal from earlier.
  • Check whether the Speakeasy CLI is available by running:
speakeasy -h

What Does This Command Do?

The help flag asks Speakeasy to list its available commands. A command list confirms that your terminal can find the installed CLI.

✔️ I see the command list

Speakeasy is already available. You can continue to the final workspace setup.

ⓧ The command is missing

The installer places the Speakeasy binary on your system path. Your operating system may request approval because that path is shared by terminal tools.

macOS

  • Install the Speakeasy CLI by running this official installer command:
curl -fsSL https://go.speakeasy.com/cli-install.sh | sh
What Does This Installer Do?

The command downloads the official installation script over HTTPS. The script selects the correct Speakeasy binary for macOS.

You may be asked for your system password. You can skip any optional agent-skills prompt for now.

Windows

  • Install the Speakeasy CLI through Windows Package Manager by running:
winget install speakeasy
What Does This Installer Do?

Windows Package Manager downloads the registered Speakeasy package. It adds the CLI to your system so future terminals can find it.

Linux

  • Install the Speakeasy CLI by running this official installer command:
curl -fsSL https://go.speakeasy.com/cli-install.sh | sh
What Does This Installer Do?

The command downloads the official installation script over HTTPS. The script selects the correct Speakeasy binary for Linux.

You may be asked for your system password. You can skip any optional agent-skills prompt for now.

  • Close the terminal after the installation completes.
  • Open a fresh terminal through system search.
  • Confirm that Speakeasy is available by running:
speakeasy -h

What Confirms the Installation?

You should see a list of Speakeasy commands. This confirms that the executable is installed on your system path.

Why Can This Run Without an Account?

The open-source generator supports direct local generation under AGPLv3. The project keeps your generated client under the license you choose.

Speakeasy Still Unavailable?

The terminal may still use the previous system path. Close every terminal window before testing again.

If the command remains unavailable, help me fix my Speakeasy CLI installation.

Create the workspace and confirm your API

The workspace keeps your specification beside each generated client. Opening its terminal in Visual Studio Code makes every future command run from the same location.

  • Switch back to Visual Studio Code.
  • Click File in the top menu.
  • Click Open Folder.
  • Create a folder named typed-api-client on your Desktop from the folder picker.
  • Select the typed-api-client folder.
  • Click Open.
  • Click Terminal in the top menu.
  • Click New Terminal.

You should see the integrated terminal open inside typed-api-client.

What Is a Base URL?

A base URL is the shared address at the front of your API endpoints. The generated client uses it to send requests to the API you own.

  • Start the API you own using its normal local or hosted workflow.
  • Locate its base URL in your API documentation or configuration.
  • Record the base URL here: https://api.example.com.
  • Call one documented endpoint using your API's normal test method.

You should receive a response from that endpoint. The response proves that the API is reachable before generation begins.

Before you run the final check, which tools do you expect the terminal to confirm?

macOS

  • Verify the complete toolchain from the integrated terminal by running:
node --version
npm --version
python3 --version
git --version
speakeasy -h

What Does This Final Check Prove?

The version lines confirm that both generated client targets can use their runtimes. The final command confirms that the generator is available inside your workspace.

Windows

  • Verify the complete toolchain from the integrated terminal by running:
node --version
npm --version
py --version
git --version
speakeasy -h

What Does This Final Check Prove?

The version lines confirm that both generated client targets can use their runtimes. The final command confirms that the generator is available inside your workspace.

Linux

  • Verify the complete toolchain from the integrated terminal by running:
node --version
npm --version
python3 --version
git --version
speakeasy -h

What Does This Final Check Prove?

The version lines confirm that both generated client targets can use their runtimes. The final command confirms that the generator is available inside your workspace.

You should see Node.js version 20 or higher. You should also see Python version 3.10 or higher.

npm and Git should each report a version. Speakeasy should print its command list.

Does One Check Still Fail?

Visual Studio Code may have inherited an older system path. Close the editor fully before reopening the typed-api-client folder.

If one tool still fails, help me diagnose the failing workspace check.

That's your foundation locked in. Next, you'll give the generator an OpenAPI specification and see what its validation catches.

Write and Validate Your OpenAPI Spec

A generated client can only be as trustworthy as the contract it reads. An OpenAPI document gives the generator a precise map of one endpoint.

In this step, you'll describe one reachable endpoint in openapi.yaml. You'll run the Speakeasy CLI linter to expose gaps before generation begins.

In this step, get ready to:
  • Choose a small endpoint from your API.
  • Create an initial OpenAPI 3.0.3 document for that endpoint.
  • Validate the first draft to reveal its structural gaps.
Choose one endpoint

An endpoint description starts with the server address plus one operation. The operation maps its input to a success shape or an error shape.

The starter document models an item lookup through one path parameter. Its response fields use string values.

  • Choose one reachable GET endpoint that fetches a single object through a path parameter.
  • Confirm the endpoint's successful JSON body includes two text fields.
  • Confirm its error JSON body includes one text message field.
Create the first draft

The document uses YAML to keep the API contract readable in source control. The highlighted values connect the template to the endpoint you chose.

  • Use your code editor's file panel to create openapi.yaml in the workspace folder from earlier.

You'll see an empty openapi.yaml tab in your editor.

  • Add this initial document to openapi.yaml:
# Declare the OpenAPI version and API identity.
openapi: 3.0.3
info:
  title: My API
  version: 1.0.0

# Point generated clients at your reachable API.
servers:
  - url: "[[API_BASE_URL="https://api.example.com"]]"

# Describe one operation with its input and outputs.
paths:
  "[[RESOURCE_PATH="/items/{itemId}"]]":
    get:
      operationId: "[[OPERATION_ID="getItem"]]"
      parameters:
        - name: "[[PATH_PARAMETER_NAME="itemId"]]"
          in: path
          schema: { type: string }
      responses:
        "200":
          content:
            application/json:
              schema:
                type: object
                properties:
                  "[[RESPONSE_ID_FIELD="id"]]": { type: string }
                  "[[RESPONSE_NAME_FIELD="name"]]": { type: string }
        "404":
          content: { application/json: { schema: { type: object, properties: { "[[ERROR_MESSAGE_FIELD="message"]]": { type: string } } } } }

What Does This Document Describe?

  • The openapi field selects version 3.0.3. The info section gives the API a title plus its own version.
  • The servers section points generated clients at your reachable API.
  • The paths section describes the endpoint. Its operationId gives the future client method a stable name.
  • The parameters section describes the value supplied through the endpoint path.
  • The responses section defines the successful payload plus the error payload.
  • This first draft is deliberately incomplete. The linter exposes its structural gaps in the next check.
  • Save openapi.yaml.

You'll see the server URL near the top of openapi.yaml. The endpoint path appears above its response schemas.

✔️ Awesome, I've got everything!

Good progress. Your first draft now captures one endpoint as a machine-readable contract.

ⓧ I'd like to double check the full code

  • Compare your complete openapi.yaml file with this reference:
# Declare the OpenAPI version and API identity.
openapi: 3.0.3
info:
  title: My API
  version: 1.0.0

# Point generated clients at your reachable API.
servers:
  - url: "[[API_BASE_URL="https://api.example.com"]]"

# Describe one operation with its input and outputs.
paths:
  "[[RESOURCE_PATH="/items/{itemId}"]]":
    get:
      operationId: "[[OPERATION_ID="getItem"]]"
      parameters:
        - name: "[[PATH_PARAMETER_NAME="itemId"]]"
          in: path
          schema: { type: string }
      responses:
        "200":
          content:
            application/json:
              schema:
                type: object
                properties:
                  "[[RESPONSE_ID_FIELD="id"]]": { type: string }
                  "[[RESPONSE_NAME_FIELD="name"]]": { type: string }
        "404":
          content: { application/json: { schema: { type: object, properties: { "[[ERROR_MESSAGE_FIELD="message"]]": { type: string } } } } }

The reference preserves the intentional validation issues. Those gaps create the diagnostic result this step needs.

File Showing YAML Problems?

  • Confirm openapi.yaml is inside the same workspace shown by your editor.
  • Check that each nested YAML line uses spaces aligned like the reference.
  • Still stuck? Help me fix my OpenAPI YAML draft.
Validate the incomplete document

Validation turns the specification into feedback before the generator consumes it. Speakeasy checks the OpenAPI structure plus rules that affect SDK generation.

Before you validate the draft, which parts do you expect the linter to flag? Keep your prediction in mind.

  • Validate openapi.yaml by running this command:
speakeasy lint openapi -s openapi.yaml

What Does This Command Check?

  • The command invokes Speakeasy's OpenAPI linter.
  • The -s flag points the linter at openapi.yaml.
  • The linter checks whether the document follows the OpenAPI structure.
  • It also checks whether the contract is ready for reliable SDK generation.

You'll see errors that point to required metadata missing from the path parameter plus each response object. This failure is intentional.

That result is useful. The linter has turned an abstract contract problem into file locations you can inspect.

  • Scroll to the first validation error in your terminal.
  • Use its file location to find the matching section in openapi.yaml.
  • Read each remaining issue to identify the affected object.
  • Keep openapi.yaml unchanged after this review.

No Diagnostics?

  • Save openapi.yaml before running the validation command again.
  • Confirm your terminal is still in the workspace containing openapi.yaml.
  • Compare the file with the double-check reference if the linter reports no issues.
  • Still stuck? Help me troubleshoot the Speakeasy validation result.

Your API contract is on disk with its first structural gaps exposed. Next up, you'll fix each finding until the document passes validation.

Generate a TypeScript Client

Your first Speakeasy validation run exposed the gaps in openapi.yaml. Those gaps block dependable code generation.

In this step, you'll turn that report into a valid OpenAPI specification. The result is a TypeScript client with typed methods for your documented endpoint.

In this step, get ready to:
  • Resolve every validation error in openapi.yaml.
  • Generate the client in generated/typescript.
  • Inspect how the generated client represents your API.
Fix the specification

Validation is the gate before code generation. A clean result means every referenced API shape is complete.

What makes the specification generation-ready?

  • The server URL tells the client where to send requests.
  • The operation ID gives each endpoint method a stable name.
  • The parameters define the values accepted by the endpoint.
  • The success response schema defines the data returned to callers.
  • The error response schema defines failures as typed results.
  • Return to openapi.yaml in your editor.
  • Resolve every issue named in the validation report from the previous step.
  • Save openapi.yaml.

Before you run the check, do you expect the repaired specification to pass cleanly?

  • Check the repaired specification by running this command:
speakeasy lint openapi -s openapi.yaml

What does this command check?

The command validates openapi.yaml against Speakeasy's OpenAPI rules. It also checks whether the specification is ready for SDK generation.

You should see the command complete without validation errors. That is a real milestone: your specification is now ready for generation.

Still seeing validation errors?

  • Fix the first remaining error before the ones below it.
  • Use the reported location to find the matching line in openapi.yaml.
  • Still stuck? Help me understand and fix my remaining OpenAPI validation errors.
Generate the TypeScript client

A generated client translates each operation ID into a callable TypeScript method. Its models carry your request shapes into the type system.

  • Create the TypeScript SDK in generated/typescript by running this command:
speakeasy generate sdk \
  --schema openapi.yaml \
  --lang typescript \
  --out generated/typescript

What does this command generate?

  • The --schema openapi.yaml option selects your validated specification.
  • The --lang typescript option selects the TypeScript generator.
  • The --out generated/typescript option places the generated SDK in a dedicated folder.
  • Use your code editor's folder picker to open generated/typescript.

You'll see package configuration plus generated source files for the client. You'll also see request models and response models derived from your specification.

Generated folder missing?

  • Confirm your terminal is in the workspace that contains openapi.yaml.
  • Return to the validation report if generation stops because of a specification issue.
  • Need another pair of eyes? Help me troubleshoot why Speakeasy did not create generated/typescript.
Inspect the generated types

Your operationId connects the specification to the generated method. Generated models expose the request fields and response fields that your editor can understand.

Before you trace the endpoint, which parts of the specification do you expect to find in the generated method?

  • Return to openapi.yaml.
  • Locate the operationId for your endpoint.
  • Record its value here: your operation ID.
  • Search generated/typescript for your operation ID.
  • Open a result inside the generated client source.
  • Hover over the method's request input.
  • Follow the response model reference into its generated definition.
  • Search the generated documentation for retries.

You'll see an endpoint method derived from your operation ID. Its signature points to generated request types and response types.

  • For a paginated endpoint, search the generated documentation for pagination.

The generated documentation shows retry support for the client. Paginated operations also show pagination support when your specification defines it.

That is the hard part done. One valid specification now produces a typed client you can inspect.

Next, you'll install the client in a scratch script. Your editor's autocomplete becomes the proof.

Call the API with TypeScript

Your generated Speakeasy client now mirrors the endpoints described in your specification. The typed methods are ready for consumer code.

A generated client earns its keep when your editor reveals the response shape before the request runs. You’ll prove that by calling one documented endpoint from a scratch TypeScript script.

In this step, get ready to:
  • Create a scratch project that installs the generated SDK.
  • Use editor autocomplete to choose typed response fields.
  • Run the generated client against your reachable API.
Prepare a local consumer

A scratch consumer keeps your generated SDK separate from the code that uses it. This makes the relationship between the library package and application code easy to see.

  • Switch back to the code editor from earlier.
  • Use your editor’s terminal controls to start a terminal in the top-level project workspace where openapi.yaml appears.
  • Create examples/typescript-call as an npm project by running these commands:
mkdir -p examples/typescript-call
cd examples/typescript-call
npm init -y
ls

What does this setup do?

  • The first command creates the examples/typescript-call folder inside your existing workspace.
  • The second command moves the terminal into that folder.
  • The npm command creates a default package.json for the scratch project.
  • The final command lists the folder contents so you can confirm the file exists.

You should see package.json in the output. Your scratch project now has its own dependency manifest.

  • Install the local generated SDK with the TypeScript runner by running:
npm install ../../generated/typescript
npm i -D tsx

How do these dependencies help?

  • The local folder install adds the package from generated/typescript to the scratch project.
  • The tsx runner executes the TypeScript script without a separate manual compilation step.
  • The generated package stays connected to the client code produced from your specification.
  • Select package.json in your editor’s Explorer sidebar.

You should see the generated SDK under dependencies. You should also see tsx under development dependencies.

Local SDK installation failing?

  • Confirm your terminal is inside examples/typescript-call before installing the local package.
  • Confirm generated/typescript/package.json exists in the workspace from earlier.
  • Still stuck? Help me troubleshoot my local generated SDK installation.
Write the typed call

The generated package already contains the exact import name for your SDK. Its usage documentation also reflects the operation IDs from your own specification.

  • Expand generated/typescript in your editor’s Explorer sidebar.
  • Select the generated package.json file.
  • Record its package name as generated SDK package name.
  • Select the generated README.md file.
  • Record the exported client class as generated SDK client class.
  • Choose one documented endpoint from the usage examples.
  • Record its complete call expression as generated endpoint call expression.
  • Record your reachable API address as your API base URL.

Why use the generated example?

Your SDK names come from your specification’s operation IDs. The generated usage example gives you the exact class name plus the exact method path for your endpoint.

Copying those names preserves the connection between openapi.yaml and the consumer script.

  • Select the examples/typescript-call folder in your editor’s Explorer sidebar.
  • Create index.ts inside that folder using the sidebar’s new-file control.
  • Add the initial client script by pasting this code into index.ts:
import { [[SDK_CLIENT_CLASS="generated SDK client class"]] } from "[[SDK_PACKAGE_NAME="generated SDK package name"]]"

// Point the generated client at the reachable API
const sdk = new [[SDK_CLIENT_CLASS="generated SDK client class"]]({
  serverURL: "[[API_BASE_URL="your API base URL"]]",
})

// Call one generated endpoint and keep its typed response
async function run() {
  const result = await [[SDK_CALL="generated endpoint call expression"]]

  // Print the complete success response for the first test
  console.log(result)
}

run()

What does this script do?

  • The import loads your generated client class from the locally installed package.
  • The sdk instance uses serverURL to target your reachable API.
  • The generated endpoint method returns the success response described by your OpenAPI schema.
  • The result variable keeps that response type available to your editor.
  • Save index.ts.

Before you run the script, what shape do you expect the generated method to return?

  • Call your endpoint from the terminal by running:
npx tsx index.ts

What does this command prove?

The runner loads index.ts with the locally installed SDK. The generated method sends the request to the configured base URL.

A printed response proves the scratch project can import the SDK. It also proves the generated client can reach your API.

You should see the endpoint’s complete success response in the terminal. The keys come from the response schema in openapi.yaml.

Request not reaching your API?

  • Confirm the base URL matches the reachable address for your API.
  • Compare the generated call expression with the endpoint example in generated/typescript/README.md.
  • Confirm every required request value has a valid value for your endpoint.
  • Still stuck? Help me debug my generated TypeScript client call.
Inspect fields with autocomplete

The terminal proves the runtime connection works. Your editor can now use the same generated response type to reveal valid fields before you run another request.

  • Return to index.ts in the editor from earlier.
  • Find this response logging section:
  // Print the complete success response for the first test
  console.log(result)

Where does autocomplete get its fields?

The generated method gives result a concrete success-response type. Your editor reads that type from the installed SDK package.

Each suggestion traces back to a property documented in your OpenAPI response schema.

  • Place your cursor immediately after result inside the logging line.
  • Type a full stop after result.

Your editor should display autocomplete suggestions for the generated response type. Each listed property is available without guessing its spelling.

  • Select one response field from the autocomplete suggestions.
  • Record the completed expression as first typed response access.
  • Duplicate the completed logging line directly below it using your editor’s copy controls.
  • Delete the selected field name after result. in the second line.
  • Select a different response field from the autocomplete suggestions.
  • Record the second completed expression as second typed response access.
  • Replace the two temporary logging lines with this labeled version:
  // Read two fields selected through generated autocomplete
  console.log("Typed field 1:", [[RESPONSE_ACCESS_ONE="first typed response access"]])
  console.log("Typed field 2:", [[RESPONSE_ACCESS_TWO="second typed response access"]])

What changed in the script?

The script now reads two properties from the typed success response. Autocomplete supplies valid property names from the generated model.

A misspelled or undocumented property becomes visible in the editor before the request runs.

  • Save index.ts.

✔️ Awesome, I've got everything!

Your saved script now initializes the generated client. It calls one documented endpoint plus reads two typed fields.

ⓧ I'd like to double check the full code

Compare examples/typescript-call/index.ts with this complete script:

import { [[SDK_CLIENT_CLASS="generated SDK client class"]] } from "[[SDK_PACKAGE_NAME="generated SDK package name"]]"

// Point the generated client at the reachable API
const sdk = new [[SDK_CLIENT_CLASS="generated SDK client class"]]({
  serverURL: "[[API_BASE_URL="your API base URL"]]",
})

// Call one generated endpoint and keep its typed response
async function run() {
  const result = await [[SDK_CALL="generated endpoint call expression"]]

  // Read two fields selected through generated autocomplete
  console.log("Typed field 1:", [[RESPONSE_ACCESS_ONE="first typed response access"]])
  console.log("Typed field 2:", [[RESPONSE_ACCESS_TWO="second typed response access"]])
}

run()

The package name plus client class must match the generated files. The method call plus field accesses must match the suggestions from your SDK.

Before you run the final script, which two live values do you expect your selected fields to print?

  • Verify the typed field accesses against your live API by running:
npx tsx index.ts

What does the final run confirm?

The same generated method makes another live request. The two logging lines read properties selected from the generated response type.

Matching values prove the editor’s type information agrees with the runtime response from your API.

You should see two labeled lines containing live values from your endpoint response. You have proved that the generated client reaches your API plus exposes its response fields to editor autocomplete.

Typed fields not printing?

  • Confirm each recorded response access starts from the same result variable returned by the endpoint call.
  • Use autocomplete again if the success payload sits inside a generated response property.
  • Choose fields that contain values in the response returned by your current API data.
  • Still stuck? Help me fix my typed response field access.

Your TypeScript consumer is working against the API you own. Next up, you’ll generate a Python client from the same specification to compare what stays consistent across languages.

Generate and Compare a Python Client

Your TypeScript client already proves that one OpenAPI contract can drive a real API call. A second language tests whether the operation survives the switch.

The shared response model should also survive that switch. This is where your specification starts acting like a reusable source of truth.

Speakeasy will read the same file with Python as the target. You will run the matching endpoint before comparing both generated clients.

In this step, get ready to:
  • Generate a Python client from the existing specification.
  • Call your API through the generated Python client.
  • Compare the operations and models in both generated clients.
Generate the Python SDK

The openapi.yaml file remains the contract. Speakeasy changes the language target while keeping the same API description.

  • Generate the Python SDK from openapi.yaml into generated/python by running:
speakeasy generate sdk --lang python --schema openapi.yaml --out generated/python

What does this command do?

  • The --schema option selects the same specification used for the TypeScript client.
  • The --lang python option selects Python as the generation target.
  • The --out option places the new SDK in generated/python.
  • Confirm the displayed generation settings if Speakeasy requests approval.
  • Inspect generated/python using the Explorer in your code editor.
  • Confirm that the folder contains Python package configuration.
  • Confirm that the folder contains generated client source.
  • Confirm that the folder contains operation methods.
  • Confirm that the folder contains request and response models.

Good progress. Your single specification now produces a second typed client in generated/python.

Python folder missing?

  • Check that your terminal is running from the workspace directory containing openapi.yaml.
  • Confirm that the earlier validation still passes before generating again.
  • Still stuck? Help me troubleshoot my Python SDK generation.
Call your API from Python

Generated package names and method names depend on your specification. The generated documentation is the safest source for the exact imports required by your client.

  • Use your editor's file controls to create a python-call folder inside the existing examples folder.
  • Use the same file controls to create main.py inside examples/python-call.

You should now see examples/python-call/main.py beside the TypeScript scratch project in your file tree.

  • Start an integrated terminal in the workspace directory using your code editor.
  • Install the generated Python package in editable mode by running:
[[PYTHON_COMMAND="your Python command"]] -m pip install -e generated/python

Why use an editable install?

An editable installation lets Python import the package directly from generated/python. Regenerated source becomes available to the scratch script without another package installation.

You should see installation output finish without an error. The generated package is now available to main.py.

Package installation failed?

  • Confirm that the terminal is running from the workspace directory.
  • Inspect generated/python for its generated package configuration.
  • Still stuck? Help me install my generated Python SDK.
  • Open generated/python/README.md in the code editor.
  • Locate the generated usage sample for the endpoint called by your TypeScript script.
  • Copy that complete Python usage sample.
  • Paste the sample into examples/python-call/main.py.
  • Update the generated client initialization to use your reachable API base URL.
  • Hover over the generated endpoint method to inspect its Python return type.

Your editor should show a generated return annotation tied to the endpoint's response model. That confirms the call is using the generated Python types.

  • Add a line that prints one field from the returned response model using editor autocomplete.
  • Save examples/python-call/main.py.

Before you run the script, which response field do you expect the Python client to print?

  • Run the Python scratch script from the workspace directory by running:
[[PYTHON_COMMAND="your Python command"]] examples/python-call/main.py

What does this command prove?

Python loads the editable package from generated/python. The generated client sends the endpoint request before converting the response into its generated model.

You should see the selected response field printed in the terminal. That is the same API contract working through a second language.

Python call not succeeding?

  • Compare the imports in main.py with the generated Python usage sample.
  • Confirm that the client uses the same reachable API base URL as the TypeScript script.
  • Confirm that the endpoint arguments match the generated Python method signature.
  • Still stuck? Help me debug my generated Python client call.
Compare both generated clients

Both SDKs come from the same API contract. Their syntax follows each language while their operations and response shapes stay aligned.

Before you compare the folders, which operation from openapi.yaml do you expect to find in both generated clients?

  • Locate the operationId for the endpoint used by both scratch scripts in openapi.yaml.
  • Search generated/typescript for the corresponding generated method.
  • Search generated/python for the corresponding generated method.
  • Open the matching response model from each generated folder in a split editor view.
  • Match at least three corresponding response fields across the two models.

You should find the same documented endpoint in both generated clients. You should also find corresponding fields in both response models.

The method casing may follow each language's conventions. The shared API surface still traces back to the same operationId and schema.

Your TypeScript client and Python client now agree on the same API contract. Next, you will reuse that specification to generate an MCP server.

Generate an MCP Server

Your TypeScript and Python callers now reach the same API through generated clients. The shared operations prove that one specification can support different programming languages.

This final step uses the open-source Speakeasy generator to turn your OpenAPI specification into a Model Context Protocol server. A coding agent can then discover your API operations as MCP tools.

In this step, get ready to:
  • Generate a local MCP server from openapi.yaml.
  • Connect a compatible coding agent to the generated server.
  • Ask a question that makes the agent call your API.
Generate the MCP server

An MCP server presents each documented API operation as a tool that an agent can select. The operation descriptions in openapi.yaml help the agent decide which tool fits a question.

  • Return to the terminal for your local project workspace.
  • Start the MCP generation wizard by running this command:
speakeasy quickstart --mcp

What Does This Command Do?

  • The quickstart command guides you through the required generation choices.
  • The --mcp flag selects a standalone MCP server as the output target.
  • Speakeasy converts the documented operations into tools that an MCP-compatible agent can discover.
  • Choose openapi.yaml when the wizard requests the source specification.
  • Enter generated/mcp-server when the wizard requests the output directory.
  • Decline the optional hosted deployment when the wizard asks about deployment.
  • Complete the remaining naming prompts for the local server.
  • Allow the generator to finish writing the server files.
  • Switch back to your project workspace in the code editor.
  • Expand generated/mcp-server in the file tree.

You should see a populated TypeScript project containing bin/mcp-server.js plus generated source for the MCP server tools. Each tool corresponds to an operation documented in openapi.yaml.

Generated Folder Missing?

  • Confirm that you ran the command from the workspace containing openapi.yaml.
  • Check that the output destination was entered as generated/mcp-server.
  • Still stuck? Help me troubleshoot my MCP server generation.
Connect your coding agent

The generated server communicates with a local MCP client through standard input and output. Your coding agent launches the server as a child process whenever it needs the API tools.

  • Locate bin/mcp-server.js inside generated/mcp-server.
  • Copy its absolute path into /absolute/path/to/generated/mcp-server/bin/mcp-server.js.
  • Reuse your API base URL from earlier by entering it here: https://api.example.com.
  • Open the local MCP server configuration for your current workspace in your compatible coding agent.

Authentication determines which configuration your agent needs. Choose the tab that matches your API.

API without authentication

  • Add this server entry to your agent's existing local MCP configuration:
{
  "mcpServers": {
    "MyAPI": {
      "command": "node",
      "args": [
        "[[MCP_SERVER_PATH="/absolute/path/to/generated/mcp-server/bin/mcp-server.js"]]",
        "start",
        "--server-url",
        "[[API_BASE_URL="https://api.example.com"]]"
      ]
    }
  }
}

How Does This Configuration Work?

  • The node command launches the generated server entry point.
  • The start argument starts the local MCP process.
  • The --server-url option sends generated tool calls to your reachable API base URL.

API with authentication

Credential setup can feel risky. This configuration stays local to your coding agent.

  • Find the required authentication environment name in the generated server's setup guide.
  • Store that name here: the auth environment variable from the generated setup guide.
  • Add this server entry to your agent's existing local MCP configuration:
{
  "mcpServers": {
    "MyAPI": {
      "command": "node",
      "args": [
        "[[MCP_SERVER_PATH="/absolute/path/to/generated/mcp-server/bin/mcp-server.js"]]",
        "start",
        "--server-url",
        "[[API_BASE_URL="https://api.example.com"]]"
      ],
      "env": {
        "[[AUTH_ENV_NAME="the auth environment variable from the generated setup guide"]]": "your-api-token-here"
      }
    }
  }
}

How Does Authentication Reach the API?

  • The env object passes authentication to the generated server at runtime.
  • The environment variable name must match the generated setup guide because it comes from your specification's authentication scheme.
  • The --server-url option points every generated tool at your reachable API.
  • Replace your-api-token-here with the required credential in your local configuration.
  • Add one env entry for each additional authentication value required by the generated setup guide.
  • Keep the local agent configuration out of version control.
  • Save your coding agent's MCP configuration.
  • Reload the coding agent so it starts the generated server.
  • Check the agent's available MCP tools for an operation from openapi.yaml.

That connection is working when your agent lists at least one generated API operation as an available tool. Your specification now has an agent-facing interface.

Tools Not Appearing?

  • Check that MCP_SERVER_PATH points to the generated bin/mcp-server.js file.
  • Confirm that your coding agent reloaded its MCP configuration after you saved it.
  • Still stuck? Help me connect my coding agent to the generated server.
Ask the agent to call your API

Seeing a tool proves that discovery works. A live tool call proves that the generated server can reach your API and return its response to the agent.

  • Choose one documented endpoint whose current response answers a concrete question.
  • Write that question here: ask a question answered by one documented API endpoint.

Before you send the prompt, which generated tool do you expect the agent to choose?

  • Ask the agent to use live API data by sending this prompt:
Use the available API tools to answer this question with live API data: [[API_QUESTION="ask a question answered by one documented API endpoint"]]

Why Use This Prompt?

The prompt asks for live API data. That requirement gives the agent a reason to invoke the generated MCP tool before composing its answer.

  • Inspect the agent's run details for a call to the generated API tool.
  • Confirm that the tool result contains fields documented by the selected endpoint.
  • Hide any credential values before capturing your result.

You should see the agent invoke a generated tool before presenting its answer. The returned values should come from your reachable API.

Agent Answered Without a Tool Call?

  • Confirm that your question requires current data from one documented endpoint.
  • Check that the selected tool is visible in the agent's MCP tool list.
  • Verify that the configured API base URL is reachable from your machine.
  • Still stuck? Help me make my coding agent call the generated MCP tool.

You made the full jump from specification to agent tool. Your API now has typed clients in two languages plus a local MCP server that a coding agent can call.

Secret mission

Trace One Operation Across Every Target

One OpenAPI operation now powers two generated SDKs plus an MCP tool. Trace that operation across every target to prove that the specification remains the source of truth.

Clean Up Your Resources

Clean Up Your Resources

Your Speakeasy generation workflow runs locally, so its generated files create no ongoing cloud costs. The options below cover keeping the workspace, pausing its running MCP connection, or deleting the local artifacts.

Resources you used:

  • The openapi.yaml file containing your valid OpenAPI specification.
  • The generated TypeScript SDK in generated/typescript.
  • The generated Python SDK in generated/python.
  • The generated MCP server in generated/mcp-server and its running process, if still active.
  • The TypeScript scratch caller in examples/typescript-call.
  • The Python scratch caller in examples/python-call.

Keep everything running

No cleanup action is needed. Choose this if you plan to update openapi.yaml or regenerate the clients as your API changes.

  • Keep openapi.yaml as the source for future client generation.
  • Keep your preferred generated client in version control so other developers can use the same typed interface.
  • Keep generated/python and generated/mcp-server if you want to continue comparing targets or testing agent access.
  • Keep both scratch callers as runnable examples for checking future generated clients.

Pause - I'll come back to this later

Shut down the generated MCP server to free its local process resources. Keep every file so you can resume from the same workspace later.

  • Stop the generated MCP server from the coding agent controls you used earlier if the agent launched it.
  • Press Ctrl+C in its existing terminal if you launched the server manually.
  • Confirm the coding agent reports that the generated MCP server is disconnected.
  • Leave openapi.yaml, the generated folders, and the examples folders in place.

Delete - I don't want to use this again

Remove the generated clients, scratch callers, MCP server, and specification from your local workspace. Deleting these artifacts can feel drastic, but your existing API remains untouched.

  • Stop the generated MCP server from the coding agent controls you used earlier if the agent launched it.
  • Press Ctrl+C in its existing terminal if you launched the server manually.
  • Confirm the coding agent reports that the generated MCP server is disconnected.

The deletion commands differ by operating system.

macOS

  • Return to the terminal for the local workspace that contains openapi.yaml.
  • Delete the project artifacts by running this command:
rm -rf generated/typescript generated/python generated/mcp-server examples/typescript-call examples/python-call openapi.yaml
What Does This Command Remove?

This removes the two generated SDKs, the generated MCP server, both scratch callers, and openapi.yaml. Other files in your local workspace remain untouched.

  • Verify that the project artifacts are gone by running this command:
ls generated/typescript generated/python generated/mcp-server examples/typescript-call examples/python-call openapi.yaml
What Should I See?

You should see output indicating that each listed path can no longer be found. That confirms the local project artifacts have been removed.

Windows

  • Return to Command Prompt in the local workspace that contains openapi.yaml.
  • Delete the project artifacts by running these commands:
rmdir /s /q generated\typescript
rmdir /s /q generated\python
rmdir /s /q generated\mcp-server
rmdir /s /q examples\typescript-call
rmdir /s /q examples\python-call
del openapi.yaml
What Do These Commands Remove?

These commands remove the two generated SDKs, the generated MCP server, both scratch callers, and openapi.yaml. Other files in your local workspace remain untouched.

  • Verify that the project artifacts are gone by running this command:
dir generated\typescript generated\python generated\mcp-server examples\typescript-call examples\python-call openapi.yaml
What Should I See?

You should see output indicating that each listed path can no longer be found. That confirms the local project artifacts have been removed.

Nice Work!

Nice Work!

You did it! Your valid openapi.yaml now powers generated TypeScript and Python SDKs. The same specification powers an MCP server that lets a coding agent call your API.

You've learned how to:

  • Validate an OpenAPI specification with Speakeasy. Then generate a typed TypeScript SDK whose methods follow your operation IDs.
  • Call your API from TypeScript and Python scratch projects. Both callers read typed fields from successful responses.
  • Expose documented API operations as MCP tools. Then configure a coding agent to call your API through the generated server.
  • Complete an optional Secret Mission to push your API client generation skills further.

Ready to quiz yourself?