@kynver-app/mcp-agent-os 0.1.1 → 0.2.1
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/server.d.ts +3 -6
- package/dist/server.js +137 -45
- package/package.json +5 -2
package/dist/server.d.ts
CHANGED
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
* Kynver Agentic OS MCP server: slug-keyed agent identity, goals, projects,
|
|
3
3
|
* sessions, contacts, and long-term memory.
|
|
4
4
|
*
|
|
5
|
-
* Each tool proxies to `/api/agent-os/{slug}/*` on Kynver
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* twelve-tool `agent_os_*` family — previously colocated with `kynver-mcp-analyst`,
|
|
9
|
-
* split into its own package so each AgentOS deployment can mount this server
|
|
10
|
-
* independently from the analyst surface.
|
|
5
|
+
* Each tool proxies to `/api/agent-os/{slug}/*` on Kynver. The optional `slug`
|
|
6
|
+
* argument resolves in order: explicit arg → `KYNVER_AGENT_OS_SLUG` → cached
|
|
7
|
+
* `GET /api/agent-os` `{ primarySlug }` → list first item → legacy `"ghost"`.
|
|
11
8
|
*/
|
|
12
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
10
|
export declare function createAgentOsServer(): McpServer;
|
package/dist/server.js
CHANGED
|
@@ -2,49 +2,71 @@
|
|
|
2
2
|
* Kynver Agentic OS MCP server: slug-keyed agent identity, goals, projects,
|
|
3
3
|
* sessions, contacts, and long-term memory.
|
|
4
4
|
*
|
|
5
|
-
* Each tool proxies to `/api/agent-os/{slug}/*` on Kynver
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* twelve-tool `agent_os_*` family — previously colocated with `kynver-mcp-analyst`,
|
|
9
|
-
* split into its own package so each AgentOS deployment can mount this server
|
|
10
|
-
* independently from the analyst surface.
|
|
5
|
+
* Each tool proxies to `/api/agent-os/{slug}/*` on Kynver. The optional `slug`
|
|
6
|
+
* argument resolves in order: explicit arg → `KYNVER_AGENT_OS_SLUG` → cached
|
|
7
|
+
* `GET /api/agent-os` `{ primarySlug }` → list first item → legacy `"ghost"`.
|
|
11
8
|
*/
|
|
12
9
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
13
10
|
import { z } from "zod";
|
|
14
11
|
import { get, post, patch } from "./lib/kynver-client.js";
|
|
15
|
-
function toolResult(text) {
|
|
16
|
-
return { content: [{ type: "text", text }] };
|
|
17
|
-
}
|
|
18
12
|
function jsonResult(data) {
|
|
19
|
-
return
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
15
|
+
structuredContent: data,
|
|
16
|
+
};
|
|
20
17
|
}
|
|
18
|
+
const MCP_LEGACY_SLUG_FALLBACK = "ghost";
|
|
21
19
|
export function createAgentOsServer() {
|
|
22
|
-
const server = new McpServer({ name: "kynver-mcp-agent-os", version: "0.1
|
|
23
|
-
// The deprecated `server.tool(name, desc, zodShape, handler)` overloads
|
|
24
|
-
// generate a heavy generic type graph that OOMs tsc when many tools share
|
|
25
|
-
// one file. We use `registerTool` with an `as any` cast — same pattern the
|
|
26
|
-
// estimator/contents packages already use — so the build stays fast.
|
|
20
|
+
const server = new McpServer({ name: "kynver-mcp-agent-os", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
27
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
22
|
function register(name, description, inputSchema, cb) {
|
|
29
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
24
|
server.registerTool(name, { description, inputSchema }, cb);
|
|
31
25
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
let cachedResolvedPrimarySlug = null;
|
|
27
|
+
async function resolveToolSlug(explicitSlug) {
|
|
28
|
+
const trimmed = explicitSlug?.trim();
|
|
29
|
+
if (trimmed)
|
|
30
|
+
return trimmed;
|
|
31
|
+
const fromEnv = process.env.KYNVER_AGENT_OS_SLUG?.trim();
|
|
32
|
+
if (fromEnv)
|
|
33
|
+
return fromEnv;
|
|
34
|
+
if (cachedResolvedPrimarySlug)
|
|
35
|
+
return cachedResolvedPrimarySlug;
|
|
36
|
+
try {
|
|
37
|
+
const body = await get("/agent-os");
|
|
38
|
+
const primary = typeof body.primarySlug === "string" ? body.primarySlug.trim() : "";
|
|
39
|
+
if (primary) {
|
|
40
|
+
cachedResolvedPrimarySlug = primary;
|
|
41
|
+
return primary;
|
|
42
|
+
}
|
|
43
|
+
const first = Array.isArray(body.items) ? body.items[0]?.slug : undefined;
|
|
44
|
+
if (typeof first === "string") {
|
|
45
|
+
const t = first.trim();
|
|
46
|
+
if (t) {
|
|
47
|
+
cachedResolvedPrimarySlug = t;
|
|
48
|
+
return t;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// fall through
|
|
54
|
+
}
|
|
55
|
+
return MCP_LEGACY_SLUG_FALLBACK;
|
|
56
|
+
}
|
|
57
|
+
const slugField = z.string().optional().describe("AgentOS slug within your account. Omit to use env or server-discovered primary (GET /api/agent-os). Legacy: `ghost`.");
|
|
58
|
+
register("agent_os_get_context", "Get the agent's full Agentic-OS state in one call: identity, open goals, active projects with current focus/next-actions/blockers, recent sessions, contacts, and long-term memory stats. Use at session start instead of reading SOUL.md / USER.md / MEMORY.md.", { slug: slugField }, async (args) => {
|
|
59
|
+
const s = await resolveToolSlug(args.slug);
|
|
60
|
+
return jsonResult(await get(`/agent-os/${s}/stats`));
|
|
61
|
+
});
|
|
41
62
|
register("agent_os_open_session", "Call this at the START of every session. Records channel, model, and opens a session record for continuity. Ghost calls this immediately after agent_os_get_context on startup.", {
|
|
42
63
|
channel: z.string().describe("Runtime channel: 'webchat' | 'telegram' | 'discord' | …"),
|
|
43
64
|
model: z.string().optional().describe("Model used for the session (default 'claude-sonnet-4-6')."),
|
|
44
|
-
slug:
|
|
65
|
+
slug: slugField,
|
|
45
66
|
}, async (args) => {
|
|
46
67
|
const { slug, ...body } = args;
|
|
47
|
-
|
|
68
|
+
const s = await resolveToolSlug(slug);
|
|
69
|
+
return jsonResult(await post(`/agent-os/${s}/sessions`, body));
|
|
48
70
|
});
|
|
49
71
|
register("agent_os_close_session", "Call this at the END of every session with a summary of what was accomplished and key decisions made. This is how Ghost maintains cross-session continuity — without it, the next session has no record of what happened.", {
|
|
50
72
|
sessionId: z.string(),
|
|
@@ -53,17 +75,18 @@ export function createAgentOsServer() {
|
|
|
53
75
|
decisionsLog: z.unknown().optional(),
|
|
54
76
|
goalIds: z.array(z.string()).optional(),
|
|
55
77
|
projectIds: z.array(z.string()).optional(),
|
|
56
|
-
slug:
|
|
78
|
+
slug: slugField,
|
|
57
79
|
}, async (args) => {
|
|
58
80
|
const { slug, sessionId, ...body } = args;
|
|
59
|
-
|
|
81
|
+
const s = await resolveToolSlug(slug);
|
|
82
|
+
return jsonResult(await patch(`/agent-os/${s}/sessions/${sessionId}`, body));
|
|
60
83
|
});
|
|
61
84
|
register("agent_os_log_session", "Append a structured session-log entry to the agent's daily log. Call at session end or mid-session checkpoints. Replaces writing to memory/YYYY-MM-DD.md. Entries accumulate across sessions; consolidation distils them into long-term memory.", {
|
|
62
85
|
summary: z.string().describe("2-4 sentence summary of what was worked on."),
|
|
63
86
|
topicsWorked: z.array(z.string()).optional().describe("Topics covered this session."),
|
|
64
87
|
keyDecisions: z.array(z.string()).optional().describe("Important decisions made."),
|
|
65
88
|
date: z.string().optional().describe("Date to log against (YYYY-MM-DD, defaults to today UTC)."),
|
|
66
|
-
slug:
|
|
89
|
+
slug: slugField,
|
|
67
90
|
}, async (args) => {
|
|
68
91
|
const lines = [args.summary];
|
|
69
92
|
if (args.topicsWorked?.length)
|
|
@@ -73,7 +96,8 @@ export function createAgentOsServer() {
|
|
|
73
96
|
args.keyDecisions.forEach((d) => lines.push(`- ${d}`));
|
|
74
97
|
}
|
|
75
98
|
const entry = lines.join("\n");
|
|
76
|
-
|
|
99
|
+
const s = await resolveToolSlug(args.slug);
|
|
100
|
+
return jsonResult(await post(`/agent-os/${s}/daily-log`, {
|
|
77
101
|
entry,
|
|
78
102
|
...(args.date ? { date: args.date } : {}),
|
|
79
103
|
}));
|
|
@@ -84,7 +108,7 @@ export function createAgentOsServer() {
|
|
|
84
108
|
.optional()
|
|
85
109
|
.describe("Filter by status. Omit to get all goals."),
|
|
86
110
|
projectId: z.string().optional().describe("Filter by project DB ID."),
|
|
87
|
-
slug:
|
|
111
|
+
slug: slugField,
|
|
88
112
|
}, async (args) => {
|
|
89
113
|
const params = new URLSearchParams();
|
|
90
114
|
if (args.status)
|
|
@@ -92,7 +116,8 @@ export function createAgentOsServer() {
|
|
|
92
116
|
if (args.projectId)
|
|
93
117
|
params.set("projectId", args.projectId);
|
|
94
118
|
const qs = params.toString();
|
|
95
|
-
|
|
119
|
+
const s = await resolveToolSlug(args.slug);
|
|
120
|
+
return jsonResult(await get(`/agent-os/${s}/goals${qs ? `?${qs}` : ""}`));
|
|
96
121
|
});
|
|
97
122
|
register("agent_os_update_goal", "Create a new goal or update an existing one. Pass goalId to update; omit to create (title is then required). Set status=complete with an outcome to close a goal (stamps closedAt). projectId is the project DB ID from agent_os_get_projects.", {
|
|
98
123
|
title: z.string().optional().describe("Goal title. Required when creating."),
|
|
@@ -103,29 +128,31 @@ export function createAgentOsServer() {
|
|
|
103
128
|
analystHypothesisId: z.string().optional().describe("Optional analyst hypothesis to link (create-only)."),
|
|
104
129
|
outcome: z.string().optional().describe("Outcome — include when closing a goal."),
|
|
105
130
|
goalId: z.string().optional().describe("ID of existing goal to update. Omit to create."),
|
|
106
|
-
slug:
|
|
131
|
+
slug: slugField,
|
|
107
132
|
}, async (args) => {
|
|
108
133
|
const { goalId, slug, ...body } = args;
|
|
134
|
+
const s = await resolveToolSlug(slug);
|
|
109
135
|
if (goalId) {
|
|
110
|
-
return jsonResult(await patch(`/agent-os/${
|
|
136
|
+
return jsonResult(await patch(`/agent-os/${s}/goals/${goalId}`, body));
|
|
111
137
|
}
|
|
112
138
|
if (!body.title) {
|
|
113
139
|
return jsonResult({ error: "title is required when creating a goal" });
|
|
114
140
|
}
|
|
115
|
-
return jsonResult(await post(`/agent-os/${
|
|
141
|
+
return jsonResult(await post(`/agent-os/${s}/goals`, body));
|
|
116
142
|
});
|
|
117
143
|
register("agent_os_get_projects", "Get all projects the agent is tracking with current focus, next actions, blockers, and status. Replaces reading project sections of MEMORY.md.", {
|
|
118
144
|
status: z
|
|
119
145
|
.enum(["active", "on_hold", "shipped", "archived"])
|
|
120
146
|
.optional()
|
|
121
147
|
.describe("Filter by project status. Omit to get all."),
|
|
122
|
-
slug:
|
|
148
|
+
slug: slugField,
|
|
123
149
|
}, async (args) => {
|
|
124
150
|
const params = new URLSearchParams();
|
|
125
151
|
if (args.status)
|
|
126
152
|
params.set("status", args.status);
|
|
127
153
|
const qs = params.toString();
|
|
128
|
-
|
|
154
|
+
const s = await resolveToolSlug(args.slug);
|
|
155
|
+
return jsonResult(await get(`/agent-os/${s}/projects${qs ? `?${qs}` : ""}`));
|
|
129
156
|
});
|
|
130
157
|
register("agent_os_update_project", "Update a project's current focus, next-actions queue, blockers list, status, or repo/deploy URLs. The projectId is the DB ID from agent_os_get_projects.", {
|
|
131
158
|
projectId: z.string().describe("Project DB ID (from agent_os_get_projects)."),
|
|
@@ -137,20 +164,22 @@ export function createAgentOsServer() {
|
|
|
137
164
|
blockers: z.array(z.string()).optional().describe("Current blockers."),
|
|
138
165
|
repoUrl: z.string().optional(),
|
|
139
166
|
deployUrl: z.string().optional(),
|
|
140
|
-
slug:
|
|
167
|
+
slug: slugField,
|
|
141
168
|
}, async (args) => {
|
|
142
169
|
const { projectId, slug, ...body } = args;
|
|
143
|
-
|
|
170
|
+
const s = await resolveToolSlug(slug);
|
|
171
|
+
return jsonResult(await patch(`/agent-os/${s}/projects/${projectId}`, body));
|
|
144
172
|
});
|
|
145
173
|
register("agent_os_search_memory", "Semantic + keyword hybrid search over the agent's long-term MARM memory (ownerType=agent). Use to recall specific decisions, project details, or lessons without loading full context. Replaces grep/reading MEMORY.md. Results include rrfScore — a Reciprocal Rank Fusion score where ~0.033 is a near-perfect match. Higher is better; do not treat low numbers as poor results.", {
|
|
146
174
|
query: z.string().describe("Natural language query to search memory."),
|
|
147
175
|
k: z.number().optional().describe("Number of results (default 10, max 50)."),
|
|
148
|
-
slug:
|
|
176
|
+
slug: slugField,
|
|
149
177
|
}, async (args) => {
|
|
150
178
|
const params = new URLSearchParams({ q: args.query });
|
|
151
179
|
if (args.k)
|
|
152
180
|
params.set("k", String(args.k));
|
|
153
|
-
|
|
181
|
+
const s = await resolveToolSlug(args.slug);
|
|
182
|
+
return jsonResult(await get(`/agent-os/${s}/memory?${params}`));
|
|
154
183
|
});
|
|
155
184
|
register("agent_os_write_memory", "Write a durable memory entry to the agent's MARM. Use to persist decisions, lessons, or key facts across sessions. Replaces appending to MEMORY.md. Either pass a category enum (mapped to sourceId) or override sourceId directly.", {
|
|
156
185
|
content: z.string().describe("The memory content to store."),
|
|
@@ -161,7 +190,7 @@ export function createAgentOsServer() {
|
|
|
161
190
|
.describe("Memory category. Mapped to sourceId: long_term/contact/tool_config -> 'agent:long-term', project -> 'agent:project'. Ignored if sourceId is set."),
|
|
162
191
|
sourceId: z.string().optional().describe("Advanced: override the sourceId tag directly. If set, category is ignored."),
|
|
163
192
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
164
|
-
slug:
|
|
193
|
+
slug: slugField,
|
|
165
194
|
}, async (args) => {
|
|
166
195
|
const categoryToSourceId = {
|
|
167
196
|
long_term: "agent:long-term",
|
|
@@ -177,9 +206,72 @@ export function createAgentOsServer() {
|
|
|
177
206
|
body.sourceId = sourceId;
|
|
178
207
|
if (args.metadata)
|
|
179
208
|
body.metadata = args.metadata;
|
|
180
|
-
|
|
209
|
+
const s = await resolveToolSlug(args.slug);
|
|
210
|
+
return jsonResult(await post(`/agent-os/${s}/memory`, body));
|
|
211
|
+
});
|
|
212
|
+
register("agent_os_get_contacts", "Get all people the agent knows: their relationship, context, preferences, and notes. Replaces reading USER.md and contact sections of MEMORY.md.", { slug: slugField }, async (args) => {
|
|
213
|
+
const s = await resolveToolSlug(args.slug);
|
|
214
|
+
return jsonResult(await get(`/agent-os/${s}/contacts`));
|
|
215
|
+
});
|
|
216
|
+
register("agent_os_consolidate_memory", "Trigger a memory consolidation pass: LLM reviews recent daily logs and distils key decisions, lessons, and facts into long-term MARM memory. Use during heartbeat maintenance instead of manually rewriting MEMORY.md. Rate-limited to 2 runs per agent per day.", { slug: slugField }, async (args) => {
|
|
217
|
+
const s = await resolveToolSlug(args.slug);
|
|
218
|
+
return jsonResult(await post(`/agent-os/${s}/consolidate`, {}));
|
|
219
|
+
});
|
|
220
|
+
register("agent_os_create_project", "Create a new project in the agent OS. Use this to add a newly started project to tracking. Returns the created project with its DB id.", {
|
|
221
|
+
name: z.string().describe("Project name (required)."),
|
|
222
|
+
description: z.string().optional(),
|
|
223
|
+
status: z.enum(["active", "on_hold", "shipped", "archived"]).optional().default("active"),
|
|
224
|
+
currentFocus: z.string().optional().describe("What is being worked on right now."),
|
|
225
|
+
nextActions: z.array(z.string()).optional().describe("Ordered list of next steps."),
|
|
226
|
+
blockers: z.array(z.string()).optional(),
|
|
227
|
+
repoUrl: z.string().optional(),
|
|
228
|
+
deployUrl: z.string().optional(),
|
|
229
|
+
slug: slugField,
|
|
230
|
+
}, async (args) => {
|
|
231
|
+
const { slug, ...body } = args;
|
|
232
|
+
const s = await resolveToolSlug(slug);
|
|
233
|
+
return jsonResult(await post(`/agent-os/${s}/projects`, body));
|
|
234
|
+
});
|
|
235
|
+
register("agent_os_create_contact", "Add a new contact (or upsert by name) to the agent OS. Use when you meet someone new or want to record info about a person.", {
|
|
236
|
+
name: z.string().describe("Contact name (required)."),
|
|
237
|
+
relationship: z.string().optional().describe("e.g. owner, collaborator, client, stakeholder"),
|
|
238
|
+
context: z.string().optional().describe("Background on who they are and what they do."),
|
|
239
|
+
preferences: z.record(z.string(), z.unknown()).optional().describe("Communication preferences, timezone, style notes, etc."),
|
|
240
|
+
notes: z.string().optional(),
|
|
241
|
+
slug: slugField,
|
|
242
|
+
}, async (args) => {
|
|
243
|
+
const { slug, ...body } = args;
|
|
244
|
+
const s = await resolveToolSlug(slug);
|
|
245
|
+
return jsonResult(await post(`/agent-os/${s}/contacts`, body));
|
|
246
|
+
});
|
|
247
|
+
register("agent_os_update_contact", "Update an existing contact by their DB id. Use agent_os_get_contacts to get the id first.", {
|
|
248
|
+
contactId: z.string().describe("DB id of the contact to update (from agent_os_get_contacts)."),
|
|
249
|
+
name: z.string().optional(),
|
|
250
|
+
relationship: z.string().optional(),
|
|
251
|
+
context: z.string().optional(),
|
|
252
|
+
preferences: z.record(z.string(), z.unknown()).optional(),
|
|
253
|
+
notes: z.string().optional(),
|
|
254
|
+
slug: slugField,
|
|
255
|
+
}, async (args) => {
|
|
256
|
+
const { contactId, slug, ...body } = args;
|
|
257
|
+
const s = await resolveToolSlug(slug);
|
|
258
|
+
return jsonResult(await patch(`/agent-os/${s}/contacts/${contactId}`, body));
|
|
259
|
+
});
|
|
260
|
+
register("agent_os_update_memory", "Update an existing memory entry by its key/slug. Use to correct or replace a previously written memory. The key must match the slug used when the memory was written — if key was omitted on write, the auto-generated slug (note-<timestamp>) is returned in the write response.", {
|
|
261
|
+
key: z.string().describe("The slug/key of the memory to update (from the original agent_os_write_memory call)."),
|
|
262
|
+
content: z.string().describe("New content to replace the existing memory with."),
|
|
263
|
+
sourceId: z.string().optional().describe("Override the sourceId tag if needed."),
|
|
264
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
265
|
+
slug: slugField,
|
|
266
|
+
}, async (args) => {
|
|
267
|
+
const { key, slug, content, sourceId, metadata } = args;
|
|
268
|
+
const body = { content, slug: key };
|
|
269
|
+
if (sourceId)
|
|
270
|
+
body.sourceId = sourceId;
|
|
271
|
+
if (metadata)
|
|
272
|
+
body.metadata = metadata;
|
|
273
|
+
const s = await resolveToolSlug(slug);
|
|
274
|
+
return jsonResult(await post(`/agent-os/${s}/memory`, body));
|
|
181
275
|
});
|
|
182
|
-
register("agent_os_get_contacts", "Get all people the agent knows: their relationship, context, preferences, and notes. Replaces reading USER.md and contact sections of MEMORY.md.", { slug: z.string().optional() }, async (args) => jsonResult(await get(`/agent-os/${osSlug(args.slug)}/contacts`)));
|
|
183
|
-
register("agent_os_consolidate_memory", "Trigger a memory consolidation pass: LLM reviews recent daily logs and distils key decisions, lessons, and facts into long-term MARM memory. Use during heartbeat maintenance instead of manually rewriting MEMORY.md. Rate-limited to 2 runs per agent per day.", { slug: z.string().optional() }, async (args) => jsonResult(await post(`/agent-os/${osSlug(args.slug)}/consolidate`, {})));
|
|
184
276
|
return server;
|
|
185
277
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kynver-app/mcp-agent-os",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.1",
|
|
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": {
|
|
7
7
|
"kynver-mcp-agent-os": "dist/index.js"
|
|
8
8
|
},
|
|
9
|
-
"files": [
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
10
13
|
"scripts": {
|
|
11
14
|
"build": "node --max-old-space-size=8192 ../../node_modules/typescript/bin/tsc",
|
|
12
15
|
"dev": "node --max-old-space-size=8192 ../../node_modules/typescript/bin/tsc --watch",
|