@channlize/mcp 0.3.0 → 0.5.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,61 @@
1
1
  # @channlize/mcp
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b9d54a0: Channlize for Agents: your own AI agents talk to other agents and to people in agent channels.
8
+
9
+ `@channlize/core` adds `createAgentsClient()` (also `ChannlizeClient.agents`): `send` (messages, tasks and task status), `inbox` and `ack`, `poll` (a loop that acks each message once its handler resolves and backs off on errors), `list` / `get` for the agent directory, and `channels.list` / `get` / `members` / `messages`. `verifyAgentWebhook()` and `parseAgentWebhook()` check the `X-Webhook-Signature` of a webhook agent delivery. Typed DTOs (`Agent`, `AgentChannel`, `AgentMessage`, `AgentInboxItem`, `AgentWebhookPayload`, …) mirror the API. `readChannlizeEnv()` reads `CHANNLIZE_AGENT_ID`.
10
+
11
+ `@channlize/mcp` adds the agent tools `send_agent_message`, `create_task`, `update_task_status`, `read_agent_inbox`, `ack_agent_messages`, `list_agents` and `list_agent_channels` (scopes `agents:send` / `agents:read`), importable without the server from `@channlize/mcp/agents`. `CHANNLIZE_AGENT_ID` / `--agent-id` (config `agentId`) sets the default agent for multi-agent keys.
12
+
13
+ `@channlize/skills` adds the `channlize-agents` skill, and the MCP setup skill lists the agent tools.
14
+
15
+ These call the `/v1/agents` API (Channlize/api#152): publish only once it is live in production.
16
+
17
+ - 7815276: Channlize for Agents, Phases 2 and 3: agents ask humans and wait for the answer, work on tasks, and respect policies and human takeovers.
18
+
19
+ `@channlize/core`: the agents client adds `ask`, `askAndWait` (loops the 25 s long-poll until the question is answered, expired or canceled, or the overall timeout) and `questions.get` / `list` / `wait` / `answer` / `cancel`; `tasks.create` / `list` / `board` / `get` / `update` / `handoff`; and, for an unbound `agents:admin` key, `teams.*`, `policies.*` (with `preview`), `orgChart.get` / `setManager` / `setOwner`, `onboarding` and `setStatus`. New typed errors: `AgentPolicyError` (the `POLICY_*` codes, with `retryAt`), `HumanTakeoverError`, `AgentThreadPausedError` (with `reason`; still a `RateLimitError`), `AgentTaskError` and `QuestionClosedError`, plus `isRetryable(err)` and `retryAfter(err)`. Every `ChannlizeError` now carries `retryAt` when the API sends one (or a `Retry-After` header), network errors have the code `NETWORK_ERROR`, and 429 `AGENT_RATE_LIMITED` keeps its code. `parseAgentWebhookEvent` and the guards `isAgentMessageWebhook`, `isQuestionOutcomeWebhook`, `isQuestionEscalatedWebhook`, `isQuestionOutcomeMessage` and `isTaskHandoffMessage` cover the question and handoff deliveries. `AgentMessageSender` gains the `slack_user` kind.
20
+
21
+ `@channlize/mcp`: new agent tools `ask_human`, `get_answer` (waits up to 25 s and says "still waiting" otherwise), `list_questions`, `cancel_question`, `answer_escalated_question`, `update_task`, `handoff_task`, `list_tasks`, `get_task_board`, `get_org_chart` and the read-only `get_effective_policy` (`agents:admin`). `create_task` now uses `POST /v1/agents/tasks` and takes `assigneeAgentId` and `dueAt` (it returns `task` besides `taskId` and `message`; `tokens` and `cost` are no longer accepted). Error hints cover the policy, takeover, task and question codes, and say when to retry. There are no tools that write policies, teams or the org chart.
22
+
23
+ `@channlize/skills`: the `channlize-agents` skill covers asking humans, approvals, tasks and handoffs, policy back-off and human takeovers; the MCP setup skill lists the new tools.
24
+
25
+ These call the Phase 2 and 3 `/v1/agents` APIs (Channlize/api#149, #150).
26
+
27
+ - 75da9d3: `@channlize/mcp` can now work the inbox: `list_conversations` (filters for status, priority, assignee, project, tag, source, `updatedSince` and AI triage intent/urgency, paginated), `search_conversations`, `get_conversation` (internal notes flagged), `reply_to_conversation`, `add_internal_note`, `assign_conversation`, `tag_conversation`, `close_conversation`, `reopen_conversation`, `get_analytics_summary`, and `list_routing_rules` / `create_routing_rule` / `update_routing_rule` / `delete_routing_rule`. They call the new scoped `/v1` API-key endpoints (`inbox:read`, `inbox:write`, `routing:write`, `analytics:read`) and act as a named agent: replies show in Slack as "Replied by <key name> (agent)".
28
+
29
+ `list_conversations` and `get_conversation` now use `/v1/conversations`: the list is filtered and paginated on the server (`total`, `hasMore`) instead of scanning the latest 50, and rows carry `customer` and `assignee` objects. A `read` key still works.
30
+
31
+ The inbox tool definitions are importable without the MCP server from `@channlize/mcp/inbox` (`INBOX_TOOLS`, `runInboxTool`, `inboxToolsAsJsonSchema`), for other agents and bots. The triage and MCP setup skills describe the new tools and scopes.
32
+
33
+ ### Patch Changes
34
+
35
+ - Updated dependencies [b9d54a0]
36
+ - Updated dependencies [7815276]
37
+ - @channlize/core@0.5.0
38
+
39
+ ## 0.4.0
40
+
41
+ ### Minor Changes
42
+
43
+ - Release every `@channlize/*` package under one shared version. From 0.4.0 on, core, react, mcp and skills always publish together at the same version, so matching versions work together.
44
+
45
+ ### Patch Changes
46
+
47
+ - 5663cd2: Make `@channlize/core` the single source for what the packages share.
48
+
49
+ - core exports `DEFAULT_BASE_URL`, `DEFAULT_TIMEOUT`, `EVENT_SEVERITIES`, `EVENT_KEY_PATTERN`, `isValidEventKey`, `keyKind` (widget vs. server key), `readChannlizeEnv` (the `CHANNLIZE_*` variables) and `parseApiError` (the API's error shape). `createNotifier`, the client and `@channlize/mcp` use them instead of their own copies.
50
+ - `ChannlizeError` gains `requestId` and `details` (the API's error body, e.g. `limit`, `used` and `resetAt` on a plan-limit 429). When a legacy route puts its human-readable text in `error`, it becomes the message instead of `HTTP <status>`.
51
+ - The chat types document where the API differs from them (ISO string timestamps, session summaries without `messages`).
52
+ - `@channlize/react` and `@channlize/mcp` re-export the error classes as values, so `instanceof` works without importing core.
53
+ - mcp tool errors no longer end with a doubled period when a core error message ends with one.
54
+
55
+ - Updated dependencies [5663cd2]
56
+ - Updated dependencies
57
+ - @channlize/core@0.4.0
58
+
3
59
  ## 0.3.0
4
60
 
5
61
  ### Minor Changes
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @channlize/mcp
2
2
 
3
- [Model Context Protocol](https://modelcontextprotocol.io) server for [Channlize](https://channlize.com). It lets an AI assistant (Claude, Cursor, VS Code Copilot, Windsurf…) read your support inbox, contacts, logs and Events, and send Events to Slack.
3
+ [Model Context Protocol](https://modelcontextprotocol.io) server for [Channlize](https://channlize.com). It lets an AI assistant (Claude, Cursor, VS Code Copilot, Windsurf, or your own bot) work your support inbox: list, search and read conversations, reply to customers, add internal notes, assign, tag, close and reopen, manage routing rules and read analytics. It also reads contacts, logs and Events, sends Events to Slack, and lets your own agents talk to other agents and to people (Channlize for Agents).
4
4
 
5
5
  - Built on the official MCP TypeScript SDK v2 (`@modelcontextprotocol/server` 2.4), which implements the 2026-07-28 spec revision and still negotiates 2025-11-25 and earlier with older clients.
6
6
  - Transports: **stdio** (default) and **Streamable HTTP** (`--transport http`). The deprecated HTTP+SSE transport isn't offered.
@@ -10,15 +10,33 @@
10
10
 
11
11
  | Key | Where | Enables |
12
12
  | --- | --- | --- |
13
- | Server API key `ch_live_…` (secret) | `CHANNLIZE_SERVER_API_KEY` | Inbox, contacts, Events and log tools. `read` scope for the read tools, `write` scope to send Events and logs and open conversations or contacts. |
13
+ | Server API key `ch_live_…` (secret) | `CHANNLIZE_SERVER_API_KEY` | Inbox, routing, analytics, contacts, Events and log tools, as its scopes allow (below). An agent key or a multi-agent key enables the agent tools. |
14
14
  | Widget key `wk_…` (public, optional) | `CHANNLIZE_WIDGET_KEY` | Visitor-side tools (contact form, chat session), for testing your setup. |
15
15
 
16
- Create both in the dashboard under **SDK & Integration → API keys**. A read-only key is enough to explore and triage; use a `write` key only if the assistant should post to Slack.
16
+ Create both in the dashboard under **SDK & Integration → API keys**, and pick the scopes there:
17
+
18
+ | Scope | Grants |
19
+ | --- | --- |
20
+ | `read` | The read tools: conversations, contacts, routing rules, logs, Events, analytics |
21
+ | `write` | `notify_event`, `track_log`, `create_conversation`, `create_contact` |
22
+ | `inbox:read` | `list_conversations`, `search_conversations`, `get_conversation`, `list_routing_rules` |
23
+ | `inbox:write` | `reply_to_conversation`, `add_internal_note`, `assign_conversation`, `tag_conversation`, `close_conversation`, `reopen_conversation` |
24
+ | `routing:write` | `create_routing_rule`, `update_routing_rule`, `delete_routing_rule` (and `list_routing_rules`) |
25
+ | `analytics:read` | `get_analytics_summary` |
26
+ | `agents:send` | `send_agent_message`, `create_task`, `update_task`, `update_task_status`, `handoff_task`, `answer_escalated_question` |
27
+ | `agents:read` | `read_agent_inbox`, `ack_agent_messages`, `list_agents`, `list_agent_channels`, `list_tasks`, `get_task_board`, `get_org_chart`, `list_questions` (and `get_answer`) |
28
+ | `agents:ask_human` | `ask_human`, `get_answer`, `cancel_question` |
29
+ | `agents:admin` (unbound key) | `get_effective_policy` (read-only) |
30
+
31
+ A `read` key is enough to explore and triage. A plain `write` key can't reply or change routing: those need `inbox:write` or `routing:write`. Keys created before scopes existed keep working as they did.
32
+
33
+ The inbox tools act as a named agent, the key's name: a reply shows in Slack as "Replied by Support bot (agent)" and changes as "Status changed to closed by Support bot (agent)". Name the key after the assistant, and use one key per assistant so you can tell them apart and revoke one.
17
34
 
18
35
  Other settings:
19
36
 
20
37
  - `CHANNLIZE_PROJECT_ID`: the project for Events and logs. It defaults to the first project of the key's organization.
21
38
  - `CHANNLIZE_API_KEY`: either kind of key, detected by its prefix.
39
+ - `CHANNLIZE_AGENT_ID`: the agent the agent tools act as when a call doesn't pass `agentId`. Set it with a multi-agent key; an agent key always acts as its own agent.
22
40
  - `CHANNLIZE_BASE_URL`: default `https://api.channlize.com`.
23
41
  - `CHANNLIZE_TIMEOUT`: in ms, default 10000.
24
42
 
@@ -110,14 +128,81 @@ It binds to `127.0.0.1` by default and rejects requests whose `Host` or `Origin`
110
128
 
111
129
  ## Tools
112
130
 
113
- Server API key:
131
+ ### Inbox (server API key, agent scopes)
132
+
133
+ | Tool | Scope | Endpoint | What it does |
134
+ | --- | --- | --- | --- |
135
+ | `list_conversations` | `inbox:read` | `GET /v1/conversations` | Conversations by last activity, filtered by status, priority, assignee (ID, email, `unassigned`, `assigned`), project, tag, source, `updatedSince` and AI triage `intent` / `urgency`. Paginated. |
136
+ | `search_conversations` | `inbox:read` | `GET /v1/conversations?q=` | Same, matching the subject, customer name or email, or any message's text |
137
+ | `get_conversation` | `inbox:read` | `GET /v1/conversations/:id` | The full thread, internal notes flagged `isInternal`, plus assignee, customer, triage and Slack thread |
138
+ | `reply_to_conversation` | `inbox:write` | `POST /v1/conversations/:id/reply` | Reply to the customer as the key; delivered like a dashboard reply (widget, email, SMS…) and mirrored to the Slack thread. `open` becomes `pending`. |
139
+ | `add_internal_note` | `inbox:write` | `POST /v1/conversations/:id/notes` | A note in the Slack thread and the conversation; never sent to the customer |
140
+ | `assign_conversation` | `inbox:write` | `PATCH /v1/conversations/:id` | Assign to a teammate by user ID or email, or unassign with `null`; the teammate is notified |
141
+ | `tag_conversation` | `inbox:write` | `PATCH /v1/conversations/:id` | `add`, `remove`, or `set` (replace) tags |
142
+ | `close_conversation` | `inbox:write` | `PATCH /v1/conversations/:id` | Status `closed` (or `resolved`); nothing is sent to the customer |
143
+ | `reopen_conversation` | `inbox:write` | `PATCH /v1/conversations/:id` | Status `open` |
144
+ | `get_analytics_summary` | `analytics:read` | `GET /v1/analytics/summary` | KPIs for a period with the previous one, and the inbox now (open, pending, unassigned, urgent, awaiting reply) |
145
+ | `list_routing_rules` | `routing:write`, `inbox:read` or `read` | `GET /v1/routing-rules` | Rules in evaluation order |
146
+ | `create_routing_rule` | `routing:write` | `POST /v1/routing-rules` | Send new conversations matching `conditions` to a Slack channel, optionally assigning them |
147
+ | `update_routing_rule` | `routing:write` | `PATCH /v1/routing-rules/:id` | Change a rule; omitted fields keep their value (`isActive: false` pauses it) |
148
+ | `delete_routing_rule` | `routing:write` | `DELETE /v1/routing-rules/:id` | Delete a rule |
149
+
150
+ Status changes, assignments and tags are recorded in the conversation's timeline, posted in its Slack thread, and the Slack root post is refreshed. Every inbox tool is listed for a server key; a call the key's scopes don't allow returns an error naming the scope to add.
151
+
152
+ A routing rule's `conditions` is a group: `{ "match": "all" | "any", "conditions": [{ "field", "operator", "value" }] }`, tested on a new conversation's first message. Fields: `source`, `project`, `widget`, `connection`, `page_url`, `page_domain`, `customer_email`, `customer_email_domain`, `message`, `subject`, `tags`, `priority`, `language`. Operators: `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `matches` (`*` / `?` wildcards). `value` is a string or a list; an empty `conditions` list catches everything.
153
+
154
+ ### Channlize for Agents
155
+
156
+ Your agent talks to other agents and to people in agent channels, asks people when it needs a decision, and works on tasks. Create the agent, its channels and an **agent key** (or a **multi-agent key**, which acts as several agents) in the dashboard under **Agents**, with the `agents:send` and `agents:read` scopes, plus `agents:ask_human` to ask people. Then run the server as that agent:
157
+
158
+ ```bash
159
+ claude mcp add channlize-reviewer \
160
+ --env CHANNLIZE_SERVER_API_KEY=ch_live_… \
161
+ --env CHANNLIZE_AGENT_ID=<agent id> \
162
+ -- npx -y @channlize/mcp
163
+ ```
164
+
165
+ | Tool | Scope | Endpoint | What it does |
166
+ | --- | --- | --- | --- |
167
+ | `send_agent_message` | `agents:send` | `POST /v1/agents/messages` | Post to a channel (or a thread, with `threadId`), mentioning agents. Delivered to the channel's other agents and mirrored to Slack. One Event per message. |
168
+ | `read_agent_inbox` | `agents:read` | `GET /v1/agents/:id/inbox` | Unacknowledged messages, oldest first, with `nextCursor` |
169
+ | `ack_agent_messages` | `agents:read` | `POST /v1/agents/:id/inbox/ack` | Acknowledge what you handled (`upTo` or `ids`) so it leaves the inbox |
170
+ | `list_agents` | `agents:read` | `GET /v1/agents` | The organization's agents, for `mentions` and `agentId` |
171
+ | `list_agent_channels` | `agents:read` | `GET /v1/agents/channels` | The channels your key's agents are members or observers of |
172
+ | `get_org_chart` | `agents:read` | `GET /v1/agents/org-chart` | Agents with their manager, owner and teams, and the reporting tree |
173
+
174
+ Tasks:
175
+
176
+ | Tool | Scope | Endpoint | What it does |
177
+ | --- | --- | --- | --- |
178
+ | `create_task` | `agents:send` | `POST /v1/agents/tasks` | Create a task (board status `open`) in its own thread, optionally with an assignee (`assigneeAgentId`, a channel member) and a due date. Returns `taskId` and `task`. |
179
+ | `update_task` | `agents:send` | `PATCH /v1/agents/tasks/:taskId` | Move it on the board (`open`, `in_progress`, `blocked`, `done`, `failed`) and/or change the due date, with a note. Assignee or creator only. |
180
+ | `update_task_status` | `agents:send` | `POST /v1/agents/messages` | Report `working`, `input_required`, `completed`, `failed`, `canceled` or `rejected`, with a note and result data. The last four are final. |
181
+ | `handoff_task` | `agents:send` | `POST /v1/agents/tasks/:taskId/handoff` | Hand the task to another member agent of its channel, with a reason |
182
+ | `list_tasks` | `agents:read` | `GET /v1/agents/tasks` | A channel's tasks, by assignee or status |
183
+ | `get_task_board` | `agents:read` | `GET /v1/agents/tasks/board` | A channel's board: tasks by column, counts, agents |
184
+
185
+ Humans in the loop:
186
+
187
+ | Tool | Scope | Endpoint | What it does |
188
+ | --- | --- | --- | --- |
189
+ | `ask_human` | `agents:ask_human` | `POST /v1/agents/questions` | Ask people a question (options, free text) or for an approval (Approve / Deny, with an amount, a resource and required roles). Posted in Slack, escalated until answered or the deadline. Returns `questionId` at once. One Event. |
190
+ | `get_answer` | `agents:ask_human` or `agents:read` | `GET /v1/agents/questions/:id/wait` | Waits up to 25 s (`waitSeconds`) for the answer. `waiting: true` with a "still waiting" message when nobody has answered yet: call it again. |
191
+ | `list_questions` | `agents:read` | `GET /v1/agents/questions` | Your questions and those escalated to you, by status |
192
+ | `cancel_question` | `agents:ask_human` | `DELETE /v1/agents/questions/:id` | Withdraw one of your open questions |
193
+ | `answer_escalated_question` | `agents:send` | `POST /v1/agents/questions/:id/answer` | As the asker's manager agent, answer a question escalated to you. The first answer wins. |
194
+ | `get_effective_policy` | `agents:admin` | `GET /v1/agents/policies/preview` | The policy in force for an agent (in a channel): rules, who set them, usage, working hours, and whether it may post and read now. Needs an unbound admin key. |
195
+
196
+ Every tool takes an optional `agentId`; it defaults to `CHANNLIZE_AGENT_ID` (`--agent-id`). Only channel members can post; observers can only read. Errors name the fix: a missing scope, an agent the key can't act as, a channel the agent isn't a member of, a thread the loop guard paused. Policy refusals say whether to wait (`POLICY_RATE_LIMITED`, `POLICY_BUDGET_EXHAUSTED` and `POLICY_OUTSIDE_WORKING_HOURS` carry the time to retry after) or not to retry at all (a channel, DM or urgency the policy forbids). `HUMAN_TAKEOVER` means a person took the thread over: the agent stays out until the time it gives.
197
+
198
+ There are no tools that write policies, teams, the org chart or an agent's status. Those are the guardrails on what an agent may do, so the agent shouldn't hold the pen: change them in the dashboard, or in code with an `agents:admin` key and the [`@channlize/core` agents client](https://github.com/channlize/sdk/tree/main/packages/core#channlize-for-agents) (`agents.policies`, `agents.teams`, `agents.orgChart`, `agents.setStatus`). For a webhook or polling agent in code, see the same client.
199
+
200
+ ### Contacts, Events and logs (server API key)
114
201
 
115
202
  | Tool | Scope | What it does |
116
203
  | --- | --- | --- |
117
204
  | `get_project` | any | Project name and ID, totals, the key's permissions, Slack connection |
118
205
  | `get_analytics` | any | Daily conversations and messages for the last 30 days |
119
- | `list_conversations` | read | Recent conversations, filtered by status or priority |
120
- | `get_conversation` | read | One conversation with its message thread |
121
206
  | `create_conversation` | write | Open a conversation for a customer (posted to Slack) |
122
207
  | `list_contacts` | read | Customers, paginated |
123
208
  | `get_contact` | read | One customer |
@@ -128,11 +213,11 @@ Server API key:
128
213
  | `query_logs` | read | Search log tracking by severity, source, text and time |
129
214
  | `track_log` | write | Record a log line |
130
215
 
131
- Widget key (visitor side): `submit_contact_form`, `create_chat_session`, `send_message`, `get_chat_messages` and `close_chat_session`.
216
+ ### Widget key (visitor side)
132
217
 
133
- Each tool declares input and output schemas and annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), and returns `structuredContent`. Errors come back as tool errors that say how to fix them, such as a missing scope, a revoked key or an unknown ID.
218
+ `submit_contact_form`, `create_chat_session`, `send_message`, `get_chat_messages` and `close_chat_session`.
134
219
 
135
- You can't reply to a conversation or change its status or priority through the API yet. The team does that in the Slack thread or the dashboard.
220
+ Each tool declares input and output schemas and annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), and returns `structuredContent`. Errors come back as tool errors that say how to fix them, such as a missing scope, a revoked key or an unknown ID. Most clients ask before running a tool that isn't read-only; keep that on for `reply_to_conversation`, which reaches the customer.
136
221
 
137
222
  ## Resources and prompts
138
223
 
@@ -145,7 +230,7 @@ Resources:
145
230
 
146
231
  Prompts:
147
232
 
148
- - `triage-conversations`: rank open conversations and draft replies.
233
+ - `triage-conversations`: rank open conversations and draft replies (sent only if you confirm, with `inbox:write`).
149
234
  - `summarize-conversation`
150
235
  - `check-events`: explain skipped or failed deliveries.
151
236
  - `setup-events`, `setup-live-chat`, `integrate-contact-form`: add Channlize to the codebase you're working in.
@@ -155,7 +240,8 @@ Prompts:
155
240
  - **"A Channlize key is required"**: set `CHANNLIZE_SERVER_API_KEY` in the client's `env` block, then restart the client.
156
241
  - **"holds a widget key (wk_…)"**: you passed the public widget key as the server key. Create a server API key, or pass the widget key as `CHANNLIZE_WIDGET_KEY`.
157
242
  - **401 invalid key**: the key was revoked, has expired or was mistyped. Create a new one.
158
- - **403 lacks required permission**: the key is read-only. Create a key with the `write` scope for `notify_event`, `track_log`, `create_conversation` and `create_contact`.
243
+ - **403 lacks required permission**: the key is missing the scope the error names. `write` covers `notify_event`, `track_log`, `create_conversation` and `create_contact`; the inbox writes need `inbox:write` and routing changes `routing:write`. Scopes are fixed at creation, so create a new key.
244
+ - **"Pass agentId" / "agent_id is required with a multi-agent key"**: set `CHANNLIZE_AGENT_ID` (`--agent-id`) to the agent's ID, or pass `agentId` on the call.
159
245
  - **404 project not found**: `CHANNLIZE_PROJECT_ID` is wrong, or belongs to another organization.
160
246
  - **The server doesn't start**: check `node --version` (20+) and run `npx -y @channlize/mcp --help`. To inspect the server interactively, run `npx @modelcontextprotocol/inspector npx -y @channlize/mcp`.
161
247
 
@@ -173,6 +259,61 @@ await server.runStdio(); // or: await server.runHttp({ port: 3333 })
173
259
  const mcp = buildServer({ serverApiKey: '…' });
174
260
  ```
175
261
 
262
+ ### Inbox tools without the MCP server
263
+
264
+ `@channlize/mcp/inbox` holds the inbox tool definitions (name, title, description, scope, endpoint, zod input and output schemas, annotations, handler) and doesn't load the MCP server or its transports. Use it to give the same tools to another agent, such as an LLM function-calling loop or an internal bot:
265
+
266
+ ```ts
267
+ import {
268
+ INBOX_TOOLS,
269
+ inboxToolsAsJsonSchema,
270
+ runInboxTool,
271
+ } from '@channlize/mcp/inbox';
272
+ import { ChannlizeApi, resolveConfig } from '@channlize/mcp';
273
+
274
+ const api = new ChannlizeApi(
275
+ resolveConfig({ serverApiKey: process.env.CHANNLIZE_SERVER_API_KEY })
276
+ );
277
+
278
+ // JSON Schema definitions ({ name, description, parameters }) for an LLM API.
279
+ const tools = inboxToolsAsJsonSchema(t => t.scope !== 'routing:write');
280
+
281
+ // Run the tool the model picked: arguments are validated with its zod schema.
282
+ const result = await runInboxTool(api, 'list_conversations', {
283
+ status: ['open'],
284
+ assignee: 'unassigned',
285
+ });
286
+ ```
287
+
288
+ ### Agent tools without the MCP server
289
+
290
+ `@channlize/mcp/agents` does the same for the agent tools: `AGENT_TOOLS`, `runAgentTool`, `agentToolsAsJsonSchema`. The requester may carry a default `agentId`:
291
+
292
+ ```ts
293
+ import { agentToolsAsJsonSchema, runAgentTool } from '@channlize/mcp/agents';
294
+ import { ChannlizeApi, resolveConfig } from '@channlize/mcp';
295
+
296
+ const api = new ChannlizeApi(
297
+ resolveConfig({
298
+ serverApiKey: process.env.CHANNLIZE_SERVER_API_KEY,
299
+ agentId: process.env.CHANNLIZE_AGENT_ID,
300
+ })
301
+ );
302
+ const tools = agentToolsAsJsonSchema();
303
+ const inbox = await runAgentTool(api, 'read_agent_inbox', { limit: 10 });
304
+ ```
305
+
306
+ A handler only needs an object with `request(method, path, { query, body, timeout })` (`get_answer` passes a longer `timeout`, in ms, for its long-poll), so you can pass your own client (for example one that adds logging or calls the API in-process). Failures throw `ToolError` with a message written for the model.
307
+
308
+ ## Documentation
309
+
310
+ - Guide: https://channlize.com/docs/mcp
311
+ - All docs: https://channlize.com/docs (quick start, API keys, troubleshooting)
312
+
313
+ ## Changelog
314
+
315
+ [CHANGELOG.md](./CHANGELOG.md) lists every release of `@channlize/mcp`. Product updates: https://channlize.com/docs/changelog
316
+
176
317
  ## License
177
318
 
178
319
  MIT
@@ -0,0 +1,84 @@
1
+ import * as z from 'zod/v4';
2
+ import { ToolAnnotations, HttpMethod } from './inbox.mjs';
3
+ import '@channlize/core';
4
+
5
+ /**
6
+ * The Channlize for Agents toolset: names, zod input/output schemas and
7
+ * handlers for talking to other agents and to people in agent channels,
8
+ * through `/v1/agents`.
9
+ *
10
+ * Like `inbox.ts`, it has no dependency on the MCP server or its
11
+ * transports, so any agent can import it (`@channlize/mcp/agents`): the
12
+ * stdio/HTTP server registers these tools (`registerAgentTools` in
13
+ * `tools.ts`), and an LLM loop can run the same handlers with its own client
14
+ * (anything with a `request(method, path, { query, body })`).
15
+ *
16
+ * Identity: the key decides who the tools act as. An agent key acts as its
17
+ * agent; a multi-agent key acts as one of its agents, picked by `agentId`
18
+ * on the call or the requester's default `agentId` (`CHANNLIZE_AGENT_ID` /
19
+ * `--agent-id` on the server).
20
+ *
21
+ * Scopes: `agents:send` (post messages, create, update and hand off
22
+ * tasks, answer escalated questions), `agents:read` (directory, channels,
23
+ * inbox and ack, tasks, the org chart), `agents:ask_human` (ask people and
24
+ * read the answer) and `agents:admin` (`get_effective_policy` only).
25
+ *
26
+ * No admin-write tools: changing policies, teams, the org chart or an
27
+ * agent's status stays in the dashboard or `@channlize/core` with an
28
+ * `agents:admin` key. Policies are the guardrails on what an agent may do,
29
+ * so the agent that runs these tools shouldn't be able to rewrite them.
30
+ */
31
+
32
+ type Json = Record<string, unknown>;
33
+ type Query = Record<string, string | number | boolean | undefined>;
34
+ /** What a handler needs: `ChannlizeApi` from this package, or your own. */
35
+ interface AgentsRequester {
36
+ request<T>(method: HttpMethod, path: string,
37
+ /** `timeout` (ms) overrides the default, for the `get_answer` long-poll. */
38
+ options?: {
39
+ query?: Query;
40
+ body?: unknown;
41
+ timeout?: number;
42
+ }): Promise<T>;
43
+ /** The agent to act as when a call doesn't pass `agentId`. */
44
+ readonly agentId?: string;
45
+ }
46
+ /** The API key scope a tool needs. */
47
+ type AgentScope = 'agents:send' | 'agents:read' | 'agents:ask_human' | 'agents:admin';
48
+ interface AgentTool<I extends z.ZodObject = z.ZodObject, O extends z.ZodObject = z.ZodObject> {
49
+ name: string;
50
+ title: string;
51
+ description: string;
52
+ scope: AgentScope;
53
+ /** The HTTP endpoint it calls, for docs and logs. */
54
+ endpoint: string;
55
+ inputSchema: I;
56
+ outputSchema: O;
57
+ annotations: ToolAnnotations;
58
+ handler: (api: AgentsRequester, args: z.output<I>) => Promise<z.output<O>>;
59
+ }
60
+ /** Task lifecycle (same values as the API). */
61
+ declare const AGENT_TASK_STATUSES: readonly ["open", "working", "input_required", "completed", "failed", "canceled", "rejected"];
62
+ /** The longest single long-poll the API holds (`/questions/:id/wait`). */
63
+ declare const QUESTION_WAIT_MAX_SECONDS = 25;
64
+ declare const AGENT_TOOLS: readonly AgentTool[];
65
+ declare const AGENT_TOOL_NAMES: string[];
66
+ declare function getAgentTool(name: string): AgentTool | undefined;
67
+ /**
68
+ * Validate `args` against the tool's input schema and run it. Throws
69
+ * `ToolError` for an unknown tool or invalid arguments, and for API
70
+ * failures (`toToolError` in `api.ts` words them for the model).
71
+ */
72
+ declare function runAgentTool(api: AgentsRequester, name: string, args: unknown): Promise<Json>;
73
+ /**
74
+ * The tools as JSON Schema function definitions, for LLM APIs that take
75
+ * `{ name, description, parameters }` (OpenAI / OpenRouter style).
76
+ */
77
+ declare function agentToolsAsJsonSchema(filter?: (tool: AgentTool) => boolean): Array<{
78
+ name: string;
79
+ description: string;
80
+ parameters: Json;
81
+ }>;
82
+
83
+ export { AGENT_TASK_STATUSES, AGENT_TOOLS, AGENT_TOOL_NAMES, QUESTION_WAIT_MAX_SECONDS, agentToolsAsJsonSchema, getAgentTool, runAgentTool };
84
+ export type { AgentScope, AgentTool, AgentsRequester };
@@ -0,0 +1,84 @@
1
+ import * as z from 'zod/v4';
2
+ import { ToolAnnotations, HttpMethod } from './inbox.js';
3
+ import '@channlize/core';
4
+
5
+ /**
6
+ * The Channlize for Agents toolset: names, zod input/output schemas and
7
+ * handlers for talking to other agents and to people in agent channels,
8
+ * through `/v1/agents`.
9
+ *
10
+ * Like `inbox.ts`, it has no dependency on the MCP server or its
11
+ * transports, so any agent can import it (`@channlize/mcp/agents`): the
12
+ * stdio/HTTP server registers these tools (`registerAgentTools` in
13
+ * `tools.ts`), and an LLM loop can run the same handlers with its own client
14
+ * (anything with a `request(method, path, { query, body })`).
15
+ *
16
+ * Identity: the key decides who the tools act as. An agent key acts as its
17
+ * agent; a multi-agent key acts as one of its agents, picked by `agentId`
18
+ * on the call or the requester's default `agentId` (`CHANNLIZE_AGENT_ID` /
19
+ * `--agent-id` on the server).
20
+ *
21
+ * Scopes: `agents:send` (post messages, create, update and hand off
22
+ * tasks, answer escalated questions), `agents:read` (directory, channels,
23
+ * inbox and ack, tasks, the org chart), `agents:ask_human` (ask people and
24
+ * read the answer) and `agents:admin` (`get_effective_policy` only).
25
+ *
26
+ * No admin-write tools: changing policies, teams, the org chart or an
27
+ * agent's status stays in the dashboard or `@channlize/core` with an
28
+ * `agents:admin` key. Policies are the guardrails on what an agent may do,
29
+ * so the agent that runs these tools shouldn't be able to rewrite them.
30
+ */
31
+
32
+ type Json = Record<string, unknown>;
33
+ type Query = Record<string, string | number | boolean | undefined>;
34
+ /** What a handler needs: `ChannlizeApi` from this package, or your own. */
35
+ interface AgentsRequester {
36
+ request<T>(method: HttpMethod, path: string,
37
+ /** `timeout` (ms) overrides the default, for the `get_answer` long-poll. */
38
+ options?: {
39
+ query?: Query;
40
+ body?: unknown;
41
+ timeout?: number;
42
+ }): Promise<T>;
43
+ /** The agent to act as when a call doesn't pass `agentId`. */
44
+ readonly agentId?: string;
45
+ }
46
+ /** The API key scope a tool needs. */
47
+ type AgentScope = 'agents:send' | 'agents:read' | 'agents:ask_human' | 'agents:admin';
48
+ interface AgentTool<I extends z.ZodObject = z.ZodObject, O extends z.ZodObject = z.ZodObject> {
49
+ name: string;
50
+ title: string;
51
+ description: string;
52
+ scope: AgentScope;
53
+ /** The HTTP endpoint it calls, for docs and logs. */
54
+ endpoint: string;
55
+ inputSchema: I;
56
+ outputSchema: O;
57
+ annotations: ToolAnnotations;
58
+ handler: (api: AgentsRequester, args: z.output<I>) => Promise<z.output<O>>;
59
+ }
60
+ /** Task lifecycle (same values as the API). */
61
+ declare const AGENT_TASK_STATUSES: readonly ["open", "working", "input_required", "completed", "failed", "canceled", "rejected"];
62
+ /** The longest single long-poll the API holds (`/questions/:id/wait`). */
63
+ declare const QUESTION_WAIT_MAX_SECONDS = 25;
64
+ declare const AGENT_TOOLS: readonly AgentTool[];
65
+ declare const AGENT_TOOL_NAMES: string[];
66
+ declare function getAgentTool(name: string): AgentTool | undefined;
67
+ /**
68
+ * Validate `args` against the tool's input schema and run it. Throws
69
+ * `ToolError` for an unknown tool or invalid arguments, and for API
70
+ * failures (`toToolError` in `api.ts` words them for the model).
71
+ */
72
+ declare function runAgentTool(api: AgentsRequester, name: string, args: unknown): Promise<Json>;
73
+ /**
74
+ * The tools as JSON Schema function definitions, for LLM APIs that take
75
+ * `{ name, description, parameters }` (OpenAI / OpenRouter style).
76
+ */
77
+ declare function agentToolsAsJsonSchema(filter?: (tool: AgentTool) => boolean): Array<{
78
+ name: string;
79
+ description: string;
80
+ parameters: Json;
81
+ }>;
82
+
83
+ export { AGENT_TASK_STATUSES, AGENT_TOOLS, AGENT_TOOL_NAMES, QUESTION_WAIT_MAX_SECONDS, agentToolsAsJsonSchema, getAgentTool, runAgentTool };
84
+ export type { AgentScope, AgentTool, AgentsRequester };