@ounie/mcp 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 +117 -0
- package/dist/api.d.ts +68 -0
- package/dist/api.js +74 -0
- package/dist/api.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -0
- package/package.json +30 -0
package/README.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# @ounie/mcp
|
|
2
|
+
|
|
3
|
+
A **Model Context Protocol (MCP)** server that exposes your **Ounie** AI Second Brain
|
|
4
|
+
(https://ounie.com) to AI clients like **Claude Desktop**, **Claude Code**, **Cursor**,
|
|
5
|
+
**Windsurf**, **VS Code**, and other MCP-capable apps.
|
|
6
|
+
|
|
7
|
+
It speaks MCP over **stdio** and calls the Ounie REST API on your behalf using a Bearer API key.
|
|
8
|
+
|
|
9
|
+
> **Full setup guide (all clients, including remote/ChatGPT): https://ounie.com/docs/mcp**
|
|
10
|
+
|
|
11
|
+
## Tools
|
|
12
|
+
|
|
13
|
+
| Tool | Arguments | Description |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `ounie_list_brains` | — | List your brains (id, name, emoji, source count). |
|
|
16
|
+
| `ounie_ask_brain` | `brainId`, `question` | Ask a grounded question; returns an answer + citations. |
|
|
17
|
+
| `ounie_add_note` | `brainId`, `title`, `body` | Save a text note as a new source. |
|
|
18
|
+
| `ounie_add_link` | `brainId`, `url` | Save a web link as a new source. |
|
|
19
|
+
| `ounie_search` | `brainId`, `query` | Retrieve & summarize relevant saved sources. |
|
|
20
|
+
|
|
21
|
+
## Get an API key
|
|
22
|
+
|
|
23
|
+
Generate a personal key (`ounie_live_…`) at **Settings → API keys**
|
|
24
|
+
(https://ounie.com/dashboard/settings/api-keys). You'll see the full key once — copy it
|
|
25
|
+
into the `OUNIE_API_KEY` field below.
|
|
26
|
+
|
|
27
|
+
## Environment variables
|
|
28
|
+
|
|
29
|
+
| Var | Required | Default | Notes |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `OUNIE_API_KEY` | ✅ | — | Bearer key from Settings → API keys. |
|
|
32
|
+
| `OUNIE_BASE_URL` | ❌ | `https://ounie.com` | Override for local/staging. |
|
|
33
|
+
|
|
34
|
+
The server exits with a clear error on stderr if `OUNIE_API_KEY` is missing.
|
|
35
|
+
(All protocol traffic uses stdout; logs go to stderr.)
|
|
36
|
+
|
|
37
|
+
## Configure in Claude Desktop
|
|
38
|
+
|
|
39
|
+
Edit your `claude_desktop_config.json`
|
|
40
|
+
(macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"ounie": {
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "@ounie/mcp"],
|
|
48
|
+
"env": { "OUNIE_API_KEY": "ounie_live_…" }
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Configure in Claude Code
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
claude mcp add ounie --env OUNIE_API_KEY=ounie_live_… -- npx -y @ounie/mcp
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Configure in Cursor / Windsurf
|
|
61
|
+
|
|
62
|
+
`~/.cursor/mcp.json` (or the in-app MCP settings) uses the same shape as Claude Desktop.
|
|
63
|
+
|
|
64
|
+
## Remote (HTTP) clients
|
|
65
|
+
|
|
66
|
+
Web clients (ChatGPT, Claude.ai connectors, Cursor remote) connect to the hosted endpoint
|
|
67
|
+
instead of running this package:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
URL: https://ounie.com/api/mcp
|
|
71
|
+
Header: Authorization: Bearer ounie_live_…
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
See https://ounie.com/docs/mcp for per-client steps and the ChatGPT auth caveat.
|
|
75
|
+
|
|
76
|
+
## Local development
|
|
77
|
+
|
|
78
|
+
Run from source without publishing:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
cd mcp
|
|
82
|
+
npm install
|
|
83
|
+
npm run build # tsc -> dist/
|
|
84
|
+
npm run dev # or: tsx src/index.ts (no build step)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Then point a client at the built file:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"mcpServers": {
|
|
92
|
+
"ounie": {
|
|
93
|
+
"command": "node",
|
|
94
|
+
"args": ["/absolute/path/to/ounie/mcp/dist/index.js"],
|
|
95
|
+
"env": {
|
|
96
|
+
"OUNIE_API_KEY": "ounie_live_…",
|
|
97
|
+
"OUNIE_BASE_URL": "http://localhost:3000"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Files
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
mcp/
|
|
108
|
+
├── package.json # @ounie/mcp, type:module, bin, SDK dep
|
|
109
|
+
├── tsconfig.json # NodeNext, strict, outDir dist
|
|
110
|
+
├── README.md
|
|
111
|
+
└── src/
|
|
112
|
+
├── index.ts # MCP stdio server + tool registration
|
|
113
|
+
└── api.ts # typed fetch client for the Ounie REST API
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`ounie_search` currently maps onto `ounie_ask_brain` with a retrieval-oriented prompt; it
|
|
117
|
+
will repoint to a dedicated search endpoint when one ships.
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed fetch client for the Ounie REST API (https://ounie.com).
|
|
3
|
+
*
|
|
4
|
+
* Configured via environment variables:
|
|
5
|
+
* - OUNIE_API_KEY (required) Bearer token issued by Ounie (Phase 4).
|
|
6
|
+
* - OUNIE_BASE_URL (optional) defaults to https://ounie.com
|
|
7
|
+
*/
|
|
8
|
+
export interface Brain {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
emoji?: string;
|
|
13
|
+
sourceCount?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface Source {
|
|
16
|
+
id: string;
|
|
17
|
+
type: "link" | "note" | "file";
|
|
18
|
+
title?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
body?: string;
|
|
21
|
+
status?: "pending" | "processing" | "ready" | "failed";
|
|
22
|
+
}
|
|
23
|
+
export interface Citation {
|
|
24
|
+
slug: string;
|
|
25
|
+
title: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Quota {
|
|
28
|
+
used?: number;
|
|
29
|
+
limit?: number;
|
|
30
|
+
remaining?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface AskAnswer {
|
|
33
|
+
id: string;
|
|
34
|
+
answerMd: string;
|
|
35
|
+
answerHtml: string;
|
|
36
|
+
citations: Citation[];
|
|
37
|
+
quota?: Quota;
|
|
38
|
+
}
|
|
39
|
+
export interface OunieClientOptions {
|
|
40
|
+
baseUrl?: string;
|
|
41
|
+
apiKey: string;
|
|
42
|
+
fetchImpl?: typeof fetch;
|
|
43
|
+
}
|
|
44
|
+
export declare class OunieApiError extends Error {
|
|
45
|
+
readonly status: number;
|
|
46
|
+
constructor(status: number, message: string);
|
|
47
|
+
}
|
|
48
|
+
export declare class OunieClient {
|
|
49
|
+
private readonly baseUrl;
|
|
50
|
+
private readonly apiKey;
|
|
51
|
+
private readonly fetchImpl;
|
|
52
|
+
constructor(opts: OunieClientOptions);
|
|
53
|
+
private request;
|
|
54
|
+
/** GET /api/brains -> { brains: [...] } (Phase 4 endpoint). */
|
|
55
|
+
listBrains(): Promise<Brain[]>;
|
|
56
|
+
/** POST /api/brains/:id/ask -> AskAnswer. */
|
|
57
|
+
ask(brainId: string, question: string): Promise<AskAnswer>;
|
|
58
|
+
/** POST /api/brains/:id/sources { type:"note", title, body } -> { source }. */
|
|
59
|
+
addNote(brainId: string, title: string, body: string): Promise<Source>;
|
|
60
|
+
/** POST /api/brains/:id/sources { type:"link", url } -> { source }. */
|
|
61
|
+
addLink(brainId: string, url: string): Promise<Source>;
|
|
62
|
+
/**
|
|
63
|
+
* Search a brain. There is no dedicated search endpoint yet, so this maps to
|
|
64
|
+
* `ask` with a retrieval-oriented prompt. Swap to a real `/api/brains/:id/search`
|
|
65
|
+
* endpoint when Phase 4 ships it.
|
|
66
|
+
*/
|
|
67
|
+
search(brainId: string, query: string): Promise<AskAnswer>;
|
|
68
|
+
}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed fetch client for the Ounie REST API (https://ounie.com).
|
|
3
|
+
*
|
|
4
|
+
* Configured via environment variables:
|
|
5
|
+
* - OUNIE_API_KEY (required) Bearer token issued by Ounie (Phase 4).
|
|
6
|
+
* - OUNIE_BASE_URL (optional) defaults to https://ounie.com
|
|
7
|
+
*/
|
|
8
|
+
export class OunieApiError extends Error {
|
|
9
|
+
status;
|
|
10
|
+
constructor(status, message) {
|
|
11
|
+
super(`Ounie API ${status}: ${message}`);
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.name = "OunieApiError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class OunieClient {
|
|
17
|
+
baseUrl;
|
|
18
|
+
apiKey;
|
|
19
|
+
fetchImpl;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
this.baseUrl = (opts.baseUrl ?? "https://ounie.com").replace(/\/+$/, "");
|
|
22
|
+
this.apiKey = opts.apiKey;
|
|
23
|
+
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
24
|
+
}
|
|
25
|
+
async request(path, init = {}) {
|
|
26
|
+
const url = `${this.baseUrl}${path}`;
|
|
27
|
+
const headers = {
|
|
28
|
+
Accept: "application/json",
|
|
29
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
30
|
+
};
|
|
31
|
+
if (init.body !== undefined) {
|
|
32
|
+
headers["Content-Type"] = "application/json";
|
|
33
|
+
}
|
|
34
|
+
const res = await this.fetchImpl(url, {
|
|
35
|
+
method: init.method ?? "GET",
|
|
36
|
+
headers,
|
|
37
|
+
body: init.body !== undefined ? JSON.stringify(init.body) : undefined,
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const text = await res.text().catch(() => "");
|
|
41
|
+
throw new OunieApiError(res.status, text || res.statusText);
|
|
42
|
+
}
|
|
43
|
+
const text = await res.text();
|
|
44
|
+
return (text ? JSON.parse(text) : null);
|
|
45
|
+
}
|
|
46
|
+
/** GET /api/brains -> { brains: [...] } (Phase 4 endpoint). */
|
|
47
|
+
async listBrains() {
|
|
48
|
+
const data = await this.request("/api/brains");
|
|
49
|
+
return data?.brains ?? [];
|
|
50
|
+
}
|
|
51
|
+
/** POST /api/brains/:id/ask -> AskAnswer. */
|
|
52
|
+
async ask(brainId, question) {
|
|
53
|
+
return this.request(`/api/brains/${encodeURIComponent(brainId)}/ask`, { method: "POST", body: { question } });
|
|
54
|
+
}
|
|
55
|
+
/** POST /api/brains/:id/sources { type:"note", title, body } -> { source }. */
|
|
56
|
+
async addNote(brainId, title, body) {
|
|
57
|
+
const data = await this.request(`/api/brains/${encodeURIComponent(brainId)}/sources`, { method: "POST", body: { type: "note", title, body } });
|
|
58
|
+
return data.source;
|
|
59
|
+
}
|
|
60
|
+
/** POST /api/brains/:id/sources { type:"link", url } -> { source }. */
|
|
61
|
+
async addLink(brainId, url) {
|
|
62
|
+
const data = await this.request(`/api/brains/${encodeURIComponent(brainId)}/sources`, { method: "POST", body: { type: "link", url } });
|
|
63
|
+
return data.source;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Search a brain. There is no dedicated search endpoint yet, so this maps to
|
|
67
|
+
* `ask` with a retrieval-oriented prompt. Swap to a real `/api/brains/:id/search`
|
|
68
|
+
* endpoint when Phase 4 ships it.
|
|
69
|
+
*/
|
|
70
|
+
async search(brainId, query) {
|
|
71
|
+
return this.ask(brainId, `Find and summarize the most relevant saved sources for: ${query}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA4CH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAEpB;IADlB,YACkB,MAAc,EAC9B,OAAe;QAEf,KAAK,CAAC,aAAa,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;QAHzB,WAAM,GAAN,MAAM,CAAQ;QAI9B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,OAAO,WAAW;IACL,OAAO,CAAS;IAChB,MAAM,CAAS;IACf,SAAS,CAAe;IAEzC,YAAY,IAAwB;QAClC,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,mBAAmB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAC3C,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,IAAY,EACZ,OAA4C,EAAE;QAE9C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACrC,MAAM,OAAO,GAA2B;YACtC,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;SACvC,CAAC;QACF,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC/C,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;YACpC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK;YAC5B,OAAO;YACP,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;SACtE,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAM,CAAC;IAC/C,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAsB,aAAa,CAAC,CAAC;QACpE,OAAO,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,6CAA6C;IAC7C,KAAK,CAAC,GAAG,CAAC,OAAe,EAAE,QAAgB;QACzC,OAAO,IAAI,CAAC,OAAO,CACjB,eAAe,kBAAkB,CAAC,OAAO,CAAC,MAAM,EAChD,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,CACvC,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,OAAO,CAAC,OAAe,EAAE,KAAa,EAAE,IAAY;QACxD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAC7B,eAAe,kBAAkB,CAAC,OAAO,CAAC,UAAU,EACpD,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CACxD,CAAC;QACF,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,uEAAuE;IACvE,KAAK,CAAC,OAAO,CAAC,OAAe,EAAE,GAAW;QACxC,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAC7B,eAAe,kBAAkB,CAAC,OAAO,CAAC,UAAU,EACpD,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,CAChD,CAAC;QACF,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,KAAa;QACzC,OAAO,IAAI,CAAC,GAAG,CACb,OAAO,EACP,2DAA2D,KAAK,EAAE,CACnE,CAAC;IACJ,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Ounie MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Exposes a user's Ounie AI Second Brain to MCP clients (Claude Desktop, Cursor,
|
|
6
|
+
* etc.) over stdio. Each tool calls the Ounie REST API using a Bearer API key
|
|
7
|
+
* provided via the OUNIE_API_KEY environment variable.
|
|
8
|
+
*
|
|
9
|
+
* Tools:
|
|
10
|
+
* - ounie_list_brains
|
|
11
|
+
* - ounie_ask_brain (brainId, question)
|
|
12
|
+
* - ounie_add_note (brainId, title, body)
|
|
13
|
+
* - ounie_add_link (brainId, url)
|
|
14
|
+
* - ounie_search (brainId, query)
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Ounie MCP server.
|
|
4
|
+
*
|
|
5
|
+
* Exposes a user's Ounie AI Second Brain to MCP clients (Claude Desktop, Cursor,
|
|
6
|
+
* etc.) over stdio. Each tool calls the Ounie REST API using a Bearer API key
|
|
7
|
+
* provided via the OUNIE_API_KEY environment variable.
|
|
8
|
+
*
|
|
9
|
+
* Tools:
|
|
10
|
+
* - ounie_list_brains
|
|
11
|
+
* - ounie_ask_brain (brainId, question)
|
|
12
|
+
* - ounie_add_note (brainId, title, body)
|
|
13
|
+
* - ounie_add_link (brainId, url)
|
|
14
|
+
* - ounie_search (brainId, query)
|
|
15
|
+
*/
|
|
16
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
17
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
19
|
+
import { OunieClient } from "./api.js";
|
|
20
|
+
// --- Config --------------------------------------------------------------------
|
|
21
|
+
const API_KEY = process.env.OUNIE_API_KEY;
|
|
22
|
+
const BASE_URL = process.env.OUNIE_BASE_URL ?? "https://ounie.com";
|
|
23
|
+
if (!API_KEY) {
|
|
24
|
+
// stderr is safe; stdout is reserved for the MCP protocol.
|
|
25
|
+
console.error("[ounie-mcp] Missing OUNIE_API_KEY. Set it in your MCP client config (env).");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const client = new OunieClient({ apiKey: API_KEY, baseUrl: BASE_URL });
|
|
29
|
+
// --- Tool definitions ----------------------------------------------------------
|
|
30
|
+
const TOOLS = [
|
|
31
|
+
{
|
|
32
|
+
name: "ounie_list_brains",
|
|
33
|
+
description: "List the user's Ounie brains (knowledge containers). Returns id, name, emoji, and source count.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {},
|
|
37
|
+
additionalProperties: false,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "ounie_ask_brain",
|
|
42
|
+
description: "Ask a question against a specific Ounie brain. Returns a grounded answer with citations to saved sources.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
brainId: { type: "string", description: "The brain's id." },
|
|
47
|
+
question: { type: "string", description: "The question to ask." },
|
|
48
|
+
},
|
|
49
|
+
required: ["brainId", "question"],
|
|
50
|
+
additionalProperties: false,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "ounie_add_note",
|
|
55
|
+
description: "Add a text note as a new source to a brain.",
|
|
56
|
+
inputSchema: {
|
|
57
|
+
type: "object",
|
|
58
|
+
properties: {
|
|
59
|
+
brainId: { type: "string", description: "The brain's id." },
|
|
60
|
+
title: { type: "string", description: "Note title." },
|
|
61
|
+
body: { type: "string", description: "Note body (plain text or Markdown)." },
|
|
62
|
+
},
|
|
63
|
+
required: ["brainId", "title", "body"],
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "ounie_add_link",
|
|
69
|
+
description: "Add a web link as a new source to a brain. Ounie fetches and processes the page.",
|
|
70
|
+
inputSchema: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
brainId: { type: "string", description: "The brain's id." },
|
|
74
|
+
url: { type: "string", description: "The URL to save." },
|
|
75
|
+
},
|
|
76
|
+
required: ["brainId", "url"],
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: "ounie_search",
|
|
82
|
+
description: "Search a brain for relevant saved sources and return a summary with citations.",
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: "object",
|
|
85
|
+
properties: {
|
|
86
|
+
brainId: { type: "string", description: "The brain's id." },
|
|
87
|
+
query: { type: "string", description: "Search query." },
|
|
88
|
+
},
|
|
89
|
+
required: ["brainId", "query"],
|
|
90
|
+
additionalProperties: false,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
// --- Server --------------------------------------------------------------------
|
|
95
|
+
const server = new Server({ name: "ounie-mcp", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
96
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
97
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
98
|
+
const { name, arguments: args = {} } = request.params;
|
|
99
|
+
try {
|
|
100
|
+
switch (name) {
|
|
101
|
+
case "ounie_list_brains": {
|
|
102
|
+
const brains = await client.listBrains();
|
|
103
|
+
return text(JSON.stringify(brains, null, 2));
|
|
104
|
+
}
|
|
105
|
+
case "ounie_ask_brain": {
|
|
106
|
+
const { brainId, question } = args;
|
|
107
|
+
const answer = await client.ask(brainId, question);
|
|
108
|
+
const cites = answer.citations
|
|
109
|
+
.map((c) => `- ${c.title} (${c.slug})`)
|
|
110
|
+
.join("\n");
|
|
111
|
+
return text(`${answer.answerMd}\n\n${cites ? `Sources:\n${cites}` : ""}`.trim());
|
|
112
|
+
}
|
|
113
|
+
case "ounie_add_note": {
|
|
114
|
+
const { brainId, title, body } = args;
|
|
115
|
+
const source = await client.addNote(brainId, title, body);
|
|
116
|
+
return text(`Added note "${title}" (source ${source.id}).`);
|
|
117
|
+
}
|
|
118
|
+
case "ounie_add_link": {
|
|
119
|
+
const { brainId, url } = args;
|
|
120
|
+
const source = await client.addLink(brainId, url);
|
|
121
|
+
return text(`Added link ${url} (source ${source.id}).`);
|
|
122
|
+
}
|
|
123
|
+
case "ounie_search": {
|
|
124
|
+
const { brainId, query } = args;
|
|
125
|
+
const answer = await client.search(brainId, query);
|
|
126
|
+
const cites = answer.citations
|
|
127
|
+
.map((c) => `- ${c.title} (${c.slug})`)
|
|
128
|
+
.join("\n");
|
|
129
|
+
return text(`${answer.answerMd}\n\n${cites ? `Sources:\n${cites}` : ""}`.trim());
|
|
130
|
+
}
|
|
131
|
+
default:
|
|
132
|
+
return text(`Unknown tool: ${name}`, true);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
137
|
+
return text(`Error calling ${name}: ${message}`, true);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
/** Build a CallTool result with a single text content block. */
|
|
141
|
+
function text(value, isError = false) {
|
|
142
|
+
return {
|
|
143
|
+
content: [{ type: "text", text: value }],
|
|
144
|
+
isError,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// --- Boot ----------------------------------------------------------------------
|
|
148
|
+
async function main() {
|
|
149
|
+
const transport = new StdioServerTransport();
|
|
150
|
+
await server.connect(transport);
|
|
151
|
+
console.error("[ounie-mcp] server running on stdio");
|
|
152
|
+
}
|
|
153
|
+
main().catch((err) => {
|
|
154
|
+
console.error("[ounie-mcp] fatal:", err);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ounie/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Model Context Protocol server exposing your Ounie AI Second Brain to AI clients (Claude, Cursor, ChatGPT).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ounie-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"dev": "tsx src/index.ts",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.0.0",
|
|
27
|
+
"tsx": "^4.0.0",
|
|
28
|
+
"typescript": "^5.5.0"
|
|
29
|
+
}
|
|
30
|
+
}
|