roboport 0.3.0 → 0.4.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.
@@ -0,0 +1,12 @@
1
+ import { Mcp as McpBase } from '../core';
2
+ type Options = {
3
+ token: string;
4
+ name?: string;
5
+ toolsets?: string[];
6
+ readOnly?: boolean;
7
+ deferred?: boolean;
8
+ };
9
+ declare class Mcp extends McpBase {
10
+ constructor(opts: Options);
11
+ }
12
+ export default Mcp;
@@ -0,0 +1,23 @@
1
+ import { Tool, type McpClient } from '../../core';
2
+ type Options = {
3
+ /**
4
+ * Slack bot token (`xoxb-…`). Requires these OAuth scopes:
5
+ * `chat:write` (post_message, reply_in_thread),
6
+ * `channels:read` (list_channels),
7
+ * `channels:history` (get_channel_history),
8
+ * `users:read` (list_users, get_user),
9
+ * `reactions:write` (add_reaction).
10
+ */
11
+ botToken: string;
12
+ name?: string;
13
+ deferred?: boolean;
14
+ };
15
+ declare class Mcp implements McpClient {
16
+ private auth;
17
+ private nameSpace;
18
+ private deferred;
19
+ constructor(opts: Options);
20
+ connect(): Promise<Tool[]>;
21
+ disconnect(): Promise<void>;
22
+ }
23
+ export default Mcp;
package/mcp/core.d.ts CHANGED
@@ -13,6 +13,7 @@ type HttpTransportConfig = {
13
13
  auth?: AuthProvider;
14
14
  };
15
15
  type McpTransportConfig = StdioTransportConfig | HttpTransportConfig;
16
+ declare function validateMcpName(name: string): void;
16
17
  declare class Mcp implements McpClient {
17
18
  name: string;
18
19
  transport: McpTransportConfig;
@@ -27,4 +28,4 @@ declare class Mcp implements McpClient {
27
28
  disconnect(): Promise<void>;
28
29
  private wrap;
29
30
  }
30
- export { Mcp, type McpTransportConfig, type StdioTransportConfig, type HttpTransportConfig, };
31
+ export { Mcp, validateMcpName, type McpTransportConfig, type StdioTransportConfig, type HttpTransportConfig, };
package/mcp/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { BearerAuth, OAuthAuth, type AuthProvider, type OAuthAuthOptions } from './auth';
2
+ import Github from './clients/github';
2
3
  import Grafana from './clients/grafana';
3
4
  import Linear from './clients/linear';
5
+ import Slack from './clients/slack';
4
6
  import Tenderly from './clients/tenderly';
5
7
  import { Mcp, type HttpTransportConfig, type McpTransportConfig, type StdioTransportConfig } from './core';
6
8
  import { FileStorage, MemoryStorage, type OAuthStorage, type TokenSet } from './storage';
7
- export { BearerAuth, FileStorage, Grafana, Linear, Mcp, MemoryStorage, OAuthAuth, Tenderly, type AuthProvider, type HttpTransportConfig, type McpTransportConfig, type OAuthAuthOptions, type OAuthStorage, type StdioTransportConfig, type TokenSet, };
9
+ export { BearerAuth, FileStorage, Github, Grafana, Linear, Mcp, MemoryStorage, OAuthAuth, Slack, Tenderly, type AuthProvider, type HttpTransportConfig, type McpTransportConfig, type OAuthAuthOptions, type OAuthStorage, type StdioTransportConfig, type TokenSet, };
package/mcp/index.js CHANGED
@@ -884,179 +884,6 @@ class Skill {
884
884
  }
885
885
  }
886
886
 
887
- // src/mcp/clients/grafana.ts
888
- var EMPTY_OBJECT = {
889
- type: "object",
890
- properties: {},
891
- additionalProperties: false
892
- };
893
- var TOOLS = [
894
- {
895
- name: "list_datasources",
896
- description: "List all configured Grafana datasources.",
897
- inputSchema: EMPTY_OBJECT,
898
- call: (_, ctx) => request(ctx, "GET", "/api/datasources")
899
- },
900
- {
901
- name: "get_datasource",
902
- description: "Fetch a single datasource by UID.",
903
- inputSchema: {
904
- type: "object",
905
- properties: {
906
- uid: { type: "string", description: "Datasource UID." }
907
- },
908
- required: ["uid"],
909
- additionalProperties: false
910
- },
911
- call: (args, ctx) => request(ctx, "GET", `/api/datasources/uid/${encodeURIComponent(String(args.uid))}`)
912
- },
913
- {
914
- name: "query",
915
- description: "Run one or more queries against Grafana datasources via /api/ds/query. Pass the query array as Grafana expects (each item needs refId and datasource.uid).",
916
- inputSchema: {
917
- type: "object",
918
- properties: {
919
- queries: {
920
- type: "array",
921
- description: "Array of query objects (refId, datasource, expr/range/etc.).",
922
- items: { type: "object" }
923
- },
924
- from: {
925
- type: "string",
926
- description: 'Range start, e.g. "now-1h" or epoch ms as string.'
927
- },
928
- to: {
929
- type: "string",
930
- description: 'Range end, e.g. "now" or epoch ms as string.'
931
- }
932
- },
933
- required: ["queries"],
934
- additionalProperties: false
935
- },
936
- call: (args, ctx) => request(ctx, "POST", "/api/ds/query", {
937
- queries: args.queries,
938
- from: args.from ?? "now-1h",
939
- to: args.to ?? "now"
940
- })
941
- },
942
- {
943
- name: "search_dashboards",
944
- description: "Search dashboards by name, tag, or folder.",
945
- inputSchema: {
946
- type: "object",
947
- properties: {
948
- query: {
949
- type: "string",
950
- description: "Substring match on dashboard title."
951
- },
952
- tag: {
953
- type: "array",
954
- items: { type: "string" },
955
- description: "Filter by tags."
956
- },
957
- folderUIDs: {
958
- type: "array",
959
- items: { type: "string" },
960
- description: "Limit to specific folder UIDs."
961
- },
962
- limit: { type: "number", description: "Max results (default 100)." }
963
- },
964
- additionalProperties: false
965
- },
966
- call: (args, ctx) => {
967
- const params = new URLSearchParams;
968
- params.set("type", "dash-db");
969
- if (typeof args.query === "string")
970
- params.set("query", args.query);
971
- if (typeof args.limit === "number")
972
- params.set("limit", String(args.limit));
973
- for (const tag of args.tag ?? [])
974
- params.append("tag", tag);
975
- for (const uid of args.folderUIDs ?? [])
976
- params.append("folderUIDs", uid);
977
- return request(ctx, "GET", `/api/search?${params.toString()}`);
978
- }
979
- },
980
- {
981
- name: "get_dashboard",
982
- description: "Fetch a dashboard JSON by UID.",
983
- inputSchema: {
984
- type: "object",
985
- properties: {
986
- uid: { type: "string", description: "Dashboard UID." }
987
- },
988
- required: ["uid"],
989
- additionalProperties: false
990
- },
991
- call: (args, ctx) => request(ctx, "GET", `/api/dashboards/uid/${encodeURIComponent(String(args.uid))}`)
992
- },
993
- {
994
- name: "list_folders",
995
- description: "List dashboard folders.",
996
- inputSchema: EMPTY_OBJECT,
997
- call: (_, ctx) => request(ctx, "GET", "/api/folders")
998
- },
999
- {
1000
- name: "list_alert_rules",
1001
- description: "List provisioned alert rules.",
1002
- inputSchema: EMPTY_OBJECT,
1003
- call: (_, ctx) => request(ctx, "GET", "/api/v1/provisioning/alert-rules")
1004
- }
1005
- ];
1006
- async function request(ctx, method, path, body) {
1007
- const headers = {
1008
- accept: "application/json",
1009
- authorization: await ctx.auth.getHeader()
1010
- };
1011
- if (body !== undefined)
1012
- headers["content-type"] = "application/json";
1013
- const res = await fetch(`${ctx.baseUrl}${path}`, {
1014
- method,
1015
- headers,
1016
- body: body !== undefined ? JSON.stringify(body) : undefined
1017
- });
1018
- const text = await res.text();
1019
- if (!res.ok) {
1020
- throw new Error(`Grafana ${method} ${path} failed: ${res.status} ${text}`);
1021
- }
1022
- if (!text)
1023
- return null;
1024
- try {
1025
- return JSON.parse(text);
1026
- } catch {
1027
- return text;
1028
- }
1029
- }
1030
-
1031
- class Mcp {
1032
- baseUrl;
1033
- auth;
1034
- nameSpace;
1035
- deferred;
1036
- constructor(opts) {
1037
- this.baseUrl = opts.url.replace(/\/$/, "");
1038
- this.auth = new BearerAuth(opts.serviceAccountToken);
1039
- this.nameSpace = opts.name ?? "grafana";
1040
- this.deferred = opts.deferred ?? true;
1041
- }
1042
- async connect() {
1043
- const ctx = { baseUrl: this.baseUrl, auth: this.auth };
1044
- return TOOLS.map((def) => new Tool({
1045
- name: `mcp__${this.nameSpace}__${def.name}`,
1046
- description: def.description,
1047
- jsonSchema: def.inputSchema,
1048
- deferred: this.deferred,
1049
- execute: async (input) => {
1050
- const args = input ?? {};
1051
- const result = await def.call(args, ctx);
1052
- return typeof result === "string" ? result : JSON.stringify(result);
1053
- }
1054
- }));
1055
- }
1056
- async disconnect() {}
1057
- }
1058
- var grafana_default = Mcp;
1059
-
1060
887
  // src/mcp/core.ts
1061
888
  var PROTOCOL_VERSION = "2024-11-05";
1062
889
 
@@ -1256,6 +1083,11 @@ class HttpTransport {
1256
1083
  await this.send(req, undefined);
1257
1084
  }
1258
1085
  }
1086
+ function validateMcpName(name) {
1087
+ if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
1088
+ throw new Error(`Invalid MCP name "${name}": use only letters, digits, underscores, and hyphens.`);
1089
+ }
1090
+ }
1259
1091
  function formatContent(content) {
1260
1092
  if (content.length === 1 && content[0]?.type === "text") {
1261
1093
  return content[0].text ?? "";
@@ -1264,7 +1096,7 @@ function formatContent(content) {
1264
1096
  `);
1265
1097
  }
1266
1098
 
1267
- class Mcp2 {
1099
+ class Mcp {
1268
1100
  name;
1269
1101
  transport;
1270
1102
  deferred;
@@ -1274,9 +1106,7 @@ class Mcp2 {
1274
1106
  transport,
1275
1107
  deferred
1276
1108
  }) {
1277
- if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
1278
- throw new Error(`Invalid MCP name "${name}": use only letters, digits, underscores, and hyphens.`);
1279
- }
1109
+ validateMcpName(name);
1280
1110
  this.name = name;
1281
1111
  this.transport = transport;
1282
1112
  this.deferred = deferred ?? true;
@@ -1324,10 +1154,210 @@ class Mcp2 {
1324
1154
  }
1325
1155
  }
1326
1156
 
1157
+ // src/mcp/clients/github.ts
1158
+ var GITHUB_MCP_URL = "https://api.githubcopilot.com/mcp/";
1159
+
1160
+ class Mcp2 extends Mcp {
1161
+ constructor(opts) {
1162
+ const headers = {};
1163
+ if (opts.toolsets?.length) {
1164
+ headers["X-MCP-Toolsets"] = opts.toolsets.join(",");
1165
+ }
1166
+ if (opts.readOnly) {
1167
+ headers["X-MCP-Readonly"] = "true";
1168
+ }
1169
+ super({
1170
+ name: opts.name ?? "github",
1171
+ deferred: opts.deferred,
1172
+ transport: {
1173
+ type: "http",
1174
+ url: GITHUB_MCP_URL,
1175
+ headers,
1176
+ auth: new BearerAuth(opts.token)
1177
+ }
1178
+ });
1179
+ }
1180
+ }
1181
+ var github_default = Mcp2;
1182
+
1183
+ // src/mcp/clients/grafana.ts
1184
+ var EMPTY_OBJECT = {
1185
+ type: "object",
1186
+ properties: {},
1187
+ additionalProperties: false
1188
+ };
1189
+ var TOOLS = [
1190
+ {
1191
+ name: "list_datasources",
1192
+ description: "List all configured Grafana datasources.",
1193
+ inputSchema: EMPTY_OBJECT,
1194
+ call: (_, ctx) => request(ctx, "GET", "/api/datasources")
1195
+ },
1196
+ {
1197
+ name: "get_datasource",
1198
+ description: "Fetch a single datasource by UID.",
1199
+ inputSchema: {
1200
+ type: "object",
1201
+ properties: {
1202
+ uid: { type: "string", description: "Datasource UID." }
1203
+ },
1204
+ required: ["uid"],
1205
+ additionalProperties: false
1206
+ },
1207
+ call: (args, ctx) => request(ctx, "GET", `/api/datasources/uid/${encodeURIComponent(String(args.uid))}`)
1208
+ },
1209
+ {
1210
+ name: "query",
1211
+ description: "Run one or more queries against Grafana datasources via /api/ds/query. Pass the query array as Grafana expects (each item needs refId and datasource.uid).",
1212
+ inputSchema: {
1213
+ type: "object",
1214
+ properties: {
1215
+ queries: {
1216
+ type: "array",
1217
+ description: "Array of query objects (refId, datasource, expr/range/etc.).",
1218
+ items: { type: "object" }
1219
+ },
1220
+ from: {
1221
+ type: "string",
1222
+ description: 'Range start, e.g. "now-1h" or epoch ms as string.'
1223
+ },
1224
+ to: {
1225
+ type: "string",
1226
+ description: 'Range end, e.g. "now" or epoch ms as string.'
1227
+ }
1228
+ },
1229
+ required: ["queries"],
1230
+ additionalProperties: false
1231
+ },
1232
+ call: (args, ctx) => request(ctx, "POST", "/api/ds/query", {
1233
+ queries: args.queries,
1234
+ from: args.from ?? "now-1h",
1235
+ to: args.to ?? "now"
1236
+ })
1237
+ },
1238
+ {
1239
+ name: "search_dashboards",
1240
+ description: "Search dashboards by name, tag, or folder.",
1241
+ inputSchema: {
1242
+ type: "object",
1243
+ properties: {
1244
+ query: {
1245
+ type: "string",
1246
+ description: "Substring match on dashboard title."
1247
+ },
1248
+ tag: {
1249
+ type: "array",
1250
+ items: { type: "string" },
1251
+ description: "Filter by tags."
1252
+ },
1253
+ folderUIDs: {
1254
+ type: "array",
1255
+ items: { type: "string" },
1256
+ description: "Limit to specific folder UIDs."
1257
+ },
1258
+ limit: { type: "number", description: "Max results (default 100)." }
1259
+ },
1260
+ additionalProperties: false
1261
+ },
1262
+ call: (args, ctx) => {
1263
+ const params = new URLSearchParams;
1264
+ params.set("type", "dash-db");
1265
+ if (typeof args.query === "string")
1266
+ params.set("query", args.query);
1267
+ if (typeof args.limit === "number")
1268
+ params.set("limit", String(args.limit));
1269
+ for (const tag of args.tag ?? [])
1270
+ params.append("tag", tag);
1271
+ for (const uid of args.folderUIDs ?? [])
1272
+ params.append("folderUIDs", uid);
1273
+ return request(ctx, "GET", `/api/search?${params.toString()}`);
1274
+ }
1275
+ },
1276
+ {
1277
+ name: "get_dashboard",
1278
+ description: "Fetch a dashboard JSON by UID.",
1279
+ inputSchema: {
1280
+ type: "object",
1281
+ properties: {
1282
+ uid: { type: "string", description: "Dashboard UID." }
1283
+ },
1284
+ required: ["uid"],
1285
+ additionalProperties: false
1286
+ },
1287
+ call: (args, ctx) => request(ctx, "GET", `/api/dashboards/uid/${encodeURIComponent(String(args.uid))}`)
1288
+ },
1289
+ {
1290
+ name: "list_folders",
1291
+ description: "List dashboard folders.",
1292
+ inputSchema: EMPTY_OBJECT,
1293
+ call: (_, ctx) => request(ctx, "GET", "/api/folders")
1294
+ },
1295
+ {
1296
+ name: "list_alert_rules",
1297
+ description: "List provisioned alert rules.",
1298
+ inputSchema: EMPTY_OBJECT,
1299
+ call: (_, ctx) => request(ctx, "GET", "/api/v1/provisioning/alert-rules")
1300
+ }
1301
+ ];
1302
+ async function request(ctx, method, path, body) {
1303
+ const headers = {
1304
+ accept: "application/json",
1305
+ authorization: await ctx.auth.getHeader()
1306
+ };
1307
+ if (body !== undefined)
1308
+ headers["content-type"] = "application/json";
1309
+ const res = await fetch(`${ctx.baseUrl}${path}`, {
1310
+ method,
1311
+ headers,
1312
+ body: body !== undefined ? JSON.stringify(body) : undefined
1313
+ });
1314
+ const text = await res.text();
1315
+ if (!res.ok) {
1316
+ throw new Error(`Grafana ${method} ${path} failed: ${res.status} ${text}`);
1317
+ }
1318
+ if (!text)
1319
+ return null;
1320
+ try {
1321
+ return JSON.parse(text);
1322
+ } catch {
1323
+ return text;
1324
+ }
1325
+ }
1326
+
1327
+ class Mcp3 {
1328
+ baseUrl;
1329
+ auth;
1330
+ nameSpace;
1331
+ deferred;
1332
+ constructor(opts) {
1333
+ this.baseUrl = opts.url.replace(/\/$/, "");
1334
+ this.auth = new BearerAuth(opts.serviceAccountToken);
1335
+ this.nameSpace = opts.name ?? "grafana";
1336
+ validateMcpName(this.nameSpace);
1337
+ this.deferred = opts.deferred ?? true;
1338
+ }
1339
+ async connect() {
1340
+ const ctx = { baseUrl: this.baseUrl, auth: this.auth };
1341
+ return TOOLS.map((def) => new Tool({
1342
+ name: `mcp__${this.nameSpace}__${def.name}`,
1343
+ description: def.description,
1344
+ jsonSchema: def.inputSchema,
1345
+ deferred: this.deferred,
1346
+ execute: async (input) => {
1347
+ const args = input ?? {};
1348
+ const result = await def.call(args, ctx);
1349
+ return typeof result === "string" ? result : JSON.stringify(result);
1350
+ }
1351
+ }));
1352
+ }
1353
+ async disconnect() {}
1354
+ }
1355
+ var grafana_default = Mcp3;
1356
+
1327
1357
  // src/mcp/clients/linear.ts
1328
1358
  var LINEAR_MCP_URL = "https://mcp.linear.app/mcp";
1329
1359
 
1330
- class Mcp3 extends Mcp2 {
1360
+ class Mcp4 extends Mcp {
1331
1361
  constructor(opts) {
1332
1362
  super({
1333
1363
  name: opts.name ?? "linear",
@@ -1339,12 +1369,217 @@ class Mcp3 extends Mcp2 {
1339
1369
  });
1340
1370
  }
1341
1371
  }
1342
- var linear_default = Mcp3;
1372
+ var linear_default = Mcp4;
1373
+
1374
+ // src/mcp/clients/slack.ts
1375
+ var SLACK_API_URL = "https://slack.com/api";
1376
+ var TOOLS2 = [
1377
+ {
1378
+ name: "post_message",
1379
+ description: "Post a message to a Slack channel.",
1380
+ inputSchema: {
1381
+ type: "object",
1382
+ properties: {
1383
+ channel: {
1384
+ type: "string",
1385
+ description: "Channel ID (e.g. C012AB3CD) or name (e.g. #general)."
1386
+ },
1387
+ text: { type: "string", description: "Message text." }
1388
+ },
1389
+ required: ["channel", "text"],
1390
+ additionalProperties: false
1391
+ },
1392
+ call: (args, ctx) => request2(ctx, "chat.postMessage", {
1393
+ channel: args.channel,
1394
+ text: args.text
1395
+ })
1396
+ },
1397
+ {
1398
+ name: "reply_in_thread",
1399
+ description: "Reply to an existing message thread in a Slack channel.",
1400
+ inputSchema: {
1401
+ type: "object",
1402
+ properties: {
1403
+ channel: { type: "string", description: "Channel ID or name." },
1404
+ thread_ts: {
1405
+ type: "string",
1406
+ description: "Timestamp (ts) of the parent message to reply to."
1407
+ },
1408
+ text: { type: "string", description: "Reply text." }
1409
+ },
1410
+ required: ["channel", "thread_ts", "text"],
1411
+ additionalProperties: false
1412
+ },
1413
+ call: (args, ctx) => request2(ctx, "chat.postMessage", {
1414
+ channel: args.channel,
1415
+ thread_ts: args.thread_ts,
1416
+ text: args.text
1417
+ })
1418
+ },
1419
+ {
1420
+ name: "list_channels",
1421
+ description: "List channels in the workspace.",
1422
+ inputSchema: {
1423
+ type: "object",
1424
+ properties: {
1425
+ types: {
1426
+ type: "string",
1427
+ description: 'Comma-separated channel types (default "public_channel").'
1428
+ },
1429
+ limit: { type: "number", description: "Max results (default 100)." },
1430
+ cursor: { type: "string", description: "Pagination cursor." },
1431
+ exclude_archived: {
1432
+ type: "boolean",
1433
+ description: "Omit archived channels (default true)."
1434
+ }
1435
+ },
1436
+ additionalProperties: false
1437
+ },
1438
+ call: (args, ctx) => request2(ctx, "conversations.list", {
1439
+ types: args.types ?? "public_channel",
1440
+ limit: args.limit ?? 100,
1441
+ cursor: args.cursor,
1442
+ exclude_archived: args.exclude_archived ?? true
1443
+ })
1444
+ },
1445
+ {
1446
+ name: "get_channel_history",
1447
+ description: "Fetch recent messages from a channel.",
1448
+ inputSchema: {
1449
+ type: "object",
1450
+ properties: {
1451
+ channel: { type: "string", description: "Channel ID." },
1452
+ limit: { type: "number", description: "Max messages (default 100)." },
1453
+ cursor: { type: "string", description: "Pagination cursor." },
1454
+ oldest: {
1455
+ type: "string",
1456
+ description: "Only messages after this ts."
1457
+ },
1458
+ latest: {
1459
+ type: "string",
1460
+ description: "Only messages before this ts."
1461
+ }
1462
+ },
1463
+ required: ["channel"],
1464
+ additionalProperties: false
1465
+ },
1466
+ call: (args, ctx) => request2(ctx, "conversations.history", {
1467
+ channel: args.channel,
1468
+ limit: args.limit ?? 100,
1469
+ cursor: args.cursor,
1470
+ oldest: args.oldest,
1471
+ latest: args.latest
1472
+ })
1473
+ },
1474
+ {
1475
+ name: "list_users",
1476
+ description: "List users in the workspace.",
1477
+ inputSchema: {
1478
+ type: "object",
1479
+ properties: {
1480
+ limit: { type: "number", description: "Max results (default 100)." },
1481
+ cursor: { type: "string", description: "Pagination cursor." }
1482
+ },
1483
+ additionalProperties: false
1484
+ },
1485
+ call: (args, ctx) => request2(ctx, "users.list", {
1486
+ limit: args.limit ?? 100,
1487
+ cursor: args.cursor
1488
+ })
1489
+ },
1490
+ {
1491
+ name: "get_user",
1492
+ description: "Fetch a single user by ID.",
1493
+ inputSchema: {
1494
+ type: "object",
1495
+ properties: {
1496
+ user: { type: "string", description: "User ID (e.g. U012AB3CD)." }
1497
+ },
1498
+ required: ["user"],
1499
+ additionalProperties: false
1500
+ },
1501
+ call: (args, ctx) => request2(ctx, "users.info", { user: args.user })
1502
+ },
1503
+ {
1504
+ name: "add_reaction",
1505
+ description: "Add an emoji reaction to a message.",
1506
+ inputSchema: {
1507
+ type: "object",
1508
+ properties: {
1509
+ channel: { type: "string", description: "Channel ID." },
1510
+ timestamp: {
1511
+ type: "string",
1512
+ description: "Timestamp (ts) of the target message."
1513
+ },
1514
+ name: {
1515
+ type: "string",
1516
+ description: 'Emoji name without colons (e.g. "thumbsup").'
1517
+ }
1518
+ },
1519
+ required: ["channel", "timestamp", "name"],
1520
+ additionalProperties: false
1521
+ },
1522
+ call: (args, ctx) => request2(ctx, "reactions.add", {
1523
+ channel: args.channel,
1524
+ timestamp: args.timestamp,
1525
+ name: args.name
1526
+ })
1527
+ }
1528
+ ];
1529
+ async function request2(ctx, method, params) {
1530
+ const body = new URLSearchParams;
1531
+ for (const [key, value] of Object.entries(params)) {
1532
+ if (value === undefined || value === null)
1533
+ continue;
1534
+ body.set(key, String(value));
1535
+ }
1536
+ const res = await fetch(`${SLACK_API_URL}/${method}`, {
1537
+ method: "POST",
1538
+ headers: {
1539
+ authorization: await ctx.auth.getHeader(),
1540
+ "content-type": "application/x-www-form-urlencoded"
1541
+ },
1542
+ body
1543
+ });
1544
+ const json = await res.json();
1545
+ if (!json.ok) {
1546
+ throw new Error(`Slack ${method} failed: ${json.error ?? res.status}`);
1547
+ }
1548
+ return json;
1549
+ }
1550
+
1551
+ class Mcp5 {
1552
+ auth;
1553
+ nameSpace;
1554
+ deferred;
1555
+ constructor(opts) {
1556
+ this.auth = new BearerAuth(opts.botToken);
1557
+ this.nameSpace = opts.name ?? "slack";
1558
+ validateMcpName(this.nameSpace);
1559
+ this.deferred = opts.deferred ?? true;
1560
+ }
1561
+ async connect() {
1562
+ const ctx = { auth: this.auth };
1563
+ return TOOLS2.map((def) => new Tool({
1564
+ name: `mcp__${this.nameSpace}__${def.name}`,
1565
+ description: def.description,
1566
+ jsonSchema: def.inputSchema,
1567
+ deferred: this.deferred,
1568
+ execute: async (input) => {
1569
+ const args = input ?? {};
1570
+ const result = await def.call(args, ctx);
1571
+ return typeof result === "string" ? result : JSON.stringify(result);
1572
+ }
1573
+ }));
1574
+ }
1575
+ async disconnect() {}
1576
+ }
1577
+ var slack_default = Mcp5;
1343
1578
 
1344
1579
  // src/mcp/clients/tenderly.ts
1345
1580
  var TENDERLY_MCP_URL = "https://mcp.tenderly.co/mcp";
1346
1581
 
1347
- class Mcp4 extends Mcp2 {
1582
+ class Mcp6 extends Mcp {
1348
1583
  constructor(opts) {
1349
1584
  const auth = new OAuthAuth({
1350
1585
  serverUrl: TENDERLY_MCP_URL,
@@ -1362,14 +1597,16 @@ class Mcp4 extends Mcp2 {
1362
1597
  });
1363
1598
  }
1364
1599
  }
1365
- var tenderly_default = Mcp4;
1600
+ var tenderly_default = Mcp6;
1366
1601
  export {
1367
1602
  tenderly_default as Tenderly,
1603
+ slack_default as Slack,
1368
1604
  OAuthAuth,
1369
1605
  MemoryStorage,
1370
- Mcp2 as Mcp,
1606
+ Mcp,
1371
1607
  linear_default as Linear,
1372
1608
  grafana_default as Grafana,
1609
+ github_default as Github,
1373
1610
  FileStorage,
1374
1611
  BearerAuth
1375
1612
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roboport",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Minimal TypeScript framework for building LLM agents.",
5
5
  "author": {
6
6
  "name": "Timur Badretdinov",