diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json new file mode 100644 index 0000000..6284c57 --- /dev/null +++ b/.cursor-plugin/marketplace.json @@ -0,0 +1,20 @@ +{ + "name": "readme", + "owner": { + "name": "ReadMe", + "email": "support@readme.io" + }, + "metadata": { + "description": "ReadMe plugins for AI coding agents" + }, + "plugins": [ + { + "name": "readme", + "source": "cursor", + "description": "Search, read, and update your ReadMe docs, API reference, and changelog.", + "minClientVersions": { + "cursor": "3.13.0" + } + } + ] +} diff --git a/.cursor-plugin/plugin.json b/.cursor-plugin/plugin.json deleted file mode 100644 index 9143deb..0000000 --- a/.cursor-plugin/plugin.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "name": "readme", - "displayName": "ReadMe", - "version": "1.0.0", - "minClientVersions": { - "cursor": "3.13.0" - }, - "description": "Search, read, and update your ReadMe docs, API reference, and changelog.", - "author": { - "name": "ReadMe", - "email": "support@readme.io" - }, - "homepage": "https://docs.readme.com/main/docs/readmes-mcp-server", - "repository": "https://github.com/readmeio/agent-plugins", - "license": "MIT", - "logo": "assets/logo.svg", - "keywords": [ - "readme", - "documentation", - "docs", - "api-reference", - "openapi", - "changelog", - "mcp" - ], - "category": "integrations", - "tags": [ - "readme", - "documentation", - "api", - "mcp" - ], - "variables": { - "type": "object", - "properties": { - "README_API_KEY": { - "type": "string", - "title": "ReadMe API key", - "description": "API key from ReadMe Account Settings > API Keys (starts with rdme_). Grants read and write access to the projects the key belongs to." - } - }, - "required": [ - "README_API_KEY" - ] - }, - "mcpServers": { - "readme": { - "url": "https://docs.readme.com/mcp", - "headers": { - "Authorization": "Bearer ${README_API_KEY}" - } - } - } -} diff --git a/.github/workflows/validate-claude-plugin.yml b/.github/workflows/validate-claude-plugin.yml deleted file mode 100644 index 3c02353..0000000 --- a/.github/workflows/validate-claude-plugin.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Validate Claude plugin - -on: - pull_request: - push: - branches: [main] - -permissions: - contents: read - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 22 - - run: npm install -g @anthropic-ai/claude-code - - run: claude plugin validate --strict claude - - run: claude plugin validate --strict .claude-plugin/marketplace.json diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..88ad04c --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,33 @@ +name: Validate + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Skills are identical across clients + run: node scripts/sync-skills.mjs --check + + - name: Skill frontmatter and size + run: node scripts/validate-skills.mjs + + - run: npm install -g @anthropic-ai/claude-code + + - name: Claude plugin manifest + run: claude plugin validate --strict claude + + - name: Claude marketplace manifest + run: claude plugin validate --strict .claude-plugin/marketplace.json diff --git a/README.md b/README.md index a6aece9..4c17b3e 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,87 @@ -# ReadMe +# ReadMe agent plugins -Search, read, and update your ReadMe documentation from your editor. +Official [ReadMe](https://readme.com) plugins that connect AI coding agents to ReadMe's +[MCP server](https://docs.readme.com/main/docs/readmes-mcp-server), so agents can search your +guides and API reference, inspect OpenAPI specs, draft changelog entries, and open documentation +updates for review. -## Overview +One plugin per client, because each client has its own manifest format and its own way of handling +credentials. -This plugin connects compatible AI assistants to [ReadMe](https://readme.com) through ReadMe's -[MCP](https://modelcontextprotocol.io/) server at `https://docs.readme.com/mcp`. +| Client | Plugin | Marketplace manifest | Install | +| ------------------- | --------------------- | ----------------------------------- | ------------------------------------------------------------------------- | +| Cursor | [`cursor/`](cursor/) | `.cursor-plugin/marketplace.json` | `/add-plugin readme`, or **Cursor Settings → Plugins** | +| Claude | [`claude/`](claude/) | `.claude-plugin/marketplace.json` | `/plugin marketplace add readmeio/agent-plugins` | +| ChatGPT and Codex | [`codex/`](codex/) | `.agents/plugins/marketplace.json` | `codex plugin marketplace add readmeio/agent-plugins` | -Once connected, your assistant can search your guides and API reference, inspect your OpenAPI specs, -draft changelog entries, and open documentation updates for review — without leaving the editor. +Each plugin directory has its own README with install steps and auth setup. -## Installation +Claude and Codex take the repository itself as a marketplace from the command line, as above. Cursor +has no CLI equivalent: teams add this repository under **Dashboard → Plugins → Team Marketplaces → +Add Marketplace → Import from Repo**, and Cursor indexes it from the root manifest. -### Cursor +## Repository structure -1. Open **Customize** in Cursor's sidebar. -2. Find **ReadMe** in the marketplace. -3. Select **Install** and choose project or user scope. -4. Set your **ReadMe API key** when prompted (see below). +The repository root is a marketplace for all three clients — it is not itself a plugin. Each client +reads only its own marketplace manifest and ignores the others. -### API key - -The server authenticates with a ReadMe API key. Open **Account Settings → API Keys** in ReadMe and -create a key. Paste it into **Plugins → Configure** as **ReadMe API key**. - -The key decides which projects the assistant can reach, and grants read and write access to them. -Rotate it from Account Settings if it is ever exposed. - -Once connected, ask Cursor to work with your docs, for example: "Find our authentication guide and -add a section on refresh tokens." - -## MCP - -```json -{ - "mcpServers": { - "readme": { - "url": "https://docs.readme.com/mcp", - "headers": { - "Authorization": "Bearer ${README_API_KEY}" - } - } - } -} +``` +agent-plugins/ +├── .cursor-plugin/marketplace.json # Cursor marketplace → cursor +├── .claude-plugin/marketplace.json # Claude marketplace → ./claude +├── .agents/plugins/marketplace.json # Codex marketplace → ./codex +├── skills/ # Canonical skills — edit these +├── cursor/ +│ ├── .cursor-plugin/plugin.json +│ ├── mcp.json +│ ├── skills/ # Generated from ../skills +│ ├── assets/logo.svg +│ ├── README.md +│ ├── CHANGELOG.md +│ └── LICENSE +├── claude/ +│ ├── .claude-plugin/plugin.json +│ ├── .mcp.json +│ ├── skills/ # Generated from ../skills +│ └── README.md +├── codex/ +│ ├── plugin.json # Agent Plugins 1.0.0 manifest +│ ├── mcp.json +│ ├── skills/ # Generated from ../skills +│ └── README.md +├── scripts/ +├── README.md +└── LICENSE ``` -## Tools - -| Tool | What it does | -| ------------------ | -------------------------------------------------------------------- | -| `search` | Search guide pages, reference pages, and docs content by keyword | -| `fetch` | Retrieve a specific guide or reference page by ID | -| `update-docs` | Open a documentation update on a new branch and return a review link | -| `list-specs` | List the OpenAPI specs available in the project | -| `search-endpoints` | Search paths, operations, and parameters | -| `list-endpoints` | List all API paths and HTTP methods with summaries | -| `get-endpoint` | Get detail on one endpoint, including security schemes and servers | -| `execute-request` | Execute an API request from a HAR request object | -| `draft-changelog` | Generate a changelog draft from merged GitHub PRs | +## Skills -`update-docs` writes to a new branch and returns a review link, so your published docs are never -edited in place. `execute-request` sends a real request to the API in the spec. +The three skills are agent-agnostic: anything a client does differently — registering an API key, +driving a browser, installing the plugin by hand — is a per-client table inside the shared file, so +there is one copy of every fact. They carry only what the MCP server cannot tell an agent at +runtime; route maps come from `list-endpoints` and `get-endpoint`, not from a skill. -## Which ReadMe MCP server is this? +Edit `skills/` and nothing else, then regenerate the three published copies: -ReadMe has two kinds of MCP server, and this plugin is the first one. +``` +node scripts/sync-skills.mjs +``` -**ReadMe's MCP server** (`https://docs.readme.com/mcp`) is what this plugin installs. You use it to -manage the documentation in your own ReadMe project. +CI runs `node scripts/sync-skills.mjs --check` and fails if a client copy has drifted. Each client +ships its own directory because each marketplace submission reads only that directory. -**Your project's MCP server** (`https://your-project.readme.io/mcp`) is the one ReadMe generates -from your API spec for _your_ users. Every project has its own URL, so it cannot be installed from -the marketplace. To connect to one, use the instructions published on that project's hub. See -[your project's MCP server](https://docs.readme.com/main/docs/your-projects-mcp-server). +## Authentication -## Support +All three plugins talk to the same endpoint, `https://docs.readme.com/mcp`, and all three ship +anonymous. Without a key you get read-only access to public docs, which is enough for searching and +reading, and none of the plugins prompt for anything on install. -- **ReadMe’s MCP server:** https://docs.readme.com/main/docs/readmes-mcp-server -- **Report issues:** https://github.com/readmeio/agent-plugins/issues -- **Contact support:** support@readme.io +Write tools such as `update-docs` need a ReadMe API key. That step is the user's, not the package's, +because each client wires credentials differently and the Agent Plugins spec forbids putting them in +a plugin at all: it requires headers to be literal package data with no secrets in them, and Codex +strips `Authorization` from plugin MCP configs regardless. In every client the shape is the same, +register the server yourself with the key and your entry replaces the plugin's anonymous one. Each +plugin README has the exact snippet. ## License diff --git a/claude/README.md b/claude/README.md index 8ae4d7e..3e04c18 100644 --- a/claude/README.md +++ b/claude/README.md @@ -59,4 +59,14 @@ Keep the single quotes so Claude Code expands the variable at startup instead of One key maps to one project. To switch projects, change the exported key and restart. -Claude Desktop Chat, Cowork and claude.ai cannot take an API key, so the plugin stays read-only there. \ No newline at end of file +Claude Desktop Chat, Cowork and claude.ai cannot take an API key, so the plugin stays read-only there. + +--- + +## Skills + +| Skill | What it does | +| ----- | ------------ | +| `mcp-server` | Keeps the agent straight on which project and which spec a call lands in: this server reads ReadMe's own docs, while `execute-request` plus your key acts on yours | +| `mcp-auth` | Establishes which key is in play and diagnoses the failures a missing or unresolved one produces | +| `onboarding` | Walks a new customer from signup to a published hub and a working API key | diff --git a/claude/skills/mcp-auth/SKILL.md b/claude/skills/mcp-auth/SKILL.md new file mode 100644 index 0000000..f26cc61 --- /dev/null +++ b/claude/skills/mcp-auth/SKILL.md @@ -0,0 +1,111 @@ +--- +name: mcp-auth +description: Establish or repair the ReadMe API key behind execute-request. Use when a ReadMe call fails with "Missing Security Schemes", "The API key couldn't be located", or "An unknown error has occurred", when the user asks which project their key reaches, when they say they set a key but it is not working, and before the first write to their project. +--- + +# The key behind execute-request + +The plugin ships anonymous. Public reads of ReadMe's documentation need no key; anything touching +the user's own project does, and it travels on the MCP server registration rather than in the tool +call. See the `mcp-server` skill for which calls land where. + +## Tools + +- `execute-request` + +## Assume anonymous + +Most users have not registered a key, so do not spend a call proving it. Go straight to the work: + +- ReadMe documentation questions — `search` and `fetch`, no key involved. +- The user's project, and they have not mentioned a key — go to **No key yet** below. +- The user says they registered one, or an `execute-request` call fails — verify with the probe. + +## Verify + +`execute-request`, spec title `ReadMe API`, with no `Authorization` header of your own: + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +| Response | Means | Next | +| --- | --- | --- | +| A project object | The registration carries a working key | Name the project and subdomain, carry on. Ask for nothing | +| `Missing Security Schemes` | No `Authorization` header at all — the server is anonymous | **No key yet** | +| `"title": "The API key couldn't be located."`, status 401 | A key is being sent, but it is not a real key | **A key is set but wrong** | +| `"title": "An unknown error has occurred."`, status 500 | The bearer is empty — the variable resolved to nothing | **A key is set but wrong** | + +Resolve this once per session. Having seen a project object, send no `Authorization` header of your +own for the rest of the session. + +## No key yet + +If the user has not named a project, ask, and wait. Pick no project, infer none from open files or +earlier turns, and read no guides to narrow it down. Ask which project, and whether they would rather +register the key with the server or paste it in chat. + +Offer the better option first: + +- **Preferred:** they register the server with the key themselves, so it stays out of the chat. They + create the key under **Configuration → API Keys** in ReadMe, then follow **Registering the key** + below. +- **Otherwise:** they paste it and you send it as a header on each call: + + ```json + { + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me", + "headers": [{ "name": "Authorization", "value": "Bearer rdme_..." }] + } + } + ``` + + This works, and it also puts the key in the transcript. Say so, and tell them to rotate it. + +Send a header of your own **only** when the probe returned `Missing Security Schemes`. A header on the +server registration overrides anything you set in `harRequest`, so against a registered server a +pasted key is silently ignored and the call lands in whichever project the registration owns — no +error, just the wrong project. + +## A key is set but wrong + +Both failures mean an `Authorization` header is reaching the API and the API is rejecting it. The +usual cause is the registration referring to an environment variable the client resolved to nothing, +or passed through as literal text, because the variable is unset where the client launched from: + +- **Status 500, `An unknown error has occurred.`** — the header arrived empty. +- **Status 401, `The API key couldn't be located.`** — a value arrived, but no such key exists. + Also what a revoked or mistyped key returns. + +Say that plainly before asking them to paste anything. Ask them to confirm the variable is exported +in the environment the client launched from, then reconnect as the **Registering the key** row for +their client describes. If the key is genuinely gone, they create a new one under **Configuration → +API Keys**. + +Do not work around either failure by probing with `curl` or by retrying against the `Legacy API` +spec. One key maps to one project; if the call reaches the wrong project, the registration is the +thing to change. + +## Registering the key + +Registering a `readme` server of their own replaces the plugin's anonymous one. The server name stays +the same, so the skills and tools keep working. + +Find the row for the client you are running in. If you cannot tell which one that is, ask the user — +the wrong row sends them to a config file their client never reads. + +| Client | How | +| --- | --- | +| Claude Code | `export README_API_KEY=rdme_…`, then `claude mcp add --scope user --transport http readme https://docs.readme.com/mcp --header 'Authorization: Bearer ${README_API_KEY}'`. Keep the single quotes: the variable is expanded when Claude Code starts, so the key never lands in a config file. Restart afterwards | +| Codex, and the ChatGPT desktop app that shares its config | `export README_API_KEY=rdme_…`, then `codex mcp add readme --url https://docs.readme.com/mcp --bearer-token-env-var README_API_KEY`. Codex reads the variable at startup, so the key never lands in a config file. Start a new session afterwards | +| Cursor | Add a `readme` entry to `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` for one: `"type": "http"`, `"url": "https://docs.readme.com/mcp"`, and a `headers` block setting `Authorization` to `Bearer ${env:README_API_KEY}`. Export the variable in the shell the editor launches from, then restart the editor, since the header resolves when the server connects | +| Claude Desktop, Cowork, claude.ai, ChatGPT web | Not possible. The connector is read-only on these surfaces and cannot take a key. Say so, and offer to carry on in a CLI or editor | diff --git a/claude/skills/mcp-server/SKILL.md b/claude/skills/mcp-server/SKILL.md new file mode 100644 index 0000000..fcbaf37 --- /dev/null +++ b/claude/skills/mcp-server/SKILL.md @@ -0,0 +1,124 @@ +--- +name: mcp-server +description: Route ReadMe MCP calls to the right project and the right spec. Use before any request touching the user's own content - "our docs", "my hub", "create a page", "update the changelog", "what endpoints do we have", "search our guides", "how many page views", "top search terms", "send our API logs to ReadMe" - and before calling execute-request for the first time in a session. +--- + +# Which project does this call land in? + +This plugin connects to `https://docs.readme.com/mcp`, which serves **ReadMe's own documentation**. +`search` and `fetch` answer questions about how ReadMe works. The user's project is reached only +through `execute-request` with their API key. Reads and writes land in different places. + +Questions about ReadMe itself need nothing else: use `search` and `fetch` and answer. + +## Tools on this server + +| Tool | Answers for | Use it when | +| --- | --- | --- | +| `search`, `fetch` | ReadMe's own product documentation | The question is about how ReadMe works | +| `list-specs`, `list-endpoints`, `get-endpoint`, `search-endpoints` | ReadMe's API definitions, as documents | Looking up a route before calling it | +| `execute-request` | **The user's project**, with their key | Reading or changing anything of theirs | +| `update-docs`, `draft-changelog` | Nothing. They return instructions for you to carry out | You want the recommended procedure | + +Tool availability is per-project configuration, not a fixed list. Call `tools/list` rather than +assuming a tool named here is present, and never assume one that is not. + +## The three specs + +`execute-request` reaches only servers declared in these definitions. There are three, and +`search-endpoints` searches all of them at once: + +| Spec title | Server | Auth | +| --- | --- | --- | +| `ReadMe API` | `https://api.readme.com/v2` | Bearer `rdme_...` | +| `Developer Metrics API` | `https://metrics.readme.io` | HTTP basic, the key as username and an empty password | +| `Legacy API` | `https://dash.readme.com/api/v1` | HTTP basic | + +**Use `ReadMe API` for all work on the user's content.** `Legacy API` is v1: it takes basic auth +rather than a bearer token, and it is unavailable to projects on ReadMe Refactored. `search-endpoints` +will surface its routes alongside the others — ignore them. Never fall back to v1 when a v2 call +fails. + +## Finding a route + +Do not guess paths or work from memory. `list-endpoints` returns every path and summary in a spec in +one cheap call; `get-endpoint` adds the full request and response schema for one of them, including +which routes exist only on ReadMe Refactored. Read them rather than reproducing them here. + +Two things the definitions do not tell you: + +- Paginated responses carry `paging.next`, `paging.previous`, `paging.first` and `paging.last`. + Query with `page` and `per_page`, max 100 and max 50 on search. +- Enterprise child projects need the child's own key. Only the API-key routes take a `{subdomain}`, + and `me` works there. + +## Calling execute-request + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +- `title` names the spec and is required whenever the server carries more than one, as this one does. + Omitting it fails validation before the request is made. +- `url` must be absolute. `get-endpoint` and `search-endpoints` report paths relative to the server + (`/projects/me`), so prepend `https://api.readme.com/v2` rather than pasting the path through. +- The key decides which project the call lands in, and the user cannot override it per-request. + Name the project before you change anything; asking them which one to write to is misleading. + +Setting up, verifying, or repairing that key is the `mcp-auth` skill. + +## When the user asks about their own docs + +"Search our docs", "what does our guide say", "find the page about X in my project" cannot be +answered by `search` or `fetch` here. Those read ReadMe's documentation and would return confident, +wrong answers about someone else's content. + +Their key already reaches their content through the ReadMe API, so switch tools and carry on rather +than sending them away to configure anything: + +| They want | Call, via `execute-request`, spec title `ReadMe API` | +| --- | --- | +| Search their content | `GET https://api.readme.com/v2/search?query=...`, optionally `section`, `version`, `projects` | +| Read one guide | `GET https://api.readme.com/v2/branches/{branch}/guides/{slug}` | +| List what is in a category | `GET https://api.readme.com/v2/branches/{branch}/categories/{section}/{title}/pages` | + +`{branch}` is a version number, `stable`, or a branch name. Everything is branch-scoped except +changelogs, images, fonts, API keys, search and the project itself. + +## Metrics + +Page views, search terms and page quality live in the `Developer Metrics API` spec, and reading them +needs the Enterprise plan — without it the API answers with an auth or plan error rather than saying +so. The registration sends a bearer token; if a call returns `Unauthorized` with a key set, send +`Authorization: Basic :">` on the HAR request instead. + +Most of what the dashboard shows has no route at all, so check here before going looking: + +| Metric | Readable via API | +| --- | --- | +| Page views, page quality, search terms | Yes | +| API calls | No — ingest only, `POST https://metrics.readme.io/request` | +| MCP tool calls, API errors, top endpoints, new users | No, dashboard only | + +For anything in the No rows, send the user to +`https://dash.readme.com/project/{subdomain}/v{version}/metrics`. + +Ingesting the user's own API logs is a server-side integration, not something to hand-build here. +Point them at the SDK for their stack — `readmeio` (Node), `readme-metrics` (Python, Ruby), +`readme/metrics` (PHP), `ReadMe.Metrics` (.NET) — and at `fetch` with id `main/sdks`. + +## A different server, when they actually want one + +Every ReadMe project publishes its own MCP server at `https://{subdomain}.readme.io/mcp`, or its +custom domain. That is for *their* end users to ask questions of *their* published hub. It is a +product feature they set up deliberately, not a workaround for this conversation, and it is not +needed to work on their own docs from here. + +A server's hostname decides which project it serves. Enterprise hostnames serve a whole group, so +`search` and `fetch` there can span several child projects. diff --git a/claude/skills/onboarding/SKILL.md b/claude/skills/onboarding/SKILL.md index d5a0167..3b500eb 100644 --- a/claude/skills/onboarding/SKILL.md +++ b/claude/skills/onboarding/SKILL.md @@ -7,6 +7,12 @@ description: Get a new customer from zero to a live ReadMe developer hub. Use wh > Onboarding endpoints are coming soon. Until then this skill only covers the web-UI flow and ReadMe basics. +## Tools + +- `search` +- `fetch` +- `execute-request` + ## What ReadMe is ReadMe hosts a developer hub for your API at `{subdomain}.readme.io` or a custom domain. One hub contains: @@ -30,35 +36,39 @@ Plans: Starter (free), Pro, Enterprise. Enterprise supports child projects and D 3. Add the API definition under **API Reference**: upload an OpenAPI file, import a URL, build one from scratch, or run `npx rdme openapi upload ` from a terminal. ReadMe validates the file and renders every endpoint. 4. Write the first guide under **Guides**. Use the AI Agent for a draft or the editor for a blank page. A "Getting Started" page is the usual first one. 5. Generate an API key at **Configuration → API Keys**, URL `https://dash.readme.com/project/{subdomain}/v{version}/api-key`. -6. Attach the key. The plugin's own `readme` server is anonymous and cannot read the key. The user registers a server with the same name at user scope, which replaces the plugin's one, then exports the key in the shell that starts the editor and restarts it. In Claude Code: +6. Attach the key. The plugin's own `readme` server is anonymous and cannot read the key. The user registers a server under the same name, which replaces the plugin's one. The command differs per client: use the registration table in the `mcp-auth` skill, which also covers repairing a key that is already set. +7. Verify: `execute-request` with spec title `ReadMe API`, `GET https://api.readme.com/v2/projects/me`. A 200 with the project name means the plugin is wired to the right project. `Missing Security Schemes` means the registration is sending no key; a 401 titled `The API key couldn't be located.` means the key is wrong; a 500 titled `An unknown error has occurred.` means it resolved to an empty string. In every case, go back to step 5. - ``` - export README_API_KEY=rdme_… - claude mcp add --scope user --transport http readme https://docs.readme.com/mcp --header 'Authorization: Bearer ${README_API_KEY}' - ``` +After step 7, the `mcp-server` skill covers which spec and which project everything else lands in. - Keep the single quotes: Claude Code expands `${README_API_KEY}` when it starts, so the key never lands in a config file. In Codex: `codex mcp add readme --url https://docs.readme.com/mcp --bearer-token-env-var README_API_KEY`. Not possible in Claude Desktop Chat, Cowork or claude.ai; see the section below. -7. Verify: `readme:execute-request` with spec title `ReadMe API`, `GET https://api.readme.com/v2/projects/me`. A 200 with the project name means the plugin is wired to the right project. A 500 titled `An unknown error has occurred.` means the key is missing or wrong; go back to step 5. +## Driving the browser yourself -## Doing it with Claude in Chrome +Steps 1 to 5 are all browser work. If you can drive a browser, offer to do them in the user's own browser instead of only listing the steps: open the signup page, create the project, upload the API definition, and open the API Keys page. Let the user type credentials and payment details themselves. Steps 6 and 7 stay in the terminal. -Steps 1 to 5 are all browser work. If the Claude in Chrome extension is connected (`mcp__claude-in-chrome__*` tools are available), offer to drive them in the user's own browser instead of only listing the steps: open the signup page, create the project, upload the API definition, and open the API Keys page. Let the user type credentials and payment details themselves. Steps 6 and 7 stay in the terminal. +Find the row for the client you are running in. If you cannot tell which one that is, ask rather than guess. -## When you cannot install anything yourself +| Client | Drive the browser with | +| --- | --- | +| Claude Code | `mcp__claude-in-chrome__*`, when the Claude in Chrome extension is connected | +| ChatGPT desktop app or ChatGPT web | `@Browser`. It has its own profile, so the user signs in to ReadMe there, and it asks before submitting forms. It cannot upload files, so for step 3 import the API definition by URL or run `npx rdme openapi upload ` from a terminal | +| Codex CLI, Codex IDE extension, Cursor | No browser of their own. List the steps for the user | -In Claude Desktop Chat, Cowork and claude.ai you have no shell and cannot add a marketplace, install a plugin or edit MCP config. Do not attempt it and do not ask the user to run commands. If the `readme:*` tools are missing, the user has to install the plugin by hand. Give them these steps exactly: +## When you cannot install anything yourself -1. Click **Customize** in the left sidebar, then **Plugins**. In Cowork, open the **Cowork** tab first. -2. Under **Personal plugins**, click **+** → **Add marketplace** and enter `readmeio/agent-plugins`. -3. Find **readme** in the list and click **Install**, then start a new chat so the tools load. +On a chat surface you have no shell and cannot add a marketplace, install a plugin or edit MCP config. Do not attempt it and do not ask the user to run commands there. If the ReadMe tools are missing, the user has to install the plugin by hand. Find their client below and give them those steps exactly. -Plugins need a paid Claude plan. On Team and Enterprise an owner may have disabled personal marketplaces, in which case they add it under **Organization settings → Plugins**. +| Client | Steps | +| --- | --- | +| Claude Desktop, Cowork, claude.ai | Click **Customize** in the left sidebar, then **Plugins** — in Cowork, open the **Cowork** tab first. Under **Personal plugins**, click **+** → **Add marketplace** and enter `readmeio/agent-plugins`. Find **readme** in the list, click **Install**, then start a new chat so the tools load. Plugins need a paid Claude plan; on Team and Enterprise an owner may have disabled personal marketplaces, in which case they add it under **Organization settings → Plugins** | +| ChatGPT desktop app | Open the **Plugins** tab and click **Add marketplace**. Enter `readmeio/agent-plugins` as the source, leave the Git ref as `main` and the sparse paths empty, then click **Add marketplace**. Install **readme** from the new marketplace and start a new chat so the tools load | +| ChatGPT web | There is no marketplace option, only the plugin directory. Until the ReadMe plugin is listed there, the user can still add the MCP server on its own: turn on **Developer mode** under **Settings → Security and login**, open **Plugins**, click **+** next to the search box, and in the **New Plugin** form set the name to `readme`, the server URL to `https://docs.readme.com/mcp`, and authentication to **No Auth**. That gives the tools but not the skills, so keep this skill's content in the conversation yourself | +| Cursor | Open **Cursor Settings → Plugins**, search for **ReadMe**, click **Install** and choose project or user scope. Or run `/add-plugin readme` in chat | -Once installed, the ReadMe connector on these surfaces is read-only: `readme:search`, `readme:fetch` and the endpoint tools work on public projects, but there is no way to supply `README_API_KEY`, so steps 6 and 7 of the Quick Start do not apply and write tools such as `readme:update-docs` fail. Say so before the user tries. For creating or updating pages, offer to continue in Claude Code, Codex or Cursor with the server registered as in step 6. +On the chat surfaces above the ReadMe connector stays read-only once installed: `search`, `fetch` and the endpoint tools work on public projects, but there is no way to supply an API key, so steps 6 and 7 of the Quick Start do not apply and write tools such as `update-docs` fail. Say so before the user tries. For creating or updating pages, offer to continue in a CLI or editor with the server registered as in step 6. ## Reading the docs meanwhile -Use `readme:search` for a question, then `readme:fetch` with the returned id. Useful pages: +Use `search` for a question, then `fetch` with the returned id. Useful pages: | Id | Page | | --- | --- | diff --git a/codex/README.md b/codex/README.md index e9aa817..7746a25 100644 --- a/codex/README.md +++ b/codex/README.md @@ -25,3 +25,11 @@ codex mcp add readme --url https://docs.readme.com/mcp --bearer-token-env-var RE ``` Your `readme` entry replaces the plugin's anonymous one. The skills keep working because the server name is unchanged. Find your key under **Configuration → API Keys** in your ReadMe project dashboard. + +## Skills + +| Skill | What it does | +| ----- | ------------ | +| `mcp-server` | Keeps the agent straight on which project and which spec a call lands in: this server reads ReadMe's own docs, while `execute-request` plus your key acts on yours | +| `mcp-auth` | Establishes which key is in play and diagnoses the failures a missing or unresolved one produces | +| `onboarding` | Walks a new customer from signup to a published hub and a working API key | diff --git a/codex/skills/mcp-auth/SKILL.md b/codex/skills/mcp-auth/SKILL.md new file mode 100644 index 0000000..f26cc61 --- /dev/null +++ b/codex/skills/mcp-auth/SKILL.md @@ -0,0 +1,111 @@ +--- +name: mcp-auth +description: Establish or repair the ReadMe API key behind execute-request. Use when a ReadMe call fails with "Missing Security Schemes", "The API key couldn't be located", or "An unknown error has occurred", when the user asks which project their key reaches, when they say they set a key but it is not working, and before the first write to their project. +--- + +# The key behind execute-request + +The plugin ships anonymous. Public reads of ReadMe's documentation need no key; anything touching +the user's own project does, and it travels on the MCP server registration rather than in the tool +call. See the `mcp-server` skill for which calls land where. + +## Tools + +- `execute-request` + +## Assume anonymous + +Most users have not registered a key, so do not spend a call proving it. Go straight to the work: + +- ReadMe documentation questions — `search` and `fetch`, no key involved. +- The user's project, and they have not mentioned a key — go to **No key yet** below. +- The user says they registered one, or an `execute-request` call fails — verify with the probe. + +## Verify + +`execute-request`, spec title `ReadMe API`, with no `Authorization` header of your own: + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +| Response | Means | Next | +| --- | --- | --- | +| A project object | The registration carries a working key | Name the project and subdomain, carry on. Ask for nothing | +| `Missing Security Schemes` | No `Authorization` header at all — the server is anonymous | **No key yet** | +| `"title": "The API key couldn't be located."`, status 401 | A key is being sent, but it is not a real key | **A key is set but wrong** | +| `"title": "An unknown error has occurred."`, status 500 | The bearer is empty — the variable resolved to nothing | **A key is set but wrong** | + +Resolve this once per session. Having seen a project object, send no `Authorization` header of your +own for the rest of the session. + +## No key yet + +If the user has not named a project, ask, and wait. Pick no project, infer none from open files or +earlier turns, and read no guides to narrow it down. Ask which project, and whether they would rather +register the key with the server or paste it in chat. + +Offer the better option first: + +- **Preferred:** they register the server with the key themselves, so it stays out of the chat. They + create the key under **Configuration → API Keys** in ReadMe, then follow **Registering the key** + below. +- **Otherwise:** they paste it and you send it as a header on each call: + + ```json + { + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me", + "headers": [{ "name": "Authorization", "value": "Bearer rdme_..." }] + } + } + ``` + + This works, and it also puts the key in the transcript. Say so, and tell them to rotate it. + +Send a header of your own **only** when the probe returned `Missing Security Schemes`. A header on the +server registration overrides anything you set in `harRequest`, so against a registered server a +pasted key is silently ignored and the call lands in whichever project the registration owns — no +error, just the wrong project. + +## A key is set but wrong + +Both failures mean an `Authorization` header is reaching the API and the API is rejecting it. The +usual cause is the registration referring to an environment variable the client resolved to nothing, +or passed through as literal text, because the variable is unset where the client launched from: + +- **Status 500, `An unknown error has occurred.`** — the header arrived empty. +- **Status 401, `The API key couldn't be located.`** — a value arrived, but no such key exists. + Also what a revoked or mistyped key returns. + +Say that plainly before asking them to paste anything. Ask them to confirm the variable is exported +in the environment the client launched from, then reconnect as the **Registering the key** row for +their client describes. If the key is genuinely gone, they create a new one under **Configuration → +API Keys**. + +Do not work around either failure by probing with `curl` or by retrying against the `Legacy API` +spec. One key maps to one project; if the call reaches the wrong project, the registration is the +thing to change. + +## Registering the key + +Registering a `readme` server of their own replaces the plugin's anonymous one. The server name stays +the same, so the skills and tools keep working. + +Find the row for the client you are running in. If you cannot tell which one that is, ask the user — +the wrong row sends them to a config file their client never reads. + +| Client | How | +| --- | --- | +| Claude Code | `export README_API_KEY=rdme_…`, then `claude mcp add --scope user --transport http readme https://docs.readme.com/mcp --header 'Authorization: Bearer ${README_API_KEY}'`. Keep the single quotes: the variable is expanded when Claude Code starts, so the key never lands in a config file. Restart afterwards | +| Codex, and the ChatGPT desktop app that shares its config | `export README_API_KEY=rdme_…`, then `codex mcp add readme --url https://docs.readme.com/mcp --bearer-token-env-var README_API_KEY`. Codex reads the variable at startup, so the key never lands in a config file. Start a new session afterwards | +| Cursor | Add a `readme` entry to `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` for one: `"type": "http"`, `"url": "https://docs.readme.com/mcp"`, and a `headers` block setting `Authorization` to `Bearer ${env:README_API_KEY}`. Export the variable in the shell the editor launches from, then restart the editor, since the header resolves when the server connects | +| Claude Desktop, Cowork, claude.ai, ChatGPT web | Not possible. The connector is read-only on these surfaces and cannot take a key. Say so, and offer to carry on in a CLI or editor | diff --git a/codex/skills/mcp-server/SKILL.md b/codex/skills/mcp-server/SKILL.md new file mode 100644 index 0000000..fcbaf37 --- /dev/null +++ b/codex/skills/mcp-server/SKILL.md @@ -0,0 +1,124 @@ +--- +name: mcp-server +description: Route ReadMe MCP calls to the right project and the right spec. Use before any request touching the user's own content - "our docs", "my hub", "create a page", "update the changelog", "what endpoints do we have", "search our guides", "how many page views", "top search terms", "send our API logs to ReadMe" - and before calling execute-request for the first time in a session. +--- + +# Which project does this call land in? + +This plugin connects to `https://docs.readme.com/mcp`, which serves **ReadMe's own documentation**. +`search` and `fetch` answer questions about how ReadMe works. The user's project is reached only +through `execute-request` with their API key. Reads and writes land in different places. + +Questions about ReadMe itself need nothing else: use `search` and `fetch` and answer. + +## Tools on this server + +| Tool | Answers for | Use it when | +| --- | --- | --- | +| `search`, `fetch` | ReadMe's own product documentation | The question is about how ReadMe works | +| `list-specs`, `list-endpoints`, `get-endpoint`, `search-endpoints` | ReadMe's API definitions, as documents | Looking up a route before calling it | +| `execute-request` | **The user's project**, with their key | Reading or changing anything of theirs | +| `update-docs`, `draft-changelog` | Nothing. They return instructions for you to carry out | You want the recommended procedure | + +Tool availability is per-project configuration, not a fixed list. Call `tools/list` rather than +assuming a tool named here is present, and never assume one that is not. + +## The three specs + +`execute-request` reaches only servers declared in these definitions. There are three, and +`search-endpoints` searches all of them at once: + +| Spec title | Server | Auth | +| --- | --- | --- | +| `ReadMe API` | `https://api.readme.com/v2` | Bearer `rdme_...` | +| `Developer Metrics API` | `https://metrics.readme.io` | HTTP basic, the key as username and an empty password | +| `Legacy API` | `https://dash.readme.com/api/v1` | HTTP basic | + +**Use `ReadMe API` for all work on the user's content.** `Legacy API` is v1: it takes basic auth +rather than a bearer token, and it is unavailable to projects on ReadMe Refactored. `search-endpoints` +will surface its routes alongside the others — ignore them. Never fall back to v1 when a v2 call +fails. + +## Finding a route + +Do not guess paths or work from memory. `list-endpoints` returns every path and summary in a spec in +one cheap call; `get-endpoint` adds the full request and response schema for one of them, including +which routes exist only on ReadMe Refactored. Read them rather than reproducing them here. + +Two things the definitions do not tell you: + +- Paginated responses carry `paging.next`, `paging.previous`, `paging.first` and `paging.last`. + Query with `page` and `per_page`, max 100 and max 50 on search. +- Enterprise child projects need the child's own key. Only the API-key routes take a `{subdomain}`, + and `me` works there. + +## Calling execute-request + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +- `title` names the spec and is required whenever the server carries more than one, as this one does. + Omitting it fails validation before the request is made. +- `url` must be absolute. `get-endpoint` and `search-endpoints` report paths relative to the server + (`/projects/me`), so prepend `https://api.readme.com/v2` rather than pasting the path through. +- The key decides which project the call lands in, and the user cannot override it per-request. + Name the project before you change anything; asking them which one to write to is misleading. + +Setting up, verifying, or repairing that key is the `mcp-auth` skill. + +## When the user asks about their own docs + +"Search our docs", "what does our guide say", "find the page about X in my project" cannot be +answered by `search` or `fetch` here. Those read ReadMe's documentation and would return confident, +wrong answers about someone else's content. + +Their key already reaches their content through the ReadMe API, so switch tools and carry on rather +than sending them away to configure anything: + +| They want | Call, via `execute-request`, spec title `ReadMe API` | +| --- | --- | +| Search their content | `GET https://api.readme.com/v2/search?query=...`, optionally `section`, `version`, `projects` | +| Read one guide | `GET https://api.readme.com/v2/branches/{branch}/guides/{slug}` | +| List what is in a category | `GET https://api.readme.com/v2/branches/{branch}/categories/{section}/{title}/pages` | + +`{branch}` is a version number, `stable`, or a branch name. Everything is branch-scoped except +changelogs, images, fonts, API keys, search and the project itself. + +## Metrics + +Page views, search terms and page quality live in the `Developer Metrics API` spec, and reading them +needs the Enterprise plan — without it the API answers with an auth or plan error rather than saying +so. The registration sends a bearer token; if a call returns `Unauthorized` with a key set, send +`Authorization: Basic :">` on the HAR request instead. + +Most of what the dashboard shows has no route at all, so check here before going looking: + +| Metric | Readable via API | +| --- | --- | +| Page views, page quality, search terms | Yes | +| API calls | No — ingest only, `POST https://metrics.readme.io/request` | +| MCP tool calls, API errors, top endpoints, new users | No, dashboard only | + +For anything in the No rows, send the user to +`https://dash.readme.com/project/{subdomain}/v{version}/metrics`. + +Ingesting the user's own API logs is a server-side integration, not something to hand-build here. +Point them at the SDK for their stack — `readmeio` (Node), `readme-metrics` (Python, Ruby), +`readme/metrics` (PHP), `ReadMe.Metrics` (.NET) — and at `fetch` with id `main/sdks`. + +## A different server, when they actually want one + +Every ReadMe project publishes its own MCP server at `https://{subdomain}.readme.io/mcp`, or its +custom domain. That is for *their* end users to ask questions of *their* published hub. It is a +product feature they set up deliberately, not a workaround for this conversation, and it is not +needed to work on their own docs from here. + +A server's hostname decides which project it serves. Enterprise hostnames serve a whole group, so +`search` and `fetch` there can span several child projects. diff --git a/codex/skills/onboarding/SKILL.md b/codex/skills/onboarding/SKILL.md index bdc5718..3b500eb 100644 --- a/codex/skills/onboarding/SKILL.md +++ b/codex/skills/onboarding/SKILL.md @@ -7,6 +7,12 @@ description: Get a new customer from zero to a live ReadMe developer hub. Use wh > Onboarding endpoints are coming soon. Until then this skill only covers the web-UI flow and ReadMe basics. +## Tools + +- `search` +- `fetch` +- `execute-request` + ## What ReadMe is ReadMe hosts a developer hub for your API at `{subdomain}.readme.io` or a custom domain. One hub contains: @@ -30,32 +36,39 @@ Plans: Starter (free), Pro, Enterprise. Enterprise supports child projects and D 3. Add the API definition under **API Reference**: upload an OpenAPI file, import a URL, build one from scratch, or run `npx rdme openapi upload ` from a terminal. ReadMe validates the file and renders every endpoint. 4. Write the first guide under **Guides**. Use the AI Agent for a draft or the editor for a blank page. A "Getting Started" page is the usual first one. 5. Generate an API key at **Configuration → API Keys**, URL `https://dash.readme.com/project/{subdomain}/v{version}/api-key`. -6. Attach the key. The plugin's own `readme` server is anonymous and cannot read the key. The user registers a server with the same name, which replaces the plugin's one, then exports the key in the shell that starts Codex and starts a new session. In Codex: +6. Attach the key. The plugin's own `readme` server is anonymous and cannot read the key. The user registers a server under the same name, which replaces the plugin's one. The command differs per client: use the registration table in the `mcp-auth` skill, which also covers repairing a key that is already set. +7. Verify: `execute-request` with spec title `ReadMe API`, `GET https://api.readme.com/v2/projects/me`. A 200 with the project name means the plugin is wired to the right project. `Missing Security Schemes` means the registration is sending no key; a 401 titled `The API key couldn't be located.` means the key is wrong; a 500 titled `An unknown error has occurred.` means it resolved to an empty string. In every case, go back to step 5. - ``` - export README_API_KEY=rdme_… - codex mcp add readme --url https://docs.readme.com/mcp --bearer-token-env-var README_API_KEY - ``` +After step 7, the `mcp-server` skill covers which spec and which project everything else lands in. - Codex reads the variable at startup, so the key never lands in a config file. The ChatGPT desktop app shares Codex's config, so this registration also covers Codex sessions there. In Claude Code the equivalent is `claude mcp add --scope user --transport http readme https://docs.readme.com/mcp --header 'Authorization: Bearer ${README_API_KEY}'`. Not possible in ChatGPT web; see the section below. -7. Verify: `readme:execute-request` with spec title `ReadMe API`, `GET https://api.readme.com/v2/projects/me`. A 200 with the project name means the plugin is wired to the right project. A 500 titled `An unknown error has occurred.` means the key is missing or wrong; go back to step 5. +## Driving the browser yourself -## Doing it with the ChatGPT browser +Steps 1 to 5 are all browser work. If you can drive a browser, offer to do them in the user's own browser instead of only listing the steps: open the signup page, create the project, upload the API definition, and open the API Keys page. Let the user type credentials and payment details themselves. Steps 6 and 7 stay in the terminal. -Steps 1 to 5 are all browser work. In the ChatGPT desktop app or ChatGPT web, offer to drive them with `@Browser` instead of only listing the steps: open the signup page, create the project, and open the API Keys page. The built-in browser has its own profile, so the user signs in to ReadMe there and types credentials and payment details themselves; ChatGPT asks before submitting forms. It cannot upload files, so for step 3 import the OpenAPI definition by URL or run `npx rdme openapi upload ` from Codex. Codex CLI and the IDE extension have no browser; list the steps there. Steps 6 and 7 stay in the terminal. +Find the row for the client you are running in. If you cannot tell which one that is, ask rather than guess. + +| Client | Drive the browser with | +| --- | --- | +| Claude Code | `mcp__claude-in-chrome__*`, when the Claude in Chrome extension is connected | +| ChatGPT desktop app or ChatGPT web | `@Browser`. It has its own profile, so the user signs in to ReadMe there, and it asks before submitting forms. It cannot upload files, so for step 3 import the API definition by URL or run `npx rdme openapi upload ` from a terminal | +| Codex CLI, Codex IDE extension, Cursor | No browser of their own. List the steps for the user | ## When you cannot install anything yourself -In a ChatGPT chat, desktop or web, you have no shell and cannot add a marketplace, install a plugin or edit MCP config. Do not attempt it and do not ask the user to run commands in the chat. If the `readme:*` tools are missing, the user has to install the plugin by hand. Give them these steps exactly: +On a chat surface you have no shell and cannot add a marketplace, install a plugin or edit MCP config. Do not attempt it and do not ask the user to run commands there. If the ReadMe tools are missing, the user has to install the plugin by hand. Find their client below and give them those steps exactly. -- ChatGPT desktop app: open the **Plugins** tab and click **Add marketplace**. Enter `readmeio/agent-plugins` as the source, leave the Git ref as `main` and the sparse paths empty, then click **Add marketplace**. Install **readme** from the new marketplace and start a new chat so the tools load. -- ChatGPT web: there is no marketplace option, only the plugin directory. Until the ReadMe plugin is listed there, the user can still add the MCP server on its own: turn on **Developer mode** under **Settings → Security and login**, open **Plugins**, click **+** next to the search box, and in the **New Plugin** form set the name to `readme`, the server URL to `https://docs.readme.com/mcp`, and authentication to **No Auth**. That gives the `readme:*` tools but not the skills, so keep this skill's content in the conversation yourself. +| Client | Steps | +| --- | --- | +| Claude Desktop, Cowork, claude.ai | Click **Customize** in the left sidebar, then **Plugins** — in Cowork, open the **Cowork** tab first. Under **Personal plugins**, click **+** → **Add marketplace** and enter `readmeio/agent-plugins`. Find **readme** in the list, click **Install**, then start a new chat so the tools load. Plugins need a paid Claude plan; on Team and Enterprise an owner may have disabled personal marketplaces, in which case they add it under **Organization settings → Plugins** | +| ChatGPT desktop app | Open the **Plugins** tab and click **Add marketplace**. Enter `readmeio/agent-plugins` as the source, leave the Git ref as `main` and the sparse paths empty, then click **Add marketplace**. Install **readme** from the new marketplace and start a new chat so the tools load | +| ChatGPT web | There is no marketplace option, only the plugin directory. Until the ReadMe plugin is listed there, the user can still add the MCP server on its own: turn on **Developer mode** under **Settings → Security and login**, open **Plugins**, click **+** next to the search box, and in the **New Plugin** form set the name to `readme`, the server URL to `https://docs.readme.com/mcp`, and authentication to **No Auth**. That gives the tools but not the skills, so keep this skill's content in the conversation yourself | +| Cursor | Open **Cursor Settings → Plugins**, search for **ReadMe**, click **Install** and choose project or user scope. Or run `/add-plugin readme` in chat | -Once installed, the ReadMe connector in a chat is read-only: `readme:search`, `readme:fetch` and the endpoint tools work on public projects, but ChatGPT cannot take an API key, so steps 6 and 7 of the Quick Start do not apply and write tools such as `readme:update-docs` fail. Say so before the user tries. For creating or updating pages, offer to continue in Codex with the server registered as in step 6. +On the chat surfaces above the ReadMe connector stays read-only once installed: `search`, `fetch` and the endpoint tools work on public projects, but there is no way to supply an API key, so steps 6 and 7 of the Quick Start do not apply and write tools such as `update-docs` fail. Say so before the user tries. For creating or updating pages, offer to continue in a CLI or editor with the server registered as in step 6. ## Reading the docs meanwhile -Use `readme:search` for a question, then `readme:fetch` with the returned id. Useful pages: +Use `search` for a question, then `fetch` with the returned id. Useful pages: | Id | Page | | --- | --- | diff --git a/plugin.json b/cursor/.cursor-plugin/plugin.json similarity index 60% rename from plugin.json rename to cursor/.cursor-plugin/plugin.json index eae1454..32074cf 100644 --- a/plugin.json +++ b/cursor/.cursor-plugin/plugin.json @@ -1,16 +1,19 @@ { - "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", "name": "readme", + "displayName": "ReadMe", "version": "1.0.0", + "minClientVersions": { + "cursor": "3.13.0" + }, "description": "Search, read, and update your ReadMe docs, API reference, and changelog.", "author": { "name": "ReadMe", - "email": "support@readme.io", - "url": "https://readme.com" + "email": "support@readme.io" }, "homepage": "https://docs.readme.com/main/docs/readmes-mcp-server", "repository": "https://github.com/readmeio/agent-plugins", "license": "MIT", + "logo": "assets/logo.svg", "keywords": [ "readme", "documentation", @@ -19,5 +22,14 @@ "openapi", "changelog", "mcp" - ] + ], + "category": "integrations", + "tags": [ + "readme", + "documentation", + "api", + "mcp" + ], + "skills": "./skills/", + "mcpServers": "./mcp.json" } diff --git a/cursor/CHANGELOG.md b/cursor/CHANGELOG.md new file mode 100644 index 0000000..a9ef7ac --- /dev/null +++ b/cursor/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog + +All notable changes to this plugin will be documented here. + +## 1.0.0 — initial release + +- Added the `readme` MCP server pointing at ReadMe's hosted Streamable HTTP endpoint (`https://docs.readme.com/mcp`). +- Ships anonymous. Public docs are readable without a key, and write access is opt-in: users register the server with their own API key when they want it. +- Added the `mcp-server` skill, so the agent knows this server reads ReadMe's own documentation and that only `execute-request` with the user's key reaches their project. +- Logo: ReadMe's official owl mark. diff --git a/cursor/LICENSE b/cursor/LICENSE new file mode 100644 index 0000000..e69fef2 --- /dev/null +++ b/cursor/LICENSE @@ -0,0 +1,18 @@ +Copyright © 2026 ReadMe + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/cursor/README.md b/cursor/README.md new file mode 100644 index 0000000..ff04304 --- /dev/null +++ b/cursor/README.md @@ -0,0 +1,124 @@ +# ReadMe + +Cursor plugin that connects agents to [ReadMe](https://readme.com) through ReadMe's remote +[Model Context Protocol](https://modelcontextprotocol.io/) server at `https://docs.readme.com/mcp`. + +Ask how ReadMe works, look up the ReadMe API, and drive your own project through that API with your +key, without leaving the editor. + +The server is bound to one project by its hostname, and this one is ReadMe's own documentation. So +`search` and `fetch` answer questions *about ReadMe*, while your project is reached through +`execute-request` and your key. The `mcp-server` skill keeps the agent straight on that. + +## Install + +1. Open **Cursor Settings → Plugins**. +2. Search for **ReadMe**. +3. Click **Install** and choose project or user scope. + +Or run `/add-plugin readme` in chat. Nothing to configure afterwards: the plugin reads public docs +straight away, and a key is only needed for writes (see below). + +### From this repository + +Teams that want the plugin from source, or ahead of the marketplace, can add the repository itself: + +1. Open **Dashboard → Plugins**. +2. Under **Team Marketplaces**, click **Add Marketplace**, then **Import from Repo**. +3. Enter `readmeio/agent-plugins`. + +Cursor reads `.cursor-plugin/marketplace.json` from the repository root and indexes the plugin from +there. Teams and Enterprise plans only. + +## MCP + +```json +{ + "mcpServers": { + "readme": { + "type": "http", + "url": "https://docs.readme.com/mcp" + } + } +} +``` + +## Authentication + +Public read access works without any setup, so the plugin ships no credential and asks for nothing +on install. + +Anything that changes your project goes through `execute-request` against `api.readme.com`, and that +needs your ReadMe API key. Register the server yourself once with the key, and your `readme` entry +replaces the plugin's anonymous one: + +```json +{ + "mcpServers": { + "readme": { + "type": "http", + "url": "https://docs.readme.com/mcp", + "headers": { + "Authorization": "Bearer ${env:README_API_KEY}" + } + } + } +} +``` + +Put that in `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` for one. Create the key +under **Account Settings → API Keys** in ReadMe and export it as `README_API_KEY` rather than +committing it. The key decides which project the agent reaches, and grants read and write access to +it. Rotate it from Account Settings if it is ever exposed. + +## What agents can do + +| Tool | What it does | Reads or acts on | +| ------------------ | ------------------------------------------------------------------ | ------------------ | +| `search` | Search guides, reference pages, and docs content by keyword | ReadMe's own docs | +| `fetch` | Retrieve a specific guide or reference page by ID | ReadMe's own docs | +| `list-specs` | List the OpenAPI specs available | ReadMe's own specs | +| `list-endpoints` | List all API paths and HTTP methods with summaries | ReadMe's own specs | +| `search-endpoints` | Search paths, operations, and parameters | ReadMe's own specs | +| `get-endpoint` | Detail on one endpoint, including security schemes and servers | ReadMe's own specs | +| `execute-request` | Execute a real API request | **Your project** | +| `update-docs` | Returns instructions for updating docs, for the agent to follow | Nothing by itself | +| `draft-changelog` | Returns instructions for drafting a changelog from merged PRs | Nothing by itself | + +`execute-request` is the only tool that changes anything, and the only one your API key applies to. +`update-docs` and `draft-changelog` are prompts rather than actions: they hand the agent a procedure, +and the agent carries it out through `execute-request`. + +## Skills + +| Skill | What it does | +| ----- | ------------ | +| `mcp-server` | Keeps the agent straight on which project and which spec a call lands in: this server reads ReadMe's own docs, while `execute-request` plus your key acts on yours | +| `mcp-auth` | Establishes which key is in play and diagnoses the failures a missing or unresolved one produces | +| `onboarding` | Walks a new customer from signup to a published hub and a working API key | + +## Which ReadMe MCP server is this? + +ReadMe has two kinds of MCP server, and this plugin is the first one. + +**ReadMe's MCP server** (`https://docs.readme.com/mcp`) is what this plugin installs. It answers +questions about ReadMe from ReadMe's own documentation, and lets the agent drive the ReadMe API +against your project once you attach a key. + +**Your project's MCP server** (`https://your-project.readme.io/mcp`) is the one ReadMe generates for +_your_ users to ask questions of your published hub. You do not need it to work on your own docs +from here: with a key, `execute-request` reaches your content through `api.readme.com/v2`. Every +project has its own URL, so it cannot be installed from the marketplace. See +[your project's MCP server](https://docs.readme.com/main/docs/your-projects-mcp-server). + +Both are the same software; the hostname is what decides which project it serves. + +## Support + +- **ReadMe's MCP server:** https://docs.readme.com/main/docs/readmes-mcp-server +- **Report issues:** https://github.com/readmeio/agent-plugins/issues +- **Contact support:** support@readme.io + +## License + +MIT diff --git a/assets/logo.svg b/cursor/assets/logo.svg similarity index 100% rename from assets/logo.svg rename to cursor/assets/logo.svg diff --git a/cursor/mcp.json b/cursor/mcp.json new file mode 100644 index 0000000..59156bc --- /dev/null +++ b/cursor/mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "readme": { + "type": "http", + "url": "https://docs.readme.com/mcp" + } + } +} diff --git a/cursor/skills/mcp-auth/SKILL.md b/cursor/skills/mcp-auth/SKILL.md new file mode 100644 index 0000000..f26cc61 --- /dev/null +++ b/cursor/skills/mcp-auth/SKILL.md @@ -0,0 +1,111 @@ +--- +name: mcp-auth +description: Establish or repair the ReadMe API key behind execute-request. Use when a ReadMe call fails with "Missing Security Schemes", "The API key couldn't be located", or "An unknown error has occurred", when the user asks which project their key reaches, when they say they set a key but it is not working, and before the first write to their project. +--- + +# The key behind execute-request + +The plugin ships anonymous. Public reads of ReadMe's documentation need no key; anything touching +the user's own project does, and it travels on the MCP server registration rather than in the tool +call. See the `mcp-server` skill for which calls land where. + +## Tools + +- `execute-request` + +## Assume anonymous + +Most users have not registered a key, so do not spend a call proving it. Go straight to the work: + +- ReadMe documentation questions — `search` and `fetch`, no key involved. +- The user's project, and they have not mentioned a key — go to **No key yet** below. +- The user says they registered one, or an `execute-request` call fails — verify with the probe. + +## Verify + +`execute-request`, spec title `ReadMe API`, with no `Authorization` header of your own: + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +| Response | Means | Next | +| --- | --- | --- | +| A project object | The registration carries a working key | Name the project and subdomain, carry on. Ask for nothing | +| `Missing Security Schemes` | No `Authorization` header at all — the server is anonymous | **No key yet** | +| `"title": "The API key couldn't be located."`, status 401 | A key is being sent, but it is not a real key | **A key is set but wrong** | +| `"title": "An unknown error has occurred."`, status 500 | The bearer is empty — the variable resolved to nothing | **A key is set but wrong** | + +Resolve this once per session. Having seen a project object, send no `Authorization` header of your +own for the rest of the session. + +## No key yet + +If the user has not named a project, ask, and wait. Pick no project, infer none from open files or +earlier turns, and read no guides to narrow it down. Ask which project, and whether they would rather +register the key with the server or paste it in chat. + +Offer the better option first: + +- **Preferred:** they register the server with the key themselves, so it stays out of the chat. They + create the key under **Configuration → API Keys** in ReadMe, then follow **Registering the key** + below. +- **Otherwise:** they paste it and you send it as a header on each call: + + ```json + { + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me", + "headers": [{ "name": "Authorization", "value": "Bearer rdme_..." }] + } + } + ``` + + This works, and it also puts the key in the transcript. Say so, and tell them to rotate it. + +Send a header of your own **only** when the probe returned `Missing Security Schemes`. A header on the +server registration overrides anything you set in `harRequest`, so against a registered server a +pasted key is silently ignored and the call lands in whichever project the registration owns — no +error, just the wrong project. + +## A key is set but wrong + +Both failures mean an `Authorization` header is reaching the API and the API is rejecting it. The +usual cause is the registration referring to an environment variable the client resolved to nothing, +or passed through as literal text, because the variable is unset where the client launched from: + +- **Status 500, `An unknown error has occurred.`** — the header arrived empty. +- **Status 401, `The API key couldn't be located.`** — a value arrived, but no such key exists. + Also what a revoked or mistyped key returns. + +Say that plainly before asking them to paste anything. Ask them to confirm the variable is exported +in the environment the client launched from, then reconnect as the **Registering the key** row for +their client describes. If the key is genuinely gone, they create a new one under **Configuration → +API Keys**. + +Do not work around either failure by probing with `curl` or by retrying against the `Legacy API` +spec. One key maps to one project; if the call reaches the wrong project, the registration is the +thing to change. + +## Registering the key + +Registering a `readme` server of their own replaces the plugin's anonymous one. The server name stays +the same, so the skills and tools keep working. + +Find the row for the client you are running in. If you cannot tell which one that is, ask the user — +the wrong row sends them to a config file their client never reads. + +| Client | How | +| --- | --- | +| Claude Code | `export README_API_KEY=rdme_…`, then `claude mcp add --scope user --transport http readme https://docs.readme.com/mcp --header 'Authorization: Bearer ${README_API_KEY}'`. Keep the single quotes: the variable is expanded when Claude Code starts, so the key never lands in a config file. Restart afterwards | +| Codex, and the ChatGPT desktop app that shares its config | `export README_API_KEY=rdme_…`, then `codex mcp add readme --url https://docs.readme.com/mcp --bearer-token-env-var README_API_KEY`. Codex reads the variable at startup, so the key never lands in a config file. Start a new session afterwards | +| Cursor | Add a `readme` entry to `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` for one: `"type": "http"`, `"url": "https://docs.readme.com/mcp"`, and a `headers` block setting `Authorization` to `Bearer ${env:README_API_KEY}`. Export the variable in the shell the editor launches from, then restart the editor, since the header resolves when the server connects | +| Claude Desktop, Cowork, claude.ai, ChatGPT web | Not possible. The connector is read-only on these surfaces and cannot take a key. Say so, and offer to carry on in a CLI or editor | diff --git a/cursor/skills/mcp-server/SKILL.md b/cursor/skills/mcp-server/SKILL.md new file mode 100644 index 0000000..fcbaf37 --- /dev/null +++ b/cursor/skills/mcp-server/SKILL.md @@ -0,0 +1,124 @@ +--- +name: mcp-server +description: Route ReadMe MCP calls to the right project and the right spec. Use before any request touching the user's own content - "our docs", "my hub", "create a page", "update the changelog", "what endpoints do we have", "search our guides", "how many page views", "top search terms", "send our API logs to ReadMe" - and before calling execute-request for the first time in a session. +--- + +# Which project does this call land in? + +This plugin connects to `https://docs.readme.com/mcp`, which serves **ReadMe's own documentation**. +`search` and `fetch` answer questions about how ReadMe works. The user's project is reached only +through `execute-request` with their API key. Reads and writes land in different places. + +Questions about ReadMe itself need nothing else: use `search` and `fetch` and answer. + +## Tools on this server + +| Tool | Answers for | Use it when | +| --- | --- | --- | +| `search`, `fetch` | ReadMe's own product documentation | The question is about how ReadMe works | +| `list-specs`, `list-endpoints`, `get-endpoint`, `search-endpoints` | ReadMe's API definitions, as documents | Looking up a route before calling it | +| `execute-request` | **The user's project**, with their key | Reading or changing anything of theirs | +| `update-docs`, `draft-changelog` | Nothing. They return instructions for you to carry out | You want the recommended procedure | + +Tool availability is per-project configuration, not a fixed list. Call `tools/list` rather than +assuming a tool named here is present, and never assume one that is not. + +## The three specs + +`execute-request` reaches only servers declared in these definitions. There are three, and +`search-endpoints` searches all of them at once: + +| Spec title | Server | Auth | +| --- | --- | --- | +| `ReadMe API` | `https://api.readme.com/v2` | Bearer `rdme_...` | +| `Developer Metrics API` | `https://metrics.readme.io` | HTTP basic, the key as username and an empty password | +| `Legacy API` | `https://dash.readme.com/api/v1` | HTTP basic | + +**Use `ReadMe API` for all work on the user's content.** `Legacy API` is v1: it takes basic auth +rather than a bearer token, and it is unavailable to projects on ReadMe Refactored. `search-endpoints` +will surface its routes alongside the others — ignore them. Never fall back to v1 when a v2 call +fails. + +## Finding a route + +Do not guess paths or work from memory. `list-endpoints` returns every path and summary in a spec in +one cheap call; `get-endpoint` adds the full request and response schema for one of them, including +which routes exist only on ReadMe Refactored. Read them rather than reproducing them here. + +Two things the definitions do not tell you: + +- Paginated responses carry `paging.next`, `paging.previous`, `paging.first` and `paging.last`. + Query with `page` and `per_page`, max 100 and max 50 on search. +- Enterprise child projects need the child's own key. Only the API-key routes take a `{subdomain}`, + and `me` works there. + +## Calling execute-request + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +- `title` names the spec and is required whenever the server carries more than one, as this one does. + Omitting it fails validation before the request is made. +- `url` must be absolute. `get-endpoint` and `search-endpoints` report paths relative to the server + (`/projects/me`), so prepend `https://api.readme.com/v2` rather than pasting the path through. +- The key decides which project the call lands in, and the user cannot override it per-request. + Name the project before you change anything; asking them which one to write to is misleading. + +Setting up, verifying, or repairing that key is the `mcp-auth` skill. + +## When the user asks about their own docs + +"Search our docs", "what does our guide say", "find the page about X in my project" cannot be +answered by `search` or `fetch` here. Those read ReadMe's documentation and would return confident, +wrong answers about someone else's content. + +Their key already reaches their content through the ReadMe API, so switch tools and carry on rather +than sending them away to configure anything: + +| They want | Call, via `execute-request`, spec title `ReadMe API` | +| --- | --- | +| Search their content | `GET https://api.readme.com/v2/search?query=...`, optionally `section`, `version`, `projects` | +| Read one guide | `GET https://api.readme.com/v2/branches/{branch}/guides/{slug}` | +| List what is in a category | `GET https://api.readme.com/v2/branches/{branch}/categories/{section}/{title}/pages` | + +`{branch}` is a version number, `stable`, or a branch name. Everything is branch-scoped except +changelogs, images, fonts, API keys, search and the project itself. + +## Metrics + +Page views, search terms and page quality live in the `Developer Metrics API` spec, and reading them +needs the Enterprise plan — without it the API answers with an auth or plan error rather than saying +so. The registration sends a bearer token; if a call returns `Unauthorized` with a key set, send +`Authorization: Basic :">` on the HAR request instead. + +Most of what the dashboard shows has no route at all, so check here before going looking: + +| Metric | Readable via API | +| --- | --- | +| Page views, page quality, search terms | Yes | +| API calls | No — ingest only, `POST https://metrics.readme.io/request` | +| MCP tool calls, API errors, top endpoints, new users | No, dashboard only | + +For anything in the No rows, send the user to +`https://dash.readme.com/project/{subdomain}/v{version}/metrics`. + +Ingesting the user's own API logs is a server-side integration, not something to hand-build here. +Point them at the SDK for their stack — `readmeio` (Node), `readme-metrics` (Python, Ruby), +`readme/metrics` (PHP), `ReadMe.Metrics` (.NET) — and at `fetch` with id `main/sdks`. + +## A different server, when they actually want one + +Every ReadMe project publishes its own MCP server at `https://{subdomain}.readme.io/mcp`, or its +custom domain. That is for *their* end users to ask questions of *their* published hub. It is a +product feature they set up deliberately, not a workaround for this conversation, and it is not +needed to work on their own docs from here. + +A server's hostname decides which project it serves. Enterprise hostnames serve a whole group, so +`search` and `fetch` there can span several child projects. diff --git a/cursor/skills/onboarding/SKILL.md b/cursor/skills/onboarding/SKILL.md new file mode 100644 index 0000000..3b500eb --- /dev/null +++ b/cursor/skills/onboarding/SKILL.md @@ -0,0 +1,80 @@ +--- +name: onboarding +description: Get a new customer from zero to a live ReadMe developer hub. Use when someone is new to ReadMe, asks what ReadMe is, wants to sign up, create a project, publish their first API reference or guide, or needs an API key for this plugin. Explains the product and walks the web-UI setup flow. +--- + +# ReadMe onboarding + +> Onboarding endpoints are coming soon. Until then this skill only covers the web-UI flow and ReadMe basics. + +## Tools + +- `search` +- `fetch` +- `execute-request` + +## What ReadMe is + +ReadMe hosts a developer hub for your API at `{subdomain}.readme.io` or a custom domain. One hub contains: + +| Section | Content | +| --- | --- | +| Guides | Markdown pages in categories, with a sidebar | +| API Reference | Interactive docs generated from an OpenAPI or Swagger definition, with a Try It console | +| Recipes | Step-by-step code walkthroughs | +| Changelog | Release notes, shared across versions | +| Custom Pages | Free-form Markdown or HTML pages | + +Guides, reference, recipes and custom pages live on a **branch** (a version such as `1.0` or `stable`). The changelog does not. Ask AI and the project's MCP server answer questions from the hub content. Metrics track page views, search, page quality, and, with SDK setup, API calls. + +Plans: Starter (free), Pro, Enterprise. Enterprise supports child projects and Developer Metrics API reads. + +## Quick Start + +1. Sign up at `https://dash.readme.com/signup`. +2. Click **Create New Project**. Set a name, upload a logo (ReadMe picks brand colors from it), and choose the subdomain. +3. Add the API definition under **API Reference**: upload an OpenAPI file, import a URL, build one from scratch, or run `npx rdme openapi upload ` from a terminal. ReadMe validates the file and renders every endpoint. +4. Write the first guide under **Guides**. Use the AI Agent for a draft or the editor for a blank page. A "Getting Started" page is the usual first one. +5. Generate an API key at **Configuration → API Keys**, URL `https://dash.readme.com/project/{subdomain}/v{version}/api-key`. +6. Attach the key. The plugin's own `readme` server is anonymous and cannot read the key. The user registers a server under the same name, which replaces the plugin's one. The command differs per client: use the registration table in the `mcp-auth` skill, which also covers repairing a key that is already set. +7. Verify: `execute-request` with spec title `ReadMe API`, `GET https://api.readme.com/v2/projects/me`. A 200 with the project name means the plugin is wired to the right project. `Missing Security Schemes` means the registration is sending no key; a 401 titled `The API key couldn't be located.` means the key is wrong; a 500 titled `An unknown error has occurred.` means it resolved to an empty string. In every case, go back to step 5. + +After step 7, the `mcp-server` skill covers which spec and which project everything else lands in. + +## Driving the browser yourself + +Steps 1 to 5 are all browser work. If you can drive a browser, offer to do them in the user's own browser instead of only listing the steps: open the signup page, create the project, upload the API definition, and open the API Keys page. Let the user type credentials and payment details themselves. Steps 6 and 7 stay in the terminal. + +Find the row for the client you are running in. If you cannot tell which one that is, ask rather than guess. + +| Client | Drive the browser with | +| --- | --- | +| Claude Code | `mcp__claude-in-chrome__*`, when the Claude in Chrome extension is connected | +| ChatGPT desktop app or ChatGPT web | `@Browser`. It has its own profile, so the user signs in to ReadMe there, and it asks before submitting forms. It cannot upload files, so for step 3 import the API definition by URL or run `npx rdme openapi upload ` from a terminal | +| Codex CLI, Codex IDE extension, Cursor | No browser of their own. List the steps for the user | + +## When you cannot install anything yourself + +On a chat surface you have no shell and cannot add a marketplace, install a plugin or edit MCP config. Do not attempt it and do not ask the user to run commands there. If the ReadMe tools are missing, the user has to install the plugin by hand. Find their client below and give them those steps exactly. + +| Client | Steps | +| --- | --- | +| Claude Desktop, Cowork, claude.ai | Click **Customize** in the left sidebar, then **Plugins** — in Cowork, open the **Cowork** tab first. Under **Personal plugins**, click **+** → **Add marketplace** and enter `readmeio/agent-plugins`. Find **readme** in the list, click **Install**, then start a new chat so the tools load. Plugins need a paid Claude plan; on Team and Enterprise an owner may have disabled personal marketplaces, in which case they add it under **Organization settings → Plugins** | +| ChatGPT desktop app | Open the **Plugins** tab and click **Add marketplace**. Enter `readmeio/agent-plugins` as the source, leave the Git ref as `main` and the sparse paths empty, then click **Add marketplace**. Install **readme** from the new marketplace and start a new chat so the tools load | +| ChatGPT web | There is no marketplace option, only the plugin directory. Until the ReadMe plugin is listed there, the user can still add the MCP server on its own: turn on **Developer mode** under **Settings → Security and login**, open **Plugins**, click **+** next to the search box, and in the **New Plugin** form set the name to `readme`, the server URL to `https://docs.readme.com/mcp`, and authentication to **No Auth**. That gives the tools but not the skills, so keep this skill's content in the conversation yourself | +| Cursor | Open **Cursor Settings → Plugins**, search for **ReadMe**, click **Install** and choose project or user scope. Or run `/add-plugin readme` in chat | + +On the chat surfaces above the ReadMe connector stays read-only once installed: `search`, `fetch` and the endpoint tools work on public projects, but there is no way to supply an API key, so steps 6 and 7 of the Quick Start do not apply and write tools such as `update-docs` fail. Say so before the user tries. For creating or updating pages, offer to continue in a CLI or editor with the server registered as in step 6. + +## Reading the docs meanwhile + +Use `search` for a question, then `fetch` with the returned id. Useful pages: + +| Id | Page | +| --- | --- | +| `main/quickstart` | Three-step setup | +| `main/creating-a-project` | Project settings on creation | +| `main/openapi-upload-and-management` | Upload, sync, and re-sync an OpenAPI file | +| `main/branches` | How branches and versions work | +| `ref:main/intro-to-the-readme-api` | API v2 overview and auth | +| `main/sdks` | Metrics SDKs for API logs | diff --git a/mcp.json b/mcp.json deleted file mode 100644 index 07a58b9..0000000 --- a/mcp.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", - "mcpServers": { - "readme": { - "type": "streamable-http", - "url": "https://docs.readme.com/mcp", - "headers": { - "Authorization": "Bearer ${README_API_KEY}" - } - } - } -} diff --git a/scripts/sync-skills.mjs b/scripts/sync-skills.mjs new file mode 100755 index 0000000..5ae219a --- /dev/null +++ b/scripts/sync-skills.mjs @@ -0,0 +1,87 @@ +#!/usr/bin/env node +import { readdir, readFile, mkdir, copyFile, rm } from 'node:fs/promises'; +import { dirname, join, relative, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const source = join(repoRoot, 'skills'); +const hosts = ['claude', 'codex', 'cursor']; + +async function listFiles(root) { + const found = []; + async function walk(dir) { + let entries; + try { + entries = await readdir(dir, { withFileTypes: true }); + } catch (error) { + if (error.code === 'ENOENT') return; + throw error; + } + for (const entry of entries) { + const full = join(dir, entry.name); + if (entry.isDirectory()) await walk(full); + else found.push(relative(root, full)); + } + } + await walk(root); + return found.sort(); +} + +async function sameContent(a, b) { + const [left, right] = await Promise.all([readFile(a), readFile(b)]); + return left.equals(right); +} + +const sourceFiles = await listFiles(source); +if (sourceFiles.length === 0) { + console.error(`No skills found in ${relative(repoRoot, source)}`); + process.exit(1); +} + +const check = process.argv.includes('--check'); +const problems = []; + +for (const host of hosts) { + const target = join(repoRoot, host, 'skills'); + const targetFiles = await listFiles(target); + const extras = targetFiles.filter((file) => !sourceFiles.includes(file)); + + for (const file of sourceFiles) { + const from = join(source, file); + const to = join(target, file); + if (check) { + if (!targetFiles.includes(file)) problems.push(`missing: ${host}/skills/${file}`); + else if (!(await sameContent(from, to))) problems.push(`differs: ${host}/skills/${file}`); + continue; + } + await mkdir(dirname(to), { recursive: true }); + await copyFile(from, to); + } + + for (const file of extras) { + if (check) problems.push(`extra: ${host}/skills/${file}`); + else await rm(join(target, file)); + } +} + +if (!check) { + for (const host of hosts) { + const target = join(repoRoot, host, 'skills'); + for (const entry of await readdir(target, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const contents = await listFiles(join(target, entry.name)); + if (contents.length === 0) await rm(join(target, entry.name), { recursive: true }); + } + } + console.log(`Synced ${sourceFiles.length} file(s) to ${hosts.join(', ')}`); + process.exit(0); +} + +if (problems.length > 0) { + console.error('Generated skills are out of sync with skills/:'); + for (const problem of problems) console.error(` ${problem}`); + console.error('\nRun `node scripts/sync-skills.mjs` and commit the result.'); + process.exit(1); +} + +console.log(`Skills are in sync across ${hosts.join(', ')}`); diff --git a/scripts/validate-skills.mjs b/scripts/validate-skills.mjs new file mode 100755 index 0000000..4bbb2c5 --- /dev/null +++ b/scripts/validate-skills.mjs @@ -0,0 +1,68 @@ +#!/usr/bin/env node +import { readdir, readFile } from 'node:fs/promises'; +import { basename, dirname, join, relative, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const SKILL_CHAR_LIMIT = 20000; + +const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..'); +const roots = ['skills', 'claude/skills', 'codex/skills', 'cursor/skills']; +const problems = []; + +function parseFrontmatter(text) { + const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/); + if (!match) return null; + const fields = {}; + for (const line of match[1].split(/\r?\n/)) { + const field = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/); + if (field) fields[field[1]] = field[2].trim(); + } + return fields; +} + +for (const root of roots) { + const dir = join(repoRoot, root); + let entries; + try { + entries = await readdir(dir, { withFileTypes: true }); + } catch { + problems.push(`${root}: directory is missing`); + continue; + } + + for (const entry of entries) { + if (!entry.isDirectory()) continue; + const path = join(dir, entry.name, 'SKILL.md'); + const label = relative(repoRoot, path); + let text; + try { + text = await readFile(path, 'utf8'); + } catch { + problems.push(`${label}: skill directory has no SKILL.md`); + continue; + } + + if (text.length > SKILL_CHAR_LIMIT) { + problems.push(`${label}: ${text.length} chars exceeds the ${SKILL_CHAR_LIMIT} limit`); + } + + const frontmatter = parseFrontmatter(text); + if (!frontmatter) { + problems.push(`${label}: no YAML frontmatter`); + continue; + } + if (!frontmatter.name) problems.push(`${label}: frontmatter has no name`); + else if (frontmatter.name !== basename(dirname(path))) { + problems.push(`${label}: name "${frontmatter.name}" does not match its directory`); + } + if (!frontmatter.description) problems.push(`${label}: frontmatter has no description`); + } +} + +if (problems.length > 0) { + console.error('Skill validation failed:'); + for (const problem of problems) console.error(` ${problem}`); + process.exit(1); +} + +console.log(`Validated frontmatter and size for skills in ${roots.join(', ')}`); diff --git a/skills/mcp-auth/SKILL.md b/skills/mcp-auth/SKILL.md new file mode 100644 index 0000000..f26cc61 --- /dev/null +++ b/skills/mcp-auth/SKILL.md @@ -0,0 +1,111 @@ +--- +name: mcp-auth +description: Establish or repair the ReadMe API key behind execute-request. Use when a ReadMe call fails with "Missing Security Schemes", "The API key couldn't be located", or "An unknown error has occurred", when the user asks which project their key reaches, when they say they set a key but it is not working, and before the first write to their project. +--- + +# The key behind execute-request + +The plugin ships anonymous. Public reads of ReadMe's documentation need no key; anything touching +the user's own project does, and it travels on the MCP server registration rather than in the tool +call. See the `mcp-server` skill for which calls land where. + +## Tools + +- `execute-request` + +## Assume anonymous + +Most users have not registered a key, so do not spend a call proving it. Go straight to the work: + +- ReadMe documentation questions — `search` and `fetch`, no key involved. +- The user's project, and they have not mentioned a key — go to **No key yet** below. +- The user says they registered one, or an `execute-request` call fails — verify with the probe. + +## Verify + +`execute-request`, spec title `ReadMe API`, with no `Authorization` header of your own: + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +| Response | Means | Next | +| --- | --- | --- | +| A project object | The registration carries a working key | Name the project and subdomain, carry on. Ask for nothing | +| `Missing Security Schemes` | No `Authorization` header at all — the server is anonymous | **No key yet** | +| `"title": "The API key couldn't be located."`, status 401 | A key is being sent, but it is not a real key | **A key is set but wrong** | +| `"title": "An unknown error has occurred."`, status 500 | The bearer is empty — the variable resolved to nothing | **A key is set but wrong** | + +Resolve this once per session. Having seen a project object, send no `Authorization` header of your +own for the rest of the session. + +## No key yet + +If the user has not named a project, ask, and wait. Pick no project, infer none from open files or +earlier turns, and read no guides to narrow it down. Ask which project, and whether they would rather +register the key with the server or paste it in chat. + +Offer the better option first: + +- **Preferred:** they register the server with the key themselves, so it stays out of the chat. They + create the key under **Configuration → API Keys** in ReadMe, then follow **Registering the key** + below. +- **Otherwise:** they paste it and you send it as a header on each call: + + ```json + { + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me", + "headers": [{ "name": "Authorization", "value": "Bearer rdme_..." }] + } + } + ``` + + This works, and it also puts the key in the transcript. Say so, and tell them to rotate it. + +Send a header of your own **only** when the probe returned `Missing Security Schemes`. A header on the +server registration overrides anything you set in `harRequest`, so against a registered server a +pasted key is silently ignored and the call lands in whichever project the registration owns — no +error, just the wrong project. + +## A key is set but wrong + +Both failures mean an `Authorization` header is reaching the API and the API is rejecting it. The +usual cause is the registration referring to an environment variable the client resolved to nothing, +or passed through as literal text, because the variable is unset where the client launched from: + +- **Status 500, `An unknown error has occurred.`** — the header arrived empty. +- **Status 401, `The API key couldn't be located.`** — a value arrived, but no such key exists. + Also what a revoked or mistyped key returns. + +Say that plainly before asking them to paste anything. Ask them to confirm the variable is exported +in the environment the client launched from, then reconnect as the **Registering the key** row for +their client describes. If the key is genuinely gone, they create a new one under **Configuration → +API Keys**. + +Do not work around either failure by probing with `curl` or by retrying against the `Legacy API` +spec. One key maps to one project; if the call reaches the wrong project, the registration is the +thing to change. + +## Registering the key + +Registering a `readme` server of their own replaces the plugin's anonymous one. The server name stays +the same, so the skills and tools keep working. + +Find the row for the client you are running in. If you cannot tell which one that is, ask the user — +the wrong row sends them to a config file their client never reads. + +| Client | How | +| --- | --- | +| Claude Code | `export README_API_KEY=rdme_…`, then `claude mcp add --scope user --transport http readme https://docs.readme.com/mcp --header 'Authorization: Bearer ${README_API_KEY}'`. Keep the single quotes: the variable is expanded when Claude Code starts, so the key never lands in a config file. Restart afterwards | +| Codex, and the ChatGPT desktop app that shares its config | `export README_API_KEY=rdme_…`, then `codex mcp add readme --url https://docs.readme.com/mcp --bearer-token-env-var README_API_KEY`. Codex reads the variable at startup, so the key never lands in a config file. Start a new session afterwards | +| Cursor | Add a `readme` entry to `~/.cursor/mcp.json` for every project, or `.cursor/mcp.json` for one: `"type": "http"`, `"url": "https://docs.readme.com/mcp"`, and a `headers` block setting `Authorization` to `Bearer ${env:README_API_KEY}`. Export the variable in the shell the editor launches from, then restart the editor, since the header resolves when the server connects | +| Claude Desktop, Cowork, claude.ai, ChatGPT web | Not possible. The connector is read-only on these surfaces and cannot take a key. Say so, and offer to carry on in a CLI or editor | diff --git a/skills/mcp-server/SKILL.md b/skills/mcp-server/SKILL.md new file mode 100644 index 0000000..fcbaf37 --- /dev/null +++ b/skills/mcp-server/SKILL.md @@ -0,0 +1,124 @@ +--- +name: mcp-server +description: Route ReadMe MCP calls to the right project and the right spec. Use before any request touching the user's own content - "our docs", "my hub", "create a page", "update the changelog", "what endpoints do we have", "search our guides", "how many page views", "top search terms", "send our API logs to ReadMe" - and before calling execute-request for the first time in a session. +--- + +# Which project does this call land in? + +This plugin connects to `https://docs.readme.com/mcp`, which serves **ReadMe's own documentation**. +`search` and `fetch` answer questions about how ReadMe works. The user's project is reached only +through `execute-request` with their API key. Reads and writes land in different places. + +Questions about ReadMe itself need nothing else: use `search` and `fetch` and answer. + +## Tools on this server + +| Tool | Answers for | Use it when | +| --- | --- | --- | +| `search`, `fetch` | ReadMe's own product documentation | The question is about how ReadMe works | +| `list-specs`, `list-endpoints`, `get-endpoint`, `search-endpoints` | ReadMe's API definitions, as documents | Looking up a route before calling it | +| `execute-request` | **The user's project**, with their key | Reading or changing anything of theirs | +| `update-docs`, `draft-changelog` | Nothing. They return instructions for you to carry out | You want the recommended procedure | + +Tool availability is per-project configuration, not a fixed list. Call `tools/list` rather than +assuming a tool named here is present, and never assume one that is not. + +## The three specs + +`execute-request` reaches only servers declared in these definitions. There are three, and +`search-endpoints` searches all of them at once: + +| Spec title | Server | Auth | +| --- | --- | --- | +| `ReadMe API` | `https://api.readme.com/v2` | Bearer `rdme_...` | +| `Developer Metrics API` | `https://metrics.readme.io` | HTTP basic, the key as username and an empty password | +| `Legacy API` | `https://dash.readme.com/api/v1` | HTTP basic | + +**Use `ReadMe API` for all work on the user's content.** `Legacy API` is v1: it takes basic auth +rather than a bearer token, and it is unavailable to projects on ReadMe Refactored. `search-endpoints` +will surface its routes alongside the others — ignore them. Never fall back to v1 when a v2 call +fails. + +## Finding a route + +Do not guess paths or work from memory. `list-endpoints` returns every path and summary in a spec in +one cheap call; `get-endpoint` adds the full request and response schema for one of them, including +which routes exist only on ReadMe Refactored. Read them rather than reproducing them here. + +Two things the definitions do not tell you: + +- Paginated responses carry `paging.next`, `paging.previous`, `paging.first` and `paging.last`. + Query with `page` and `per_page`, max 100 and max 50 on search. +- Enterprise child projects need the child's own key. Only the API-key routes take a `{subdomain}`, + and `me` works there. + +## Calling execute-request + +```json +{ + "title": "ReadMe API", + "harRequest": { + "method": "get", + "url": "https://api.readme.com/v2/projects/me" + } +} +``` + +- `title` names the spec and is required whenever the server carries more than one, as this one does. + Omitting it fails validation before the request is made. +- `url` must be absolute. `get-endpoint` and `search-endpoints` report paths relative to the server + (`/projects/me`), so prepend `https://api.readme.com/v2` rather than pasting the path through. +- The key decides which project the call lands in, and the user cannot override it per-request. + Name the project before you change anything; asking them which one to write to is misleading. + +Setting up, verifying, or repairing that key is the `mcp-auth` skill. + +## When the user asks about their own docs + +"Search our docs", "what does our guide say", "find the page about X in my project" cannot be +answered by `search` or `fetch` here. Those read ReadMe's documentation and would return confident, +wrong answers about someone else's content. + +Their key already reaches their content through the ReadMe API, so switch tools and carry on rather +than sending them away to configure anything: + +| They want | Call, via `execute-request`, spec title `ReadMe API` | +| --- | --- | +| Search their content | `GET https://api.readme.com/v2/search?query=...`, optionally `section`, `version`, `projects` | +| Read one guide | `GET https://api.readme.com/v2/branches/{branch}/guides/{slug}` | +| List what is in a category | `GET https://api.readme.com/v2/branches/{branch}/categories/{section}/{title}/pages` | + +`{branch}` is a version number, `stable`, or a branch name. Everything is branch-scoped except +changelogs, images, fonts, API keys, search and the project itself. + +## Metrics + +Page views, search terms and page quality live in the `Developer Metrics API` spec, and reading them +needs the Enterprise plan — without it the API answers with an auth or plan error rather than saying +so. The registration sends a bearer token; if a call returns `Unauthorized` with a key set, send +`Authorization: Basic :">` on the HAR request instead. + +Most of what the dashboard shows has no route at all, so check here before going looking: + +| Metric | Readable via API | +| --- | --- | +| Page views, page quality, search terms | Yes | +| API calls | No — ingest only, `POST https://metrics.readme.io/request` | +| MCP tool calls, API errors, top endpoints, new users | No, dashboard only | + +For anything in the No rows, send the user to +`https://dash.readme.com/project/{subdomain}/v{version}/metrics`. + +Ingesting the user's own API logs is a server-side integration, not something to hand-build here. +Point them at the SDK for their stack — `readmeio` (Node), `readme-metrics` (Python, Ruby), +`readme/metrics` (PHP), `ReadMe.Metrics` (.NET) — and at `fetch` with id `main/sdks`. + +## A different server, when they actually want one + +Every ReadMe project publishes its own MCP server at `https://{subdomain}.readme.io/mcp`, or its +custom domain. That is for *their* end users to ask questions of *their* published hub. It is a +product feature they set up deliberately, not a workaround for this conversation, and it is not +needed to work on their own docs from here. + +A server's hostname decides which project it serves. Enterprise hostnames serve a whole group, so +`search` and `fetch` there can span several child projects. diff --git a/skills/onboarding/SKILL.md b/skills/onboarding/SKILL.md new file mode 100644 index 0000000..3b500eb --- /dev/null +++ b/skills/onboarding/SKILL.md @@ -0,0 +1,80 @@ +--- +name: onboarding +description: Get a new customer from zero to a live ReadMe developer hub. Use when someone is new to ReadMe, asks what ReadMe is, wants to sign up, create a project, publish their first API reference or guide, or needs an API key for this plugin. Explains the product and walks the web-UI setup flow. +--- + +# ReadMe onboarding + +> Onboarding endpoints are coming soon. Until then this skill only covers the web-UI flow and ReadMe basics. + +## Tools + +- `search` +- `fetch` +- `execute-request` + +## What ReadMe is + +ReadMe hosts a developer hub for your API at `{subdomain}.readme.io` or a custom domain. One hub contains: + +| Section | Content | +| --- | --- | +| Guides | Markdown pages in categories, with a sidebar | +| API Reference | Interactive docs generated from an OpenAPI or Swagger definition, with a Try It console | +| Recipes | Step-by-step code walkthroughs | +| Changelog | Release notes, shared across versions | +| Custom Pages | Free-form Markdown or HTML pages | + +Guides, reference, recipes and custom pages live on a **branch** (a version such as `1.0` or `stable`). The changelog does not. Ask AI and the project's MCP server answer questions from the hub content. Metrics track page views, search, page quality, and, with SDK setup, API calls. + +Plans: Starter (free), Pro, Enterprise. Enterprise supports child projects and Developer Metrics API reads. + +## Quick Start + +1. Sign up at `https://dash.readme.com/signup`. +2. Click **Create New Project**. Set a name, upload a logo (ReadMe picks brand colors from it), and choose the subdomain. +3. Add the API definition under **API Reference**: upload an OpenAPI file, import a URL, build one from scratch, or run `npx rdme openapi upload ` from a terminal. ReadMe validates the file and renders every endpoint. +4. Write the first guide under **Guides**. Use the AI Agent for a draft or the editor for a blank page. A "Getting Started" page is the usual first one. +5. Generate an API key at **Configuration → API Keys**, URL `https://dash.readme.com/project/{subdomain}/v{version}/api-key`. +6. Attach the key. The plugin's own `readme` server is anonymous and cannot read the key. The user registers a server under the same name, which replaces the plugin's one. The command differs per client: use the registration table in the `mcp-auth` skill, which also covers repairing a key that is already set. +7. Verify: `execute-request` with spec title `ReadMe API`, `GET https://api.readme.com/v2/projects/me`. A 200 with the project name means the plugin is wired to the right project. `Missing Security Schemes` means the registration is sending no key; a 401 titled `The API key couldn't be located.` means the key is wrong; a 500 titled `An unknown error has occurred.` means it resolved to an empty string. In every case, go back to step 5. + +After step 7, the `mcp-server` skill covers which spec and which project everything else lands in. + +## Driving the browser yourself + +Steps 1 to 5 are all browser work. If you can drive a browser, offer to do them in the user's own browser instead of only listing the steps: open the signup page, create the project, upload the API definition, and open the API Keys page. Let the user type credentials and payment details themselves. Steps 6 and 7 stay in the terminal. + +Find the row for the client you are running in. If you cannot tell which one that is, ask rather than guess. + +| Client | Drive the browser with | +| --- | --- | +| Claude Code | `mcp__claude-in-chrome__*`, when the Claude in Chrome extension is connected | +| ChatGPT desktop app or ChatGPT web | `@Browser`. It has its own profile, so the user signs in to ReadMe there, and it asks before submitting forms. It cannot upload files, so for step 3 import the API definition by URL or run `npx rdme openapi upload ` from a terminal | +| Codex CLI, Codex IDE extension, Cursor | No browser of their own. List the steps for the user | + +## When you cannot install anything yourself + +On a chat surface you have no shell and cannot add a marketplace, install a plugin or edit MCP config. Do not attempt it and do not ask the user to run commands there. If the ReadMe tools are missing, the user has to install the plugin by hand. Find their client below and give them those steps exactly. + +| Client | Steps | +| --- | --- | +| Claude Desktop, Cowork, claude.ai | Click **Customize** in the left sidebar, then **Plugins** — in Cowork, open the **Cowork** tab first. Under **Personal plugins**, click **+** → **Add marketplace** and enter `readmeio/agent-plugins`. Find **readme** in the list, click **Install**, then start a new chat so the tools load. Plugins need a paid Claude plan; on Team and Enterprise an owner may have disabled personal marketplaces, in which case they add it under **Organization settings → Plugins** | +| ChatGPT desktop app | Open the **Plugins** tab and click **Add marketplace**. Enter `readmeio/agent-plugins` as the source, leave the Git ref as `main` and the sparse paths empty, then click **Add marketplace**. Install **readme** from the new marketplace and start a new chat so the tools load | +| ChatGPT web | There is no marketplace option, only the plugin directory. Until the ReadMe plugin is listed there, the user can still add the MCP server on its own: turn on **Developer mode** under **Settings → Security and login**, open **Plugins**, click **+** next to the search box, and in the **New Plugin** form set the name to `readme`, the server URL to `https://docs.readme.com/mcp`, and authentication to **No Auth**. That gives the tools but not the skills, so keep this skill's content in the conversation yourself | +| Cursor | Open **Cursor Settings → Plugins**, search for **ReadMe**, click **Install** and choose project or user scope. Or run `/add-plugin readme` in chat | + +On the chat surfaces above the ReadMe connector stays read-only once installed: `search`, `fetch` and the endpoint tools work on public projects, but there is no way to supply an API key, so steps 6 and 7 of the Quick Start do not apply and write tools such as `update-docs` fail. Say so before the user tries. For creating or updating pages, offer to continue in a CLI or editor with the server registered as in step 6. + +## Reading the docs meanwhile + +Use `search` for a question, then `fetch` with the returned id. Useful pages: + +| Id | Page | +| --- | --- | +| `main/quickstart` | Three-step setup | +| `main/creating-a-project` | Project settings on creation | +| `main/openapi-upload-and-management` | Upload, sync, and re-sync an OpenAPI file | +| `main/branches` | How branches and versions work | +| `ref:main/intro-to-the-readme-api` | API v2 overview and auth | +| `main/sdks` | Metrics SDKs for API logs |