Skip to content

Commit 5ef8c7a

Browse files
committed
Add built-in latex editor
1 parent c581c3f commit 5ef8c7a

6 files changed

Lines changed: 383 additions & 6 deletions

File tree

README.md

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ It's built for scientists, analysts, and curious people who want a powerful AI w
1919
- **Choose your AI model** — Pick from 40+ models (OpenAI, Anthropic, Google, xAI, Qwen, and more) through a simple dropdown in the app. You're not stuck with one.
2020
- **326 ready-to-use workflows** — Browse a built-in library of workflow templates spanning 22 disciplines — from genomics and drug discovery to finance and astrophysics. Pick a workflow, fill in the variables, select a model, and launch. Each workflow comes with curated skill suggestions so the agent knows exactly which tools to reach for. Workflows that need uploaded data are clearly marked, and you can upload files directly from the launch dialog.
2121
- **Run heavy computations remotely** — Optionally connect [Modal](https://modal.com/) to run demanding workloads on powerful cloud hardware instead of your laptop.
22+
- **Add custom MCP servers** — Extend the AI experts' capabilities by adding your own [MCP](https://modelcontextprotocol.io/) servers through the Settings panel. Custom servers are merged with the built-in defaults and persist across app restarts.
2223

2324
> **Note:** The model you select in the dropdown only applies to Kady (the main agent). Expert execution and coding tasks use the Gemini CLI, which always runs through a Gemini model on [OpenRouter](https://openrouter.ai/) regardless of your dropdown selection.
2425
@@ -105,9 +106,31 @@ k-dense-byok/
105106
│ ├── agent.py ← Main agent definition
106107
│ └── tools/ ← Tools Kady can use (web search, delegation, etc.)
107108
├── web/ ← Frontend (the UI you see in your browser)
108-
└── sandbox/ ← Workspace for files and expert tasks (created on first run)
109+
├── sandbox/ ← Workspace for files and expert tasks (created on first run)
110+
└── user_config/ ← Your persistent settings (custom MCP servers, etc.)
109111
```
110112

113+
## Adding custom MCP servers
114+
115+
You can extend the tools available to Kady's expert agents by adding your own [MCP](https://modelcontextprotocol.io/) servers. Click the **gear icon** in the top-right corner of the app, then open the **MCP Servers** tab.
116+
117+
The editor accepts a JSON object where each key is a server name and its value is the server configuration. For example:
118+
119+
```json
120+
{
121+
"my-server": {
122+
"command": "npx",
123+
"args": ["-y", "my-mcp-server"]
124+
},
125+
"remote-api": {
126+
"httpUrl": "https://mcp.example.com/api",
127+
"headers": { "Authorization": "Bearer your-token" }
128+
}
129+
}
130+
```
131+
132+
Your custom servers are **merged** with the built-in defaults (docling, parallel-search) and passed to the Gemini CLI. The custom configuration is saved in `user_config/custom_mcps.json` at the project root — outside the `sandbox/` directory — so it survives sandbox deletion and app restarts.
133+
111134
## Why "BYOK"?
112135

113136
BYOK stands for **Bring Your Own Keys**. Instead of paying a subscription to a single AI company, you plug in API keys from whatever providers you prefer. You stay in control of which models you use, how much you spend, and where your data goes.
@@ -177,7 +200,7 @@ If you hit a case where a skill isn't behaving as expected, try re-running the t
177200
- Better utilization of Skills
178201
- Choose what model to use with Gemini CLI
179202
- Choice between Claude Code or Gemini CLI as the delegation expert
180-
- Support of MCP config in the UI (can be done in the code right now but we want to make it easier)
203+
- ~~Support of MCP config in the UI~~ — Done! Open **Settings > MCP Servers** to add custom servers.
181204
- Better UI experience tailored to scientific workflows
182205
- Faster PDF parsing
183206
- AutoResearch integration

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "kady"
3-
version = "0.2.7"
3+
version = "0.2.8"
44
description = "Kady — K-Dense BYOK Agent"
55
readme = "README.md"
66
requires-python = ">=3.13"

web/src/app/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ function mentionIconForFile(name: string): ReactNode {
207207
if (cat === "biotable") return <TableIcon className="size-3.5 text-indigo-500" />;
208208
if (cat === "image") return <FileImageIcon className="size-3.5 text-rose-500" />;
209209
if (cat === "markdown") return <FileTextIcon className="size-3.5 text-emerald-600" />;
210+
if (cat === "latex") return <FileCodeIcon className="size-3.5 text-teal-500" />;
210211
if (ext === "json" || ext === "jsonl") return <FileJsonIcon className="size-3.5 text-amber-600" />;
211212
const codeExts = ["py","ts","tsx","js","jsx","rs","go","java","c","cpp","h","rb","sh","bash","css","html","xml","yaml","yml","toml","sql"];
212213
if (codeExts.includes(ext)) return <FileCodeIcon className="size-3.5 text-violet-500" />;
@@ -845,6 +846,7 @@ export default function ChatPage() {
845846
onDownload={sandbox.downloadFile}
846847
onSaveText={sandbox.saveFile}
847848
onSaveImageBlob={sandbox.saveImageBlob}
849+
onCompileLatex={sandbox.compileLatex}
848850
/>
849851
</div>
850852
)}

web/src/components/file-preview-panel.tsx

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
"use client";
22

33
import { MessageResponse } from "@/components/ai-elements/message";
4+
import { LatexEditor } from "@/components/latex-editor";
45
import { cn } from "@/lib/utils";
5-
import { fileCategory, rawFileUrl, type Tab } from "@/lib/use-sandbox";
6+
import { fileCategory, rawFileUrl, type Tab, type LatexCompileResult } from "@/lib/use-sandbox";
67
import CodeMirror, { EditorView } from "@uiw/react-codemirror";
78
import { loadLanguage, type LanguageName } from "@uiw/codemirror-extensions-langs";
89
import { githubLight } from "@uiw/codemirror-theme-github";
@@ -95,6 +96,7 @@ function categoryLabel(name: string): string {
9596
if (cat === "markdown") return "markdown";
9697
if (cat === "csv") return "csv";
9798
if (cat === "notebook") return "jupyter";
99+
if (cat === "latex") return "latex";
98100
if (cat === "fasta") return ext === "fastq" || ext === "fq" ? "fastq" : "fasta";
99101
if (cat === "biotable") return ext;
100102
return langForFile(name);
@@ -1165,6 +1167,7 @@ export interface FilePreviewPanelProps {
11651167
onDownload: (path: string) => void;
11661168
onSaveText: (path: string, content: string) => Promise<boolean>;
11671169
onSaveImageBlob: (path: string, blob: Blob) => Promise<boolean>;
1170+
onCompileLatex?: (path: string, engine?: string) => Promise<LatexCompileResult>;
11681171
}
11691172

11701173
export function FilePreviewPanel({
@@ -1175,6 +1178,7 @@ export function FilePreviewPanel({
11751178
onDownload,
11761179
onSaveText,
11771180
onSaveImageBlob,
1181+
onCompileLatex,
11781182
}: FilePreviewPanelProps) {
11791183
// Per-tab mode tracking
11801184
const [tabModes, setTabModes] = useState<Record<string, PanelMode>>({});
@@ -1257,8 +1261,26 @@ export function FilePreviewPanel({
12571261
</div>
12581262
)}
12591263

1260-
{/* Edit mode */}
1261-
{selectedPath && mode === "edit" && (
1264+
{/* Edit mode — LaTeX gets the split-pane editor */}
1265+
{selectedPath && mode === "edit" && cat === "latex" && onCompileLatex && (
1266+
<>
1267+
{header}
1268+
<div className="flex-1 min-h-0">
1269+
<LatexEditor
1270+
key={selectedPath}
1271+
path={selectedPath}
1272+
name={selectedName ?? ""}
1273+
initialContent={fileContent ?? ""}
1274+
onSave={(content) => onSaveText(selectedPath, content)}
1275+
onCompile={onCompileLatex}
1276+
onDiscard={() => setMode(selectedPath, "view")}
1277+
/>
1278+
</div>
1279+
</>
1280+
)}
1281+
1282+
{/* Edit mode — standard text editor */}
1283+
{selectedPath && mode === "edit" && (cat !== "latex" || !onCompileLatex) && (
12621284
<>
12631285
{header}
12641286
<div className="flex-1 min-h-0">

0 commit comments

Comments
 (0)