DEV Community

chovy
chovy

Posted on Originally published at dev.profullstack.com

myna lists your product now, over MCP

myna posts to social networks. As of 0.17.0 it also lists the product itself, in a software directory, over that directory's MCP server.

myna directory saasrow https://example.com --dry-run
myna directory saasrow https://example.com
Enter fullscreen mode Exit fullscreen mode

It reads the page, works out the name, writes the description, picks the category, and submits.

A listing is not a post

I started by trying to make a directory just another network adapter. That was wrong. A post is text with optional media. A listing is a name, a website, a description, a category and some controlled vocabulary, and a human being at the directory reads it before anyone else does. Pushing that through post() would have given all 30 network adapters fields they have no use for.

So directories are a separate subsystem with a separate command and separate credentials.

The credentials part turned out to matter more than I expected. myna post --to all resolves to every account whose network is not marked as an explicit target, and a network it does not recognise reads as "not explicit". A directory key stored among the accounts would therefore have quietly become a posting target. Somebody's stray thought about lunch, submitted as a product. Directory keys live in their own part of the vault, and there is a test that fails if that ever changes.

Reading the server instead of guessing

The interesting half is that myna is now an MCP client, not only a server.

SaaSRow publishes an MCP endpoint. So rather than writing a REST client against its listing API, myna calls its tools. The tool schemas describe the fields, which means SaaSRow can add one without a myna release.

That works so well it seemed silly to stop at directories myna ships:

myna directory add acme https://acme.example/api/mcp
myna directory tools acme
myna directory acme https://example.com
Enter fullscreen mode Exit fullscreen mode

There is no adapter behind that. myna reads the tool table, finds whichever tool creates a listing whatever it happens to be called, and sends each field under the name that tool's own schema uses. A directory whose field is product_url rather than website needs no code. A field the server marks required and myna could not work out fails before the call rather than after it.

myna directory tools <id> prints what a server actually offers, which is the first useful thing to look at when a submission gets refused.

Signing in is the exception. There is no MCP method for it, and SaaSRow's is an emailed one-time code, which is a conversation with a person rather than a tool call. So that adapter stays written, and a custom directory takes a key you already have.

Two things I got wrong on the way

The product name came out as widget.test in testing. fetchPage falls back to the hostname when a page declares no og:site_name, and I was preferring site name over title. A site name equal to the host carries no information. Fixed, with a test.

And an empty result printed nothing at all, which reads as a command that never ran rather than a directory with nothing to say. Empty output is not a pass.

Where it is

myna directory in the shell, /directory in the TUI, a Directories screen in the desktop app, and four tools over MCP so an agent can do it. cli-tools 0.31.0 adds /myna:directory as a slash command.

myna 0.17.0 is out: https://github.com/profullstack/mynaposter/releases/tag/v0.17.0

This post was drafted with AI assistance and edited by me.

Top comments (0)