Developers
Build a connector
A connector teaches KissMyChat to read from a system you use. It’s a small folder — a manifest plus a bit of code — that does three things: sync documents into the Brain, expose live tools an agent can call at answer time, and test itself. The kmc CLI scaffolds, validates, tests, builds, and publishes one — fast enough to pair with Claude Code.
Quickstart
Five commands, start to published:
pnpm kmc new acme --auth api_key --egress api.acme.com pnpm kmc validate acme # manifest + wiring + egress coverage pnpm kmc test acme --account apiKey=$ACME_KEY --sync pnpm kmc build acme # bundle + checksum pnpm kmc publish acme # upload; lands in review
Authoring with an AI agent? Hand it the full contract and let it fill the TODOs:
pnpm kmc spec --json # the manifest JSON Schema + a worked example
Anatomy of a connector
kmc new writes a folder:
acme/
connector.json # the manifest — identity, auth, capabilities, tools, egress
index.ts # export default defineConnector({ sync, tools, test })
lib/** # anything else; import an npm SDK if you likeThe manifest (connector.json)
Declarative. It produces the source type, the provider auth, and the tool schemas.
{
"id": "acme", // == the source type; snake_case
"name": "Acme",
"version": "0.1.0",
"category": "CRM & Support",
"capabilities": ["knowledge", "readTools"],
"auth": {
"kind": "api_key", // "none" | "oauth2" | "api_key"
"keyHint": "Acme → Settings → API",
"inject": { "header": "authorization", "format": "Bearer {token}" }
},
"egress": ["api.acme.com"], // hosts you may call (enforced in the sandbox)
"tools": [
{ "id": "acme_search", "kind": "read",
"label": "Search Acme", "description": "Search Acme live.",
"inputSchema": { "type": "object", "properties": { "query": { "type": "string" } }, "required": ["query"] } }
],
"testAccount": { "fields": [{ "key": "apiKey", "target": "config", "label": "Acme test token", "input": "text" }] }
}The module (index.ts)
The runtime. Every method is optional. You only ever touch the outside world through ctx.
import { defineConnector } from "@kma/connectors-sdk";
export default defineConnector({
async sync(ctx) {
const res = await ctx.http.fetch("https://api.acme.com/docs", ctx.auth.authorize());
const items = (await res.json()) as { id: string; title: string; body: string; url: string }[];
return { docs: items.map((i) => ({ externalId: i.id, title: i.title, url: i.url, markdown: i.body })) };
},
tools: {
acme_search: {
async run(ctx, input) {
const res = await ctx.http.fetch(`https://api.acme.com/search?q=${encodeURIComponent(String(input.query))}`, ctx.auth.authorize());
const hits = (await res.json()) as { title: string; url: string }[];
return hits.map((h) => `- ${h.title} (${h.url})`).join("\n") || "No results.";
},
},
},
async test(ctx) {
const res = await ctx.http.fetch("https://api.acme.com/me", ctx.auth.authorize());
return { ok: res.ok, detail: res.ok ? "auth OK" : `status ${res.status}` };
},
});The context (and why it's safe)
ctx.http.fetch(url, init)— the only network primitive. In the sandbox it’s the only way out, and it’s restricted to the hosts inegress.ctx.auth.authorize(init)— marks a request authenticated. The host injects the real token during egress, formatted per the manifest. Your code never sees the credential — so the same connector runs trusted (official, in-process) or sandboxed (community) with no changes.ctx.label/ctx.config— the source instance’s identifier + settings.ctx.state·ctx.logger·ctx.signal— cursor storage, logging, cancellation.
Worked example: Notion
Notion ships as an official connector — a good template. Its sync searches shared pages and mirrors each as markdown; its tools live-read at answer time; its test pings the API. Test it end-to-end with a Notion internal integration token (share a page with the integration first):
pnpm kmc test packages/connectors-host/connectors/notion \ --account token=secret_XXXXXXXX --sync
💋 kmc · test notion ✓ passing · 412ms · auth OK ✓ sync 7 document(s) · 1980ms · Engineering Handbook · Refund Policy · …
In the product you’d instead connect Notion via OAuth in a workspace, add a Notion source, and it syncs on a schedule.
Publish & review
kmc build bundles your folder (with a checksum); kmc publish uploads it to the registry, where it lands in review. Official connectors are first-party and run in-process; community connectors are untrusted and run in a network-restricted sandbox — the egress allow-list is enforced and the credential is injected outside your code. A workspace installs a connector at a pinned version.
Reference
pnpm kmc help— every command.pnpm kmc spec— the authoring contract (add--jsonfor the machine schema).@kma/connectors-sdk— the types you write against.