@kammcs/buildit-mcp 0.7.0 → 0.8.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.
@@ -11,6 +11,8 @@ import { APPROVAL_HOOK_TOOLS, createApprovalHookTools, READ_ONLY_SAFE_HOOKS, cre
11
11
  import { createAskTool } from '../tools/ask.js';
12
12
  import { createReplyTool, createTurnEndedTool } from '../tools/reply.js';
13
13
  import { createListThreadsTool } from '../tools/threads.js';
14
+ import { createHandoffTools } from '../tools/handoff.js';
15
+ import { suggestedCapabilities } from '../capabilities.js';
14
16
  import { CATALOG } from '../toolsets/catalog.js';
15
17
  import { compareVersions, SERVER_VERSION } from '../version.js';
16
18
  /**
@@ -273,9 +275,21 @@ function channelTools(policy, state, ask, approve) {
273
275
  createTurnEndedTool(state),
274
276
  createApproveTool(state, approve),
275
277
  ...createApprovalHookTools(state),
278
+ ...createHandoffTools(state),
276
279
  ].filter((tool) => (!policy.readOnly || tool.annotations.readOnlyHint || READ_ONLY_SAFE_HOOKS.has(tool.name)) &&
277
280
  !policy.excludeTools.includes(tool.name));
278
281
  }
282
+ /**
283
+ * The channel tools this server's API serves: a tool that needs operations
284
+ * the API's /v1/meta leaves out is not listed (when the API doesn't say,
285
+ * every tool is, and an older one says so when called).
286
+ */
287
+ function servedTools(api, tools) {
288
+ const served = api.served?.operations;
289
+ if (served === undefined)
290
+ return [...tools];
291
+ return tools.filter((t) => (t.requiresOperations ?? []).every((op) => served.has(op)));
292
+ }
279
293
  export function startStdio(config, logger, options = {}) {
280
294
  if (config.token === undefined)
281
295
  throw new Error('stdio mode needs a token');
@@ -336,11 +350,27 @@ export function startStdio(config, logger, options = {}) {
336
350
  tools: base.tools.filter((t) => !extraTools.includes(t) || t.runsAfterSessionEnd === true),
337
351
  };
338
352
  };
353
+ // Read what the API serves at startup: it warns when the API needs a newer server, and
354
+ // channel tools newer than the server (the handoffs) are left out of the listing.
355
+ const metaRead = api
356
+ .getMeta()
357
+ .then((meta) => {
358
+ if (meta.min_mcp_version && compareVersions(SERVER_VERSION, meta.min_mcp_version) < 0) {
359
+ logger.warn('this buildit-mcp is older than the API supports; update it', {
360
+ version: SERVER_VERSION,
361
+ min_version: meta.min_mcp_version,
362
+ });
363
+ }
364
+ })
365
+ .catch(() => undefined);
339
366
  const identity = new StdioIdentity(async () => {
340
367
  if (isEnded())
341
368
  return frozen();
342
369
  const listed = await resolveListedTools(identityFromApi(api), catalog, policy, logger);
343
- const shown = extraTools.length > 0 ? { ...listed, tools: [...listed.tools, ...extraTools] } : listed;
370
+ if (extraTools.length > 0)
371
+ await metaRead;
372
+ const served = servedTools(api, extraTools);
373
+ const shown = served.length > 0 ? { ...listed, tools: [...listed.tools, ...served] } : listed;
344
374
  if (shown.identity === 'known')
345
375
  lastKnown = shown;
346
376
  // The session may have ended while the identity was read.
@@ -351,18 +381,6 @@ export function startStdio(config, logger, options = {}) {
351
381
  };
352
382
  // Read the identity at startup.
353
383
  void identity.refresh();
354
- // Warn early when the API needs a newer server.
355
- void api
356
- .getMeta()
357
- .then((meta) => {
358
- if (meta.min_mcp_version && compareVersions(SERVER_VERSION, meta.min_mcp_version) < 0) {
359
- logger.warn('this buildit-mcp is older than the API supports; update it', {
360
- version: SERVER_VERSION,
361
- min_version: meta.min_mcp_version,
362
- });
363
- }
364
- })
365
- .catch(() => undefined);
366
384
  // Everything the configuration allows is registered; the identity decides what is shown.
367
385
  const allowed = listedByPolicy(catalog, policy);
368
386
  const wire = options.stdin && options.stdout
@@ -373,7 +391,9 @@ export function startStdio(config, logger, options = {}) {
373
391
  const handle = serveStdio(async () => {
374
392
  const listed = await identity.refresh();
375
393
  const switchable = createSwitchableMcpServer({
376
- tools: [...allowed.tools, ...extraTools],
394
+ // What /v1/meta said is known by now (refresh waited for it): the instructions leave out
395
+ // the channel tools this API doesn't serve.
396
+ tools: [...allowed.tools, ...servedTools(api, extraTools)],
377
397
  resources: allowed.resources,
378
398
  prompts: allowed.prompts,
379
399
  api,
@@ -412,6 +432,8 @@ export function startStdio(config, logger, options = {}) {
412
432
  logger: logger.child({ component: 'channel' }),
413
433
  state,
414
434
  notify: (params) => mcp.notification({ method: CHANNEL_NOTIFICATION, params }),
435
+ // What this computer can do, as tag names only (BUILDIT_CAPABILITIES replaces it).
436
+ capabilities: () => suggestedCapabilities(config.channel?.capabilities),
415
437
  ...(options.channelLock === undefined
416
438
  ? {}
417
439
  : {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kammcs/buildit-mcp",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "MCP server for buildIt.Social: lets AI agents work with an org's projects, pages and channels through the buildIt.Social agent API, with a personal access token.",
5
5
  "license": "MIT",
6
6
  "author": "Kamm Creative Solutions",