This commit is contained in:
seb
2026-10-14 00:08:11 +02:00
parent 11f6a8fc0f
commit 74b24686bc
14 changed files with 962 additions and 12 deletions

53
mcp.md
View File

@@ -6,12 +6,12 @@ This document explains the architecture, database schema, OAuth 2.1 authenticati
## 1. Overview & Purpose
The Dashback backend provides an MCP server running over **Streamable HTTP** at `/mcp`. It exposes the internal **`dashback_request`** tool (the same tool provided to the local agent via `src/tools/dashback-request.js`) to external MCP clients such as:
The Dashback backend provides an MCP server running over **Streamable HTTP** at `/mcp`. It exposes internal tools (`dashback_request`, `dash-subagent`, and `search-articles`) to external MCP clients such as:
- Visual Studio Code / GitHub Copilot
- Claude Desktop
- Cursor / Windsurf
All operations executed via `dashback_request` are cryptographically authenticated and scoped to the authorizing Dashback user (`google_auth_id`) and their selected company.
All operations executed via these tools are cryptographically authenticated and scoped to the authorizing Dashback user (`google_auth_id`) and their selected company.
---
@@ -176,9 +176,12 @@ The client connects to `/mcp` with `Authorization: Bearer <access_token>`.
---
## 5. The Exposed Tool: `dashback_request`
## 5. The Exposed Tools
The MCP server registers the exact same tool exposed to the internal local agent (`src/tools/dashback-request.js`):
The MCP server exposes three tools to external clients:
### 1. `dashback_request`
Performs HTTP requests against the Dashback API:
```javascript
server.registerTool('dashback_request', {
@@ -191,14 +194,44 @@ server.registerTool('dashback_request', {
body: z.union([z.string(), z.record(z.string(), z.any())]).nullable().optional(),
note: z.string().nullable().optional(),
},
}, async ({ method = 'GET', path, query = null, body = null, note }) => {
// Generates a 5-minute JWT signed with env.jwtSecret scoped to the authenticated user's ID
// Performs fetch against http://127.0.0.1:<port>/api<path>
// Returns status and truncated body (up to 20,000 characters)
});
}, async ({ method = 'GET', path, query = null, body = null, note }) => { ... });
```
Because the internal fetch uses a JWT containing the user's `google_auth_id`, all Dashback route-level permissions, company isolation (`req.user.selectedCompanyId`), audit logging, and data scoping apply automatically.
### 2. `dash-subagent`
Delegates a self-contained task to an isolated subagent with access to Dashback tools and VFS:
```javascript
server.registerTool('dash-subagent', {
title: 'Dashback Subagent',
description: 'Delegate a task to an isolated subagent with access to Dashback tools and VFS.',
inputSchema: {
task: z.string().describe('Specific instructions and goal for the subagent.'),
skills: z.array(z.string()).nullable().optional().describe('Array of SKILL names or slugs to inject.'),
conversation_id: z.number().int().positive().nullable().optional().describe('Existing subagent conversation ID to follow up on, or null to create a new one.'),
max_iterations: z.number().int().min(1).max(30).nullable().optional().describe('Safety bound on tool loops (default 15, max 30).'),
model: z.string().nullable().optional().describe('Optional OpenRouter model override.'),
note: z.string().nullable().optional(),
},
}, async ({ task, skills, conversation_id, max_iterations, model, note }, extra) => { ... });
```
### 3. `search-articles`
Searches articles across name, brand, EAN, SKU, and supplier SKU (AND logic, ordered by name):
```javascript
server.registerTool('search-articles', {
title: 'Search Articles',
description: 'Search articles by name, brand, EAN, SKU, or supplier SKU (AND logic, ordered by name).',
inputSchema: {
searchquery: z.string().nullable().optional().describe('Keywords matching name, brand, EAN, SKU, or supplier SKU (AND logic).'),
excludewords: z.string().nullable().optional().describe('Keywords to exclude (AND logic).'),
offset: z.number().int().min(0).nullable().optional().describe('Pagination offset (default 0).'),
limit: z.number().int().min(1).max(20).nullable().optional().describe('Window size 1–20 (default 3).'),
},
}, async ({ searchquery, excludewords, offset, limit }) => { ... });
```
Because all tool executions use credentials scoped to the authenticated user's `google_auth_id`, all Dashback route-level permissions, company isolation (`req.user.selectedCompanyId`), audit logging, and data scoping apply automatically.
---