---
name: facet
description: Suggests instructions for a prompt the person is writing, such as an agent's system prompt or a skill, from a library of prompts broken into their separate instructions. Use it when the person writes or reviews a prompt, and to rate the suggestions and report how they worked.
---

# Prompt Analytics

Prompt Analytics reads a library of prompts written by many authors and broken into their separate instructions. You reach it as the MCP server and skill named facet. For a prompt the person is writing, it shows what the closest library prompts add, what their authors write more often than the rest, and, with a filter, what prompts of the same kind write most.

## Set up

If facet is already set up in this agent, skip this section.

The person gave you a key with this file. These steps suit any agent, whichever company makes it.

An agent with MCP over HTTP adds the server facet at https://facet-prompt-analytics.fly.dev/mcp with the header `Authorization: Bearer KEY`, with the key in place of KEY. In Claude Code, that is this command.

    claude mcp add --transport http facet https://facet-prompt-analytics.fly.dev/mcp --header "Authorization: Bearer $FACET_KEY"

Ask the person to set FACET_KEY to the key in their user-level environment first, so the key stays off the command line. Type the key in place of $FACET_KEY only when FACET_KEY is not set. Some agents hide environment variables whose names contain KEY, TOKEN or SECRET from the commands they run. If $FACET_KEY comes out empty, put the key in the agent's user-level MCP settings instead.

Other agents take the same URL and header in their own MCP settings. An agent without MCP over HTTP calls the JSON API instead, as "Without MCP" below describes.

Put the key only in your agent's user-level settings or in an environment variable, such as FACET_KEY. Never put it in a file inside a repository, such as AGENTS.md, a rules file or a project's MCP config, and keep it out of your replies.

The person revokes or replaces a key on the Keys page at https://facet-prompt-analytics.fly.dev/keys. If your calls start getting 401 with "this key is not recognised or was revoked", ask the person for a new key.

Save this file where your agent keeps standing instructions, so later sessions know how to use facet. This file holds no key. In Claude Code, that is ~/.claude/skills/facet/SKILL.md. An agent that reads AGENTS.md or a rules file can keep it there, after asking the person, since those files are often shared in a repository. Change no permissions to do this. If your agent loads MCP tools when a session starts, tell the person to restart the session.

## Use

Before you send the first prompt in a session, tell the person that it leaves this machine and that facet keeps it with their records, with no end date, unless they turn that off on the Keys page.

1. Call recommend with the prompt's text. Leave the filter out at first. To add one, call library to read the context axes, choose the one or two that matter most, and pass them as cell, the filter, such as {"subject": "health"}.
2. Show the person the suggestions that fit their prompt, each with the close prompt it came from. Read a close prompt with get_prompt before you recommend what it adds.
3. Every answer carries a record, the saved run, with its id. Ask the person which suggestions helped. Call rate with the record's id, a thumb for the whole run and a thumb for each suggestion they judged, by the suggestion's unit, its instruction id.
4. After the person applies suggestions and runs their agent, you may report what they adopted and how the run went. The record's "agent reports" holds their setting. With "ask", ask them first and send the report with user_agreed true only if they agree. With "always", you may report without asking. Leave user_agreed out unless you asked. With "never", send none. If the person declines a report, facet keeps nothing. Send it with user_agreed false, or send nothing.

### What an answer holds

"placed" lists the prompt's sentences, each with the library instruction it was read as. "carries" lists the instructions the prompt was read to carry, which the lists leave out. "close prompts" holds two searches, "by shared instructions" and "by meaning". Each has "neighbours", the close prompts with their ids, and "from each neighbour", what each close prompt adds that the prompt lacks. Each also has "distinctive", the instructions the close prompts' authors write more than other authors, and "similar", the instructions most of them write. With a cell, "frequent" holds what the cell's authors write most, "concentrated" what they write more than the same axis's other values, and "trending" what rises along an ordered column. full adds the similar lists grouped by aspect and, without a cell, the library's most common instructions. Every suggestion is an item with its "unit", the instruction's id that rate and report take, and its "name", as the sample answer under "Without MCP" shows.

## A repository's prompts

When the person asks you to find the prompts in a repository and analyze them, follow these steps.

1. Find text an LLM call reads. That is system prompts and prompt templates in string literals and f-strings, and files such as .md, .txt, .tpl, .j2, .jinja and .prompt that the code loads as prompts. Skip READMEs, changelogs, licenses, docs and test fixtures unless the code loads them as prompts. Skip vendored and build folders and anything .gitignore excludes.
2. Put each prompt together whole. Join concatenated strings, f-strings and joined lists. Keep placeholders such as {question} as they are, and never fill them from fixtures or logs. Give a template's branches separately, each named.
3. Never send .env files, credentials, or text that looks like a key or token. If a prompt holds a secret, replace it with a placeholder such as {SECRET} and tell the person.
4. List what you found, each prompt with its file and line. A prompt found in several places is listed once, with every place. With many prompts, group them by folder.
5. Before sending anything, tell the person that the chosen prompts leave this machine and that facet keeps them with their records, with no end date, unless they turn that off on the Keys page. Ask whether that is all right, and which prompts to send.
6. Call recommend on each chosen prompt, one at a time. Show each prompt's suggestions under its file and line, with the close prompts they came from and the record's link, https://facet-prompt-analytics.fly.dev/records/ID.
7. On a later scan, send again only prompts that changed, and ask first.

facet keeps the prompts the person sends with their records, unless they turn this off. Ratings and reports are kept with the record and change no list. records shows the person's records.

## Without MCP

An agent without MCP calls six of the tools as JSON over HTTPS at https://facet-prompt-analytics.fly.dev/api, which are recommend, rate, report, records, library and get_prompt. Every call sends the header `Authorization: Bearer KEY`. Read the key from an environment variable, such as FACET_KEY, rather than writing it into a command, where shell history keeps it. A POST also sends `Content-Type: application/json` and a JSON object. The answers are the tools' own answers. An error is a JSON object with "error" and "status". The status is 400 for a malformed call or a refused value, 401 for a missing or revoked key, 404 for a record that is not the person's or a library prompt that does not exist, 409 when the person's report setting refuses a report, and 503 while the library is loading after a restart. After a 503, wait the seconds its Retry-After header gives and try again. A recommend answered 503 saved no record, so the retry makes no duplicate. Through MCP, the same moment gives an error that says the library is loading.

- `POST https://facet-prompt-analytics.fly.dev/api/recommend` takes the recommend tool's fields. Give "text", the prompt. You may add "cell", the filter, and "full". The answer holds the suggestions and "record", the saved run, with its "id".
- `POST https://facet-prompt-analytics.fly.dev/api/rate` takes "record", the record's id, with "run", which is "up" or "down", and "items", which maps an instruction id (a suggestion's "unit") to "up" or "down". "note" is optional.
- `POST https://facet-prompt-analytics.fly.dev/api/report` takes "record", "result" (a few sentences on how the run went), "adopted" (instruction ids) and "user_agreed".
- `GET https://facet-prompt-analytics.fly.dev/api/records` lists the person's newest records, 20 unless "?n=" says more. `GET https://facet-prompt-analytics.fly.dev/api/records/ID` shows one record with its ratings and reports.
- `GET https://facet-prompt-analytics.fly.dev/api/library` gives the library tool's answer, with the context axes, their definitions and every value's criterion. "?column=" lists one column's values whole.
- `GET https://facet-prompt-analytics.fly.dev/api/prompts/ID` gives one library prompt, a close prompt's id from an answer, with its text. "?chars=" sets how much of the text, 2000 unless it says more.

For example, this call asks for suggestions.

    curl -s https://facet-prompt-analytics.fly.dev/api/recommend -H "Authorization: Bearer $FACET_KEY" -H "Content-Type: application/json" \
      -d '{"text": "You are a support agent. Answer from the help center articles only."}'

Its answer starts like this.

    {"library": {"library": "pooled", ...}, "record": {"id": "r_0a1b2c3d4e", "prompt kept": true, "agent reports": "ask"},
     "placed": [...], "carries": [...],
     "close prompts": {"by shared instructions": {"neighbours": [...],
        "from each neighbour": [{"id": "...", "adds": [{"unit": 181865, "name": "Cite the article you answer from", "aspect": "content", ...}]}],
        "distinctive": {"items": [...]}, "similar": {"items": [...]}},
      "by meaning": {...}}}

This call rates that run and one suggestion in it.

    curl -s https://facet-prompt-analytics.fly.dev/api/rate -H "Authorization: Bearer $FACET_KEY" -H "Content-Type: application/json" \
      -d '{"record": "r_0a1b2c3d4e", "run": "up", "items": {"181865": "down"}}'

It answers {"record": "r_0a1b2c3d4e", "rated": {"run": "up", "181865": "down"}}.
