rmcp-openapi

Library Listed

Library for converting OpenAPI specifications to MCP tools

Checked · Sources

Downloads
4,330
Dependents
2
Latest release
0.32.1 ·

About

From the project README ·

A Rust workspace providing OpenAPI to MCP (Model Context Protocol) conversion tools.

Overview

This workspace contains two crates that work together to bridge OpenAPI specifications and the Model Context Protocol (MCP):

  • rmcp-openapi (library): Core functionality for converting OpenAPI specifications to MCP tools
  • rmcp-openapi-server (binary): MCP server executable that exposes OpenAPI endpoints as tools

This enables AI assistants to interact with REST APIs through a standardized interface.

Read the full README

Features

  • Automatic Tool Generation: Parse OpenAPI specifications and automatically generate MCP tools for all operations
  • Flexible Spec Loading: Support for both URL-based and local file OpenAPI specifications
  • HTTP Client Integration: Built-in HTTP client with configurable base URLs and request handling
  • Parameter Mapping: Intelligent mapping of OpenAPI parameters (path, query, body) to MCP tool parameters
  • Output Schema Support: Automatic generation of output schemas from OpenAPI response definitions
  • Structured Content: Returns parsed JSON responses as structured content when output schemas are defined
  • Image Response Support: Automatic detection and handling of binary image responses with base64 encoding and MIME type preservation
  • Dual Usage Modes: Use as a standalone MCP server or integrate as a Rust library
  • Transport Support: StreamableHttp transport for MCP communication
  • Comprehensive Testing: Includes integration tests with JavaScript and Python MCP clients
  • Built with Official SDK: Uses the official Rust MCP SDK for reliable protocol compliance
  • Authorization Header Handling: Configurable authorization modes to balance MCP compliance with proxy requirements
  • MCP Tool Annotations: Automatic annotation hints based on HTTP method semantics for better AI understanding
  • Response Transformers: Modify/filter tool call responses before returning to the LLM to reduce token count or improve LLM comprehension

Security

rmcp-openapi acts as a proxy between MCP clients and OpenAPI services, which creates unique security considerations regarding authorization header handling.

Important: Please read the SECURITY.md file for detailed information about:

  • MCP specification compliance
  • Authorization modes (compliant, passthrough-warn, passthrough-silent)
  • Security implications and recommendations
  • Compile-time feature flags for authorization passthrough

By default, the server operates in MCP-compliant mode and does not forward authorization headers. Non-compliant modes require explicit opt-in via compile-time features and runtime configuration.

Contributing

We welcome contributions to rmcp-openapi! Please follow these guidelines:

How to Contribute
  1. Fork the repository on GitLab
  2. Create a feature branch from main: git checkout -b feature/my-new-feature
  3. Make your changes and ensure they follow the project's coding standards
  4. Add tests for your changes if applicable
  5. Run the test suite to ensure nothing is broken: cargo test
  6. Commit your changes with clear, descriptive commit messages
  7. Push to your fork and create a merge request
Development Setup
# Clone your fork
git clone https://gitlab.com/your-username/rmcp-openapi.git
cd rmcp-openapi

# Build the project
cargo build --workspace

# Run tests
cargo test
Code Standards
  • Follow Rust conventions and use cargo fmt to format code
  • Run cargo clippy --all-targets to catch common mistakes
  • Add documentation for public APIs
  • Include tests for new functionality
Reporting Issues

Found a bug or have a feature request? Please report it on our GitLab issue tracker.

Installation

Install Server Binary
cargo install rmcp-openapi-server
Build from Source
# Build entire workspace
cargo build --workspace --release

# Build specific crates
cargo build --package rmcp-openapi --release       # Library only
cargo build --package rmcp-openapi-server --release # Server only
Using as a Library

Add to your Cargo.toml:

[dependencies]
rmcp-openapi = "0.8.2"

Cargo Features

Transport Features
  • rustls-tls (default): Use rustls for TLS support
  • native-tls: Use native TLS implementation
Security Features
  • authorization-token-passthrough: Enable non-compliant authorization header forwarding (see SECURITY.md)
Usage Examples
[dependencies]
rmcp-openapi = "0.13.0"

Usage as a Library

Basic Example
use rmcp_openapi::Server;
use serde_json::Value;
use url::Url;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load OpenAPI specification JSON (from URL, file, or embedded)
    let openapi_json: Value = {
        // Example: Load from embedded JSON string
        let spec_content = r#"{
            "openapi": "3.0.3",
            "info": {"title": "Pet Store", "version": "1.0.0"},
            "paths": {
                "/pets": {
                    "get": {
                        "operationId": "listPets",
                        "responses": {"200": {"description": "List of pets"}}
                    }
                }
            }
        }"#;
        serde_json::from_str(spec_content)?
        // In practice, you'd load from file or URL using your preferred method
    };

    // Create server with OpenAPI specification and base URL
    let mut server = Server::builder()
        .openapi_spec(openapi_json)
        .base_url(Url::parse("https://api.example.com")?)
        .build();

    // Parse the OpenAPI specification and generate tools
    server.load_openapi_spec()?;

    // Get information about generated tools
    println!("Generated {} tools", server.tool_count());
    println!("Available tools: {}", server.get_tool_names().join(", "));

    Ok(())
}
Advanced Example with Custom Configuration
use rmcp_openapi::Server;
use reqwest::header::HeaderMap;
use serde_json::Value;
use url::Url;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load OpenAPI specification from file (async I/O handled by your code)
    let openapi_json: Value = {
        let content = tokio::fs::read_to_string("./api-spec.json").await?;
        serde_json::from_str(&content)?
    };

    let base_url = Url::parse("https://api.example.com")?;

    // Create headers
    let mut headers = HeaderMap::new();
    headers.insert("Authorization", "Bearer token123".parse()?);

    // Create server with custom configuration using builder pattern
    let mut server = Server::builder()
        .openapi_spec(openapi_json)
        .base_url(base_url)
        .default_headers(headers)
        .filters(
            Filters::builder()
                .tags(Filter::Include(vec![
                    "user".to_string(),
                    "pets".to_string(),
                ]))
                .build(),
        )
        .build();

    // Parse specification and generate tools
    server.load_openapi_spec()?;

    // Get tool information
    println!("Generated {} tools", server.tool_count());
    println!("Tool stats: {}", server.get_tool_stats());

    Ok(())
}
Response Transformers

Response transformers allow you to modify/filter tool call responses before they are returned to the LLM. Common use cases include:

  • Removing irrelevant fields to reduce token count
  • Removing null value fields
  • Making responses more suitable for LLM consumption
use rmcp_openapi::{ResponseTransformer, Server};
use serde_json::Value;
use std::sync::Arc;
use url::Url;

/// Transformer that removes null fields from responses
struct RemoveNulls;

impl ResponseTransformer for RemoveNulls {
    fn transform_response(&self, response: Value) -> Value {
        remove_nulls(response)
    }

    fn transform_schema(&self, schema: Value) -> Value {
        // Mark all fields as optional since they may be removed
        mark_fields_optional(schema)
    }
}

fn remove_nulls(value: Value) -> Value {
    match value {
        Value::Object(map) => {
            let filtered: serde_json::Map<String, Value> = map
                .into_iter()
                .filter(|(_, v)| !v.is_null())
                .map(|(k, v)| (k, remove_nulls(v)))
                .collect();
            Value::Object(filtered)
        }
        Value::Array(arr) => Value::Array(arr.into_iter().map(remove_nulls).collect()),
        other => other,
    }
}

fn mark_fields_optional(schema: Value) -> Value {
    // Implementation would remove required fields from schema
    schema
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let openapi_json = serde_json::json!({
        "openapi": "3.0.3",
        "info": {"title": "Pet Store", "version": "1.0.0"},
        "paths": {}
    });

    // Global transformer applied to all tools
    let mut server = Server::builder()
        .openapi_spec(openapi_json)
        .base_url(Url::parse("https://api.example.com")?)
        .response_transformer(Arc::new(RemoveNulls))
        .build();

    server.load_openapi_spec()?;

    // Per-tool override (takes precedence over global transformer)
    // server.set_tool_transformer("verbose-endpoint", Arc::new(AggressiveFilter))?;

    Ok(())
}
Transformer Resolution Order
  1. Per-tool transformer (if set via set_tool_transformer()) takes precedence
  2. Else global server transformer (if set via .response_transformer() in builder)
  3. Else no transformation

Usage as an MCP Server

Basic Usage
# Basic usage with Petstore API
rmcp-openapi-server https://petstore.swagger.io/v2/swagger.json

# See all available options
rmcp-openapi-server --help
MCP Client Connection

The server exposes a StreamableHttp endpoint for MCP clients.

Example with Claude Desktop

Add to your Claude Desktop MCP configuration:

{
  "servers": {
    "petstore-api": {
      "command": "rmcp-openapi-server",
      "args": ["https://petstore.swagger.io/v2/swagger.json", "--port", "8080"]
    }
  }
}
Generated Tools

The server automatically generates MCP tools for each OpenAPI operation:

  • Tool Names: Uses operationId or generates from HTTP method + path
  • Parameters: Maps OpenAPI parameters (path, query, body) to tool parameters
  • Descriptions: Combines OpenAPI summary and description fields
  • Validation: Includes parameter schemas for validation
  • Output Schemas: Automatically generated from OpenAPI response definitions

Example generated tools for Petstore API:

  • addPet: Add a new pet to the store
  • findPetsByStatus: Find pets by status
  • getPetById: Find pet by ID
  • updatePet: Update an existing pet
  • deletePet: Delete a pet
MCP Tool Annotations

The server automatically generates MCP tool annotation hints based on the HTTP method of each OpenAPI operation. These annotations help AI assistants understand the semantic properties of each tool:

HTTP MethodRead-OnlyDestructiveIdempotentExample Use Case
GET, HEAD, OPTIONS✓✗✓Fetching data, metadata
POST✗✗✗Creating new resources
PUT✗✓✓Replacing/updating resources
PATCH✗✓✗Partial updates
DELETE✗✓✓Removing resources

All tools include openWorldHint: true since they interact with external HTTP APIs.

Example: A getPetById tool (GET operation) will have:

  • readOnlyHint: true - Safe read operation
  • destructiveHint: false - Doesn't modify data
  • idempotentHint: true - Multiple calls return same result
  • openWorldHint: true - Calls external API

These annotations follow HTTP semantics (RFC 9110) and the MCP specification.

Output Schema Support

The server now generates output schemas for all tools based on OpenAPI response definitions. This enables:

  1. Type-Safe Responses: MCP clients can validate response data against the schema
  2. Structured Content: When an output schema is defined, the server returns parsed JSON as structured_content
  3. Consistent Format: All responses are wrapped in a standard structure:
{
  "status": 200,           // HTTP status code
  "body": {                // Actual response data
    "id": 123,
    "name": "Fluffy",
    "status": "available"
  }
}

This wrapper ensures:

  • All output schemas are objects (required by MCP)
  • HTTP status codes are preserved
  • Both success and error responses follow the same structure
  • Clients can uniformly handle all responses

Example output schema for getPetById:

{
  "type": "object",
  "properties": {
    "status": {
      "type": "integer",
      "description": "HTTP status code"
    },
    "body": {
      "type": "object",
      "properties": {
        "id": { "type": "integer", "format": "int64" },
        "name": { "type": "string" },
        "status": { "type": "string", "enum": ["available", "pending", "sold"] }
      }
    }
  },
  "required": ["status", "body"]
}

Error Handling

The library distinguishes between two types of errors:

Validation Errors (MCP Protocol Errors)

These occur before tool execution and are returned as MCP protocol errors:

  • ToolNotFound: Requested tool doesn't exist (includes suggestions for similar tool names)
  • InvalidParameters: Parameter validation failed (unknown names, missing required, constraint violations)
  • RequestConstructionError: Failed to construct the HTTP request
Execution Errors (Tool Output Errors)

These occur during tool execution and are returned as structured content in the tool response:

  • HttpError: HTTP error response from the API (4xx, 5xx status codes)
  • NetworkError: Network/connection failures (timeout, DNS, connection refused)
  • ResponseParsingError: Failed to parse the response
Error Response Format

For tools with output schemas, execution errors are wrapped in the standard response structure:

{
  "status": 404,
  "body": {
    "error": {
      "type": "http-error",
      "status": 404,
      "message": "Pet not found"
    }
  }
}

Validation errors are returned as MCP protocol errors:

{
  "code": -32602,
  "message": "Validation failed with 1 error",
  "data": {
    "type": "validation-errors",
    "violations": [
      {
        "type": "invalid-parameter",
        "parameter": "pet_id",
        "suggestions": ["petId"],
        "valid_parameters": ["petId", "status"]
      }
    ]
  }
}

The library provides clear, context-aware error messages for null values that help distinguish between nullable, optional, and required parameters:

For required parameters with null values:

{
  "type": "constraint-violation",
  "parameter": "request_body",
  "message": "Parameter 'name' is required and must be non-null (expected: string)",
  "field_path": "request_body/name",
  "expected_type": "string"
}

For optional parameters with null values:

{
  "type": "constraint-violation",
  "parameter": "request_body",
  "message": "Parameter 'status' must be string when provided (null not allowed, omit if not needed)",
  "field_path": "request_body/status",
  "expected_type": "string"
}

Logging Configuration

The server uses structured logging with the tracing crate for comprehensive observability and debugging.

Log Levels

Set the log level using the RMCP_OPENAPI_LOG environment variable:

# Info level (default for normal operation)
RMCP_OPENAPI_LOG=info rmcp-openapi-server https://petstore.swagger.io/v2/swagger.json

# Debug level (detailed operation info)
RMCP_OPENAPI_LOG=debug rmcp-openapi-server https://petstore.swagger.io/v2/swagger.json

# Trace level (very detailed debugging)
RMCP_OPENAPI_LOG=trace rmcp-openapi-server https://petstore.swagger.io/v2/swagger.json

# Or use the --verbose flag for debug level
rmcp-openapi-server --verbose https://petstore.swagger.io/v2/swagger.json
Log Level Details
  • error: Critical errors that need attention
  • warn: Potential issues or warnings
  • info: Important operational events (server startup, tool registration, HTTP request completion)
  • debug: General debugging information (parameter extraction, tool lookup)
  • trace: Very detailed debugging (detailed parameter parsing)

Note: Request and response bodies are never logged for security reasons.

Structured Logging Format

Logs include structured fields for easy parsing and filtering:

2025-08-19T10:30:45.123Z INFO rmcp_openapi_server::main: OpenAPI MCP Server starting bind_address="127.0.0.1:8080"
2025-08-19T10:30:45.125Z INFO rmcp_openapi::server: Loaded tools from OpenAPI spec tool_count=12
2025-08-19T10:30:45.130Z INFO http_request{tool_name="getPetById" method="GET" path="/pet/{petId}"}: rmcp_openapi::http_client: HTTP request completed status=200 elapsed_ms=45
Module-Specific Logging

You can control logging for specific modules:

# Only HTTP client debug logs
RMCP_OPENAPI_LOG=rmcp_openapi::http_client=debug rmcp-openapi-server spec.json

# Only server info logs, everything else warn
RMCP_OPENAPI_LOG=warn,rmcp_openapi::server=info rmcp-openapi-server spec.json

# Debug parameter extraction and tool generation
RMCP_OPENAPI_LOG=info,rmcp_openapi::tool_generator=debug rmcp-openapi-server spec.json

Examples

See the examples/ directory for usage examples:

  • petstore.sh: Demonstrates server usage with the Swagger Petstore API

Testing

# Run all tests
cargo test

# Run with live API testing
RMCP_TEST_LIVE_API=true cargo test

# Run specific integration tests
cargo test test_http_integration

License

MIT License - see LICENSE file for details.

Runs on
GPU support unknown
What shows it
  • no dependency or feature matches a backend row
Native code
None found
  • No native code found
Python bindings
No Python binding crate found
License
MIT

Compare with similar projects

Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days. ML Rust runs this catalogue, so its closest project always gets a row.

rmcp-openapi beside the projects that share a topic with it
ProjectDownloadsDependentsStarsLatest releaseRuns on (GPU)Native codeLicense
rmcp-openapi This page Listed

Library for converting OpenAPI specifications to MCP tools

4,330 downloads over 30 days 2 Not measured 0.32.1 GPU support unknown No native code found MIT
rig Featured

An opinionated library for building LLM powered applications.

64,485 downloads over 30 days 234 9,096 0.46.0 GPU support unknown Binds native code: libsqlite3-sys (feature sqlite), ring (feature surrealdb) MIT
rust-mcp-sdk Featured

An asynchronous SDK and framework for building MCP-Servers and MCP-Clients, leveraging the rust-mcp-schema for type safe MCP Schema Objects.

29,764 downloads over 30 days 51 199 2.0.0 GPU support unknown No native code found MIT
adk-rust Listed

Rust Agent Development Kit - Build AI agents in Rust with modular components for models, tools, memory, and more.

5,419 downloads over 30 days 25 743 2.3.0 CUDA, Metal Binds native code: alsa-sys (feature desktop-audio), audiopus_sys (feature openai-webrtc), cxx (feature livekit), espeak-rs-sys (feature kokoro), libsqlite3-sys (feature sqlite) and 4 more Apache-2.0
memvid Listed

Core library for Memvid v2, a crash-safe, deterministic, single-file AI memory.

5,276 downloads over 30 days 2 17,386 2.0.140 CUDA, Metal Binds native code: zstd-sys, link-cplusplus (feature llama-cpp), llama_cpp_sys (feature llama-cpp), ort-sys (feature logic_mesh) Apache-2.0
llm-chain Listed Dormant

A library for running chains of LLMs (such as ChatGPT) in series to complete complex tasks, such as text summation.

2,575 downloads over 30 days 1 1,602 0.13.0 CUDA Binds native code: llm-chain-llama-sys. Build uses a native toolchain: bindgen MIT
axp App Listed

An open protocol that gives AI agents a secure, sandboxed place to run code and use tools — with lean context usage. AXP works alongside MCP

Not measured Not applicable 3 Not measured Not measured Not measured Apache-2.0
Why ListedNo archive, New or Featured rule places it, so it is Listed.

What it lacks for Featured

  • The check holds no value for GitHub stars, GitHub forks, Contributors with a GitHub account. A gate that lacks its signal does not pass.

What else the check found

  • The last activity is on 2 Nov 2026, 7 days ago. The rules expect an update within 365 days. Dormancy rule

Not measured: GitHub stars, GitHub forks, Contributors with a GitHub account.

Check notes

  • repo host gitlab.com has no facts source: repository signals unknown
  • the repo manifest crates/rmcp-openapi/Cargo.toml declares the crate, and no release commit proves the link

Exact rules: Listed, Featured library. The check on applied thresholds version 9.

Every rule outcome, as the check records it
  • expected-interval: version 0.32.1 base 300, raised to the 365-day floor: E = 365 days
  • dormancy: last activity 2026-11-02, 7 days ago, E = 365: active
  • featured: not featured: unknown stars, forks, contributors
  • listed: listed: no archive, new or featured rule applies
Crates2 crates

Measurements and sources

Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.

Sources4 sources
Adoption2 active dependents, 2 new dependents in 365 days1 not measured
Adoption measurements
SignalValueSourceFetchedCounts
Active dependents2crates.io database dumpYes
New dependents (365 days)2crates.io database dumpYes
Peak active dependents2crates.io database dumpYes
Active dependents as a share of the peak1crates.io database dumpYes
Binary downloads per releaseNot measuredYes
MaintenanceNot measured
Maintenance measurements
SignalValueSourceFetchedCounts
Latest human commitNot measuredYes
Latest release2026-11-02crates.io database dumpYes
First release2025-07-09crates.io database dumpYes
Latest version0.32.1crates.io database dumpYes
Days the oldest outside issue waits for a replyNot measuredYes
Maintainer replies over 365 daysNot measuredYes
Repository archivedNot measuredYes
Repository goneNot measuredYes
Release commit or manifest in the repotruerepo manifestYes
Published docstruecrates.io database dumpYes
Latest release fails cargo checkNot measuredYes
Build check dateNot measuredYes
Build check toolchainNot measuredYes
SecurityUnpatched vulnerabilities: none
Security measurements
SignalValueSourceFetchedCounts
Unpatched vulnerabilities past the grace windownoneRustSec advisory databaseYes
RustSec unmaintained advisorynoneRustSec advisory databaseYes
Deprecation noticenonecrates.io database dumpYes
Deprecated requirementnonecrates.io database dumpYes
Distribution97 releases, 4,330 downloads over 30 days5 not measured
Distribution measurements
SignalValueSourceFetchedCounts
Releases97crates.io database dumpYes
Downloads over 30 days4330crates.io database dumpYes
Binary downloads over the last 3 releasesNot measuredYes
Homebrew installs on request over 365 daysNot measuredYes
Repology repository familiesNot measuredYes
Docker pull growth over 90 daysNot measuredYes
Docker pulls at the growth baselineNot measuredYes
Every version a prereleasefalsecrates.io database dumpYes
Every version 0.0.xfalsecrates.io database dumpYes
Every version yankedfalsecrates.io database dumpYes
Crate deleted from crates.iofalsecrates.io database dumpYes
RepositoryNot measured
Repository measurements
SignalValueSourceFetchedCounts
GitHub starsNot measuredYes — one part of the repository route
GitHub forksNot measuredYes — one part of the repository route
Contributors with a GitHub accountNot measuredYes — one part of the repository route
Commits on the default branchNot measuredNo
Repository's main crate left out of the entrynonecrates.io database dumpYes
Facts that never count
Facts that never count measurements
SignalValueSourceFetchedCounts
LicenseMITcrates.io database dumpNo
Runs on, from listed crates and their direct dependenciesGPU support unknowncrates.io database dumpNo
What shows each backendno dependency or feature matches a backend rowcrates.io database dumpNo
Native code, from listed crates and their dependenciesNo native code foundcrates.io database dumpNo
Python bindings, from direct dependencies of listed cratesNo Python binding crate foundcrates.io database dumpNo
Crates that depend on a listed crate directly2crates.io database dumpNo

Corrections and review history

Report a correction

No correction applies to this entry.

Judgments

A judgment enters only through an approved issue and a merged pull request.

No judgment applies to this entry.