stevensDemogenerateFunFacts.ts5 matches
77async function generateFunFacts(previousFacts) {
78try {
79// Get API key from environment
80const apiKey = Deno.env.get("ANTHROPIC_API_KEY");
81if (!apiKey) {
82console.error("Anthropic API key is not configured.");
83return null;
84}
8586// Initialize Anthropic client
87const anthropic = new Anthropic({ apiKey });
8889// Format previous facts for the prompt
stevensDemo.cursorrules10 matches
20### 2. HTTP Vals
2122- Create web APIs and endpoints
23- Handle HTTP requests and responses
24- Example structure:
66- Generate code in TypeScript
67- Add appropriate TypeScript types and interfaces for all data structures
68- Prefer official SDKs or libraries than writing API calls directly
69- Ask the user to supply API or library documentation if you are at all unsure about it
70- **Never bake in secrets into the code** - always use environment variables
71- Include comments explaining complex logic (avoid commenting obvious operations)
190- For AI-generated images, use: `https://maxm-imggenurl.web.val.run/the-description-of-your-image`
191- **Storage:** DO NOT use the Deno KV module for storage
192- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
193- **Weather Data:** Use open-meteo for weather data (doesn't require API keys) unless otherwise specified
194- **View Source:** Add a view source link with `import.meta.url.replace("esm.town", "val.town")` and include `target="_top"` attribute
195- **Error Debugging:** Add `<script src="https://esm.town/v/std/catch"></script>` to HTML to capture client-side errors
196- **Error Handling:** Only use try...catch when there's a clear local resolution; avoid catches that merely log or return 500s - let errors bubble up with full context
197- **Environment Variables:** Use `Deno.env.get('keyname')` and minimize their use - prefer APIs without keys
198- **Imports:** Use `https://esm.sh` for npm and Deno dependencies to ensure compatibility on server and browser
199- **Storage Strategy:** Only use backend storage if explicitly required; prefer simple static client-side sites
230231### Backend (Hono) Best Practices
232- Hono is the recommended API framework (similar to Express, Flask, or Sinatra)
233- Main entry point should be `backend/index.ts`
234- **Static asset serving:** Use the utility functions to read and serve project files:
251});
252```
253- Create RESTful API routes for CRUD operations
254- Be careful with error handling as Hono tends to swallow errors
255- Always include this snippet at the top-level Hono app to re-throwing errors to see full stack traces:
268- Use React 18.2.0 consistently in all imports and the `@jsxImportSource` pragma
269- Follow the React component pattern from the example project
270- Handle API calls properly with proper error catching
271272### Database Patterns
299- For files in the project, use `readFile` helpers
3003015. **API Design:**
302- `fetch` handler is the entry point for HTTP vals
303- Run the Hono app with `export default app.fetch // This is the entry point for HTTP vals`
stevensDemoApp.tsx8 matches
10import { NotebookView } from "./NotebookView.tsx";
1112const API_BASE = "/api/memories";
13const MEMORIES_PER_PAGE = 20; // Increased from 7 to 20 memories per page
149091// Fetch avatar image
92fetch("/api/images/stevens.jpg")
93.then((response) => {
94if (response.ok) return response.blob();
104105// Fetch wood background
106fetch("/api/images/wood.jpg")
107.then((response) => {
108if (response.ok) return response.blob();
133setError(null);
134try {
135const response = await fetch(API_BASE);
136if (!response.ok) {
137throw new Error(`HTTP error! status: ${response.status}`);
176177try {
178const response = await fetch(API_BASE, {
179method: "POST",
180headers: { "Content-Type": "application/json" },
199200try {
201const response = await fetch(`${API_BASE}/${id}`, {
202method: "DELETE",
203});
231232try {
233const response = await fetch(`${API_BASE}/${editingMemory.id}`, {
234method: "PUT",
235headers: { "Content-Type": "application/json" },
606<div className="font-pixel text-[#f8f1e0]">
607<style jsx>{`
608@import url("https://fonts.googleapis.com/css2?family=Pixelify+Sans&display=swap");
609610@tailwind base;
blob_adminmain.tsx6 matches
1415// Public route without authentication
16app.get("/api/public/:id", async (c) => {
17const key = `__public/${c.req.param("id")}`;
18const { blob } = await import("https://esm.town/v/std/blob");
132};
133134app.get("/api/blobs", checkAuth, async (c) => {
135const prefix = c.req.query("prefix") || "";
136const limit = parseInt(c.req.query("limit") || "20", 10);
141});
142143app.get("/api/blob", checkAuth, async (c) => {
144const key = c.req.query("key");
145if (!key) return c.text("Missing key parameter", 400);
149});
150151app.put("/api/blob", checkAuth, async (c) => {
152const key = c.req.query("key");
153if (!key) return c.text("Missing key parameter", 400);
158});
159160app.delete("/api/blob", checkAuth, async (c) => {
161const key = c.req.query("key");
162if (!key) return c.text("Missing key parameter", 400);
166});
167168app.post("/api/blob", checkAuth, async (c) => {
169const { file, key } = await c.req.parseBody();
170if (!file || !key) return c.text("Missing file or key", 400);
blob_adminapp.tsx19 matches
70const menuRef = useRef(null);
71const isPublic = blob.key.startsWith("__public/");
72const publicUrl = isPublic ? `${window.location.origin}/api/public/${encodeURIComponent(blob.key.slice(9))}` : null;
7374useEffect(() => {
234setLoading(true);
235try {
236const response = await fetch(`/api/blobs?prefix=${encodeKey(searchPrefix)}&limit=${limit}`);
237const data = await response.json();
238setBlobs(data);
261setBlobContentLoading(true);
262try {
263const response = await fetch(`/api/blob?key=${encodeKey(clickedBlob.key)}`);
264const content = await response.text();
265setSelectedBlob({ ...clickedBlob, key: decodeKey(clickedBlob.key) });
275const handleSave = async () => {
276try {
277await fetch(`/api/blob?key=${encodeKey(selectedBlob.key)}`, {
278method: "PUT",
279body: editContent,
287const handleDelete = async (key) => {
288try {
289await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
290setBlobs(blobs.filter(b => b.key !== key));
291if (selectedBlob && selectedBlob.key === key) {
304const key = `${searchPrefix}${file.name}`;
305formData.append("key", encodeKey(key));
306await fetch("/api/blob", { method: "POST", body: formData });
307const newBlob = { key, size: file.size, lastModified: new Date().toISOString() };
308setBlobs([newBlob, ...blobs]);
326try {
327const fullKey = `${searchPrefix}${key}`;
328await fetch(`/api/blob?key=${encodeKey(fullKey)}`, {
329method: "PUT",
330body: "",
341const handleDownload = async (key) => {
342try {
343const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
344const blob = await response.blob();
345const url = window.URL.createObjectURL(blob);
360if (newKey && newKey !== oldKey) {
361try {
362const response = await fetch(`/api/blob?key=${encodeKey(oldKey)}`);
363const content = await response.blob();
364await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
365method: "PUT",
366body: content,
367});
368await fetch(`/api/blob?key=${encodeKey(oldKey)}`, { method: "DELETE" });
369setBlobs(blobs.map(b => b.key === oldKey ? { ...b, key: newKey } : b));
370if (selectedBlob && selectedBlob.key === oldKey) {
380const newKey = `__public/${key}`;
381try {
382const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
383const content = await response.blob();
384await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
385method: "PUT",
386body: content,
387});
388await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
389setBlobs(blobs.map(b => b.key === key ? { ...b, key: newKey } : b));
390if (selectedBlob && selectedBlob.key === key) {
399const newKey = key.slice(9); // Remove "__public/" prefix
400try {
401const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
402const content = await response.blob();
403await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
404method: "PUT",
405body: content,
406});
407await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
408setBlobs(blobs.map(b => b.key === key ? { ...b, key: newKey } : b));
409if (selectedBlob && selectedBlob.key === key) {
554onClick={() =>
555copyToClipboard(
556`${window.location.origin}/api/public/${encodeURIComponent(selectedBlob.key.slice(9))}`,
557)}
558className="text-blue-400 hover:text-blue-300 text-sm"
577>
578<img
579src={`/api/blob?key=${encodeKey(selectedBlob.key)}`}
580alt="Blob content"
581className="max-w-full h-auto"
197});
198199// API documentation endpoint
200app.get("/api/docs", async (c) => {
201const { TOOLS } = await import("./mcp/tools.ts");
202
207mcp: "POST /mcp - Main MCP protocol endpoint",
208health: "GET /health - Health check",
209docs: "GET /api/docs - This documentation"
210},
211examples: {
MCPChatInterface.tsx1 match
125<div className="flex items-center gap-2">
126<span>{getMessageIcon(message.role)}</span>
127<span className="font-semibold capitalize">{message.role}</span>
128</div>
129<span className="text-xs text-gray-500">
MCPApiKeyInput.tsx17 matches
2import React, { useState } from "https://esm.sh/react@18.2.0?deps=react@18.2.0";
34interface ApiKeyInputProps {
5apiKey: string;
6onApiKeyChange: (key: string) => void;
7}
89export function ApiKeyInput({ apiKey, onApiKeyChange }: ApiKeyInputProps) {
10const [isVisible, setIsVisible] = useState(false);
11const [tempKey, setTempKey] = useState(apiKey);
1213const handleSave = () => {
14onApiKeyChange(tempKey);
15};
1617const handleClear = () => {
18setTempKey('');
19onApiKeyChange('');
20localStorage.removeItem('anthropic_api_key');
21};
2223return (
24<div className="bg-white rounded-lg shadow-md p-6">
25<h2 className="text-2xl font-semibold text-gray-800 mb-4">🔑 Anthropic API Key</h2>
26
27<div className="space-y-4">
28<div>
29<label htmlFor="apiKey" className="block text-sm font-medium text-gray-700 mb-2">
30Enter your Anthropic API Key
31</label>
32<div className="flex gap-2">
33<div className="flex-1 relative">
34<input
35id="apiKey"
36type={isVisible ? "text" : "password"}
37value={tempKey}
38onChange={(e) => setTempKey(e.target.value)}
39placeholder="sk-ant-api03-..."
40className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
41/>
67<p className="mb-2">
68<strong>Status:</strong>{' '}
69<span className={apiKey ? 'text-green-600' : 'text-red-600'}>
70{apiKey ? '✅ API key configured' : '❌ No API key set'}
71</span>
72</p>
73<p className="mb-2">
74Your API key is stored locally in your browser and sent directly to Anthropic's API.
75</p>
76<p>
77Get your API key from{' '}
78<a
79href="https://console.anthropic.com/account/keys"
2import React, { useState, useEffect, useRef } from "https://esm.sh/react@18.2.0?deps=react@18.2.0";
3import { MCPClient } from "./MCPClient.tsx";
4import { ApiKeyInput } from "./ApiKeyInput.tsx";
5import { ChatInterface } from "./ChatInterface.tsx";
62324export function App() {
25const [apiKey, setApiKey] = useState<string>('');
26const [messages, setMessages] = useState<Message[]>([]);
27const [isLoading, setIsLoading] = useState(false);
29const [availableTools, setAvailableTools] = useState<any[]>([]);
3031// Initialize MCP client and load API key from localStorage
32useEffect(() => {
33const savedApiKey = localStorage.getItem('anthropic_api_key');
34if (savedApiKey) {
35setApiKey(savedApiKey);
36}
3752id: '1',
53role: 'system',
54content: 'Welcome to the Val Town MCP Client Demo! This chat interface connects to Claude via the Anthropic API and uses the Val Town MCP server to execute tools. Enter your Anthropic API key to get started.',
55timestamp: new Date()
56}]);
57}, []);
5859const handleApiKeyChange = (key: string) => {
60setApiKey(key);
61localStorage.setItem('anthropic_api_key', key);
62};
6364const sendMessage = async (content: string) => {
65if (!apiKey.trim()) {
66alert('Please enter your Anthropic API key first');
67return;
68}
92When you need to use these tools, I will execute them for you and provide the results. Be helpful and use the tools when appropriate to assist the user.`;
9394// Call Anthropic API
95const response = await fetch('https://api.anthropic.com/v1/messages', {
96method: 'POST',
97headers: {
98'Content-Type': 'application/json',
99'x-api-key': apiKey,
100'anthropic-version': '2023-06-01'
101},
121if (!response.ok) {
122const errorData = await response.json();
123throw new Error(`Anthropic API error: ${errorData.error?.message || response.statusText}`);
124}
125205206<div className="grid gap-6">
207<ApiKeyInput
208apiKey={apiKey}
209onApiKeyChange={handleApiKeyChange}
210/>
211
8- **SQLite Database**: Execute queries and manage database operations
9- **Email**: Send emails through Val Town's email service
10- **OpenAI Integration**: Access OpenAI API through Val Town's service
11- **File Operations**: Read and list project files
12- **Environment Variables**: Access environment configuration
20- JSON-RPC 2.0 compliant messaging
2122## API Endpoints
2324- `POST /mcp` - Main MCP protocol endpoint
34- **MCP Protocol**: `POST /mcp` - JSON-RPC 2.0 endpoint for MCP requests
35- **Health Check**: `GET /health` - Server health status
36- **API Documentation**: `GET /api/docs` - Complete API documentation in JSON format
3738### Example MCP Requests