api-patterns
3CX's native PBX MCP server: the per-PBX endpoint shape (every PBX is its own FQDN and its own OAuth authorization server — there is no shared mcp.3cx.com),…
Locating the right Keeper record without reading any of them: the metadata returned by `list_secrets`, `search_secrets` and `list_folders`, how record UIDs, titles and folders relate, what `search_secrets` actually matches on (and what it silently does not), the query strings
$ npx -y skills add wyre-technology/msp-claude-plugins --skill finding-secrets --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/finding-secretsContext preview
The summary Claude sees to decide when to auto-load this skill.
Locating the right Keeper record without reading any of them: the metadata returned by `list_secrets`, `search_secrets` and `list_folders`, how record UIDs, titles and folders relate, what `search_secrets` actually matches on (and what it silently does not), the query strings
name: "Keeper Secret Discovery" description: > Locating the right Keeper record without reading any of them: the metadata returned by `list_secrets`, `search_secrets` and `list_folders`, how record UIDs, titles and folders relate, what `search_secrets` actually matches on (and what it silently does not), the query strings its validator rejects, and why an empty result set usually means scope rather than absence. when_to_use: >- When turning a description of a credential into the record UID that retrieval needs, or when auditing what a Keeper connection can reach. Use when: find a keeper secret, search keeper, list_secrets, search_secrets, list_folders, keeper record uid, keeper folder, what secrets can you see, or "which record holds the".
Discovery is the half of Keeper work that involves no credential material at all. `list_secrets`, `search_secrets` and `list_folders` return metadata only — no passwords, no field values, nothing masked because there is nothing sensitive to mask. Do all the narrowing here, end up with one UID, and only then reach for a retrieval tool.
Every record in the application's scope, optionally filtered by folder.
{} // everything in scope
{ "folder_uid": "kJ3v9PqR2mXwL7nBcD4sTg" } // one folder
{ "folder_uids": ["uid1", "uid2"] } // several — preferred`folder_uids` is the multi-folder form and is pushed down to Keeper's folder filtering; `folder_uid` exists for backward compatibility. Passing both ignores the singular one.
Response — `count` plus one entry per record:
{ "count": 2, "secrets": [
{ "uid": "NJ_xXSkk3xYI1h9ql5lAiQ", "title": "Contoso DC01 Local Admin",
"type": "serverCredentials", "folder": "kJ3v9PqR2mXwL7nBcD4sTg" },
{ "uid": "8fQ2wKmT5vXyL9pRnB3cAa", "title": "Contoso Firewall",
"type": "login", "folder": "kJ3v9PqR2mXwL7nBcD4sTg" }
] }Four fields, and that is the whole surface: `uid`, `title`, `type`, `folder`. `type` is the Keeper record type — it tells you which fields the record can carry, and so which field names KSM notation is likely to address. To see the actual names, read them off a masked `get_secret`.
Every folder shared to the application, as a flat list of `uid` / `name` / `parent_uid`. Nesting is expressed by `parent_uid` pointers, not by structure, so build the tree yourself if you need one. A folder whose `parent_uid` names a folder that is not in the list is a subfolder of something outside the application's scope — normal, not an error.
This is also the fastest honest answer to "what can this connection see?".
One required `query` string. Returns the same four metadata fields under `results`, plus a `count`.
What it actually matches — verified against the upstream implementation, in this order, stopping at the first hit:
1. Record **title** 2. Record **notes** 3. Record **type** (so `query: "login"` matches every login record) 4. The values of four standard fields only: **`login`, `url`, `hostname`, `address`** 5. **Attachment** filenames and titles
All matching is case-insensitive substring. It is not a Keeper-side search: the tool pulls every record in scope and filters locally, so there is no pagination, no ranking and no result cap — a broad query returns the whole scope.
1. `list_folders` — orient, and confirm the scope is what you expect. 2. `search_secrets` with the most distinctive token you have (a hostname, a tenant name, a device name). Prefer one distinctive word over a long phrase; the match is a plain substring, so `"Contoso DC01"` fails against a record titled `"DC01 (Contoso) Local Admin"` while `"DC01"` succeeds. 3. If the result set is large, narrow with `list_secrets` on the folder the likely candidate sits in. 4. Confirm the choice by title and type, then hand the UID to `get_field` — not to `get_secret`, unless the whole record is genuinely needed.
When two candidates look equally plausible, ask the user which record they mean. Do not disambiguate by fetching both and comparing credentials.
**`search_secrets` does not search custom fields.** The upstream code carries an explicit TODO to that effect. A record whose only mention of "Azure" is a custom field labelled `Tenant ID` will not be found by searching `Azure`. Fall back to `list_secrets` over the likely folder and read titles.
**Some perfectly ordinary queries are rejected, and client names are the usual casualty.** The query validator refuses, case-insensitively, any query *containing* `union`, `select`, `insert`, `update`, `delete`, `drop`, `xp_`, `sp_`, `--`, `/*`, `*/`, `';` or `";` — plus shell metacharacters and queries over 256 characters. It is a SQL-injection filter applied to a search that never touches SQL, and it matches on substrings, so it catches real business names:
| Query | Rejected because it contains | |-------|------------------------------| | `Union Bank` | `union` | | `Updates Server` | `update` | | `Select Insurance` | `select` | | `Dropbox` | `drop` | | `Insertech` | `insert` |
All of them fail with `search query contains suspicious patterns`. That is input validation, not a permissions problem and not evidence the record is missing — never report it as either. Search a different token from the same record: a hostname, a login, part of a URL. Not a folder name — `search_secrets` never matches those (see the match list above); to narrow by folder use `list_secrets` with `folder_uids`.
**A hit does not mean the title matched.** Matching on notes and on `login` / `url` / `hostname` / `address` values means a result can look unrelated to the query. Read the returned `title` before acting, and say which record you chose.
**`type` matching makes generic queries useless.** Searching `login` ma
One command to supercharge Claude Code for MSP workflows. Then restart Claude Code. That's it. Documentation: mcp.wyre.ai
Repo: wyre-technology/msp-claude-plugins
3CX's native PBX MCP server: the per-PBX endpoint shape (every PBX is its own FQDN and its own OAuth authorization server — there is no shared mcp.3cx.com),…
3CX's live-operations surface: read-only visibility into active calls, recordings, voicemail, department and queue membership, and forwarding/presence…
3CX's read-only directory surface: resolving a caller by email or by exact extension, searching the PBX's own phonebooks, searching contacts synced from an…
3CX's system-and-configuration surface: server time, PBX event log and application log search, service status, database schema and the read-only SELECT-only…
Abnormal Security abuse mailbox cases: user-reported email submissions, case statuses and judgments, the case lifecycle, bulk and remediation actions, and…
Abnormal Security message analysis: message retrieval, email header inspection, attachments, sender reputation, delivery context, and SPF/DKIM/DMARC…