From 2387d8dcc986cad3ca4fd11de60d0d1802320b2a Mon Sep 17 00:00:00 2001 From: ProgrammerIn-wonderland <30693865+ProgrammerIn-wonderland@users.noreply.github.com> Date: Wed, 3 Jun 2026 17:13:13 -0400 Subject: [PATCH] MCP whoami (#3203) --- src/mcp-connector/README.md | 12 ++++++-- src/mcp-connector/mcpb/manifest.json | 1 + src/mcp-connector/src/tools.js | 42 ++++++++++++++++++++++------ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/mcp-connector/README.md b/src/mcp-connector/README.md index 37a1957d2..6a78c469e 100644 --- a/src/mcp-connector/README.md +++ b/src/mcp-connector/README.md @@ -11,6 +11,11 @@ OAuth "Sign in with Puter" flow the Worker hosts itself (see ## Tools +### Account +| Tool | Description | +| --- | --- | +| `whoami` | Get the authenticated user's info (username, uuid, home directory). | + ### Filesystem | Tool | Description | | --- | --- | @@ -55,9 +60,12 @@ its associated file (there is no separate update call). | `puter_docs_get` | Fetch a specific docs page as Markdown by topic path (e.g. `Workers/router`). | ### Paths -Tools pass paths straight to puter.js, so the usual conventions apply: absolute +Every path lives under your home directory (`/`). Tools pass paths +straight to puter.js, so the usual conventions apply: absolute (`/your-username/Desktop/file.txt`), home-relative (`~/Desktop/file.txt`), or -relative (`Desktop/file.txt`, resolved against your home directory). +relative (`Desktop/file.txt`, resolved against your home directory). Bare root +paths like `/portfolio/index.html` are **not** valid — call `whoami` to get your +username, then use `~/...` or `//...`. ## How it works diff --git a/src/mcp-connector/mcpb/manifest.json b/src/mcp-connector/mcpb/manifest.json index a17c8dd57..0aba797f3 100644 --- a/src/mcp-connector/mcpb/manifest.json +++ b/src/mcp-connector/mcpb/manifest.json @@ -27,6 +27,7 @@ }, "tools_generated": false, "tools": [ + { "name": "whoami", "description": "Get the authenticated user's info (username, uuid, home directory). Use it to build valid paths." }, { "name": "fs_read_file", "description": "Read a file (UTF-8 or base64; optional offset/length)." }, { "name": "fs_stat", "description": "Stat a file or directory (size, type, timestamps, uid)." }, { "name": "fs_write_file", "description": "Create or overwrite a file (UTF-8 or base64 content)." }, diff --git a/src/mcp-connector/src/tools.js b/src/mcp-connector/src/tools.js index 962c42d75..3c04b5946 100644 --- a/src/mcp-connector/src/tools.js +++ b/src/mcp-connector/src/tools.js @@ -87,7 +87,31 @@ async function fetchDocText(url) { return resp.text(); } +// Every Puter path lives under the user's home directory (/). Agents +// routinely guess bare root paths like "/portfolio/index.html", which do NOT +// exist — this note steers them to valid forms. +const HOME_PATH_NOTE = + 'Paths must live under your home directory: use "~/..." or "//..." ' + + '(call whoami to get your ). Bare root paths like "/portfolio/index.html" are INVALID. Also, don\'t pollute the home directory. Create subpaths and folders for your projects.'; + export const TOOLS = [ + // ----- account / identity ---------------------------------------------- + { + name: 'whoami', + description: + 'Get the authenticated Puter user\'s account info — including username, uuid, and the ' + + 'home_directory (/) that ALL filesystem paths must live under. Call this first ' + + 'to learn your username so you can build valid absolute paths (e.g. "//portfolio/' + + 'index.html") instead of invalid bare root paths. Equivalent to PuterJS puter.auth.getUser().', + inputSchema: { type: 'object', properties: {} }, + async handler(puter) { + const user = await puter.auth.getUser(); + return user && user.username + ? { ...user, home_directory: `/${user.username}` } + : user; + }, + }, + // ----- filesystem ------------------------------------------------------ { name: 'fs_read_file', @@ -98,7 +122,7 @@ export const TOOLS = [ inputSchema: { type: 'object', properties: { - path: { type: 'string', description: 'File path. Absolute (/user/...), ~/relative, or relative to home.' }, + path: { type: 'string', description: `File path to read. ${HOME_PATH_NOTE}` }, encoding: { type: 'string', enum: ['utf8', 'base64'], default: 'utf8' }, offset: { type: 'integer', minimum: 0, description: 'Byte offset to start reading from.' }, length: { type: 'integer', minimum: 1, description: 'Maximum number of bytes to read.' }, @@ -120,7 +144,7 @@ export const TOOLS = [ inputSchema: { type: 'object', properties: { - path: { type: 'string', description: 'Path to a file or directory.' }, + path: { type: 'string', description: `Path to a file or directory. ${HOME_PATH_NOTE}` }, return_size: { type: 'boolean', default: true, description: 'Compute size for directories.' }, }, required: ['path'], @@ -137,7 +161,7 @@ export const TOOLS = [ inputSchema: { type: 'object', properties: { - path: { type: 'string', description: 'Destination file path.' }, + path: { type: 'string', description: `Destination file path. ${HOME_PATH_NOTE}` }, content: { type: 'string', description: 'File contents (UTF-8, or base64 if encoding=base64).' }, encoding: { type: 'string', enum: ['utf8', 'base64'], default: 'utf8' }, overwrite: { type: 'boolean', default: true, description: 'Overwrite an existing file.' }, @@ -171,7 +195,7 @@ export const TOOLS = [ inputSchema: { type: 'object', properties: { - path: { type: 'string', description: 'Directory path to create.' }, + path: { type: 'string', description: `Directory path to create. ${HOME_PATH_NOTE}` }, create_missing_parents: { type: 'boolean', default: true, @@ -191,7 +215,7 @@ export const TOOLS = [ type: 'object', properties: { path: { - description: 'Path (string) or list of paths to delete.', + description: `Path (string) or list of paths to delete. ${HOME_PATH_NOTE}`, anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], }, recursive: { type: 'boolean', default: true, description: 'Recurse into directories.' }, @@ -209,7 +233,7 @@ export const TOOLS = [ inputSchema: { type: 'object', properties: { - path: { type: 'string', description: 'Directory path to list.' }, + path: { type: 'string', description: `Directory path to list. ${HOME_PATH_NOTE}` }, }, required: ['path'], }, @@ -271,7 +295,7 @@ export const TOOLS = [ }, root_dir: { type: 'string', - description: 'Puter directory path whose files are served as the website (e.g. "/me/my-site"). Omit to create the subdomain without content for now.', + description: 'Puter directory path whose files are served as the website (e.g. "~/my-site" or "//my-site"). Omit to create the subdomain without content for now.', }, }, required: ['subdomain'], @@ -339,7 +363,7 @@ export const TOOLS = [ '(`puter`) for storage, KV, AI, and more — authenticated as you, the deployer. Puter Workers ' + 'are designed to be used WITH puter.js and Puter authentication, so BEFORE writing worker ' + 'code load the router guide and examples via puter_docs_get with path "Workers/router". ' + - 'Typical flow: fs_write_file the worker code to a path (e.g. "/me/workers/api.js"), then ' + + 'Typical flow: fs_write_file the worker code to a path (e.g. "~/workers/api.js"), then ' + 'workers_create with that file_path. TO UPDATE a deployed worker, simply write the new code ' + 'to the SAME file with fs_write_file — there is no separate update call; the worker serves ' + 'the current contents of its associated file (propagation takes ~5-30s). Requires a Puter ' + @@ -353,7 +377,7 @@ export const TOOLS = [ }, file_path: { type: 'string', - description: 'Path to the worker JS file in Puter (e.g. "/me/workers/api.js"). The file must define handlers on the global `router` object. Max 10MB. Writing to this same path later updates the deployed worker.', + description: 'Path to the worker JS file in Puter (e.g. "~/workers/api.js" or "//workers/api.js"). The file must define handlers on the global `router` object. Max 10MB. Writing to this same path later updates the deployed worker.', }, }, required: ['worker_name', 'file_path'],