@joe-broadhead/opencode-slack 0.0.3 → 0.0.4

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
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
4
 
5
+ ## [0.0.4] - 2026-11-04
6
+
7
+ ### Added
8
+
9
+ - Jelly shows the plugin's one-line summary and icon: `jelly.summary`, and `jelly.icon`, a speech bubble on a muted slate tile, shipped as `assets/icon.svg`.
10
+ - In Jelly, a web tab can sign in to Slack through Jelly's sign-in relay: the `slack` sign-in declares `relay`, and `docs/jelly.md` gives the relay address to add to your Slack app. The app manifest still lists only the loopback address.
11
+
12
+ ### Changed
13
+
14
+ - The tools RPC, without a Slack app client ID, refuses with `CLIENT_ID_MISSING` (or `CLIENT_ID_INVALID` for a client ID saved in `/connect` that is empty or has spaces) instead of `NOT_REGISTERED`, with `/slack-status`'s next step: run `/connect`, choose Slack app and paste the client ID. `describe` gives the same code as each method's `reason`. `NOT_REGISTERED` now means a client ID is set but OpenCode has not registered the server, and its next step is to run `/slack-status`.
15
+ - The README's sign-out step says what to do when `opencode mcp logout slack` answers "MCP server not found" right after OpenCode starts: wait a few seconds and run it again.
16
+
5
17
  ## [0.0.3] - 2026-11-04
6
18
 
7
19
  ### Added
package/README.md CHANGED
@@ -207,7 +207,7 @@ A script's errors from the tools RPC have their own codes, listed in [Scripting]
207
207
 
208
208
  Sign out while the plugin is still installed: once it is removed, OpenCode no longer knows the `slack` server or the **Slack app** entry, so it cannot remove their credentials, and `opencode auth list` stops showing them.
209
209
 
210
- 1. Sign out of Slack: `opencode mcp logout slack`.
210
+ 1. Sign out of Slack: `opencode mcp logout slack`. If it answers "MCP server not found" right after OpenCode starts, wait a few seconds and run it again.
211
211
  2. If you saved a client ID in `/connect`, remove it: `opencode auth logout slack-app`, and choose the **Slack app** account. Without a terminal, name it: `opencode auth logout slack-app "Slack app"`.
212
212
  3. Check: `opencode auth list` lists neither **Slack** nor **Slack app**.
213
213
  4. Remove the plugin: `opencode plugin remove @joe-broadhead/opencode-slack`, then `opencode reload`. `opencode mcp list` no longer lists `slack`.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48"><rect width="48" height="48" rx="12" fill="#475569"/><g fill="none" stroke="#FFFFFF" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 12h18a5 5 0 0 1 5 5v10a5 5 0 0 1-5 5H23l-7 6v-6h-1a5 5 0 0 1-5-5V17a5 5 0 0 1 5-5z"/><path d="M17 20h14"/><path d="M17 25.5h9"/></g></svg>
package/dist/index.js CHANGED
@@ -155,8 +155,8 @@ var NEXT_STEPS = {
155
155
  CONNECTING: "Wait a few seconds, then call the method again.",
156
156
  CONNECTION_FAILED: "Run /slack-status and follow its next step.",
157
157
  DISABLED: "Enable the Slack server in /mcps, then call the method again.",
158
- // The usual cause: no Slack app client ID, so the plugin registered no server (CLIENT_ID_MISSING in /slack-status).
159
- NOT_REGISTERED: "Run /connect and choose Slack app, then paste your Slack app's client ID; if one is set, run /slack-status and follow its next step.",
158
+ // Without a client ID, setup's notRegistered hook gives CLIENT_ID_MISSING or CLIENT_ID_INVALID and /connect instead.
159
+ NOT_REGISTERED: "Run /slack-status and follow its next step.",
160
160
  TOOL_UNAVAILABLE: "Call describe to see which methods OpenCode lists now, and run /slack-status.",
161
161
  UPSTREAM_ERROR: "Check the input against the method's schema from describe, and run /slack-status if it keeps failing.",
162
162
  CANCELLED: "Call the method again if you still need the result."
@@ -188,6 +188,7 @@ var DESCRIBED = object(
188
188
  description: STRING,
189
189
  available: { type: "boolean" },
190
190
  reason: STRING,
191
+ detail: STRING,
191
192
  input: ANY_OBJECT,
192
193
  output: ANY_OBJECT
193
194
  },
@@ -321,12 +322,15 @@ async function registerToolsRpc(ctx, options) {
321
322
  const rules = [...agent.data.permissions, ...info.permissions ?? []];
322
323
  return { agent: info.agent, rules };
323
324
  };
325
+ const notRegistered = (server) => mcp?.notRegistered?.(server);
324
326
  const unavailable = async (server, tool, connected) => {
325
327
  const status2 = (await ctx.mcp.list()).data.find((entry) => entry.name === server)?.status.status;
326
328
  const code = connection(status2) ?? connected;
327
329
  if (code === "TOOL_UNAVAILABLE") return fail(code, `OpenCode does not list ${tool} on the connected "${server}" server`);
328
330
  if (status2 === "connected") return fail(code, `the "${server}" server was reconnecting when ${tool} ran`);
329
- return fail(code, `the "${server}" server ${STATUS_TEXT[code] ?? "is unavailable"}`);
331
+ const what = `the "${server}" server ${STATUS_TEXT[code] ?? "is unavailable"}`;
332
+ const own = code === "NOT_REGISTERED" ? notRegistered(server) : void 0;
333
+ return own ? new ScriptError({ code: own.code, what, next: own.next }) : fail(code, what);
330
334
  };
331
335
  const denied = (action, error, asked = action) => {
332
336
  const reason = upstreamText(record(error) && typeof error["message"] === "string" ? error["message"] : String(error));
@@ -358,11 +362,15 @@ async function registerToolsRpc(ctx, options) {
358
362
  }
359
363
  };
360
364
  };
361
- const ownedTarget = (spec, envelope2) => ({
362
- action: spec.action,
363
- mcp: false,
364
- run: async (call) => await spec.run(envelope2.input, call) ?? null
365
- });
365
+ const ownedTarget = (spec, envelope2) => {
366
+ if (accounts) serverOf(envelope2.account);
367
+ const account = accounts && envelope2.account !== void 0 ? { account: envelope2.account } : {};
368
+ return {
369
+ action: spec.action,
370
+ mcp: false,
371
+ run: async (call) => await spec.run(envelope2.input, { ...call, ...account }) ?? null
372
+ };
373
+ };
366
374
  const invoke = async (target, raw, context) => {
367
375
  const envelope2 = raw;
368
376
  let method;
@@ -415,7 +423,9 @@ async function registerToolsRpc(ctx, options) {
415
423
  for (const name of KNOWN_READS) {
416
424
  const id = `${server}_${name}`;
417
425
  const tool = managed ? tools.find((entry) => entry.id === id) : void 0;
418
- const reason = !managed ? "SERVER_CONFLICT" : tool ? void 0 : connection(status2) ?? "TOOL_UNAVAILABLE";
426
+ const code = !managed ? "SERVER_CONFLICT" : tool ? void 0 : connection(status2) ?? "TOOL_UNAVAILABLE";
427
+ const reason = code === "NOT_REGISTERED" ? notRegistered(server)?.code ?? code : code;
428
+ const detail = code === void 0 ? void 0 : mcp.unavailableDetail?.(name, code);
419
429
  const input = jsonSchema(tool?.input) ?? ANY_OBJECT;
420
430
  const output = jsonSchema(tool?.output);
421
431
  methods.push({
@@ -426,6 +436,7 @@ async function registerToolsRpc(ctx, options) {
426
436
  description: tool?.description ?? "",
427
437
  available: reason === void 0,
428
438
  ...reason ? { reason } : {},
439
+ ...detail === void 0 ? {} : { detail },
429
440
  input,
430
441
  output: mcpOutput(output)
431
442
  });
@@ -830,12 +841,15 @@ function buildStatus(input) {
830
841
  ...base,
831
842
  state: "not_configured",
832
843
  ...input.client.invalid ? { code: "CLIENT_ID_INVALID", message: `The client ID saved in the /connect ${app} entry is empty or has spaces, so Slack is not added.` } : { code: "CLIENT_ID_MISSING", message: `No Slack app client ID is set, so Slack is not added. To create the app, use the manifest at ${APP_MANIFEST_URL}.` },
833
- nextStep: `Run /connect and choose ${app}, then paste your Slack app's client ID.`
844
+ nextStep: clientNextStep(server)
834
845
  };
835
846
  }
836
847
  const status2 = input.servers.find((item) => item.name === server)?.status;
837
848
  return { ...base, ...connection2(server, status2, unguardedAgents, names.length) };
838
849
  }
850
+ function clientNextStep(server) {
851
+ return `Run /connect and choose ${appDisplayName(server)}, then paste your Slack app's client ID.`;
852
+ }
839
853
  function callbackRow(callback, port) {
840
854
  const register = "register exactly this URL in the app's Redirect URLs";
841
855
  if (port === void 0) return `${callback} (${register})`;
@@ -899,7 +913,7 @@ function formatStatus(status2) {
899
913
  }
900
914
 
901
915
  // src/version.ts
902
- var VERSION = "0.0.3";
916
+ var VERSION = "0.0.4";
903
917
 
904
918
  // src/index.ts
905
919
  var STARTUP_POLL_MS = 250;
@@ -1007,7 +1021,13 @@ var src_default = Plugin.define({
1007
1021
  }
1008
1022
  });
1009
1023
  const stopTools = options === void 0 ? async () => {
1010
- } : await registerToolsRpc(ctx, { mcp: { server: options.server, managed: () => !nameInUse } });
1024
+ } : await registerToolsRpc(ctx, {
1025
+ mcp: {
1026
+ server: options.server,
1027
+ managed: () => !nameInUse,
1028
+ notRegistered: () => client.client !== void 0 ? void 0 : { code: client.invalid ? "CLIENT_ID_INVALID" : "CLIENT_ID_MISSING", next: clientNextStep(options.server) }
1029
+ }
1030
+ });
1011
1031
  await ctx.command.transform((editor) => {
1012
1032
  editor.add({
1013
1033
  name: "slack-status",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joe-broadhead/opencode-slack",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Unofficial OpenCode plugin for Slack",
5
5
  "keywords": [
6
6
  "opencode",
@@ -23,6 +23,7 @@
23
23
  },
24
24
  "files": [
25
25
  "dist",
26
+ "assets/icon.svg",
26
27
  "skills",
27
28
  "README.md",
28
29
  "LICENSE",
@@ -58,6 +59,8 @@
58
59
  },
59
60
  "jelly": {
60
61
  "title": "Slack",
62
+ "summary": "Search and read Slack; anything that sends asks first.",
63
+ "icon": "assets/icon.svg",
61
64
  "tools": [
62
65
  {
63
66
  "name": "slack.slack_search_public",
@@ -242,7 +245,8 @@
242
245
  "id": "slack",
243
246
  "label": "Slack",
244
247
  "server": "slack",
245
- "after": "app"
248
+ "after": "app",
249
+ "relay": {}
246
250
  }
247
251
  ],
248
252
  "contributes": {