First-party MCP servers¶
The repository ships four small stdio MCP servers under servers/:
| Server | Source | Backend | Configured by |
|---|---|---|---|
| Postgres | servers/postgres |
A PostgreSQL database | LEANPROXY_POSTGRES_* |
| Redis | servers/redis |
A Redis server | LEANPROXY_REDIS_* |
| Filesystem | servers/filesystem |
One local directory | LEANPROXY_FILESYSTEM_ROOTS |
| GitHub | servers/github |
The GitHub REST API | GITHUB_TOKEN |
They are ordinary MCP servers. LeanProxy-MCP does not start them on its own.
You declare them in leanproxy_servers.yaml like any other stdio server, and
they go through the same pipeline (redaction, policy, pinning, governor and so
on).
They are deliberately small. Official vendor servers exist for most of these backends and usually cover more. Use these when you want a minimal, auditable surface.
Not included in release archives
The release archives contain only the leanproxy-mcp binary. Build the
servers from a clone of the repository (Go 1.25 or later):
git clone https://github.com/mmornati/leanproxy-mcp.git
cd leanproxy-mcp
go build -o ~/bin/leanproxy-mcp-postgres ./servers/postgres
go build -o ~/bin/leanproxy-mcp-redis ./servers/redis
go build -o ~/bin/leanproxy-mcp-filesystem ./servers/filesystem
go build -o ~/bin/leanproxy-mcp-github ./servers/github
Use the absolute path of the built binary as stdio.command.
Common behaviour¶
- Transport: newline-delimited JSON-RPC on stdin/stdout. Logs go to stderr.
- Methods:
initialize,tools/list,tools/call,ping. Any other method returns "method not found". The servers report MCP protocol version2024-11-05and only thetoolscapability (no resources or prompts). - A tool result is returned as a single
textcontent block that holds a JSON document. - Configuration comes only from environment variables. Since v0.11, stdio
children get a least-privilege environment, so you must pass these variables
explicitly with
stdio.envorstdio.env_passthrough. See Child Process Environment.
Postgres¶
Query a PostgreSQL database. Read-only by default.
Environment variables. The full table, with the reasoning behind the read-only design, is in Configuration › First-Party Servers.
| Variable | Default |
|---|---|
LEANPROXY_POSTGRES_CONNECTION |
required, e.g. postgres://user:pass@host:5432/db |
LEANPROXY_POSTGRES_POOL_SIZE |
10 |
LEANPROXY_POSTGRES_STATEMENT_TIMEOUT |
30s |
LEANPROXY_POSTGRES_READ_ONLY |
true |
Tools.
| Tool | Arguments | Notes |
|---|---|---|
postgresql_query |
query |
Runs inside a READ ONLY transaction that is rolled back, whatever READ_ONLY says. Only SELECT, EXPLAIN and WITH are accepted. One statement per call. |
postgresql_list_tables |
schema (optional, default public) |
Tables with estimated row counts. |
postgresql_describe |
table (e.g. public.users) |
Column names, types, nullability. |
postgresql_execute |
statement |
INSERT, UPDATE, DELETE, DDL. Only registered when LEANPROXY_POSTGRES_READ_ONLY=false. |
Safety defaults.
- Read-only mode is on unless you set
LEANPROXY_POSTGRES_READ_ONLY=false. - The server connects and pings the database at start. It exits with an error if the connection string is missing or the database is unreachable.
- The flag is defense in depth. Connect with a database role that has only the privileges you want to grant.
servers:
- name: postgres
transport: stdio
stdio:
command: /home/me/bin/leanproxy-mcp-postgres
env:
- "LEANPROXY_POSTGRES_CONNECTION=${APP_DB_URL}" # expanded from the proxy's environment
- "LEANPROXY_POSTGRES_STATEMENT_TIMEOUT=15s"
Redis¶
Read and write keys on a Redis server.
Environment variables. Full table in Configuration › Redis.
| Variable | Default |
|---|---|
LEANPROXY_REDIS_ADDRESS |
127.0.0.1:6379 |
LEANPROXY_REDIS_PASSWORD |
none |
LEANPROXY_REDIS_POOL_SIZE |
10 |
LEANPROXY_REDIS_TLS |
off (true or 1 enables TLS 1.2+) |
LEANPROXY_REDIS_DIAL_TIMEOUT |
5s |
LEANPROXY_REDIS_COMMAND_TIMEOUT |
5s |
LEANPROXY_REDIS_MAX_BULK_LEN |
16777216 (16 MiB) |
LEANPROXY_REDIS_MAX_ARRAY_LEN |
1000000 |
The server always uses database 0. There is no variable to select another one.
Tools.
| Tool | Arguments | Redis command |
|---|---|---|
redis_get |
key |
GET |
redis_set |
key, value, ttl_seconds (optional) |
SET (with EX when a TTL is given) |
redis_delete |
keys (array) |
DEL |
redis_keys |
pattern (glob, e.g. user:*) |
KEYS |
redis_exists |
keys (array) |
EXISTS |
Safety defaults.
- There is no generic "run any command" tool.
FLUSHALL,CONFIG,EVALand similar commands cannot be reached. - There is no read-only mode:
redis_setandredis_deleteare always exposed. To make it read-only, deny both tools with a policy rule (match: "redis.redis_set"andmatch: "redis.redis_delete",action: deny), or remove write permissions from the Redisdefaultuser. The server authenticates withAUTH <password>, so it always connects asdefault. redis_keysusesKEYS, which blocks Redis while it scans. Avoid broad patterns on large production databases.- The server opens all pool connections at start and exits if Redis is unreachable.
servers:
- name: redis
transport: stdio
stdio:
command: /home/me/bin/leanproxy-mcp-redis
env:
- "LEANPROXY_REDIS_ADDRESS=cache.internal:6380"
- "LEANPROXY_REDIS_TLS=true"
env_passthrough: ["LEANPROXY_REDIS_PASSWORD"] # copied from the proxy's environment
Filesystem¶
Read and write files under one local directory.
Environment variables.
| Variable | Default | Description |
|---|---|---|
LEANPROXY_FILESYSTEM_ROOTS |
required | Comma-separated list of directories. Only the first entry is used (see the warning below). |
The server exits at start if the variable is empty or the first directory
cannot be opened. Use an absolute path: a relative path is resolved against the
server's working directory (stdio.cwd).
Only the first root is served
The server accepts a comma-separated list and logs all of it, but every tool resolves paths against the first directory only. Paths under the other directories cannot be reached. Run one server entry per directory if you need several.
Tools. All paths are relative to the root. Absolute paths and paths that
contain .. are rejected. The root is opened with Go's os.Root, so symbolic
links cannot lead outside it either.
| Tool | Arguments | Notes |
|---|---|---|
read_file |
path |
Returns at most the first 1 MiB. Larger files come back with "truncated": true. |
read_multiple_files |
paths (array) |
Same 1 MiB limit per file. Per-file errors are listed in errors; the call itself succeeds. |
write_file |
path, content |
Creates or overwrites the file. Creates missing parent directories. |
list_directory |
path (. for the root) |
Name, size, is_dir and mode of each entry. |
file_info |
path |
Name, size, is_dir, mode, modification time (UTC). |
search_files |
pattern, root (optional subdirectory, default .) |
Matches each file's path relative to the root with Go's filepath.Match. |
search_files patterns
filepath.Match has no recursive **, and * does not cross /. The
pattern is matched against the whole relative path, so *.go finds only
Go files at the top level, and **/*.go behaves like */*.go (exactly
one directory deep). Write one pattern per depth, for example *.go,
*/*.go, */*/*.go.
Safety defaults.
- Access is confined to one directory.
- There is no read-only mode:
write_fileis always exposed. To block writes, denywrite_filewith a policy rule (match: "files.write_file",action: deny), or make the directory read-only for the user that runs the server.
servers:
- name: files
transport: stdio
stdio:
command: /home/me/bin/leanproxy-mcp-filesystem
env:
- "LEANPROXY_FILESYSTEM_ROOTS=/home/me/projects/app"
GitHub¶
Read repositories and issues from GitHub, and open pull requests when a token is set.
Environment variables.
| Variable | Default | Description |
|---|---|---|
GITHUB_TOKEN |
none | Personal access token. Without it, the server runs unauthenticated and read-only. |
Tools.
| Tool | Arguments | Available |
|---|---|---|
list_repos |
owner, type (optional), per_page (optional, 1–100, default 30) |
Always |
get_issue |
owner, repo, issue_number |
Always |
create_pr |
owner, repo, title, head, base, body (optional) |
Only with GITHUB_TOKEN |
list_repos calls GitHub's "list repositories for a user" endpoint
(GET /users/{owner}/repos). That endpoint returns public repositories only,
with or without a token. Only the first page is returned.
Safety defaults.
- Without
GITHUB_TOKEN,create_pris not listed and calling it fails. The server prints a notice on stderr at start. - With a token, what the server can do is limited by the token's scopes. Use a fine-grained token limited to the repositories you need.
- The server keeps its own client-side budget of 5000 calls per hour. When it is used up, calls fail with a "rate limit exceeded" error until it resets. GitHub's own limit for unauthenticated requests is much lower (60 per hour), so without a token you will usually hit GitHub's limit first.
servers:
- name: github
transport: stdio
stdio:
command: /home/me/bin/leanproxy-mcp-github
env_passthrough: ["GITHUB_TOKEN"] # omit to run read-only
See also¶
- Configuration › First-Party Servers for the detailed Postgres and Redis reference.
- Security for the protections LeanProxy-MCP applies to every server, first-party or not.