Drive PSF Guard from an agent with MCP

A running PSF Guard server is also a Model Context Protocol (MCP) server. An agent such as Claude Code can list your catalogs, read grades and quality evidence, score a night's frames, apply grades, and start imports, quality scans and WBPP runs. The endpoint is /api/mcp on the same port as the grader, and the same login rules guard it.

Create an API token

On a server with user accounts, an agent signs in with a personal API token rather than a password. Open Settings → Users → API tokens, choose New token, give it a label such as claude on laptop, and pick whether it is read only and when it expires. The token is shown once. Copy it then.

The command line does the same and prints the token once. Restart the server after a CLI change, as with user accounts:

psf-guard users token create editor --label "claude on laptop" --expires-days 90
psf-guard users token list
psf-guard users token revoke <id>

A token acts as its user and never gains more than that user has. A viewer's token reads; an editor's token can also grade and start jobs unless it was minted read only. Everyone signed in manages their own tokens; an editor sees them all and can mint one for another user. A token cannot mint or revoke tokens, so a leaked token cannot renew itself. Revoking a token stops it at once, and removing a user removes that user's tokens.

A loopback server with no accounts, which is what the desktop app and psf-guard server on your own machine run, needs no token. A network server with no accounts answers 401 here as it does everywhere else; see Server Accounts.

Connect a client

Any client that speaks MCP's streamable HTTP transport works. Give it the URL and the token as a bearer header. For Claude Code:

claude mcp add --transport http psf-guard https://guard.example/api/mcp \
  --header "Authorization: Bearer psfg_…"

On the desktop app or a local server with no accounts, drop the header:

claude mcp add --transport http psf-guard http://127.0.0.1:3000/api/mcp

The endpoint is stateless and answers in plain JSON, so a reverse proxy needs nothing extra and there is no session to expire. Revoke the token to cut an agent off.

What the tools do

Every tool but list_databases takes a database: the id or name that list_databases reports. Results are the same JSON the grader itself reads.

ToolWhat it answersNeeds
list_databasesOpen catalogs: id, name, path, image foldersread
list_projectsProjects with targets, plans, progress and recent framesread
list_targetsTargets with coordinates, grade counts and last captureread
list_imagesLights with grade, filter, exposure and stored metrics, filtered by project, target or grade, in pagesread
get_imageOne image: grade, file location, header metadata, metricsread
get_image_qualityScore, issues found, and place in the sequence, from stored evidenceread
analyze_sequenceRelative scores and suggested rejects for a target, a project, or the whole databaseread
get_statisticsWhole-database countsread
get_calibration_reportCalibration frames the library matches to a project's nightsread
get_sky_coverageEvery target's footprint and exposure by filterread
get_jobsImport, quality scan and WBPP progressread
astrobin_csvThe AstroBin acquisition CSV for a project or targetread
grade_imagesSet accepted, rejected or pending with a reason; returns the grades it replacedwrite
start_quality_backfillStart the background quality scanwrite
start_importScan the configured folders for new lights and calibration frameswrite
start_wbpp_runRun PixInsight WBPP on a project's or target's accepted lightswrite and database management
cancel_wbpp_runStop the running WBPP runwrite and database management

Jobs return at once; the agent polls get_jobs. A tool that the token may not use answers with a tool error that says so, not a protocol failure, so the agent can explain it.

The server also hands the agent PSF Guard's ground rules: sequence analysis and quality context suggest, and nothing changes until grade_images runs; catalog predictions and header values are not pixel evidence, and a conclusion should say which it rests on.

Try it without a client

TOKEN=psfg_…
curl -s https://guard.example/api/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"list_databases","arguments":{}}}'
The reference, with the token file format and operator notes, is docs/MCP.md in the main repository.

More projects from theatr.us