smithery-runtime
v1.0.1
Published
Smithery hosted-runtime bootstrap. Wraps user modules (createServer + createAuthAdapter) with the MCP-SDK transport, OTel instrumentation, auth dispatch, and Secrets Store env resolution. Consumed by apps/api at deploy time (string-bundle form) and by SDK
Maintainers
Readme
smithery-runtime
Smithery hosted-runtime bootstrap. Wraps user modules with the MCP-SDK transport, OTel instrumentation, auth dispatch, and Secrets Store env resolution.
Consumed by:
- Smithery's deploy pipeline at deploy time, via the bundled string at
smithery-runtime/bootstrap-source. The deploy workflow concatenates this with youruser-module.jsand uploads the combined script to Cloudflare Workers-for-Platforms. - SDK consumers at test time, via
simulate()fromsmithery-runtime/testing. Drive your user module through the same dispatch logic the platform runs, without going through the full release pipeline.
User module contract
A Smithery hosted server exports:
// default — required
export default function createServer({ config, env }: ServerContext): McpServer
// optional — required for OAuth providers
export const createAuthAdapter: (ctx: { env: Record<string, unknown> }) => Promise<AuthAdapter>
// optional — for HTTP routes outside the MCP path
export const handleHttp?: HandleHttpFnThe runtime's bootstrap inspects these exports and routes incoming requests:
POST /__smithery/auth→createAuthAdapter(auth-dispatch fromapps/auth)- non-
/paths →handleHttp /→createServerthen through MCP-SDK transport
Testing your user module
import { simulate } from "smithery-runtime/testing"
const userModule = await import("./your-bundle.js")
const sim = await simulate({
userModule,
env: {
OAUTH_CLIENT_ID: "...",
OAUTH_CLIENT_SECRET: "...",
OAUTH_AUTHORIZE_URL: "...",
OAUTH_TOKEN_URL: "...",
OAUTH_SCOPES: "...",
},
})
sim.assertShape() // throws if default export isn't a function
sim.hasAuthAdapter() // boolean
const res = await sim.dispatchAuth({
action: "getAuthorizationUrl",
payload: {
callbackUrl: "https://auth.smithery.ai/connect",
state: "test",
codeChallenge: "...",
config: {},
},
})
// res = { status: 200, body: { ok: true, value: { authorizationUrl } } }The simulator mirrors the bootstrap's two-stage error envelope:
| Source | Response |
|---|---|
| createAuthAdapter factory throws | { ok: false, error: "adapter_init_failed", message } (status 500) |
| adapter.{getAuthorizationUrl,exchangeCode,refreshToken} throws | { ok: false, error: "adapter_error", message } (status 500) |
| Adapter method resolves | { ok: true, value } (status 200) |
| Unknown action | { ok: false, error: "unknown_action", message } (status 400) |
| createAuthAdapter not exported | { ok: false, error: "no_auth_adapter", message } (status 500) |
What the bootstrap doesn't catch
simulate() faithfully runs the bootstrap's auth dispatch path, but two opaque pieces remain platform-only and aren't proven by passing tests:
apps/authissuing correctly-shaped synthetic auth-dispatch requests- The gateway attaching stored OAuth tokens on subsequent
tools/callrequests
Both are well-tested production code shared with every other OAuth-proxy provider.
License
MIT.
