Files
mcpctl/src/cli/src/commands/console/components/raw-jsonrpc.tsx
Michal a59d2237b9 feat: interactive MCP console (mcpctl console <project>)
Ink-based TUI that shows exactly what an LLM sees through MCP.
Browse tools/resources/prompts, execute them, and see raw JSON-RPC
traffic in a protocol log. Supports gated session flow with
begin_session, raw JSON-RPC input, and session reconnect.

- McpSession class wrapping HTTP transport with typed methods
- 12 React/Ink components (header, protocol-log, menu, tool/resource/prompt views, etc.)
- 21 unit tests for McpSession against a mock MCP server
- Fish + Bash completions with project name argument
- bun compile with --external react-devtools-core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:56:23 +00:00

72 lines
1.8 KiB
TypeScript

import { useState } from 'react';
import { Box, Text } from 'ink';
import { TextInput, Spinner } from '@inkjs/ui';
import type { McpSession } from '../mcp-session.js';
interface RawJsonRpcViewProps {
session: McpSession;
onBack: () => void;
}
export function RawJsonRpcView({ session }: RawJsonRpcViewProps) {
const [loading, setLoading] = useState(false);
const [result, setResult] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
const [input, setInput] = useState('');
const handleSubmit = async () => {
if (!input.trim()) return;
setLoading(true);
setResult(null);
setError(null);
try {
const response = await session.sendRaw(input);
try {
setResult(JSON.stringify(JSON.parse(response), null, 2));
} catch {
setResult(response);
}
} catch (err) {
setError(err instanceof Error ? err.message : String(err));
} finally {
setLoading(false);
}
};
return (
<Box flexDirection="column">
<Text bold>Raw JSON-RPC</Text>
<Text dimColor>Enter a full JSON-RPC message and press Enter to send:</Text>
<Box marginTop={1}>
<Text color="cyan">&gt; </Text>
<TextInput
placeholder='{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
onChange={setInput}
onSubmit={handleSubmit}
/>
</Box>
{loading && (
<Box marginTop={1}>
<Spinner label="Sending..." />
</Box>
)}
{error && (
<Box marginTop={1}>
<Text color="red">Error: {error}</Text>
</Box>
)}
{result && (
<Box flexDirection="column" marginTop={1}>
<Text bold>Response:</Text>
<Text>{result}</Text>
</Box>
)}
</Box>
);
}