@paneui/mcp 0.0.23 → 0.0.25
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/dist/capabilities.d.ts +9 -0
- package/dist/capabilities.js +50 -0
- package/dist/config.d.ts +65 -0
- package/dist/guide.d.ts +12 -0
- package/dist/guide.js +49 -0
- package/dist/index.d.ts +2 -0
- package/dist/server.d.ts +18 -0
- package/dist/server.js +31 -2
- package/dist/skill.d.ts +24 -0
- package/dist/skill.js +28 -0
- package/dist/tools.d.ts +51 -0
- package/dist/tools.js +260 -10
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -1
- package/package.json +8 -2
- package/server.json +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export declare const GUIDE_RESOURCE_URI = "pane://guide";
|
|
3
|
+
export declare const GUIDE_PROMPT_NAME = "pane_guide";
|
|
4
|
+
/**
|
|
5
|
+
* Register the `pane_guide` prompt and the `pane://guide` resource on `server`.
|
|
6
|
+
* `getGuide()` returns the current MCP-flavoured guide markdown (called lazily
|
|
7
|
+
* on each read so a relay can serve an updated guide without re-registering).
|
|
8
|
+
*/
|
|
9
|
+
export declare function registerGuideCapabilities(server: McpServer, getGuide: () => string | Promise<string>): void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Register pane's MCP prompt + resource on an McpServer.
|
|
2
|
+
//
|
|
3
|
+
// Both the stdio server (packages/mcp/src/server.ts) and the relay's HTTP MCP
|
|
4
|
+
// server call this so an MCP-native client can discover the conceptual guide
|
|
5
|
+
// without a tool call:
|
|
6
|
+
//
|
|
7
|
+
// - prompt `pane_guide` — surfaces the guide as a prompt the client can
|
|
8
|
+
// insert into context ("teach me pane").
|
|
9
|
+
// - resource `pane://guide` — the same guide as a readable resource.
|
|
10
|
+
//
|
|
11
|
+
// The guide text is supplied by the host: the relay composes it in-process
|
|
12
|
+
// (MCP-INVOCATION.md + the core extracted from SKILL.md); the stdio server
|
|
13
|
+
// fetches it from the relay over HTTP and falls back to a short pointer when
|
|
14
|
+
// the relay is unreachable at registration time (registration must not block on
|
|
15
|
+
// the network — the get_skill tool is the always-fresh path).
|
|
16
|
+
export const GUIDE_RESOURCE_URI = "pane://guide";
|
|
17
|
+
export const GUIDE_PROMPT_NAME = "pane_guide";
|
|
18
|
+
/**
|
|
19
|
+
* Register the `pane_guide` prompt and the `pane://guide` resource on `server`.
|
|
20
|
+
* `getGuide()` returns the current MCP-flavoured guide markdown (called lazily
|
|
21
|
+
* on each read so a relay can serve an updated guide without re-registering).
|
|
22
|
+
*/
|
|
23
|
+
export function registerGuideCapabilities(server, getGuide) {
|
|
24
|
+
server.registerResource(GUIDE_PROMPT_NAME, GUIDE_RESOURCE_URI, {
|
|
25
|
+
title: "Pane usage guide",
|
|
26
|
+
description: "The pane conceptual guide for MCP clients: when to use pane, events vs records, schema design, the house style, and the round-trip mental model — with MCP tool-call invocation grammar.",
|
|
27
|
+
mimeType: "text/markdown",
|
|
28
|
+
}, async () => {
|
|
29
|
+
const text = await getGuide();
|
|
30
|
+
return {
|
|
31
|
+
contents: [
|
|
32
|
+
{ uri: GUIDE_RESOURCE_URI, mimeType: "text/markdown", text },
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
server.registerPrompt(GUIDE_PROMPT_NAME, {
|
|
37
|
+
title: "Pane usage guide",
|
|
38
|
+
description: "Insert the pane usage guide (MCP invocation + conceptual core) into the conversation so the model knows how to drive pane's tools.",
|
|
39
|
+
}, async () => {
|
|
40
|
+
const text = await getGuide();
|
|
41
|
+
return {
|
|
42
|
+
messages: [
|
|
43
|
+
{
|
|
44
|
+
role: "user",
|
|
45
|
+
content: { type: "text", text },
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PaneClient } from "@paneui/core";
|
|
2
|
+
/**
|
|
3
|
+
* The hosted Pane relay — the URL fallback when nothing else is set. A
|
|
4
|
+
* self-hoster overrides it with PANE_URL or a registered profile.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_RELAY_URL = "https://relay.paneui.com";
|
|
7
|
+
/**
|
|
8
|
+
* Profile name used when this server auto-registers a fresh agent. Matches the
|
|
9
|
+
* CLI's DEFAULT_PROFILE_NAME so the two share the same default identity.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_PROFILE_NAME = "default";
|
|
12
|
+
/** Absolute path to the shared CLI/MCP config file (honours XDG_CONFIG_HOME). */
|
|
13
|
+
export declare function storePath(): string;
|
|
14
|
+
/**
|
|
15
|
+
* Clear the active saved profile from the shared store (mirrors `pane agent
|
|
16
|
+
* logout` for the active-profile case). Removes the profile entry and unsets
|
|
17
|
+
* `current_profile` so the next resolve falls back to env / the default URL.
|
|
18
|
+
* Local-only: it does NOT revoke the key on the relay (use the `key` tool's
|
|
19
|
+
* `revoke` action for that). Idempotent — clearing an empty store is a no-op.
|
|
20
|
+
* Returns the profile name that was cleared (or null when nothing was active)
|
|
21
|
+
* and the store path.
|
|
22
|
+
*/
|
|
23
|
+
export declare function clearActiveProfile(): {
|
|
24
|
+
cleared: boolean;
|
|
25
|
+
profile: string | null;
|
|
26
|
+
path: string;
|
|
27
|
+
};
|
|
28
|
+
/** Resolve the relay URL using the same precedence as the CLI. */
|
|
29
|
+
export declare function resolveUrl(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Describe how the server is currently configured WITHOUT touching the network
|
|
32
|
+
* — the resolved relay URL, the active profile name, where the key is coming
|
|
33
|
+
* from, and whether a key is present at all. Backs the `agent` tool's `whoami`
|
|
34
|
+
* action so an MCP client can introspect its own identity / relay binding the
|
|
35
|
+
* way `pane config show` does for the CLI. No secrets are returned (the API key
|
|
36
|
+
* plaintext is never surfaced — only its source + whether it exists).
|
|
37
|
+
*/
|
|
38
|
+
export declare function describeActiveConfig(): {
|
|
39
|
+
url: string;
|
|
40
|
+
profile: string | null;
|
|
41
|
+
api_key_present: boolean;
|
|
42
|
+
api_key_source: "env" | "profile" | "none";
|
|
43
|
+
store_path: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a ready-to-use PaneClient.
|
|
47
|
+
*
|
|
48
|
+
* First-run setup: if no API key is resolvable from the environment or the
|
|
49
|
+
* shared store, the server auto-registers a fresh agent against the relay and
|
|
50
|
+
* persists the key under the `default` profile in the shared store — so the
|
|
51
|
+
* CLI and any later MCP launch reuse the same identity, and the human never
|
|
52
|
+
* has to run `pane agent register` by hand.
|
|
53
|
+
*
|
|
54
|
+
* A self-hoster on a `secret`-mode relay (or anyone who prefers explicit
|
|
55
|
+
* provisioning) sets PANE_API_KEY / PANE_TOKEN and the auto-register path is
|
|
56
|
+
* never taken.
|
|
57
|
+
*
|
|
58
|
+
* `opts.agentName` labels the auto-registered agent on the relay.
|
|
59
|
+
* `opts.registerSecret` is forwarded as the registration secret for
|
|
60
|
+
* REGISTRATION_MODE=secret relays.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveClient(opts?: {
|
|
63
|
+
agentName?: string;
|
|
64
|
+
registerSecret?: string;
|
|
65
|
+
}): Promise<PaneClient>;
|
package/dist/guide.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract every `<!-- pane:core:start -->…<!-- pane:core:end -->` block from a
|
|
3
|
+
* SKILL.md body, concatenated in document order (markers removed). Returns the
|
|
4
|
+
* transport-agnostic conceptual core with no CLI command grammar.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractCore(skillMarkdown: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Build the full MCP guide: the MCP invocation layer followed by the shared
|
|
9
|
+
* conceptual core extracted from SKILL.md. `mcpInvocation` is the contents of
|
|
10
|
+
* skills/pane/MCP-INVOCATION.md; `skillMarkdown` is the contents of SKILL.md.
|
|
11
|
+
*/
|
|
12
|
+
export declare function composeMcpGuide(mcpInvocation: string, skillMarkdown: string): string;
|
package/dist/guide.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Compose the MCP-flavoured pane guide from the shared conceptual core + the
|
|
2
|
+
// MCP invocation layer.
|
|
3
|
+
//
|
|
4
|
+
// Single source of truth: the conceptual core lives in skills/pane/SKILL.md
|
|
5
|
+
// between `<!-- pane:core:start -->` / `<!-- pane:core:end -->` markers (the
|
|
6
|
+
// CLI invocation grammar lives OUTSIDE those markers, so the CLI document and
|
|
7
|
+
// the MCP guide share the exact same prose for "when to use pane / events vs
|
|
8
|
+
// records / schema design / house style / the round-trip mental model"). The
|
|
9
|
+
// MCP invocation layer (tool-call grammar) lives in skills/pane/MCP-INVOCATION.md.
|
|
10
|
+
//
|
|
11
|
+
// The MCP guide = MCP-INVOCATION.md (with its trailing "the rest is the core"
|
|
12
|
+
// pointer) + every core block extracted from SKILL.md, in document order. No
|
|
13
|
+
// `pane ...` command grammar leaks into it.
|
|
14
|
+
//
|
|
15
|
+
// This is pure string manipulation so both the relay (which reads the files at
|
|
16
|
+
// boot and serves the result) and any other consumer can share one
|
|
17
|
+
// implementation without dragging in I/O.
|
|
18
|
+
const CORE_START = "<!-- pane:core:start -->";
|
|
19
|
+
const CORE_END = "<!-- pane:core:end -->";
|
|
20
|
+
/**
|
|
21
|
+
* Extract every `<!-- pane:core:start -->…<!-- pane:core:end -->` block from a
|
|
22
|
+
* SKILL.md body, concatenated in document order (markers removed). Returns the
|
|
23
|
+
* transport-agnostic conceptual core with no CLI command grammar.
|
|
24
|
+
*/
|
|
25
|
+
export function extractCore(skillMarkdown) {
|
|
26
|
+
const blocks = [];
|
|
27
|
+
let cursor = 0;
|
|
28
|
+
for (;;) {
|
|
29
|
+
const start = skillMarkdown.indexOf(CORE_START, cursor);
|
|
30
|
+
if (start === -1)
|
|
31
|
+
break;
|
|
32
|
+
const afterStart = start + CORE_START.length;
|
|
33
|
+
const end = skillMarkdown.indexOf(CORE_END, afterStart);
|
|
34
|
+
if (end === -1)
|
|
35
|
+
break;
|
|
36
|
+
blocks.push(skillMarkdown.slice(afterStart, end).trim());
|
|
37
|
+
cursor = end + CORE_END.length;
|
|
38
|
+
}
|
|
39
|
+
return blocks.join("\n\n");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build the full MCP guide: the MCP invocation layer followed by the shared
|
|
43
|
+
* conceptual core extracted from SKILL.md. `mcpInvocation` is the contents of
|
|
44
|
+
* skills/pane/MCP-INVOCATION.md; `skillMarkdown` is the contents of SKILL.md.
|
|
45
|
+
*/
|
|
46
|
+
export function composeMcpGuide(mcpInvocation, skillMarkdown) {
|
|
47
|
+
const core = extractCore(skillMarkdown);
|
|
48
|
+
return `${mcpInvocation.trim()}\n\n${core}\n`;
|
|
49
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { PaneClient } from "@paneui/core";
|
|
3
|
+
export interface BuildServerOptions {
|
|
4
|
+
/** Display name for the auto-registered agent (when no key is configured). */
|
|
5
|
+
agentName?: string;
|
|
6
|
+
/** Registration secret for REGISTRATION_MODE=secret relays. */
|
|
7
|
+
registerSecret?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Inject a pre-built client (tests). When set, the lazy resolver is skipped
|
|
10
|
+
* entirely and no network/store access happens.
|
|
11
|
+
*/
|
|
12
|
+
client?: PaneClient;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Construct (but do not connect) the Pane MCP server. Call `.connect(transport)`
|
|
16
|
+
* on the returned server to start serving.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildServer(opts?: BuildServerOptions): McpServer;
|
package/dist/server.js
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
// (an MCP host can enumerate the tools without the relay being reachable), and
|
|
8
8
|
// only the first actual tool call provisions a key if needed.
|
|
9
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
-
import { resolveClient } from "./config.js";
|
|
10
|
+
import { resolveClient, resolveUrl } from "./config.js";
|
|
11
11
|
import { TOOLS } from "./tools.js";
|
|
12
12
|
import { VERSION } from "./version.js";
|
|
13
|
+
import { fetchMcpGuide } from "./skill.js";
|
|
14
|
+
import { registerGuideCapabilities } from "./capabilities.js";
|
|
13
15
|
/**
|
|
14
16
|
* Construct (but do not connect) the Pane MCP server. Call `.connect(transport)`
|
|
15
17
|
* on the returned server to start serving.
|
|
@@ -37,10 +39,37 @@ export function buildServer(opts = {}) {
|
|
|
37
39
|
}
|
|
38
40
|
return clientPromise;
|
|
39
41
|
};
|
|
42
|
+
// MCP consumers get the MCP-flavoured guide (tool-call grammar), not the
|
|
43
|
+
// CLI-grammar SKILL.md. get_skill fetches /skills/pane/MCP.md from the
|
|
44
|
+
// configured relay; everything else keeps its CLI defaults (the stdio server
|
|
45
|
+
// reads identity from the shared CLI config store).
|
|
46
|
+
const toolEnv = {
|
|
47
|
+
getSkill: (versionOnly) => fetchMcpGuide(resolveUrl(), { version: versionOnly }),
|
|
48
|
+
};
|
|
49
|
+
// Conceptual guide as an MCP prompt + resource. Fetched from the relay lazily
|
|
50
|
+
// on read; a relay-unreachable read surfaces a short pointer to get_skill
|
|
51
|
+
// rather than failing registration.
|
|
52
|
+
registerGuideCapabilities(server, async () => {
|
|
53
|
+
try {
|
|
54
|
+
const { markdown } = await fetchMcpGuide(resolveUrl());
|
|
55
|
+
return markdown ?? "";
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
59
|
+
return ("# pane\n\nThe pane guide could not be fetched from the relay " +
|
|
60
|
+
`(${message}).\n\nCall the \`get_skill\` tool to retrieve it once the ` +
|
|
61
|
+
"relay is reachable.\n");
|
|
62
|
+
}
|
|
63
|
+
});
|
|
40
64
|
for (const tool of TOOLS) {
|
|
41
65
|
server.registerTool(tool.name, {
|
|
66
|
+
// `title` (top-level, display name) + `annotations` (the ToolAnnotations
|
|
67
|
+
// behavioural hints, which also carry a title) both flow into tools/list
|
|
68
|
+
// so MCP hosts / Anthropic's connector directory can classify the tool.
|
|
69
|
+
title: tool.annotations.title,
|
|
42
70
|
description: tool.description,
|
|
43
71
|
inputSchema: tool.inputSchema,
|
|
72
|
+
annotations: tool.annotations,
|
|
44
73
|
}, async (args) => {
|
|
45
74
|
let client;
|
|
46
75
|
try {
|
|
@@ -62,7 +91,7 @@ export function buildServer(opts = {}) {
|
|
|
62
91
|
isError: true,
|
|
63
92
|
};
|
|
64
93
|
}
|
|
65
|
-
return tool.handler(client, args);
|
|
94
|
+
return tool.handler(client, args, toolEnv);
|
|
66
95
|
});
|
|
67
96
|
}
|
|
68
97
|
return server;
|
package/dist/skill.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET the relay's full SKILL.md markdown. `version: true` instead fetches just
|
|
3
|
+
* the relay's reported skill version (the "is my local copy stale?" probe).
|
|
4
|
+
* Throws on a non-2xx or network failure with a message the tool layer can
|
|
5
|
+
* surface.
|
|
6
|
+
*/
|
|
7
|
+
export declare function fetchSkill(relayUrl: string, opts?: {
|
|
8
|
+
version?: boolean;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
markdown?: string;
|
|
11
|
+
version?: string;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* GET the relay's MCP-flavoured guide (the conceptual core + MCP tool-call
|
|
15
|
+
* invocation grammar) from GET /skills/pane/MCP.md, or just its version from
|
|
16
|
+
* GET /skills/pane/MCP.md/version. This is what an MCP consumer should read
|
|
17
|
+
* (not the CLI-grammar SKILL.md). Served unauthenticated, same as the skill.
|
|
18
|
+
*/
|
|
19
|
+
export declare function fetchMcpGuide(relayUrl: string, opts?: {
|
|
20
|
+
version?: boolean;
|
|
21
|
+
}): Promise<{
|
|
22
|
+
markdown?: string;
|
|
23
|
+
version?: string;
|
|
24
|
+
}>;
|
package/dist/skill.js
CHANGED
|
@@ -37,6 +37,34 @@ export async function fetchSkill(relayUrl, opts = {}) {
|
|
|
37
37
|
const markdown = await res.text();
|
|
38
38
|
return { markdown };
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* GET the relay's MCP-flavoured guide (the conceptual core + MCP tool-call
|
|
42
|
+
* invocation grammar) from GET /skills/pane/MCP.md, or just its version from
|
|
43
|
+
* GET /skills/pane/MCP.md/version. This is what an MCP consumer should read
|
|
44
|
+
* (not the CLI-grammar SKILL.md). Served unauthenticated, same as the skill.
|
|
45
|
+
*/
|
|
46
|
+
export async function fetchMcpGuide(relayUrl, opts = {}) {
|
|
47
|
+
const base = relayUrl.replace(/\/$/, "");
|
|
48
|
+
if (opts.version) {
|
|
49
|
+
const res = await fetchOrThrow(base + "/skills/pane/MCP.md/version");
|
|
50
|
+
let body;
|
|
51
|
+
try {
|
|
52
|
+
body = await res.json();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
body = null;
|
|
56
|
+
}
|
|
57
|
+
const version = body !== null &&
|
|
58
|
+
typeof body === "object" &&
|
|
59
|
+
typeof body.version === "string"
|
|
60
|
+
? body.version
|
|
61
|
+
: "0.0.0";
|
|
62
|
+
return { version };
|
|
63
|
+
}
|
|
64
|
+
const res = await fetchOrThrow(base + "/skills/pane/MCP.md");
|
|
65
|
+
const markdown = await res.text();
|
|
66
|
+
return { markdown };
|
|
67
|
+
}
|
|
40
68
|
async function fetchOrThrow(url) {
|
|
41
69
|
let res;
|
|
42
70
|
try {
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import type { PaneClient } from "@paneui/core";
|
|
4
|
+
/**
|
|
5
|
+
* A structured MCP tool result (text content + optional error flag). The
|
|
6
|
+
* index signature keeps it structurally assignable to the SDK's
|
|
7
|
+
* CallToolResult (which carries an open `[x: string]: unknown`).
|
|
8
|
+
*/
|
|
9
|
+
export interface ToolResult {
|
|
10
|
+
content: {
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
isError?: boolean;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Host-supplied capabilities for the handful of tools that aren't pure
|
|
19
|
+
* PaneClient wrappers. The stdio server leaves this undefined and the
|
|
20
|
+
* handlers fall back to the CLI config store + a network skill fetch; the
|
|
21
|
+
* relay's HTTP MCP server injects an `env` so those tools resolve against the
|
|
22
|
+
* relay itself (no CLI config on disk, no self-HTTP loop for the skill).
|
|
23
|
+
*
|
|
24
|
+
* This is the single seam that keeps the TOOLS array transport-agnostic and
|
|
25
|
+
* reusable by BOTH servers — every other tool is already a thin PaneClient
|
|
26
|
+
* call and needs nothing from the host.
|
|
27
|
+
*/
|
|
28
|
+
export interface ToolEnv {
|
|
29
|
+
/** `agent` action=whoami — describe the active identity (no secrets). */
|
|
30
|
+
describeConfig?: () => Record<string, unknown>;
|
|
31
|
+
/** `agent` action=logout — clear the locally-saved profile. */
|
|
32
|
+
clearProfile?: () => Record<string, unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* `get_skill` — return the MCP-flavoured skill markdown + its version. The
|
|
35
|
+
* relay passes its in-process renderer; the stdio server fetches it over
|
|
36
|
+
* HTTP from the relay's /skills route.
|
|
37
|
+
*/
|
|
38
|
+
getSkill?: (versionOnly: boolean) => Promise<{
|
|
39
|
+
markdown?: string;
|
|
40
|
+
version?: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/** One registered tool: name, human/LLM description, Zod input shape, handler. */
|
|
44
|
+
export interface ToolDef {
|
|
45
|
+
name: string;
|
|
46
|
+
description: string;
|
|
47
|
+
inputSchema: z.ZodRawShape;
|
|
48
|
+
annotations: ToolAnnotations;
|
|
49
|
+
handler: (client: PaneClient, args: Record<string, unknown>, env?: ToolEnv) => Promise<ToolResult>;
|
|
50
|
+
}
|
|
51
|
+
export declare const TOOLS: ToolDef[];
|
package/dist/tools.js
CHANGED
|
@@ -265,7 +265,32 @@ const upgradePaneShape = {
|
|
|
265
265
|
.int()
|
|
266
266
|
.positive()
|
|
267
267
|
.optional()
|
|
268
|
-
.describe("Target version of the SAME template. Defaults to the template head's latest version."),
|
|
268
|
+
.describe("Target version of the SAME template. Defaults to the template head's latest version. Mutually exclusive with `html`."),
|
|
269
|
+
html: z
|
|
270
|
+
.string()
|
|
271
|
+
.min(1)
|
|
272
|
+
.optional()
|
|
273
|
+
.describe("INLINE EDIT: the new HTML. The relay appends a fresh template version with this HTML and re-pins the pane to it in one call — editing an INLINE pane's HTML in place (same id/URL), no separate version step needed. Any schema you don't pass below is inherited from the pane's current version, so to change only the HTML pass only `html`. Inline panes only; a named/reusable template must go through the `template` tool (action: version) + `template_version`. Mutually exclusive with `template_version`."),
|
|
274
|
+
template_type: z
|
|
275
|
+
.string()
|
|
276
|
+
.optional()
|
|
277
|
+
.describe("Type for the `html` version. Default: html-inline."),
|
|
278
|
+
event_schema: z
|
|
279
|
+
.unknown()
|
|
280
|
+
.optional()
|
|
281
|
+
.describe("New event schema for the `html` version. Omit to inherit."),
|
|
282
|
+
input_schema: z
|
|
283
|
+
.record(z.string(), z.unknown())
|
|
284
|
+
.optional()
|
|
285
|
+
.describe("New input schema for the `html` version. Omit to inherit."),
|
|
286
|
+
record_schema: z
|
|
287
|
+
.unknown()
|
|
288
|
+
.optional()
|
|
289
|
+
.describe("New record schema for the `html` version. Omit to inherit."),
|
|
290
|
+
template_record_schema: z
|
|
291
|
+
.unknown()
|
|
292
|
+
.optional()
|
|
293
|
+
.describe("New template-level record schema for the `html` version. Omit to inherit."),
|
|
269
294
|
force: z
|
|
270
295
|
.boolean()
|
|
271
296
|
.optional()
|
|
@@ -683,8 +708,15 @@ const getSkillShape = {
|
|
|
683
708
|
export const TOOLS = [
|
|
684
709
|
{
|
|
685
710
|
name: "create_pane",
|
|
686
|
-
description: "Hand the human a rich interactive UI by URL and (optionally) get structured data back. Build the UI as inline HTML (pass `name` + `html`) OR reuse a saved template (pass `template_id`). The relay hosts it and returns a URL. ALWAYS give the returned url to the human — paste it into the conversation and ask them to open it. Reach for this whenever a text reply is the wrong shape: forms, approvals, pickers, surveys, dashboards, diff/doc review, wizards. If the page captures input it emits events back to you (poll them with get_events) or mutates record collections (the record tools). Returns { pane_id, url, urls, title, expires_at }.",
|
|
711
|
+
description: "Hand the human a rich interactive UI by URL and (optionally) get structured data back. Build the UI as inline HTML (pass `name` + `html`) OR reuse a saved template (pass `template_id`). The relay hosts it and returns a URL. ALWAYS give the returned url to the human — paste it into the conversation and ask them to open it. Reach for this whenever a text reply is the wrong shape: forms, approvals, pickers, surveys, dashboards, diff/doc review, wizards. If the page captures input it emits events back to you (poll them with get_events) or mutates record collections (the record tools). BEFORE authoring: call get_skill for the events-vs-records decision + schema grammar, and the `taste` tool (action: get) for the human's house style — both shape the HTML you write. Returns { pane_id, url, urls, title, expires_at }.",
|
|
687
712
|
inputSchema: createPaneShape,
|
|
713
|
+
annotations: {
|
|
714
|
+
title: "Create Pane",
|
|
715
|
+
readOnlyHint: false,
|
|
716
|
+
destructiveHint: true,
|
|
717
|
+
idempotentHint: false,
|
|
718
|
+
openWorldHint: true,
|
|
719
|
+
},
|
|
688
720
|
handler: async (client, args) => {
|
|
689
721
|
try {
|
|
690
722
|
const hasTemplateId = str(args, "template_id") !== undefined;
|
|
@@ -756,6 +788,11 @@ export const TOOLS = [
|
|
|
756
788
|
name: "get_pane_state",
|
|
757
789
|
description: "Fetch a pane's current metadata (status, title, template version, timestamps, expires_at) WITHOUT its event log. Use it to check whether a pane is still open or has expired. To read what the human did, use get_events.",
|
|
758
790
|
inputSchema: getPaneStateShape,
|
|
791
|
+
annotations: {
|
|
792
|
+
title: "Get Pane State",
|
|
793
|
+
readOnlyHint: true,
|
|
794
|
+
openWorldHint: false,
|
|
795
|
+
},
|
|
759
796
|
handler: async (client, args) => {
|
|
760
797
|
try {
|
|
761
798
|
return jsonResult(await client.getPane(String(args["pane_id"])));
|
|
@@ -769,6 +806,11 @@ export const TOOLS = [
|
|
|
769
806
|
name: "get_events",
|
|
770
807
|
description: "Poll a pane's append-only event log for what the human did (form submissions, approvals, picks). This is how you receive the round-trip result — there is no push/streaming in MCP. Poll loop: call with no `since` first; process the returned events; remember next_cursor; call again passing it as `since` to get only newer events. To WAIT for a human who hasn't acted yet, pass wait_seconds (~25) so the relay holds the request open until an event arrives or it times out, then call again with the same cursor. Returns { events, next_cursor }.",
|
|
771
808
|
inputSchema: getEventsShape,
|
|
809
|
+
annotations: {
|
|
810
|
+
title: "Get Events",
|
|
811
|
+
readOnlyHint: true,
|
|
812
|
+
openWorldHint: false,
|
|
813
|
+
},
|
|
772
814
|
handler: async (client, args) => {
|
|
773
815
|
try {
|
|
774
816
|
const page = await client.getEvents(String(args["pane_id"]), {
|
|
@@ -786,6 +828,13 @@ export const TOOLS = [
|
|
|
786
828
|
name: "send_to_pane",
|
|
787
829
|
description: "Push an event INTO an open pane — update the live UI the human is looking at (progress, a new message, a status change, fresh data). The event type must be declared in the pane's event_schema with 'agent' in its emittedBy. For mutable collections (todos, line items, comment threads) prefer the record tools instead. Returns { event, deduped }.",
|
|
788
830
|
inputSchema: sendToPaneShape,
|
|
831
|
+
annotations: {
|
|
832
|
+
title: "Send to Pane",
|
|
833
|
+
readOnlyHint: false,
|
|
834
|
+
destructiveHint: true,
|
|
835
|
+
idempotentHint: false,
|
|
836
|
+
openWorldHint: true,
|
|
837
|
+
},
|
|
789
838
|
handler: async (client, args) => {
|
|
790
839
|
try {
|
|
791
840
|
const res = await client.sendEvent(String(args["pane_id"]), {
|
|
@@ -804,6 +853,13 @@ export const TOOLS = [
|
|
|
804
853
|
name: "update_pane",
|
|
805
854
|
description: "Edit instance-level fields on a LIVE pane in place (PATCH) without minting a new one — the pane keeps its id, URL, event log, and template pin. Settable: ttl_seconds OR expires_at (mutually exclusive), title, preamble, input_data (replaced wholesale + revalidated), metadata, tags, icon_emoji / icon_attachment_id (or clear_* to drop the override). Pass at least one field. Returns the full new pane state + an updated_fields array. To swap the HTML/schemas, use upgrade_pane instead.",
|
|
806
855
|
inputSchema: updatePaneShape,
|
|
856
|
+
annotations: {
|
|
857
|
+
title: "Update Pane",
|
|
858
|
+
readOnlyHint: false,
|
|
859
|
+
destructiveHint: true,
|
|
860
|
+
idempotentHint: true,
|
|
861
|
+
openWorldHint: false,
|
|
862
|
+
},
|
|
807
863
|
handler: async (client, args) => {
|
|
808
864
|
try {
|
|
809
865
|
const body = {};
|
|
@@ -853,13 +909,36 @@ export const TOOLS = [
|
|
|
853
909
|
},
|
|
854
910
|
{
|
|
855
911
|
name: "upgrade_pane",
|
|
856
|
-
description: "Re-pin a LIVE pane to
|
|
912
|
+
description: "Re-pin a LIVE pane to swap its HTML (design) + event/input/record schemas in place — same URL, no new pane. Two ways: (1) pass `html` to EDIT AN INLINE PANE'S HTML in one call — the relay appends a fresh version with that HTML and re-pins (schemas you omit are inherited from the current version, so to change only the HTML pass only `html`); inline panes only. (2) pass `template_version` to re-pin to a version you already appended with the `template` tool (action: version) — for named/reusable templates. By default a strict schema-compat gate refuses an upgrade that would narrow the schema (returns schema_incompatible_upgrade + details.breaks); pass force:true to apply anyway. Returns { pane_id, template_version, upgraded, breaks, compat }.",
|
|
857
913
|
inputSchema: upgradePaneShape,
|
|
914
|
+
annotations: {
|
|
915
|
+
title: "Upgrade Pane",
|
|
916
|
+
readOnlyHint: false,
|
|
917
|
+
destructiveHint: true,
|
|
918
|
+
idempotentHint: false,
|
|
919
|
+
openWorldHint: false,
|
|
920
|
+
},
|
|
858
921
|
handler: async (client, args) => {
|
|
859
922
|
try {
|
|
860
923
|
const opts = {};
|
|
861
924
|
if (args["template_version"] !== undefined)
|
|
862
925
|
opts.template_version = args["template_version"];
|
|
926
|
+
if (args["html"] !== undefined) {
|
|
927
|
+
const tpl = {
|
|
928
|
+
source: String(args["html"]),
|
|
929
|
+
};
|
|
930
|
+
if (args["template_type"] !== undefined)
|
|
931
|
+
tpl.type = String(args["template_type"]);
|
|
932
|
+
if (args["event_schema"] !== undefined)
|
|
933
|
+
tpl.event_schema = args["event_schema"];
|
|
934
|
+
if (args["input_schema"] !== undefined)
|
|
935
|
+
tpl.input_schema = args["input_schema"];
|
|
936
|
+
if (args["record_schema"] !== undefined)
|
|
937
|
+
tpl.record_schema = args["record_schema"];
|
|
938
|
+
if (args["template_record_schema"] !== undefined)
|
|
939
|
+
tpl.template_record_schema = args["template_record_schema"];
|
|
940
|
+
opts.template = tpl;
|
|
941
|
+
}
|
|
863
942
|
if (args["force"])
|
|
864
943
|
opts.compat = "force";
|
|
865
944
|
return jsonResult(await client.upgradePane(String(args["pane_id"]), opts));
|
|
@@ -873,6 +952,11 @@ export const TOOLS = [
|
|
|
873
952
|
name: "list_panes",
|
|
874
953
|
description: "Enumerate YOUR agent's panes (newest first). Use it to find a pane_id you lost, audit what's open, or get a cursor for pagination. No secrets in the response (participant tokens are unrecoverable — mint a fresh URL with the participant tool). Filter by status (open|closed|all) or template_id. Returns { items, next_cursor }.",
|
|
875
954
|
inputSchema: listPanesShape,
|
|
955
|
+
annotations: {
|
|
956
|
+
title: "List Panes",
|
|
957
|
+
readOnlyHint: true,
|
|
958
|
+
openWorldHint: false,
|
|
959
|
+
},
|
|
876
960
|
handler: async (client, args) => {
|
|
877
961
|
try {
|
|
878
962
|
const opts = {};
|
|
@@ -895,6 +979,13 @@ export const TOOLS = [
|
|
|
895
979
|
name: "delete_pane",
|
|
896
980
|
description: "Close/delete a pane (idempotent — an already-closed pane still succeeds). The human's URL stops working. To merely edit a pane keep it alive with update_pane; to recover a soft-deleted pane use the trash tool (action: restore).",
|
|
897
981
|
inputSchema: deletePaneShape,
|
|
982
|
+
annotations: {
|
|
983
|
+
title: "Delete Pane",
|
|
984
|
+
readOnlyHint: false,
|
|
985
|
+
destructiveHint: true,
|
|
986
|
+
idempotentHint: true,
|
|
987
|
+
openWorldHint: false,
|
|
988
|
+
},
|
|
898
989
|
handler: async (client, args) => {
|
|
899
990
|
try {
|
|
900
991
|
await client.deletePane(String(args["pane_id"]));
|
|
@@ -910,6 +1001,11 @@ export const TOOLS = [
|
|
|
910
1001
|
name: "list_records",
|
|
911
1002
|
description: "List rows in a pane's mutable record collection (todo list, shopping list, kanban board, comment thread). Records are the right primitive when the page shows several mutable items and the CURRENT state matters more than the history. This also doubles as the POLL/watch for records (no streaming in MCP): pass the prior next_since to fetch only newer/changed rows. include_tombstones:true surfaces deletions. Returns { records, next_since, has_more }.",
|
|
912
1003
|
inputSchema: listRecordsShape,
|
|
1004
|
+
annotations: {
|
|
1005
|
+
title: "List Records",
|
|
1006
|
+
readOnlyHint: true,
|
|
1007
|
+
openWorldHint: false,
|
|
1008
|
+
},
|
|
913
1009
|
handler: async (client, args) => {
|
|
914
1010
|
try {
|
|
915
1011
|
const out = await client.listRecords(String(args["pane_id"]), String(args["collection"]), {
|
|
@@ -934,6 +1030,11 @@ export const TOOLS = [
|
|
|
934
1030
|
name: "get_record",
|
|
935
1031
|
description: "Fetch a single record row by its key from a pane collection (scans the collection — fine for a one-off lookup, not a hot loop). Returns { record } or an isError record_not_found.",
|
|
936
1032
|
inputSchema: getRecordShape,
|
|
1033
|
+
annotations: {
|
|
1034
|
+
title: "Get Record",
|
|
1035
|
+
readOnlyHint: true,
|
|
1036
|
+
openWorldHint: false,
|
|
1037
|
+
},
|
|
937
1038
|
handler: async (client, args) => {
|
|
938
1039
|
try {
|
|
939
1040
|
const row = await client.getRecord(String(args["pane_id"]), String(args["collection"]), String(args["record_key"]));
|
|
@@ -949,8 +1050,15 @@ export const TOOLS = [
|
|
|
949
1050
|
},
|
|
950
1051
|
{
|
|
951
1052
|
name: "upsert_record",
|
|
952
|
-
description: "Create a row in a pane's record collection, or return the existing row if record_key is already present (deduped:true). Use to add a todo, a line item, a comment, etc. The collection must be declared in the pane's record schema with 'agent' allowed to write. Returns { record, deduped }.",
|
|
1053
|
+
description: "Create a row in a pane's record collection, or return the existing row if record_key is already present (deduped:true). Use to add a todo, a line item, a comment, etc. The collection must be declared in the pane's record schema with 'agent' allowed to write. If you're still designing the pane, call get_skill first for the records-vs-events decision and the x-pane-collections schema grammar. Returns { record, deduped }.",
|
|
953
1054
|
inputSchema: upsertRecordShape,
|
|
1055
|
+
annotations: {
|
|
1056
|
+
title: "Upsert Record",
|
|
1057
|
+
readOnlyHint: false,
|
|
1058
|
+
destructiveHint: true,
|
|
1059
|
+
idempotentHint: true,
|
|
1060
|
+
openWorldHint: false,
|
|
1061
|
+
},
|
|
954
1062
|
handler: async (client, args) => {
|
|
955
1063
|
try {
|
|
956
1064
|
const body = {
|
|
@@ -969,6 +1077,13 @@ export const TOOLS = [
|
|
|
969
1077
|
name: "update_record",
|
|
970
1078
|
description: "Update an existing row in a pane's record collection (replaces its data). Pass if_match with the row's current version for an optimistic-locked update — on a version mismatch the relay returns the current row so you can retry. Returns { record }.",
|
|
971
1079
|
inputSchema: updateRecordShape,
|
|
1080
|
+
annotations: {
|
|
1081
|
+
title: "Update Record",
|
|
1082
|
+
readOnlyHint: false,
|
|
1083
|
+
destructiveHint: true,
|
|
1084
|
+
idempotentHint: true,
|
|
1085
|
+
openWorldHint: false,
|
|
1086
|
+
},
|
|
972
1087
|
handler: async (client, args) => {
|
|
973
1088
|
try {
|
|
974
1089
|
const body = {
|
|
@@ -987,6 +1102,13 @@ export const TOOLS = [
|
|
|
987
1102
|
name: "delete_record",
|
|
988
1103
|
description: "Soft-delete a row from a pane's record collection. The page sees the deletion live (the row becomes a tombstone in list_records). Pass if_match for an optimistic-locked delete. Returns { deleted: true }.",
|
|
989
1104
|
inputSchema: deleteRecordShape,
|
|
1105
|
+
annotations: {
|
|
1106
|
+
title: "Delete Record",
|
|
1107
|
+
readOnlyHint: false,
|
|
1108
|
+
destructiveHint: true,
|
|
1109
|
+
idempotentHint: true,
|
|
1110
|
+
openWorldHint: false,
|
|
1111
|
+
},
|
|
990
1112
|
handler: async (client, args) => {
|
|
991
1113
|
try {
|
|
992
1114
|
await client.deleteRecord(String(args["pane_id"]), String(args["collection"]), String(args["record_key"]), args["if_match"] !== undefined
|
|
@@ -1003,6 +1125,13 @@ export const TOOLS = [
|
|
|
1003
1125
|
name: "delete_record_collection",
|
|
1004
1126
|
description: "Drop a WHOLE per-pane record collection at once: every row plus the collection row itself. Use this to reset or remove a collection (todo list, comment thread, board) rather than deleting rows one by one with delete_record. Owner-only and destructive, so it requires confirm:true. Collection names are immutable, so to rename a collection drop the old one and write under the new name. Returns { deleted: true, collection }.",
|
|
1005
1127
|
inputSchema: deleteRecordCollectionShape,
|
|
1128
|
+
annotations: {
|
|
1129
|
+
title: "Delete Record Collection",
|
|
1130
|
+
readOnlyHint: false,
|
|
1131
|
+
destructiveHint: true,
|
|
1132
|
+
idempotentHint: true,
|
|
1133
|
+
openWorldHint: false,
|
|
1134
|
+
},
|
|
1006
1135
|
handler: async (client, args) => {
|
|
1007
1136
|
try {
|
|
1008
1137
|
if (args["confirm"] !== true) {
|
|
@@ -1021,6 +1150,17 @@ export const TOOLS = [
|
|
|
1021
1150
|
name: "template",
|
|
1022
1151
|
description: "Manage reusable, versioned UI templates (author once, instance many times via create_pane's template_id). ONE tool with an `action` enum: create | version | update | search | list | show | get_version | delete | publish | unpublish | search_public | set_icon. Required fields per action are documented on the `action` parameter. A template is HTML + an event schema (+ optional input/record/template-record schemas); a pane is one use of one version of it.",
|
|
1023
1152
|
inputSchema: templateShape,
|
|
1153
|
+
// Consolidated action-enum tool: read sub-actions (search/list/show/
|
|
1154
|
+
// get_version/search_public) coexist with mutating ones (create/version/
|
|
1155
|
+
// update/delete/publish/...). The hint reflects the most-privileged action
|
|
1156
|
+
// (delete is destructive), so readOnlyHint:false + destructiveHint:true.
|
|
1157
|
+
annotations: {
|
|
1158
|
+
title: "Manage Templates",
|
|
1159
|
+
readOnlyHint: false,
|
|
1160
|
+
destructiveHint: true,
|
|
1161
|
+
idempotentHint: false,
|
|
1162
|
+
openWorldHint: false,
|
|
1163
|
+
},
|
|
1024
1164
|
handler: async (client, args) => {
|
|
1025
1165
|
const action = String(args["action"]);
|
|
1026
1166
|
try {
|
|
@@ -1163,6 +1303,15 @@ export const TOOLS = [
|
|
|
1163
1303
|
name: "template_records",
|
|
1164
1304
|
description: "CRUD for TEMPLATE-level record collections — owner-curated content anchored to a template head and visible to every pane derived from any of its versions (vs per-pane records, which are the discrete record tools). ONE tool with an `action` enum: list | get | upsert | update | delete | delete_collection. The template version must declare the collection via template_record_schema (set it with the `template` tool first).",
|
|
1165
1305
|
inputSchema: templateRecordsShape,
|
|
1306
|
+
// Consolidated tool: read actions (list/get) + mutating ones (upsert/
|
|
1307
|
+
// update/delete/delete_collection). Hint reflects the destructive action.
|
|
1308
|
+
annotations: {
|
|
1309
|
+
title: "Manage Template Records",
|
|
1310
|
+
readOnlyHint: false,
|
|
1311
|
+
destructiveHint: true,
|
|
1312
|
+
idempotentHint: false,
|
|
1313
|
+
openWorldHint: false,
|
|
1314
|
+
},
|
|
1166
1315
|
handler: async (client, args) => {
|
|
1167
1316
|
const action = String(args["action"]);
|
|
1168
1317
|
const templateId = String(args["template_id"]);
|
|
@@ -1241,6 +1390,15 @@ export const TOOLS = [
|
|
|
1241
1390
|
name: "participant",
|
|
1242
1391
|
description: "Manage a pane's participant URLs (recovery + leak-containment). ONE tool with an `action` enum: list | new | revoke. Use `new` when you lost the original URL (the plaintext token is returned ONCE — save it). Token URLs are stored hashed and cannot be recovered.",
|
|
1243
1392
|
inputSchema: participantShape,
|
|
1393
|
+
// Consolidated tool: read action (list) + mutating ones (new mints a URL,
|
|
1394
|
+
// revoke invalidates one). Hint reflects the destructive action.
|
|
1395
|
+
annotations: {
|
|
1396
|
+
title: "Manage Participants",
|
|
1397
|
+
readOnlyHint: false,
|
|
1398
|
+
destructiveHint: true,
|
|
1399
|
+
idempotentHint: false,
|
|
1400
|
+
openWorldHint: false,
|
|
1401
|
+
},
|
|
1244
1402
|
handler: async (client, args) => {
|
|
1245
1403
|
const action = String(args["action"]);
|
|
1246
1404
|
const paneId = String(args["pane_id"]);
|
|
@@ -1272,6 +1430,16 @@ export const TOOLS = [
|
|
|
1272
1430
|
name: "share",
|
|
1273
1431
|
description: "Identity sharing on a pane (layered on top of participant tokens). ONE tool with an `action` enum: list (access_mode + grants) | invite (a human by email, role participant|viewer) | set_access (the /p access mode: invite_only|link|public) | revoke (one grant by id). Token (/s/<token>) links are independent of access_mode and keep working.",
|
|
1274
1432
|
inputSchema: shareShape,
|
|
1433
|
+
// Consolidated tool: read action (list) + mutating/side-effecting ones
|
|
1434
|
+
// (invite emails a human, set_access, revoke). openWorld:true because
|
|
1435
|
+
// invite delivers a message to an external recipient.
|
|
1436
|
+
annotations: {
|
|
1437
|
+
title: "Manage Pane Sharing",
|
|
1438
|
+
readOnlyHint: false,
|
|
1439
|
+
destructiveHint: true,
|
|
1440
|
+
idempotentHint: false,
|
|
1441
|
+
openWorldHint: true,
|
|
1442
|
+
},
|
|
1275
1443
|
handler: async (client, args) => {
|
|
1276
1444
|
const action = String(args["action"]);
|
|
1277
1445
|
const paneId = String(args["pane_id"]);
|
|
@@ -1315,6 +1483,17 @@ export const TOOLS = [
|
|
|
1315
1483
|
name: "attachments",
|
|
1316
1484
|
description: "Binary attachments (images, PDFs, audio, video) referenced from event payloads / input_data via `format: pane-attachment-id`. ONE tool with an `action` enum: upload | download | show | list | delete | mint_token | revoke_token | list_tokens. upload reads an ABSOLUTE file_path; download writes to an ABSOLUTE out_path (or returns base64). Scope an upload to agent (default, reusable), pane, or template. mint_token returns a /b/<token> capability URL (ONCE) a browser can GET without your API key.",
|
|
1317
1485
|
inputSchema: attachmentsShape,
|
|
1486
|
+
// Consolidated tool: read actions (download/show/list/list_tokens) +
|
|
1487
|
+
// mutating ones (upload/delete/mint_token/revoke_token). openWorld:true
|
|
1488
|
+
// because upload pushes bytes into external relay storage + mint_token
|
|
1489
|
+
// produces a publicly-fetchable capability URL.
|
|
1490
|
+
annotations: {
|
|
1491
|
+
title: "Manage Attachments",
|
|
1492
|
+
readOnlyHint: false,
|
|
1493
|
+
destructiveHint: true,
|
|
1494
|
+
idempotentHint: false,
|
|
1495
|
+
openWorldHint: true,
|
|
1496
|
+
},
|
|
1318
1497
|
handler: async (client, args) => {
|
|
1319
1498
|
const action = String(args["action"]);
|
|
1320
1499
|
try {
|
|
@@ -1414,6 +1593,15 @@ export const TOOLS = [
|
|
|
1414
1593
|
name: "taste",
|
|
1415
1594
|
description: "Read / write / clear the agent's freeform UI taste notes (a small markdown document of presentation preferences learned from human feedback — 'denser layout', 'no rounded corners'). ONE tool with an `action` enum: get | set | clear. Call `get` BEFORE generating a pane so prior feedback shapes the output; `set` does a whole-document replace (not append). Keep entries about UI/presentation only.",
|
|
1416
1595
|
inputSchema: tasteShape,
|
|
1596
|
+
// Consolidated tool: read action (get) + mutating ones (set replaces the
|
|
1597
|
+
// doc, clear deletes it). Hint reflects the destructive action.
|
|
1598
|
+
annotations: {
|
|
1599
|
+
title: "Manage UI Taste Notes",
|
|
1600
|
+
readOnlyHint: false,
|
|
1601
|
+
destructiveHint: true,
|
|
1602
|
+
idempotentHint: false,
|
|
1603
|
+
openWorldHint: false,
|
|
1604
|
+
},
|
|
1417
1605
|
handler: async (client, args) => {
|
|
1418
1606
|
const action = String(args["action"]);
|
|
1419
1607
|
try {
|
|
@@ -1442,6 +1630,16 @@ export const TOOLS = [
|
|
|
1442
1630
|
name: "key",
|
|
1443
1631
|
description: "Inspect or revoke the calling agent's API key. ONE tool with an `action` enum: list (key info — agent_id, key_prefix, timestamps) | revoke (self-destruct the agent's OWN key; it stops working immediately and is irreversible — pass confirm:true). The relay scopes keys to the caller, so both act only on your own key.",
|
|
1444
1632
|
inputSchema: keyShape,
|
|
1633
|
+
// Consolidated tool: read action (list) + a mutating one (revoke
|
|
1634
|
+
// self-destructs the agent's own key). Hint reflects the destructive
|
|
1635
|
+
// action.
|
|
1636
|
+
annotations: {
|
|
1637
|
+
title: "Manage API Key",
|
|
1638
|
+
readOnlyHint: false,
|
|
1639
|
+
destructiveHint: true,
|
|
1640
|
+
idempotentHint: false,
|
|
1641
|
+
openWorldHint: false,
|
|
1642
|
+
},
|
|
1445
1643
|
handler: async (client, args) => {
|
|
1446
1644
|
const action = String(args["action"]);
|
|
1447
1645
|
try {
|
|
@@ -1469,6 +1667,16 @@ export const TOOLS = [
|
|
|
1469
1667
|
name: "trash",
|
|
1470
1668
|
description: "Manage soft-deleted panes + templates. ONE tool with an `action` enum: list | restore (pane id) | restore_template (template id|slug) | purge (pane id) | purge_template (template id|slug). purge bypasses the retention window and is permanent. Soft-deleted rows live in trash until the sweeper reclaims them.",
|
|
1471
1669
|
inputSchema: trashShape,
|
|
1670
|
+
// Consolidated tool: read action (list) + mutating ones (restore/purge/
|
|
1671
|
+
// restore_template/purge_template; purge is permanent). Hint reflects the
|
|
1672
|
+
// destructive action.
|
|
1673
|
+
annotations: {
|
|
1674
|
+
title: "Manage Trash",
|
|
1675
|
+
readOnlyHint: false,
|
|
1676
|
+
destructiveHint: true,
|
|
1677
|
+
idempotentHint: false,
|
|
1678
|
+
openWorldHint: false,
|
|
1679
|
+
},
|
|
1472
1680
|
handler: async (client, args) => {
|
|
1473
1681
|
const action = String(args["action"]);
|
|
1474
1682
|
try {
|
|
@@ -1508,6 +1716,15 @@ export const TOOLS = [
|
|
|
1508
1716
|
name: "feedback",
|
|
1509
1717
|
description: "Send or list feedback to the relay operator. ONE tool with an `action` enum: create (a bug|feature|note with a message, optional pane_id) | list (the agent's own submissions, newest first, paginated by before).",
|
|
1510
1718
|
inputSchema: feedbackShape,
|
|
1719
|
+
// Consolidated tool: read action (list) + a side-effecting one (create
|
|
1720
|
+
// submits feedback to the relay operator). Hint reflects the write action.
|
|
1721
|
+
annotations: {
|
|
1722
|
+
title: "Manage Feedback",
|
|
1723
|
+
readOnlyHint: false,
|
|
1724
|
+
destructiveHint: true,
|
|
1725
|
+
idempotentHint: false,
|
|
1726
|
+
openWorldHint: false,
|
|
1727
|
+
},
|
|
1511
1728
|
handler: async (client, args) => {
|
|
1512
1729
|
const action = String(args["action"]);
|
|
1513
1730
|
try {
|
|
@@ -1545,19 +1762,31 @@ export const TOOLS = [
|
|
|
1545
1762
|
name: "agent",
|
|
1546
1763
|
description: "Agent identity + binding. ONE tool with an `action` enum: whoami (the resolved relay URL, active profile, whether a key is configured — no network, no secrets) | claim (bind this agent to a human via a one-shot claim code from their Settings UI; one-way) | logout (clear the locally-saved key/profile; does NOT revoke it on the relay — use the `key` tool's revoke for that).",
|
|
1547
1764
|
inputSchema: agentShape,
|
|
1548
|
-
|
|
1765
|
+
// Consolidated tool: read action (whoami) + mutating ones (claim binds
|
|
1766
|
+
// this agent to a human, logout clears the local profile). Hint reflects
|
|
1767
|
+
// the state-changing action.
|
|
1768
|
+
annotations: {
|
|
1769
|
+
title: "Manage Agent Identity",
|
|
1770
|
+
readOnlyHint: false,
|
|
1771
|
+
destructiveHint: true,
|
|
1772
|
+
idempotentHint: false,
|
|
1773
|
+
openWorldHint: false,
|
|
1774
|
+
},
|
|
1775
|
+
handler: async (client, args, env) => {
|
|
1549
1776
|
const action = String(args["action"]);
|
|
1550
1777
|
try {
|
|
1551
1778
|
switch (action) {
|
|
1552
1779
|
case "whoami":
|
|
1553
|
-
// No network — pure local config introspection.
|
|
1554
|
-
|
|
1780
|
+
// No network — pure local config introspection. The relay's HTTP
|
|
1781
|
+
// server injects describeConfig (active token's agent identity);
|
|
1782
|
+
// the stdio server reads the CLI config store.
|
|
1783
|
+
return jsonResult((env?.describeConfig ?? describeActiveConfig)());
|
|
1555
1784
|
case "claim":
|
|
1556
1785
|
if (str(args, "code") === undefined)
|
|
1557
1786
|
return invalidArgs("claim requires `code`");
|
|
1558
1787
|
return jsonResult(await client.claimAgent(String(args["code"])));
|
|
1559
1788
|
case "logout":
|
|
1560
|
-
return jsonResult(clearActiveProfile());
|
|
1789
|
+
return jsonResult((env?.clearProfile ?? clearActiveProfile)());
|
|
1561
1790
|
default:
|
|
1562
1791
|
return invalidArgs(`unknown agent action '${action}'`);
|
|
1563
1792
|
}
|
|
@@ -1571,6 +1800,11 @@ export const TOOLS = [
|
|
|
1571
1800
|
name: "run_query",
|
|
1572
1801
|
description: "Run read-only SQL over YOUR scoped data (panes, records, events) — the relay scopes every row to panes you own. Use it to summarise activity, find panes/records by content, or build a report. Tables + columns and JSON projection operators are documented on the `sql` parameter. Default output is { columns, rows, truncated, scope, elapsed_ms } (format:json); csv/tsv/table render the rows as text. Capped at 10,000 rows; 10s timeout.",
|
|
1573
1802
|
inputSchema: runQueryShape,
|
|
1803
|
+
annotations: {
|
|
1804
|
+
title: "Run SQL Query",
|
|
1805
|
+
readOnlyHint: true,
|
|
1806
|
+
openWorldHint: false,
|
|
1807
|
+
},
|
|
1574
1808
|
handler: async (client, args) => {
|
|
1575
1809
|
try {
|
|
1576
1810
|
const result = await client.query(String(args["sql"]), str(args, "pane_id") !== undefined
|
|
@@ -1592,10 +1826,26 @@ export const TOOLS = [
|
|
|
1592
1826
|
name: "get_skill",
|
|
1593
1827
|
description: "Fetch the relay's auto-updating SKILL.md (the full Pane usage guide) — UNAUTHENTICATED, needs no API key. Call this to self-teach the Pane workflow (events vs records, schema grammars, the poll loop) before driving the other tools. Pass version_only:true to get just the relay's skill version string (to check if a cached copy is stale).",
|
|
1594
1828
|
inputSchema: getSkillShape,
|
|
1595
|
-
|
|
1829
|
+
annotations: {
|
|
1830
|
+
title: "Get Skill Guide",
|
|
1831
|
+
readOnlyHint: true,
|
|
1832
|
+
openWorldHint: false,
|
|
1833
|
+
},
|
|
1834
|
+
handler: async (_client, args, env) => {
|
|
1596
1835
|
try {
|
|
1836
|
+
const versionOnly = args["version_only"] === true;
|
|
1837
|
+
// The relay's HTTP server injects getSkill so MCP consumers receive
|
|
1838
|
+
// the MCP-invocation rendering of the skill (tool-call grammar, not
|
|
1839
|
+
// `pane ...` commands) straight from the relay image. The stdio server
|
|
1840
|
+
// falls back to fetching SKILL.md over HTTP from its configured relay.
|
|
1841
|
+
if (env?.getSkill) {
|
|
1842
|
+
const { markdown, version } = await env.getSkill(versionOnly);
|
|
1843
|
+
if (versionOnly)
|
|
1844
|
+
return jsonResult({ version });
|
|
1845
|
+
return textResult(markdown ?? "");
|
|
1846
|
+
}
|
|
1597
1847
|
const url = resolveUrl();
|
|
1598
|
-
if (
|
|
1848
|
+
if (versionOnly) {
|
|
1599
1849
|
const { version } = await fetchSkill(url, { version: true });
|
|
1600
1850
|
return jsonResult({ version });
|
|
1601
1851
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "0.0.25";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paneui/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.25",
|
|
4
4
|
"description": "Model Context Protocol (stdio) server for Pane: lets any MCP client (Claude Desktop, Cursor, …) hand a human a rich interactive UI by URL and get structured data back.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,6 +30,12 @@
|
|
|
30
30
|
"bin": {
|
|
31
31
|
"pane-mcp": "dist/index.js"
|
|
32
32
|
},
|
|
33
|
+
"exports": {
|
|
34
|
+
"./tools": "./dist/tools.js",
|
|
35
|
+
"./guide": "./dist/guide.js",
|
|
36
|
+
"./capabilities": "./dist/capabilities.js",
|
|
37
|
+
"./server": "./dist/server.js"
|
|
38
|
+
},
|
|
33
39
|
"files": [
|
|
34
40
|
"dist",
|
|
35
41
|
"server.json",
|
|
@@ -44,7 +50,7 @@
|
|
|
44
50
|
},
|
|
45
51
|
"dependencies": {
|
|
46
52
|
"@modelcontextprotocol/sdk": "^1.20.0",
|
|
47
|
-
"@paneui/core": "^0.0.
|
|
53
|
+
"@paneui/core": "^0.0.25",
|
|
48
54
|
"zod": "^4.4.3"
|
|
49
55
|
},
|
|
50
56
|
"devDependencies": {
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.aerolalit/pane",
|
|
4
4
|
"title": "Pane",
|
|
5
5
|
"description": "Hand a human a rich interactive UI by URL and get structured data back — forms, approvals, pickers, dashboards, diff review — from any MCP client.",
|
|
6
|
-
"version": "0.0.
|
|
6
|
+
"version": "0.0.25",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/aerolalit/paneui",
|
|
9
9
|
"source": "github"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@paneui/mcp",
|
|
16
|
-
"version": "0.0.
|
|
16
|
+
"version": "0.0.25",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
},
|