AI Integration
Connect Nocarta to an AI assistant using MCP (Model Context Protocol), an open standard that lets AI assistants talk to external applications. Nocarta offers two integrations with different scopes — pick the one that fits what you want the assistant to do.
Choose your integration
| Cloud | Desktop | |
|---|---|---|
| What the assistant does | Reads (and, with the right scopes, writes) your data — templates, instances, scripts, profile | Acts as a user — navigates pages, fills forms, clicks, creates and runs scripts |
| Where it runs | Talks to Nocarta's servers over the internet | Talks only to your own computer (loopback) |
| Requires Desktop app | No — any browser | Yes — Nocarta Desktop must be running |
| Authentication | Long-lived API token you create in Settings | Short-lived token that rotates every time you enable the server |
| Good for | "Show me my templates," "What fields are in this form?" | "Create a script that converts this CSV and run it," "Open my automation and add a new step" |
The two are independent. You can use either, both, or neither.
Cloud — API tokens for REST and MCP
Set up once, works from anywhere. The assistant connects to app.nocarta.ai with a token you create. The same token authenticates any REST call against /api/v1/* and any MCP client — Claude Desktop, Claude Code, Cursor, Continue, OpenAI Codex. Each token acts within the scopes you grant it.
Step 1 — Create an API token
- Go to Settings → Profile and Connections.
- Click New Token.
- Fill in:
- Token Name — anything memorable, e.g. "Claude Desktop", "Cursor", "payroll-importer".
- Scopes — tick the permissions this token grants. See the table below.
- Expiration — Never / 30 / 90 / 180 / 365 days. 90 days is a sensible default for normal tokens.
- Click Create. Your token appears in a dialog — copy it now.
Tokens are shown exactly once. The plaintext value starts with nc_token_. Copy it before dismissing the dialog. If you lose it, revoke and create a new one — there's no way to recover the original.
Scopes
Each scope grants exactly one capability. Pick the smallest set you need; create separate tokens with different scopes for different clients.
| Scope | What it grants |
|---|---|
user:read | Read your account profile (name, email, enabled features). |
form_templates:read | List and inspect form templates, notebooks, and fields. |
form_templates:write | Create, edit, scan, and delete form templates. |
form_instances:read | List and view filled form instances and field values. |
form_instances:write | Create, edit, submit, and bulk-operate on form instances. |
notebooks:write | Create, edit, scan, and save values on notebooks. |
value_sets:read | List and view value sets, entries, search, and export. |
value_sets:read_one | View a single value set and its entries/search/export (no listing). |
value_sets:write | Create, edit, delete value sets, import entries, record usage. |
automations:read | List and inspect automations (name and description). |
search:read | Search everything the caller owns by meaning — documents and value-set entries. |
indexing:read | Read which of the caller's content is searchable by meaning. |
indexing:write | Turn meaning-search indexing on or off per notebook set, automation set, or template. |
partners:read | List and view partner-enabled templates and their configuration. |
partners:write | Enable and configure partner mode on templates you publish. |
partner_onboarding:read | View partner onboarding/compliance status and guidance. |
partner_onboarding:write | Complete partner onboarding/compliance setup. |
partner_instances:read | Read a partner template's field map. |
partner_instances:write | Create instances via the partner API. |
partner_service:read | View the account-level partner service status. |
partner_service:write | Activate and update partner service settings. |
The super_user scope — use with care
super_user is a wildcard — it grants every other scope at once. To prevent accidental misuse, the UI enforces hard constraints when you tick it:
- Expiration must be 30 or 90 days. "Never", 180 days, and 1 year are disabled while
super_useris selected. - An IP allowlist is required. Enter one CIDR per line (e.g.
203.0.113.42,198.51.100.0/24,2001:db8::/32). Requests from any IP outside the list are rejected with a generic 401 — off-allowlist callers can't tell whether the token exists. - Every request is audited. Each call made with a
super_usertoken is logged with timestamp, method, path, status, and source IP.
Use super_user only for one-off operator tasks or short-lived automation that genuinely needs broad reach. For everything else, pick granular scopes.
Step 2 — Connect your AI assistant
Claude Desktop
- Open Claude Desktop.
- Go to Settings → Developer → Edit Config.
- Add the following block, replacing
nc_token_your_tokenwith the token you copied:{ "mcpServers": { "nocarta": { "url": "https://app.nocarta.ai/api/v1/mcp", "headers": { "Authorization": "Bearer nc_token_your_token" } } } } - Save and restart Claude Desktop.
- Test it — ask Claude: "List my Nocarta form templates."
Claude Code (CLI): Add the same mcpServers block to ~/.claude/settings.json, or scope it to a project with a .mcp.json file at the project root.
Claude on the web: Claude.ai supports custom MCP connectors — see Anthropic's documentation for the current setup steps.
OpenAI Codex
Codex registers MCP servers via CLI; pass the token through an environment variable:
export NOCARTA_TOKEN=nc_token_your_token
codex mcp add nocarta \
--url https://app.nocarta.ai/api/v1/mcp \
--bearer-token-env-var NOCARTA_TOKEN
Verify the server is registered:
codex mcp list
Other MCP clients
Any client that supports remote HTTP MCP servers works with endpoint https://app.nocarta.ai/api/v1/mcp and an Authorization: Bearer nc_token_… header.
REST API
The same token authenticates any direct REST call against /api/v1/*:
curl -H "Authorization: Bearer nc_token_your_token" \
https://app.nocarta.ai/api/v1/form_templates
Finding things by meaning
search_content searches the user's templates, notebooks, form instances and automations by what they are about, not just by name. Use it for open questions — "what do I have about invoices?", "find the notes on the Milan lease" — where you do not already know the record's name. It works across languages: a search for "construction company" finds a supplier recorded as Rossi Costruzioni.
Each hit comes back with a URL, a snippet, and a relevance score. If you already know the name, search_form_templates is a cheaper direct lookup.
| Tool | Scope | Use it when |
|---|---|---|
search_content | search:read | You don't know the name — you know the topic. |
suggest_actions | per resource type | You want the right page to send the user to. |
get_indexing_settings | indexing:read | Checking why a search returned nothing. |
set_indexing_settings | indexing:write | Turning meaning-search on for a template. |
Empty results are usually a switch, not a bug. Meaning-search is opt-in: notebooks and automations are on by default, form templates are off until switched on per template. Call get_indexing_settings before concluding a user has no matching content — and note that results always fall back to word matching, so you still get hits even when nothing is indexed.
The warnings field
Tools that write user content — create_form_instance, update_form_instance, bulk_create_form_instances — may return a warnings array alongside the normal result:
{
"id": "…",
"name": "Invoice 4471",
"warnings": ["Saved. Heads up: this content contains instructions that look aimed at an AI assistant. …"]
}
This means the text you just stored reads like an instruction to an AI rather than ordinary data — the classic shape of a prompt-injection payload. The write still happened; nothing was rejected or altered.
What to do with it: surface it to the user rather than swallowing it. You are writing content that the assistant will later read back, so a payload written through the API is exactly the case a human never sees. The key is absent when there is nothing to report, so you can check for its presence without special-casing an empty array.
Desktop — Full app control
The Local MCP is a small server that runs inside the Nocarta Desktop app. When you enable it, any MCP-compatible AI assistant (Claude Code, Cursor, Continue, and others) can drive your desktop app the same way you do — open pages, fill forms, click buttons, write a script, and run it. It's the way to ask an assistant to do things in Nocarta, not just look at them.
What this is for
Use it when you want an assistant to perform a task end-to-end. Examples that work well:
- "Create a Node script that converts last month's CSV file to JSON and run it."
- "Open the Customers template and fill the first 10 instances with rows from this file."
- "Take a screenshot of every page in the Automations section so I can document them."
Anything you can do as a user, the assistant can do — including making changes. Treat it accordingly.
Security at a glance
- Loopback only. The server listens on
127.0.0.1. Your home network, office network, or anyone else on the internet cannot reach it. Only programs already running on your computer can. - Token-gated. Every request must carry a secret token. The token rotates each time you click Enable.
- Host-header check. Defeats a common browser-side trick where a malicious page tries to talk to your loopback port.
- Local data only. The traffic between your AI assistant and the Local MCP never leaves your computer.
The assistant has the same reach as you do. If you're signed into the desktop, the assistant operates as you. Only enable the Local MCP when you're actively using an assistant; click Disable when you're done.
Step 1 — Enable the Local MCP server
- Open the Nocarta Desktop app and sign in.
- Go to Settings → Profile and Connections.
- Scroll to the Local MCP Server card.
- Click Enable. The status flips to "Running", and a token is generated.
- Click Copy MCP config — this copies a JSON snippet you'll paste into your assistant's settings.
Click Disable any time to stop the server.
Step 2 — Point your AI assistant at it
Paste the snippet you copied into your assistant's MCP configuration. The exact location depends on the assistant:
- Claude Code:
~/.claude/settings.json, or a project-scoped.mcp.json. - Cursor / Continue / others: see the assistant's documentation for where MCP servers are configured.
Restart the assistant after pasting. The new nocarta tools become available immediately.
The token rotates every time you click Enable. Your assistant automatically picks up the new value on its next call — no need to restart it after a rotation.
What your assistant can do
The Local MCP exposes two tool surfaces. Use app_* to drive the desktop app's own pages directly; use the browser_*/relay tools when you want the assistant to drive the Automation Assistant's own browser (for automating external websites) or hold a natural-language conversation with it.
app_* — direct app control
| Tool | What it does |
|---|---|
app_navigate | Open an app page by its path, e.g. /system_scripts/new. |
app_url | Read the current page's URL and title without navigating. |
app_snapshot | Return a structured outline of the current page — headings, buttons, links, form fields, iframes. The assistant uses this to discover what to interact with. |
app_click | Click a button, link, tab, or other interactive element. |
app_fill | Set the value of a text field, textarea, dropdown, or checkbox. |
app_wait_for | Pause until something changes — an element appears, the URL changes, or a condition becomes true. Keeps the assistant in sync with pages that load progressively. |
app_evaluate | Run a small piece of JavaScript inside the current page and return the result. Escape hatch for cases the other tools don't cover. |
app_screenshot | Capture the current page as an image. Useful for documentation and debugging. |
app_upload_file | Set a file on a file-upload field, given an absolute path on your computer. |
app_drag | Drag from one point to another. For canvas-based editors like scan-area definition. |
app_keyboard | Send keystrokes — e.g. Enter, Escape, Control+s. |
browser_* and relay tools — the Automation Assistant's browser
These drive the Automation Assistant's own automation browser (for automating external sites) or send a message through the assistant's full natural-language chat pipeline — the same as typing into its chat panel yourself:
| Tool | What it does |
|---|---|
browser_open | Open a new browser session at a URL. |
browser_navigate | Navigate the open browser to a URL. |
browser_click | Click an element (CSS selector or visible text). |
browser_fill | Fill an input field. |
browser_snapshot | Get DOM structure, current URL, and title. |
browser_screenshot | Capture the current page as an image. |
browser_back / browser_forward / browser_refresh | History navigation. |
browser_execute_js | Run JavaScript in the page. |
browser_close | Close the automation browser. |
read_table / read_table_pw | Read a table as structured data. |
list_links / list_page_structure | Page introspection. |
find_by_text / click_by_text / fill_by_text | Text-based element interaction. |
select_option | Select a dropdown value. |
switch_to_iframe / exit_iframe | Frame context switching. |
switch_to_table / exit_table | Table cursor mode. |
sleep | Wait. |
send_message | Send any free-text message through the assistant's full chat pipeline — the escape hatch for anything not covered by a dedicated tool (e.g. automation CRUD, "list my automations"). |
Use app_* when you want the assistant to drive a Nocarta page directly (Scripts, Automations, Forms, Settings, instances). Use browser_* when you want it to drive an external site through the Automation Assistant's own browser. Use send_message for anything conversational.
Identifying things on the page
Most tools take a target — the thing on the page you want to act on. You can identify it in any of four ways. Pick exactly one:
testid— a stable identifier set by Nocarta on important elements (e.g.field-name,tab-runs,form-submit). The most reliable option when one's available.selector— a CSS selector. Use this for elements without a testid.text— the exact visible text of a button, link, label, or heading.label— the text of the label associated with an input field (useful when the field itself has no helpful identifier).
If the locator matches multiple elements, pass nth (zero-based) to pick a specific one. Default is the first match.
Working with iframes
Some pages embed iframes (the scan-area editor, for example). To act on something inside an iframe, pass a frame parameter to the tool — a CSS selector for the iframe element. For nested iframes, pass an array of selectors (outer to inner). The assistant doesn't need to "enter" or "exit" frames; every call is independent.
Worked example — create and run a Node script
This sequence drives the entire flow: open the create form, fill it, submit, then run the resulting script. An assistant calls one tool per JSON line:
{ "action": "app_navigate", "params": { "path": "/system_scripts/new" } }
{ "action": "app_snapshot" } // discover testids
{ "action": "app_fill", "params": { "testid": "field-name", "value": "CSV to JSON" } }
{ "action": "app_click", "params": { "testid": "field-kind-node" } }
{ "action": "app_fill", "params": { "testid": "field-source-filename", "value": "main.js" } }
{ "action": "app_fill", "params": {
"testid": "field-source-code",
"value": "module.exports = async function ({ inputDir, outputDir, log }) {\n const fs = require('fs');\n const path = require('path');\n const csv = fs.readFileSync(path.join(inputDir, 'data.csv'), 'utf8');\n const rows = csv.trim().split('\n').map(r => r.split(','));\n fs.writeFileSync(path.join(outputDir, 'data.json'), JSON.stringify(rows, null, 2));\n log('converted ' + rows.length + ' rows');\n};\n"
} }
{ "action": "app_click", "params": { "testid": "field-persist-logs" } } // keep the log output
{ "action": "app_click", "params": { "testid": "form-submit" } }
{ "action": "app_wait_for", "params": { "url": { "regex": "/system_scripts/[0-9a-f-]+$" } } }
// At this point the script exists. Click Run to execute it:
{ "action": "app_click", "params": { "text": "Run" } }
{ "action": "app_wait_for", "params": {
"jsPredicate": "document.querySelector('[data-testid=run-row-status]')?.innerText === 'completed'",
"timeoutMs": 30000
} }
When the wait completes, the Runs tab shows the new run with status, exit code, and any output files. See Scripts for what a Node script's source code can do.
Tips for AI assistants
- Call
app_snapshotearly. It reveals the testids on the current page, so subsequentapp_clickandapp_fillcalls can target reliably instead of guessing. - Wait explicitly between steps that change the page. Use
app_wait_forafter submitting forms or clicking links — pages often load progressively. - Watch the activity log. The Local MCP panel shows every command the assistant just sent, with status and timing. It's the fastest way to see what the assistant did and where it stuck.
- Read error categories. When something fails, the response includes a structured category (e.g.
element_not_found,iframe_not_found,timeout). Treat them as actionable signals, not exceptions.
Known limits
- Biometric / passkey prompts can't be driven by the assistant — by design. If a flow requires your fingerprint or hardware key, you'll have to provide it.
- Cross-origin iframes (embeds from a different domain) are off-limits to the assistant. Standard browser security boundary.
- File uploads require the assistant to use a temporarily attached debugging channel. This won't work while you have the developer tools open on the desktop's main window — close them first, then try the upload again.
Revoking access
Cloud token
- Go to Settings → Profile and Connections.
- Find the token in the list and click the revoke icon (⊘) next to it.
- The token's status flips to Revoked and any in-flight or future requests with it are rejected immediately.
Desktop server
- Open Settings → Profile and Connections.
- In the Local MCP Server card, click Disable.
- The server stops. The assistant can no longer reach the desktop until you click Enable again.
Troubleshooting
"Unauthorized" error (cloud)
- Confirm the token is active in Settings → Profile and Connections
- Make sure the full value is pasted — it starts with
nc_token_ - Confirm the header is exactly
Authorization: Bearer nc_token_…(no extra characters) - If the token is
super_user, confirm the calling IP is in the allowlist — off-allowlist requests return a generic 401 indistinguishable from an invalid token.
Assistant says it has no tools
- The token's scopes control which tools appear — check that the scopes match what you need
- Restart your AI client after saving the config
Assistant can't find a document
- Your assistant only sees documents you own or that are shared with you
- Archived templates are excluded by default — ask your assistant to include archived ones
Desktop assistant can't reach the server
- Confirm the Nocarta Desktop app is running and the Local MCP Server card shows "Running".
- Restart your AI assistant after clicking Enable, so it picks up the latest config.
Desktop commands time out
- The desktop window must be visible (not closed or minimized to tray-only).
- For commands that drive the app's main window, make sure you're signed in to Nocarta.
File upload from the assistant fails
- Close the desktop's developer tools window if you have it open, then ask the assistant to retry.
Next Steps
- Scripts Guide — Author and run Node or Shell scripts (great for the Desktop integration)
- Forms Guide — Creating and filling templates
- Secrets Guide — How credentials stay protected
- Automation Guide — Automate browser tasks with the Desktop app
Need More Help?