Skip to main content

Overview

Most MCP servers need user-provided values like API keys, preferences, or settings. How these values reach your server depends on how it’s deployed:
Server TypeHow Users Provide Config
Remote (HTTP)Query parameters or HTTP headers
Local (stdio)Command-line arguments
When you define a configuration schema, Smithery automatically:
  • Generates an OAuth UI form for remote servers
  • Passes values to your server in the appropriate format
  • Validates inputs and applies defaults
Configuration schemas are limited to 20 fields and 1KB total size. Keep schemas focused on essential settings.

Defining Your Schema

Export a configSchema using Zod to declare what configuration your server accepts:
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod/v4";
import type { ServerContext } from "@smithery/sdk";

export const configSchema = z.object({
  apiKey: z.string()
    .meta({ "x-from": { header: "x-api-key" } })
    .describe("Your API key"),
  model: z.string().default("gpt-4").describe("Model to use"),
  temperature: z.number().min(0).max(1).default(0.7).describe("Temperature"),
});

export default function createServer({
  config,
}: ServerContext<z.infer<typeof configSchema>>) {
  const server = new McpServer({
    name: "My Server",
    version: "1.0.0",
  });

  // Access user-provided values
  console.log(`API Key: ${config.apiKey}`);
  console.log(`Model: ${config.model}`);

  return server.server;
}
Smithery extracts this schema automatically — no additional configuration needed.

Config Transport (x-from and x-to)

The x-from and x-to extensions control how config values flow through the gateway:

x-from — Where Smithery reads config

Specifies where Smithery looks for the value when a user connects:
z.string().meta({
  "x-from": { header: "x-api-key" }  // Read from header
})

z.string().meta({
  "x-from": { query: "model" }  // Read from query param
})
Default: If no x-from is specified, defaults to { query: "<propertyName>" }.

x-to — Where Smithery sends config to upstream

Specifies how Smithery forwards the value to your upstream server. Use this when your server expects a different header name than what clients provide:
z.string().meta({
  "x-from": { header: "api-key" },      // Client sends: api-key header
  "x-to": { header: "Authorization" }   // Upstream receives: Authorization header
})
This is useful when:
  • Your upstream server expects an Authorization header, but you can’t use authorization as x-from (it’s reserved for Smithery OAuth)
  • You want to rename headers for compatibility with existing APIs
  • You need to map user-friendly parameter names to technical header names
Default: If no x-to is specified, values are forwarded using the same location as x-from.

Example: PostHog API Key

export const configSchema = z.object({
  posthogApiKey: z.string()
    .meta({
      "x-from": { header: "posthog-api-key" },  // Client provides this header
      "x-to": { header: "Authorization" }       // PostHog expects Authorization
    })
    .describe("Your PostHog API key"),
});
With this config:
  • Clients connect with header posthog-api-key: sk-xxx
  • Your server receives header Authorization: sk-xxx

Type Support

Only simple types support x-from:
  • string
  • number
  • boolean
Nested objects and arrays are not supported — only flat schemas are allowed.

Reserved Headers

The following headers cannot be used as x-from sources:
  • authorization — Used for Smithery OAuth
  • cookie — Reserved for session management
  • cf-* — Cloudflare infrastructure headers
  • smithery-* — Internal service headers
These restrictions only apply to x-from. You can use any header name (including Authorization) in x-to to forward values to your upstream server.

How Configuration Reaches Your Server

For URL-published servers, Smithery Gateway passes through all query parameters and headers to your upstream server.
GET /mcp?apiKey=sk-xxx&model=gpt-4
x-api-key: sk-xxx
Your server receives headers and query params directly — Smithery proxies them as-is.

Type Coercion

Since query parameters, headers, and CLI arguments are strings, Smithery automatically coerces values:
Schema TypeCoercion
stringNo coercion
numberNumber(value) — fails if non-numeric
boolean"true" / "1"true, "false" / "0"false

Best Practices

  • Use clear descriptions — These become form labels and help text
  • Set sensible defaults — Minimize required fields
  • Use enums for fixed options — Creates dropdown menus in the UI
  • Keep required fields minimal — Only require what’s essential
  • Use headers for secrets — Configure "x-from": { header: "x-api-key" } for API keys
  • Never log sensitive values — Treat keys and tokens as secrets
  • Validate server-side — Don’t rely solely on client validation

Troubleshooting

  • Export configSchema from the same file as createServer
  • Ensure schema is a valid Zod object
  • Accept { config } in your createServer function
  • Use z.infer<typeof configSchema> for typing

Common Questions

No — configuration is bound at connection time. A new connection is needed for different settings.
Yes — use .optional() or provide .default() values.
View the API tab on any server’s page on Smithery.

See Also

  • Build — Build an MCP server with Smithery CLI
  • Publish — Publish your MCP on Smithery