On this page
An MCP server exposes your API as tools that AI agents can call. The Model Context Protocol defines the transport and schema. You provide the tools.
There are two ways to build one.
The manual way
Install the SDK and write TypeScript:
npm install @modelcontextprotocol/sdk
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({
name: 'sentry',
version: '1.0.0',
});
server.tool(
'list_issues',
'List issues for a project',
{
organization_slug: z.string(),
project_slug: z.string(),
},
async ({ organization_slug, project_slug }) => {
const res = await fetch(
`https://sentry.io/api/0/projects/${organization_slug}/${project_slug}/issues/`,
{ headers: { Authorization: `Bearer ${process.env.SENTRY_TOKEN}` } }
);
const data = await res.json();
return { content: [{ type: 'text', text: JSON.stringify(data) }] };
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
That’s one tool. For six tools with input validation, error handling, auth forwarding, and permission checks, you’re looking at a few hundred lines. For each API you want to expose.
The paso way
Write YAML. Run one command.
npm install -g usepaso
usepaso init --name "Sentry"
This creates a usepaso.yaml file. Fill in your capabilities:
version: "1.0"
service:
name: Sentry
description: Error monitoring for software teams
base_url: https://sentry.io/api/0
auth:
type: bearer
capabilities:
- name: list_issues
description: List issues for a project
method: GET
path: /projects/{organization_slug}/{project_slug}/issues/
permission: read
inputs:
organization_slug:
type: string
required: true
description: The organization slug
in: path
project_slug:
type: string
required: true
description: The project slug
in: path
Validate it:
usepaso validate
valid (Sentry, 1 capability, 0 regrets)
Start the server:
USEPASO_AUTH_TOKEN=your-sentry-token usepaso serve
usepaso serving "Sentry" (1 capability). Agents welcome.
That’s a production MCP server. paso handles protocol compliance, request routing, auth forwarding, input validation, and error formatting.
What you don’t write
No TypeScript request handlers. No Zod schema definitions. No transport setup. No auth forwarding logic. No error formatting.
Each new capability is a few lines of YAML:
- name: resolve_issue
description: Resolve an issue
method: PUT
path: /issues/{issue_id}/
permission: write
consent_required: true
inputs:
issue_id:
type: string
required: true
description: The issue ID
in: path
status:
type: enum
required: true
values: [resolved, unresolved, ignored]
description: New status for the issue
Six capabilities. One YAML file. No TypeScript to maintain.
Side-by-side
| Manual MCP server | paso | |
|---|---|---|
| Language | TypeScript or Python | YAML |
| Lines of code | 100-500+ per API | 0 |
| Schema definitions | Zod / Pydantic in code | Declared in YAML |
| Auth handling | You write it | Automatic |
| Input validation | You write it | Automatic |
| New endpoint | New handler function | New YAML block |
| Protocol changes | Rewrite handlers | Update paso |
| Custom logic | Full control | Not supported |
Already have an OpenAPI spec?
Generate the declaration from it:
usepaso init --from-openapi ./openapi.json --name "Sentry"
paso reads the spec, generates the YAML, and you edit it to curate which endpoints agents can access.
Connect to Claude Desktop
usepaso connect claude-desktop
paso writes the config for you. Restart Claude Desktop. Your API is now agent-ready.
Verify before you ship
paso gives you five checks:
usepaso validate --strict # Structure + best practices
usepaso inspect # Review what agents will see
usepaso test --all --dry-run # Verify all requests build correctly
usepaso doctor # End-to-end setup check
usepaso serve # Ship it
The tradeoff
The manual approach gives you full control over every handler. You can run database queries, call multiple APIs in sequence, or apply complex business logic before returning a result. paso gives you an MCP server in minutes with no protocol code. If your API follows REST conventions and you want agents to call it, paso is faster. If your MCP server needs custom orchestration logic, write it by hand.
Questions
Does paso work with Cursor and other MCP clients? Yes. paso generates a standard MCP server. Any MCP client that supports the Model Context Protocol can connect to it: Claude Desktop, Cursor, Windsurf, and others.
Can I use paso with Python?
Yes. pip install usepaso. Same CLI, same YAML format, same output. Read more about Python support.
What if my API needs custom logic per request? paso handles REST endpoint mapping. If a capability requires database queries, multi-step orchestration, or custom transformations, write that handler manually using the MCP SDK.
Related:
- The Complete Guide to MCP Servers. the full reference
- OpenAPI to MCP in 60 Seconds if you already have a spec
- Five Ways to Test Before You Ship to verify your server
- How to Make Your API Work with Claude for Claude Desktop setup