Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, email [email protected] and we’ll put it right.

Build a secure MCP server from day one

Building a secure MCP server comes down to a handful of habits: build on an official SDK rather than hand-rolling the protocol, expose narrow tools with clear, well-typed schemas, treat every tool description and result as untrusted text an agent will read, and set up supply-chain and provenance hygiene (a lockfile, an OSI licence, a linked repository, signed build provenance) before your first release. This is a practical guide that walks through each of those on an MCP server that scores well on VerifyMCP from day one.

The trust signals we measure are the natural by-product of building carefully, so it’s cheapest to get them right from the start.

We’ll build a minimal server with one well-described tool, then walk through the signals that earn (or cost) trust.

Scaffold the server

An MCP server is a small program that speaks the protocol over a transport (stdio for a locally-run package, Streamable HTTP for a remote endpoint) and exposes tools the model can call. Use an official SDK; don’t hand-roll the protocol. Here’s a complete one-tool server over stdio:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "weather", version: "1.0.0" });

server.registerTool(
"get_forecast",
{
  title: "Get weather forecast",
  description: "Return a short weather forecast for a given city.",
  inputSchema: { city: z.string().describe("City name, e.g. 'Berlin'") },
},
async ({ city }) => ({
  content: [{ type: "text", text: `Forecast for ${city}: sunny, 21°C.` }],
}),
);

const transport = new StdioServerTransport();
await server.connect(transport);

Both examples use an official SDK, the TypeScript SDK and the Python SDK, which handle the protocol and transport for you. Install one first: npm install @modelcontextprotocol/sdk zod for Node, or pip install "mcp[cli]" for Python.

Write tools an agent can understand

A tool’s name, description, and parameter schema are the only things the model sees. They’re effectively the tool’s API for a non-deterministic caller, so treat them as carefully as you would a public interface:

  • Name tools for intent, in snake_case: get_forecast, not do_it.
  • Describe what the tool does and when to use it, not how it’s implemented.
  • Give every parameter a type and a one-line description (the .describe() calls above). A typed, documented schema is also a signal we score on the remote rubric.
  • Keep tools narrow. One clear job per tool beats a single overloaded tool with a mode flag; it’s easier for the model to call correctly and harder to misuse.

Score well from day one

Most of what VerifyMCP rewards is provenance and supply-chain hygiene on the package you publish. Set these up before your first release. Each links to a focused fix guide:

  • Commit a lockfile so installs are reproducible and tampering is detectable. See commit a lockfile.
  • Declare an OSI licence so users know they can actually use the code. See license.
  • Link a verifiable source repository in your manifest; it underpins the lockfile, license, and security-policy checks. See declare a repository.
  • Add a security policy (SECURITY.md) so vulnerabilities have somewhere to go. See security policy.
  • Avoid install-time scripts: don’t run code on npm install. See install scripts.
  • Keep dependencies patched and a steady release cadence so known vulnerabilities don’t accumulate.

Publish with provenance

Build provenance ties the artifact you publish to the exact source commit and build that produced it, so users can prove the download matches the code they’re reading. The cheapest way to get it is to publish from CI with provenance attestations enabled rather than from a laptop:

# Publish from a CI job with id-token permission to attach
# a provenance attestation (see the maintenance guide's workflow).
npm publish --provenance --access public

See build provenance for the full setup and the other ecosystems. Provenance plus a verifiable repository is what lets a user (or VerifyMCP) confirm your published server really is what its source says it is.

Pick your language or target

The walkthrough above builds a minimal server in the abstract. When you come to build the real thing, pick the guide that matches how you plan to ship it, since the specifics differ between a hosted endpoint and a published package.

Hosting a remote endpoint (a live HTTPS service clients connect to over the network)? Start from your language: TypeScript, Python, .NET, Go, Java, Kotlin, or Rust.

Publishing a package to a registry that clients download and run? Pick your target: an npm package, a PyPI package, a NuGet package, an OCI image, or an MCPB bundle.

Either way, the shared release mechanics (a committed lockfile, a declared source and licence, and signed build provenance from CI) live in publishing an MCP server.

Next steps

  • What is MCP?: the protocol and the packaged-vs-remote split, if you haven’t read it yet.
  • MCP security: the threat model your server is judged against.
  • Publish, then find your server in the directory to see how it scores.

Written by Stuart Blackler · Last reviewed 30 June 2026.