val-town-http-mcp-servertownie.txt2 matches
26* DO NOT use the alert(), prompt(), or confirm() methods.
2728* If the user's app needs weather data, use open-meteo unless otherwise specified because it doesn't require any API keys.
2930* Tastefully add a view source link back to the user's val if there's a natural spot for it. Generate the val source url via `import.meta.url.replace("esm.town", "val.town")`. This link element should include a target="_top" attribute.
38Val Town's client-side catch script automatically catches client-side errors to aid in debugging.
3940* Don't use any environment variables unless strictly necessary. For example use APIs that don't require a key.
41If you need environment variables use `Deno.env.get('keyname')`
42
2import {z} from "npm:zod"
3import {Config} from "../lib/types.ts"
4import {callValTownApi} from "../lib/api.ts"
5import {getErrorMessage} from "../lib/errorUtils.ts"
615async ({statement}: {statement: string}) => {
16try {
17const data = await callValTownApi(config, "/v1/sqlite/execute", {
18method: "POST",
19body: JSON.stringify({statement}),
46}) => {
47try {
48const data = await callValTownApi(config, "/v1/sqlite/batch", {
49method: "POST",
50body: JSON.stringify({statements, mode}),
73async ({statement}: {statement: string}) => {
74try {
75const data = await callValTownApi(config, "/v1/sqlite/query", {
76method: "POST",
77body: JSON.stringify({statement}),
104}) => {
105try {
106const data = await callValTownApi(config, "/v1/sqlite/exec", {
107method: "POST",
108body: JSON.stringify({statements, mode}),
2import {z} from "npm:zod"
3import {Config} from "../lib/types.ts"
4import {callValTownApi} from "../lib/api.ts"
5import {getCliAvailability, runVtCommand} from "../lib/vtCli.ts"
616async ({limit, offset}: {limit: number; offset: number}) => {
17try {
18const data = await callValTownApi(
19config,
20`/v1/me/projects?limit=${limit}&offset=${offset}`
44async ({projectId}: {projectId: string}) => {
45try {
46const data = await callValTownApi(config, `/v1/projects/${projectId}`)
4748return {
84}
85}
86// If CLI fails, fall back to API
87} catch (error) {
88console.error("CLI error:", error)
89// Continue to API fallback
90}
91}
9293// Fallback to original API implementation
94try {
95const data = await callValTownApi(
96config,
97`/v1/alias/projects/${encodeURIComponent(username)}/${encodeURIComponent(projectName)}`
162}
163164const data = await callValTownApi(config, "/v1/projects", {
165method: "POST",
166body: JSON.stringify(payload),
190async ({projectId}: {projectId: string}) => {
191try {
192await callValTownApi(config, `/v1/projects/${projectId}`, {
193method: "DELETE",
194})
104- For AI-generated images, use: `https://maxm-imggenurl.web.val.run/the-description-of-your-image`
105- **Storage:** DO NOT use the Deno KV module for storage
106- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
107- **Weather Data:** Use open-meteo for weather data (doesn't require API keys) unless otherwise specified
108- **View Source:** Add a view source link with `import.meta.url.replace("esm.town", "val.town")` and include `target="_top"` attribute
109- **Error Debugging:** Add `<script src="https://esm.town/v/std/catch"></script>` to HTML to capture client-side errors
110- **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
111- **Environment Variables:** Use `Deno.env.get('keyname')` and minimize their use - prefer APIs without keys
112- **Imports:** Use JSR.io, NPM and ESM imports in that order to ensure the latest and stable versions are available to use from official providers.
113- **Storage Strategy:** Only use backend storage if explicitly required; prefer simple static client-side sites
194195### Backend (Hono) Best Practices
196- Hono is the recommended API framework (similar to Express, Flask, or Sinatra)
197- Main entry point should be `backend/index.ts`
198- **Static asset serving:** Use the utility functions to read and serve project files:
215});
216```
217- Create RESTful API routes for CRUD operations
218- Be careful with error handling as Hono tends to swallow errors
219- Consider re-throwing errors to see full stack traces:
232- Use React 18.2.0 consistently in all imports and the `@jsxImportSource` pragma
233- Follow the React component pattern from the example project
234- Handle API calls properly with proper error catching
235236### Database Patterns
263- For files in the project, use `readFile` helpers
2642655. **API Design:**
266- `fetch` handler is the entry point for HTTP vals
267- Run the Hono app with `export default app.fetch // This is the entry point for HTTP vals`
46app.post("/mcp", async (c) => {
47try {
48// Extract API token from headers
49const apiToken = c.req.header("X-Val-Town-Token") ||
50c.req.header("Authorization")?.replace("Bearer ", "")
5152if (!apiToken) {
53return c.json({
54jsonrpc: "2.0",
55error: {code: -32000, message: "Missing API token in X-Val-Town-Token header or Authorization header"},
56id: null
57}, 401)
60// Load remote configuration
61const config = await loadConfig(true)
62config.apiToken = apiToken
6364console.log({apiToken})
65// Convert Hono request to Node.js-style req/res
66const {req, res} = toReqRes(c.req.raw)
val-town-http-mcp-serverfileTools.ts30 matches
1import {McpServer} from "npm:@modelcontextprotocol/sdk/server/mcp.js"
2import {Config} from "../lib/types.ts"
3import {callValTownApi} from "../lib/api.ts"
4import {getErrorMessage} from "../lib/errorUtils.ts"
5import {getCliAvailability, runVtCommand, parseCliJsonOutput, prepareValWorkspace, cleanupTempDirectory} from "../lib/vtCli.ts"
122console.error(`Failed to checkout branch: ${checkoutResult.error}`)
123await cleanupTempDirectory(workspace.workspacePath!)
124// Fall back to API
125console.error("CLI error when checking out branch, falling back to API")
126throw new Error("Failed to checkout branch")
127}
147}
148149console.error(`CLI error when listing files, falling back to API: ${workspace.error || "Unknown error"}`)
150// Fall back to API on error
151} catch (error) {
152console.error("CLI error, falling back to API:", getErrorMessage(error))
153// Fall back to API on error
154}
155}
156157// API implementation (original code)
158try {
159let queryParams = `?path=${encodeURIComponent(path)}&recursive=${recursive}&limit=${limit}&offset=${offset}`
162}
163164const data = await callValTownApi(
165config,
166`/v2/vals/${valId}/files${queryParams}`
197198const response = await fetch(
199`${config.apiBase}/v2/vals/${valId}/files/content${queryParams}`,
200{
201headers: {
202'Authorization': `Bearer ${config.apiToken}`,
203},
204}
207if (!response.ok) {
208const errorText = await response.text()
209throw new Error(`API error (${response.status}): ${errorText}`)
210}
211295}
296} else {
297console.error(`CLI error when creating ${type}, falling back to API: ${result.error}`)
298// Fall back to API
299}
300} catch (error) {
301console.error("CLI error, falling back to API:", getErrorMessage(error))
302// Fall back to API on error
303}
304}
305306// API implementation (original code)
307try {
308let queryParams = `?path=${encodeURIComponent(filePath)}`
316}
317318const data = await callValTownApi(
319config,
320`/v2/vals/${valId}/files${queryParams}`,
384}
385} else {
386console.error(`CLI error when updating file, falling back to API: ${result.error}`)
387// Fall back to API
388}
389} catch (error) {
390console.error("CLI error, falling back to API:", getErrorMessage(error))
391// Fall back to API on error
392}
393}
394395// API implementation (original code)
396try {
397let queryParams = `?path=${encodeURIComponent(filePath)}`
400}
401402const data = await callValTownApi(
403config,
404`/v2/vals/${valId}/files${queryParams}`,
488}
489} else {
490console.error(`CLI error when deleting path, falling back to API: ${result.error}`)
491// Fall back to API
492}
493} catch (error) {
494console.error("CLI error, falling back to API:", getErrorMessage(error))
495// Fall back to API on error
496}
497}
498499// API implementation (original code)
500try {
501let queryParams = `?path=${encodeURIComponent(filePath)}&recursive=${recursive}`
504}
505506await callValTownApi(
507config,
508`/v2/vals/${valId}/files${queryParams}`,
val-town-http-mcp-server.env.example2 matches
1# Val Town API token - get from https://www.val.town/settings
2VAL_TOWN_API_TOKEN=your_api_token_here
3
val-town-http-mcp-serverconfig.ts7 matches
6// For remote: expect token in request headers, use local prompt file
7return {
8apiToken: null, // Will be set from headers
9apiBase: "https://api.val.town",
10cli: {
11preferCli: false,
22dotenvConfig({ export: true });
2324const API_TOKEN = Deno.env.get("VAL_TOWN_API_TOKEN");
25if (!API_TOKEN) {
26console.error("Error: VAL_TOWN_API_TOKEN environment variable is required");
27Deno.exit(1);
28}
46: undefined;
47return {
48apiToken: API_TOKEN,
49apiBase: "https://api.val.town",
50cli: {
51preferCli: PREFER_CLI,
20- Feature branches: `feature/description`, bug fixes: `fix/description`
21- Validate all user inputs and follow least privilege principle
22- Never commit API tokens or secrets
23- Each tool should have a clear purpose with descriptive parameters
val-town-http-mcp-serverbranchTools.ts22 matches
1import {McpServer} from "npm:@modelcontextprotocol/sdk/server/mcp.js"
2import {Config} from "../lib/types.ts"
3import {callValTownApi} from "../lib/api.ts"
4import {getErrorMessage} from "../lib/errorUtils.ts"
5import {getCliAvailability, runVtCommand, parseCliJsonOutput, prepareValWorkspace, cleanupTempDirectory} from "../lib/vtCli.ts"
48}
49
50console.error(`CLI error when listing branches, falling back to API: ${workspace.error || "Unknown error"}`);
51// Fall back to API on error
52} catch (error) {
53console.error("CLI error, falling back to API:", getErrorMessage(error));
54// Fall back to API on error
55}
56}
5758// API implementation (original code)
59try {
60const data = await callValTownApi(
61config,
62`/v2/vals/${valId}/branches?limit=${limit}&offset=${offset}`
85async ({valId, branchId}) => {
86try {
87const data = await callValTownApi(
88config,
89`/v2/vals/${valId}/branches/${branchId}`
132console.error(`Failed to checkout source branch: ${checkoutResult.error}`);
133await cleanupTempDirectory(workspace.workspacePath);
134// Fall back to API
135console.error("CLI error when checking out source branch, falling back to API");
136throw new Error("Failed to checkout source branch");
137}
155}
156
157console.error(`CLI error when creating branch, falling back to API: ${workspace.error || "Unknown error"}`);
158// Fall back to API on error
159} catch (error) {
160console.error("CLI error, falling back to API:", getErrorMessage(error));
161// Fall back to API on error
162}
163}
164165// API implementation (original code)
166try {
167const requestBody = {
170}
171172const data = await callValTownApi(
173config,
174`/v2/vals/${valId}/branches`,
227}
228
229console.error(`CLI error when deleting branch, falling back to API: ${workspace.error || "Unknown error"}`);
230// Fall back to API on error
231} catch (error) {
232console.error("CLI error, falling back to API:", getErrorMessage(error));
233// Fall back to API on error
234}
235}
236237// API implementation (original code)
238try {
239await callValTownApi(
240config,
241`/v2/vals/${valId}/branches/${branchId}`,