@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 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};