@unson/brainbase-mcp 0.14.0 → 0.15.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.
@@ -53,4 +53,14 @@ export type FoundationPublicToolDependencies = FoundationAuthenticatedToolDepend
53
53
  export declare const foundationPublicTools: Tool[];
54
54
  export declare function isFoundationPublicToolName(name: string): name is FoundationPublicToolName;
55
55
  export declare function handleFoundationPublicToolCall(name: string, args: Record<string, unknown>, dependencies: FoundationPublicToolDependencies): Promise<FoundationAuthenticatedToolResult | null>;
56
+ /**
57
+ * brainbase-project ADR-015: adopt a Foundation draft for judgment and
58
+ * evaluation through the canonical API. The caller passes the revision and
59
+ * digest they reviewed (for example with foundation_read); the API refuses a
60
+ * draft that changed since, the draft's own owner, a caller without a gm or
61
+ * above grant for the project, and a definition not ready for judgment.
62
+ */
63
+ export declare const foundationAdoptionTools: Tool[];
64
+ export declare function isFoundationAdoptionToolName(name: string): boolean;
65
+ export declare function handleFoundationAdoptionToolCall(name: string, args: Record<string, unknown>, dependencies: FoundationAuthenticatedToolDependencies): Promise<FoundationAuthenticatedToolResult | null>;
56
66
  export {};
@@ -345,3 +345,89 @@ export async function handleFoundationPublicToolCall(name, args, dependencies) {
345
345
  }
346
346
  return { status: 'ok', scope: { project_codes: context.scope }, data: fetched.payload };
347
347
  }
348
+ const FOUNDATION_ADOPTION_TYPES = new Set(['objective', 'variable', 'model', 'constraint']);
349
+ const FOUNDATION_ADOPTION_PATH = '/api/company-os/foundation-adoptions';
350
+ /**
351
+ * brainbase-project ADR-015: adopt a Foundation draft for judgment and
352
+ * evaluation through the canonical API. The caller passes the revision and
353
+ * digest they reviewed (for example with foundation_read); the API refuses a
354
+ * draft that changed since, the draft's own owner, a caller without a gm or
355
+ * above grant for the project, and a definition not ready for judgment.
356
+ */
357
+ export const foundationAdoptionTools = [{
358
+ name: 'foundation_adopt',
359
+ description: [
360
+ 'Adopt a Foundation draft (objective, variable, model or constraint) so it can be used for judgment and evaluation, never execution.',
361
+ 'Read the draft first with foundation_read and pass the exact revision and digest you reviewed; the content is adopted unchanged as the next revision.',
362
+ 'Only a gm or above of the project who is not the owner of the draft can adopt. This is a write: confirm with the person before calling it.',
363
+ ].join(' '),
364
+ inputSchema: {
365
+ type: 'object',
366
+ properties: {
367
+ scope_id: { type: 'string', minLength: 1, maxLength: MAX_SCOPE_ID_LENGTH, description: 'Project code the draft belongs to.' },
368
+ type: { type: 'string', enum: ['objective', 'variable', 'model', 'constraint'] },
369
+ id: { type: 'string', minLength: 1, maxLength: 256 },
370
+ revision: { type: 'string', pattern: '^[1-9][0-9]*$', description: 'The draft revision you reviewed.' },
371
+ digest: { type: 'string', pattern: '^sha256:[0-9a-f]{64}$', description: 'The digest of the reviewed revision, as foundation_read returned it.' },
372
+ },
373
+ required: ['scope_id', 'type', 'id', 'revision', 'digest'],
374
+ additionalProperties: false,
375
+ },
376
+ }];
377
+ export function isFoundationAdoptionToolName(name) {
378
+ return name === 'foundation_adopt';
379
+ }
380
+ function isValidAdoptionPayload(args, payload) {
381
+ if (!isRecord(payload) || payload.action !== 'adopt' || !isRecord(payload.result))
382
+ return false;
383
+ const result = payload.result;
384
+ return result.id === args.id
385
+ && result.type === args.type
386
+ && result.revision === String(Number(args.revision) + 1)
387
+ && isNonEmptyString(result.digest)
388
+ && DIGEST_PATTERN.test(result.digest);
389
+ }
390
+ export async function handleFoundationAdoptionToolCall(name, args, dependencies) {
391
+ if (!isFoundationAdoptionToolName(name))
392
+ return null;
393
+ const scopeId = readScopeId(args);
394
+ if (!scopeId)
395
+ return invalidInput('scope_id is required and must be a safe project identifier', dependencies);
396
+ const unexpected = rejectUnexpectedKeys(args, ['scope_id', 'type', 'id', 'revision', 'digest'], dependencies);
397
+ if (unexpected)
398
+ return unexpected;
399
+ const type = readRequiredString(args, 'type');
400
+ const id = readRequiredString(args, 'id');
401
+ const revision = readRequiredString(args, 'revision');
402
+ const digest = readRequiredString(args, 'digest');
403
+ if (!type || !FOUNDATION_ADOPTION_TYPES.has(type))
404
+ return invalidInput('foundation type must be objective, variable, model or constraint', dependencies);
405
+ if (!id)
406
+ return invalidInput('foundation id is required', dependencies);
407
+ if (!revision || !REVISION_PATTERN.test(revision))
408
+ return invalidInput('foundation revision is invalid', dependencies);
409
+ if (!digest || !DIGEST_PATTERN.test(digest))
410
+ return invalidInput('foundation digest is invalid', dependencies);
411
+ const authenticated = await dependencies.auth.authenticateProject({ project_code: scopeId }, { requireProject: true });
412
+ if ('status' in authenticated)
413
+ return authenticated;
414
+ const context = selectedContext(authenticated, scopeId);
415
+ const csrf = await fetchFoundationCsrfToken(dependencies, context);
416
+ if (!csrf.ok)
417
+ return csrf.result;
418
+ const fetched = await dependencies.auth.fetchAuthenticatedJson(context, {
419
+ path: FOUNDATION_ADOPTION_PATH,
420
+ method: 'POST',
421
+ body: { id, type, revision, digest },
422
+ headers: { 'x-brainbase-scope': scopeId, 'x-session-id': csrf.sessionId, 'x-csrf-token': csrf.token },
423
+ });
424
+ if (!fetched.ok)
425
+ return fetched.result;
426
+ if (!fetched.response.ok)
427
+ return errorFromResponse(fetched.response, fetched.payload, context.scope, dependencies);
428
+ const normalizedArgs = { id, type, revision };
429
+ if (!fetched.payloadParsed || !isValidAdoptionPayload(normalizedArgs, fetched.payload)) {
430
+ return dependencies.auth.toolError('error', 'foundation_api_response_invalid', 'Brainbase API returned an invalid adoption response', context.scope, fetched.response.status);
431
+ }
432
+ return { status: 'ok', scope: { project_codes: context.scope }, data: fetched.payload };
433
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unson/brainbase-mcp",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Local-first Brainbase judgment OS that lets supported AI agents reuse a user's approved goals, priorities, decisions, and judgment criteria.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -352,5 +352,5 @@
352
352
  "engines": {
353
353
  "node": ">=20"
354
354
  },
355
- "gitHead": "4c9f27c5bc49aed6cb9132c442739865a41f8cc5"
355
+ "gitHead": "2d242e478ef24937acfaf02363995d445974ed8e"
356
356
  }