@kynver-app/mcp-agent-os 0.3.109 → 0.3.112
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/README.md +201 -183
- package/SECURITY.md +31 -31
- package/dist/agent-chat.js +2 -0
- package/dist/index.js +4 -4
- package/dist/sse.js +4 -4
- package/dist/worker.js +0 -0
- package/package.json +48 -47
package/README.md
CHANGED
|
@@ -1,183 +1,201 @@
|
|
|
1
|
-
# @kynver-app/mcp-agent-os
|
|
2
|
-
|
|
3
|
-
Kynver Agentic OS MCP server. Exposes the `agent_os_*` family that
|
|
4
|
-
proxies to `/api/agent-os/{slug}/*` on a Kynver deployment. Each tool accepts
|
|
5
|
-
an optional `slug` argument; if omitted, it falls back to
|
|
6
|
-
`KYNVER_AGENT_OS_SLUG` env, then the account's primary AgentOS slug, then to `"ghost"`.
|
|
7
|
-
|
|
8
|
-
This is the **external surface only**. The in-app Kynver agent does not
|
|
9
|
-
dispatch these tools — they exist for MCP clients (Claude Code, Cursor,
|
|
10
|
-
OpenClaw, etc.) that connect to a running Kynver deployment.
|
|
11
|
-
|
|
12
|
-
## Tools
|
|
13
|
-
|
|
14
|
-
The full family:
|
|
15
|
-
|
|
16
|
-
- `agent_os_get_context`
|
|
17
|
-
- `agent_os_open_session`
|
|
18
|
-
- `agent_os_close_session`
|
|
19
|
-
- `agent_os_log_session_event`
|
|
20
|
-
- `agent_os_log_session`
|
|
21
|
-
- `agent_os_list_goals`
|
|
22
|
-
- `agent_os_update_goal`
|
|
23
|
-
- `agent_os_get_projects`
|
|
24
|
-
- `agent_os_create_project`
|
|
25
|
-
- `agent_os_update_project`
|
|
26
|
-
- `agent_os_search_memory`
|
|
27
|
-
- `agent_os_write_memory`
|
|
28
|
-
- `agent_os_update_memory`
|
|
29
|
-
- `agent_os_list_skills`
|
|
30
|
-
- `agent_os_get_skill`
|
|
31
|
-
- `agent_os_create_skill`
|
|
32
|
-
- `agent_os_update_skill`
|
|
33
|
-
- `agent_os_import_skills`
|
|
34
|
-
- `agent_os_bind_skill`
|
|
35
|
-
- `agent_os_write_skill_memory`
|
|
36
|
-
- `agent_os_get_contacts`
|
|
37
|
-
- `agent_os_create_contact`
|
|
38
|
-
- `agent_os_update_contact`
|
|
39
|
-
- `agent_os_consolidate_memory`
|
|
40
|
-
|
|
41
|
-
AgentTask board (Phase 2 — batch 1 CRUD + batch 1.5 steering/lease):
|
|
42
|
-
|
|
43
|
-
- `agent_os_task_create`
|
|
44
|
-
- `agent_os_task_get`
|
|
45
|
-
- `agent_os_task_list`
|
|
46
|
-
- `agent_os_task_update`
|
|
47
|
-
- `agent_os_task_log_event`
|
|
48
|
-
- `agent_os_task_close`
|
|
49
|
-
- `agent_os_task_steer`
|
|
50
|
-
- `agent_os_task_abort`
|
|
51
|
-
- `agent_os_task_claim`
|
|
52
|
-
- `agent_os_task_renew_lease`
|
|
53
|
-
- `agent_os_task_operator_priority`
|
|
54
|
-
|
|
55
|
-
AgentPlan first-class plans (Phase 5A — versioned operational artifacts
|
|
56
|
-
separate from goals/projects/tasks):
|
|
57
|
-
|
|
58
|
-
- `agent_os_plan_create`
|
|
59
|
-
- `agent_os_plan_list`
|
|
60
|
-
- `agent_os_plan_get`
|
|
61
|
-
- `agent_os_plan_update`
|
|
62
|
-
- `agent_os_plan_add_version`
|
|
63
|
-
- `agent_os_plan_mark_current`
|
|
64
|
-
- `agent_os_plan_add_link`
|
|
65
|
-
- `agent_os_plan_list_links`
|
|
66
|
-
- `agent_os_plan_list_versions`
|
|
67
|
-
|
|
68
|
-
Schemas are mirrored in `lib/mcp/tool-manifests/kynver-mcp-agent-os.json` in
|
|
69
|
-
the Kynver repo (used as the static fallback when stdio spawn isn't available
|
|
70
|
-
— e.g. on Vercel).
|
|
71
|
-
|
|
72
|
-
Memory write/update tools are the memory-writer surface. They accept
|
|
73
|
-
`sourceRefs` for structured provenance plus `memoryType`, `confidence`,
|
|
74
|
-
`reviewStatus`, `projectId`, `goalId`, `contactId`, and `skillId`. Use those fields to
|
|
75
|
-
attach repo paths, commits, PRs, map files, sessions, tools, URLs, and review
|
|
76
|
-
state to important memories instead of burying those pointers in freeform text.
|
|
77
|
-
Set `reviewStatus: "needs_review"` for important or uncertain memories that a
|
|
78
|
-
human should be able to find and correct in the dashboard.
|
|
79
|
-
|
|
80
|
-
Active-state and correction tools expose the "what is true right now" layer:
|
|
81
|
-
`agent_os_record_state` / `agent_os_get_state` manage one current
|
|
82
|
-
`active_state` row per live PR, branch, or deployment, while
|
|
83
|
-
`agent_os_correct_memory` writes an auditable correction that supersedes stale
|
|
84
|
-
or wrong remembered claims instead of silently editing history.
|
|
85
|
-
|
|
86
|
-
Skill tools expose the AgentOS skill registry and skill-tagged memory contract.
|
|
87
|
-
Use `agent_os_get_skill` to fetch instructions on demand, `agent_os_create_skill`
|
|
88
|
-
/`agent_os_update_skill` to manage user-authored skills, `agent_os_import_skills`
|
|
89
|
-
to import external skill markdown/JSON as draft unbound user skills, `agent_os_bind_skill`
|
|
90
|
-
to enable or update a workspace binding, and `agent_os_write_skill_memory` for
|
|
91
|
-
lessons learned while applying a skill. Built-in skill memories use the built-in
|
|
92
|
-
slug as `skillId`; user-authored skill memories use `AgentSkill.id`.
|
|
93
|
-
|
|
94
|
-
`agent_os_log_session_event` appends structured session events. Close-session
|
|
95
|
-
summaries can then be synthesized from actual topics, actions, decisions, and
|
|
96
|
-
follow-ups instead of generic lifecycle text.
|
|
97
|
-
|
|
98
|
-
AgentPlan tools manage first-class plans — versioned operational artifacts
|
|
99
|
-
distinct from goals/projects/tasks. Each `AgentPlan` owns a chain of immutable
|
|
100
|
-
`AgentPlanVersion` snapshots (one stamped current at a time) plus typed
|
|
101
|
-
`AgentPlanLink` rows pointing at goals/projects/tasks/memories/sessions/plans
|
|
102
|
-
or remote refs (repo/branch/commit/PR/URL/markdown). The plan body itself is
|
|
103
|
-
immutable — to record a new snapshot call `agent_os_plan_add_version`, which
|
|
104
|
-
appends a new version and (by default) advances `currentVersionId` atomically.
|
|
105
|
-
Use `planSlug` for the plan's own stable slug; the workspace `slug` argument
|
|
106
|
-
is reserved for the AgentOS workspace. Workspace-owned link targets are
|
|
107
|
-
re-validated server-side against the same AgentOS workspace; remote refs
|
|
108
|
-
carry metadata only.
|
|
109
|
-
|
|
110
|
-
AgentTask tools expose the durable task board. `agent_os_task_create` is
|
|
111
|
-
idempotent within an AgentOS — pass `idempotencyKey` (or `requestId`) from the
|
|
112
|
-
foreground turn so a retry or restart returns the existing task instead of
|
|
113
|
-
spawning a duplicate. `agent_os_task_log_event` accepts a stable `eventKey` so a
|
|
114
|
-
board-down executor can replay its local event spool without creating
|
|
115
|
-
duplicates; `artifact` events take an `artifactVisibility` tier (`internal` |
|
|
116
|
-
`telegram_safe` | `public_pr_safe`, default `internal`). `agent_os_task_claim`
|
|
117
|
-
and `agent_os_task_renew_lease` implement the lease model — an executor must
|
|
118
|
-
hold a live lease to own a task, and a claim is an atomic conditional update
|
|
119
|
-
that increments `attempt`.
|
|
120
|
-
|
|
121
|
-
CMO marketing knowledge graph (shared doctrine; reads for any workspace member, writes
|
|
122
|
-
platform-operator only, DRY RUN unless `apply: true`, every write audited with a reason):
|
|
123
|
-
|
|
124
|
-
- `agent_os_cmo_context_pack_get`, `agent_os_cmo_node_get`, `agent_os_cmo_control_status_get` (existing reads)
|
|
125
|
-
- `agent_os_cmo_lesson_record` (existing upsert; `confirm: true` writes)
|
|
126
|
-
- `agent_os_cmo_graph_search`, `agent_os_cmo_graph_get_node`, `agent_os_cmo_graph_list_domains`
|
|
127
|
-
- `agent_os_cmo_routes_list`, `agent_os_cmo_context_pack_preview`
|
|
128
|
-
- `agent_os_cmo_lesson_propose`, `agent_os_cmo_lesson_promote`, `agent_os_cmo_lesson_retire` (`restore: true` undoes)
|
|
129
|
-
- `agent_os_cmo_edge_upsert`, `agent_os_cmo_edge_remove`
|
|
130
|
-
- `agent_os_cmo_route_upsert`, `agent_os_cmo_route_remove` (domain lanes + probe routes live in the database — no deploy)
|
|
131
|
-
- `agent_os_cmo_settings_set` (`pack.maxNodesPerSlot`, default 8)
|
|
132
|
-
- `agent_os_cmo_lessons_export` (backup PR of `docs/cmo-lessons/`; `schedule: true` for nightly)
|
|
133
|
-
- `audienceProfile` on `agent_os_cmo_context_pack_get` / `_preview` scopes a pack to one channel's `AudienceSegment` profile
|
|
134
|
-
- `agent_os_cmo_applied_record` (which pack nodes a piece of content applied + optional metrics; workspace data — owner key,
|
|
135
|
-
strict no-PII schema, idempotent per sourceSystem + contentRef + metricsWindow, dry run unless `apply: true`, audited),
|
|
136
|
-
`agent_os_cmo_applied_list`, `agent_os_cmo_node_outcomes` (per-node medians, this workspace, read-only),
|
|
137
|
-
`agent_os_cmo_outcome_weighting` (per-workspace opt-in outcome-weighted ranking, off by default)
|
|
138
|
-
|
|
139
|
-
## Env
|
|
140
|
-
|
|
141
|
-
| Var | Purpose |
|
|
142
|
-
|---|---|
|
|
143
|
-
| `KYNVER_API_URL` | Kynver origin, e.g. `https://kynver.com` |
|
|
144
|
-
| `KYNVER_API_KEY` | Bearer token for external authentication |
|
|
145
|
-
| `KYNVER_AGENT_OS_SLUG` | Default agent slug when the tool's `slug` arg is omitted (defaults to `"ghost"`) |
|
|
146
|
-
| `KYNVER_SERVICE_SECRET` | Required when running under SSE and forwarding internal `userId` headers |
|
|
147
|
-
| `MCP_SSE_HOST` | SSE bind address (defaults to `127.0.0.1`) |
|
|
148
|
-
| `PORT` | SSE port (defaults to `3101`) |
|
|
149
|
-
|
|
150
|
-
## OpenClaw / Claude Code config
|
|
151
|
-
|
|
152
|
-
Once this package is published, the Ghost workspace's MCP config needs **two
|
|
153
|
-
separate** Kynver MCP server entries — one for the analyst surface and one for
|
|
154
|
-
this Agentic-OS surface:
|
|
155
|
-
|
|
156
|
-
```jsonc
|
|
157
|
-
{
|
|
158
|
-
"mcpServers": {
|
|
159
|
-
"kynver-analyst": {
|
|
160
|
-
"command": "npx",
|
|
161
|
-
"args": ["-y", "@kynver-app/mcp-analyst"],
|
|
162
|
-
"env": {
|
|
163
|
-
"KYNVER_API_URL": "https://kynver.com",
|
|
164
|
-
"KYNVER_API_KEY": "${KYNVER_API_KEY}"
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
"kynver-agent-os": {
|
|
168
|
-
"command": "npx",
|
|
169
|
-
"args": ["-y", "@kynver-app/mcp-agent-os"],
|
|
170
|
-
"env": {
|
|
171
|
-
"KYNVER_API_URL": "https://kynver.com",
|
|
172
|
-
"KYNVER_API_KEY": "${KYNVER_API_KEY}",
|
|
173
|
-
"KYNVER_AGENT_OS_SLUG": "ghost"
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
The split mirrors the server-side change of 2026-05-14: the `agent_os_*`
|
|
181
|
-
family was extracted from `@kynver-app/mcp-analyst` into this dedicated
|
|
182
|
-
package so each AgentOS deployment can mount it independently from the
|
|
183
|
-
analyst tools.
|
|
1
|
+
# @kynver-app/mcp-agent-os
|
|
2
|
+
|
|
3
|
+
Kynver Agentic OS MCP server. Exposes the `agent_os_*` family that
|
|
4
|
+
proxies to `/api/agent-os/{slug}/*` on a Kynver deployment. Each tool accepts
|
|
5
|
+
an optional `slug` argument; if omitted, it falls back to
|
|
6
|
+
`KYNVER_AGENT_OS_SLUG` env, then the account's primary AgentOS slug, then to `"ghost"`.
|
|
7
|
+
|
|
8
|
+
This is the **external surface only**. The in-app Kynver agent does not
|
|
9
|
+
dispatch these tools — they exist for MCP clients (Claude Code, Cursor,
|
|
10
|
+
OpenClaw, etc.) that connect to a running Kynver deployment.
|
|
11
|
+
|
|
12
|
+
## Tools
|
|
13
|
+
|
|
14
|
+
The full family:
|
|
15
|
+
|
|
16
|
+
- `agent_os_get_context`
|
|
17
|
+
- `agent_os_open_session`
|
|
18
|
+
- `agent_os_close_session`
|
|
19
|
+
- `agent_os_log_session_event`
|
|
20
|
+
- `agent_os_log_session`
|
|
21
|
+
- `agent_os_list_goals`
|
|
22
|
+
- `agent_os_update_goal`
|
|
23
|
+
- `agent_os_get_projects`
|
|
24
|
+
- `agent_os_create_project`
|
|
25
|
+
- `agent_os_update_project`
|
|
26
|
+
- `agent_os_search_memory`
|
|
27
|
+
- `agent_os_write_memory`
|
|
28
|
+
- `agent_os_update_memory`
|
|
29
|
+
- `agent_os_list_skills`
|
|
30
|
+
- `agent_os_get_skill`
|
|
31
|
+
- `agent_os_create_skill`
|
|
32
|
+
- `agent_os_update_skill`
|
|
33
|
+
- `agent_os_import_skills`
|
|
34
|
+
- `agent_os_bind_skill`
|
|
35
|
+
- `agent_os_write_skill_memory`
|
|
36
|
+
- `agent_os_get_contacts`
|
|
37
|
+
- `agent_os_create_contact`
|
|
38
|
+
- `agent_os_update_contact`
|
|
39
|
+
- `agent_os_consolidate_memory`
|
|
40
|
+
|
|
41
|
+
AgentTask board (Phase 2 — batch 1 CRUD + batch 1.5 steering/lease):
|
|
42
|
+
|
|
43
|
+
- `agent_os_task_create`
|
|
44
|
+
- `agent_os_task_get`
|
|
45
|
+
- `agent_os_task_list`
|
|
46
|
+
- `agent_os_task_update`
|
|
47
|
+
- `agent_os_task_log_event`
|
|
48
|
+
- `agent_os_task_close`
|
|
49
|
+
- `agent_os_task_steer`
|
|
50
|
+
- `agent_os_task_abort`
|
|
51
|
+
- `agent_os_task_claim`
|
|
52
|
+
- `agent_os_task_renew_lease`
|
|
53
|
+
- `agent_os_task_operator_priority`
|
|
54
|
+
|
|
55
|
+
AgentPlan first-class plans (Phase 5A — versioned operational artifacts
|
|
56
|
+
separate from goals/projects/tasks):
|
|
57
|
+
|
|
58
|
+
- `agent_os_plan_create`
|
|
59
|
+
- `agent_os_plan_list`
|
|
60
|
+
- `agent_os_plan_get`
|
|
61
|
+
- `agent_os_plan_update`
|
|
62
|
+
- `agent_os_plan_add_version`
|
|
63
|
+
- `agent_os_plan_mark_current`
|
|
64
|
+
- `agent_os_plan_add_link`
|
|
65
|
+
- `agent_os_plan_list_links`
|
|
66
|
+
- `agent_os_plan_list_versions`
|
|
67
|
+
|
|
68
|
+
Schemas are mirrored in `lib/mcp/tool-manifests/kynver-mcp-agent-os.json` in
|
|
69
|
+
the Kynver repo (used as the static fallback when stdio spawn isn't available
|
|
70
|
+
— e.g. on Vercel).
|
|
71
|
+
|
|
72
|
+
Memory write/update tools are the memory-writer surface. They accept
|
|
73
|
+
`sourceRefs` for structured provenance plus `memoryType`, `confidence`,
|
|
74
|
+
`reviewStatus`, `projectId`, `goalId`, `contactId`, and `skillId`. Use those fields to
|
|
75
|
+
attach repo paths, commits, PRs, map files, sessions, tools, URLs, and review
|
|
76
|
+
state to important memories instead of burying those pointers in freeform text.
|
|
77
|
+
Set `reviewStatus: "needs_review"` for important or uncertain memories that a
|
|
78
|
+
human should be able to find and correct in the dashboard.
|
|
79
|
+
|
|
80
|
+
Active-state and correction tools expose the "what is true right now" layer:
|
|
81
|
+
`agent_os_record_state` / `agent_os_get_state` manage one current
|
|
82
|
+
`active_state` row per live PR, branch, or deployment, while
|
|
83
|
+
`agent_os_correct_memory` writes an auditable correction that supersedes stale
|
|
84
|
+
or wrong remembered claims instead of silently editing history.
|
|
85
|
+
|
|
86
|
+
Skill tools expose the AgentOS skill registry and skill-tagged memory contract.
|
|
87
|
+
Use `agent_os_get_skill` to fetch instructions on demand, `agent_os_create_skill`
|
|
88
|
+
/`agent_os_update_skill` to manage user-authored skills, `agent_os_import_skills`
|
|
89
|
+
to import external skill markdown/JSON as draft unbound user skills, `agent_os_bind_skill`
|
|
90
|
+
to enable or update a workspace binding, and `agent_os_write_skill_memory` for
|
|
91
|
+
lessons learned while applying a skill. Built-in skill memories use the built-in
|
|
92
|
+
slug as `skillId`; user-authored skill memories use `AgentSkill.id`.
|
|
93
|
+
|
|
94
|
+
`agent_os_log_session_event` appends structured session events. Close-session
|
|
95
|
+
summaries can then be synthesized from actual topics, actions, decisions, and
|
|
96
|
+
follow-ups instead of generic lifecycle text.
|
|
97
|
+
|
|
98
|
+
AgentPlan tools manage first-class plans — versioned operational artifacts
|
|
99
|
+
distinct from goals/projects/tasks. Each `AgentPlan` owns a chain of immutable
|
|
100
|
+
`AgentPlanVersion` snapshots (one stamped current at a time) plus typed
|
|
101
|
+
`AgentPlanLink` rows pointing at goals/projects/tasks/memories/sessions/plans
|
|
102
|
+
or remote refs (repo/branch/commit/PR/URL/markdown). The plan body itself is
|
|
103
|
+
immutable — to record a new snapshot call `agent_os_plan_add_version`, which
|
|
104
|
+
appends a new version and (by default) advances `currentVersionId` atomically.
|
|
105
|
+
Use `planSlug` for the plan's own stable slug; the workspace `slug` argument
|
|
106
|
+
is reserved for the AgentOS workspace. Workspace-owned link targets are
|
|
107
|
+
re-validated server-side against the same AgentOS workspace; remote refs
|
|
108
|
+
carry metadata only.
|
|
109
|
+
|
|
110
|
+
AgentTask tools expose the durable task board. `agent_os_task_create` is
|
|
111
|
+
idempotent within an AgentOS — pass `idempotencyKey` (or `requestId`) from the
|
|
112
|
+
foreground turn so a retry or restart returns the existing task instead of
|
|
113
|
+
spawning a duplicate. `agent_os_task_log_event` accepts a stable `eventKey` so a
|
|
114
|
+
board-down executor can replay its local event spool without creating
|
|
115
|
+
duplicates; `artifact` events take an `artifactVisibility` tier (`internal` |
|
|
116
|
+
`telegram_safe` | `public_pr_safe`, default `internal`). `agent_os_task_claim`
|
|
117
|
+
and `agent_os_task_renew_lease` implement the lease model — an executor must
|
|
118
|
+
hold a live lease to own a task, and a claim is an atomic conditional update
|
|
119
|
+
that increments `attempt`.
|
|
120
|
+
|
|
121
|
+
CMO marketing knowledge graph (shared doctrine; reads for any workspace member, writes
|
|
122
|
+
platform-operator only, DRY RUN unless `apply: true`, every write audited with a reason):
|
|
123
|
+
|
|
124
|
+
- `agent_os_cmo_context_pack_get`, `agent_os_cmo_node_get`, `agent_os_cmo_control_status_get` (existing reads)
|
|
125
|
+
- `agent_os_cmo_lesson_record` (existing upsert; `confirm: true` writes)
|
|
126
|
+
- `agent_os_cmo_graph_search`, `agent_os_cmo_graph_get_node`, `agent_os_cmo_graph_list_domains`
|
|
127
|
+
- `agent_os_cmo_routes_list`, `agent_os_cmo_context_pack_preview`
|
|
128
|
+
- `agent_os_cmo_lesson_propose`, `agent_os_cmo_lesson_promote`, `agent_os_cmo_lesson_retire` (`restore: true` undoes)
|
|
129
|
+
- `agent_os_cmo_edge_upsert`, `agent_os_cmo_edge_remove`
|
|
130
|
+
- `agent_os_cmo_route_upsert`, `agent_os_cmo_route_remove` (domain lanes + probe routes live in the database — no deploy)
|
|
131
|
+
- `agent_os_cmo_settings_set` (`pack.maxNodesPerSlot`, default 8)
|
|
132
|
+
- `agent_os_cmo_lessons_export` (backup PR of `docs/cmo-lessons/`; `schedule: true` for nightly)
|
|
133
|
+
- `audienceProfile` on `agent_os_cmo_context_pack_get` / `_preview` scopes a pack to one channel's `AudienceSegment` profile
|
|
134
|
+
- `agent_os_cmo_applied_record` (which pack nodes a piece of content applied + optional metrics; workspace data — owner key,
|
|
135
|
+
strict no-PII schema, idempotent per sourceSystem + contentRef + metricsWindow, dry run unless `apply: true`, audited),
|
|
136
|
+
`agent_os_cmo_applied_list`, `agent_os_cmo_node_outcomes` (per-node medians, this workspace, read-only),
|
|
137
|
+
`agent_os_cmo_outcome_weighting` (per-workspace opt-in outcome-weighted ranking, off by default)
|
|
138
|
+
|
|
139
|
+
## Env
|
|
140
|
+
|
|
141
|
+
| Var | Purpose |
|
|
142
|
+
|---|---|
|
|
143
|
+
| `KYNVER_API_URL` | Kynver origin, e.g. `https://kynver.com` |
|
|
144
|
+
| `KYNVER_API_KEY` | Bearer token for external authentication |
|
|
145
|
+
| `KYNVER_AGENT_OS_SLUG` | Default agent slug when the tool's `slug` arg is omitted (defaults to `"ghost"`) |
|
|
146
|
+
| `KYNVER_SERVICE_SECRET` | Required when running under SSE and forwarding internal `userId` headers |
|
|
147
|
+
| `MCP_SSE_HOST` | SSE bind address (defaults to `127.0.0.1`) |
|
|
148
|
+
| `PORT` | SSE port (defaults to `3101`) |
|
|
149
|
+
|
|
150
|
+
## OpenClaw / Claude Code config
|
|
151
|
+
|
|
152
|
+
Once this package is published, the Ghost workspace's MCP config needs **two
|
|
153
|
+
separate** Kynver MCP server entries — one for the analyst surface and one for
|
|
154
|
+
this Agentic-OS surface:
|
|
155
|
+
|
|
156
|
+
```jsonc
|
|
157
|
+
{
|
|
158
|
+
"mcpServers": {
|
|
159
|
+
"kynver-analyst": {
|
|
160
|
+
"command": "npx",
|
|
161
|
+
"args": ["-y", "@kynver-app/mcp-analyst"],
|
|
162
|
+
"env": {
|
|
163
|
+
"KYNVER_API_URL": "https://kynver.com",
|
|
164
|
+
"KYNVER_API_KEY": "${KYNVER_API_KEY}"
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
"kynver-agent-os": {
|
|
168
|
+
"command": "npx",
|
|
169
|
+
"args": ["-y", "@kynver-app/mcp-agent-os"],
|
|
170
|
+
"env": {
|
|
171
|
+
"KYNVER_API_URL": "https://kynver.com",
|
|
172
|
+
"KYNVER_API_KEY": "${KYNVER_API_KEY}",
|
|
173
|
+
"KYNVER_AGENT_OS_SLUG": "ghost"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The split mirrors the server-side change of 2026-05-14: the `agent_os_*`
|
|
181
|
+
family was extracted from `@kynver-app/mcp-analyst` into this dedicated
|
|
182
|
+
package so each AgentOS deployment can mount it independently from the
|
|
183
|
+
analyst tools.
|
|
184
|
+
|
|
185
|
+
## Chat with your owner from any agent (`kynver-agent-chat-mcp`)
|
|
186
|
+
|
|
187
|
+
A second bin in this package lets **any** MCP-capable agent use Kynver chat as its main way of talking to its owner. In Kynver, choose **Connect → Any other agent**; you get a connection key and a ready-made config. The key speaks for that one agent only.
|
|
188
|
+
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"mcpServers": {
|
|
192
|
+
"kynver-chat": {
|
|
193
|
+
"command": "npx",
|
|
194
|
+
"args": ["-y", "-p", "@kynver-app/mcp-agent-os", "kynver-agent-chat-mcp"],
|
|
195
|
+
"env": { "KYNVER_AGENT_CONNECTOR_KEY": "kyc_…" }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Tools: `kynver_chat_wait_for_messages` (long poll, 25 s), `kynver_chat_reply`, `kynver_chat_stream`, `kynver_chat_typing`, `kynver_chat_upload_file`, `kynver_chat_ask` (buttons), `kynver_chat_get_answer`, `kynver_chat_history`. Optional `KYNVER_AGENT_CONNECTOR_URL` (https; default `https://www.kynver.com/api/agent-connector/v1`). This server never reads `KYNVER_API_KEY`. Plain-HTTPS examples and the optional signed webhook: https://www.kynver.com/docs/connect-your-agent
|
package/SECURITY.md
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
# Security — Kynver MCP packages
|
|
2
|
-
|
|
3
|
-
## Recommended deployment
|
|
4
|
-
|
|
5
|
-
- **Prefer stdio** (`npx @kynver-app/mcp-*`) from Cursor, Claude Code, or OpenClaw. The MCP host process owns the trust boundary.
|
|
6
|
-
- **Avoid exposing SSE** (`start:sse`) to the public internet unless you terminate TLS and restrict access at a reverse proxy.
|
|
7
|
-
|
|
8
|
-
## Secrets
|
|
9
|
-
|
|
10
|
-
- Store `KYNVER_API_KEY` and `KYNVER_SERVICE_SECRET` in environment variables or your MCP host's secret store — never in git, query strings, or shared logs.
|
|
11
|
-
- `KYNVER_SERVICE_SECRET` plus a valid user API key can impersonate any user on your Kynver deployment. Treat it as tier-0.
|
|
12
|
-
|
|
13
|
-
## SSE mode (`start:sse`)
|
|
14
|
-
|
|
15
|
-
When you run the optional HTTP/SSE server:
|
|
16
|
-
|
|
17
|
-
| Control | Default | Purpose |
|
|
18
|
-
|---------|---------|---------|
|
|
19
|
-
| `MCP_SSE_HOST` | `127.0.0.1` | Bind address — keep localhost unless behind a proxy |
|
|
20
|
-
| `PORT` | package-specific | Listen port |
|
|
21
|
-
|
|
22
|
-
Authentication rules:
|
|
23
|
-
|
|
24
|
-
- **Headers only** for internal auth on `GET /sse` — `X-Kynver-User-Id` + `X-Kynver-Service-Secret`. Never pass `secret` or `userId` in query strings.
|
|
25
|
-
- **External API keys** may use `Authorization: Bearer <apiKey>` (preferred) or `?apiKey=` on `GET /sse` when your MCP client cannot set headers (EventSource). Query keys may appear in access logs.
|
|
26
|
-
- **`POST /messages` requires the same API key or internal headers** used to open the SSE session — not `sessionId` alone.
|
|
27
|
-
- Request bodies are capped at **1 MB**.
|
|
28
|
-
|
|
29
|
-
## Reporting
|
|
30
|
-
|
|
31
|
-
Report vulnerabilities to Kynver through your usual security channel for the Kynver deployment you operate.
|
|
1
|
+
# Security — Kynver MCP packages
|
|
2
|
+
|
|
3
|
+
## Recommended deployment
|
|
4
|
+
|
|
5
|
+
- **Prefer stdio** (`npx @kynver-app/mcp-*`) from Cursor, Claude Code, or OpenClaw. The MCP host process owns the trust boundary.
|
|
6
|
+
- **Avoid exposing SSE** (`start:sse`) to the public internet unless you terminate TLS and restrict access at a reverse proxy.
|
|
7
|
+
|
|
8
|
+
## Secrets
|
|
9
|
+
|
|
10
|
+
- Store `KYNVER_API_KEY` and `KYNVER_SERVICE_SECRET` in environment variables or your MCP host's secret store — never in git, query strings, or shared logs.
|
|
11
|
+
- `KYNVER_SERVICE_SECRET` plus a valid user API key can impersonate any user on your Kynver deployment. Treat it as tier-0.
|
|
12
|
+
|
|
13
|
+
## SSE mode (`start:sse`)
|
|
14
|
+
|
|
15
|
+
When you run the optional HTTP/SSE server:
|
|
16
|
+
|
|
17
|
+
| Control | Default | Purpose |
|
|
18
|
+
|---------|---------|---------|
|
|
19
|
+
| `MCP_SSE_HOST` | `127.0.0.1` | Bind address — keep localhost unless behind a proxy |
|
|
20
|
+
| `PORT` | package-specific | Listen port |
|
|
21
|
+
|
|
22
|
+
Authentication rules:
|
|
23
|
+
|
|
24
|
+
- **Headers only** for internal auth on `GET /sse` — `X-Kynver-User-Id` + `X-Kynver-Service-Secret`. Never pass `secret` or `userId` in query strings.
|
|
25
|
+
- **External API keys** may use `Authorization: Bearer <apiKey>` (preferred) or `?apiKey=` on `GET /sse` when your MCP client cannot set headers (EventSource). Query keys may appear in access logs.
|
|
26
|
+
- **`POST /messages` requires the same API key or internal headers** used to open the SSE session — not `sessionId` alone.
|
|
27
|
+
- Request bodies are capped at **1 MB**.
|
|
28
|
+
|
|
29
|
+
## Reporting
|
|
30
|
+
|
|
31
|
+
Report vulnerabilities to Kynver through your usual security channel for the Kynver deployment you operate.
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{McpServer as A}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as P}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as o}from"zod";var C="https://www.kynver.com/api/agent-connector/v1";function y(t=process.env){let e=t.KYNVER_AGENT_CONNECTOR_KEY?.trim();if(!e||!/^kyc_[A-Za-z0-9_-]{43}$/.test(e))return{error:"KYNVER_AGENT_CONNECTOR_KEY is missing or not a Kynver connection key (it starts with kyc_). Copy it from Kynver \u2192 Connect."};let r=(t.KYNVER_AGENT_CONNECTOR_URL?.trim()||C).replace(/\/+$/,"");return!/^https:\/\//.test(r)&&!/^http:\/\/localhost(:\d+)?\//.test(`${r}/`)?{error:"KYNVER_AGENT_CONNECTOR_URL must be an https address."}:{baseUrl:r,key:e}}var l=class extends Error{constructor(r,a){super(a);this.status=r;this.name="AgentChatError"}status};async function f(t,e,r,a,c=fetch){if(!r.startsWith("/")||r.includes(".."))throw new l(400,"bad path");let s=await c(`${t.baseUrl}${r}`,{method:e,headers:{authorization:`Bearer ${t.key}`,...a===void 0?{}:{"content-type":"application/json"}},body:a===void 0?void 0:JSON.stringify(a),redirect:"error",signal:AbortSignal.timeout(45e3)}),d=await s.json().catch(()=>({}));if(!s.ok)throw new l(s.status,typeof d.error=="string"?d.error:`request failed (${s.status})`);return d}async function v(t,e,r=fetch){let a=new URLSearchParams({name:e.name,...e.mime?{mime:e.mime}:{}}),c=await r(`${t.baseUrl}/files?${a}`,{method:"POST",headers:{authorization:`Bearer ${t.key}`,"content-type":"application/octet-stream"},body:Buffer.from(e.contentBase64,"base64"),redirect:"error",signal:AbortSignal.timeout(45e3*2)}),s=await c.json().catch(()=>({}));if(!c.ok)throw new l(c.status,typeof s.error=="string"?s.error:`upload failed (${c.status})`);return s}import{existsSync as T,readFileSync as S}from"node:fs";import{dirname as h,join as k}from"node:path";import{fileURLToPath as w}from"node:url";function b(t){let e=h(w(t));for(let r=0;r<6;r+=1){if(T(k(e,"package.json")))return e;let a=h(e);if(a===e)break;e=a}throw new Error(`package.json not found above ${h(w(t))}`)}function E(t,e){let r=k(b(t),"package.json"),c=JSON.parse(S(r,"utf8"))[e];if(typeof c!="string"||!c.trim())throw new Error(`Missing package.json ${e} at ${r}`);return c}function u(t=import.meta.url){return E(t,"version")}function R(t){return t.some(e=>e==="--version"||e==="-v")}function O(t,e){console.log(e?`${e} ${t}`:t),process.exit(0)}function _(t,e=import.meta.url,r){return R(t)?(O(u(e),r),!0):!1}var $=u(import.meta.url);function N(t){return{content:[{type:"text",text:JSON.stringify(t)}],structuredContent:t}}function x(t){return{content:[{type:"text",text:t}],isError:!0}}function U(t=process.env,e=fetch){let r=new A({name:"kynver-agent-chat",version:$},{capabilities:{tools:{}}}),a=y(t),c="error"in a?null:a,s=async n=>{if(!c)return x("error"in a?a.error:"Not configured.");try{return N(await n(c))}catch(i){return x(i instanceof Error?i.message:"That didn't work.")}},d=(n,i,m,I)=>f(n,i,m,I,e),p=r.registerTool.bind(r),g=o.guid().optional().describe("conversationId from the message you are answering. Omit for the chat's open conversation.");return p("kynver_chat_wait_for_messages",{description:"Wait (up to 25 seconds) for new messages from the person you chat with in Kynver. Returns { events, cursor }. Call it again with the cursor you got back; start with since=0. Each event has text, messageId, conversationId, attachments and quote. Calling this marks you connected.",inputSchema:{since:o.number().int().min(0).optional(),waitSeconds:o.number().int().min(0).max(25).optional(),limit:o.number().int().min(1).max(50).optional()}},async n=>s(i=>d(i,"GET",`/messages?${new URLSearchParams({since:String(n.since??0),wait:String(n.waitSeconds??25),limit:String(n.limit??20)})}`))),p("kynver_chat_reply",{description:"Send your reply in the Kynver chat (Markdown). Pass replyTo (the messageId you answer) and conversationId from the event. idempotencyKey makes a retry safe: the same key never posts twice. Optionally attach files made with kynver_chat_upload_file.",inputSchema:{text:o.string().max(32e3).optional(),conversationId:g,replyTo:o.string().min(1).max(80).optional(),idempotencyKey:o.string().min(4).max(100).optional(),fileIds:o.array(o.guid()).max(10).optional(),notify:o.boolean().optional().describe("Buzz the person's phone. Default true.")}},async n=>s(i=>d(i,"POST","/messages",{text:n.text??"",...n.conversationId?{conversationId:n.conversationId}:{},...n.replyTo?{replyTo:n.replyTo,idempotencyKey:n.idempotencyKey??`reply-${n.replyTo}`}:n.idempotencyKey?{idempotencyKey:n.idempotencyKey}:{},attachments:(n.fileIds??[]).map(m=>({fileId:m})),...n.notify===void 0?{}:{notify:n.notify}}))),p("kynver_chat_stream",{description:"Stream one reply that grows. First call without messageId and with text: it appears in the chat now and you get a messageId. Then call with that messageId and append (more text) as often as you like; finish with done=true (that is when the badge and phone notification fire).",inputSchema:{messageId:o.string().min(1).max(80).optional(),text:o.string().max(32e3).optional(),append:o.string().max(32e3).optional(),done:o.boolean().optional(),conversationId:g,replyTo:o.string().min(1).max(80).optional()}},async n=>s(i=>{if(!n.messageId)return d(i,"POST","/messages",{text:n.text??"\u2026",stream:!0,...n.conversationId?{conversationId:n.conversationId}:{},...n.replyTo?{replyTo:n.replyTo}:{}});let m=n.conversationId?`?conversationId=${encodeURIComponent(n.conversationId)}`:"";return d(i,"PATCH",`/messages/${encodeURIComponent(n.messageId)}${m}`,{...n.append!==void 0?{append:n.append}:n.text!==void 0?{text:n.text}:{},done:n.done===!0})})),p("kynver_chat_typing",{description:'Show that you are typing or working (for about 25 seconds). Optional label in plain words, e.g. "searching the web".',inputSchema:{label:o.string().min(1).max(40).optional(),conversationId:g}},async n=>s(i=>d(i,"POST","/typing",{...n.label?{label:n.label}:{},...n.conversationId?{conversationId:n.conversationId}:{}}))),p("kynver_chat_upload_file",{description:"Upload a file (up to 20 MB) and get a fileId to attach with kynver_chat_reply.",inputSchema:{name:o.string().min(1).max(200),contentBase64:o.string().min(1),mime:o.string().max(120).optional()}},async n=>s(i=>v(i,n,e))),p("kynver_chat_ask",{description:"Ask the person a question with buttons. They tap one; read the answer with kynver_chat_get_answer (or it also arrives as their next message). Button text is what they send back.",inputSchema:{id:o.string().min(1).max(80).describe("A short id you choose, e.g. pick-1."),question:o.string().min(1).max(500),options:o.array(o.string().min(1).max(40)).min(1).max(6),conversationId:g}},async n=>s(i=>d(i,"POST","/messages",{text:"",ask:{id:n.id,question:n.question,options:n.options.map(m=>({label:m}))},...n.conversationId?{conversationId:n.conversationId}:{}}))),p("kynver_chat_get_answer",{description:"Has the person answered a question you asked with kynver_chat_ask? Returns { answered, answer }.",inputSchema:{id:o.string().min(1).max(80),conversationId:g}},async n=>s(i=>d(i,"GET",`/asks/${encodeURIComponent(n.id)}${n.conversationId?`?conversationId=${encodeURIComponent(n.conversationId)}`:""}`))),p("kynver_chat_history",{description:"Recent messages of this chat, oldest first, so you can answer in context.",inputSchema:{limit:o.number().int().min(1).max(80).optional(),conversationId:g}},async n=>s(i=>d(i,"GET",`/history?${new URLSearchParams({limit:String(n.limit??40),...n.conversationId?{conversationId:n.conversationId}:{}})}`))),r}var K=process.argv[1]?.endsWith("agent-chat.js")||process.argv[1]?.endsWith("agent-chat.ts");K&&(_(process.argv.slice(2),import.meta.url,"kynver-agent-chat-mcp"),await U().connect(new P));export{U as createAgentChatServer};
|