Skip to main content

MCP Service -- Host and Manage MCP Servers

Host production MCP servers with OAuth authentication, tool discovery, capability negotiation, and streamable HTTP transport. Integrate with Claude Desktop, Claude Code, and any MCP-compatible client.

TL;DR: systemprompt.io hosts production MCP (Model Context Protocol) servers as standalone Rust binaries with OAuth authentication, typed tool schemas, and streamable HTTP transport. Each server registers tools that AI models discover and invoke automatically. The platform handles routing, authentication, RBAC enforcement, artifact persistence, and integration with clients like Claude Desktop and Claude Code.


What MCP Is and Why It Matters

AI models reason well but cannot act on the world directly. They cannot query databases, call APIs, manage files, or interact with external systems without tooling. The Model Context Protocol (MCP) is an open standard that solves this by defining how AI clients discover and invoke tools hosted on remote servers.

MCP matters because it replaces ad-hoc tool integrations with a single protocol. Instead of building custom integrations for every AI client, you build one MCP server and every compatible client -- Claude Desktop, Claude Code, or any other -- can use it immediately.

systemprompt.io takes this further. Rather than running MCP servers as local stdio processes, the platform hosts them as authenticated HTTP services. This means your tools are available from anywhere, secured by OAuth, and managed through a unified control plane.

Architecture

MCP in systemprompt.io follows a client-server model with three layers:

┌─────────────────────┐     ┌──────────────────────┐     ┌─────────────────┐
│  AI Client          │     │  systemprompt.io      │     │  MCP Server     │
│  (Claude Desktop,   │────▶│  Router + OAuth       │────▶│  (Rust binary)  │
│   Claude Code)      │     │  /api/v1/mcp/{name}/  │     │  port 5010-5999 │
└─────────────────────┘     └──────────────────────┘     └─────────────────┘
  1. MCP Server -- A Rust binary implementing ServerHandler from the rmcp crate. Exposes tools, resources, and capabilities over the MCP protocol.
  2. Router -- An Axum HTTP layer that terminates streamable HTTP transport, enforces OAuth, and proxies requests to the correct server process.
  3. AI Client -- Any MCP-compatible client that connects over HTTP, discovers tools, and calls them during conversations.

Each server runs as an independent process on its own port within the configured range (default 5000-5999). The platform router maps /api/v1/mcp/{server-name}/mcp to the correct backend.

Server Hosting with OAuth

Every MCP server is configured with a YAML manifest and an optional OAuth policy. The platform enforces authentication before any tool call reaches your handler code.

Manifest Configuration

Each server has a manifest.yaml in its extension directory:

# extensions/mcp/marketplace/manifest.yaml
extension:
  type: mcp
  name: marketplace
  binary: systemprompt-mcp-marketplace
  description: Marketplace MCP Server - Manage skills, secrets, and sync
  port: 5050
  build_type: workspace
  enabled: true

OAuth Configuration

OAuth is configured per-server in the service YAML files under services/mcp/:

mcp_servers:
  marketplace:
    binary: "systemprompt-mcp-marketplace"
    package: "marketplace"
    port: 5050
    endpoint: "http://localhost:8080/api/v1/mcp/marketplace/mcp"
    enabled: true
    display_in_web: true
    description: "Marketplace MCP Server"

    oauth:
      required: true
      scopes: ["admin"]
      audience: "mcp"
OAuth Field Type Description
required boolean Enforce authentication on all tool calls
scopes string[] Required OAuth scopes (e.g., admin, user, tools:read)
audience string Expected JWT audience claim
client_id string Optional restriction to a specific OAuth client

When oauth.required is true, the platform middleware calls enforce_rbac_from_registry before dispatching to the server handler. Unauthenticated requests are rejected before your code runs.

Server Configuration Fields

Field Type Description
binary string Compiled binary name
package string Cargo package/crate name
port number Listening port (5000-5999 range)
endpoint string Full API endpoint URL
enabled boolean Whether the server is active
display_in_web boolean Show in the admin web UI
description string Human-readable server description

Tool Discovery and Capability Negotiation

MCP servers advertise their capabilities during the initialization handshake. When a client connects, it receives the server's protocol version, supported capabilities, and metadata.

Initialization Response

Every server implements get_info() which returns:

  • Protocol version -- Currently V_2025_06_18 (the latest version supported by the rmcp crate)
  • Capabilities -- Which features the server supports (tools, resources, experimental)
  • Server info -- Name, version, icons, website URL
  • Instructions -- Guidance for the AI model on how to use the server

Tool Listing

Clients call tools/list to discover available tools. Each tool is defined with:

  • Name and title -- Machine-readable identifier and human-readable label
  • Description -- Detailed usage instructions for the AI model
  • Input schema -- JSON Schema defining required and optional parameters
  • Output schema -- JSON Schema describing the response structure
  • UI metadata -- Hints for rendering in visual clients

Resource Listing

Servers can also expose resources (static or dynamic content) through resources/list and resources/read. For example, the systemprompt server exposes an artifact viewer as a UI resource with content security policies.

Discovery Endpoints

Endpoint Description
/api/v1/mcp/registry List all registered MCP servers
/api/v1/mcp/{server}/mcp Individual server endpoint (streamable HTTP)

The AI service uses the registry endpoint to auto-discover servers and make their tools available to agents.

Building MCP Servers

MCP servers in systemprompt.io are Rust binaries built with the rmcp crate. The platform provides helper utilities for routing, OAuth enforcement, response building, and artifact persistence.

Project Structure

extensions/mcp/marketplace/
├── Cargo.toml
├── manifest.yaml
└── src/
    ├── main.rs              # Entry point: config, listener, router
    ├── lib.rs               # Re-exports server type
    ├── server/
    │   ├── mod.rs           # ServerHandler implementation
    │   └── constructor.rs   # Server initialization with services
    └── tools/
        ├── mod.rs           # Tool registry and dispatch
        ├── create_skill/
        │   ├── mod.rs       # Tool definition (schema, metadata)
        │   ├── schema.rs    # Input/output JSON schemas
        │   └── handler.rs   # Tool execution logic
        └── list_skills/
            └── ...

Entry Point

The main.rs initializes configuration, creates the server, builds the router, and starts listening:

let server = MarketplaceServer::new(
    ctx.db_pool().clone(),
    service_id.clone(),
    ctx.clone(),
).await?;

let router = systemprompt::mcp::create_router(server, ctx.db_pool());
let listener = TcpListener::bind(&addr).await?;
axum::serve(listener, router).await?;

The create_router function wraps the server handler with streamable HTTP transport and OAuth middleware.

Implementing ServerHandler

Each server implements the ServerHandler trait from rmcp:

impl ServerHandler for MarketplaceServer {
    fn get_info(&self) -> ServerInfo { /* capabilities */ }

    async fn initialize(&self, request, ctx) -> Result<InitializeResult, McpError> { ... }
    async fn list_tools(&self, request, ctx) -> Result<ListToolsResult, McpError> { ... }
    async fn call_tool(&self, request, ctx) -> Result<CallToolResult, McpError> { ... }
    async fn list_resources(&self, request, ctx) -> Result<ListResourcesResult, McpError> { ... }
    async fn read_resource(&self, request, ctx) -> Result<ReadResourceResult, McpError> { ... }
}

Inside call_tool, enforce authentication before processing:

let auth_result = enforce_rbac_from_registry(&ctx, self.service_id.as_str())
    .await?
    .expect_authenticated("requires OAuth")?;

Defining Tools

Each tool has a schema module and a handler module. The schema defines typed input/output:

pub fn input_schema() -> serde_json::Value {
    serde_json::json!({
        "type": "object",
        "properties": {
            "skill_id": {
                "type": "string",
                "description": "The skill UUID to analyze"
            }
        },
        "required": ["skill_id"]
    })
}

Tools are registered in the list_tools() function of the tools module and dispatched by name in call_tool.

Available MCP Servers

systemprompt.io ships with three MCP servers:

Server Port Tools Purpose
systemprompt 5010 systemprompt (CLI executor) Execute CLI commands with artifact rendering
content-manager 5040 research_blog, create_blog_post, generate_featured_image AI-powered content creation pipeline
marketplace 5050 create_skill, update_skill, list_skills, analyze_skill, manage_secrets, get_secrets, sync_skills Skill and secret management

Integration with Claude Desktop and Claude Code

Claude Desktop

Add your MCP server to the Claude Desktop configuration file:

{
  "mcpServers": {
    "systemprompt": {
      "url": "https://your-domain.com/api/v1/mcp/systemprompt/mcp",
      "transport": "streamable-http"
    }
  }
}

For local development, use http://localhost:8080/api/v1/mcp/systemprompt/mcp.

Claude Code

Claude Code supports MCP servers through its configuration. Add the server URL and it will discover tools automatically during sessions.

Any MCP Client

Any client that supports the MCP protocol over streamable HTTP can connect. The server responds to standard MCP methods: initialize, tools/list, tools/call, resources/list, and resources/read.

Security Model

MCP security in systemprompt.io operates at multiple layers:

  1. Transport security -- HTTPS in production. All endpoints behind TLS.
  2. OAuth authentication -- JWT tokens validated on every tool call. Configurable scopes and audience per server.
  3. RBAC enforcement -- The enforce_rbac_from_registry middleware checks the token against the server's registered OAuth policy before dispatching to the handler.
  4. Scope-based access control -- Common scope patterns:
    • admin -- Full administrative access
    • user -- Standard user operations
    • tools:read -- Read-only tool access
    • tools:write -- Tool execution permission
  5. Per-server isolation -- Each server runs as a separate process with its own port, database pool, and service dependencies.
  6. Secret encryption -- Secrets managed through MCP tools are encrypted at rest in the database.
  7. Content Security Policy -- UI resources served by MCP servers include strict CSP headers.

Managing MCP Servers

Syncing Configuration

After modifying MCP server YAML files, sync to the database:

systemprompt cloud sync local mcp --direction to-db -y

Building Servers

MCP servers are built as part of the workspace:

# Build all MCP servers
systemprompt build mcp

# Build a specific server
cargo build --package systemprompt-mcp-marketplace --release

CLI Reference

Command Description
systemprompt plugins mcp list List MCP server configurations
systemprompt plugins mcp status Show running server status with binary info
systemprompt plugins mcp show <name> Show details for a specific server
systemprompt plugins mcp tools <name> List tools from a running server
systemprompt plugins mcp call <tool> Execute a tool on an MCP server
systemprompt plugins mcp validate Validate MCP connection
systemprompt plugins mcp logs <name> View MCP server logs
systemprompt plugins mcp list-packages List package names for build

Run systemprompt plugins mcp <command> --help for detailed options.

Troubleshooting

Server not discovered -- Verify enabled: true in the manifest. Check that the endpoint is accessible and the AI service has auto_discover: true set.

Tool execution fails -- Inspect server logs with systemprompt plugins mcp logs <name>. Confirm the OAuth token carries the required scopes. Increase timeout settings for long-running tools.

Authentication errors -- Ensure the client token includes the correct scopes and audience. Cross-check the oauth block in the server's YAML configuration.

Connection refused -- Confirm the server process is running on the expected port. Use systemprompt plugins mcp status to check binary and port status.

Build failures -- Run systemprompt plugins mcp list-packages to verify package names. Check that Cargo.toml dependencies are up to date.