@dain-os/mcp-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -0
- package/dist/client.d.ts +35 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +123 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +92 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/comments.d.ts +3 -0
- package/dist/tools/comments.d.ts.map +1 -0
- package/dist/tools/comments.js +19 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +15 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/projects.d.ts +3 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +103 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/tasks.d.ts +3 -0
- package/dist/tools/tasks.d.ts.map +1 -0
- package/dist/tools/tasks.js +123 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/types.d.ts +28 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +66 -0
- package/dist/tools/types.js.map +1 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# @dain-os/mcp-server
|
|
2
|
+
|
|
3
|
+
MCP server for DainOS. Lets Claude Code and other MCP clients manage your projects, tasks, and task comments through a small, well-scoped tool surface.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Wraps the DainOS Express API behind 12 tools:
|
|
8
|
+
|
|
9
|
+
| Domain | Tools |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Projects | `list_projects`, `get_project`, `create_project`, `update_project`, `archive_project` |
|
|
12
|
+
| Tasks | `list_tasks`, `get_task`, `create_task`, `update_task`, `complete_task`, `archive_task` |
|
|
13
|
+
| Comments | `add_task_comment` |
|
|
14
|
+
|
|
15
|
+
All operations run as the user who minted the token. Tenant scoping is enforced server-side.
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
### 1. Mint a personal access token
|
|
20
|
+
|
|
21
|
+
Sign in to DainOS, go to **Settings -> API tokens**, click **New token**, give it a name (e.g. `claude-code-laptop`), and copy the value. The raw token is shown exactly once.
|
|
22
|
+
|
|
23
|
+
Tokens look like `dain_pat_<43 base64url chars>`.
|
|
24
|
+
|
|
25
|
+
### 2. Add the server to your MCP client
|
|
26
|
+
|
|
27
|
+
#### Claude Code
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
claude mcp add dainos -- npx -y @dain-os/mcp-server
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then set the token in your environment (or pass it inline):
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
export DAINOS_API_TOKEN=dain_pat_<your token>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or, for one-machine setup, add it to your Claude Code config directly:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"dainos": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "@dain-os/mcp-server"],
|
|
47
|
+
"env": {
|
|
48
|
+
"DAINOS_API_TOKEN": "dain_pat_<your token>"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Restart your MCP client
|
|
56
|
+
|
|
57
|
+
Claude Code picks up new MCP servers on restart.
|
|
58
|
+
|
|
59
|
+
## Environment
|
|
60
|
+
|
|
61
|
+
| Variable | Required | Default | Notes |
|
|
62
|
+
|---|---|---|---|
|
|
63
|
+
| `DAINOS_API_TOKEN` | yes | n/a | Personal access token from `/settings/api-tokens` |
|
|
64
|
+
| `DAINOS_API_URL` | no | `https://api.dainos.app` | Override for local dev or staging |
|
|
65
|
+
|
|
66
|
+
## Usage examples
|
|
67
|
+
|
|
68
|
+
Once connected, ask Claude Code things like:
|
|
69
|
+
|
|
70
|
+
- "List my active projects in DainOS."
|
|
71
|
+
- "What tasks are assigned to me?"
|
|
72
|
+
- "Create a task on the Portunus project called 'wire up the new sidebar' due Friday."
|
|
73
|
+
- "Mark that task complete and add a comment that the PR is #312."
|
|
74
|
+
|
|
75
|
+
The model uses `list_projects` to find ids, `create_task` / `update_task` / `complete_task` to mutate state, and `add_task_comment` for follow-ups.
|
|
76
|
+
|
|
77
|
+
## Troubleshooting
|
|
78
|
+
|
|
79
|
+
### 401 Unauthorized
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
DainOS rejected the token (401). Mint a new one at https://dainos.app/settings/api-tokens
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The token is revoked, expired, or never existed. Visit Settings -> API tokens, mint a new one, and update your MCP config.
|
|
86
|
+
|
|
87
|
+
### 403 Forbidden
|
|
88
|
+
|
|
89
|
+
The token is valid but your account does not have permission for the operation (e.g. archiving a project you do not own).
|
|
90
|
+
|
|
91
|
+
### Tool is not visible to Claude Code
|
|
92
|
+
|
|
93
|
+
Restart Claude Code after `claude mcp add ...`. MCP servers are loaded once at startup.
|
|
94
|
+
|
|
95
|
+
## Local development
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
git clone https://github.com/dain-agency/dain-os
|
|
99
|
+
cd dain-os/packages/mcp-server
|
|
100
|
+
npm install
|
|
101
|
+
npm run build
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then point your MCP client at the local build:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"mcpServers": {
|
|
109
|
+
"dainos-local": {
|
|
110
|
+
"command": "node",
|
|
111
|
+
"args": ["/path/to/dain-os/packages/mcp-server/dist/index.js"],
|
|
112
|
+
"env": {
|
|
113
|
+
"DAINOS_API_TOKEN": "dain_pat_...",
|
|
114
|
+
"DAINOS_API_URL": "http://localhost:3001"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Why a small tool surface
|
|
122
|
+
|
|
123
|
+
The Notion MCP works well because it exposes a focused, opinionated set of operations rather than a one-to-one mirror of every API endpoint. The DainOS MCP server follows the same principle: project and task work covers the bulk of the day-to-day "keep things updated" use case, and the model picks the right verb every time. Tools for time entries, deals, proposals, and attachments are tracked for follow-up versions.
|
|
124
|
+
|
|
125
|
+
## Licence
|
|
126
|
+
|
|
127
|
+
Proprietary. Internal use by Dain only.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin fetch wrapper around the DainOS Express API.
|
|
3
|
+
*
|
|
4
|
+
* Authentication is by Personal Access Token (PAT) only. Tokens are minted in
|
|
5
|
+
* the web app at /settings/api-tokens and passed in via the DAINOS_API_TOKEN
|
|
6
|
+
* environment variable when the MCP server starts.
|
|
7
|
+
*
|
|
8
|
+
* Every method returns the API envelope's `data` field (unwrapping the
|
|
9
|
+
* `{ success, data, ... }` shape), or throws `DainOsApiError` on a non-2xx
|
|
10
|
+
* response. Error messages are written for humans (and the model) — a 401
|
|
11
|
+
* tells the user exactly where to mint a new token.
|
|
12
|
+
*/
|
|
13
|
+
export declare class DainOsApiError extends Error {
|
|
14
|
+
readonly status: number;
|
|
15
|
+
readonly body: string;
|
|
16
|
+
constructor(status: number, body: string, message?: string);
|
|
17
|
+
}
|
|
18
|
+
export declare class DainOsClient {
|
|
19
|
+
readonly baseUrl: string;
|
|
20
|
+
readonly token: string;
|
|
21
|
+
constructor(baseUrl: string | undefined, token: string);
|
|
22
|
+
/**
|
|
23
|
+
* Issue a JSON request and return the unwrapped `data` field.
|
|
24
|
+
*
|
|
25
|
+
* @throws DainOsApiError on non-2xx responses, with a human-friendly hint
|
|
26
|
+
* for the common cases (401, 403, 404).
|
|
27
|
+
*/
|
|
28
|
+
request<T>(method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', path: string, body?: unknown): Promise<T>;
|
|
29
|
+
get<T>(path: string): Promise<T>;
|
|
30
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
31
|
+
put<T>(path: string, body?: unknown): Promise<T>;
|
|
32
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
33
|
+
delete<T>(path: string): Promise<T>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAM3D;AASD,qBAAa,YAAY;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM;IAKtD;;;;;OAKG;IACG,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,EACnD,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC;IAsCb,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIjD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAGpC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin fetch wrapper around the DainOS Express API.
|
|
3
|
+
*
|
|
4
|
+
* Authentication is by Personal Access Token (PAT) only. Tokens are minted in
|
|
5
|
+
* the web app at /settings/api-tokens and passed in via the DAINOS_API_TOKEN
|
|
6
|
+
* environment variable when the MCP server starts.
|
|
7
|
+
*
|
|
8
|
+
* Every method returns the API envelope's `data` field (unwrapping the
|
|
9
|
+
* `{ success, data, ... }` shape), or throws `DainOsApiError` on a non-2xx
|
|
10
|
+
* response. Error messages are written for humans (and the model) — a 401
|
|
11
|
+
* tells the user exactly where to mint a new token.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_API_URL = 'https://api.dainos.app';
|
|
14
|
+
export class DainOsApiError extends Error {
|
|
15
|
+
status;
|
|
16
|
+
body;
|
|
17
|
+
constructor(status, body, message) {
|
|
18
|
+
super(message ?? `DainOS API returned ${status}`);
|
|
19
|
+
this.name = 'DainOsApiError';
|
|
20
|
+
this.status = status;
|
|
21
|
+
this.body = body;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class DainOsClient {
|
|
25
|
+
baseUrl;
|
|
26
|
+
token;
|
|
27
|
+
constructor(baseUrl, token) {
|
|
28
|
+
this.baseUrl = baseUrl ?? DEFAULT_API_URL;
|
|
29
|
+
this.token = token;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Issue a JSON request and return the unwrapped `data` field.
|
|
33
|
+
*
|
|
34
|
+
* @throws DainOsApiError on non-2xx responses, with a human-friendly hint
|
|
35
|
+
* for the common cases (401, 403, 404).
|
|
36
|
+
*/
|
|
37
|
+
async request(method, path, body) {
|
|
38
|
+
const url = `${this.baseUrl}/api/v1${path}`;
|
|
39
|
+
const init = {
|
|
40
|
+
method,
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${this.token}`,
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
Accept: 'application/json',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
if (body !== undefined) {
|
|
48
|
+
init.body = JSON.stringify(body);
|
|
49
|
+
}
|
|
50
|
+
const response = await fetch(url, init);
|
|
51
|
+
if (response.status === 204) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const text = await response.text();
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
throw new DainOsApiError(response.status, text, friendlyMessage(response.status, text));
|
|
57
|
+
}
|
|
58
|
+
// Some endpoints reply with the envelope, some with raw JSON. Be tolerant.
|
|
59
|
+
const parsed = parseJson(text);
|
|
60
|
+
if (isEnvelope(parsed)) {
|
|
61
|
+
if (parsed.success === false) {
|
|
62
|
+
const message = parsed.error?.message ?? parsed.message ?? 'Request failed';
|
|
63
|
+
throw new DainOsApiError(response.status, text, message);
|
|
64
|
+
}
|
|
65
|
+
return parsed.data ?? undefined;
|
|
66
|
+
}
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
69
|
+
get(path) {
|
|
70
|
+
return this.request('GET', path);
|
|
71
|
+
}
|
|
72
|
+
post(path, body) {
|
|
73
|
+
return this.request('POST', path, body);
|
|
74
|
+
}
|
|
75
|
+
put(path, body) {
|
|
76
|
+
return this.request('PUT', path, body);
|
|
77
|
+
}
|
|
78
|
+
patch(path, body) {
|
|
79
|
+
return this.request('PATCH', path, body);
|
|
80
|
+
}
|
|
81
|
+
delete(path) {
|
|
82
|
+
return this.request('DELETE', path);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function parseJson(text) {
|
|
86
|
+
if (!text)
|
|
87
|
+
return null;
|
|
88
|
+
try {
|
|
89
|
+
return JSON.parse(text);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function isEnvelope(value) {
|
|
96
|
+
return (typeof value === 'object' &&
|
|
97
|
+
value !== null &&
|
|
98
|
+
'success' in value &&
|
|
99
|
+
typeof value.success === 'boolean');
|
|
100
|
+
}
|
|
101
|
+
function friendlyMessage(status, body) {
|
|
102
|
+
if (status === 401) {
|
|
103
|
+
return [
|
|
104
|
+
'DainOS rejected the token (401).',
|
|
105
|
+
'Mint a new one at https://dainos.app/settings/api-tokens and set DAINOS_API_TOKEN in your MCP config.',
|
|
106
|
+
].join(' ');
|
|
107
|
+
}
|
|
108
|
+
if (status === 403) {
|
|
109
|
+
return 'DainOS denied the request (403). Your account does not have permission for this action.';
|
|
110
|
+
}
|
|
111
|
+
if (status === 404) {
|
|
112
|
+
return 'DainOS could not find the requested resource (404). Check the id and try again.';
|
|
113
|
+
}
|
|
114
|
+
if (status === 429) {
|
|
115
|
+
return 'DainOS rate-limited the request (429). Wait a minute and try again.';
|
|
116
|
+
}
|
|
117
|
+
if (status >= 500) {
|
|
118
|
+
return `DainOS returned a server error (${status}). The body was: ${body.slice(0, 200)}`;
|
|
119
|
+
}
|
|
120
|
+
// Default: surface the body. Models read this.
|
|
121
|
+
return `DainOS returned ${status}: ${body.slice(0, 300)}`;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* DainOS MCP server entry point.
|
|
4
|
+
*
|
|
5
|
+
* Stdio transport. Reads DAINOS_API_TOKEN (required) and DAINOS_API_URL (optional,
|
|
6
|
+
* defaults to https://api.dainos.app) from the environment, then exposes the 12
|
|
7
|
+
* project/task/comment tools to any MCP client (Claude Code, etc).
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;GAMG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* DainOS MCP server entry point.
|
|
4
|
+
*
|
|
5
|
+
* Stdio transport. Reads DAINOS_API_TOKEN (required) and DAINOS_API_URL (optional,
|
|
6
|
+
* defaults to https://api.dainos.app) from the environment, then exposes the 12
|
|
7
|
+
* project/task/comment tools to any MCP client (Claude Code, etc).
|
|
8
|
+
*/
|
|
9
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
12
|
+
import { DainOsClient, DainOsApiError } from './client.js';
|
|
13
|
+
import { allTools, getTool } from './tools/index.js';
|
|
14
|
+
import { toJsonSchema } from './tools/types.js';
|
|
15
|
+
const PACKAGE_NAME = 'dainos';
|
|
16
|
+
const PACKAGE_VERSION = '0.1.0';
|
|
17
|
+
function fail(message) {
|
|
18
|
+
process.stderr.write(`[dainos-mcp] ${message}\n`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
async function main() {
|
|
22
|
+
const token = process.env.DAINOS_API_TOKEN;
|
|
23
|
+
if (!token) {
|
|
24
|
+
fail('DAINOS_API_TOKEN environment variable is required. Mint a token at https://dainos.app/settings/api-tokens and add it to your MCP config.');
|
|
25
|
+
}
|
|
26
|
+
if (!token.startsWith('dain_pat_')) {
|
|
27
|
+
fail('DAINOS_API_TOKEN must start with "dain_pat_". You may have pasted a Supabase JWT by mistake.');
|
|
28
|
+
}
|
|
29
|
+
const baseUrl = process.env.DAINOS_API_URL;
|
|
30
|
+
const client = new DainOsClient(baseUrl, token);
|
|
31
|
+
const server = new Server({ name: PACKAGE_NAME, version: PACKAGE_VERSION }, { capabilities: { tools: {} } });
|
|
32
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
33
|
+
tools: allTools.map((tool) => ({
|
|
34
|
+
name: tool.name,
|
|
35
|
+
description: tool.description,
|
|
36
|
+
inputSchema: toJsonSchema(tool.inputSchema),
|
|
37
|
+
})),
|
|
38
|
+
}));
|
|
39
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
40
|
+
const tool = getTool(request.params.name);
|
|
41
|
+
if (!tool) {
|
|
42
|
+
return {
|
|
43
|
+
content: [
|
|
44
|
+
{ type: 'text', text: `Unknown tool: ${request.params.name}` },
|
|
45
|
+
],
|
|
46
|
+
isError: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const parsed = tool.inputSchema.safeParse(request.params.arguments ?? {});
|
|
50
|
+
if (!parsed.success) {
|
|
51
|
+
return {
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: `Invalid input for ${tool.name}: ${parsed.error.message}`,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const result = await tool.handler(client, parsed.data);
|
|
63
|
+
return {
|
|
64
|
+
content: [
|
|
65
|
+
{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: JSON.stringify(result, null, 2),
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
const message = error instanceof DainOsApiError
|
|
74
|
+
? error.message
|
|
75
|
+
: error instanceof Error
|
|
76
|
+
? error.message
|
|
77
|
+
: String(error);
|
|
78
|
+
return {
|
|
79
|
+
content: [{ type: 'text', text: message }],
|
|
80
|
+
isError: true,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
const transport = new StdioServerTransport();
|
|
85
|
+
await server.connect(transport);
|
|
86
|
+
process.stderr.write(`[dainos-mcp] connected (${allTools.length} tools, api=${client.baseUrl})\n`);
|
|
87
|
+
}
|
|
88
|
+
main().catch((error) => {
|
|
89
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
+
fail(`Fatal: ${message}`);
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/tools/comments.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAoBjD,eAAO,MAAM,YAAY,EAAE,cAAc,EAAqB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const addTaskComment = {
|
|
3
|
+
name: 'add_task_comment',
|
|
4
|
+
description: 'Add a comment to a task. Comments are internal by default (visible to the workspace, not to clients on the portal). Pass isInternal: false to make the comment client-visible.',
|
|
5
|
+
inputSchema: z.object({
|
|
6
|
+
taskId: z.string().uuid(),
|
|
7
|
+
content: z.string().min(1).max(10000),
|
|
8
|
+
isInternal: z
|
|
9
|
+
.boolean()
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Defaults to true. Set false to make the comment client-visible.'),
|
|
12
|
+
}),
|
|
13
|
+
handler: async (client, input) => {
|
|
14
|
+
const { taskId, ...body } = input;
|
|
15
|
+
return client.post(`/tasks/${taskId}/comments`, body);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
export const commentTools = [addTaskComment];
|
|
19
|
+
//# sourceMappingURL=comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/tools/comments.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,cAAc,GAAmB;IACrC,IAAI,EAAE,kBAAkB;IACxB,WAAW,EACT,gLAAgL;IAClL,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;QACzB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;QACrC,UAAU,EAAE,CAAC;aACV,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,iEAAiE,CAAC;KAC/E,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;QAClC,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,MAAM,WAAW,EAAE,IAAI,CAAC,CAAC;IACxD,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAqB,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ToolDefinition } from './types.js';
|
|
2
|
+
import { projectTools } from './projects.js';
|
|
3
|
+
import { taskTools } from './tasks.js';
|
|
4
|
+
import { commentTools } from './comments.js';
|
|
5
|
+
export { projectTools, taskTools, commentTools };
|
|
6
|
+
export type { ToolDefinition };
|
|
7
|
+
/** All tools exposed by the DainOS MCP server (12 total). */
|
|
8
|
+
export declare const allTools: ToolDefinition[];
|
|
9
|
+
/** Look up a tool by its exposed name. */
|
|
10
|
+
export declare function getTool(name: string): ToolDefinition | undefined;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,6DAA6D;AAC7D,eAAO,MAAM,QAAQ,EAAE,cAAc,EAIpC,CAAC;AAEF,0CAA0C;AAC1C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEhE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { projectTools } from './projects.js';
|
|
2
|
+
import { taskTools } from './tasks.js';
|
|
3
|
+
import { commentTools } from './comments.js';
|
|
4
|
+
export { projectTools, taskTools, commentTools };
|
|
5
|
+
/** All tools exposed by the DainOS MCP server (12 total). */
|
|
6
|
+
export const allTools = [
|
|
7
|
+
...projectTools,
|
|
8
|
+
...taskTools,
|
|
9
|
+
...commentTools,
|
|
10
|
+
];
|
|
11
|
+
/** Look up a tool by its exposed name. */
|
|
12
|
+
export function getTool(name) {
|
|
13
|
+
return allTools.find((tool) => tool.name === name);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAGjD,6DAA6D;AAC7D,MAAM,CAAC,MAAM,QAAQ,GAAqB;IACxC,GAAG,YAAY;IACf,GAAG,SAAS;IACZ,GAAG,YAAY;CAChB,CAAC;AAEF,0CAA0C;AAC1C,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA0GjD,eAAO,MAAM,YAAY,EAAE,cAAc,EAMxC,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Project tools. Five operations: list, get, create, update, archive.
|
|
4
|
+
*
|
|
5
|
+
* Descriptions are written for the model, not the user — they appear in the
|
|
6
|
+
* tool-discovery output and shape how the model decides which tool to call.
|
|
7
|
+
*/
|
|
8
|
+
const projectStatusEnum = z.enum([
|
|
9
|
+
'planning',
|
|
10
|
+
'active',
|
|
11
|
+
'on_hold',
|
|
12
|
+
'completed',
|
|
13
|
+
'archived',
|
|
14
|
+
'cancelled',
|
|
15
|
+
]);
|
|
16
|
+
const listProjects = {
|
|
17
|
+
name: 'list_projects',
|
|
18
|
+
description: 'List projects in the current workspace. Returns id, name, code, status, and progress for each. Use this to discover project ids before calling get_project / create_task / list_tasks.',
|
|
19
|
+
inputSchema: z.object({
|
|
20
|
+
status: projectStatusEnum
|
|
21
|
+
.optional()
|
|
22
|
+
.describe('Filter by status. Omit to return all non-archived projects.'),
|
|
23
|
+
limit: z.number().int().min(1).max(100).optional().describe('Default 25, max 100.'),
|
|
24
|
+
}),
|
|
25
|
+
handler: async (client, input) => {
|
|
26
|
+
const params = new URLSearchParams();
|
|
27
|
+
if (input.status)
|
|
28
|
+
params.set('status', input.status);
|
|
29
|
+
if (input.limit !== undefined)
|
|
30
|
+
params.set('limit', String(input.limit));
|
|
31
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
32
|
+
return client.get(`/projects${query}`);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const getProject = {
|
|
36
|
+
name: 'get_project',
|
|
37
|
+
description: 'Get one project by id. Returns full project details including tasks count, members, owner, and client company.',
|
|
38
|
+
inputSchema: z.object({
|
|
39
|
+
id: z.string().uuid().describe('Project id (UUID). Get one from list_projects.'),
|
|
40
|
+
}),
|
|
41
|
+
handler: async (client, input) => client.get(`/projects/${input.id}`),
|
|
42
|
+
};
|
|
43
|
+
const createProject = {
|
|
44
|
+
name: 'create_project',
|
|
45
|
+
description: 'Create a new project. Only `name` is required. Returns the created project with its id.',
|
|
46
|
+
inputSchema: z.object({
|
|
47
|
+
name: z.string().min(1).max(200).describe('Project name. Required.'),
|
|
48
|
+
description: z.string().max(2000).optional(),
|
|
49
|
+
clientCompanyId: z.string().uuid().optional().describe('Link to a client company.'),
|
|
50
|
+
ownerId: z.string().uuid().optional().describe('User id of the project owner.'),
|
|
51
|
+
projectType: z
|
|
52
|
+
.enum(['internal', 'client', 'product', 'research'])
|
|
53
|
+
.optional()
|
|
54
|
+
.describe('Defaults to "client" on the server.'),
|
|
55
|
+
billingMethod: z
|
|
56
|
+
.enum(['fixed', 'hourly', 'retainer', 'non_billable'])
|
|
57
|
+
.optional(),
|
|
58
|
+
startDate: z.string().optional().describe('ISO 8601 date.'),
|
|
59
|
+
targetEndDate: z.string().optional().describe('ISO 8601 date.'),
|
|
60
|
+
budgetHours: z.number().min(0).optional(),
|
|
61
|
+
budgetAmount: z.number().min(0).optional(),
|
|
62
|
+
priority: z.number().int().min(0).max(100).optional(),
|
|
63
|
+
tags: z.array(z.string()).optional(),
|
|
64
|
+
}),
|
|
65
|
+
handler: async (client, input) => client.post('/projects', input),
|
|
66
|
+
};
|
|
67
|
+
const updateProject = {
|
|
68
|
+
name: 'update_project',
|
|
69
|
+
description: 'Update an existing project. Pass any subset of fields; omitted fields are left unchanged. Use this to rename, change status, retarget the end date, etc.',
|
|
70
|
+
inputSchema: z.object({
|
|
71
|
+
id: z.string().uuid(),
|
|
72
|
+
name: z.string().min(1).max(200).optional(),
|
|
73
|
+
description: z.string().max(2000).optional(),
|
|
74
|
+
status: projectStatusEnum.optional(),
|
|
75
|
+
ownerId: z.string().uuid().optional(),
|
|
76
|
+
startDate: z.string().optional(),
|
|
77
|
+
targetEndDate: z.string().optional(),
|
|
78
|
+
budgetHours: z.number().min(0).optional(),
|
|
79
|
+
budgetAmount: z.number().min(0).optional(),
|
|
80
|
+
priority: z.number().int().min(0).max(100).optional(),
|
|
81
|
+
tags: z.array(z.string()).optional(),
|
|
82
|
+
}),
|
|
83
|
+
handler: async (client, input) => {
|
|
84
|
+
const { id, ...rest } = input;
|
|
85
|
+
return client.put(`/projects/${id}`, rest);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
const archiveProject = {
|
|
89
|
+
name: 'archive_project',
|
|
90
|
+
description: 'Archive a project. The project is soft-deleted (status -> archived) and no longer appears in list_projects by default. Existing tasks remain accessible by id.',
|
|
91
|
+
inputSchema: z.object({
|
|
92
|
+
id: z.string().uuid(),
|
|
93
|
+
}),
|
|
94
|
+
handler: async (client, input) => client.delete(`/projects/${input.id}`),
|
|
95
|
+
};
|
|
96
|
+
export const projectTools = [
|
|
97
|
+
listProjects,
|
|
98
|
+
getProject,
|
|
99
|
+
createProject,
|
|
100
|
+
updateProject,
|
|
101
|
+
archiveProject,
|
|
102
|
+
];
|
|
103
|
+
//# sourceMappingURL=projects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.js","sourceRoot":"","sources":["../../src/tools/projects.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA8HjD,eAAO,MAAM,SAAS,EAAE,cAAc,EAOrC,CAAC"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Task tools. Six operations: list, get, create, update, complete, archive.
|
|
4
|
+
*
|
|
5
|
+
* `complete_task` is a thin wrapper around `update_task` with `status: "done"` —
|
|
6
|
+
* but it makes the verb discoverable to the model, which matters more than DRY
|
|
7
|
+
* here. The model will reach for it when the user says "mark this task done".
|
|
8
|
+
*/
|
|
9
|
+
const taskStatusEnum = z.enum([
|
|
10
|
+
'backlog',
|
|
11
|
+
'todo',
|
|
12
|
+
'in_progress',
|
|
13
|
+
'in_review',
|
|
14
|
+
'blocked',
|
|
15
|
+
'done',
|
|
16
|
+
'cancelled',
|
|
17
|
+
]);
|
|
18
|
+
const priorityMoscowEnum = z.enum(['must', 'should', 'could', 'wont']);
|
|
19
|
+
const listTasks = {
|
|
20
|
+
name: 'list_tasks',
|
|
21
|
+
description: 'List tasks. All filters are optional. Common patterns: list_tasks({ projectId }) for one project, list_tasks({ assigneeId: "<me>" }) for the caller\'s work, list_tasks({ status: "in_progress" }) for active work.',
|
|
22
|
+
inputSchema: z.object({
|
|
23
|
+
projectId: z.string().uuid().optional().describe('Filter by project.'),
|
|
24
|
+
assigneeId: z.string().uuid().optional().describe('Filter by assignee user id.'),
|
|
25
|
+
status: taskStatusEnum.optional(),
|
|
26
|
+
limit: z.number().int().min(1).max(100).optional().describe('Default 25, max 100.'),
|
|
27
|
+
}),
|
|
28
|
+
handler: async (client, input) => {
|
|
29
|
+
const params = new URLSearchParams();
|
|
30
|
+
if (input.projectId)
|
|
31
|
+
params.set('projectId', input.projectId);
|
|
32
|
+
if (input.assigneeId)
|
|
33
|
+
params.set('assigneeId', input.assigneeId);
|
|
34
|
+
if (input.status)
|
|
35
|
+
params.set('status', input.status);
|
|
36
|
+
if (input.limit !== undefined)
|
|
37
|
+
params.set('limit', String(input.limit));
|
|
38
|
+
const query = params.toString() ? `?${params.toString()}` : '';
|
|
39
|
+
return client.get(`/tasks${query}`);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const getTask = {
|
|
43
|
+
name: 'get_task',
|
|
44
|
+
description: 'Get one task by id. Returns the task plus assignee, dependencies, and comment count.',
|
|
45
|
+
inputSchema: z.object({
|
|
46
|
+
id: z.string().uuid(),
|
|
47
|
+
}),
|
|
48
|
+
handler: async (client, input) => client.get(`/tasks/${input.id}`),
|
|
49
|
+
};
|
|
50
|
+
const createTask = {
|
|
51
|
+
name: 'create_task',
|
|
52
|
+
description: 'Create a new task. Only `title` is required, but you almost always want `projectId` too. If you have a projectId, prefer the nested endpoint (handled automatically).',
|
|
53
|
+
inputSchema: z.object({
|
|
54
|
+
title: z.string().min(1).max(500),
|
|
55
|
+
description: z.string().max(10000).optional(),
|
|
56
|
+
projectId: z.string().uuid().optional(),
|
|
57
|
+
milestoneId: z.string().uuid().optional(),
|
|
58
|
+
parentTaskId: z.string().uuid().optional().describe('For subtasks.'),
|
|
59
|
+
assigneeId: z.string().uuid().optional(),
|
|
60
|
+
reporterId: z.string().uuid().optional(),
|
|
61
|
+
status: taskStatusEnum.optional().describe('Defaults to backlog.'),
|
|
62
|
+
priorityMoscow: priorityMoscowEnum.optional(),
|
|
63
|
+
storyPoints: z.number().int().min(0).max(100).optional(),
|
|
64
|
+
estimatedHours: z.number().min(0).optional(),
|
|
65
|
+
dueDate: z.string().optional().describe('ISO 8601.'),
|
|
66
|
+
startDate: z.string().optional().describe('ISO 8601.'),
|
|
67
|
+
tags: z.array(z.string()).optional(),
|
|
68
|
+
isBillable: z.boolean().optional(),
|
|
69
|
+
}),
|
|
70
|
+
handler: async (client, input) => {
|
|
71
|
+
const { projectId, ...rest } = input;
|
|
72
|
+
if (projectId) {
|
|
73
|
+
return client.post(`/projects/${projectId}/tasks`, rest);
|
|
74
|
+
}
|
|
75
|
+
return client.post('/tasks', input);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
const updateTask = {
|
|
79
|
+
name: 'update_task',
|
|
80
|
+
description: 'Update a task. Pass any subset of fields. Use this to reassign, change status, retarget the due date, edit the description, etc. For just "mark done", prefer complete_task.',
|
|
81
|
+
inputSchema: z.object({
|
|
82
|
+
id: z.string().uuid(),
|
|
83
|
+
title: z.string().min(1).max(500).optional(),
|
|
84
|
+
description: z.string().max(10000).optional(),
|
|
85
|
+
status: taskStatusEnum.optional(),
|
|
86
|
+
assigneeId: z.string().uuid().optional(),
|
|
87
|
+
priorityMoscow: priorityMoscowEnum.optional(),
|
|
88
|
+
storyPoints: z.number().int().min(0).max(100).optional(),
|
|
89
|
+
estimatedHours: z.number().min(0).optional(),
|
|
90
|
+
dueDate: z.string().optional(),
|
|
91
|
+
startDate: z.string().optional(),
|
|
92
|
+
tags: z.array(z.string()).optional(),
|
|
93
|
+
}),
|
|
94
|
+
handler: async (client, input) => {
|
|
95
|
+
const { id, ...rest } = input;
|
|
96
|
+
return client.put(`/tasks/${id}`, rest);
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
const completeTask = {
|
|
100
|
+
name: 'complete_task',
|
|
101
|
+
description: 'Mark a task as done. Equivalent to update_task with status: "done", but more discoverable. Returns the updated task.',
|
|
102
|
+
inputSchema: z.object({
|
|
103
|
+
id: z.string().uuid(),
|
|
104
|
+
}),
|
|
105
|
+
handler: async (client, input) => client.put(`/tasks/${input.id}`, { status: 'done' }),
|
|
106
|
+
};
|
|
107
|
+
const archiveTask = {
|
|
108
|
+
name: 'archive_task',
|
|
109
|
+
description: 'Archive a task. Soft-deletes the task (status -> cancelled and removed from active queries). Use this when the task is no longer needed, not when it\'s complete.',
|
|
110
|
+
inputSchema: z.object({
|
|
111
|
+
id: z.string().uuid(),
|
|
112
|
+
}),
|
|
113
|
+
handler: async (client, input) => client.delete(`/tasks/${input.id}`),
|
|
114
|
+
};
|
|
115
|
+
export const taskTools = [
|
|
116
|
+
listTasks,
|
|
117
|
+
getTask,
|
|
118
|
+
createTask,
|
|
119
|
+
updateTask,
|
|
120
|
+
completeTask,
|
|
121
|
+
archiveTask,
|
|
122
|
+
];
|
|
123
|
+
//# sourceMappingURL=tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/tools/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;GAMG;AAEH,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC;IAC5B,SAAS;IACT,MAAM;IACN,aAAa;IACb,WAAW;IACX,SAAS;IACT,MAAM;IACN,WAAW;CACZ,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAEvE,MAAM,SAAS,GAAmB;IAChC,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,qNAAqN;IACvN,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QACtE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;QAChF,MAAM,EAAE,cAAc,CAAC,QAAQ,EAAE;QACjC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;KACpF,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC9D,IAAI,KAAK,CAAC,UAAU;YAAE,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QACjE,IAAI,KAAK,CAAC,MAAM;YAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,OAAO,MAAM,CAAC,GAAG,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC;IACtC,CAAC;CACF,CAAC;AAEF,MAAM,OAAO,GAAmB;IAC9B,IAAI,EAAE,UAAU;IAChB,WAAW,EACT,sFAAsF;IACxF,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;KACtB,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,EAAE,EAAE,CAAC;CACnE,CAAC;AAEF,MAAM,UAAU,GAAmB;IACjC,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,uKAAuK;IACzK,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;QACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;QAC7C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;QACvC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;QACzC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC;QACpE,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;QACxC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;QACxC,MAAM,EAAE,cAAc,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QAClE,cAAc,EAAE,kBAAkB,CAAC,QAAQ,EAAE;QAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;QACxD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QAC5C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;QACpD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;QACtD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;QACpC,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;KACnC,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;QACrC,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,SAAS,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3D,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;CACF,CAAC;AAEF,MAAM,UAAU,GAAmB;IACjC,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,8KAA8K;IAChL,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;QACrB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;QAC5C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE;QAC7C,MAAM,EAAE,cAAc,CAAC,QAAQ,EAAE;QACjC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE;QACxC,cAAc,EAAE,kBAAkB,CAAC,QAAQ,EAAE;QAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;QACxD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;QAC5C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAChC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;KACrC,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE;QAC/B,MAAM,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;QAC9B,OAAO,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;CACF,CAAC;AAEF,MAAM,YAAY,GAAmB;IACnC,IAAI,EAAE,eAAe;IACrB,WAAW,EACT,sHAAsH;IACxH,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;KACtB,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,KAAK,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;CACvF,CAAC;AAEF,MAAM,WAAW,GAAmB;IAClC,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,mKAAmK;IACrK,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;KACtB,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,KAAK,CAAC,EAAE,EAAE,CAAC;CACtE,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAqB;IACzC,SAAS;IACT,OAAO;IACP,UAAU;IACV,UAAU;IACV,YAAY;IACZ,WAAW;CACZ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { DainOsClient } from '../client.js';
|
|
3
|
+
/**
|
|
4
|
+
* A single MCP tool definition.
|
|
5
|
+
*
|
|
6
|
+
* The `inputSchema` is a Zod schema. The MCP SDK expects a JSON Schema in its
|
|
7
|
+
* `inputSchema` field; we convert at registration time via {@link toJsonSchema}.
|
|
8
|
+
*
|
|
9
|
+
* Handlers receive the parsed input (already validated by Zod) and the API
|
|
10
|
+
* client. They return whatever the underlying API call returned; the MCP server
|
|
11
|
+
* wraps the result as text content for the model.
|
|
12
|
+
*/
|
|
13
|
+
export interface ToolDefinition<TInput extends z.ZodTypeAny = z.ZodTypeAny> {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
inputSchema: TInput;
|
|
17
|
+
handler: (client: DainOsClient, input: z.infer<TInput>) => Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Minimal Zod -> JSON Schema converter for the shapes used by this package.
|
|
21
|
+
*
|
|
22
|
+
* Avoids pulling in `zod-to-json-schema` (extra dep) since our schemas are
|
|
23
|
+
* straightforward objects with primitive fields and a handful of enums. If we
|
|
24
|
+
* ever add `.refine` chains, `.discriminatedUnion`, or other advanced features
|
|
25
|
+
* here we should switch to the library.
|
|
26
|
+
*/
|
|
27
|
+
export declare function toJsonSchema(schema: z.ZodTypeAny): Record<string, unknown>;
|
|
28
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc,CAAC,MAAM,SAAS,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE1E"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal Zod -> JSON Schema converter for the shapes used by this package.
|
|
4
|
+
*
|
|
5
|
+
* Avoids pulling in `zod-to-json-schema` (extra dep) since our schemas are
|
|
6
|
+
* straightforward objects with primitive fields and a handful of enums. If we
|
|
7
|
+
* ever add `.refine` chains, `.discriminatedUnion`, or other advanced features
|
|
8
|
+
* here we should switch to the library.
|
|
9
|
+
*/
|
|
10
|
+
export function toJsonSchema(schema) {
|
|
11
|
+
return zodToJsonSchema(schema);
|
|
12
|
+
}
|
|
13
|
+
function zodToJsonSchema(schema) {
|
|
14
|
+
if (schema instanceof z.ZodObject) {
|
|
15
|
+
const shape = schema.shape;
|
|
16
|
+
const properties = {};
|
|
17
|
+
const required = [];
|
|
18
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
19
|
+
properties[key] = zodToJsonSchema(value);
|
|
20
|
+
if (!value.isOptional())
|
|
21
|
+
required.push(key);
|
|
22
|
+
}
|
|
23
|
+
const result = {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties,
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
};
|
|
28
|
+
if (required.length > 0)
|
|
29
|
+
result.required = required;
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {
|
|
33
|
+
return zodToJsonSchema(schema._def.innerType);
|
|
34
|
+
}
|
|
35
|
+
if (schema instanceof z.ZodDefault) {
|
|
36
|
+
const inner = zodToJsonSchema(schema._def.innerType);
|
|
37
|
+
inner.default = schema._def.defaultValue();
|
|
38
|
+
return inner;
|
|
39
|
+
}
|
|
40
|
+
if (schema instanceof z.ZodString) {
|
|
41
|
+
const result = { type: 'string' };
|
|
42
|
+
const description = schema._def.description;
|
|
43
|
+
if (description)
|
|
44
|
+
result.description = description;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
if (schema instanceof z.ZodNumber) {
|
|
48
|
+
return { type: 'number' };
|
|
49
|
+
}
|
|
50
|
+
if (schema instanceof z.ZodBoolean) {
|
|
51
|
+
return { type: 'boolean' };
|
|
52
|
+
}
|
|
53
|
+
if (schema instanceof z.ZodEnum) {
|
|
54
|
+
return { type: 'string', enum: [...schema._def.values] };
|
|
55
|
+
}
|
|
56
|
+
if (schema instanceof z.ZodArray) {
|
|
57
|
+
return { type: 'array', items: zodToJsonSchema(schema._def.type) };
|
|
58
|
+
}
|
|
59
|
+
if (schema instanceof z.ZodLiteral) {
|
|
60
|
+
const value = schema._def.value;
|
|
61
|
+
return { type: typeof value, enum: [value] };
|
|
62
|
+
}
|
|
63
|
+
// Fallback: permissive object. Should never hit in practice with our schemas.
|
|
64
|
+
return { type: 'object' };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dain-os/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for DainOS — manage projects and tasks from Claude Code and other MCP clients",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dain-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"files": ["dist", "README.md"],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsx watch src/index.ts",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"check-types": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
20
|
+
"zod": "^3.23.8"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.11.0",
|
|
24
|
+
"tsx": "^4.7.0",
|
|
25
|
+
"typescript": "5.8.2",
|
|
26
|
+
"vitest": "^1.6.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
}
|
|
34
|
+
}
|