claude-threads 1.13.1 → 1.14.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 +5 -0
- package/dist/index.js +27 -4
- package/dist/mcp/mcp-server.js +28 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.14.0] - 2026-05-05
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **`read_post` allows cross-channel reads on public Mattermost channels.** The cross-channel guard kept rejecting permalinks that pointed outside the bot's session channel — appropriate for private/DM/group channels (real privacy concern: a thread participant without access to the source channel sees content they shouldn't), but overzealous for public channels where anyone with an account can already navigate to the post. `McpPost` now carries a `channelType?: 'public' | 'private'` field, populated for Mattermost via `/channels/{id}` (type `O` = public; `P`/`D`/`G` = private) with a per-process cache so chatty threads don't trigger N redundant lookups. The resolver skips the wrong-channel check when `channelType === 'public'`; missing `channelType` is treated as private (fail-safe). Slack is unchanged: its MCP-side `readPost` is hard-scoped to the bot's configured channel via `conversations.history`, so cross-channel reads aren't possible there regardless of channel visibility. (#369)
|
|
14
|
+
|
|
10
15
|
## [1.13.1] - 2026-05-05
|
|
11
16
|
|
|
12
17
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -51783,6 +51783,14 @@ async function getPostRaw(config, postId) {
|
|
|
51783
51783
|
return null;
|
|
51784
51784
|
}
|
|
51785
51785
|
}
|
|
51786
|
+
async function getChannelRaw(config, channelId) {
|
|
51787
|
+
try {
|
|
51788
|
+
return await mattermostApi(config, "GET", `/channels/${channelId}`);
|
|
51789
|
+
} catch (err) {
|
|
51790
|
+
apiLog.debug(`Failed to get channel ${channelId}: ${err}`);
|
|
51791
|
+
return null;
|
|
51792
|
+
}
|
|
51793
|
+
}
|
|
51786
51794
|
async function getThreadRaw(config, threadRootId) {
|
|
51787
51795
|
try {
|
|
51788
51796
|
return await mattermostApi(config, "GET", `/posts/${threadRootId}/thread`);
|
|
@@ -51820,6 +51828,7 @@ class MattermostMcpPlatformApi {
|
|
|
51820
51828
|
config;
|
|
51821
51829
|
formatter = new MattermostFormatter;
|
|
51822
51830
|
botUserIdCache = null;
|
|
51831
|
+
channelTypeCache = new Map;
|
|
51823
51832
|
constructor(config) {
|
|
51824
51833
|
this.config = config;
|
|
51825
51834
|
this.apiConfig = {
|
|
@@ -51960,7 +51969,19 @@ class MattermostMcpPlatformApi {
|
|
|
51960
51969
|
if (!post)
|
|
51961
51970
|
return null;
|
|
51962
51971
|
const username = post.user_id ? await this.getUsername(post.user_id) : null;
|
|
51963
|
-
|
|
51972
|
+
const channelType = await this.getChannelType(post.channel_id);
|
|
51973
|
+
return toMcpPost(post, username, channelType);
|
|
51974
|
+
}
|
|
51975
|
+
async getChannelType(channelId) {
|
|
51976
|
+
const cached = this.channelTypeCache.get(channelId);
|
|
51977
|
+
if (cached)
|
|
51978
|
+
return cached;
|
|
51979
|
+
const channel = await getChannelRaw(this.apiConfig, channelId);
|
|
51980
|
+
if (!channel)
|
|
51981
|
+
return;
|
|
51982
|
+
const visibility = channel.type === "O" ? "public" : "private";
|
|
51983
|
+
this.channelTypeCache.set(channelId, visibility);
|
|
51984
|
+
return visibility;
|
|
51964
51985
|
}
|
|
51965
51986
|
async readThread(threadRootId, options) {
|
|
51966
51987
|
mcpLogger.debug(`readThread: ${formatShortId(threadRootId)}`);
|
|
@@ -51975,10 +51996,11 @@ class MattermostMcpPlatformApi {
|
|
|
51975
51996
|
usernameByUserId.set(p.user_id, await this.getUsername(p.user_id));
|
|
51976
51997
|
}
|
|
51977
51998
|
}
|
|
51978
|
-
|
|
51999
|
+
const channelType = limited[0] ? await this.getChannelType(limited[0].channel_id) : undefined;
|
|
52000
|
+
return limited.map((p) => toMcpPost(p, p.user_id ? usernameByUserId.get(p.user_id) ?? null : null, channelType));
|
|
51979
52001
|
}
|
|
51980
52002
|
}
|
|
51981
|
-
function toMcpPost(post, username) {
|
|
52003
|
+
function toMcpPost(post, username, channelType) {
|
|
51982
52004
|
return {
|
|
51983
52005
|
id: post.id,
|
|
51984
52006
|
channelId: post.channel_id,
|
|
@@ -51986,7 +52008,8 @@ function toMcpPost(post, username) {
|
|
|
51986
52008
|
username,
|
|
51987
52009
|
message: post.message,
|
|
51988
52010
|
createAt: post.create_at ?? 0,
|
|
51989
|
-
threadRootId: post.root_id || undefined
|
|
52011
|
+
threadRootId: post.root_id || undefined,
|
|
52012
|
+
channelType
|
|
51990
52013
|
};
|
|
51991
52014
|
}
|
|
51992
52015
|
|
package/dist/mcp/mcp-server.js
CHANGED
|
@@ -56086,6 +56086,14 @@ async function getPostRaw(config3, postId) {
|
|
|
56086
56086
|
return null;
|
|
56087
56087
|
}
|
|
56088
56088
|
}
|
|
56089
|
+
async function getChannelRaw(config3, channelId) {
|
|
56090
|
+
try {
|
|
56091
|
+
return await mattermostApi(config3, "GET", `/channels/${channelId}`);
|
|
56092
|
+
} catch (err) {
|
|
56093
|
+
apiLog.debug(`Failed to get channel ${channelId}: ${err}`);
|
|
56094
|
+
return null;
|
|
56095
|
+
}
|
|
56096
|
+
}
|
|
56089
56097
|
async function getThreadRaw(config3, threadRootId) {
|
|
56090
56098
|
try {
|
|
56091
56099
|
return await mattermostApi(config3, "GET", `/posts/${threadRootId}/thread`);
|
|
@@ -56123,6 +56131,7 @@ class MattermostMcpPlatformApi {
|
|
|
56123
56131
|
config;
|
|
56124
56132
|
formatter = new MattermostFormatter;
|
|
56125
56133
|
botUserIdCache = null;
|
|
56134
|
+
channelTypeCache = new Map;
|
|
56126
56135
|
constructor(config3) {
|
|
56127
56136
|
this.config = config3;
|
|
56128
56137
|
this.apiConfig = {
|
|
@@ -56263,7 +56272,19 @@ class MattermostMcpPlatformApi {
|
|
|
56263
56272
|
if (!post2)
|
|
56264
56273
|
return null;
|
|
56265
56274
|
const username = post2.user_id ? await this.getUsername(post2.user_id) : null;
|
|
56266
|
-
|
|
56275
|
+
const channelType = await this.getChannelType(post2.channel_id);
|
|
56276
|
+
return toMcpPost(post2, username, channelType);
|
|
56277
|
+
}
|
|
56278
|
+
async getChannelType(channelId) {
|
|
56279
|
+
const cached3 = this.channelTypeCache.get(channelId);
|
|
56280
|
+
if (cached3)
|
|
56281
|
+
return cached3;
|
|
56282
|
+
const channel = await getChannelRaw(this.apiConfig, channelId);
|
|
56283
|
+
if (!channel)
|
|
56284
|
+
return;
|
|
56285
|
+
const visibility = channel.type === "O" ? "public" : "private";
|
|
56286
|
+
this.channelTypeCache.set(channelId, visibility);
|
|
56287
|
+
return visibility;
|
|
56267
56288
|
}
|
|
56268
56289
|
async readThread(threadRootId, options) {
|
|
56269
56290
|
mcpLogger.debug(`readThread: ${formatShortId(threadRootId)}`);
|
|
@@ -56278,10 +56299,11 @@ class MattermostMcpPlatformApi {
|
|
|
56278
56299
|
usernameByUserId.set(p.user_id, await this.getUsername(p.user_id));
|
|
56279
56300
|
}
|
|
56280
56301
|
}
|
|
56281
|
-
|
|
56302
|
+
const channelType = limited[0] ? await this.getChannelType(limited[0].channel_id) : undefined;
|
|
56303
|
+
return limited.map((p) => toMcpPost(p, p.user_id ? usernameByUserId.get(p.user_id) ?? null : null, channelType));
|
|
56282
56304
|
}
|
|
56283
56305
|
}
|
|
56284
|
-
function toMcpPost(post2, username) {
|
|
56306
|
+
function toMcpPost(post2, username, channelType) {
|
|
56285
56307
|
return {
|
|
56286
56308
|
id: post2.id,
|
|
56287
56309
|
channelId: post2.channel_id,
|
|
@@ -56289,7 +56311,8 @@ function toMcpPost(post2, username) {
|
|
|
56289
56311
|
username,
|
|
56290
56312
|
message: post2.message,
|
|
56291
56313
|
createAt: post2.create_at ?? 0,
|
|
56292
|
-
threadRootId: post2.root_id || undefined
|
|
56314
|
+
threadRootId: post2.root_id || undefined,
|
|
56315
|
+
channelType
|
|
56293
56316
|
};
|
|
56294
56317
|
}
|
|
56295
56318
|
function createMattermostMcpPlatformApi(config3) {
|
|
@@ -56896,7 +56919,7 @@ async function resolvePermalink(api3, postId, botChannelId, opts = {}) {
|
|
|
56896
56919
|
if (!post2) {
|
|
56897
56920
|
return { ok: false, error: { kind: "not-found" } };
|
|
56898
56921
|
}
|
|
56899
|
-
if (botChannelId !== undefined && post2.channelId !== botChannelId) {
|
|
56922
|
+
if (botChannelId !== undefined && post2.channelId !== botChannelId && post2.channelType !== "public") {
|
|
56900
56923
|
return { ok: false, error: { kind: "wrong-channel" } };
|
|
56901
56924
|
}
|
|
56902
56925
|
if (!opts.includeThread) {
|