Spanly Docs

Upstreaming guide

Read a shim's diff, apply the same change in your MCP server source, and let Mend retire the shim automatically.

A shim is a stopgap. It makes clients see a better manifest while your server still produces the old one. The end state for every shim is that you ship the same change in your own source, so your server serves the improved manifest directly and the shim is no longer needed. This page is how you get there.

Once you upstream a fix, Mend detects it on the next scan, retires the shim, and resolves the underlying fault as fixed_at_source. Your wire goes back to pure pass-through for that fix.

Read the diff

Each shim carries the patch it applies: the tools it targets and the exact list of operations. Open the shim in the Shims view to see, per tool, what the overlay changes relative to your live manifest. Every operation maps to a concrete edit in your tool definition:

OperationWhat to change in source
set_descriptionSet the tool's description.
set_param_descriptionSet the parameter's description in its input schema.
add_annotationSet the annotation hint (readOnlyHint, destructiveHint, idempotentHint, or openWorldHint) on the tool.
add_param_constraintAdd the JSON Schema keyword (enum, pattern, minimum, maximum, or format) to the parameter.
add_requiredAdd the parameter to the tool's required list.
set_output_schemaDeclare the tool's outputSchema.

None of these change what your tool does. They change what your manifest tells a client about it, which is exactly the change a shim is already serving.

Apply it in your MCP server

The edit lives wherever you declare your tools. The examples below use the official MCP SDKs.

TypeScript SDK

If you register tools with server.registerTool, the description, annotations, and schemas all live in the registration call. For example, applying a set_description and an add_annotation (readOnlyHint):

server.registerTool(
  'search_orders',
  {
    description: 'Search orders by customer, date range, or status. Read only.',
    annotations: { readOnlyHint: true },
    inputSchema: {
      customerId: z.string().describe('The customer id to search within.'),
      status: z.enum(['open', 'shipped', 'cancelled']),
    },
  },
  handler,
);

An add_param_constraint becomes a tighter Zod (or JSON Schema) type on the parameter (z.enum([...]), .regex(...), .min(...), .max(...)). An add_required means the parameter is no longer optional. A set_output_schema becomes an outputSchema on the registration.

Python SDK

With FastMCP, the description comes from the docstring or the @tool argument, and parameter constraints come from your type hints and Field(...) declarations:

@mcp.tool(annotations={"readOnlyHint": True})
def search_orders(
    customer_id: str = Field(description="The customer id to search within."),
    status: Literal["open", "shipped", "cancelled"] = "open",
) -> OrderPage:
    """Search orders by customer, date range, or status. Read only."""
    ...

A Literal or an Enum applies an enum constraint, Field(pattern=..., ge=..., le=...) applies pattern, minimum, and maximum, a non-defaulted parameter is required, and a typed return value plus output_schema applies the output schema.

What happens after you ship

When your server starts serving the improved manifest, the tool-list content hash changes. On the next scan Mend sees the new manifest and does two things:

  • The shim retires. Because a shim is anchored to the manifest version it was written for, a changed manifest suspends it immediately on the wire (the drift-safety invariant on the Trust and safety page). Mend then classifies each suspended shim: if your new manifest already contains the fix, the shim is marked upstreamed; if the fix still applies cleanly to an unrelated change, it is re-anchored to the new version; if the tool it patched is gone, it is retired.

  • The fault resolves. The fault the shim was carrying is resolved with reason fixed_at_source. It leaves your open faults because your server now does the right thing on its own, not because Mend is masking it.

You do not need to tell Mend that you upstreamed a change. Detection is automatic on the next scan. If you want to record the handoff explicitly (for example the moment you merge the change), you can mark a shim as upstreamed from the Shims view, but shipping the manifest is what actually retires it.

On this page