Spice integrates with tools and services using the Model Context Protocol (opens in a new tab) (MCP). MCP tools can be configured to run internally or connect to external servers over HTTP using the Streamable HTTP (opens in a new tab) transport.
MCP helps extend the capabilities of the Spice runtime by enabling integration with external tools and services. This includes:
Spice is dual-era. It serves the 2026-07-28 (opens in a new tab) revision and still answers the legacy initialize handshake, so existing clients on earlier revisions — including 2025-03-26 (opens in a new tab) — keep working unchanged.
| Era | How a client talks to Spice | Session |
|---|---|---|
Modern (2026-07-28) | server/discover, then tools/list and tools/call with per-request _meta — no initialize first | Sessionless. No Mcp-Session-Id. |
Legacy (2025-11-25 and earlier, including 2025-03-26) | initialize, then notifications/initialized | Spice mints an Mcp-Session-Id and requires it on subsequent requests. |
Spice advertises 2026-07-28 and accepts 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25, and 2026-07-28, the list server/discover returns in supportedVersions. A legacy client negotiates the revision it requests in initialize. An initialize that requests 2026-07-28, or a revision Spice does not support, is answered with 2025-11-25, the newest legacy revision.
Modern clients send the MCP-Protocol-Version, Mcp-Method, and — for tools/call — Mcp-Name Streamable HTTP headers. A tool argument annotated with x-mcp-header additionally requires a matching Mcp-Param-<name> header whose value equals that argument in the JSON-RPC body.
| Condition | Response |
|---|---|
| A modern request whose protocol version this runtime does not support | HTTP 400 with JSON-RPC -32022; data.supported lists the revisions it accepts |
| A Streamable HTTP header that disagrees with the JSON-RPC body | HTTP 400 with JSON-RPC -32020 |
GET /v1/mcp (the server-to-client SSE stream) and DELETE /v1/mcp (session teardown) are legacy-era only — both act on an Mcp-Session-Id. Spice does not serve subscriptions/listen, the 2026-07-28 notification stream: the request returns HTTP 404 with JSON-RPC -32601 (method not found).
When Spice connects to another MCP server (from: mcp:<url>, or a stdio server), it negotiates the same two eras automatically: it prefers server/discover at 2026-07-28 and falls back to initialize at 2025-03-26 when the peer answers server/discover with an error or does not answer it within 10 seconds. A peer that never answers server/discover therefore delays runtime startup by 10 seconds. Liveness is probed with ping, and — because 2026-07-28 has no ping — a failed ping is retried as an uncached tools/list before the connection is treated as dead.
To configure MCP tools, define them in the tools section of your spicepod.yaml file. The from field specifies the transport mechanism, such as mcp:npx for stdio-based tools or an HTTP URL for Streamable HTTP-based tools.
The following example demonstrates how to configure an MCP tool using npx to run a Google Maps MCP server:
This example shows how to connect to an external MCP server over Streamable HTTP:
Streamable HTTP MCP tools support sending an Authorization: Bearer token via mcp_auth_token, or arbitrary HTTP headers via mcp_headers. Both parameters resolve secret references (opens in a new tab) before the MCP client is constructed.
Sending a bearer token:
Sending a custom header (e.g., API key):
If both mcp_auth_token and a custom Authorization header in mcp_headers are set, mcp_auth_token wins and a warning is logged.
Once configured, MCP tools can be assigned to models via the tools parameter. For example:
Spice can also act as an MCP server, exposing its tools over Streamable HTTP. This enables other Spice instances or external systems to connect and use the tools.
By default the /v1/mcp endpoint only accepts requests with a Host header matching localhost, 127.0.0.1, or ::1 to prevent DNS rebinding attacks. To allow additional hosts, configure runtime.mcp.allowed_hosts (opens in a new tab):
Set allowed_hosts: ["*"] to disable host checking entirely. An empty list (allowed_hosts: []) uses the localhost defaults; it does not disable the check.
Separately from the Host check, /v1/mcp validates the browser Origin header against runtime.cors.allowed_origins (opens in a new tab) — there is no MCP-specific origin setting.
runtime.cors.allowed_origins | Effect on /v1/mcp |
|---|---|
Default (["*"]) or an empty list | Expands to the localhost origins http://localhost, http://127.0.0.1, http://[::1] and their https:// forms. Entries carry no port, so any port on those hosts matches. |
| Explicit list | Only the listed origins are accepted. A request whose Origin is not on the list receives 403 Forbidden. |
A request that sends no Origin header at all is accepted in every case, which is why MCP clients such as Cursor and Claude Desktop are unaffected.
:::warning
["*"] does not accept every Origin on /v1/mcp. * is not a valid RFC 6454 (opens in a new tab) origin, so it expands to the localhost defaults above rather than disabling the check — unlike runtime.mcp.allowed_hosts (opens in a new tab), where ["*"] does disable the Host check. A remote browser-based MCP client must be given a concrete runtime.cors.allowed_origins list:
:::
This affects only MCP Origin validation. Browser CORS on the other HTTP endpoints is unchanged: allowed_origins: ["*"] remains allow-all there.
fromThe from field specifies the transport mechanism for the MCP tool:
http://localhost:8090/v1/mcp).mcp:npx or mcp:docker. Additional arguments can be passed via params.mcp_args.paramsThe params field provides additional configuration for MCP tools.
For stdio-based tools, use mcp_args to specify command-line arguments:
For Streamable HTTP tools, the following auth parameters are supported:
mcp_auth_token — Sends Authorization: Bearer <token> on every request to the MCP server.mcp_headers — Sends additional HTTP headers using the same Header: Value comma- or semicolon-delimited format as the HTTP data connector's http_headers (opens in a new tab). Header values are marked sensitive.Both parameters support secret expansion (opens in a new tab). When mcp_auth_token is set, an Authorization header in mcp_headers is ignored and a warning is logged to avoid duplicate auth headers.
envFor stdio-based MCP tools, environment variables can be set using the env field.
A stdio MCP server does not inherit the spiced environment, so secrets in that environment do not reach the server. The server process starts with only these variables from the spiced environment, when they are set, plus the variables in env:
| Platform | Inherited variables |
|---|---|
| Linux and macOS | HOME, LOGNAME, PATH, SHELL, TERM, USER |
| Windows | APPDATA, HOMEDRIVE, HOMEPATH, LOCALAPPDATA, PATH, PATHEXT, PROCESSOR_ARCHITECTURE, SYSTEMDRIVE, SYSTEMROOT, TEMP, USERNAME, USERPROFILE |
This is the same set the MCP Python SDK (opens in a new tab) passes to stdio servers. A variable in env overrides an inherited variable of the same name. Pass any other variable the server needs through env, for example from a secret store (opens in a new tab):
:::note
In v2.4.0-rc.1, a stdio MCP server inherited the full spiced environment. A server that relied on that, for example to read a token, now needs the variable set in env.
:::
descriptionThe description field provides a textual description of the tool. This description is passed to any language model that uses the tool.
For a tool that proxies an MCP server, Spice adds the description before the description of each tool the server provides, separated by a blank line. A proxied tool without an upstream description uses the description alone. The combined description appears to models, in GET /v1/tools, and in the /v1/mcp tools/list response.
For more details, see the MCP Tools Reference (opens in a new tab).