@kynver-app/mcp-agent-os 0.3.46 → 0.3.49
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.
- package/dist/lib/mcp-sse-server.js +3 -16
- package/dist/server.js +9 -0
- package/dist/tools/kariad-marketing-loop.d.ts +21 -0
- package/dist/tools/kariad-marketing-loop.js +41 -0
- package/dist/tools/kariad-outcome-import.d.ts +17 -0
- package/dist/tools/kariad-outcome-import.js +37 -0
- package/dist/tools/macro-controls.d.ts +23 -0
- package/dist/tools/macro-controls.js +54 -0
- package/dist/tools/plan-to-factory.d.ts +2 -0
- package/dist/tools/plan-to-factory.js +23 -0
- package/dist/tools/tasks.js +1 -1
- package/package.json +1 -1
|
@@ -4,7 +4,7 @@ import { createServer } from "node:http";
|
|
|
4
4
|
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
|
-
const FORBIDDEN_QUERY_KEYS = ["secret", "userId"];
|
|
7
|
+
const FORBIDDEN_QUERY_KEYS = ["apiKey", "secret", "userId"];
|
|
8
8
|
export function headerValue(req, name) {
|
|
9
9
|
const v = req.headers[name];
|
|
10
10
|
return typeof v === "string" ? v : Array.isArray(v) ? (v[0] ?? null) : null;
|
|
@@ -19,9 +19,6 @@ export function timingSafeEqualString(a, b) {
|
|
|
19
19
|
export function hasForbiddenQueryAuth(url) {
|
|
20
20
|
return FORBIDDEN_QUERY_KEYS.some((key) => url.searchParams.has(key));
|
|
21
21
|
}
|
|
22
|
-
function queryApiKey(url) {
|
|
23
|
-
return url.searchParams.get("apiKey")?.trim() ?? "";
|
|
24
|
-
}
|
|
25
22
|
export function resolveListenHost() {
|
|
26
23
|
const configured = process.env.MCP_SSE_HOST?.trim();
|
|
27
24
|
return configured || "127.0.0.1";
|
|
@@ -130,7 +127,7 @@ function resolveInternalAuth(req, serviceSecret) {
|
|
|
130
127
|
}
|
|
131
128
|
function verifyPostAuth(req, url, sessionAuth, serviceSecret) {
|
|
132
129
|
if (sessionAuth.kind === "apiKey") {
|
|
133
|
-
const token = bearerToken(req)
|
|
130
|
+
const token = bearerToken(req);
|
|
134
131
|
return token.length > 0 && timingSafeEqualString(token, sessionAuth.apiKey);
|
|
135
132
|
}
|
|
136
133
|
const internal = resolveInternalAuth(req, serviceSecret);
|
|
@@ -170,7 +167,6 @@ export function startKynverMcpSseServer(options) {
|
|
|
170
167
|
const maxSessionsPerUser = intFromEnv("MCP_SSE_MAX_SESSIONS_PER_USER", 20);
|
|
171
168
|
const idleTimeoutMs = intFromEnv("MCP_SSE_IDLE_TIMEOUT_MS", 5 * 60 * 1000);
|
|
172
169
|
const rateLimitPerMin = intFromEnv("MCP_SSE_RATE_LIMIT_PER_MIN", 120);
|
|
173
|
-
const rejectQueryApiKey = /^(1|true|yes)$/i.test(process.env.MCP_SSE_REJECT_QUERY_APIKEY ?? "");
|
|
174
170
|
const rateLimiter = createFixedWindowRateLimiter(rateLimitPerMin, 60 * 1000);
|
|
175
171
|
// Close idle SSE sessions so a flood of half-open connections can't pin memory.
|
|
176
172
|
const idleSweep = setInterval(() => {
|
|
@@ -198,15 +194,6 @@ export function startKynverMcpSseServer(options) {
|
|
|
198
194
|
});
|
|
199
195
|
return;
|
|
200
196
|
}
|
|
201
|
-
if (url.searchParams.has("apiKey")) {
|
|
202
|
-
if (rejectQueryApiKey) {
|
|
203
|
-
json(res, 400, {
|
|
204
|
-
error: "apiKey via query param is disabled. Use Authorization: Bearer <key>.",
|
|
205
|
-
});
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
console.warn("[MCP SSE] WARNING: apiKey in query string may leak via logs. Prefer Authorization header when your client supports it.");
|
|
209
|
-
}
|
|
210
197
|
let userId = null;
|
|
211
198
|
let auth = null;
|
|
212
199
|
const internal = serviceSecret ? resolveInternalAuth(req, serviceSecret) : null;
|
|
@@ -215,7 +202,7 @@ export function startKynverMcpSseServer(options) {
|
|
|
215
202
|
auth = internal.auth;
|
|
216
203
|
}
|
|
217
204
|
else {
|
|
218
|
-
const apiKey = bearerToken(req)
|
|
205
|
+
const apiKey = bearerToken(req);
|
|
219
206
|
if (apiKey) {
|
|
220
207
|
userId = await resolveUserFromApiKey(baseUrl, apiKey);
|
|
221
208
|
if (userId)
|
package/dist/server.js
CHANGED
|
@@ -11,6 +11,7 @@ import { z } from "zod";
|
|
|
11
11
|
import { get, post, patch } from "./lib/kynver-client.js";
|
|
12
12
|
import { registerPlanTools } from "./tools/plans.js";
|
|
13
13
|
import { registerPlanProgressTools } from "./tools/plan-progress.js";
|
|
14
|
+
import { registerPlanToFactoryTools } from "./tools/plan-to-factory.js";
|
|
14
15
|
import { registerTaskTools } from "./tools/tasks.js";
|
|
15
16
|
import { registerActionRequiredTools } from "./tools/action-required.js";
|
|
16
17
|
import { registerGraphTools } from "./tools/graph.js";
|
|
@@ -26,6 +27,9 @@ import { registerSkillsTools } from "./tools/skills.js";
|
|
|
26
27
|
import { registerMemoryLedgerTools } from "./tools/memory-ledger.js";
|
|
27
28
|
import { registerMemoryVaultTools } from "./tools/memory-vault.js";
|
|
28
29
|
import { registerMonitorTools } from "./tools/monitor.js";
|
|
30
|
+
import { registerMacroControlsTools } from "./tools/macro-controls.js";
|
|
31
|
+
import { registerKariAdMarketingLoopTools } from "./tools/kariad-marketing-loop.js";
|
|
32
|
+
import { registerKariAdOutcomeImportTools } from "./tools/kariad-outcome-import.js";
|
|
29
33
|
import { readOwnPackageVersion } from "./lib/package-version.js";
|
|
30
34
|
const PACKAGE_VERSION = readOwnPackageVersion(import.meta.url);
|
|
31
35
|
function jsonResult(data) {
|
|
@@ -709,6 +713,7 @@ export function createAgentOsServer() {
|
|
|
709
713
|
// First-class AgentOS plans (Phase 5A): versioned operational artifacts.
|
|
710
714
|
registerPlanTools({ register, resolveToolSlug, jsonResult, compact });
|
|
711
715
|
registerPlanProgressTools({ register, resolveToolSlug, jsonResult, compact });
|
|
716
|
+
registerPlanToFactoryTools({ register, resolveToolSlug, jsonResult, compact });
|
|
712
717
|
// Action Required inbox: agent_os_ask_human + agent_os_await_action (slice 1).
|
|
713
718
|
registerActionRequiredTools({ register, jsonResult, compact });
|
|
714
719
|
// Operational graph insights — Agent SEE (agent_os_graph_query).
|
|
@@ -734,5 +739,9 @@ export function createAgentOsServer() {
|
|
|
734
739
|
registerMemoryLedgerTools({ register, resolveToolSlug, jsonResult });
|
|
735
740
|
registerMemoryVaultTools({ register, resolveToolSlug, jsonResult });
|
|
736
741
|
registerMonitorTools({ register, resolveToolSlug, jsonResult });
|
|
742
|
+
registerMacroControlsTools({ register, resolveToolSlug, jsonResult });
|
|
743
|
+
registerKariAdMarketingLoopTools({ register, resolveToolSlug, jsonResult });
|
|
744
|
+
// KariAd marketing loop — M6 outcome import (analytics feedback + memory learning).
|
|
745
|
+
registerKariAdOutcomeImportTools({ register, resolveToolSlug, jsonResult });
|
|
737
746
|
return server;
|
|
738
747
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KariAd campaign brief MCP tool (M4).
|
|
3
|
+
*
|
|
4
|
+
* agent_os_kariad_campaign_brief_create — fuse macro trends, Kynver goals,
|
|
5
|
+
* and KariAd deep memory into a cited RuntimeCampaignBrief.
|
|
6
|
+
* Does not publish or spend.
|
|
7
|
+
*/
|
|
8
|
+
type ToolResult = {
|
|
9
|
+
content: {
|
|
10
|
+
type: "text";
|
|
11
|
+
text: string;
|
|
12
|
+
}[];
|
|
13
|
+
structuredContent?: unknown;
|
|
14
|
+
};
|
|
15
|
+
export interface KariAdMarketingLoopToolDeps {
|
|
16
|
+
register: (name: string, description: string, inputSchema: Record<string, unknown>, cb: (args: any) => any) => void;
|
|
17
|
+
resolveToolSlug: (explicitSlug?: string) => Promise<string>;
|
|
18
|
+
jsonResult: (data: unknown) => ToolResult;
|
|
19
|
+
}
|
|
20
|
+
export declare function registerKariAdMarketingLoopTools({ register, resolveToolSlug, jsonResult, }: KariAdMarketingLoopToolDeps): void;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KariAd campaign brief MCP tool (M4).
|
|
3
|
+
*
|
|
4
|
+
* agent_os_kariad_campaign_brief_create — fuse macro trends, Kynver goals,
|
|
5
|
+
* and KariAd deep memory into a cited RuntimeCampaignBrief.
|
|
6
|
+
* Does not publish or spend.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { post } from "../lib/kynver-client.js";
|
|
10
|
+
const slugField = z
|
|
11
|
+
.string()
|
|
12
|
+
.optional()
|
|
13
|
+
.describe("AgentOS slug. Defaults to env/primary AgentOS.");
|
|
14
|
+
export function registerKariAdMarketingLoopTools({ register, resolveToolSlug, jsonResult, }) {
|
|
15
|
+
register("agent_os_kariad_campaign_brief_create", [
|
|
16
|
+
"Create a compact, cited RuntimeCampaignBrief by combining on-demand macro/social trend signals,",
|
|
17
|
+
"Kynver marketplace/runtime goals, and KariAd deep brand memory.",
|
|
18
|
+
"Returns content angles with source links, target audiences, selected brand/workspace, risk labels,",
|
|
19
|
+
"and recommended next KariAd actions.",
|
|
20
|
+
"Does NOT publish, schedule, or spend — approval is always required before KariAd execution tools (M5).",
|
|
21
|
+
"Call agent_os_macro_controls_get first when trend awareness matters for the marketing task.",
|
|
22
|
+
].join(" "), {
|
|
23
|
+
slug: slugField,
|
|
24
|
+
workspaceId: z.string().describe("KariAd workspace id (from kari_whoami / connection preflight)."),
|
|
25
|
+
brandId: z.string().describe("KariAd brand id for voice, audience, and calendar context."),
|
|
26
|
+
brandName: z.string().optional().describe("Optional display name when brand id is known."),
|
|
27
|
+
goalStatement: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Campaign goal override. When omitted, inferred from active Kynver goals/projects."),
|
|
31
|
+
}, async (args) => {
|
|
32
|
+
const s = await resolveToolSlug(args.slug);
|
|
33
|
+
const payload = await post(`/agent-os/${encodeURIComponent(s)}/kariad-marketing-loop/briefs`, {
|
|
34
|
+
workspaceId: args.workspaceId,
|
|
35
|
+
brandId: args.brandId,
|
|
36
|
+
...(args.brandName ? { brandName: args.brandName } : {}),
|
|
37
|
+
...(args.goalStatement ? { goalStatement: args.goalStatement } : {}),
|
|
38
|
+
});
|
|
39
|
+
return jsonResult(payload);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M6 outcome import MCP tool — agent_os_kariad_outcome_import
|
|
3
|
+
*/
|
|
4
|
+
type ToolResult = {
|
|
5
|
+
content: {
|
|
6
|
+
type: "text";
|
|
7
|
+
text: string;
|
|
8
|
+
}[];
|
|
9
|
+
structuredContent?: unknown;
|
|
10
|
+
};
|
|
11
|
+
export interface KariAdOutcomeImportToolDeps {
|
|
12
|
+
register: (name: string, description: string, inputSchema: Record<string, unknown>, cb: (args: any) => any) => void;
|
|
13
|
+
resolveToolSlug: (explicitSlug?: string) => Promise<string>;
|
|
14
|
+
jsonResult: (data: unknown) => ToolResult;
|
|
15
|
+
}
|
|
16
|
+
export declare function registerKariAdOutcomeImportTools({ register, resolveToolSlug, jsonResult, }: KariAdOutcomeImportToolDeps): void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M6 outcome import MCP tool — agent_os_kariad_outcome_import
|
|
3
|
+
*/
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { post } from "../lib/kynver-client.js";
|
|
6
|
+
const slugField = z
|
|
7
|
+
.string()
|
|
8
|
+
.optional()
|
|
9
|
+
.describe("AgentOS slug. Defaults to env/primary AgentOS.");
|
|
10
|
+
export function registerKariAdOutcomeImportTools({ register, resolveToolSlug, jsonResult, }) {
|
|
11
|
+
register("agent_os_kariad_outcome_import", [
|
|
12
|
+
"Import KariAd campaign outcome summaries and produce separate KariAd vs Kynver memory proposals",
|
|
13
|
+
"with correct ownership, provider-quality metrics, and duplicate suppression.",
|
|
14
|
+
"Set writeback=true to route cleared proposals to KariAd submit_memory_proposal and Kynver MARM.",
|
|
15
|
+
"Requires planId + briefId from the marketing loop execution window.",
|
|
16
|
+
].join(" "), {
|
|
17
|
+
slug: slugField,
|
|
18
|
+
workspaceId: z.string().describe("KariAd workspace id."),
|
|
19
|
+
brandId: z.string().describe("KariAd brand id."),
|
|
20
|
+
planId: z.string().describe("KariAd execution plan id for attribution."),
|
|
21
|
+
briefId: z.string().describe("Runtime campaign brief id for attribution."),
|
|
22
|
+
writeback: z
|
|
23
|
+
.boolean()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("When true, persist proposals to KariAd/Kynver. Default dry-run."),
|
|
26
|
+
}, async (args) => {
|
|
27
|
+
const s = await resolveToolSlug(args.slug);
|
|
28
|
+
const payload = await post(`/agent-os/${encodeURIComponent(s)}/kariad-marketing-loop/outcomes/import`, {
|
|
29
|
+
workspaceId: args.workspaceId,
|
|
30
|
+
brandId: args.brandId,
|
|
31
|
+
planId: args.planId,
|
|
32
|
+
briefId: args.briefId,
|
|
33
|
+
writeback: args.writeback === true,
|
|
34
|
+
});
|
|
35
|
+
return jsonResult(payload);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Macro/social trend on-demand catch-up MCP tool.
|
|
3
|
+
*
|
|
4
|
+
* Runtime agents call agent_os_macro_controls_get before producing social media
|
|
5
|
+
* posts, ad copy, campaign briefs, launch announcements, or any trend-sensitive
|
|
6
|
+
* marketing content so they can adapt angle and tone to current signals.
|
|
7
|
+
*
|
|
8
|
+
* Read-only. Backed by GET /api/agent-os/{slug}/macro-controls.
|
|
9
|
+
*/
|
|
10
|
+
type ToolResult = {
|
|
11
|
+
content: {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}[];
|
|
15
|
+
structuredContent?: unknown;
|
|
16
|
+
};
|
|
17
|
+
export interface MacroControlsToolDeps {
|
|
18
|
+
register: (name: string, description: string, inputSchema: Record<string, unknown>, cb: (args: any) => any) => void;
|
|
19
|
+
resolveToolSlug: (explicitSlug?: string) => Promise<string>;
|
|
20
|
+
jsonResult: (data: unknown) => ToolResult;
|
|
21
|
+
}
|
|
22
|
+
export declare function registerMacroControlsTools({ register, resolveToolSlug, jsonResult, }: MacroControlsToolDeps): void;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Macro/social trend on-demand catch-up MCP tool.
|
|
3
|
+
*
|
|
4
|
+
* Runtime agents call agent_os_macro_controls_get before producing social media
|
|
5
|
+
* posts, ad copy, campaign briefs, launch announcements, or any trend-sensitive
|
|
6
|
+
* marketing content so they can adapt angle and tone to current signals.
|
|
7
|
+
*
|
|
8
|
+
* Read-only. Backed by GET /api/agent-os/{slug}/macro-controls.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { get } from "../lib/kynver-client.js";
|
|
12
|
+
const slugField = z
|
|
13
|
+
.string()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe("AgentOS slug. Defaults to env/primary AgentOS.");
|
|
16
|
+
export function registerMacroControlsTools({ register, resolveToolSlug, jsonResult, }) {
|
|
17
|
+
register("agent_os_macro_controls_get", [
|
|
18
|
+
"Fetch the current macro/social trend snapshot so you can adapt marketing content to active signals.",
|
|
19
|
+
"",
|
|
20
|
+
"WHEN TO CALL: before generating social media posts, ad copy, campaign briefs, launch announcements,",
|
|
21
|
+
"trend-reactive content, or any marketing material where current events could affect brand perception.",
|
|
22
|
+
"This is an on-demand catch-up step — call it at the START of social/content/marketing work, not on",
|
|
23
|
+
"every message.",
|
|
24
|
+
"",
|
|
25
|
+
"WHAT YOU GET: a compact MacroControlSnapshot with active MacroTrendSignals (topic, sensitivity,",
|
|
26
|
+
"summary, confidence, sourceLinks), a globalSensitivity aggregate ('safe'|'cautious'|'sensitive'|'blocked'),",
|
|
27
|
+
"isStale flag, and agentGuidance text you should incorporate into your content plan.",
|
|
28
|
+
"",
|
|
29
|
+
"GATING RULES:",
|
|
30
|
+
"- globalSensitivity='blocked': do NOT publish related content — surface the blocked topics to the",
|
|
31
|
+
" operator and wait for explicit approval before proceeding.",
|
|
32
|
+
"- globalSensitivity='sensitive': draft with extra care, avoid direct references to flagged topics,",
|
|
33
|
+
" prefer evergreen angles, and flag for human review before posting.",
|
|
34
|
+
"- globalSensitivity='cautious': note flagged topics in your plan; adjust tone but may proceed.",
|
|
35
|
+
"- globalSensitivity='safe' or isStale=true with no signals: proceed with standard brand voice.",
|
|
36
|
+
"",
|
|
37
|
+
"If isStale=true AND signals are present, treat the data as advisory only and note staleness in your",
|
|
38
|
+
"output so the operator knows to refresh. Read-only; safe for unattended invocation.",
|
|
39
|
+
].join("\n"), {
|
|
40
|
+
slug: slugField,
|
|
41
|
+
includeSensitiveSignals: z
|
|
42
|
+
.boolean()
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("When true, include 'sensitive' and 'blocked' signals in full detail. " +
|
|
45
|
+
"Default true — omit only when generating a safe-for-display summary."),
|
|
46
|
+
}, async ({ slug, includeSensitiveSignals = true, }) => {
|
|
47
|
+
const s = await resolveToolSlug(slug);
|
|
48
|
+
const snapshot = await get(`/agent-os/${encodeURIComponent(s)}/macro-controls`);
|
|
49
|
+
if (!includeSensitiveSignals && Array.isArray(snapshot.signals)) {
|
|
50
|
+
snapshot.signals = snapshot.signals.filter((sig) => sig.sensitivity === "safe" || sig.sensitivity === "cautious");
|
|
51
|
+
}
|
|
52
|
+
return jsonResult(snapshot);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { post } from "../lib/kynver-client.js";
|
|
3
|
+
export function registerPlanToFactoryTools({ register, resolveToolSlug, jsonResult, }) {
|
|
4
|
+
const slugField = z
|
|
5
|
+
.string()
|
|
6
|
+
.optional()
|
|
7
|
+
.describe("AgentOS workspace slug. Omit to use env or the server-discovered primary.");
|
|
8
|
+
register("agent_os_plan_run_factory", "Run a plan through the factory: decompose progress rows, mint parent + child harness tasks, wire dependencies and owned paths, and return the next runnable slice.", {
|
|
9
|
+
planId: z.string(),
|
|
10
|
+
dryRun: z.boolean().optional(),
|
|
11
|
+
command: z
|
|
12
|
+
.string()
|
|
13
|
+
.optional()
|
|
14
|
+
.describe('Operator phrase; defaults to "run this plan through the factory".'),
|
|
15
|
+
slug: slugField,
|
|
16
|
+
}, async (args) => {
|
|
17
|
+
const s = await resolveToolSlug(args.slug);
|
|
18
|
+
return jsonResult(await post(`/agent-os/${s}/plans/${args.planId}/run-factory`, {
|
|
19
|
+
dryRun: args.dryRun,
|
|
20
|
+
command: args.command,
|
|
21
|
+
}));
|
|
22
|
+
});
|
|
23
|
+
}
|
package/dist/tools/tasks.js
CHANGED
|
@@ -147,7 +147,7 @@ export function registerTaskTools({ register, resolveToolSlug, jsonResult, compa
|
|
|
147
147
|
scheduledFor,
|
|
148
148
|
dependsOnTaskIds,
|
|
149
149
|
lastSummary: z.string().optional().describe("Latest progress summary."),
|
|
150
|
-
blocker: z.string().optional(),
|
|
150
|
+
blocker: z.string().nullable().optional().describe("Blocker text, or null to clear it."),
|
|
151
151
|
branch: z.string().optional(),
|
|
152
152
|
worktreePath: z.string().optional(),
|
|
153
153
|
prUrl: z.string().optional(),
|
package/package.json
CHANGED