@brainervirus/workit-mcp 1.0.9 → 1.0.11

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.
Files changed (3) hide show
  1. package/dist/index.js +359 -222
  2. package/package.json +2 -2
  3. package/src/server.ts +51 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-mcp",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "private": false,
5
5
  "description": "Workit shared MCP transport for the eight task and policy operation families",
6
6
  "keywords": [
@@ -42,7 +42,7 @@
42
42
  "build": "bun scripts/build.ts"
43
43
  },
44
44
  "dependencies": {
45
- "@brainervirus/workit-core": "^1.0.9",
45
+ "@brainervirus/workit-core": "^1.0.11",
46
46
  "@modelcontextprotocol/sdk": "1.30.0",
47
47
  "zod": "4.5.4"
48
48
  },
package/src/server.ts CHANGED
@@ -133,6 +133,46 @@ const toolInputSchema = (family: OperationFamily) => {
133
133
  return { type: "object" as const, ...schema };
134
134
  };
135
135
 
136
+ const schemaBranches = (schema: Record<string, unknown>): unknown[] =>
137
+ Array.isArray(schema.oneOf)
138
+ ? schema.oneOf
139
+ : Array.isArray(schema.anyOf)
140
+ ? schema.anyOf
141
+ : [schema];
142
+
143
+ const readOnlyBranches = (family: OperationFamily): unknown[] =>
144
+ schemaBranches(boundedOperationJsonSchema(family) as Record<string, unknown>).filter((branch) => {
145
+ const action = (branch as { properties?: { action?: { const?: unknown } } }).properties?.action
146
+ ?.const;
147
+ return typeof action === "string" && READ_ONLY_ACTIONS.has(action);
148
+ });
149
+
150
+ /** Attested callers (host sessions that can mutate) see the full family union;
151
+ * unattested MCP callers see only read-only actions, and families with no
152
+ * read-only action are not advertised as callable tools at all. */
153
+ export const advertisedToolSchemas = (
154
+ attested: boolean,
155
+ ): { name: string; description: string; inputSchema: Record<string, unknown> }[] => {
156
+ if (attested)
157
+ return OPERATION_FAMILIES.map((family) => ({
158
+ name: `workit_${family}`,
159
+ description: operationDescription(family),
160
+ inputSchema: toolInputSchema(family),
161
+ }));
162
+ return OPERATION_FAMILIES.flatMap((family) => {
163
+ const branches = readOnlyBranches(family);
164
+ if (!branches.length) return [];
165
+ const schema = boundedOperationJsonSchema(family) as Record<string, unknown>;
166
+ return [
167
+ {
168
+ name: `workit_${family}`,
169
+ description: `${operationDescription(family)} Unattested MCP callers can only run read-only actions; mutating operations require an attested host session.`,
170
+ inputSchema: { type: "object" as const, ...schema, oneOf: branches },
171
+ },
172
+ ];
173
+ });
174
+ };
175
+
136
176
  const sanitizeFailure = (result: Result<unknown>, workspaceRoot?: string): Result<unknown> => {
137
177
  if (result.ok) return result;
138
178
  const sanitize = (value: unknown): unknown => {
@@ -198,13 +238,16 @@ export function createMcpServer(host: McpHost, contextProvider: NativeContextPro
198
238
  { capabilities: { tools: {}, resources: {} } },
199
239
  );
200
240
 
201
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
202
- tools: OPERATION_FAMILIES.map((family) => ({
203
- name: `workit_${family}`,
204
- description: operationDescription(family),
205
- inputSchema: toolInputSchema(family),
206
- })),
207
- }));
241
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
242
+ let attested = false;
243
+ try {
244
+ const context = await contextProvider.current();
245
+ attested = context.callerAttested === true;
246
+ } catch {
247
+ attested = false;
248
+ }
249
+ return { tools: advertisedToolSchemas(attested) };
250
+ });
208
251
 
209
252
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
210
253
  resources: CONTEXT_KINDS.map((kind) => ({
@@ -338,7 +381,7 @@ export function createMcpServer(host: McpHost, contextProvider: NativeContextPro
338
381
  }
339
382
  const parsed = parseOperation(family, request.params.arguments);
340
383
  if (!parsed.ok) return resultForClient(parsed, workspaceRoot);
341
- if (context.callerAttested === false && requiresCallerIdentity(parsed.data))
384
+ if (context.callerAttested !== true && requiresCallerIdentity(parsed.data))
342
385
  return resultForClient(
343
386
  {
344
387
  ok: false,