@kynver-app/mcp-agent-os 0.3.63 → 0.3.65

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.
@@ -49,8 +49,13 @@ function getHeaders() {
49
49
  const userId = ctx?.userId;
50
50
  const serviceSecret = process.env.KYNVER_SERVICE_SECRET;
51
51
  if (userId) {
52
+ const runnerToken = process.env.KYNVER_RUNNER_TOKEN?.trim();
53
+ if (runnerToken) {
54
+ headers["X-Kynver-Runner-Token"] = runnerToken;
55
+ return headers;
56
+ }
52
57
  if (!serviceSecret) {
53
- throw new Error("X-Kynver-User-Id was set in context but KYNVER_SERVICE_SECRET is missing; refusing to send an unscoped request.");
58
+ throw new Error("User context requires authentication: set KYNVER_RUNNER_TOKEN (preferred) or KYNVER_SERVICE_SECRET in the MCP server environment.");
54
59
  }
55
60
  headers["X-Kynver-User-Id"] = userId;
56
61
  headers["X-Kynver-Service-Secret"] = serviceSecret;
@@ -5,6 +5,13 @@ import { timingSafeEqual } from "node:crypto";
5
5
  import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
6
6
  export const MAX_SSE_BODY_BYTES = 1 * 1024 * 1024;
7
7
  const FORBIDDEN_QUERY_KEYS = ["apiKey", "secret", "userId"];
8
+ const TRUTHY_ENV = new Set(["1", "true", "yes", "on"]);
9
+ function isServiceSecretImpersonationRejectedInProduction() {
10
+ const override = process.env.KYNVER_ALLOW_SERVICE_SECRET_IMPERSONATION?.trim().toLowerCase();
11
+ if (override !== undefined && TRUTHY_ENV.has(override))
12
+ return false;
13
+ return process.env.NODE_ENV === "production" || process.env.VERCEL_ENV === "production";
14
+ }
8
15
  export function headerValue(req, name) {
9
16
  const v = req.headers[name];
10
17
  return typeof v === "string" ? v : Array.isArray(v) ? (v[0] ?? null) : null;
@@ -121,6 +128,8 @@ function resolveInternalAuth(req, serviceSecret) {
121
128
  const internalSecret = headerValue(req, "x-kynver-service-secret");
122
129
  if (!internalUserId || !serviceSecret || !internalSecret)
123
130
  return null;
131
+ if (isServiceSecretImpersonationRejectedInProduction())
132
+ return null;
124
133
  if (!timingSafeEqualString(internalSecret, serviceSecret))
125
134
  return null;
126
135
  return { userId: internalUserId, auth: { kind: "internal", userId: internalUserId } };
@@ -125,10 +125,18 @@ export function registerGraphTools({ register, resolveToolSlug, jsonResult }) {
125
125
  grantToken: z
126
126
  .string()
127
127
  .describe("Signed expansion grant (kve:g:…) bound to this graph handle and envelope."),
128
- purpose: z.string().describe("Why expansion is requested (audit-bound)."),
128
+ purpose: z
129
+ .string()
130
+ .describe("Informational only — the server enforces the graph-expansion purpose bound into the grant and ignores this value."),
129
131
  envelopeId: z.string().describe("Active context envelope id binding this expansion."),
130
132
  asOf: z.string().optional().describe("Optional UTC ISO as-of instant for temporal filtering."),
131
- maxDepth: z.number().int().min(1).max(2).optional(),
133
+ maxDepth: z
134
+ .number()
135
+ .int()
136
+ .min(1)
137
+ .max(1)
138
+ .optional()
139
+ .describe("Traversal depth; the server caps expansion depth at 1."),
132
140
  maxNodes: z.number().int().min(1).max(36).optional(),
133
141
  }, async (args) => {
134
142
  const slug = await resolveToolSlug(args.slug);
@@ -19,6 +19,20 @@ const commandKind = z.enum([
19
19
  "yield",
20
20
  "take",
21
21
  ]);
22
+ /** Keep in sync with COMMAND_CENTER_VIEW_MODES in command-center-url-state.ts */
23
+ const workbenchView = z.enum([
24
+ "overview",
25
+ "flow",
26
+ "marketing",
27
+ "projects",
28
+ "tasks",
29
+ "goals",
30
+ "dq",
31
+ "web",
32
+ "review",
33
+ "system",
34
+ "health",
35
+ ]);
22
36
  export function registerWorkbenchTools({ register, resolveToolSlug, jsonResult, }) {
23
37
  register("agent_os_workbench_query", [
24
38
  "Read the operator's current workbench focus (view mode, drilldown stack, focused entity)",
@@ -37,7 +51,7 @@ export function registerWorkbenchTools({ register, resolveToolSlug, jsonResult,
37
51
  ].join(" "), {
38
52
  slug: z.string().optional(),
39
53
  kind: commandKind,
40
- view: z.enum(["projects", "tasks", "goals", "dq", "web"]).optional(),
54
+ view: workbenchView.optional(),
41
55
  target: z.string().optional().describe("EntityRef e.g. task:<id>, plan:<id>"),
42
56
  push: z.boolean().optional().describe("For drill: push onto stack (default true)"),
43
57
  panel: z.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kynver-app/mcp-agent-os",
3
- "version": "0.3.63",
3
+ "version": "0.3.65",
4
4
  "description": "Kynver Agentic OS MCP server — slug-keyed agent identity, goals, projects, sessions, and long-term memory",
5
5
  "type": "module",
6
6
  "bin": {