数据解析MCP服务器

parse-mcp是一个面向AI代理的MCP服务器,提供只读的数据解析工具,支持JSON、JSONL、CSV/TSV、HTML查询,正则提取和文本处理,适用于数据抽取、转换与日志分析等场景。

  • by LAPSrj
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 LAPSrj/parse-mcp

使用说明

parse-mcp

MCP server with scoped, read-only parsing tools (JSON, JSONL, CSV/TSV, regex, HTML) plus file-listing and text utilities.

Tools

Agents often guess parameter names from other tools, so the server renames the common guesses before validating a call: path/file/filepath/filename → file_path, query/expr/jmespath → expression, max_matches/max_results/max_count/max_items/head_limit → limit, dir/directory → cwd (or file_list's path), pattern → glob and cwd → path on file_list, regex → pattern on regex_extract, ignore_case/case_insensitive: true → the i flag, context → context_lines on regex_extract, a top-level find/replace pair → text_replace's replacements, and a one-element paths → file_path on single-file tools. A rename applies only when the tool doesn't have a parameter of that name. Any other unknown parameter fails with an error that lists the tool's valid parameters.

json_query

Query JSON data using a JMESPath expression. With paths or glob, returns {scanned, matched, results}, where results holds one {file, result} per file whose result isn't null (or {file, error} for a file that doesn't parse).

Parameter Type Default Description
file_path string? Absolute path to a JSON file
text string? Inline JSON string (alternative to file_path)
paths string[]? Multiple JSON files to query; returns one result per file
glob string? Glob (e.g. **/*.json) to query many files; returns one result per file
cwd string? Base directory for a relative glob
expression string JMESPath expression to evaluate
parse_nested boolean? false Recursively parse string values that contain JSON
limit number? 50 Max array items to return
max_string_length number? Cut every string in the result to this many characters, followed by …[+N chars] (JMESPath has no substring function)

json_transform

Transform JSON data: apply string replacements across all values, set or delete fields by path, and optionally write the result to a file. Designed for content migration (URL rewrites, ID swaps) and bulk JSON manipulation without ad-hoc scripts.

Parameter Type Default Description
file_path string? Absolute path to a JSON file
text string? Inline JSON string (alternative to file_path)
parse_nested boolean? false Recursively parse string values that contain JSON before transforming
replacements array? [] Find/replace pairs applied to every string value in the tree. Each entry: {from, to, regex?}
set array? [] Set values at dot-separated paths. Each entry: {path, value}
delete string[]? [] Dot-separated paths to delete
output_file string? Write the result to this file instead of returning it

Examples

Replace URLs in all string values:

{
  "file_path": "/tmp/page.json",
  "replacements": [
    { "from": "http://staging.localhost", "to": "https://production.com" },
    { "from": "wp-content/uploads/2024/", "to": "wp-content/uploads/2025/", "regex": false }
  ]
}

Set and delete fields:

{
  "text": "{\"title\":\"Hello\",\"draft\":true,\"meta\":{\"author\":\"old\"}}",
  "set": [{ "path": "meta.author", "value": "new" }],
  "delete": ["draft"]
}

Write large results to a file to avoid blowing context:

{
  "file_path": "/tmp/large-export.json",
  "replacements": [{ "from": "media-id-100", "to": "media-id-200" }],
  "output_file": "/tmp/large-export-transformed.json"
}

text_replace

Find/replace on plain text or a file — literal or regex, applied in order, with optional write-to-file. The plain-text counterpart to json_transform (which only operates on JSON trees); replaces sed s/.../.../. Regex replacements support capture-group references ($1, $2) in the replacement.

Parameter Type Default Description
file_path string? Absolute path to the file
text string? Inline text (alternative to file_path)
replacements array Find/replace pairs applied in order. Each entry: {from, to, regex?, flags?}
output_file string? Write the result here and return a summary instead of the text

Returns the transformed text, or Made N replacement(s), wrote B bytes to … when output_file is set.

Example

Regex replace with a capture group, written back to the file:

{
  "file_path": "/tmp/config.env",
  "replacements": [{ "from": "PORT=(\\d+)", "to": "PORT=8080", "regex": true }],
  "output_file": "/tmp/config.env"
}

jsonl_query

Query newline-delimited JSON (JSONL/NDJSON) — transcripts, logs, exports — one JSON object per line. Applies a JMESPath expression per record, with optional filtering, group-by aggregation, and multi-file fan-out. Unparseable lines are skipped. This is what json_query cannot do: it parses a single document; JSONL is many.

Parameter Type Default Description
file_path string? Absolute path to a .jsonl file
text string? Inline JSONL string (alternative to file_path)
paths string[]? Multiple .jsonl file paths read as one stream of records
glob string? Glob (e.g. **/*.jsonl) to read many files as one stream
cwd string? Base directory for a relative glob
expression string? JMESPath applied to each record (default: the whole record)
filter string? JMESPath boolean; records kept only when it evaluates truthy
group_by string? JMESPath key expression; returns {scanned, matched, groups: [{key, count}]} sorted by count desc (like GROUP BY)
agg string? "count" count, sum, avg, min, max. The last four need agg_field, add the value to each group ({key, count, sum}), and sort groups by it desc
agg_field string? JMESPath for the numeric value to aggregate per record (e.g. message.usage.output_tokens); missing and non-numeric values are skipped
parse_nested boolean? false Recursively parse JSON strings embedded inside record values
include_source boolean? false Tag each per-record result with its source file as {source, value}
limit number? 100 Max results (records or groups) to return
max_string_length number? Cut every string in each result to this many characters, followed by …[+N chars], for a compact view of long text fields

Examples

Aggregate message roles across every transcript in a directory:

{
  "glob": "/path/to/transcripts/*.jsonl",
  "group_by": "message.role"
}

Total output tokens per model:

{
  "glob": "/path/to/transcripts/*.jsonl",
  "group_by": "message.model",
  "agg": "sum",
  "agg_field": "message.usage.output_tokens"
}

Filter then project — pull user prompts out of one transcript:

{
  "file_path": "/path/to/session.jsonl",
  "filter": "type == 'user'",
  "expression": "message.content"
}

csv_query

Query delimited text (CSV / TSV / whitespace columns) — select columns, filter rows, and group-by with count/sum/avg/min/max aggregation. Each row becomes a record object addressable by header name (or c0, c1, … when header is false), so filter, expression, and group_by all take JMESPath. Replaces awk/cut for column work; supports multi-file paths/glob.

Parameter Type Default Description
file_path string? Absolute path to a delimited file
text string? Inline delimited text (alternative to file_path)
paths string[]? Multiple delimited files read as one set of rows
glob string? Glob to read many delimited files as one set
cwd string? Base directory for a relative glob
delimiter string? "," ",", "\t" (TSV), "|", ";", or "whitespace"
header boolean? true First row is column names; if false, columns are c0, c1, …
columns string[]? Column names/keys to keep in the output (default: all)
filter string? JMESPath boolean over the row object; rows kept only when truthy
expression string? JMESPath applied to each row object (overrides columns)
group_by string? JMESPath key expression; returns aggregated groups instead of rows
agg string? "count" count, sum, avg, min, max (the last four need agg_field, and groups are then sorted by that value desc)
agg_field string? Column to aggregate numerically when agg is sum/avg/min/max; empty and non-numeric cells are skipped
limit number? 100 Max rows or groups to return

Example

Total quantity per region across a CSV:

{
  "file_path": "/tmp/sales.csv",
  "group_by": "region",
  "agg": "sum",
  "agg_field": "qty"
}

regex_extract

Extract regex matches from a file, inline text, or many files at once (paths/glob).

With a single file_path/text, returns the matches directly. With a glob or paths array, returns {scanned, matched, results}, where results holds one {file, result} per file that matched — files with no matches are omitted, so scanned is the total examined and matched is results.length. A file whose regex overruns the per-file timeout is reported as {file, error} and the scan continues past it. If the whole call exceeds its budget, a note field says so and the results are partial.

Parameter Type Default Description
file_path string? Absolute path to the file
text string? Inline text (alternative to file_path)
paths string[]? Multiple file paths to scan; returns one result per matching file
glob string? Glob (e.g. **/*.ts) to scan many files; returns one result per matching file
cwd string? Base directory for a relative glob
pattern string JavaScript regular expression. A leading inline flag group ((?i), (?s), (?m), or a combination like (?is)) is moved into flags; text_replace and json_transform regex replacements accept the same syntax
flags string? "g" Regex flags
group number? Capture group index to return (0 = full match)
limit number? 50 Max matches to return
count_only boolean? false Return {count} instead of matches (like grep -c). With glob/paths, the response also has total across all files
unique boolean? false Deduplicate matches, return [{match, count}] sorted by frequency (like sort | uniq -c). With glob/paths, returns one list across all files, {scanned, matched, distinct, results: [{match, count, files}]}, where files is how many files contain the match
with_lines boolean? false Return {line, match} with the 1-based line number (like grep -n). With group, the line points at that capture group
with_cols boolean? false Also return the 1-based column as {line, col, match} (like rg --column). Requires with_lines
context_lines number? 0 Lines of context around each match (like grep -C). Implies with_lines and adds text (the whole matching line) plus before/after arrays. Ignored with count_only or unique

html_select

Extract elements from HTML using CSS selectors (via Cheerio).

Parameter Type Default Description
file_path string? Absolute path to an HTML file
text string? Inline HTML string (alternative to file_path)
selector string CSS selector
attribute string? Return a specific attribute value instead of text
output string? "text" "text", "html", or "outer"
limit number? 50 Max elements to return

text_count

Count lines, words, characters, and bytes in a file or inline text (like wc).

Parameter Type Default Description
file_path string? Absolute path to the file
text string? Inline text (alternative to file_path)

Returns { lines, words, characters, bytes }.

file_list

List files matching a glob, with optional mtime / size / type filters and sorting — a structured replacement for find/ls. The natural front half of a parse workflow: list the files, then query their contents. Supports **, *, ?, and {a,b} alternation. Does not follow symlinks.

Parameter Type Default Description
glob string? "*" Glob pattern. Absolute (e.g. /var/log/**/*.log) or relative to path. The default lists the top level of path, like ls
path string? Base directory for a relative glob (default: process cwd)
since string? Only files modified at/after this time. Relative duration (2d, 30m, 1w), epoch ms, or ISO date
until string? Only files modified at/before this time (same formats)
min_size number? Minimum size in bytes
max_size number? Maximum size in bytes
type string? "file" "file", "dir", or "any"
stat boolean? false Return {path, size, mtime, type} objects instead of bare paths
sort string? "path" Sort key: "path", "mtime", or "size"
desc boolean? false Sort descending
limit number? 1000 Max entries to return

Returns { count, returned, files }.

Example

Newest log files touched in the last two days:

{
  "glob": "/var/log/**/*.log",
  "since": "2d",
  "stat": true,
  "sort": "mtime",
  "desc": true
}

Setup

Option 1: Download a prebuilt binary

Grab the latest binary for your platform from Releases:

Platform File
Linux x64 parse-mcp-linux-x64
macOS x64 parse-mcp-darwin-x64
macOS ARM parse-mcp-darwin-arm64
Windows x64 parse-mcp-win-x64.exe

Make it executable (Linux/macOS):

chmod +x parse-mcp-*

MCP client config using the binary:

{
  "mcpServers": {
    "parse-mcp": {
      "command": "/path/to/parse-mcp-linux-x64"
    }
  }
}

Option 2: Build from source

Requires Node.js >= 18.0.0.

git clone <repo-url>
cd parse-mcp
npm install
npm run build

This compiles TypeScript from src/ into dist/.

MCP client config using Node:

{
  "mcpServers": {
    "parse-mcp": {
      "command": "node",
      "args": ["/path/to/parse-mcp/dist/index.js"]
    }
  }
}

Add the config to your MCP client settings (e.g. Claude Desktop claude_desktop_config.json, Claude Code settings.json, or VS Code MCP config).

No environment variables are required. All tools accept input via file_path or inline text parameters.

CLAUDE.md rule

Add the following to your project's CLAUDE.md so the agent prefers parse-mcp tools over writing one-off scripts:

## Parsing & data extraction

When you need to parse, query, or transform JSON, JSONL, CSV/TSV, HTML, or text
files, or to list files, use the parse-mcp tools (json_query, jsonl_query,
csv_query, json_transform, text_replace, regex_extract, html_select, text_count,
file_list) instead of writing custom scripts, shell pipelines, or inline code.

- **JSON**: Use `json_query` with JMESPath for reads, `json_transform` for bulk
  find/replace, field set/delete, or migration rewrites.
- **JSONL/NDJSON** (transcripts, logs, exports — one JSON object per line): Use
  `jsonl_query` instead of a `cat | jq` / per-line Node loop. It applies a
  JMESPath per record and supports `filter`, `group_by` aggregation, and
  multi-file `glob`/`paths` fan-out.
- **CSV/TSV/columns**: Use `csv_query` instead of `awk`/`cut` for column select,
  row filter, and group-by `sum`/`avg`/`min`/`max`/`count`.
- **HTML**: Use `html_select` with CSS selectors instead of regex or custom DOM
  parsing.
- **Regex**: Use `regex_extract` instead of grep pipelines or throwaway scripts.
  Use `count_only` instead of `grep -c`, and `unique` instead of
  `sort | uniq -c`. Pass `glob`/`paths` to scan many files at once.
- **Text find/replace**: Use `text_replace` instead of `sed s/.../.../` for
  literal or regex substitution on plain text (the non-JSON counterpart to
  `json_transform`).
- **Counting**: Use `text_count` instead of `wc` or manual counting.
- **Listing/finding files**: Use `file_list` with a glob (plus `since`/`until`,
  size, and type filters) instead of `find`/`ls`.

All tools accept a `file_path` or inline `text` — no temp scripts needed.

Hook: parse-guard.sh

An optional Claude Code hook that blocks Bash commands which duplicate parse-mcp capabilities. When the agent tries to use jq, wc, grep -o, etc., the hook denies the command with an explanation of which parse-mcp tool to use instead.

What it catches

Pattern Example Suggested tool
Python JSON parsing python3 -c "import json; json.load(...)" json_query / jsonl_query
jq jq '.field' data.json json_query / jsonl_query
JSON.parse node -e "JSON.parse(...)" json_query / jsonl_query
json_pp json_pp < file.json json_query
wc wc -l file.txt, ... | wc -l text_count
grep -o grep -oE "pattern" file regex_extract
grep -c grep -c "pattern" file regex_extract with count_only
standalone grep grep "pattern" file (not piped) Grep tool / regex_extract
ls / find (listing) find . -name "*.ts", ls -la file_list
awk print / sed -n p awk '/re/{print}', sed -n '/re/p' Grep tool / regex_extract
cut cut -d, -f2 data.csv csv_query
sed s/// sed 's/foo/bar/g' file text_replace

Piped/streaming forms, find -exec/-delete, processing awk (-F, BEGIN/END, field math), cut -c/-b, and line-range sed -n '5,20p' are deliberately not blocked — they have no single parse-mcp equivalent.

Installation

  1. Copy the hook script to your Claude hooks directory:
mkdir -p ~/.claude/hooks
cp hooks/parse-guard.sh ~/.claude/hooks/parse-guard.sh
  1. Add the hook to your global ~/.claude/settings.json:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/parse-guard.sh"
          }
        ]
      }
    ]
  }
}

If you already have a hooks section, merge the PreToolUse entry into your existing array.

The hook requires jq to be installed (used to read the tool input from stdin).

Customization

The script is a plain Bash file. Each check follows the same pattern:

printf '%s' "$cmd" | grep -qE 'PATTERN' \
  && deny "REASON"

Add new blocks to catch additional patterns, or remove checks you don't want enforced.