@naumu/mcp 0.11.1 → 0.12.1
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 +176 -34
- package/dist/index.js +262 -18
- package/package.json +7 -6
- package/server.json +58 -0
package/README.md
CHANGED
|
@@ -1,14 +1,59 @@
|
|
|
1
1
|
# @naumu/mcp
|
|
2
2
|
|
|
3
|
-
MCP server for [Naumu](https://naumu.ai)
|
|
3
|
+
MCP server for [Naumu](https://naumu.ai) - access your knowledge graph from Claude Code, Cursor, and other AI agents.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Naumu speaks MCP over two transports:
|
|
6
|
+
|
|
7
|
+
1. **Remote, over OAuth** at `https://naumu.ai/api/mcp` - the recommended path. You sign in through your browser, and there is no API key to create, paste, or keep in sync. This package is not involved.
|
|
8
|
+
2. **Local stdio**, this package, launched on demand with `npx` and authenticated with an API key. Use it for clients that cannot do browser OAuth: CI jobs, bot and external-agent identities, and older stdio-only harnesses.
|
|
9
|
+
|
|
10
|
+
## Recommended setup: remote MCP over OAuth
|
|
11
|
+
|
|
12
|
+
Register the server once. The first time a session uses a Naumu tool, your browser opens to sign in and approve access. After that the tools are available in every session.
|
|
13
|
+
|
|
14
|
+
### Claude Code
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
claude mcp add --transport http --scope user naumu https://naumu.ai/api/mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`--scope user` registers the server for your whole machine rather than the current project, so you do not have to repeat it per repository.
|
|
21
|
+
|
|
22
|
+
### Cursor
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"naumu": {
|
|
28
|
+
"url": "https://naumu.ai/api/mcp"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Leave any OAuth client ID and secret fields blank. Naumu registers the client for you automatically during sign-in, using Dynamic Client Registration (DCR).
|
|
35
|
+
|
|
36
|
+
Other MCP-capable clients that support remote servers follow the same shape: point them at `https://naumu.ai/api/mcp` and let the browser handle sign-in. Use the front-end origin (`naumu.ai`), not a backend address - sign-in and consent rely on your Naumu session, which lives on the front-end origin.
|
|
37
|
+
|
|
38
|
+
You can revoke a connected tool's access at any time in Naumu under **Settings > Your agents**, in the **Active access** list.
|
|
39
|
+
|
|
40
|
+
See [Local MCP](https://naumu.ai/docs/local-mcp) for the full walkthrough.
|
|
41
|
+
|
|
42
|
+
## Fallback setup: local stdio server with an API key
|
|
43
|
+
|
|
44
|
+
Use this package when your client cannot do browser OAuth - CI, bots and external agent identities, or a harness that only speaks stdio.
|
|
6
45
|
|
|
7
46
|
### 1. Get an API key
|
|
8
47
|
|
|
9
|
-
|
|
48
|
+
1. Open **Settings > Your agents** in Naumu.
|
|
49
|
+
2. Select **Create an API key** and give it a recognizable name (such as the tool it is for).
|
|
50
|
+
3. Copy the key right away. It is shown only once and cannot be retrieved later. Keys start with `nmu_`.
|
|
51
|
+
|
|
52
|
+
Use a separate key per tool, so a leak only costs you that one key.
|
|
10
53
|
|
|
11
|
-
### 2. Configure your
|
|
54
|
+
### 2. Configure your client
|
|
55
|
+
|
|
56
|
+
You do not install this package globally; your client launches it on demand with `npx`.
|
|
12
57
|
|
|
13
58
|
#### Claude Code
|
|
14
59
|
|
|
@@ -20,8 +65,6 @@ claude mcp add naumu -s user \
|
|
|
20
65
|
|
|
21
66
|
#### Cursor
|
|
22
67
|
|
|
23
|
-
Add to your Cursor MCP settings:
|
|
24
|
-
|
|
25
68
|
```json
|
|
26
69
|
{
|
|
27
70
|
"mcpServers": {
|
|
@@ -36,39 +79,138 @@ Add to your Cursor MCP settings:
|
|
|
36
79
|
}
|
|
37
80
|
```
|
|
38
81
|
|
|
82
|
+
### Environment variables
|
|
83
|
+
|
|
84
|
+
| Variable | Required | Default | Description |
|
|
85
|
+
|----------|----------|---------|-------------|
|
|
86
|
+
| `NAUMU_API_KEY` | Yes | - | Your API key (starts with `nmu_`) |
|
|
87
|
+
| `NAUMU_API_URL` | No | `https://naumu.ai` | Naumu API base URL. You do not need to set this |
|
|
88
|
+
|
|
39
89
|
## Available tools
|
|
40
90
|
|
|
91
|
+
Both transports expose the same tool surface. Tools marked **bot** are only registered for bot identity keys (`nmu_bot_*`), which additionally receive a filtered subset based on the identity's permissions.
|
|
92
|
+
|
|
93
|
+
### Spaces and members
|
|
94
|
+
|
|
41
95
|
| Tool | Description |
|
|
42
96
|
|------|-------------|
|
|
43
|
-
| `naumu_list_graphs` | List
|
|
44
|
-
| `
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `naumu_filter` | Filter nodes by type and attributes with deterministic results |
|
|
48
|
-
| `naumu_get_node` | Get a node with all its properties and connections |
|
|
49
|
-
| `naumu_add_node` | Create a new node |
|
|
50
|
-
| `naumu_update_node` | Update an existing node |
|
|
51
|
-
| `naumu_add_edge` | Create a relationship between two nodes |
|
|
52
|
-
| `naumu_remove_node` | Delete a node and its connections |
|
|
53
|
-
| `naumu_remove_edge` | Delete a relationship |
|
|
54
|
-
| `naumu_create_topic` | Create a topic (filing destination) in a space; admin-only, name must be a lowercase slug, returns an `id` for `topicIds` params |
|
|
55
|
-
| `naumu_ask` | Ask @Naumu a question and get a synthesised answer back (answer + sources + confidence + threadId + status); leaves a visible thread in the space |
|
|
56
|
-
| `naumu_delegate` | Hand @Naumu a task to carry out asynchronously (add knowledge, make changes); returns a `threadId` immediately, then poll with `naumu_read_thread` |
|
|
57
|
-
| `naumu_read_thread` | Read a thread's messages; each message carries a `status` of `processing` or `complete` |
|
|
58
|
-
| `naumu_whoami` | (Bot keys) Self-introspection — id, graphId, name, instructions, allowed tools |
|
|
59
|
-
| `naumu_list_threads` | (Bot keys) List threads in your graph with `isParticipant` flag |
|
|
60
|
-
| `naumu_get_thread` | (Bot keys) Fetch a thread plus its human + bot participant rosters |
|
|
61
|
-
| `naumu_create_thread` | (Bot keys only) Start a new conversation, attach participants, post initial message, optionally file into topics via `topicIds` |
|
|
62
|
-
| `naumu_request_attachment_upload` | (Bot keys) Get a presigned S3 URL to upload a file; pair with `naumu_post_message`'s `attachmentIds` |
|
|
63
|
-
| `naumu_add_reaction` | (Bot keys) Add an emoji reaction to a message in a thread you participate in (idempotent) |
|
|
64
|
-
| `naumu_remove_reaction` | (Bot keys) Remove your own reaction from a message |
|
|
65
|
-
|
|
66
|
-
## Environment variables
|
|
97
|
+
| `naumu_list_graphs` | List every space (graph) the key can access, with ids, names, and roles |
|
|
98
|
+
| `naumu_create_graph` | Create a new, empty space owned by the authenticated user |
|
|
99
|
+
| `naumu_list_members` | List a space's members with their roles |
|
|
100
|
+
| `naumu_whoami` | Return who the calling key is (user, or bot identity with its graph and allowed tools) |
|
|
67
101
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
|
71
|
-
|
|
102
|
+
### Schema
|
|
103
|
+
|
|
104
|
+
| Tool | Description |
|
|
105
|
+
|------|-------------|
|
|
106
|
+
| `naumu_get_schema` | Get a space's schema (node types, attributes, connections) |
|
|
107
|
+
| `naumu_update_schema` | Replace the schema with a full new definition |
|
|
108
|
+
| `naumu_add_node_type` | Add one node type without resending the whole schema |
|
|
109
|
+
| `naumu_add_connection` | Add one connection from an existing node type to another |
|
|
110
|
+
| `naumu_add_attribute` | Add or extend an attribute on an existing node type |
|
|
111
|
+
| `naumu_list_schema_violations` | Audit a space against its schema and report violations |
|
|
112
|
+
|
|
113
|
+
### Reading the graph
|
|
114
|
+
|
|
115
|
+
| Tool | Description |
|
|
116
|
+
|------|-------------|
|
|
117
|
+
| `naumu_search` | Hybrid (meaning plus text) search over graph nodes |
|
|
118
|
+
| `naumu_filter` | Filter nodes by type and attributes, with deterministic and complete results |
|
|
119
|
+
| `naumu_get_node` | Get one node with its properties, edges, notes, threads, and generated context |
|
|
120
|
+
| `naumu_list_node_connections` | List a single node's connections |
|
|
121
|
+
| `naumu_list_dense_nodes` | Find over-connected hub nodes by child count |
|
|
122
|
+
| `naumu_ask` | Ask @Naumu a question and get a synthesised, node-grounded answer with sources and confidence |
|
|
123
|
+
|
|
124
|
+
### Writing to the graph
|
|
125
|
+
|
|
126
|
+
| Tool | Description |
|
|
127
|
+
|------|-------------|
|
|
128
|
+
| `naumu_add_node` | Create 1-25 nodes in one call |
|
|
129
|
+
| `naumu_update_node` | Overwrite properties of an existing node |
|
|
130
|
+
| `naumu_remove_node` | Delete a node and all of its connections (destructive) |
|
|
131
|
+
| `naumu_add_edge` | Create 1-25 edges between existing nodes |
|
|
132
|
+
| `naumu_remove_edge` | Delete one edge identified by (source, target, label) |
|
|
133
|
+
| `naumu_remove_edges_bulk` | Delete 1-100 edges in one atomic call |
|
|
134
|
+
| `naumu_reparent` | Atomically swap a node's parent edge |
|
|
135
|
+
| `naumu_batch_reparent` | Reparent 1-25 nodes onto a shared new parent |
|
|
136
|
+
| `naumu_delegate` | Hand @Naumu a task to carry out asynchronously; returns a `threadId` to poll |
|
|
137
|
+
|
|
138
|
+
### Threads and topics
|
|
139
|
+
|
|
140
|
+
| Tool | Description |
|
|
141
|
+
|------|-------------|
|
|
142
|
+
| `naumu_list_threads` | List threads by last activity, with an `isParticipant` flag |
|
|
143
|
+
| `naumu_get_thread` | Fetch one thread plus its human and bot participant rosters |
|
|
144
|
+
| `naumu_read_thread` | Read a thread's messages; each carries `processing` or `complete` status |
|
|
145
|
+
| `naumu_post_message` | Post a markdown message in a thread you participate in |
|
|
146
|
+
| `naumu_create_thread` | **bot** Start a new conversation, attach participants, post a first message, optionally file into topics |
|
|
147
|
+
| `naumu_add_reaction` | Add an emoji reaction to a message (idempotent) |
|
|
148
|
+
| `naumu_remove_reaction` | Remove your own reaction from a message |
|
|
149
|
+
| `naumu_typing` | Show or hide your "is typing" indicator in a thread |
|
|
150
|
+
| `naumu_list_topics` | List a space's topics (filing destinations) for use in `topicIds` |
|
|
151
|
+
| `naumu_create_topic` | Create a topic; admin-only, name must be a lowercase slug |
|
|
152
|
+
|
|
153
|
+
### Notes
|
|
154
|
+
|
|
155
|
+
| Tool | Description |
|
|
156
|
+
|------|-------------|
|
|
157
|
+
| `naumu_create_note` | Create a note, optionally with its full markdown body in the same call |
|
|
158
|
+
| `naumu_note_read` | Read a note's current markdown plus the nodes it is tied to |
|
|
159
|
+
| `naumu_note_append` | Append markdown blocks to the end of a note |
|
|
160
|
+
| `naumu_note_insert` | Insert markdown immediately after a named heading |
|
|
161
|
+
| `naumu_note_replace_section` | Replace the body under a named heading |
|
|
162
|
+
| `naumu_note_delete_section` | Delete a heading and its body (destructive) |
|
|
163
|
+
| `naumu_note_replace` | Replace an entire note's content (destructive) |
|
|
164
|
+
| `naumu_note_find_replace` | Literal find and replace within a note |
|
|
165
|
+
|
|
166
|
+
### Attachments
|
|
167
|
+
|
|
168
|
+
| Tool | Description |
|
|
169
|
+
|------|-------------|
|
|
170
|
+
| `naumu_request_attachment_upload` | Get a presigned S3 URL to upload a file, then reference it via `attachmentIds` |
|
|
171
|
+
| `naumu_get_attachment` | Read a chat attachment by `attachmentId` |
|
|
172
|
+
| `naumu_persist_canvas_attachment` | Promote a canvas-bound pending attachment to a persisted one |
|
|
173
|
+
|
|
174
|
+
### Space admission
|
|
175
|
+
|
|
176
|
+
| Tool | Description |
|
|
177
|
+
|------|-------------|
|
|
178
|
+
| `naumu_admission_status` | Show who can auto-join a space and who is waiting for approval |
|
|
179
|
+
| `naumu_whitelist_members` | Whitelist emails so those people auto-join on sign-in |
|
|
180
|
+
| `naumu_resolve_admission` | Join (or request to join) the space a repo's `.naumu` file references |
|
|
181
|
+
| `naumu_resolve_join_request` | Approve or deny a pending join request |
|
|
182
|
+
|
|
183
|
+
### ChatGPT compatibility
|
|
184
|
+
|
|
185
|
+
The OpenAI MCP surface requires tools literally named `search` and `fetch`. These wrap cross-space search and node reads in the response shapes that client expects.
|
|
186
|
+
|
|
187
|
+
| Tool | Description |
|
|
188
|
+
|------|-------------|
|
|
189
|
+
| `search` | Search across every accessible space; returns `{ results: [{ id, title, url }] }` |
|
|
190
|
+
| `fetch` | Fetch a node returned by `search`; returns `{ id, title, text, url }` |
|
|
191
|
+
|
|
192
|
+
## Troubleshooting
|
|
193
|
+
|
|
194
|
+
When the stdio server is misconfigured it exits before the MCP handshake, and most clients simply show no server at all, with no error. Run the built-in self-check to find out why:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npx -y -p @naumu/mcp naumu-mcp doctor
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
It checks, in order, that `NAUMU_API_KEY` is set (the key itself is never printed), that the API is reachable and accepts the key, and that this build matches the latest published version. It prints one line per check plus a verdict, and exits non-zero if anything failed:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
naumu-mcp doctor (v0.11.1, API https://naumu.ai)
|
|
204
|
+
|
|
205
|
+
[fail] API key: NAUMU_API_KEY is missing from the environment
|
|
206
|
+
[warn] API: reachable (https://naumu.ai answered HTTP 403), but there is no NAUMU_API_KEY to verify
|
|
207
|
+
[ok] Version: running 0.11.1 (npm latest)
|
|
208
|
+
|
|
209
|
+
Verdict: 1 check failed - the MCP server will not work until this is fixed.
|
|
210
|
+
Most likely fix: Set NAUMU_API_KEY - create a key at https://naumu.ai/settings (Settings > Your agents > Create an API key)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Run it with the same environment your MCP client uses, otherwise the key check reports on your shell rather than on the client's configuration.
|
|
72
214
|
|
|
73
215
|
## License
|
|
74
216
|
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ function safeErrorMessage(status, upstream) {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// ../mcp-core/src/version.ts
|
|
33
|
-
var NAUMU_MCP_VERSION = "0.
|
|
33
|
+
var NAUMU_MCP_VERSION = "0.12.1";
|
|
34
34
|
|
|
35
35
|
// ../mcp-core/src/client.ts
|
|
36
36
|
var HEADER_VALUE_MAX_LENGTH = 100;
|
|
@@ -677,7 +677,7 @@ function registerGetNode(server2, client2) {
|
|
|
677
677
|
{
|
|
678
678
|
title: "Get Node",
|
|
679
679
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
680
|
-
description: "Get a single node with all its properties
|
|
680
|
+
description: "Get a single node with all its properties, connections (incoming and outgoing edges with neighbor labels), and tied context: attached notes, conversations (threads), scheduled tasks, and the generated summary when one exists.",
|
|
681
681
|
inputSchema: z11.object({
|
|
682
682
|
graphId: z11.string().describe("The graph ID"),
|
|
683
683
|
nodeId: z11.string().describe("The node ID")
|
|
@@ -1171,15 +1171,16 @@ function registerPostMessage(server2, client2) {
|
|
|
1171
1171
|
idempotentHint: false,
|
|
1172
1172
|
openWorldHint: false
|
|
1173
1173
|
},
|
|
1174
|
-
description: 'Post a message in a Naumu thread you participate in. Use it to reply to humans (or other bots) in a thread that pinged you. Write `content` in markdown (the default format): **bold**, *italic*, `inline code`, fenced code blocks, `- ` bullets, `1. ` ordered lists, and `> ` quotes all render natively; headings and tables are not supported and render as plain text. Mentions are inline pills: `@[Name](id)` mentions a person or bot and `#[label](topic-id)` tags a topic. For a human the mention id is their User id (from naumu_list_members or naumu_get_thread participantDetails) or their email - both work; for a bot/agent use its identity id; `@[Naumu](naumu-ai)` addresses the @Naumu agent (a bare `@naumu` in prose also summons it, so only type it when you mean to). @mentioning people loops them in (mention notifications, prompts) without invoking @Naumu. Set contentFormat to "tiptap" only when you need rich content beyond the markdown subset, passing a Tiptap JSON document with mention nodes (`{ type: "mention", attrs: { id, label } }`). To attach files call naumu_request_attachment_upload first, PUT the bytes to the returned uploadUrl, then pass the resulting attachmentIds here. The message needs either `content` or `attachmentIds`. Returns the created message JSON. To get a synthesised answer from @Naumu, use naumu_ask.',
|
|
1174
|
+
description: 'Post a message in a Naumu thread you participate in. Use it to reply to humans (or other bots) in a thread that pinged you. Write `content` in markdown (the default format): **bold**, *italic*, `inline code`, fenced code blocks, `- ` bullets, `1. ` ordered lists, and `> ` quotes all render natively; headings and tables are not supported and render as plain text. Mentions are inline pills: `@[Name](id)` mentions a person or bot and `#[label](topic-id)` tags a topic. For a human the mention id is their User id (from naumu_list_members or naumu_get_thread participantDetails) or their email - both work; for a bot/agent use its identity id; `@[Naumu](naumu-ai)` addresses the @Naumu agent (a bare `@naumu` in prose also summons it, so only type it when you mean to). @mentioning people loops them in (mention notifications, prompts) without invoking @Naumu. Set contentFormat to "tiptap" only when you need rich content beyond the markdown subset, passing a Tiptap JSON document with mention nodes (`{ type: "mention", attrs: { id, label } }`). To attach files call naumu_request_attachment_upload first, PUT the bytes to the returned uploadUrl, then pass the resulting attachmentIds here. The message needs either `content` or `attachmentIds`. Returns the created message JSON. By default this only appends text: @Naumu is NOT summoned unless the thread auto-responds or you mention it, so nothing is committed to the graph. Pass `invokeAgent: true` when you need @Naumu to act on the message (record a work-log entry, file a status update); it summons @Naumu unconditionally, even in paused threads, and @Naumu works in the background - poll naumu_read_thread to see what it did. To get a synthesised answer from @Naumu, use naumu_ask.',
|
|
1175
1175
|
inputSchema: z20.object({
|
|
1176
1176
|
threadId: z20.string().describe("The thread ID to post into. You must be a participant in this thread."),
|
|
1177
1177
|
content: z20.string().optional().describe('Message body. Markdown by default (see the tool description for the supported subset and the `@[Name](id)` mention pill syntax); a Tiptap JSON document when contentFormat is "tiptap". Optional when `attachmentIds` is provided.'),
|
|
1178
1178
|
contentFormat: z20.enum(["tiptap", "markdown"]).optional().describe('Format of `content`. Defaults to "markdown" (rendered subset plus `@[Name](id)` / `#[label](topic-id)` mention pills). Use "tiptap" for full rich content, e.g. a doc containing `{ type: "mention", attrs: { id: userIdOrEmailOrIdentityId, label: displayName } }`.'),
|
|
1179
|
-
attachmentIds: z20.array(z20.string().min(1)).max(25).optional().describe("Attachment IDs from prior `naumu_request_attachment_upload` calls. Each must be a successfully-uploaded pending attachment in this graph (1-hour TTL). Up to 25 per message.")
|
|
1179
|
+
attachmentIds: z20.array(z20.string().min(1)).max(25).optional().describe("Attachment IDs from prior `naumu_request_attachment_upload` calls. Each must be a successfully-uploaded pending attachment in this graph (1-hour TTL). Up to 25 per message."),
|
|
1180
|
+
invokeAgent: z20.boolean().optional().describe("Set true to summon @Naumu on this message unconditionally (mention-equivalent), even in threads where auto-response is paused. Required whenever the post must be committed to the graph (work-log entries, status updates). @Naumu processes in the background; poll naumu_read_thread. Defaults to false: plain append, no agent turn.")
|
|
1180
1181
|
})
|
|
1181
1182
|
},
|
|
1182
|
-
async ({ threadId, content, contentFormat, attachmentIds }) => {
|
|
1183
|
+
async ({ threadId, content, contentFormat, attachmentIds, invokeAgent }) => {
|
|
1183
1184
|
try {
|
|
1184
1185
|
const body = {
|
|
1185
1186
|
// 'markdown' is the announced name for the API's 'text' mode;
|
|
@@ -1188,6 +1189,10 @@ function registerPostMessage(server2, client2) {
|
|
|
1188
1189
|
};
|
|
1189
1190
|
if (content !== void 0) body.content = content;
|
|
1190
1191
|
if (attachmentIds && attachmentIds.length > 0) body.attachmentIds = attachmentIds;
|
|
1192
|
+
if (invokeAgent === true) {
|
|
1193
|
+
body.invokeAgent = true;
|
|
1194
|
+
body.async = true;
|
|
1195
|
+
}
|
|
1191
1196
|
const data = await client2.post(`/api/threads/${threadId}/messages`, body);
|
|
1192
1197
|
return {
|
|
1193
1198
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
@@ -1283,15 +1288,31 @@ function registerListThreads(server2, client2) {
|
|
|
1283
1288
|
{
|
|
1284
1289
|
title: "List Threads",
|
|
1285
1290
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1286
|
-
description: "List threads sorted by last activity (newest first), for self-discovery before deciding which to engage. With a user API key, pass `graphId` to list threads you can see in that space (resolve it via naumu_list_graphs). With a bot identity key, omit `graphId` to list threads in your own graph \u2014 each row carries an `isParticipant` flag (TRUE means you were explicitly invited and your replies fan out via webhook). Page back with `cursor` set to the oldest `lastActivityAt` from the previous page.",
|
|
1291
|
+
description: "List threads sorted by last activity (newest first), for self-discovery before deciding which to engage. With a user API key, pass `graphId` to list threads you can see in that space (resolve it via naumu_list_graphs). Pass `nodeId` alongside `graphId` to list only the conversations tied to that node (attached, or that created/modified it). With a bot identity key, omit `graphId` to list threads in your own graph \u2014 each row carries an `isParticipant` flag (TRUE means you were explicitly invited and your replies fan out via webhook). Page back with `cursor` set to the oldest `lastActivityAt` from the previous page.",
|
|
1287
1292
|
inputSchema: z23.object({
|
|
1288
1293
|
graphId: z23.string().optional().describe("Graph (space) ID. Required for user API keys; omit for bot identity keys (defaults to your own graph)."),
|
|
1294
|
+
nodeId: z23.string().optional().describe("Scope the listing to conversations tied to this node. Requires `graphId`."),
|
|
1289
1295
|
cursor: z23.number().int().optional().describe("Unix timestamp ms \u2014 returns threads with `lastActivityAt` strictly older than this. Omit for the newest page."),
|
|
1290
1296
|
limit: z23.number().int().min(1).max(200).optional().describe("Page size, default 50, max 200.")
|
|
1291
1297
|
})
|
|
1292
1298
|
},
|
|
1293
|
-
async ({ graphId, cursor, limit }) => {
|
|
1299
|
+
async ({ graphId, nodeId, cursor, limit }) => {
|
|
1294
1300
|
try {
|
|
1301
|
+
if (nodeId) {
|
|
1302
|
+
if (!graphId) {
|
|
1303
|
+
return {
|
|
1304
|
+
content: [{ type: "text", text: "Error: `nodeId` requires `graphId`." }],
|
|
1305
|
+
isError: true
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
const data2 = await client2.get(
|
|
1309
|
+
`/api/threads?graphId=${encodeURIComponent(graphId)}&nodeId=${encodeURIComponent(nodeId)}`
|
|
1310
|
+
);
|
|
1311
|
+
const clean2 = Array.isArray(data2) ? data2.map(sanitizeThreadParticipants) : data2;
|
|
1312
|
+
return {
|
|
1313
|
+
content: [{ type: "text", text: JSON.stringify(clean2, null, 2) }]
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1295
1316
|
const params = new URLSearchParams();
|
|
1296
1317
|
if (cursor !== void 0) params.set("cursor", String(cursor));
|
|
1297
1318
|
if (limit !== void 0) params.set("limit", String(limit));
|
|
@@ -1841,7 +1862,7 @@ function registerNoteRead(server2, client2) {
|
|
|
1841
1862
|
{
|
|
1842
1863
|
title: "Read Note",
|
|
1843
1864
|
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
|
|
1844
|
-
description: "Read the current contents of a note as markdown; use before editing so you know what you're working with. `naumu_note_find_replace` and the section-based tools (`naumu_note_insert`, `naumu_note_replace_section`, `naumu_note_delete_section`) anchor on text/headings present in the live doc.",
|
|
1865
|
+
description: "Read the current contents of a note as markdown, with its title and `connections` (the graph nodes the note is tied to); use before editing so you know what you're working with. `naumu_note_find_replace` and the section-based tools (`naumu_note_insert`, `naumu_note_replace_section`, `naumu_note_delete_section`) anchor on text/headings present in the live doc.",
|
|
1845
1866
|
inputSchema: z34.object({
|
|
1846
1867
|
noteId: z34.string().describe("The note (Thought) ID")
|
|
1847
1868
|
})
|
|
@@ -2345,7 +2366,8 @@ var NON_ATTRIBUTE_PROPS = /* @__PURE__ */ new Set([
|
|
|
2345
2366
|
"updatedAt",
|
|
2346
2367
|
"heatConvSignal",
|
|
2347
2368
|
"heatConvComputedAt",
|
|
2348
|
-
"heatComputedAt"
|
|
2369
|
+
"heatComputedAt",
|
|
2370
|
+
"context"
|
|
2349
2371
|
]);
|
|
2350
2372
|
function parseResourceId(id) {
|
|
2351
2373
|
const sep = id.indexOf(":");
|
|
@@ -2374,14 +2396,42 @@ ${attrLines.join("\n")}`);
|
|
|
2374
2396
|
const edgeLines = [];
|
|
2375
2397
|
for (const e of connections?.outgoing ?? []) {
|
|
2376
2398
|
if (!e?.target) continue;
|
|
2377
|
-
|
|
2399
|
+
const target = e.targetLabel ? `${e.targetLabel} (${e.target})` : e.target;
|
|
2400
|
+
edgeLines.push(`- ${e.label ?? "related"} \u2192 ${target}${e.isParent ? " (parent)" : ""}`);
|
|
2378
2401
|
}
|
|
2379
2402
|
for (const e of connections?.incoming ?? []) {
|
|
2380
2403
|
if (!e?.source) continue;
|
|
2381
|
-
|
|
2404
|
+
const source = e.sourceLabel ? `${e.sourceLabel} (${e.source})` : e.source;
|
|
2405
|
+
edgeLines.push(`- ${source} \u2192 ${e.label ?? "related"} (incoming)`);
|
|
2382
2406
|
}
|
|
2383
2407
|
if (edgeLines.length) parts.push(`Connections:
|
|
2384
2408
|
${edgeLines.join("\n")}`);
|
|
2409
|
+
const ctx = node.context ?? {};
|
|
2410
|
+
if (ctx.notes?.length) {
|
|
2411
|
+
const more = (ctx.notesTotal ?? ctx.notes.length) - ctx.notes.length;
|
|
2412
|
+
const lines = ctx.notes.map((n) => `- ${n.title || "Untitled"} (${n.id})`);
|
|
2413
|
+
if (more > 0) lines.push(`- \u2026and ${more} more`);
|
|
2414
|
+
parts.push(`Notes:
|
|
2415
|
+
${lines.join("\n")}`);
|
|
2416
|
+
}
|
|
2417
|
+
if (ctx.threads?.length) {
|
|
2418
|
+
const more = (ctx.threadsTotal ?? ctx.threads.length) - ctx.threads.length;
|
|
2419
|
+
const lines = ctx.threads.map(
|
|
2420
|
+
(t) => `- ${t.title || "Untitled"} (${t.id}${t.relations?.length ? ` \xB7 ${t.relations.join(", ")}` : ""})`
|
|
2421
|
+
);
|
|
2422
|
+
if (more > 0) lines.push(`- \u2026and ${more} more`);
|
|
2423
|
+
parts.push(`Conversations:
|
|
2424
|
+
${lines.join("\n")}`);
|
|
2425
|
+
}
|
|
2426
|
+
if (ctx.scheduledTasks?.length) {
|
|
2427
|
+
const lines = ctx.scheduledTasks.map(
|
|
2428
|
+
(t) => `- ${t.title} (${t.id}${t.cron ? ` \xB7 cron ${t.cron}` : ""})`
|
|
2429
|
+
);
|
|
2430
|
+
parts.push(`Scheduled tasks:
|
|
2431
|
+
${lines.join("\n")}`);
|
|
2432
|
+
}
|
|
2433
|
+
if (ctx.summary?.content) parts.push(`Summary:
|
|
2434
|
+
${ctx.summary.content}`);
|
|
2385
2435
|
return parts.join("\n\n");
|
|
2386
2436
|
}
|
|
2387
2437
|
function registerChatgptFetch(server2, client2) {
|
|
@@ -2642,11 +2692,12 @@ var TOOL_REGISTRARS = {
|
|
|
2642
2692
|
naumu_whoami: registerWhoami,
|
|
2643
2693
|
naumu_list_threads: registerListThreads,
|
|
2644
2694
|
naumu_list_topics: registerListTopics,
|
|
2645
|
-
// User-surface only.
|
|
2646
|
-
//
|
|
2647
|
-
//
|
|
2648
|
-
//
|
|
2649
|
-
//
|
|
2695
|
+
// User-surface only. Omitted from BOT_ONLY_TOOL_NAMES and from the backend
|
|
2696
|
+
// PERMISSION_TO_MCP_TOOLS map (same treatment as the admission tools
|
|
2697
|
+
// below), so bot manifests never include it. Note the backend gate alone
|
|
2698
|
+
// no longer keeps bots out: `space:manage-topics` is editor+ since
|
|
2699
|
+
// 2026-08-11 and bot identities resolve to the editor role — the manifest
|
|
2700
|
+
// omission is what keeps this tool off the bot surface.
|
|
2650
2701
|
naumu_create_topic: registerCreateTopic,
|
|
2651
2702
|
naumu_get_thread: registerGetThread,
|
|
2652
2703
|
naumu_create_thread: registerCreateThread,
|
|
@@ -2715,11 +2766,204 @@ function registerNamedTools(server2, client2, toolNames) {
|
|
|
2715
2766
|
console.error(`[mcp] registered ${registered.length} tool(s) for bot key`);
|
|
2716
2767
|
}
|
|
2717
2768
|
|
|
2769
|
+
// src/doctor.ts
|
|
2770
|
+
var MARKERS = {
|
|
2771
|
+
pass: "[ok] ",
|
|
2772
|
+
fail: "[fail]",
|
|
2773
|
+
warn: "[warn]"
|
|
2774
|
+
};
|
|
2775
|
+
var BOT_KEY_PREFIX = "nmu_bot_";
|
|
2776
|
+
var SETTINGS_URL = "https://naumu.ai/settings";
|
|
2777
|
+
var REGISTRY_LATEST_URL = "https://registry.npmjs.org/@naumu/mcp/latest";
|
|
2778
|
+
var API_TIMEOUT_MS = 1e4;
|
|
2779
|
+
var REGISTRY_TIMEOUT_MS = 4e3;
|
|
2780
|
+
var errorMessage = (err) => err instanceof Error ? err.message : String(err);
|
|
2781
|
+
var timeoutSignal = (ms) => typeof AbortSignal?.timeout === "function" ? AbortSignal.timeout(ms) : void 0;
|
|
2782
|
+
var formatDoctorCheck = (check) => `${MARKERS[check.status]} ${check.label}: ${check.detail}`;
|
|
2783
|
+
var checkApiKey = (apiKey2) => {
|
|
2784
|
+
if (!apiKey2) {
|
|
2785
|
+
return {
|
|
2786
|
+
label: "API key",
|
|
2787
|
+
status: "fail",
|
|
2788
|
+
detail: "NAUMU_API_KEY is missing from the environment",
|
|
2789
|
+
fix: `Set NAUMU_API_KEY - create a key at ${SETTINGS_URL} (Settings > Your agents > Create an API key)`
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
const kind = apiKey2.startsWith(BOT_KEY_PREFIX) ? "bot key" : "user key";
|
|
2793
|
+
return {
|
|
2794
|
+
label: "API key",
|
|
2795
|
+
status: "pass",
|
|
2796
|
+
detail: `NAUMU_API_KEY is set (${kind}; value not printed)`
|
|
2797
|
+
};
|
|
2798
|
+
};
|
|
2799
|
+
var describeWhoami = (data) => {
|
|
2800
|
+
const row = data ?? {};
|
|
2801
|
+
if (row.kind === "user") {
|
|
2802
|
+
const email = row.email ? ` <${row.email}>` : "";
|
|
2803
|
+
return `authenticated as ${row.name ?? "unknown user"}${email}`;
|
|
2804
|
+
}
|
|
2805
|
+
if (row.id) {
|
|
2806
|
+
return `authenticated as bot identity ${row.name ?? row.id} (graph ${row.graphId ?? "unknown"})`;
|
|
2807
|
+
}
|
|
2808
|
+
return "authenticated, but the identity payload was not recognized";
|
|
2809
|
+
};
|
|
2810
|
+
var checkApiReachability = async (options) => {
|
|
2811
|
+
const label = "API";
|
|
2812
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
2813
|
+
const headers = { Accept: "application/json" };
|
|
2814
|
+
if (options.apiKey) headers.Authorization = `Bearer ${options.apiKey}`;
|
|
2815
|
+
let res;
|
|
2816
|
+
try {
|
|
2817
|
+
res = await doFetch(`${options.apiUrl}/api/identities/me/whoami`, {
|
|
2818
|
+
headers,
|
|
2819
|
+
signal: timeoutSignal(options.timeoutMs ?? API_TIMEOUT_MS)
|
|
2820
|
+
});
|
|
2821
|
+
} catch (err) {
|
|
2822
|
+
return {
|
|
2823
|
+
label,
|
|
2824
|
+
status: "fail",
|
|
2825
|
+
detail: `unreachable - no response from ${options.apiUrl} (${errorMessage(err)})`,
|
|
2826
|
+
fix: `Check your network and NAUMU_API_URL - nothing answered at ${options.apiUrl}`
|
|
2827
|
+
};
|
|
2828
|
+
}
|
|
2829
|
+
if (res.status === 401 || res.status === 403) {
|
|
2830
|
+
if (!options.apiKey) {
|
|
2831
|
+
return {
|
|
2832
|
+
label,
|
|
2833
|
+
status: "warn",
|
|
2834
|
+
detail: `reachable (${options.apiUrl} answered HTTP ${res.status}), but there is no NAUMU_API_KEY to verify`
|
|
2835
|
+
};
|
|
2836
|
+
}
|
|
2837
|
+
return {
|
|
2838
|
+
label,
|
|
2839
|
+
status: "fail",
|
|
2840
|
+
detail: `reachable, but ${options.apiUrl} rejected the key (HTTP ${res.status})`,
|
|
2841
|
+
fix: `NAUMU_API_KEY is invalid or revoked - create a new key at ${SETTINGS_URL} and update your MCP config`
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
if (!res.ok) {
|
|
2845
|
+
return {
|
|
2846
|
+
label,
|
|
2847
|
+
status: "fail",
|
|
2848
|
+
detail: `reachable, but ${options.apiUrl} answered HTTP ${res.status}`,
|
|
2849
|
+
fix: "The Naumu API returned an unexpected status - retry shortly, or check NAUMU_API_URL points at the front-end origin"
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
try {
|
|
2853
|
+
const data = await res.json();
|
|
2854
|
+
return { label, status: "pass", detail: `reachable at ${options.apiUrl}; ${describeWhoami(data)}` };
|
|
2855
|
+
} catch (err) {
|
|
2856
|
+
return {
|
|
2857
|
+
label,
|
|
2858
|
+
status: "fail",
|
|
2859
|
+
detail: `reachable, but the response from ${options.apiUrl} was not JSON (${errorMessage(err)})`,
|
|
2860
|
+
fix: "NAUMU_API_URL does not look like a Naumu API - point it at https://naumu.ai"
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2863
|
+
};
|
|
2864
|
+
var compareVersions = (a, b) => {
|
|
2865
|
+
const parse = (value) => value.split(".").slice(0, 3).map((part) => Number.parseInt(part, 10) || 0);
|
|
2866
|
+
const left = parse(a);
|
|
2867
|
+
const right = parse(b);
|
|
2868
|
+
for (let i = 0; i < 3; i += 1) {
|
|
2869
|
+
const diff = (left[i] ?? 0) - (right[i] ?? 0);
|
|
2870
|
+
if (diff !== 0) return diff < 0 ? -1 : 1;
|
|
2871
|
+
}
|
|
2872
|
+
return 0;
|
|
2873
|
+
};
|
|
2874
|
+
var checkVersionFreshness = async (options = {}) => {
|
|
2875
|
+
const label = "Version";
|
|
2876
|
+
const current = options.currentVersion ?? NAUMU_MCP_VERSION;
|
|
2877
|
+
const doFetch = options.fetchImpl ?? fetch;
|
|
2878
|
+
let latest;
|
|
2879
|
+
try {
|
|
2880
|
+
const res = await doFetch(REGISTRY_LATEST_URL, {
|
|
2881
|
+
headers: { Accept: "application/json" },
|
|
2882
|
+
signal: timeoutSignal(options.timeoutMs ?? REGISTRY_TIMEOUT_MS)
|
|
2883
|
+
});
|
|
2884
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
2885
|
+
const body = await res.json();
|
|
2886
|
+
latest = body?.version;
|
|
2887
|
+
} catch (err) {
|
|
2888
|
+
return {
|
|
2889
|
+
label,
|
|
2890
|
+
status: "warn",
|
|
2891
|
+
detail: `running ${current}; could not check the npm registry for the latest version (${errorMessage(err)})`
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
if (!latest) {
|
|
2895
|
+
return {
|
|
2896
|
+
label,
|
|
2897
|
+
status: "warn",
|
|
2898
|
+
detail: `running ${current}; could not check the npm registry for the latest version (no version field in the response)`
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
const comparison = compareVersions(current, latest);
|
|
2902
|
+
if (comparison < 0) {
|
|
2903
|
+
return {
|
|
2904
|
+
label,
|
|
2905
|
+
status: "fail",
|
|
2906
|
+
detail: `running ${current}, but npm latest is ${latest}`,
|
|
2907
|
+
fix: 'Update @naumu/mcp - run "npm cache clean --force" and reinstall (globally installed: "npm i -g @naumu/mcp@latest"; npx users: pin "@naumu/mcp@latest")'
|
|
2908
|
+
};
|
|
2909
|
+
}
|
|
2910
|
+
if (comparison > 0) {
|
|
2911
|
+
return { label, status: "pass", detail: `running ${current}, ahead of npm latest ${latest} (unpublished build)` };
|
|
2912
|
+
}
|
|
2913
|
+
return { label, status: "pass", detail: `running ${current} (npm latest)` };
|
|
2914
|
+
};
|
|
2915
|
+
var runDoctorChecks = async (options) => {
|
|
2916
|
+
const checks = [];
|
|
2917
|
+
checks.push(checkApiKey(options.apiKey));
|
|
2918
|
+
checks.push(
|
|
2919
|
+
await checkApiReachability({
|
|
2920
|
+
apiUrl: options.apiUrl,
|
|
2921
|
+
apiKey: options.apiKey,
|
|
2922
|
+
fetchImpl: options.fetchImpl
|
|
2923
|
+
})
|
|
2924
|
+
);
|
|
2925
|
+
checks.push(
|
|
2926
|
+
await checkVersionFreshness({
|
|
2927
|
+
currentVersion: options.currentVersion,
|
|
2928
|
+
fetchImpl: options.fetchImpl
|
|
2929
|
+
})
|
|
2930
|
+
);
|
|
2931
|
+
return checks;
|
|
2932
|
+
};
|
|
2933
|
+
var renderDoctorReport = (checks, context) => {
|
|
2934
|
+
const version = context.version ?? NAUMU_MCP_VERSION;
|
|
2935
|
+
const lines = [
|
|
2936
|
+
`naumu-mcp doctor (v${version}, API ${context.apiUrl})`,
|
|
2937
|
+
"",
|
|
2938
|
+
...checks.map(formatDoctorCheck),
|
|
2939
|
+
""
|
|
2940
|
+
];
|
|
2941
|
+
const failures = checks.filter((check) => check.status === "fail");
|
|
2942
|
+
if (failures.length === 0) {
|
|
2943
|
+
lines.push("Verdict: all checks passed - the MCP server should start normally.");
|
|
2944
|
+
return { text: lines.join("\n"), exitCode: 0 };
|
|
2945
|
+
}
|
|
2946
|
+
const plural = failures.length === 1 ? "check" : "checks";
|
|
2947
|
+
lines.push(`Verdict: ${failures.length} ${plural} failed - the MCP server will not work until this is fixed.`);
|
|
2948
|
+
const fix = failures.find((check) => check.fix)?.fix;
|
|
2949
|
+
if (fix) lines.push(`Most likely fix: ${fix}`);
|
|
2950
|
+
return { text: lines.join("\n"), exitCode: 1 };
|
|
2951
|
+
};
|
|
2952
|
+
var runDoctorCli = async (options) => {
|
|
2953
|
+
const checks = await runDoctorChecks({ apiUrl: options.apiUrl, apiKey: options.apiKey });
|
|
2954
|
+
const report = renderDoctorReport(checks, { apiUrl: options.apiUrl });
|
|
2955
|
+
console.log(report.text);
|
|
2956
|
+
return report.exitCode;
|
|
2957
|
+
};
|
|
2958
|
+
|
|
2718
2959
|
// src/index.ts
|
|
2719
2960
|
var DEFAULT_API_URL = "https://naumu.ai";
|
|
2720
|
-
var
|
|
2961
|
+
var BOT_KEY_PREFIX2 = "nmu_bot_";
|
|
2721
2962
|
var apiUrl = (process.env.NAUMU_API_URL || DEFAULT_API_URL).replace(/\/api\/?$/, "");
|
|
2722
2963
|
var apiKey = process.env.NAUMU_API_KEY;
|
|
2964
|
+
if (process.argv[2] === "doctor") {
|
|
2965
|
+
process.exit(await runDoctorCli({ apiUrl, apiKey }));
|
|
2966
|
+
}
|
|
2723
2967
|
if (!apiKey) {
|
|
2724
2968
|
console.error("Missing required environment variable: NAUMU_API_KEY");
|
|
2725
2969
|
process.exit(1);
|
|
@@ -2734,7 +2978,7 @@ var server = new McpServer(
|
|
|
2734
2978
|
instructions: NAUMU_INSTRUCTIONS
|
|
2735
2979
|
}
|
|
2736
2980
|
);
|
|
2737
|
-
var isBotKey = apiKey.startsWith(
|
|
2981
|
+
var isBotKey = apiKey.startsWith(BOT_KEY_PREFIX2);
|
|
2738
2982
|
if (isBotKey) {
|
|
2739
2983
|
try {
|
|
2740
2984
|
const manifest = await client.get("/api/identities/me/mcp-tool-manifest");
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naumu/mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Naumu
|
|
3
|
+
"version": "0.12.1",
|
|
4
|
+
"description": "MCP server for Naumu - access your knowledge graph from Claude Code, Cursor, and other AI coding agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Naumu <hello@naumu.ai>",
|
|
7
|
+
"mcpName": "ai.naumu/mcp",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/naumu-
|
|
10
|
-
"directory": "packages/mcp"
|
|
10
|
+
"url": "https://github.com/naumu-ai/mcp"
|
|
11
11
|
},
|
|
12
12
|
"homepage": "https://naumu.ai",
|
|
13
|
-
"bugs": "https://github.com/naumu-
|
|
13
|
+
"bugs": "https://github.com/naumu-ai/mcp/issues",
|
|
14
14
|
"keywords": [
|
|
15
15
|
"mcp",
|
|
16
16
|
"model-context-protocol",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"type": "module",
|
|
29
29
|
"files": [
|
|
30
30
|
"dist",
|
|
31
|
-
"README.md"
|
|
31
|
+
"README.md",
|
|
32
|
+
"server.json"
|
|
32
33
|
],
|
|
33
34
|
"engines": {
|
|
34
35
|
"node": ">=18"
|
package/server.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "ai.naumu/mcp",
|
|
4
|
+
"title": "Naumu",
|
|
5
|
+
"description": "Search, extend, and act on your team's Naumu knowledge graph: notes, threads, and nodes.",
|
|
6
|
+
"version": "0.12.1",
|
|
7
|
+
"websiteUrl": "https://naumu.ai",
|
|
8
|
+
"repository": {
|
|
9
|
+
"url": "https://github.com/naumu-ai/mcp",
|
|
10
|
+
"source": "github"
|
|
11
|
+
},
|
|
12
|
+
"icons": [
|
|
13
|
+
{
|
|
14
|
+
"src": "https://cdn.naumu.ai/brand/icon-256.png",
|
|
15
|
+
"mimeType": "image/png",
|
|
16
|
+
"sizes": ["256x256"]
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"remotes": [
|
|
20
|
+
{
|
|
21
|
+
"type": "streamable-http",
|
|
22
|
+
"url": "https://naumu.ai/api/mcp"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"packages": [
|
|
26
|
+
{
|
|
27
|
+
"registryType": "npm",
|
|
28
|
+
"registryBaseUrl": "https://registry.npmjs.org",
|
|
29
|
+
"identifier": "@naumu/mcp",
|
|
30
|
+
"version": "0.12.1",
|
|
31
|
+
"runtimeHint": "npx",
|
|
32
|
+
"transport": { "type": "stdio" },
|
|
33
|
+
"environmentVariables": [
|
|
34
|
+
{
|
|
35
|
+
"name": "NAUMU_API_KEY",
|
|
36
|
+
"description": "Naumu API key (starts with nmu_), created in Naumu under Settings > Your agents.",
|
|
37
|
+
"isRequired": true,
|
|
38
|
+
"isSecret": true,
|
|
39
|
+
"format": "string"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "NAUMU_API_URL",
|
|
43
|
+
"description": "Override the Naumu API base URL. Defaults to https://naumu.ai.",
|
|
44
|
+
"isRequired": false,
|
|
45
|
+
"isSecret": false,
|
|
46
|
+
"format": "string"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "NAUMU_IDENTITY_ID",
|
|
50
|
+
"description": "Optional bot identity id to act as when the key covers several identities.",
|
|
51
|
+
"isRequired": false,
|
|
52
|
+
"isSecret": false,
|
|
53
|
+
"format": "string"
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|