fix(web): address PR review (clean .md output, one MCP config source)

- .md output rewrites MDX-only syntax (Tabs, TabItem, asides, the MCP
  client component) into plain Markdown.
- MCP client setups live in one data file used by the landing page, the
  setup page and agents/install; the smoke-test claim names the five
  agents agent-smoke.sh actually drives (not Cursor).
- The landing page reads the version from Cargo.toml; the hand-copied BBCA
  data stays dated, and the design doc no longer promises capture.sh.
- MCP overview counts 11 tools plus the opt-in ownership_sync.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
hermes 2026-10-15 05:29:02 +00:00
commit 3623c50953
8 changed files with 101 additions and 121 deletions

View file

@ -120,7 +120,7 @@ stack.
| --- | --- | --- | | --- | --- | --- |
| `/cli/reference` | `idx --help` per command | a script runs the built binary and writes Markdown | | `/cli/reference` | `idx --help` per command | a script runs the built binary and writes Markdown |
| `/mcp/tools` | `crates/idx-mcp/tests/golden/tools.json` | rendered at build time from the golden | | `/mcp/tools` | `crates/idx-mcp/tests/golden/tools.json` | rendered at build time from the golden |
| Terminal panels | real `idx` runs | `web/scripts/capture.sh` saves output with a date; refreshed by hand | | Terminal panels, BBCA table | real `idx` runs | copied by hand and labelled with their date; refresh after a new month |
| Install, guides | hand-written | reviewed when the README changes | | Install, guides | hand-written | reviewed when the README changes |
The README shrinks to a pitch, one install line per channel, and a link to the The README shrinks to a pitch, one install line per channel, and a link to the
@ -167,7 +167,7 @@ lives:
- **Hermes:** `config.yaml`, `mcp_servers.idx`. - **Hermes:** `config.yaml`, `mcp_servers.idx`.
- **Other:** the `idx-mcp` command over stdio. - **Other:** the `idx-mcp` command over stdio.
These configs are the ones `tests/agents/agent-smoke.sh` already runs, so the The Claude Code, Codex, opencode, pi and Hermes configs are the ones `tests/agents/agent-smoke.sh` already runs, so the
site can link that script as proof they work. site can link that script as proof they work.
## Stack ## Stack

View file

@ -0,0 +1,15 @@
---
import { Code, TabItem, Tabs } from '@astrojs/starlight/components';
import { mcpClients } from '../data/mcp-clients';
---
<Tabs syncKey="agent">
{
mcpClients.map((c) => (
<TabItem label={c.label}>
<p>{/[~/]/.test(c.file) ? <code>{c.file}</code> : c.file}</p>
<Code code={c.code} lang={c.lang} />
</TabItem>
))
}
</Tabs>

View file

@ -3,6 +3,8 @@ title: Agent-led install
description: Instructions for a coding agent to install idx and register idx-mcp with itself. description: Instructions for a coding agent to install idx and register idx-mcp with itself.
--- ---
import McpClientTabs from '../../../components/McpClientTabs.astro';
This page is written for a coding agent. A user sent you here to install This page is written for a coding agent. A user sent you here to install
**idx** (a CLI for Indonesian stock market and KSEI ownership data) and **idx** (a CLI for Indonesian stock market and KSEI ownership data) and
register its MCP server, **idx-mcp**, with the agent harness you are running register its MCP server, **idx-mcp**, with the agent harness you are running
@ -44,22 +46,14 @@ Confirm with `idx version` (or `npx -y idx-cli version`).
## 3. Register idx-mcp with yourself ## 3. Register idx-mcp with yourself
Work out which harness you are, then apply its setup. Use `npx` and Work out which harness you are, then apply its setup below. If you installed
`["-y", "idx-mcp"]` as command and arguments, or `idx-mcp` with no arguments the `idx-mcp` binary instead of using Node.js, use the command `idx-mcp` with
if you installed the binary. no arguments in place of `npx -y idx-mcp`. For any other harness, find how it
registers a stdio MCP server, and ask the user if unsure.
| Harness | How | <McpClientTabs />
| --- | --- |
| Claude Code | run `claude mcp add idx -- npx -y idx-mcp` |
| Codex | add to `~/.codex/config.toml`: `[mcp_servers.idx]` with `command = "npx"`, `args = ["-y", "idx-mcp"]` |
| Cursor | add to `~/.cursor/mcp.json` under `mcpServers`: `"idx": {"command": "npx", "args": ["-y", "idx-mcp"]}` |
| opencode | add to `~/.config/opencode/opencode.json` under `mcp`: `"idx": {"type": "local", "command": ["npx", "-y", "idx-mcp"], "enabled": true}` |
| pi | add to `~/.pi/agent/mcp.json` under `mcpServers`: `"idx": {"command": "npx", "args": ["-y", "idx-mcp"], "exposure": "direct"}` |
| Hermes | add to `~/.hermes/config.yaml` under `mcp_servers`: `idx` with `command: npx`, `args: ["-y", "idx-mcp"]`, `enabled: true` |
| anything else | find how your harness registers a stdio MCP server, and ask the user if unsure |
Merge into existing files; never overwrite other servers or settings. Full Merge into existing files; never overwrite other servers or settings.
examples: [MCP setup](/mcp/setup/).
## 4. Download ownership data ## 4. Download ownership data

View file

@ -5,7 +5,7 @@ description: idx-mcp serves idx's stock and ownership data to AI agents over MCP
`idx-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) `idx-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io)
server over stdio. It gives an agent the same data as the `idx` CLI, as server over stdio. It gives an agent the same data as the `idx` CLI, as
[11 tools](/mcp/tools/): quotes, price history, screening, company data, [11 tools](/mcp/tools/) (plus an opt-in `ownership_sync`): quotes, price history, screening, company data,
analysis, comparison, and the KSEI ownership queries. analysis, comparison, and the KSEI ownership queries.
It shares idx's [config, cache and ownership database](/start/config/#files-idx-keeps), It shares idx's [config, cache and ownership database](/start/config/#files-idx-keeps),

View file

@ -5,7 +5,7 @@ sidebar:
label: Setup label: Setup
--- ---
import { Tabs, TabItem } from '@astrojs/starlight/components'; import McpClientTabs from '../../../components/McpClientTabs.astro';
:::tip[Quickest: let your agent do it] :::tip[Quickest: let your agent do it]
Paste this into your coding agent. It follows Paste this into your coding agent. It follows
@ -33,91 +33,10 @@ nix profile install github:0xrsydn/idx-cli#idx-mcp
## 2. Add it to your client ## 2. Add it to your client
<Tabs syncKey="agent"> <McpClientTabs />
<TabItem label="Claude Code">
```bash The Claude Code, Codex, opencode, pi and Hermes setups are the ones
claude mcp add idx -- npx -y idx-mcp `tests/agents/agent-smoke.sh` drives with the real agents.
```
</TabItem>
<TabItem label="Codex">
`~/.codex/config.toml`:
```toml
[mcp_servers.idx]
command = "npx"
args = ["-y", "idx-mcp"]
```
</TabItem>
<TabItem label="Cursor">
`~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project):
```json
{
"mcpServers": {
"idx": { "command": "npx", "args": ["-y", "idx-mcp"] }
}
}
```
</TabItem>
<TabItem label="opencode">
`opencode.json` in a project, or `~/.config/opencode/opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"idx": { "type": "local", "command": ["npx", "-y", "idx-mcp"], "enabled": true }
}
}
```
</TabItem>
<TabItem label="pi">
`~/.pi/agent/mcp.json`:
```json
{
"mcpServers": {
"idx": { "command": "npx", "args": ["-y", "idx-mcp"], "exposure": "direct" }
}
}
```
`"exposure": "direct"` gives the agent each tool by name.
</TabItem>
<TabItem label="Hermes">
`~/.hermes/config.yaml`:
```yaml
mcp_servers:
idx:
command: npx
args: ["-y", "idx-mcp"]
enabled: true
```
</TabItem>
<TabItem label="Other">
Any MCP client that runs stdio servers: command `npx`, arguments
`-y idx-mcp` (or the command `idx-mcp` if installed). Add `--allow-sync` to
the arguments to expose `ownership_sync`.
</TabItem>
</Tabs>
These are the setups that `tests/agents/agent-smoke.sh` drives with real
agents.
## 3. Get ownership data ## 3. Get ownership data

View file

@ -0,0 +1,32 @@
// The one list of MCP client setups, used by the landing page, the MCP setup
// page and agents/install.md (and their .md output).
export interface McpClient {
id: string;
label: string;
/** Where the config goes, shown above the snippet. */
file: string;
lang: string;
code: string;
/** Driven by tests/agents/agent-smoke.sh against the real agent. */
smokeTested: boolean;
}
export const mcpClients: McpClient[] = [
{ id: 'claude', label: 'Claude Code', file: 'Run in a terminal', lang: 'bash', smokeTested: true, code: 'claude mcp add idx -- npx -y idx-mcp' },
{ id: 'codex', label: 'Codex', file: '~/.codex/config.toml', lang: 'toml', smokeTested: true, code: '[mcp_servers.idx]\ncommand = "npx"\nargs = ["-y", "idx-mcp"]' },
{ id: 'cursor', label: 'Cursor', file: '~/.cursor/mcp.json (or .cursor/mcp.json in a project)', lang: 'json', smokeTested: false, code: '{\n "mcpServers": {\n "idx": { "command": "npx", "args": ["-y", "idx-mcp"] }\n }\n}' },
{ id: 'opencode', label: 'opencode', file: '~/.config/opencode/opencode.json (or opencode.json in a project)', lang: 'json', smokeTested: true, code: '{\n "$schema": "https://opencode.ai/config.json",\n "mcp": {\n "idx": { "type": "local", "command": ["npx", "-y", "idx-mcp"], "enabled": true }\n }\n}' },
{ id: 'pi', label: 'pi', file: '~/.pi/agent/mcp.json', lang: 'json', smokeTested: true, code: '{\n "mcpServers": {\n "idx": { "command": "npx", "args": ["-y", "idx-mcp"], "exposure": "direct" }\n }\n}' },
{ id: 'hermes', label: 'Hermes', file: '~/.hermes/config.yaml', lang: 'yaml', smokeTested: true, code: 'mcp_servers:\n idx:\n command: npx\n args: ["-y", "idx-mcp"]\n enabled: true' },
{ id: 'other', label: 'Other', file: 'Any MCP client that runs stdio servers', lang: 'text', smokeTested: false, code: 'command: npx\nargs: -y idx-mcp\n# or the command idx-mcp, if installed\n# add --allow-sync to expose ownership_sync' },
];
export const smokeTestedLabels = mcpClients.filter((c) => c.smokeTested).map((c) => c.label);
/** The setups as Markdown, for the .md output of pages that render <McpClientTabs />. */
export function mcpClientsMarkdown(): string {
return mcpClients
.map((c) => `### ${c.label}\n\n${c.file}:\n\n\`\`\`${c.lang}\n${c.code}\n\`\`\``)
.join('\n\n');
}

View file

@ -1,15 +1,37 @@
// Serves every docs page as raw Markdown at `<path>.md`, so agents can read // Serves every docs page as plain Markdown at `<path>.md`, so agents can read
// the docs without HTML (for example /agents/install.md). // the docs without HTML (for example /agents/install.md). MDX-only syntax is
// rewritten into Markdown.
import type { APIRoute, GetStaticPaths } from 'astro'; import type { APIRoute, GetStaticPaths } from 'astro';
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import { mcpClientsMarkdown } from '../data/mcp-clients';
export const getStaticPaths: GetStaticPaths = async () => { export const getStaticPaths: GetStaticPaths = async () => {
const docs = await getCollection('docs'); const docs = await getCollection('docs');
return docs.map((entry) => ({ params: { slug: entry.id }, props: { entry } })); return docs.map((entry) => ({ params: { slug: entry.id }, props: { entry } }));
}; };
function toPlainMarkdown(title: string, body: string): string {
const text = body
.replace(/<!--[\s\S]*?-->\n*/g, '')
.replace(/^import .*\n+/gm, '')
.replace(/^<McpClientTabs \/>$/gm, () => mcpClientsMarkdown())
// <Tabs>: each <TabItem label="X"> becomes a bold label.
.replace(/^<\/?Tabs[^>]*>\n*/gm, '')
.replace(/^<TabItem label="([^"]+)">$/gm, '**$1**')
.replace(/^<\/TabItem>\n*/gm, '')
// Asides (:::tip[Title] ... :::) become blockquote-free sections.
.replace(/^:::\w+\[([^\]]+)\]$/gm, '**$1**\n')
.replace(/^:::\w+$/gm, '')
.replace(/^:::$/gm, '')
.replace(/^```(\w+) wrap$/gm, '```$1')
.replace(/\n{3,}/g, '\n\n')
.trim();
return `# ${title}\n\n${text}\n`;
}
export const GET: APIRoute = ({ props }) => { export const GET: APIRoute = ({ props }) => {
const { entry } = props; const { entry } = props;
const body = `# ${entry.data.title}\n\n${entry.body ?? ''}`.replace(/<!--[\s\S]*?-->\n*/g, '').replace(/^import .*\n+/gm, ''); return new Response(toPlainMarkdown(entry.data.title, entry.body ?? ''), {
return new Response(body, { headers: { 'Content-Type': 'text/markdown; charset=utf-8' } }); headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
});
}; };

View file

@ -4,6 +4,11 @@ import '@fontsource/ibm-plex-sans/600.css';
import '@fontsource/ibm-plex-sans/700.css'; import '@fontsource/ibm-plex-sans/700.css';
import '@fontsource/jetbrains-mono/400.css'; import '@fontsource/jetbrains-mono/400.css';
import '@fontsource/jetbrains-mono/700.css'; import '@fontsource/jetbrains-mono/700.css';
import { readFileSync } from 'node:fs';
import { mcpClients, smokeTestedLabels } from '../data/mcp-clients';
// The released version, from the workspace Cargo.toml (builds run in web/).
const version = readFileSync('../Cargo.toml', 'utf8').match(/^version = "([^"]+)"/m)?.[1] ?? '';
const agentPrompt = const agentPrompt =
'Install idx-cli and its MCP server for me by following https://idx.rasyidanaf.com/agents/install.md, then show me that it works.'; 'Install idx-cli and its MCP server for me by following https://idx.rasyidanaf.com/agents/install.md, then show me that it works.';
@ -16,17 +21,10 @@ const installs = [
{ id: 'agent', label: 'Ask your agent', cmd: agentPrompt, prompt: true, note: 'Paste into Claude Code, Codex, Cursor or any coding agent. It installs idx, registers idx-mcp with itself, and asks before changing global config.' }, { id: 'agent', label: 'Ask your agent', cmd: agentPrompt, prompt: true, note: 'Paste into Claude Code, Codex, Cursor or any coding agent. It installs idx, registers idx-mcp with itself, and asks before changing global config.' },
]; ];
const agents = [ const agents = mcpClients;
{ id: 'claude', label: 'Claude Code', file: 'terminal', code: 'claude mcp add idx -- npx -y idx-mcp' },
{ id: 'codex', label: 'Codex', file: '~/.codex/config.toml', code: '[mcp_servers.idx]\ncommand = "npx"\nargs = ["-y", "idx-mcp"]' },
{ id: 'cursor', label: 'Cursor', file: '~/.cursor/mcp.json', code: '{\n "mcpServers": {\n "idx": { "command": "npx", "args": ["-y", "idx-mcp"] }\n }\n}' },
{ id: 'opencode', label: 'opencode', file: 'opencode.json', code: '{\n "mcp": {\n "idx": { "type": "local", "command": ["npx", "-y", "idx-mcp"] }\n }\n}' },
{ id: 'pi', label: 'pi', file: '~/.pi/agent/mcp.json', code: '{\n "mcpServers": {\n "idx": { "command": "npx", "args": ["-y", "idx-mcp"], "exposure": "direct" }\n }\n}' },
{ id: 'hermes', label: 'Hermes', file: '~/.hermes/config.yaml', code: 'mcp_servers:\n idx:\n command: npx\n args: ["-y", "idx-mcp"]\n enabled: true' },
{ id: 'other', label: 'Other', file: 'any MCP client (stdio)', code: 'command: npx\nargs: -y idx-mcp\n# add --allow-sync to expose ownership_sync' },
];
// Captured from `idx ownership ticker BBCA` on the 2026-09-30 snapshot. // Captured by hand from `idx ownership ticker BBCA` on the 2026-09-30 snapshot;
// the section shows that date. Refresh it (and the terminal panel) after a new month.
const holders = [ const holders = [
{ name: 'DWIMURIA INVESTAMA ANDALAN', type: 'Corporate', shares: '67,729,950,000', pct: 54.94 }, { name: 'DWIMURIA INVESTAMA ANDALAN', type: 'Corporate', shares: '67,729,950,000', pct: 54.94 },
{ name: 'ANTHONI SALIM', type: 'Individual', shares: '1,416,306,835', pct: 1.15 }, { name: 'ANTHONI SALIM', type: 'Individual', shares: '1,416,306,835', pct: 1.15 },
@ -69,7 +67,7 @@ const copyIcon =
<main> <main>
<section class="wrap hero"> <section class="wrap hero">
<div class="hero-text"> <div class="hero-text">
<div class="eyebrow">v0.3.1 · Rust · MIT</div> <div class="eyebrow">v{version} · Rust · MIT</div>
<h1>Indonesian stocks and ownership, in your terminal and your agent.</h1> <h1>Indonesian stocks and ownership, in your terminal and your agent.</h1>
<p class="lede">Quotes, fundamentals, technicals and KSEI shareholder data for every IDX stock. Tables for you, JSON for scripts, and an MCP server for AI agents.</p> <p class="lede">Quotes, fundamentals, technicals and KSEI shareholder data for every IDX stock. Tables for you, JSON for scripts, and an MCP server for AI agents.</p>
@ -143,7 +141,7 @@ TLKM.JK 2,250 <span class="up">+0.45%</span> 87,159,900
<section id="agents" class="wrap split"> <section id="agents" class="wrap split">
<div class="side"> <div class="side">
<h2 class="h2">Add idx to your agent</h2> <h2 class="h2">Add idx to your agent</h2>
<p class="muted-p">idx-mcp serves the same data over stdio. Each setup below is the one our agent smoke test runs against real agents.</p> <p class="muted-p">idx-mcp serves the same data over stdio. Our agent smoke test runs the {new Intl.ListFormat("en", { type: "conjunction" }).format(smokeTestedLabels)} setups against the real agents.</p>
<div class="card"> <div class="card">
<div class="label">Ask it</div> <div class="label">Ask it</div>
<div class="ask">"Who are BBCA's largest holders, and what changed last month?"</div> <div class="ask">"Who are BBCA's largest holders, and what changed last month?"</div>
@ -156,7 +154,7 @@ TLKM.JK 2,250 <span class="up">+0.45%</span> 87,159,900
<button type="button" class="copy" data-copy={agentPrompt} aria-label="Copy agent prompt" set:html={copyIcon} /> <button type="button" class="copy" data-copy={agentPrompt} aria-label="Copy agent prompt" set:html={copyIcon} />
</div> </div>
</div> </div>
<a class="more" href="/mcp/tools/">See all 11 tools →</a> <a class="more" href="/mcp/tools/">See all tools →</a>
</div> </div>
<div class="main-col tabs" data-tabs> <div class="main-col tabs" data-tabs>