# Local agent tools

Let a local coding agent use the same app tools as your website assistant through the DeepSpace CLI.

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](/guides/ai-chat#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:

```ts
// 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:

```ts
// 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:

1. DeepSpace verifies the caller's login token.
2. 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.
3. Your optional `authorize` function can apply an additional app-specific rule.
4. 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:

```ts
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:

```bash
npx deepspace auth login
```

Then discover the app's tools before invoking one:

```bash
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.

```bash
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.

DeepSpace does not install or save an app-specific connection for the local agent. Give the agent the two CLI commands above through your normal agent instructions. Each command resolves the app and uses the current CLI session.

## 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](/guides/ai-chat) - customize `buildTools` and the website assistant.
* [CLI command reference](/cli-reference/commands#agent) - every agent command and flag.
* [Permissions](/concepts/permissions) - the permission boundary applied to tool execution.
