@paneui/mcp 0.0.19 → 0.0.20
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 +44 -3
- package/dist/config.js +55 -0
- package/dist/index.js +5 -2
- package/dist/skill.js +54 -0
- package/dist/tools.js +1354 -54
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/server.json +2 -2
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
|
-
|
|
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
|
|
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
|
-
| `
|
|
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,
|
|
69
|
-
|
|
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
|
+
}
|