# Talkano MCP — Entegrasyon & API Dökümanı

Bu belge, **Talkano.ai**'ye programatik olarak bağlanıp bir işletmenin bilgi
tabanını (soru-cevap, ürün, döküman, persona, çalışma saatleri) **paket
sınırları içinde** doldurmak isteyen geliştiriciler/AI ajanları içindir.

> Son kullanıcı kurulum rehberi (ekran ekran Claude / ChatGPT / Gemini
> adımları, güvenlik, sorun giderme): https://talkano.ai/docs/mcp-baglanti
> (İngilizce: https://talkano.ai/en/docs/mcp-baglanti)

> **ÖNEMLİ — kimlik doğrulama:** Talkano'da klasik "statik API Key / Bearer
> token yapıştır" YOKTUR. Entegrasyon yüzeyi bir **MCP (Model Context Protocol)
> sunucusudur** ve kimlik doğrulama **OAuth 2.1** (PKCE + kullanıcı onayı) ile
> yapılır. Erişim jetonu (Bearer JWT) bu OAuth akışının SONUCUNDA alınır. Yani
> makineler-arası (kullanıcısız) bir client-credentials/API-key yolu yoktur;
> her bağlantı bir Talkano kullanıcısına aittir ve onun onayıyla oluşur.
>
> ChatGPT, Claude ve Gemini CLI bu OAuth+MCP akışını **yerleşik olarak**
> destekler — çoğu durumda tek yapman gereken connector'a
> `https://talkano.ai/api/mcp` adresini vermek; gerisini (kayıt, authorize,
> token) istemci halleder.

- **MCP sunucu adresi (resource):** `https://talkano.ai/api/mcp`
- **Transport:** Streamable HTTP (tek uç; JSON-RPC 2.0). SSE değil.
- **Protokol sürümü:** `2025-06-18` (MCP)
- **Kimlik:** OAuth 2.1 · Authorization Code + PKCE (S256) · Bearer JWT

---

## 1. Hızlı yol (önerilen): ChatGPT / Claude / Gemini connector

En pratik entegrasyon, MCP'yi yerleşik destekleyen bir istemci (ChatGPT,
Claude veya Gemini CLI) üzerinden bağlanmaktır:

1. İstemcinin connector/MCP ayarına git.
2. Sunucu adresi olarak `https://talkano.ai/api/mcp` ver.
3. İstemci OAuth'u otomatik başlatır → **Talkano giriş/kayıt + izin** ekranı
   açılır → onayla.
4. Bağlantı hazır. Artık aşağıdaki **araçları** (tools) doğal dille
   çağırabilirsin: "bana bir işletme oluştur", "şu siteyi tara ve doldur",
   "şu soru-cevabı ekle" …

**Gemini CLI** için tek komut yeter (dosya düzenleme yok):

```bash
gemini mcp add --transport http talkano https://talkano.ai/api/mcp
```

Sonra `gemini` başlat — OAuth ilk kullanımda otomatik tetiklenir (401 → keşif →
tarayıcı; loopback `http://localhost:<port>` redirect'i desteklenir, RFC 8252).
Gerekirse elle: `/mcp auth talkano`. Not: Gemini **web** uygulaması özel MCP
bağlayıcılarını henüz desteklemiyor.

Kendi istemcini/ajanını yazıyorsan (aşağıdaki bölümler) OAuth akışını ve MCP
JSON-RPC çağrılarını elle yapman gerekir.

---

## 2. OAuth 2.1 akışı (kendi istemcin için)

Talkano bir OAuth 2.1 **Authorization Server**'dır. Keşif ve uçlar:

### 2.1 Keşif (discovery)

- **Protected Resource Metadata (RFC 9728):**
  `GET https://talkano.ai/.well-known/oauth-protected-resource`
  ```json
  {
    "resource": "https://talkano.ai/api/mcp",
    "authorization_servers": ["https://talkano.ai"],
    "scopes_supported": ["talkano"],
    "bearer_methods_supported": ["header"]
  }
  ```
- **Authorization Server Metadata (RFC 8414):**
  `GET https://talkano.ai/.well-known/oauth-authorization-server`
  ```json
  {
    "issuer": "https://talkano.ai",
    "authorization_endpoint": "https://talkano.ai/oauth/authorize",
    "token_endpoint": "https://talkano.ai/oauth/token",
    "registration_endpoint": "https://talkano.ai/oauth/register",
    "response_types_supported": ["code"],
    "grant_types_supported": ["authorization_code", "refresh_token"],
    "code_challenge_methods_supported": ["S256"],
    "token_endpoint_auth_methods_supported": ["none"],
    "scopes_supported": ["talkano"]
  }
  ```

Kimliksiz `POST /api/mcp` çağrısı **401** + `WWW-Authenticate: Bearer …
resource_metadata="…/.well-known/oauth-protected-resource"` döner — istemci bu
zincirle AS'i bulur.

### 2.2 Dynamic Client Registration (RFC 7591)

Public client (secret yok, PKCE zorunlu). `redirect_uris` **https** ya da
loopback (`http://localhost`, `http://127.0.0.1`, `http://[::1]` — RFC 8252)
olmalı; loopback dışı `http` reddedilir.

```
POST https://talkano.ai/oauth/register
Content-Type: application/json

{ "client_name": "Benim Ajanım", "redirect_uris": ["https://benim-uygulamam.com/callback"] }
```
Yanıt (201):
```json
{
  "client_id": "…",
  "redirect_uris": ["https://benim-uygulamam.com/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
```

### 2.3 Authorization (tarayıcı — kullanıcı onayı)

Kullanıcıyı tarayıcıda şu adrese yönlendir (PKCE `code_verifier` üret,
`code_challenge = BASE64URL(SHA256(code_verifier))`):

```
GET https://talkano.ai/oauth/authorize
  ?response_type=code
  &client_id=<client_id>
  &redirect_uri=<kayıtlı redirect_uri>
  &code_challenge=<S256 challenge>
  &code_challenge_method=S256
  &scope=talkano
  &state=<rastgele>
```
Kullanıcı Talkano'ya giriş yapar / **üye olur** ve izin verir. Ardından
`redirect_uri?code=<auth_code>&state=<state>` adresine döner. (Kod ~5 dk geçerli,
tek kullanımlık.)

### 2.4 Token exchange (authorization_code + PKCE)

```
POST https://talkano.ai/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=<auth_code>
&redirect_uri=<aynı redirect_uri>
&client_id=<client_id>
&code_verifier=<PKCE verifier>
```
Yanıt:
```json
{
  "access_token": "<JWT>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "<opaque>",
  "scope": "talkano"
}
```
- **access_token**: 1 saat geçerli imzalı JWT (aud=`https://talkano.ai/api/mcp`).
  MCP çağrılarında `Authorization: Bearer <access_token>` olarak gönder.
- **refresh_token**: 30 gün. Yenileme (rotasyonlu — `client_id` ZORUNLU):
  ```
  POST /oauth/token
  grant_type=refresh_token&refresh_token=<…>&client_id=<client_id>
  ```

---

## 3. MCP çağrıları (JSON-RPC 2.0 over Streamable HTTP)

Tüm çağrılar tek uca, **Bearer** ile:

```
POST https://talkano.ai/api/mcp
Authorization: Bearer <access_token>
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2025-06-18
```
Yanıt gövdesi genelde **SSE** biçimindedir (`data: {json}`); JSON'u `data:`
satırından ayıkla. Sunucu **stateless**'tır (oturum id'si tutmaya gerek yok).

### 3.1 initialize
```json
{ "jsonrpc": "2.0", "id": 1, "method": "initialize",
  "params": { "protocolVersion": "2025-06-18", "capabilities": {},
              "clientInfo": { "name": "benim-ajanim", "version": "1.0" } } }
```

### 3.2 tools/list
```json
{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
```
→ 9 aracı ve JSON Schema girdilerini döner (aşağıdaki bölüm).

### 3.3 tools/call
```json
{ "jsonrpc": "2.0", "id": 3, "method": "tools/call",
  "params": { "name": "add_qa",
              "arguments": { "question": "Otopark var mı?", "answer": "Evet, ücretsiz." } } }
```
→ Sonuç:
```json
{ "result": { "content": [ { "type": "text", "text": "Soru-cevap eklendi (ornek-pansiyon). \"Otopark var mı?\"" } ] } }
```
Hata (kota aşımı, yetki vb.) yine `result.content[0].text` içinde kullanıcı-dostu
bir mesajla ve `"isError": true` ile döner (JSON-RPC error değil).

---

## 4. Araçlar (tools) — "API operasyonları"

Hepsi çok-kiracılı sınırda çalışır: her araç, **jetondaki kullanıcının üyesi
olduğu** işletmeye yazar (owner/admin rolü şart). `business` parametresi işletme
"slug"udur; kullanıcının TEK işletmesi varsa boş bırakılabilir, birden çoksa
gereklidir (`whoami` ile öğren).

| Araç | Girdi (JSON Schema özeti) | Ne yapar |
|---|---|---|
| `whoami` | `{}` | Kullanıcının işletmeleri (slug, ad, paket, rol) + yön bulma. Yazmadan önce çağır. |
| `create_business` | `{ name: string(2–120) }` | Yeni işletme (org + owner). Demo paketle açılır (kullanım kotası dahil). Kullanıcı başına en çok 10. |
| `add_qa` | `{ business?, question: string(1–500), answer: string(1–4000) }` | Bilgi tabanına soru-cevap ekler. |
| `add_product` | `{ business?, name: string(1–200), description?: string(≤2000), price?: number(≥0), currency?: string(≤8, vars. TRY) }` | Ürün/hizmet ekler. |
| `add_document` | `{ business?, title: string(1–200), content: string(1–60000) }` | Uzun metin döküman (menü, şartlar…). Demo pakette döküman kapalıdır. |
| `set_persona` | `{ business?, instructions?: string(≤16000), tone?: "friendly"\|"professional"\|"playful"\|"formal"\|"custom", language?: "auto"\|"tr"\|"en"\|"de"\|"fr"\|"es"\|"ar"\|"ru", forbiddenTopics?: string(≤4000), handoffMessage?: string(≤1000) }` | Asistanın kişiliği/talimatı. Yalnız verdiğin alanlar güncellenir. |
| `set_hours` | `{ business?, enabled: boolean, timezone?: string(vars. Europe/Istanbul), open?: "HH:MM", close?: "HH:MM", days?: ("mon"\|"tue"\|"wed"\|"thu"\|"fri"\|"sat"\|"sun")[], offHoursMode?: "silent"\|"away_message"\|"delayed_ai", offHoursMessage?: string(≤1000) }` | Çalışma saatleri. `enabled:false` → her saat cevap. |
| `crawl_site_and_fill` | `{ business?, url: string }` | Siteyi güvenle tarar; paket destekliyorsa döküman kaydeder, HER durumda çıkarılan metni döndürür → sonra add_qa/add_product/set_persona ile yapılandır. **Sıfırdan doldurmak için en hızlı yol.** |
| `knowledge_summary` | `{ business? }` | Bilgi tabanı doluluğu (ürün/soru-cevap/döküman/foto sayıları + paket limitleri) + persona durumu. |

---

## 5. Tipik "veri doldurma" akışı

1. `whoami` → hangi işletme(ler) var, hangi paket. Yoksa →
2. `create_business { name }` → yeni işletme (slug döner).
3. `crawl_site_and_fill { url }` → siteyi tara; dönen metinden bilgileri çıkar.
4. Çıkarılan bilgiyi yapılandır:
   - `add_qa` (SSS), `add_product` (ürün/oda/hizmet), `add_document` (menü/şartlar).
   - `set_persona` (ton/dil/talimat), `set_hours` (çalışma saatleri).
5. `knowledge_summary` → ne eklendi, ne eksik, limitlerin neresindesin.

---

## 6. Paket limitleri, hız sınırı, hatalar

- **Paket limitleri** (yazarken uygulanır; aşınca araç nazikçe reddeder ve bir
  üst paketi adıyla önerir):
  - Demo: 20 soru-cevap, 5 ürün, **0 döküman**, 0 foto.
  - Başlangıç: 50 soru-cevap, 20 ürün, 5 döküman, 20 foto.
  - Pro: 1000 soru-cevap (+ vektörel arama), 100 ürün, 20 döküman, 200 foto.
  - Custom: sözleşmeye göre.
- **Hız sınırı:** işletme başına **saatte 120** bilgi-tabanı yazımı
  (soru-cevap + ürün + döküman toplamı). Aşılırsa geçici reddeder.
- **create_business:** kullanıcı başına en çok **10** işletme.
- **Hata biçimi:** İş kuralı/kota hataları `tools/call` sonucunda
  `result.isError=true` + `content[0].text` (Türkçe mesaj) olarak döner —
  JSON-RPC `error` alanı değil. OAuth/kimlik hataları HTTP `401` + standart
  OAuth `error` gövdesiyle döner.
- **Güvenlik:** access token yalnız `aud=https://talkano.ai/api/mcp` için
  geçerlidir; başka servise iletme. `crawl_site_and_fill` SSRF-korumalıdır
  (özel/iç ağ adresleri reddedilir). Model/sağlayıcı adı asla sızmaz (kapalı kutu).

---

## 7. Uçtan uca örnek (kavramsal, JS)

```js
// 1) DCR
const reg = await fetch('https://talkano.ai/oauth/register', {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ client_name: 'Ajan', redirect_uris: ['https://app.example/cb'] }),
}).then(r => r.json())

// 2) PKCE + kullanıcıyı /oauth/authorize'a yönlendir → code al (tarayıcı adımı)
//    code_challenge = base64url(sha256(code_verifier))

// 3) Token
const tok = await fetch('https://talkano.ai/oauth/token', {
  method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code', code, redirect_uri: 'https://app.example/cb',
    client_id: reg.client_id, code_verifier,
  }),
}).then(r => r.json())

// 4) MCP çağrısı
async function mcp(method, params) {
  const r = await fetch('https://talkano.ai/api/mcp', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${tok.access_token}`,
      'Content-Type': 'application/json',
      Accept: 'application/json, text/event-stream',
      'MCP-Protocol-Version': '2025-06-18',
    },
    body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method, params }),
  })
  const t = await r.text()
  const m = t.match(/data:\s*(\{[\s\S]*\})/)
  return m ? JSON.parse(m[1]) : JSON.parse(t)
}

await mcp('initialize', { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'ajan', version: '1' } })
console.log(await mcp('tools/list'))
console.log(await mcp('tools/call', { name: 'whoami', arguments: {} }))
```

---

**Özet:** Talkano'nun entegrasyon yüzeyi = `https://talkano.ai/api/mcp` (MCP,
JSON-RPC/Streamable HTTP) + **OAuth 2.1** (statik API key YOK; Bearer JWT OAuth
akışıyla alınır). 9 araçla, paket sınırları içinde, işletme bilgisi eklenir.
En kolay yol: ChatGPT/Claude connector'ına yukarıdaki adresi vermek.
