AgentStack
MCP verified MIT Self-run

Rust Mcp Core

mcp-nullablevariant-rust-mcp-core · by nullablevariant

A config-driven MCP server core built on the official Rust SDK. Define tools, auth, prompts, resources, and HTTP behavior in YAML or JSON configuration -- the library handles execution, validation, and protocol compliance with minimal Rust code.

No reviews yet
0 installs
12 views
0.0% view→install

Install

$ agentstack add mcp-nullablevariant-rust-mcp-core

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Rust Mcp Core? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

rust-mcp-core

[](LICENSE) []() []() []() []()

A config-driven MCP server core built on the official Rust SDK (rmcp). Define tools, auth, prompts, resources, and HTTP behavior in YAML or JSON configuration -- the library handles execution, validation, and protocol compliance with minimal Rust code.

Fully implements the Model Context Protocol specification (2025-11-25).

Tested AI CLI compatibility

This library has been tested and verified with:

  • Claude Code
  • Codex CLI
  • Gemini CLI

What this is for

  • Reduced boilerplate. Stand up a spec-compliant MCP server by writing configuration instead of protocol plumbing. A working server with HTTP tools needs only a YAML file and a few lines of Rust.
  • Built-in HTTP API tooling. Define outbound HTTP tool calls entirely in config with URL templating, header injection, query parameters, and structured response mapping.
  • Authentication out of the box. Supports inbound bearer token, JWT/JWKS validation, and OAuth token introspection with scope enforcement and WWW-Authenticate challenges. Also supports outbound upstream auth for HTTP tools (none, bearer, basic, oauth2) including OAuth2 client-credentials/refresh-token grants with optional mTLS at the token endpoint.
  • Extensible plugin system. When config-driven behavior is not enough, register plugins for custom tool logic, auth validation, prompt/resource providers, completion providers, and HTTP router extensions.
  • Both transports. Works with stdio and streamable HTTP transports.
  • Works with the official SDK. This library builds on rmcp and uses its transport runtime, ServerHandler trait, and MCP type definitions directly -- no reimplementation.

What this is not

  • Not a toolbox. This library does not include built-in tools like file servers, database connectors, or shell executors. The only built-in execution type is outbound HTTP calls. Any other tool behavior must be provided through a tool plugin.

How this complements the rmcp SDK

The rmcp SDK provides low-level MCP protocol primitives: transport, message framing, the ServerHandler trait, and type definitions. rust-mcp-core builds on top of that to provide a config-driven server framework.

What the SDK provides (used directly, not reimplemented):

  • Transport runtime (stdio + streamable HTTP)
  • ServerHandler trait and JSON-RPC dispatch
  • All MCP model types (Tool, Prompt, Resource, Task, etc.)
  • Default handler implementations (e.g., ping)

What rust-mcp-core adds:

  • Config loading with ${env:ENV} expansion and JSON schema validation
  • Tool execution engine (HTTP tools with templating + plugin tools)
  • Output schema validation and structured content rendering
  • Plugin registries (tool, auth, prompt, resource, completion, HTTP router)
  • Config-driven prompts, resources, and completion providers
  • Auth middleware (bearer, JWT/JWKS, introspection, scope enforcement)
  • Task store with peer isolation, TTL, cooperative cancellation, and status notifications

The complementary relationship is clearest with auth. The SDK provides client-side OAuth (PKCE flows, token acquisition, credential storage, automatic refresh). rust-mcp-core provides server-side auth (token validation, scope enforcement, WWW-Authenticate challenges, and the protected resource metadata endpoint). An rmcp client obtains a token and sends it; a rust-mcp-core server receives and validates it.

MCP specification compliance

This library implements the following capabilities from the MCP 2025-11-25 specification:

Server capabilities

| Capability | Description | |------------|-------------| | Tools | Config-driven and plugin-driven tool definitions. Supports input/output schema validation, list-changed notifications, and pagination. | | Prompts | Inline (config-driven) and plugin-driven prompt providers with argument validation, template rendering, and list-changed notifications. | | Resources | Inline and plugin-driven resource providers with subscribe/unsubscribe support and list-changed notifications. | | Completion | Autocompletion for prompt and resource template arguments from inline value lists or plugin sources. | | Logging | Structured log messages via notifications/message with syslog severity levels. Clients control notification threshold via logging/setLevel. | | Progress | Long-running operation tracking via notifications/progress with rate limiting and monotonic progress enforcement. | | Cancellation | In-progress request termination. Cancellable tools are aborted automatically; non-cancellable tools receive a fresh token and run to completion. | | Tasks | Experimental task utility for long-running operations. Supports task-augmented tool calls, polling, deferred result retrieval, cooperative cancellation, TTL, peer isolation, and status notifications. | | Pagination | Cursor-based pagination for tools/list and other list operations. |

Server-initiated client features

These require a registered plugin to invoke. The framework handles config validation and client capability negotiation; plugin code calls helpers via params.ctx (params: PluginCallParams).

| Feature | Description | |---------|-------------| | Sampling | Request LLM text/image/audio generation from the client, optionally with tool use. | | Roots | Query the client for filesystem root boundaries. | | Elicitation | Request structured user input (form mode) or trigger out-of-band interactions like OAuth flows (URL mode). |

Protocol fundamentals

  • Capability negotiation during initialize handshake
  • Ping/pong for connection health
  • Feature-gate enforcement: disabled features return method-not-found and are omitted from capability advertisement

Compile-time features

All features are enabled by default. Disable with default-features = false and enable selectively.

| Feature | Description | Implies | |---------|-------------|---------| | streamable_http | HTTP transport (server.transport.mode=streamable_http) and HTTP router plugin surface | -- | | http_hardening | Streamable HTTP hardening middleware (max_request_bytes, inbound rate limits, session abuse controls, panic/sensitive-header guards) | streamable_http | | auth | Auth middleware: bearer, JWT/JWKS, OAuth introspection, scope enforcement | streamable_http | | http_tools | Built-in outbound HTTP tool execution (tools.items[].execute.type=http) plus upstream auth (none, bearer, basic, oauth2) | -- | | prompts | prompts/list + prompts/get capability | -- | | resources | resources/list, resources/read, resources/templates/list, subscribe/unsubscribe | -- | | completion | completion/complete for prompt/resource argument autocompletion | -- | | client_logging | logging/setLevel + notifications/message | -- | | progress_utility | notifications/progress via params.ctx.notify_progress(...) | -- | | tasks_utility | Experimental: task-augmented tools/call, tasks/get, tasks/list, tasks/cancel | -- | | client_features | Server-initiated client helpers via params.ctx: request_roots(), request_sampling(), request_elicitation() | -- |

Minimal build (stdio + plugin tools only):

cargo build --no-default-features

If config references a disabled feature, startup fails with a clear error message.

Installation

From crates.io:

[dependencies]
rust-mcp-core = "0.1"

From GitHub:

[dependencies]
rust-mcp-core = { git = "https://github.com/nullablevariant/rust-mcp-core" }

Quick start

Configuration

Create a YAML config file. This example defines one HTTP tool and one inline prompt over streamable HTTP transport. Stdio transport is also supported by setting transport.mode: stdio. If you want a full copy/paste starter with every supported field, use [mcp_config.template.yml](mcp_config.template.yml).

version: 1
server:
  host: 0.0.0.0
  port: 3000
  endpoint_path: /mcp
  logging:
    level: info
  transport:
    mode: streamable_http  # also supports: stdio
  auth:
    enabled: false

client_logging:
  level: info

upstreams:
  api:
    base_url: ${env:API_BASE_URL}

tools:
  items:
    - name: api.list_items
      description: List items from the API
      input_schema:
        type: object
        properties:
          query:
            type: string
      execute:
        type: http
        upstream: api
        method: GET
        path: /items
        query:
          q: "${query}"
      response:
        type: structured
        template:
          items: "${$.items}"
        fallback: text

Usage

use std::path::PathBuf;
use rust_mcp_core::{load_mcp_config_from_path, runtime, PluginRegistry};
use rust_mcp_core::McpError;

#[tokio::main]
async fn main() -> Result {
    let config = load_mcp_config_from_path(PathBuf::from("config/mcp_config.yml"))?;
    let plugins = PluginRegistry::new();
    runtime::run_from_config(config, plugins).await
}

With a custom tool plugin:

use rust_mcp_core::{load_mcp_config_from_path, runtime, McpError, PluginRegistry};

#[tokio::main]
async fn main() -> Result {
    let config = load_mcp_config_from_path("config/mcp_config.yml".into())?;
    let plugins = PluginRegistry::new()
        .register_tool(MyToolPlugin)?;
    runtime::run_from_config(config, plugins).await
}

Any plugin referenced by config (for example via tools.items[].execute.plugin or provider plugin fields) must be both declared in config plugins[] and registered in PluginRegistry. Extra registered plugins that are not declared are ignored with a warning. See [Plugin Guide](docs/PLUGINS.md) for the full plugin contract.

Config reload (consumer-managed)

runtime::run_from_config(...) is a convenience entrypoint and does not expose reload control.

If you need config reload:

  • use runtime::build_runtime(...),
  • keep the returned runtime handle,
  • load updated config input yourself,
  • call runtime.reload_config(new_config).await.

rust-mcp-core does not automatically watch config files or trigger reloads.

use std::path::PathBuf;
use rust_mcp_core::{load_mcp_config_from_path, runtime, McpError, PluginRegistry};

#[tokio::main]
async fn main() -> Result {
    let initial = load_mcp_config_from_path(PathBuf::from("config/mcp_config.yml"))?;
    let runtime = runtime::build_runtime(initial, PluginRegistry::new()).await?;

    // Consumer-owned trigger (file watcher, signal, admin endpoint, etc.)
    let updated = load_mcp_config_from_path(PathBuf::from("config/mcp_config.reload.yml"))?;
    runtime.reload_config(updated).await?;

    runtime.run().await
}

Upstream auth for HTTP tools

upstreams..auth controls outbound auth for tools.items[].execute.type=http:

  • type: none -> no auth header injected.
  • type: bearer -> injects Authorization: Bearer .
  • type: basic -> injects HTTP Basic auth.
  • type: oauth2 -> acquires/caches access tokens via client_credentials or refresh_token grant, injects bearer token, and can retry once on 401 after forced refresh.

Token-endpoint secrets support inline, env, and path sources. Optional token-endpoint mTLS is configured via auth.mtls (client_cert, client_key, optional ca_cert). See [Auth](docs/AUTH.md) and [Config Schema](docs/CONFIG_SCHEMA.md) for full field-level details.

Streamable HTTP request flow (manual clients)

When server.transport.mode=streamable_http, manual clients (for example curl) should follow this flow:

  • Send initialize first, capture MCP-Session-Id from response headers when sessions are enabled.
  • Send notifications/initialized on the same endpoint with the same session header.
  • Include Accept: application/json, text/event-stream on POST requests.
  • If a client sends MCP-Protocol-Version on HTTP requests:
  • default server.transport.streamable_http.protocol_version_negotiation.mode: strict keeps the header; unsupported values return HTTP 400.
  • mode: negotiate keeps known RMCP versions and drops unknown versions before rmcp validation.
  • Reuse the MCP-Session-Id header on subsequent requests in session modes that require it.
  • Expect streamable HTTP responses to be SSE-framed.

Why this exists:

  • rust-mcp-core is coupled to RMCP's known protocol version set.
  • Some AI clients send a newer date-stamped MCP-Protocol-Version header than the bundled RMCP supports.
  • In strict mode this fails fast with 400 (explicit mismatch).
  • In negotiate mode unknown header values are removed so requests can continue without forcing a reverse proxy/header rewrite layer.
  • This is a compatibility layer for HTTP headers only; MCP capability/version negotiation still happens in normal initialize flow.

For client-specific interoperability constraints (for example schema subset limits, structuredContent shape expectations, protocol-header normalization, and Accept requirements), see [AI Client Compatibility](docs/AICLIENTCOMPATIBILITY.md).

Streamable HTTP hardening (http_hardening)

With http_hardening enabled, core can enforce:

  • inbound request body cap (max_request_bytes, HTTP 413 on exceed),
  • general inbound rate limiting (rate_limit, HTTP 429 on exceed),
  • session abuse controls (max_sessions, idle_ttl_secs, max_lifetime_secs, creation_rate),
  • panic-to-500 and sensitive-header transport guards.

See [Transport](docs/TRANSPORT.md) and [Config Schema](docs/CONFIG_SCHEMA.md) for field-level behavior and validation rules.

Plugin logging helper

Tool plugins can emit server logs and/or MCP client notifications:

let ctx = params.ctx;
ctx.log_event(rust_mcp_core::LogEventParams {
    level: rust_mcp_core::mcp::LoggingLevel::Info,
    message: "synced records".to_owned(),
    data: Some(serde_json::json!({"count": 42})),
    channels: &[rust_mcp_core::LogChannel::Server, rust_mcp_core::LogChannel::Client],
}).await?;
  • LogChannel::Server writes to the server's tracing output (controlled by server.logging.level).
  • LogChannel::Client sends an MCP notifications/message to the client (controlled by client_logging.level and logging/setLevel).
  • logging/setLevel does not change server tracing verbosity. It only updates client notification filtering.
  • On config reload, server.logging.level updates are applied when rust-mcp-core owns the global tracing subscriber. If a host app already set the global subscriber, rust-mcp-core logs a warning and cannot override it.

Plugin outbound OAuth2 helper (http_tools)

When http_tools is enabled and an upstream uses auth.type: oauth2, plugins can reuse native token acquisition via PluginContext:

let token = params.ctx.upstream_access_token("partner_api", false).await?;
let (name, value) = params
    .ctx
    .upstream_bearer_header("partner_api", false)
    .await?;

upstream_bearer_header returns ("Authorization", "Bearer "). force_refresh=true forces one refresh attempt through the same outbound OAuth2 token manager used by HTTP tools.

For outbound requests to configured upstreams, plugins can use the built-in helper path:

let response = params.ctx.send(
    "partner_api",
    rust_mcp_core::OutboundHttpRequest {
        method: "GET".to_owned(),
        url: "/partners".to_owned(),
        ..rust_mcp_core::OutboundHttpRequest::default()
    },
).await?;

This path applies the same outbound default-resolution behavior as built-in HTTP tools. Use send_with(...) for per-call auth overrides and send_raw(...) when you intentiona

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.