# @aigentyc/mcp
> Model Context Protocol server for the Aigentyc platform. Lets Claude Code, Cursor, Windsurf, Claude Desktop, and any MCP-compatible agent drive an Aigentyc project end-to-end: add documents, configure the chat agent, build custom tools — and scaffold or embed the front-end chat widget (`@aigentyc/chat-sdk`) — all from one conversation. The output is a working chat experience on the user's site.
## Zero-to-chat in one conversation
```
You: "Add my docs/ folder, set the system prompt to be terse and helpful,
and scaffold a Next.js chat app at ./my-app."
Claude: aigentyc_get_started → "kb empty, no system prompt"
files_upload({ paths: [...] }) → 14 docs ingested
config_update({ patch: { systemPrompt: "..." } })
aigentyc_get_started → "snippetReady: true"
chat_widget_scaffold({ destination: "./my-app", template: "next" })
→ ✓ done. cd my-app && npm install && npm run dev
```
## End-to-end recipe
1. **Discover state** — `aigentyc_get_started` returns project status + prioritised `nextSteps`. Always call this first in a new conversation.
2. **Add content** — pick the right ingest tool: `files_upload` (local files), `documents_create_from_text` (raw text), `extract_from_urls` (batch URLs), `link_sources_create` (crawl a website).
3. **Tune the chat** — `config_update` for the system prompt + chat model, `personas_upsert` for audience-specific tone, `tools_create` for custom REST/JS actions exposed to chat.
4. **Embed** — three options:
- **`chat_widget_setup`** — confirms readiness, returns paste-ready snippet + install commands. Use when user has an app already.
- **`chat_widget_scaffold`** — runs `npm create aigentyc-chat@latest
` to bootstrap a fresh Vite or Next.js starter wired to the project. For greenfield apps.
- **`chat_widget_get_snippet`** — just the snippet, no readiness check. For when you've already verified setup.
## Install
```bash
npx @aigentyc/mcp login \
--api-key tyco_pk_XXXX \
--project-id proj_XXXX
```
`login` verifies the key against `/api/auth/api-keys/verify` and writes `~/.aigentyc/config.json` with `0600` perms. The server refuses to start if perms are wider.
**Dev-only flag**: pass `--allow-insecure` to permit plaintext HTTP against non-loopback hosts. Never use this against production.
## Wire into your agent
### Claude Desktop / Claude Code
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
```json
{
"mcpServers": {
"aigentyc": {
"command": "npx",
"args": ["-y", "@aigentyc/mcp"]
}
}
}
```
### Cursor
Settings → MCP → add server:
```json
{
"aigentyc": {
"command": "npx",
"args": ["-y", "@aigentyc/mcp"]
}
}
```
Restart the client. The new tools appear under the `aigentyc` namespace.
## Commands
```
aigentyc-mcp serve Start the stdio MCP server (default)
aigentyc-mcp login Save + verify an API key profile
aigentyc-mcp logout Remove a profile
aigentyc-mcp doctor Verify config + transport + clock skew + reachability
```
## Tools (86 total, 20 domains)
- **embed (E2E entry points)** — aigentyc_get_started, chat_widget_setup, chat_widget_get_snippet, chat_widget_scaffold
- **documents** — list, get, delete, create_from_text, merge, versions
- **files** — list, get, upload (path-based), delete, reprocess, versions, bulk_audience
- **extract** — from_urls (jobified for api-key)
- **link_sources** — list, get, create, update, delete, list_items, update_item, delete_item
- **page_questions** — list, set_manual, auto_generate (jobified), delete_page
- **data_stores** — full CRUD + bulk_upsert + import + export + scan_website (jobified)
- **tools** (custom-tool CRUD) — list, get, create, update, delete, execute_dry_run
- **flows** — list, get, create, update, delete
- **golden_answers** — list, create, update, delete + ai_improve + ai_generate_variations
- **corrections** — list, create, update_status
- **personas** — list, upsert, delete
- **config** — get (secrets redacted for api-key), update (secret writes rejected)
- **backups** — list, create (jobified), restore (jobified), download, import, delete
- **analytics** — overview, sessions, session_timeline, comments
- **branches** — list
- **search** — kb_search (read-only authoring aid)
- **jobs** — status, list, wait
## Async job pattern
Long-running ops (backup create/restore, extract, data-store scan, page-questions auto-generate) return `{ jobId, status: "queued" }`. Poll with `jobs_wait`:
```
extract_from_urls({ urls: [...] })
→ { jobId: "abc-123", status: "queued" }
jobs_wait({ jobId: "abc-123" })
→ blocks until terminal; returns { status: "completed", result: {...} }
```
Concurrency cap: 3 active jobs per project.
## Security
- **Project-scoped keys**: a key for project A cannot read/write project B (403).
- **Per-key rate limits**: 300 reads/min, 60 writes/min. 429 with `Retry-After` over the limit.
- **Secrets**: `config.get` redacts `openaiApiKey` + `customHttpHeaders` to `***` for api-key callers. `config.update` rejects writes to those fields with 403. Backup downloads (single + ZIP) deep-redact secrets.
- **Custom tool code**: `tools.create` rejects `transformCode` / `executeCode` containing `process.env`. Use `apiConfig.headers` with template placeholders for secrets.
- **Destructive ops** (`*.delete`, `backups.restore`): require `confirm: true`.
- **Path uploads**: `files.upload` refuses paths outside `$CWD` / `$HOME`, refuses non-regular files, caps 50MB/file, 500MB/batch.
- **URL inputs**: `extract.from_urls` + `link_sources.create` reject RFC1918 / loopback / cloud-metadata hosts.
- **Audit log**: every api-key request is logged server-side (`api_key_audit_log` table) with `keyId`, `projectId`, `route`, `method`, `status`, `latencyMs`, `requestId`. Reads sampled at 10%; writes 100%.
## Recipes
### Vibe-coder one-shot — docs to embedded chat in 60 seconds
```
1. aigentyc_get_started()
2. files_upload({ paths: ["docs/**/*.md"], indexAsDocuments: true })
3. config_update({ patch: { systemPrompt: "You are the support agent for ACME. Cite sources from docs." } })
4. chat_widget_scaffold({ destination: "./acme-chat", template: "next", confirm: true })
5. → cd acme-chat && npm install && npm run dev
```
### Add a markdown KB from local files
```
files_upload({ paths: ["docs/intro.md", "docs/api.md"], indexAsDocuments: true })
```
The MCP reads each file (text formats only — `.md`, `.txt`, `.json`, `.csv`, `.html`, …), uploads metadata to `uploaded_files`, then chunks + embeds + indexes via `documents/create`. After ingest, `chat_widget_setup` returns paste-ready snippet.
### Crawl a website + dedupe before adding more docs
```
1. link_sources_create({ sourceUrl: "https://example.com", sourceType: "website" })
2. jobs_wait({ jobId: ... }) — wait for crawl to finish
3. search_kb_search({ query: "billing FAQ" }) // confirm it indexed
```
### Build a custom tool end-to-end
```
1. tools_create({
name: "submitForm",
toolType: "rest_api",
apiConfig: { method: "POST", url: "...", body: "name={{name}}" },
inputSchema: [{ name: "name", type: "string", required: true }],
})
2. tools_execute_dry_run({ toolId, input: { name: "Test" } })
// verify output before exposing to chat
```
### Update system prompt
```
config_update({ patch: { systemPrompt: "You are the support agent for ACME..." } })
```
(Cannot update `openaiApiKey` over api-key — use the dashboard for secrets.)
## Troubleshooting
- **404 on login** — auth-service may be on a different origin in dev. `login` falls back to dashboard ping. Use `--base-url http://localhost:3000` for local Docker.
- **Tool calls hang** — confirm `aigentyc-mcp doctor` passes. Most failures are wrong base-url, expired key, or clock skew (>60s breaks auth).
- **Tool created but UI doesn't show it** — fully restart Claude Desktop / Cursor. Server-side caches invalidate on every write; client-side router caches don't.
- **Empty fields on tool call** — required string fields with `min(1)` get rejected by Zod, prompting the LLM to gather real values from the user. If a tool fires with empty inputs, the field wasn't marked required at creation.
## Versioning
Server (`@aigentyc/mcp` on npm) and platform (`app.aigentyc.com`) version independently. The platform maintains backward compatibility with older MCP releases — endpoints aren't broken without deprecation.