@paneui/mcp 0.0.19 → 0.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -94,21 +94,62 @@ Config precedence mirrors the CLI: env vars win over the saved profile, which fa
94
94
 
95
95
  ## Tools
96
96
 
97
- MCP tools are request/response — there is no long-lived "watch". To receive a human's response you **poll** `get_events` with the cursor from the previous call (optionally with `wait_seconds` to long-poll). Each tool description spells out the pattern for the model.
97
+ This server has **full parity with the [`pane` CLI](https://www.npmjs.com/package/@paneui/cli)** every capability the CLI exposes is reachable here.
98
+
99
+ MCP tools are request/response — there is no long-lived "watch". To receive a human's response you **poll** `get_events` with the cursor from the previous call (optionally with `wait_seconds` to long-poll); to watch a record collection, re-call `list_records` with the prior `since`. Each tool description spells out the pattern for the model.
100
+
101
+ To keep the tool list compact (a flat 50+ tools would bloat client context and degrade selection), **hot-path nouns stay discrete tools** while **multi-verb management nouns collapse into one tool each with a required `action` enum**.
102
+
103
+ ### Hot-path (discrete) tools
98
104
 
99
105
  | Tool | What it does |
100
106
  | --- | --- |
101
- | `create_pane` | Create a pane from inline HTML (+ optional event/record schema). Returns `{ pane_id, url, expires_at }`. **Give `url` to the human.** |
107
+ | `create_pane` | Create a pane inline HTML (`name`+`html`) **or** reuse a saved template (`template_id`). Optional event/input/record schema, participants, tags, icon, callback, `context_key`. Returns `{ pane_id, url, urls, title, expires_at }`. **Give `url` to the human.** |
102
108
  | `get_pane_state` | Fetch a pane's metadata (status, title, expiry) without its event log. |
103
109
  | `get_events` | Poll the pane's append-only event log for what the human did. Pass `since` (cursor) and optional `wait_seconds` (long-poll). |
104
110
  | `send_to_pane` | Push an event into an open pane to update the live UI. |
105
- | `list_records` | List rows in a pane's mutable record collection (todos, line items, comments…). |
111
+ | `update_pane` | Edit a live pane in place (ttl/title/preamble/input_data/metadata/tags/icon). |
112
+ | `upgrade_pane` | Re-pin a live pane to another version of its template (swap HTML+schemas, same URL). |
113
+ | `list_panes` | Enumerate your panes (filter by status/template_id; paginated). |
114
+ | `delete_pane` | Close/delete a pane. |
115
+ | `list_records` | List rows in a pane's mutable record collection (also the records poll/watch). |
116
+ | `get_record` | Fetch one record row by key. |
106
117
  | `upsert_record` | Create/return a record row (dedups on `record_key`). |
107
118
  | `update_record` | Update a record row (optional `if_match` optimistic lock). |
108
119
  | `delete_record` | Soft-delete a record row (the page sees it live). |
109
120
 
121
+ ### Consolidated tools (one tool, required `action`)
122
+
123
+ | Tool | Actions |
124
+ | --- | --- |
125
+ | `template` | `create` · `version` · `update` · `search` · `list` · `show` · `get_version` · `delete` · `publish` · `unpublish` · `search_public` · `set_icon` |
126
+ | `template_records` | `list` · `get` · `upsert` · `update` · `delete` · `delete_collection` |
127
+ | `participant` | `list` · `new` · `revoke` |
128
+ | `share` | `list` · `invite` · `set_access` · `revoke` |
129
+ | `attachments` | `upload` · `download` · `show` · `list` · `delete` · `mint_token` · `revoke_token` · `list_tokens` |
130
+ | `taste` | `get` · `set` · `clear` |
131
+ | `key` | `list` · `revoke` |
132
+ | `trash` | `list` · `restore` · `restore_template` · `purge` · `purge_template` |
133
+ | `feedback` | `create` · `list` |
134
+ | `agent` | `whoami` · `claim` · `logout` |
135
+
136
+ ### Single-purpose tools
137
+
138
+ | Tool | What it does |
139
+ | --- | --- |
140
+ | `run_query` | Read-only SQL over your scoped panes/records/events (`format`: json/csv/tsv/table). |
141
+ | `get_skill` | Fetch the relay's auto-updating `SKILL.md` (unauthenticated) to self-teach the workflow. |
142
+
143
+ **Attachments** take/return file paths: `upload` reads an absolute `file_path`; `download` writes to an absolute `out_path` (or returns base64 when omitted).
144
+
110
145
  **Events vs records.** Events are an append-only journal — forms, approvals, surveys, pickers. Records are a mutable collection where the current state matters more than the edit history — todo lists, kanban boards, comment threads. Reach for records when the page shows several mutable items.
111
146
 
147
+ ### Not exposed (and why)
148
+
149
+ - The CLI's `config show` is replaced by `agent`→`whoami` (resolved relay URL + active profile + whether a key is set; no secrets).
150
+ - `agent register` isn't a tool — the server auto-registers on first use and shares the CLI's key store; `agent`→`claim` binds it to a human afterward.
151
+ - `demo` (the interactive 60-second terminal tour) and the CLI self-updater are terminal/CLI concerns with no agent use; omitted.
152
+
112
153
  ## Typical flow
113
154
 
114
155
  1. `create_pane` with your HTML + an `event_schema` declaring the events the page emits → returns a `url`.
package/dist/config.js CHANGED
@@ -118,6 +118,29 @@ function upsertProfile(name, patch, setCurrent) {
118
118
  writeFileSync(path, serialize(store), { mode: 0o600 });
119
119
  chmodSync(path, 0o600);
120
120
  }
121
+ /**
122
+ * Clear the active saved profile from the shared store (mirrors `pane agent
123
+ * logout` for the active-profile case). Removes the profile entry and unsets
124
+ * `current_profile` so the next resolve falls back to env / the default URL.
125
+ * Local-only: it does NOT revoke the key on the relay (use the `key` tool's
126
+ * `revoke` action for that). Idempotent — clearing an empty store is a no-op.
127
+ * Returns the profile name that was cleared (or null when nothing was active)
128
+ * and the store path.
129
+ */
130
+ export function clearActiveProfile() {
131
+ const store = readStore();
132
+ const path = storePath();
133
+ const name = store.currentProfile;
134
+ if (name === undefined || store.profiles[name] === undefined) {
135
+ return { cleared: true, profile: null, path };
136
+ }
137
+ delete store.profiles[name];
138
+ store.currentProfile = undefined;
139
+ mkdirSync(dirname(path), { recursive: true });
140
+ writeFileSync(path, serialize(store), { mode: 0o600 });
141
+ chmodSync(path, 0o600);
142
+ return { cleared: true, profile: name, path };
143
+ }
121
144
  /** Resolve the relay URL using the same precedence as the CLI. */
122
145
  export function resolveUrl() {
123
146
  const store = readStore();
@@ -127,6 +150,38 @@ export function resolveUrl() {
127
150
  const url = process.env.PANE_URL ?? active?.url ?? DEFAULT_RELAY_URL;
128
151
  return url.replace(/\/$/, "");
129
152
  }
153
+ /**
154
+ * Describe how the server is currently configured WITHOUT touching the network
155
+ * — the resolved relay URL, the active profile name, where the key is coming
156
+ * from, and whether a key is present at all. Backs the `agent` tool's `whoami`
157
+ * action so an MCP client can introspect its own identity / relay binding the
158
+ * way `pane config show` does for the CLI. No secrets are returned (the API key
159
+ * plaintext is never surfaced — only its source + whether it exists).
160
+ */
161
+ export function describeActiveConfig() {
162
+ const store = readStore();
163
+ const url = resolveUrl();
164
+ const profile = store.currentProfile ?? null;
165
+ let source = "none";
166
+ if ((process.env.PANE_API_KEY && process.env.PANE_API_KEY !== "") ||
167
+ (process.env.PANE_TOKEN && process.env.PANE_TOKEN !== "")) {
168
+ source = "env";
169
+ }
170
+ else {
171
+ const active = store.currentProfile
172
+ ? store.profiles[store.currentProfile]
173
+ : undefined;
174
+ if (active?.apiKey && active.apiKey !== "")
175
+ source = "profile";
176
+ }
177
+ return {
178
+ url,
179
+ profile,
180
+ api_key_present: source !== "none",
181
+ api_key_source: source,
182
+ store_path: storePath(),
183
+ };
184
+ }
130
185
  /** Resolve the API key (env → PANE_TOKEN alias → active profile). */
131
186
  function resolveApiKey() {
132
187
  const store = readStore();
package/dist/index.js CHANGED
@@ -65,8 +65,11 @@ Environment:
65
65
  PANE_AGENT_NAME Display name for the auto-registered agent.
66
66
  PANE_REGISTER_SECRET Registration secret (REGISTRATION_MODE=secret relays).
67
67
 
68
- Tools exposed: create_pane, get_pane_state, get_events, send_to_pane,
69
- list_records, upsert_record, update_record, delete_record.
68
+ Tools exposed (full CLI parity): create_pane, get_pane_state, get_events,
69
+ send_to_pane, update_pane, upgrade_pane, list_panes, delete_pane,
70
+ list_records, get_record, upsert_record, update_record, delete_record,
71
+ template, template_records, participant, share, attachments, taste, key,
72
+ trash, feedback, agent, run_query, get_skill.
70
73
 
71
74
  See https://github.com/aerolalit/paneui for docs.
72
75
  `;
package/dist/skill.js ADDED
@@ -0,0 +1,54 @@
1
+ // Fetch the relay's auto-updating SKILL.md over plain HTTP.
2
+ //
3
+ // Mirrors `pane skill show|version` (packages/cli/src/commands/skill.ts): the
4
+ // relay serves its skill at GET /skills/pane/SKILL.md and the version at GET
5
+ // /skills/pane/SKILL.md/version. Both routes are UNAUTHENTICATED — no API key
6
+ // needed — so an MCP client can self-teach the Pane workflow before (or without)
7
+ // provisioning a key. We don't go through PaneClient here precisely because no
8
+ // auth is required and the skill routes are exempt from the version-skew check.
9
+ import { VERSION } from "./version.js";
10
+ /**
11
+ * GET the relay's full SKILL.md markdown. `version: true` instead fetches just
12
+ * the relay's reported skill version (the "is my local copy stale?" probe).
13
+ * Throws on a non-2xx or network failure with a message the tool layer can
14
+ * surface.
15
+ */
16
+ export async function fetchSkill(relayUrl, opts = {}) {
17
+ const base = relayUrl.replace(/\/$/, "");
18
+ if (opts.version) {
19
+ const target = base + "/skills/pane/SKILL.md/version";
20
+ const res = await fetchOrThrow(target);
21
+ let body;
22
+ try {
23
+ body = await res.json();
24
+ }
25
+ catch {
26
+ body = null;
27
+ }
28
+ const version = body !== null &&
29
+ typeof body === "object" &&
30
+ typeof body.version === "string"
31
+ ? body.version
32
+ : "0.0.0";
33
+ return { version };
34
+ }
35
+ const target = base + "/skills/pane/SKILL.md";
36
+ const res = await fetchOrThrow(target);
37
+ const markdown = await res.text();
38
+ return { markdown };
39
+ }
40
+ async function fetchOrThrow(url) {
41
+ let res;
42
+ try {
43
+ res = await fetch(url, { headers: { "x-pane-cli-version": VERSION } });
44
+ }
45
+ catch (e) {
46
+ const msg = e instanceof Error ? e.message : String(e);
47
+ throw new Error(`could not reach ${url}: ${msg}`, { cause: e });
48
+ }
49
+ if (!res.ok) {
50
+ const body = await res.text().catch(() => "");
51
+ throw new Error(`relay returned ${res.status} for ${url}${body ? ": " + body.slice(0, 200) : ""}`);
52
+ }
53
+ return res;
54
+ }