ptext-mcp
Provides OpenAI function-calling tool schemas and a handler so OpenAI models can invoke PText's deterministic text-processing tools.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
PText
Paste anything. Get clean data.
PText is a set of deterministic text tools for extracting, cleaning, splitting into columns, converting, encoding and formatting text. It also has a local profiler that looks at a paste and suggests the right tool. An AI may pick the tool, but code always produces the result, so the output is exact, repeatable and testable.
One engine ships as a library, an MCP server, a CLI and OpenAI/Anthropic tool schemas.
Zero runtime dependencies. Pure ESM TypeScript, about 17 KB gzipped. The core uses no DOM and no Node APIs.
Runs anywhere JS runs: browser, Cloudflare Workers, Node ≥ 22, Deno, Bun.
Private by design. No network calls, no telemetry. The optional AI ranker sees only counts and flags, never your text.
Never loses data silently. Anything skipped, guessed or changed is reported as a warning code.
Contract-tested. Every tool has language-neutral golden fixtures that any port (PHP, Python…) can run.
Contents
Related MCP server: casper-tools
Tools
Tool | Category | What it does |
| extract | Find all email addresses in any text (logs, HTML, CSV, chat). Supports + tags and Unicode. |
| extract | Find http(s):// and www. links in text, trimming trailing punctuation and unbalanced brackets. |
| extract | Extract hostnames from URLs, emails and plain text. Returns full hostnames (subdomains kept); does not compute registrable domains. |
| extract | Find valid IPv4 and IPv6 addresses (incl. compressed IPv6, IPv4-mapped IPv6 and ip:port) in text. |
| clean | Trim lines, remove empty lines, collapse repeated spaces and strip invisible characters (zero-width, BOM). |
| clean | Remove duplicate lines while keeping the first occurrence and original order. |
| clean | Keep (or drop) lines that contain, start with, end with, equal, or match a regex pattern. |
| columns | Split delimited lines (e.g. user|pass|mail, tab-separated, quoted CSV) into columns and output CSV. |
| columns | Pick and reorder columns by 1-based index ("3,1", "2-4", "-1" = last cell of each row) from delimited text. |
| convert | Turn a plain list (one item per line) into a single-column CSV, quoting values safely. |
| convert | Convert CSV/TSV (quoted fields supported) into a JSON array of objects (with header) or arrays. |
| encode | Encode text to Base64 or decode Base64 (standard or URL-safe) to UTF-8 text. Decoded content is only returned as text, never executed. |
| encode | Percent-encode or decode text (component or full-URI mode). Decoding "+" as space is supported in form mode. |
| format | Validate JSON and pretty-print or minify it. Reports line and column of syntax errors. |
Every tool's options are plain JSON Schema with descriptions and defaults. To see the full catalog, including option schemas and input cards, run ptext list or call listTools().
Install
Package | What it is | Runtime |
Tools, catalog, profiler, router, AI adapters | Any JS runtime | |
MCP server: | Node (stdio) · Workers, Deno, Bun (HTTP) | |
| Node |
npm install @ptext/core # library
npm install -g @ptext/cli # `ptext` command
npx -y @ptext/mcp # MCP server over stdioNot on npm yet. Until the first npm release, build from source:
git clone https://github.com/mr-sonj/ptext-core.git && cd ptext-core pnpm install && pnpm build node packages/cli/dist/cli.js list
Quick start
import { suggestTools, runTool } from '@ptext/core';
const text = 'Contact sales@acme.io or Support@Acme.io, see https://acme.io/help.';
suggestTools(text).suggestions[0]; // { tool: 'extract-emails', score: …, reason: '…' }
runTool('extract-emails', text).output; // "sales@acme.io\nSupport@acme.io"echo 'a@x.com, b@y.io, a@x.com' | ptext run extract-emails
# a@x.com
# b@y.ioLibrary
import { listTools, runTool, suggestTools, PTextError } from '@ptext/core';
const r = runTool('select-columns', 'id,name,mail\n1,Ann,a@x.com', { columns: '3,1' });
r.output; // copy-ready text: "mail,id\na@x.com,1"
r.data; // structured result when useful (list, rows, objects)
r.stats; // { inputLines, outputLines, count?, removed? }
r.warnings; // [{ code: 'RAGGED_ROWS', message, line? }, …], never silent
const { profile, suggestions } = suggestTools(text); // local and deterministic, < 1 ms on typical inputrunTool(id, input, options?, { limits? }) throws a PTextError with a code from a closed set:
Code | Meaning | CLI exit |
| No tool with that id | 2 |
| Option fails the schema, or the combination makes no sense | 2 |
| The tool cannot process this input ( | 3 |
| Input exceeds | 3 |
| A bug. Anything that is not a | 1 |
errorToJSON(e) turns any error into { code, message, details? }, the same shape the CLI, MCP server and Worker return.
CLI
ptext <list|catalog|prompt|jev|suggest|run <tool>|exec> [--option=value] [--json]Command | Reads | Prints |
| Every tool, with option schema and input card (JSON) | |
| text on stdin | The tool's output. Warnings go to stderr. Add |
| text on stdin |
|
|
|
|
| Profile field meanings and every tool's input card, for an AI ranker | |
| The ranking prompt and reply schema for any LLM | |
| The static part of a Jev router request | |
| Version |
printf 'user|pass|mail\nann|x1|a@x.com\n' | ptext run split-columns
cat access.log | ptext run extract-ip --version=4 --unique=true
echo '{"tool":"json-format","input":"{\"b\":1,\"a\":2}","options":{"sortKeys":true}}' | ptext execExit codes: 0 ok · 2 usage or option error · 3 invalid input · 1 internal error.
MCP server
The server exposes every tool as ptext_<id> (for example ptext_extract_emails), plus ptext_suggest (profile and suggestions for a text) and ptext_catalog (field meanings and input cards). Each tool description includes Use when: … and Not for: … so the client's model picks the right tool.
Local (stdio)
Claude Desktop, Cursor, Windsurf, Codex and any other client that reads an mcpServers config:
{
"mcpServers": {
"ptext": { "command": "npx", "args": ["-y", "@ptext/mcp"] }
}
}Claude Code:
claude mcp add ptext -- npx -y @ptext/mcpFrom a source checkout, use "command": "node", "args": ["/absolute/path/to/ptext-core/packages/mcp/dist/stdio.js"].
Remote (Streamable HTTP on Cloudflare Workers, Deno, Bun)
import { createFetchHandler } from '@ptext/mcp';
import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/cfworker';
const mcp = createFetchHandler({ path: '/mcp', jsonSchemaValidator: new CfWorkerJsonSchemaValidator() });
export default { fetch: (req: Request) => mcp(req) };On Workers, pass CfWorkerJsonSchemaValidator. The default validator (Ajv) uses new Function, which Workers block. examples/cloudflare-worker adds a REST API next to MCP:
Route | Body | Returns |
| MCP JSON-RPC | MCP Streamable HTTP (stateless) |
|
| Tool result |
|
|
|
| Tools with input cards | |
| Field meanings and input cards | |
| Ranking prompt and reply schema |
cd examples/cloudflare-worker && npx wrangler devAI function calling
import { openAITools, anthropicTools, handleToolCall } from '@ptext/core';
// Pass openAITools() or anthropicTools() as `tools`. When the model calls one:
const result = handleToolCall(call.name, call.arguments); // never throws, JSON-serializableEach description comes from modelDescription(tool): what the tool does, Use when: … and Not for: …. The MCP server uses the same text.
Choosing a tool with any AI
input → profileInput() counts, ratios and flags only (PROFILE_FIELDS describes each one)
→ each tool's suggest() local, deterministic
→ route(input, { router }) optional: an AI re-ranks from the profile and the catalogEvery tool carries an input card: accepts (what the input looks like), signals (profile fields that point to it), avoid (a look-alike and the tool to use instead) and examples. Rankers never see the raw text. They get the profile and the input cards.
import { route, llmRouter } from '@ptext/core';
// Any model: send prompt.system + prompt.user, return the reply text.
const ai = llmRouter(async (prompt, signal) => callYourModel(prompt.system, prompt.user, signal));
const { suggestions, source, issues } = await route(text, { router: ai, timeoutMs: 1500 });The router is asked even when local rules find nothing, and it may return any catalog tool.
Malformed entries are dropped and listed in
issues. A timeout, an error, or a reply that is not a ranking falls back to the local result, so routing never blocks.Generative models use
llmRouter(complete)with the sharedrankPrompt(),rankResponseSchema()andparseRankResponse(). Decision models such as Jev (TypeSafe System One) usejevRouter(post). In both cases your code makes the HTTP call and holds the key.Backends in other languages can fetch the same catalog and prompt with
ptext catalog/ptext promptor from the Worker atGET /api/catalogandGET /api/prompt.
PHP / Laravel / other languages
Call the CLI with a JSON request:
echo '{"tool":"extract-emails","input":"a@x.com"}' | ptext execexamples/laravel shows both setups: @ptext/core in the frontend (Vite + Alpine), where text never leaves the browser, and a backend that calls the CLI through Process.
Privacy model
The core runs inside your process: browser tab, MCP process or Worker. It makes no network calls and sends no telemetry.
profileInput()returns only counts, ratios and flags. A test checks that no raw text leaks into it.Decoded content (Base64, URL encoding) is returned as text and never executed.
When PText runs as an MCP server inside an AI client, that client may still send your text to its own model provider. PText cannot make that whole chain offline, and does not claim to.
Project layout
packages/core tools/<category>/<id>.ts (one file per tool) · profiler · router · AI adapters
packages/mcp transport-agnostic server · fetch handler (HTTP) · stdio bin
packages/cli ptext command (runCli is a pure, tested function)
fixtures/ golden/<id>.json (tool contract) · suggest.json (profiler benchmark)
examples/ cloudflare-worker · laravel · browser (parity harness)
scripts/ new-tool.mjs (scaffold) · release.mjs (versioning, changelog, release notes)Development
Requirements: Node ≥ 22.12 (the test runner needs it; the packages need Node ≥ 22) and pnpm 10 (corepack enable).
pnpm install
pnpm build && pnpm test # golden fixtures, catalog rules, profiler corpus, MCP parity, CLI, Worker
pnpm typecheck
pnpm test:tool extract-emails # one tool's casesAdding a tool takes two files: the tool and its fixture. Scaffold both with:
pnpm new-tool csv-to-yaml --category=convertThe scaffold stays red until the fixture cases, the input card and the README row are filled in. CONTRIBUTING.md lists the rules the tests enforce.
Browser parity harness: run pnpm build, serve the repo root (npx serve .), then open /examples/browser/.
Releases
Versions follow Semantic Versioning and come from Conventional Commits: feat!: / BREAKING CHANGE: → major (minor while on 0.x), feat: → minor, anything else → patch.
Each release is one chore(release): vX.Y.Z commit that writes the version to every package.json and to VERSION and adds a CHANGELOG.md section, tagged vX.Y.Z. CI builds and tests the tag, then publishes a GitHub release with that changelog section. Packages are not published to npm yet.
Contributing
Issues and pull requests are welcome. Read CONTRIBUTING.md before you open a pull request that adds or changes a tool.
Security
Please report vulnerabilities privately. See SECURITY.md.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
AI-writing-tells detector (rules-based) + CSV/JSON, hashing, slugs, word counts, UUIDs. Free.
238+ dev tools via MCP: JSON, QR, PDF, DNS, hash, UUID, code review, JWT, SSL, WHOIS, and more
518.PLUS: MCP tool marketplace. Run tools on files, text or JSON, share results, publish your own.
300+ deterministic data utilities for AI agents: validate, normalize, parse, match, redact.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA text processing workbench and MCP server that provides 22 tools for cleaning, transforming, and extracting data from text. It enables AI assistants to perform complex operations like regex extraction, log normalization, and encoding through a unified interface.140 npm39 PyPI4MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to perform common utility tasks through a streamable HTTP MCP endpoint, including JSON inspection, regex testing, cron parsing, hashing, base64 encoding/decoding, URL analysis, color conversion, text diffing, CSV parsing, JWT decode/verification, Markdown-to-HTML rendering, and UUID/token generation without API keys.MIT
- FlicenseNot gradedqualityBmaintenanceProvides nine deterministic data and lookup tools over MCP, paid per call in USDC via x402, including JSON repair, YAML conversion, cron scheduling, text diff/extraction, WHOIS, DNS, GitHub stats, and email validation.-
- FlicenseNot gradedqualityBmaintenanceEnables AI agents and MCP clients to convert ASCII/OCR text dumps, aligned spacing, and CSV/TSV data into clean GitHub Flavored Markdown tables and typed JSON deterministically.7-