Local agent tools
Let a local coding agent use the same app tools as your website assistant through the DeepSpace CLI.
On this page
A DeepSpace app can expose one set of server-side tools in two places:
- the in-app assistant is the AI chat running on the app's website;
- a local agent is Codex, Claude Code, or another agent running on the user's machine.
The local agent reaches the app through the DeepSpace CLI. The CLI uses the user's current DeepSpace login, the app checks whether that user has access, and DeepSpace record tools run with that user's normal permissions.
These are direct REST requests, not an MCP connection. There is no app connection to create, approve, list, or delete, and normal tool calls do not open a browser.
Define tools once#
Keep tool definitions in src/ai/tools.ts. Its buildTools(executor) function defines each tool's name, description, input schema, and implementation.
The website assistant and local agent both use that function. When you add, remove, or change a tool, you change it once. The local discovery command returns only tools that have an implementation, together with their descriptions and JSON Schemas, so a local agent knows which arguments it can send.
See adding custom tools for the tool definition used by buildTools.
Enable the website and local agents#
Every new scaffold includes src/ai/agent.ts. Its registerAgent function belongs to the app and is imported from that local file; it is not an additional package or service. It lets the website assistant and local agent share one tool function and one access rule.
The copilot scaffold enables both by default:
// worker.ts
import { registerAgent } from './src/ai/agent.js'
import { buildTools } from './src/ai/tools.js'
registerAgent(app, { tools: buildTools })
Choose a different arrangement only when the product needs it:
// Website assistant and local agent.
registerAgent(app, { tools: buildTools })
// Website assistant only.
registerAgent(app, { tools: buildTools, local: false })
// Local agent only. No chat UI or chat collections are required.
registerAgent(app, { tools: buildTools, inApp: false })
// Neither: do not call registerAgent.
Keep this registration with the app's other API routes in worker.ts, before the platform proxy and static fallback. A starter app enables neither assistant until you make one of the calls above.
Control access#
Every request from the website assistant or local agent passes through the same checks:
- DeepSpace verifies the caller's login token.
- The app owner is accepted; another caller must already have a user row in that app. Using the signed-in app normally creates or refreshes that row.
- Your optional
authorizefunction can apply an additional app-specific rule. - DeepSpace applies the caller's normal collection and row permissions when a tool uses the provided executor.
A DeepSpace CLI login identifies the caller. It does not automatically give that person access to every DeepSpace app.
Use authorize for subscriptions, teams, roles, account status, or any other condition owned by the app:
registerAgent(app, {
tools: buildTools,
authorize: async ({ userId, env }) => hasActiveSubscription(env, userId),
})
The callback returns true or false and applies to both assistants. It runs after the user's identity and app membership have been verified. It can make access stricter; it cannot add a user to the app or bypass the app's existing permissions.
Those permissions protect DeepSpace record tools. A custom tool that calls another service directly must enforce any resource-specific rules in its own implementation.
The developer chooses the available tools in buildTools. DeepSpace does not show the user a separate tool checklist or consent screen for local access.
Use tools from a local agent#
Sign in once on the machine if the CLI has no active session:
npx deepspace auth login
Then discover the app's tools before invoking one:
npx deepspace agent tools my-app --json
The result identifies the app and returns each tool's name, description, input schema, and optional output schema. Choose a returned tool and construct its input from that schema.
npx deepspace agent invoke my-app records_query \
--input-file tool-input.json \
--json
For small inputs, pass inline JSON with --input. Use --input-file <path> for a file or --input-file - for stdin. --timeout sets the request timeout in milliseconds.
The app argument is either its canonical name (my-app) or its root canonical DeepSpace URL. Use a loopback URL for local development. Custom domains are not accepted as agent targets; use the app's canonical *.app.space URL instead. Production and staging use separate CLI sessions and app domains.
Existing apps#
An SDK dependency update cannot rewrite Worker files owned by the app. An app created before this feature existed must copy src/ai/agent.ts from the current scaffold, bring the authentication changes from the current src/ai/chat-routes.ts into its existing file, and then register the desired arrangement in worker.ts.
Review those source changes, type-check, test, and redeploy the app. Deploying the app adds its local tool routes. This feature needs no database migration or separate DeepSpace platform-worker deployment.
See also#
- AI chat - customize
buildToolsand the website assistant. - CLI command reference - every agent command and flag.
- Permissions - the permission boundary applied to tool execution.