Vellum

Vellum

Provides exact text analysis tools for counting characters and words, scoring readability, diffing texts, testing regular expressions, and computing hashes.

Category
Visit Server

README

Vellum

An MCP server that gives exact answers about text · By Shuja Jamal

A language model reads tokens, not characters. Ask one how many letters are in a paragraph and it estimates, often badly. The same is true of diffing two files by eye, or predicting what a regular expression will match without running it.

These are not hard problems. They are simply not the kind of problem a model is built to solve. So it should ask instead.

Live vellum-mcp.vercel.app
Smithery sjshujaj/vellum
Write-up EXPERIENCE.md, on using existing MCPs before building this

The five tools

Tool What it answers Instead of
count_text Characters, words, lines, sentences, bytes an estimate from token count
score_readability Flesch reading ease, grade level, longest sentence an impression
diff_texts Unified diff, lines added and removed, similarity reading both and describing
test_regex Every match, its position and capture groups reasoning about the pattern
hash_text md5, sha1, sha256, sha512 nothing, it cannot

count_text reports visible characters separately from code points, because café is four characters to a person, five to len(), and six bytes. Which of those you want depends on why you asked, so it returns all three.


Using it

Claude Code

claude mcp add --transport http vellum https://vellum-mcp.vercel.app/mcp

Or locally, over stdio:

claude mcp add vellum -- python -m vellum.server

One thing that cost me time when installing somebody else's server: a server added mid-session shows as connected but its tools are not callable until the session restarts. A working install and a usable install are different states.

Claude Desktop

In claude_desktop_config.json:

{
  "mcpServers": {
    "vellum": {
      "command": "python",
      "args": ["-m", "vellum.server"],
      "cwd": "/path/to/vellum-mcp"
    }
  }
}

Running it

pip install -r requirements.txt
python -m vellum.server

That is stdio, which is what a local client speaks. For the HTTP transport and the landing page:

python -m vellum.server --http --port 8000

Then http://localhost:8000 for the page and http://localhost:8000/mcp for the endpoint.


Testing

python tests/test_vellum.py

46 checks. The first half calls the analysis functions directly. The second half is the part that matters: it launches the server as a subprocess and speaks MCP to it, doing the real handshake, listing the tools, calling them and reading the results back. A server whose functions are correct can still fail to speak the protocol, and only the second half would catch that.

python tests/test_deploy.py

Checks the deployed shape before deploying it: that a rewritten request reaches the MCP app at the path it expects, that ?action=demo reaches the demo instead, that the static server card describes tools that actually exist, and that there is exactly one function because that is what the runtime builds.


Deploying

Vercel, for the hosting

vercel

No environment variables and no secrets: every tool is a pure function of its arguments.

The routing is deliberate and worth explaining, because the obvious version does not work.

A Vercel rewrite does not hand the function the path the browser asked for. Rewriting /(.*) to a single function means every request arrives as /api/index, so an app that routes on path answers every URL with its own 404 while looking, from outside, completely dead. I lost an afternoon to exactly that on an earlier project.

And the Python runtime builds one function for the whole project, not one per file. I had api/demo.py sitting next to api/index.py, declared in vercel.json and bundled into the deployment, and /api/demo still returned Vercel's own 404. vercel inspect showed the reason plainly: a single python lambda. Filesystem routing across several Python entrypoints does not happen.

So the deployment is one function plus static files:

/                                  static    public/index.html
/.well-known/mcp/server-card.json  static    generated by build_card.py
/mcp             -> /api/index     function
/api/demo        -> /api/index     function, told apart by ?action=demo

api/index.py dispatches on the query string, because that is the part a rewrite preserves while it replaces the path, and it puts the path back before the MCP app routes on it. There are tests for both, because neither is something you want to discover from a live URL.

The two things that worked first time were the landing page and the server card, and they are exactly the two that never touch a function.

Smithery, for the listing

Smithery's current model is bring your own hosting: you give it a public HTTPS URL to a streamable HTTP server and its gateway proxies to it. There is no container to build.

  1. Deploy to Vercel first and note the URL
  2. Go to smithery.ai/new
  3. Enter https://your-deployment.vercel.app/mcp
  4. Complete the publishing flow

Smithery then reads the server's tools for the listing page. The documented path is a live scan, with a static card at /.well-known/mcp/server-card.json as the fallback when a scan cannot complete.

In practice the fallback was the primary path. The publish log reads:

Server metadata discovered (server card: 5 tools).
Using .well-known/mcp/server-card.json: (5 tools)

So the card was not insurance against a failure, it was the mechanism. build_card.py generates it from the server's own tool definitions, which is why it could not have listed tools that do not exist.

The log also warns that no config schema was provided. That is correct and intended here: every tool is a pure function of its arguments, so there is nothing to prompt a user for. The warning matters for a server that wraps an API and needs a key.

smithery.yaml records the details the publishing flow asks for. Note that it does not drive a build: the older container-based deployment path is no longer how this works.

Both tiers are free. Vercel's hobby tier hosts the function and the static files; Smithery's registry listing costs nothing.


How it is put together

vellum/
  analysis.py    the actual work, with no MCP anywhere in it
  server.py      the five tools, their descriptions, and the transports
api/
  index.py       the one Vercel function: MCP, and the demo behind a query flag
public/
  index.html     the landing page
  .well-known/mcp/server-card.json   generated
tests/
  test_vellum.py  analysis, then a real MCP handshake over stdio
  test_deploy.py  the routing and the card

analysis.py imports nothing from MCP. That is what lets the tests call it directly, and it is why the landing page's demo can call the same functions the tools call rather than being a second implementation that drifts.


Notes on writing tools for a model

The description is the interface. It is not documentation for a developer; it is the only thing the model reads when deciding whether a tool is relevant. A vague one means the tool is never chosen, a wrong one means it is chosen at the wrong moment. So count_text does not say "returns a dictionary of counts". It says to use this when the answer needs to be a precise number, because reading tokens is not the same as counting characters.

Failure should be readable. A broken regex comes back as {"valid": false, "error": ...} rather than an exception, and an unknown hash algorithm names the ones that exist. A model can act on that; it cannot act on a stack trace.

Pick something the model genuinely cannot do. The temptation is to wrap an API and call it a tool, but a "summarise this text" tool adds a network round trip to something the model already does better itself. Counting, hashing and running a regex are real gaps. Filling a real gap is what makes a tool get used rather than politely ignored.


By Shuja Jamal, August 2026.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured