Two problems, one root cause: the server treated every Pinchflat endpoint as fair game.
1. get_settings leaked the app's own API token. Pinchflat's settings record embeds api_token; the tool returned the whole row verbatim, so any tool caller received a credential that outlives the MCP session and grants full API access. Fixed by scrubbing at the transport boundary (_scrub), not per-tool: every response from _get/_post/_put/_delete now redacts api_token, api_key, token, secret, password, password_hash, youtube_api_key, and apprise_server, recursively (nested dicts and lists included).
2. Admin/config tools were exposed to every caller.get_settings and get_app_info describe the installation, not a subscription. They now live in an admin tier that is not registered unless PINCHFLAT_MCP_TOOLSET asks for it:
subscriptions (default): sources, media, profiles, tasks — no settings, no app info.
admin / all: the above plus get_settings and get_app_info.
Registration is conditional (@tool("admin") simply doesn't register), so the tools are absent from tools/list rather than present-and-refusing — nothing to discover or probe.
3. update_source could not set retention. The whitelist omitted retention_period_days, which forced a direct REST call with the leaked token to do routine retention work. Added retention_period_days, prevent_culling and download_media to update_source, and retention_period_days to create_source. An empty update now returns a message instead of issuing an empty PUT.
Also folded delete_media's hand-rolled request into _delete (same scrubbing, same timeout) and documented that delete_source does not delete files from disk.
Verified offline with stubbed fastmcp/httpx: default toolset exposes 18 tools with neither admin tool present; admin exposes both plus the subscription tools; nested api_token/youtube_api_key/password all come back [redacted]; update_source(retention_period_days=30) emits the expected PUT payload.
Two problems, one root cause: the server treated every Pinchflat endpoint as fair game.
**1. `get_settings` leaked the app's own API token.** Pinchflat's settings record embeds `api_token`; the tool returned the whole row verbatim, so any tool caller received a credential that outlives the MCP session and grants full API access. Fixed by scrubbing at the transport boundary (`_scrub`), not per-tool: every response from `_get`/`_post`/`_put`/`_delete` now redacts `api_token`, `api_key`, `token`, `secret`, `password`, `password_hash`, `youtube_api_key`, and `apprise_server`, recursively (nested dicts and lists included).
**2. Admin/config tools were exposed to every caller.** `get_settings` and `get_app_info` describe the installation, not a subscription. They now live in an `admin` tier that is not registered unless `PINCHFLAT_MCP_TOOLSET` asks for it:
- `subscriptions` (default): sources, media, profiles, tasks — no settings, no app info.
- `admin` / `all`: the above plus `get_settings` and `get_app_info`.
Registration is conditional (`@tool("admin")` simply doesn't register), so the tools are absent from `tools/list` rather than present-and-refusing — nothing to discover or probe.
**3. `update_source` could not set retention.** The whitelist omitted `retention_period_days`, which forced a direct REST call with the leaked token to do routine retention work. Added `retention_period_days`, `prevent_culling` and `download_media` to `update_source`, and `retention_period_days` to `create_source`. An empty update now returns a message instead of issuing an empty PUT.
Also folded `delete_media`'s hand-rolled request into `_delete` (same scrubbing, same timeout) and documented that `delete_source` does not delete files from disk.
Verified offline with stubbed `fastmcp`/`httpx`: default toolset exposes 18 tools with neither admin tool present; `admin` exposes both plus the subscription tools; nested `api_token`/`youtube_api_key`/`password` all come back `[redacted]`; `update_source(retention_period_days=30)` emits the expected PUT payload.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two problems, one root cause: the server treated every Pinchflat endpoint as fair game.
1.
get_settingsleaked the app's own API token. Pinchflat's settings record embedsapi_token; the tool returned the whole row verbatim, so any tool caller received a credential that outlives the MCP session and grants full API access. Fixed by scrubbing at the transport boundary (_scrub), not per-tool: every response from_get/_post/_put/_deletenow redactsapi_token,api_key,token,secret,password,password_hash,youtube_api_key, andapprise_server, recursively (nested dicts and lists included).2. Admin/config tools were exposed to every caller.
get_settingsandget_app_infodescribe the installation, not a subscription. They now live in anadmintier that is not registered unlessPINCHFLAT_MCP_TOOLSETasks for it:subscriptions(default): sources, media, profiles, tasks — no settings, no app info.admin/all: the above plusget_settingsandget_app_info.Registration is conditional (
@tool("admin")simply doesn't register), so the tools are absent fromtools/listrather than present-and-refusing — nothing to discover or probe.3.
update_sourcecould not set retention. The whitelist omittedretention_period_days, which forced a direct REST call with the leaked token to do routine retention work. Addedretention_period_days,prevent_cullinganddownload_mediatoupdate_source, andretention_period_daystocreate_source. An empty update now returns a message instead of issuing an empty PUT.Also folded
delete_media's hand-rolled request into_delete(same scrubbing, same timeout) and documented thatdelete_sourcedoes not delete files from disk.Verified offline with stubbed
fastmcp/httpx: default toolset exposes 18 tools with neither admin tool present;adminexposes both plus the subscription tools; nestedapi_token/youtube_api_key/passwordall come back[redacted];update_source(retention_period_days=30)emits the expected PUT payload.