Vellum
Provides exact text analysis tools for counting characters and words, scoring readability, diffing texts, testing regular expressions, and computing hashes.
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.
- Deploy to Vercel first and note the URL
- Go to smithery.ai/new
- Enter
https://your-deployment.vercel.app/mcp - 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
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.
E2B
Using MCP to run code via e2b.