pi-roundtable-mcp 0.2.0 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.3.0] - 2026-10-01
9
+
10
+ ### Added
11
+
12
+ - `Connectors.resolve(serverName)` reads a virtual server by name, and `Connectors.admin` (`gateways()`, `servers()`) shows what ContextForge holds, for a host that manages some servers itself or shows their state.
13
+ - The `REMOTE_MCP` service of `remoteMcp`: the owner's bundles and grants, read only, and `describeGrant(client, grant)`, so a host can show them in its own status view.
14
+ - The types `GatewayState`, `ChannelBundle`, `ChannelGrant`, and `RemoteMcpService`.
15
+
8
16
  ## [0.2.0] - 2026-10-01
9
17
 
10
18
  ### Added
package/README.md CHANGED
@@ -90,6 +90,8 @@ It provides the `CONNECTORS` service (`Connectors`):
90
90
  | `servers()` | The virtual servers (name, URL, tool names) of the connectors that could be read |
91
91
  | `profileSources()` | One `{ name, description, serverName }` per connector, the shape a host builds a per-agent MCP profile from |
92
92
  | `token` | The bearer token that the virtual servers' URLs expect; one per process, valid for a year |
93
+ | `resolve(serverName)` | Reads one virtual server's URL and tools by name, for servers the host manages itself rather than as connectors |
94
+ | `admin` | `gateways()` and `servers()`: what ContextForge holds, upstream gateways with their state and every virtual server with its tool names, for a host's status view |
93
95
 
94
96
  The plugin does not decide which agent uses which connector.
95
97
  The host reads the service, builds its own profiles, and compares `version` with the one it built at.
@@ -177,6 +179,13 @@ The owner grants channels on Discord with `/<root> mcp`:
177
179
  | `describe` | Changes the channel name and purpose that the agent sees (Discord's own name and topic stay) |
178
180
  | `token` | Replaces a bundle's MCP URL; the old one stops working at once and the channel settings stay |
179
181
 
182
+ The plugin provides the `REMOTE_MCP` service (`RemoteMcpService`) for a host that shows the owner what outside agents may reach:
183
+
184
+ | Member | What it is |
185
+ | --- | --- |
186
+ | `grants` | `bundles()` and `grants(bundleId?)`, read only: every bundle, and the channel grants of one bundle or of all |
187
+ | `describeGrant(client, grant)` | One grant as lines of text for a Discord message: the name the agent sees, where the channel is, its purpose, and the allowed operations, in the plugin's wording |
188
+
180
189
  #### The default conversation
181
190
 
182
191
  Without `answer` and `claim`, a relayed turn runs on the core: `context.turns.run` of kind `remote`, for an owner-tier speaker, in the channel `mcp:<session>`, through the agent server's runtime.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-roundtable-mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP connectors through ContextForge and a remote MCP endpoint for pi-roundtable",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,6 +54,10 @@ export interface Connectors {
54
54
  profileSources(): ConnectorProfileSource[];
55
55
  /** The bearer token that ContextForge's virtual server URLs expect; one per process. */
56
56
  readonly token: string;
57
+ /** Reads a virtual server's endpoint and tools by name, for the servers a host manages itself. */
58
+ resolve(serverName: string): Promise<VirtualServer>;
59
+ /** What ContextForge holds, upstream gateways and virtual servers, for a host's status view. */
60
+ readonly admin: Pick<ContextForgeAdmin, "gateways" | "servers">;
57
61
  }
58
62
 
59
63
  export const CONNECTORS = serviceKey<Connectors>(
@@ -89,9 +93,11 @@ export function mcpConnectors(options: McpConnectorsOptions): RoundtablePlugin {
89
93
  setup: async ({ database, services, logger }) => {
90
94
  const { jwtSecret, user } = options.contextForge;
91
95
  const token = contextForgeToken(jwtSecret, user, TOKEN_TTL_SECONDS);
96
+ const admin = new ContextForgeAdmin(url, token);
97
+ const resolve = (name: string) => resolveVirtualServer(url, token, name);
92
98
  const registry = await ConnectorRegistry.attach(database(), {
93
- admin: new ContextForgeAdmin(url, token),
94
- resolve: (name) => resolveVirtualServer(url, token, name),
99
+ admin,
100
+ resolve,
95
101
  logger,
96
102
  serverPrefix: options.serverPrefix ?? DEFAULT_SERVER_PREFIX,
97
103
  maxToolName: options.maxToolName ?? DEFAULT_MAX_TOOL_NAME,
@@ -107,6 +113,11 @@ export function mcpConnectors(options: McpConnectorsOptions): RoundtablePlugin {
107
113
  servers: () => registry.servers(),
108
114
  profileSources: () => registry.profileSources(),
109
115
  token,
116
+ resolve,
117
+ admin: {
118
+ gateways: () => admin.gateways(),
119
+ servers: () => admin.servers(),
120
+ },
110
121
  });
111
122
  return {};
112
123
  },
package/src/index.ts CHANGED
@@ -11,8 +11,12 @@ export type {
11
11
  McpConnectorsOptions,
12
12
  } from "./connectors/connectors-plugin.ts";
13
13
  export { CONNECTORS, mcpConnectors } from "./connectors/connectors-plugin.ts";
14
- export type { UpstreamAuth } from "./connectors/contextforge.ts";
14
+ export type { GatewayState, UpstreamAuth } from "./connectors/contextforge.ts";
15
15
  export type { ConnectorMessages } from "./connectors/messages.ts";
16
+ export type {
17
+ ChannelBundle,
18
+ ChannelGrant,
19
+ } from "./remote-mcp/channel-grants.ts";
16
20
  export type {
17
21
  RemoteMcpMessages,
18
22
  RemoteToolNames,
@@ -24,3 +28,5 @@ export type {
24
28
  RemoteMcpOptions,
25
29
  } from "./remote-mcp/remote-mcp-plugin.ts";
26
30
  export { remoteMcp } from "./remote-mcp/remote-mcp-plugin.ts";
31
+ export type { RemoteMcpService } from "./remote-mcp/remote-mcp-service.ts";
32
+ export { REMOTE_MCP } from "./remote-mcp/remote-mcp-service.ts";
@@ -26,6 +26,7 @@ import {
26
26
  } from "./messages.ts";
27
27
  import { RemoteAgent } from "./remote-agent.ts";
28
28
  import { type RemoteClaimHooks, remoteClaim } from "./remote-claim.ts";
29
+ import { REMOTE_MCP, remoteMcpService } from "./remote-mcp-service.ts";
29
30
  import { RemoteSessionStore } from "./remote-session-store.ts";
30
31
  import { RemoteSessionSweeper } from "./session-sweeper.ts";
31
32
 
@@ -104,6 +105,7 @@ function checkOptions(options: RemoteMcpOptions): void {
104
105
  * An MCP server over HTTP for outside agents: `/mcp/personal` relays turns to the owner's agent
105
106
  * and returns the result when polled, and `/mcp/discord/<token>` offers the Discord channel
106
107
  * tools granted to one bundle. Grants are approved on Discord with `/<root> mcp`.
108
+ * Provides `REMOTE_MCP`.
107
109
  */
108
110
  export function remoteMcp(options: RemoteMcpOptions): RoundtablePlugin {
109
111
  checkOptions(options);
@@ -112,6 +114,7 @@ export function remoteMcp(options: RemoteMcpOptions): RoundtablePlugin {
112
114
  return definePlugin({
113
115
  name: "remote-mcp",
114
116
  requires: [DISCORD],
117
+ provides: [REMOTE_MCP],
115
118
  migrations: [ChannelGrantStore.migration, RemoteSessionStore.migration],
116
119
  setup: async (context) => {
117
120
  const { services, logger, conversations, database } = context;
@@ -142,6 +145,7 @@ export function remoteMcp(options: RemoteMcpOptions): RoundtablePlugin {
142
145
  discord.commands.add(
143
146
  mcpGrantCommands(discord.guard, grants, options.publicUrl, text),
144
147
  );
148
+ services.provide(REMOTE_MCP, remoteMcpService(grants, text));
145
149
  return {
146
150
  services: [
147
151
  {
@@ -0,0 +1,31 @@
1
+ import type { Client } from "discord.js";
2
+ import { serviceKey } from "pi-roundtable";
3
+ import type { ChannelGrant, ChannelGrantStore } from "./channel-grants.ts";
4
+ import { describeGrant } from "./mcp-grant-panels.ts";
5
+ import type { RemoteMcpMessages } from "./messages.ts";
6
+
7
+ /** What a host reads to show the owner which channels outside agents may use. */
8
+ export interface RemoteMcpService {
9
+ /** The owner's bundles and their channel grants, read only. */
10
+ grants: Pick<ChannelGrantStore, "bundles" | "grants">;
11
+ /** One grant as lines of text: the agent's name for the channel, where it is, its purpose, and the allowed operations. */
12
+ describeGrant(client: Client, grant: ChannelGrant): Promise<string>;
13
+ }
14
+
15
+ export const REMOTE_MCP = serviceKey<RemoteMcpService>(
16
+ "pi-roundtable-mcp.remote-mcp",
17
+ );
18
+
19
+ /** The service over the plugin's grant store and its wording. */
20
+ export function remoteMcpService(
21
+ grants: ChannelGrantStore,
22
+ text: RemoteMcpMessages,
23
+ ): RemoteMcpService {
24
+ return {
25
+ grants: {
26
+ bundles: () => grants.bundles(),
27
+ grants: (...args) => grants.grants(...args),
28
+ },
29
+ describeGrant: (client, grant) => describeGrant(client, grant, text),
30
+ };
31
+ }