41advice: async () => {
42console.log("handler.advice");
43const res = await fetch("https://api.adviceslip.com/advice");
44if (!res.ok) {
45console.error("advice fetch failed", res.status);
64trivia: async () => {
65console.log("handler.trivia");
66const res = await fetch("http://numbersapi.com/random/trivia?json");
67if (!res.ok) {
68console.error("trivia fetch failed", res.status);
74compliment: async () => {
75console.log("handler.compliment");
76const res = await fetch("https://complimentr.com/api");
77if (!res.ok) {
78console.error("compliment fetch failed", res.status);
215216// try {
217// const res = await fetch(`https://ohmanda.com/api/horoscope/${sign}`);
218// if (!res.ok) {
219// console.error("horoscope fetch failed", res.status);
1# Anthropic Streaming Chat with MCP
23A mobile-optimized single page chat application that uses the Anthropic Messages API with **real-time streaming** and MCP (Model Context Protocol) server support, featuring **centralized client management** and **performance optimizations**.
45Source: https://www.val.town/x/c15r/Chat
38const clientPool = new MCPClientPool(connectedClients, serverConfigs);
3940// Unified API across all components
41await clientPool.testServer(serverName);
42await clientPool.fetchTools();
116117The app stores configuration and chat history in localStorage:
118- `anthropic_api_key`: Your Anthropic API key
119- `selected_model`: The chosen Claude model (defaults to claude-3-5-sonnet-20241022)
120- `mcp_servers`: Array of configured MCP servers
144For detailed testing information, see [TESTING.md](./TESTING.md).
145146### API Endpoints
147148- `GET /` - Main application (serves frontend)
1551561. Open the app at the provided URL
1572. Click "Settings" in the footer to configure your Anthropic API key and select your preferred Claude model
1583. Add/remove/toggle MCP servers as needed
1594. Use the "Test" button next to each MCP server to verify connectivity (shows β for success, β for errors)
200- **Auto-scroll**: Messages automatically scroll to bottom during streaming
201- **Auto-resize**: Input field grows with content
202- **Error Handling**: Clear error messages for API issues with stream recovery
203- **Loading States**: Visual feedback during API calls and streaming
204- **Structured Responses**: MCP tool use and results are displayed in organized, collapsible sections
205- **Clean Interface**: Maximized chat area with no header, footer contains all controls
ChatStreamingChat.tsx8 matches
177/** Retry a user message */
178const retryMessage = async (messageId: string) => {
179if (status !== "idle" || !config.anthropicApiKey) return;
180181const userText = onRetryFromMessage(messageId);
203console.log("[Chat] fire send", { userText, input });
204const messageText = userText || input.trim();
205if (!messageText || status !== "idle" || !config.anthropicApiKey) return;
206207// Only clear input if we're using the current input (not NextSteps execution)
262};
263264const canSend = input?.trim() && status === "idle" && config.anthropicApiKey;
265266/* ββ UI βββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
268<>
269<div className="chat-messages">
270{!config.anthropicApiKey && (
271<div className="message system">
272Please configure your Anthropic API key in settings to start chatting
273</div>
274)}
379}}
380onKeyDown={handleKeyDown}
381placeholder={config.anthropicApiKey
382? streaming
383? "Streamingβ¦"
385? "Waiting for your input aboveβ¦"
386: "Type your message or / for commandsβ¦"
387: "Configure API key in settings first"}
388className="chat-input"
389disabled={!config.anthropicApiKey || thinking || waitingForUser}
390rows={1}
391/>
49}
5051// Check if user is authenticated (for API endpoints)
52function isAuthenticated(c: any): boolean {
53const sessionToken = getSignedCookie(c, SESSION_SECRET, "admin_session");
115});
116117// API info endpoint moved to /api
118app.get("/api", async (c) => {
119// Only show cache stats if authenticated
120let cacheInfo = {};
133134return c.json({
135message: "Plant Information API",
136usage: "GET /plant/:name - Get information about a specific plant",
137example: "/plant/rose",
weatherStylist_Skienmain.tsx2 matches
3const lon = 9.6090;
45const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}`
6+ `¤t_weather=true&hourly=relativehumidity_2m,precipitation_probability&timezone=Europe%2FOslo`;
78try {
9const res = await fetch(url);
10if (!res.ok) throw new Error(`API error: ${res.status} ${res.statusText}`);
11const data = await res.json();
12
Skien_Stylist_UImain.tsx2 matches
56// Fetch current weather + hourly humidity & precipitation from Open-Meteo
7const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}`
8+ `¤t_weather=true&hourly=relativehumidity_2m,precipitation_probability&timezone=Europe%2FOslo`;
910try {
11const res = await fetch(url);
12if (!res.ok) throw new Error(`API error: ${res.status} ${res.statusText}`);
13const data = await res.json();
14
Skien_Stylistmain.tsx2 matches
1export default async function main() {
2const apiUrl = "https://val.town/x/sjaskeprut/weatherStylist_Skien"; // example
34try {
5const res = await fetch(apiUrl);
6if (!res.ok) throw new Error(`Failed to fetch: ${res.status} ${res.statusText}`);
7
Weather_Skienmain.tsx1 match
34const url =
5`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t=temperature_2m,weathercode`;
67const res = await fetch(url);
untitled-2623main.tsx1 match
1// @title SportifyMA Core API β Multilingual Event Info
2// @desc Returns event details, transport info, ticket mock, and alerts in user-selected language
3
weatherFetchermain.tsx1 match
45const url =
6`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t=temperature_2m,weathercode`;
78const res = await fetch(url);