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.

File bundle, not a single functionBring any npm SDKCredentials never touch your 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 like

The 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 in egress.
  • 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 --json for the machine schema).
  • @kma/connectors-sdk — the types you write against.