@viewert/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 +95 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +134 -0
- package/package.json +31 -0
- package/src/index.ts +203 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# @viewert/mcp
|
|
2
|
+
|
|
3
|
+
MCP server for [Viewert](https://viewert.com) — expose your Librams (AI context collections) to any MCP-compatible AI client.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
Connects Claude Desktop, Cursor, Windsurf, or any MCP client to your Viewert Librams. Each Libram is a curated collection of Vellums (notes/documents) that you've marked as available for AI context.
|
|
8
|
+
|
|
9
|
+
## Setup
|
|
10
|
+
|
|
11
|
+
### 1. Get an API key
|
|
12
|
+
|
|
13
|
+
Go to **Settings → API Keys** on Viewert and create a key. Copy it — it's shown only once.
|
|
14
|
+
|
|
15
|
+
### 2. Add to your MCP client config
|
|
16
|
+
|
|
17
|
+
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"viewert": {
|
|
23
|
+
"command": "npx",
|
|
24
|
+
"args": ["-y", "@viewert/mcp"],
|
|
25
|
+
"env": {
|
|
26
|
+
"VIEWERT_API_KEY": "vwt_your_key_here"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Cursor / Windsurf** (`.cursor/mcp.json` or `.windsurf/mcp.json` in your project, or global config):
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"viewert": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["-y", "@viewert/mcp"],
|
|
41
|
+
"env": {
|
|
42
|
+
"VIEWERT_API_KEY": "vwt_your_key_here"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. Restart your AI client
|
|
50
|
+
|
|
51
|
+
The Viewert tools will appear automatically.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Tools
|
|
56
|
+
|
|
57
|
+
| Tool | Description |
|
|
58
|
+
|------|-------------|
|
|
59
|
+
| `list_librams` | List all Librams in your account with names and IDs |
|
|
60
|
+
| `get_libram_context` | Fetch all AI-enabled Vellums from a Libram as **markdown** |
|
|
61
|
+
| `get_libram_context_json` | Fetch AI-enabled Vellums as **structured JSON** (id, title, content) |
|
|
62
|
+
|
|
63
|
+
## Resources
|
|
64
|
+
|
|
65
|
+
| URI | Description |
|
|
66
|
+
|-----|-------------|
|
|
67
|
+
| `librams://list` | JSON list of all your Librams |
|
|
68
|
+
| `librams://{id}/context` | Markdown context for a specific Libram |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Environment variables
|
|
73
|
+
|
|
74
|
+
| Variable | Required | Default |
|
|
75
|
+
|----------|----------|---------|
|
|
76
|
+
| `VIEWERT_API_KEY` | ✅ Yes | — |
|
|
77
|
+
| `VIEWERT_API_URL` | No | `https://viewert.com/api` |
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Example usage in Claude
|
|
82
|
+
|
|
83
|
+
> "Load my **Project Notes** Libram and summarise the key points."
|
|
84
|
+
|
|
85
|
+
Claude will call `list_librams` to find the ID, then `get_libram_context` to pull the content — all inline in the conversation.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Local development
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
cd mcp
|
|
93
|
+
pnpm install
|
|
94
|
+
VIEWERT_API_KEY=vwt_... pnpm dev
|
|
95
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Viewert MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Viewert Librams as context resources and tools to any MCP-compatible
|
|
6
|
+
* AI client (Claude Desktop, Cursor, Windsurf, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Required env vars:
|
|
9
|
+
* VIEWERT_API_KEY — your vwt_... API key from viewert.com/settings
|
|
10
|
+
*
|
|
11
|
+
* Optional env vars:
|
|
12
|
+
* VIEWERT_API_URL — defaults to https://viewert.com/api
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Viewert MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Viewert Librams as context resources and tools to any MCP-compatible
|
|
6
|
+
* AI client (Claude Desktop, Cursor, Windsurf, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Required env vars:
|
|
9
|
+
* VIEWERT_API_KEY — your vwt_... API key from viewert.com/settings
|
|
10
|
+
*
|
|
11
|
+
* Optional env vars:
|
|
12
|
+
* VIEWERT_API_URL — defaults to https://viewert.com/api
|
|
13
|
+
*/
|
|
14
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
15
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
const API_KEY = process.env.VIEWERT_API_KEY;
|
|
18
|
+
const API_BASE = (process.env.VIEWERT_API_URL ?? 'https://viewert.com/api').replace(/\/$/, '');
|
|
19
|
+
if (!API_KEY) {
|
|
20
|
+
process.stderr.write('[viewert-mcp] ERROR: VIEWERT_API_KEY is not set.\n' +
|
|
21
|
+
'Generate a key at https://viewert.com/settings and add it to your MCP config.\n');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
|
25
|
+
async function apiFetch(path, opts = {}) {
|
|
26
|
+
return fetch(`${API_BASE}${path}`, {
|
|
27
|
+
...opts,
|
|
28
|
+
headers: {
|
|
29
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
...(opts.headers ?? {}),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async function apiJSON(path) {
|
|
36
|
+
const res = await apiFetch(path);
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
const body = await res.text().catch(() => '');
|
|
39
|
+
throw new Error(`Viewert API ${res.status}: ${body}`);
|
|
40
|
+
}
|
|
41
|
+
return res.json();
|
|
42
|
+
}
|
|
43
|
+
async function apiText(path) {
|
|
44
|
+
const res = await apiFetch(path);
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const body = await res.text().catch(() => '');
|
|
47
|
+
throw new Error(`Viewert API ${res.status}: ${body}`);
|
|
48
|
+
}
|
|
49
|
+
return res.text();
|
|
50
|
+
}
|
|
51
|
+
// ── Server ────────────────────────────────────────────────────────────────────
|
|
52
|
+
const server = new McpServer({
|
|
53
|
+
name: 'viewert',
|
|
54
|
+
version: '0.1.0',
|
|
55
|
+
});
|
|
56
|
+
// ── Tool: list_librams ────────────────────────────────────────────────────────
|
|
57
|
+
server.tool('list_librams', 'List all Librams (AI context collections) in your Viewert account.', {}, async () => {
|
|
58
|
+
const data = await apiJSON('/librams/mine');
|
|
59
|
+
const librams = data.librams ?? [];
|
|
60
|
+
if (librams.length === 0) {
|
|
61
|
+
return {
|
|
62
|
+
content: [{ type: 'text', text: 'You have no Librams yet. Create one at https://viewert.com/librams' }],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const lines = librams.map((l) => `• ${l.emoji || '📁'} **${l.name}** (id: \`${l.id}\`)` +
|
|
66
|
+
` — ${l.ai_count} AI vellums` +
|
|
67
|
+
(l.description ? ` — ${l.description}` : ''));
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: `**Your Viewert Librams (${librams.length})**\n\n${lines.join('\n')}\n\nUse \`get_libram_context\` with a libram id to load its content.`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
// ── Tool: get_libram_context ──────────────────────────────────────────────────
|
|
78
|
+
server.tool('get_libram_context', 'Fetch the full AI-enabled Vellum contents of a Viewert Libram as markdown. ' +
|
|
79
|
+
'Use this to inject knowledge from your Libram into the current conversation.', {
|
|
80
|
+
libram_id: z.string().describe('The ID of the Libram to fetch. Use list_librams to find IDs.'),
|
|
81
|
+
}, async ({ libram_id }) => {
|
|
82
|
+
const markdown = await apiText(`/librams/${libram_id}/context?format=markdown`);
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: 'text', text: markdown }],
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
// ── Tool: get_libram_context_json ─────────────────────────────────────────────
|
|
88
|
+
server.tool('get_libram_context_json', 'Fetch AI-enabled Vellums from a Libram as structured JSON (id, title, content per vellum). ' +
|
|
89
|
+
'Use this when you need to reference individual vellums by title or process them separately.', {
|
|
90
|
+
libram_id: z.string().describe('The ID of the Libram to fetch.'),
|
|
91
|
+
}, async ({ libram_id }) => {
|
|
92
|
+
const data = await apiJSON(`/librams/${libram_id}/context?format=json`);
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: 'text',
|
|
97
|
+
text: JSON.stringify(data, null, 2),
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
// ── Resource: librams://list ──────────────────────────────────────────────────
|
|
103
|
+
server.resource('librams', 'librams://list', { mimeType: 'application/json', description: 'All Librams in your Viewert account' }, async () => {
|
|
104
|
+
const data = await apiJSON('/librams/mine');
|
|
105
|
+
return {
|
|
106
|
+
contents: [
|
|
107
|
+
{
|
|
108
|
+
uri: 'librams://list',
|
|
109
|
+
mimeType: 'application/json',
|
|
110
|
+
text: JSON.stringify(data.librams ?? [], null, 2),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
});
|
|
115
|
+
// ── Resource: librams://{id}/context ─────────────────────────────────────────
|
|
116
|
+
server.resource('libram-context', new ResourceTemplate('librams://{id}/context', { list: undefined }), { mimeType: 'text/markdown', description: 'AI-enabled Vellum contents of a Libram as markdown' }, async (uri, variables) => {
|
|
117
|
+
const libramId = variables['id'];
|
|
118
|
+
if (!libramId)
|
|
119
|
+
throw new Error(`Missing libram id in URI: ${uri.href}`);
|
|
120
|
+
const markdown = await apiText(`/librams/${libramId}/context?format=markdown`);
|
|
121
|
+
return {
|
|
122
|
+
contents: [
|
|
123
|
+
{
|
|
124
|
+
uri: uri.href,
|
|
125
|
+
mimeType: 'text/markdown',
|
|
126
|
+
text: markdown,
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
132
|
+
const transport = new StdioServerTransport();
|
|
133
|
+
await server.connect(transport);
|
|
134
|
+
process.stderr.write('[viewert-mcp] Server started. Waiting for MCP client...\n');
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@viewert/mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Viewert Librams — expose AI-enabled Vellums as context to any MCP-compatible AI client",
|
|
5
|
+
"keywords": ["mcp", "viewert", "libram", "ai-context", "model-context-protocol"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"bin": {
|
|
10
|
+
"viewert-mcp": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"packageManager": "pnpm@9.0.0",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"start": "node dist/index.js",
|
|
17
|
+
"prepublishOnly": "pnpm run build"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
21
|
+
"zod": "^3.24.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/node": "^22.0.0",
|
|
25
|
+
"tsx": "^4.19.0",
|
|
26
|
+
"typescript": "^5.7.0"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Viewert MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Viewert Librams as context resources and tools to any MCP-compatible
|
|
6
|
+
* AI client (Claude Desktop, Cursor, Windsurf, etc.).
|
|
7
|
+
*
|
|
8
|
+
* Required env vars:
|
|
9
|
+
* VIEWERT_API_KEY — your vwt_... API key from viewert.com/settings
|
|
10
|
+
*
|
|
11
|
+
* Optional env vars:
|
|
12
|
+
* VIEWERT_API_URL — defaults to https://viewert.com/api
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
16
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
17
|
+
import { z } from 'zod'
|
|
18
|
+
|
|
19
|
+
const API_KEY = process.env.VIEWERT_API_KEY
|
|
20
|
+
const API_BASE = (process.env.VIEWERT_API_URL ?? 'https://viewert.com/api').replace(/\/$/, '')
|
|
21
|
+
|
|
22
|
+
if (!API_KEY) {
|
|
23
|
+
process.stderr.write(
|
|
24
|
+
'[viewert-mcp] ERROR: VIEWERT_API_KEY is not set.\n' +
|
|
25
|
+
'Generate a key at https://viewert.com/settings and add it to your MCP config.\n'
|
|
26
|
+
)
|
|
27
|
+
process.exit(1)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── HTTP helpers ──────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
async function apiFetch(path: string, opts: RequestInit = {}): Promise<Response> {
|
|
33
|
+
return fetch(`${API_BASE}${path}`, {
|
|
34
|
+
...opts,
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
...(opts.headers as Record<string, string> ?? {}),
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function apiJSON<T>(path: string): Promise<T> {
|
|
44
|
+
const res = await apiFetch(path)
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const body = await res.text().catch(() => '')
|
|
47
|
+
throw new Error(`Viewert API ${res.status}: ${body}`)
|
|
48
|
+
}
|
|
49
|
+
return res.json() as Promise<T>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function apiText(path: string): Promise<string> {
|
|
53
|
+
const res = await apiFetch(path)
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const body = await res.text().catch(() => '')
|
|
56
|
+
throw new Error(`Viewert API ${res.status}: ${body}`)
|
|
57
|
+
}
|
|
58
|
+
return res.text()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
interface Libram {
|
|
64
|
+
id: string
|
|
65
|
+
name: string
|
|
66
|
+
description: string
|
|
67
|
+
emoji: string
|
|
68
|
+
is_public: boolean
|
|
69
|
+
vellum_count: number
|
|
70
|
+
ai_count: number
|
|
71
|
+
updated_at: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Server ────────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
const server = new McpServer({
|
|
77
|
+
name: 'viewert',
|
|
78
|
+
version: '0.1.0',
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// ── Tool: list_librams ────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
server.tool(
|
|
84
|
+
'list_librams',
|
|
85
|
+
'List all Librams (AI context collections) in your Viewert account.',
|
|
86
|
+
{},
|
|
87
|
+
async () => {
|
|
88
|
+
const data = await apiJSON<{ librams: Libram[] }>('/librams/mine')
|
|
89
|
+
const librams = data.librams ?? []
|
|
90
|
+
|
|
91
|
+
if (librams.length === 0) {
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: 'text', text: 'You have no Librams yet. Create one at https://viewert.com/librams' }],
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const lines = librams.map((l) =>
|
|
98
|
+
`• ${l.emoji || '📁'} **${l.name}** (id: \`${l.id}\`)` +
|
|
99
|
+
` — ${l.ai_count} AI vellums` +
|
|
100
|
+
(l.description ? ` — ${l.description}` : '')
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
content: [
|
|
105
|
+
{
|
|
106
|
+
type: 'text',
|
|
107
|
+
text: `**Your Viewert Librams (${librams.length})**\n\n${lines.join('\n')}\n\nUse \`get_libram_context\` with a libram id to load its content.`,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
// ── Tool: get_libram_context ──────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
server.tool(
|
|
117
|
+
'get_libram_context',
|
|
118
|
+
'Fetch the full AI-enabled Vellum contents of a Viewert Libram as markdown. ' +
|
|
119
|
+
'Use this to inject knowledge from your Libram into the current conversation.',
|
|
120
|
+
{
|
|
121
|
+
libram_id: z.string().describe('The ID of the Libram to fetch. Use list_librams to find IDs.'),
|
|
122
|
+
},
|
|
123
|
+
async ({ libram_id }) => {
|
|
124
|
+
const markdown = await apiText(`/librams/${libram_id}/context?format=markdown`)
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: 'text', text: markdown }],
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
// ── Tool: get_libram_context_json ─────────────────────────────────────────────
|
|
132
|
+
|
|
133
|
+
server.tool(
|
|
134
|
+
'get_libram_context_json',
|
|
135
|
+
'Fetch AI-enabled Vellums from a Libram as structured JSON (id, title, content per vellum). ' +
|
|
136
|
+
'Use this when you need to reference individual vellums by title or process them separately.',
|
|
137
|
+
{
|
|
138
|
+
libram_id: z.string().describe('The ID of the Libram to fetch.'),
|
|
139
|
+
},
|
|
140
|
+
async ({ libram_id }) => {
|
|
141
|
+
const data = await apiJSON<{
|
|
142
|
+
libram_name: string
|
|
143
|
+
vellums: Array<{ id: string; title: string; content: string }>
|
|
144
|
+
}>(`/librams/${libram_id}/context?format=json`)
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
content: [
|
|
148
|
+
{
|
|
149
|
+
type: 'text',
|
|
150
|
+
text: JSON.stringify(data, null, 2),
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
// ── Resource: librams://list ──────────────────────────────────────────────────
|
|
158
|
+
|
|
159
|
+
server.resource(
|
|
160
|
+
'librams',
|
|
161
|
+
'librams://list',
|
|
162
|
+
{ mimeType: 'application/json', description: 'All Librams in your Viewert account' },
|
|
163
|
+
async () => {
|
|
164
|
+
const data = await apiJSON<{ librams: Libram[] }>('/librams/mine')
|
|
165
|
+
return {
|
|
166
|
+
contents: [
|
|
167
|
+
{
|
|
168
|
+
uri: 'librams://list',
|
|
169
|
+
mimeType: 'application/json',
|
|
170
|
+
text: JSON.stringify(data.librams ?? [], null, 2),
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// ── Resource: librams://{id}/context ─────────────────────────────────────────
|
|
178
|
+
|
|
179
|
+
server.resource(
|
|
180
|
+
'libram-context',
|
|
181
|
+
new ResourceTemplate('librams://{id}/context', { list: undefined }),
|
|
182
|
+
{ mimeType: 'text/markdown', description: 'AI-enabled Vellum contents of a Libram as markdown' },
|
|
183
|
+
async (uri: URL, variables: Record<string, string | string[]>) => {
|
|
184
|
+
const libramId = variables['id'] as string
|
|
185
|
+
if (!libramId) throw new Error(`Missing libram id in URI: ${uri.href}`)
|
|
186
|
+
const markdown = await apiText(`/librams/${libramId}/context?format=markdown`)
|
|
187
|
+
return {
|
|
188
|
+
contents: [
|
|
189
|
+
{
|
|
190
|
+
uri: uri.href,
|
|
191
|
+
mimeType: 'text/markdown',
|
|
192
|
+
text: markdown,
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
200
|
+
|
|
201
|
+
const transport = new StdioServerTransport()
|
|
202
|
+
await server.connect(transport)
|
|
203
|
+
process.stderr.write('[viewert-mcp] Server started. Waiting for MCP client...\n')
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src"]
|
|
14
|
+
}
|