@live-context/mcp 0.3.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 ADDED
@@ -0,0 +1,47 @@
1
+ # @live-context/mcp
2
+
3
+ Stdio MCP proxy for [Live Context](https://live-context.com) — your team's shared AI knowledge layer. This package is a thin client that forwards MCP tool calls to the hosted API at `live-context.com/api/mcp/*`. It does not connect to any database directly. The only thing your machine needs is an MCP API key.
4
+
5
+ ## Install
6
+
7
+ The fastest path is the dashboard's one-liner. After signing in at [live-context.com](https://live-context.com), go to **Settings → MCP**, copy your key, and run:
8
+
9
+ ```bash
10
+ claude mcp add live-context -e MCP_API_KEY=<your-key> -- npx -y @live-context/mcp
11
+ ```
12
+
13
+ Restart Claude Code. Live Context is now wired in.
14
+
15
+ ## Manual configuration
16
+
17
+ For Claude Desktop, Cursor, or any other MCP client, add this to your `mcpServers` config:
18
+
19
+ ```json
20
+ {
21
+ "mcpServers": {
22
+ "live-context": {
23
+ "command": "npx",
24
+ "args": ["-y", "@live-context/mcp"],
25
+ "env": {
26
+ "MCP_API_KEY": "<your-key>"
27
+ }
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Environment variables
34
+
35
+ | Variable | Required | Purpose |
36
+ | --- | --- | --- |
37
+ | `MCP_API_KEY` | yes | Your team's `lc_team_…` key from Settings → MCP. |
38
+ | `LC_USER_TOKEN` | no | Personal token for notebook-scoped reads/writes. |
39
+ | `LC_API_BASE_URL` | no | Override the API base. Defaults to `https://live-context.com`. |
40
+
41
+ ## Tools
42
+
43
+ `get_context`, `list_spaces`, `list_collections`, `list_notebooks`, `list_context`, `write_context`, `health_check`. See the [Live Context docs](https://live-context.com/docs) for usage.
44
+
45
+ ## Get a key
46
+
47
+ [live-context.com/dashboard/settings/mcp](https://live-context.com/dashboard/settings/mcp)
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Live Context MCP — stdio proxy to the hosted HTTP API.
4
+ *
5
+ * Phase 5 Wave 5 design pivot: the MCP server no longer talks to Supabase
6
+ * directly. It's a thin stdio-to-HTTP translator. All data work happens at
7
+ * https://live-context.com/api/mcp/* server-side. Customer-side: only one
8
+ * required env var (MCP_API_KEY); no Supabase keys ever cross the wire.
9
+ *
10
+ * Required environment variables:
11
+ * MCP_API_KEY — The team's mcp_api_key (lc_team_*) value
12
+ *
13
+ * Optional:
14
+ * LC_USER_TOKEN — Personal API token from /dashboard/settings/mcp
15
+ * (required for notebook-scoped operations)
16
+ * LC_API_BASE_URL — Override the API base URL (default: production).
17
+ * Useful for staging / local development.
18
+ *
19
+ * Transport: StdioServerTransport (Claude Code, Cursor, Claude Desktop).
20
+ * Usage: `npx -y @live-context/mcp` (after publishing).
21
+ */
22
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Live Context MCP — stdio proxy to the hosted HTTP API.
4
+ *
5
+ * Phase 5 Wave 5 design pivot: the MCP server no longer talks to Supabase
6
+ * directly. It's a thin stdio-to-HTTP translator. All data work happens at
7
+ * https://live-context.com/api/mcp/* server-side. Customer-side: only one
8
+ * required env var (MCP_API_KEY); no Supabase keys ever cross the wire.
9
+ *
10
+ * Required environment variables:
11
+ * MCP_API_KEY — The team's mcp_api_key (lc_team_*) value
12
+ *
13
+ * Optional:
14
+ * LC_USER_TOKEN — Personal API token from /dashboard/settings/mcp
15
+ * (required for notebook-scoped operations)
16
+ * LC_API_BASE_URL — Override the API base URL (default: production).
17
+ * Useful for staging / local development.
18
+ *
19
+ * Transport: StdioServerTransport (Claude Code, Cursor, Claude Desktop).
20
+ * Usage: `npx -y @live-context/mcp` (after publishing).
21
+ */
22
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
24
+ import { z } from 'zod';
25
+ // ---------------------------------------------------------------------------
26
+ // Config
27
+ // ---------------------------------------------------------------------------
28
+ const MCP_API_KEY = process.env.MCP_API_KEY ?? '';
29
+ const LC_USER_TOKEN = process.env.LC_USER_TOKEN ?? null;
30
+ const API_BASE_URL = process.env.LC_API_BASE_URL?.replace(/\/$/, '') ?? 'https://live-context.com';
31
+ if (!MCP_API_KEY) {
32
+ console.error('[mcp] MCP_API_KEY is not set — tools will fail with auth errors');
33
+ }
34
+ async function callMcpApi(tool, args, method = 'POST') {
35
+ const url = `${API_BASE_URL}/api/mcp/${tool}`;
36
+ const headers = {
37
+ Authorization: `Bearer ${MCP_API_KEY}`,
38
+ 'Content-Type': 'application/json',
39
+ };
40
+ if (LC_USER_TOKEN) {
41
+ headers['X-LC-User-Token'] = LC_USER_TOKEN;
42
+ }
43
+ try {
44
+ const response = await fetch(url, {
45
+ method,
46
+ headers,
47
+ body: method === 'POST' ? JSON.stringify({ args }) : undefined,
48
+ });
49
+ if (!response.ok) {
50
+ const errBody = (await response.json().catch(() => ({})));
51
+ return {
52
+ content: [
53
+ {
54
+ type: 'text',
55
+ text: JSON.stringify({
56
+ error: errBody.error ?? 'http_error',
57
+ message: errBody.message ?? `HTTP ${response.status}`,
58
+ status: response.status,
59
+ }),
60
+ },
61
+ ],
62
+ };
63
+ }
64
+ const json = (await response.json());
65
+ // health_check returns a plain object; tools return McpToolResult shape.
66
+ if ('content' in json && Array.isArray(json.content)) {
67
+ return json;
68
+ }
69
+ return {
70
+ content: [{ type: 'text', text: JSON.stringify(json) }],
71
+ };
72
+ }
73
+ catch (err) {
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text',
78
+ text: JSON.stringify({
79
+ error: 'network_error',
80
+ message: err.message,
81
+ }),
82
+ },
83
+ ],
84
+ };
85
+ }
86
+ }
87
+ // ---------------------------------------------------------------------------
88
+ // Server definition — schemas mirror the v1 tool surface; handlers are HTTP
89
+ // proxies. zod schemas stay client-side because the MCP protocol expects them
90
+ // in the tool registration; the hosted API also re-validates server-side.
91
+ // ---------------------------------------------------------------------------
92
+ const server = new McpServer({
93
+ name: 'live-context-mcp',
94
+ version: '0.3.0', // bumped for Wave 5 hosted-proxy rewrite
95
+ });
96
+ server.registerTool('get_context', {
97
+ description: 'Retrieve relevant context entries. Returns L0 summaries (title, type, snippet) by default. Pass drawer_id to get full page body.',
98
+ inputSchema: {
99
+ query: z.string().optional().describe('Search query. Omit for L0 init summary.'),
100
+ limit: z.number().int().positive().max(10).optional().describe('Max results (default 10, max 10)'),
101
+ space: z.string().optional().describe('Filter by Space name'),
102
+ notebook: z.string().optional().describe('Filter by Notebook name (requires user token)'),
103
+ scope: z
104
+ .enum(['team', 'personal', 'all'])
105
+ .optional()
106
+ .describe('Search scope: team, personal, or all (default)'),
107
+ drawer_id: z
108
+ .string()
109
+ .optional()
110
+ .describe('Page ID to retrieve full body (drill-down from L0 result)'),
111
+ type: z
112
+ .string()
113
+ .optional()
114
+ .describe('Filter by knowledge type (decision, constraint, pattern, entity, open_question, event, preference)'),
115
+ },
116
+ }, (args) => callMcpApi('get-context', args));
117
+ server.registerTool('list_spaces', {
118
+ description: 'List all knowledge Spaces available to the team',
119
+ inputSchema: {
120
+ include_page_counts: z.boolean().optional().describe('Include published page counts per Space'),
121
+ include_shared: z.boolean().optional().describe('Include Spaces shared with this team (read-only)'),
122
+ },
123
+ }, (args) => callMcpApi('list-spaces', args));
124
+ server.registerTool('list_collections', {
125
+ description: 'List Collections within a Space or Notebook',
126
+ inputSchema: {
127
+ space_id: z.string().optional().describe('Space ID to list collections for'),
128
+ notebook_id: z.string().optional().describe('Notebook ID to list collections for'),
129
+ include_page_counts: z.boolean().optional().describe('Include published page counts per Collection'),
130
+ },
131
+ }, (args) => callMcpApi('list-collections', args));
132
+ server.registerTool('list_notebooks', {
133
+ description: 'List your personal Notebooks (requires user token)',
134
+ inputSchema: {},
135
+ }, () => callMcpApi('list-notebooks', {}));
136
+ server.registerTool('list_context', {
137
+ description: 'Paginated listing of all context entries for the team',
138
+ inputSchema: {
139
+ page: z.number().int().positive().optional().describe('Page number (1-indexed)'),
140
+ per_page: z.number().int().positive().optional().describe('Results per page'),
141
+ type: z.string().optional().describe('Filter by knowledge type'),
142
+ space: z.string().optional().describe('Filter by Space name'),
143
+ notebook: z.string().optional().describe('Filter by Notebook name'),
144
+ },
145
+ }, (args) => callMcpApi('list-context', args));
146
+ server.registerTool('write_context', {
147
+ description: `Save team or personal knowledge to Live Context. Routes to a Space (team) or Notebook (personal) based on scope.
148
+
149
+ IMPORTANT — Structure the content field using clear type signals so it classifies accurately:
150
+ - Decisions: Include "we chose/decided/evaluated" + alternatives considered + reasoning.
151
+ - Constraints: Use mandate language — "must", "must not", "never", "always", "required".
152
+ - Patterns: Describe reusable steps — "The pattern for X is: (1)... (2)... (3)...".
153
+ - Entities: Define what something IS — "X is...", "X refers to...", "There are N types of X".
154
+ - Events: Lead with a specific date — "On YYYY-MM-DD, [what happened]".
155
+ - Open questions: Frame as an explicit unresolved question — "Should we...?", "Unresolved: ...".
156
+ - Preferences: First-person only — "I prefer", "I use", "my approach".
157
+
158
+ DO NOT write content that restates code, git history, or existing drawers. Call list_context or get_context first if unsure — update beats duplicate.
159
+
160
+ When migrating from an existing doc, set the source field to the relative file path so provenance is preserved.
161
+
162
+ If you know the knowledge type, pass it in type_hint for higher classification accuracy.`,
163
+ inputSchema: {
164
+ content: z.string().describe('The knowledge content with clear type signals.'),
165
+ type_hint: z
166
+ .enum(['decision', 'constraint', 'pattern', 'entity', 'open_question', 'event', 'preference'])
167
+ .optional()
168
+ .describe('Optional knowledge type hint. Provide when obvious.'),
169
+ scope: z.enum(['team', 'personal']).optional().describe('Routing scope: team (default) or personal (requires user token)'),
170
+ space: z.string().optional().describe('Target Space name (team scope)'),
171
+ notebook: z.string().optional().describe('Target Notebook name (personal scope)'),
172
+ source: z.string().optional().describe('Origin source identifier. For doc migrations, the relative file path.'),
173
+ metadata: z.record(z.string(), z.unknown()).optional().describe('Arbitrary metadata key/value pairs'),
174
+ },
175
+ }, (args) => callMcpApi('write-context', args));
176
+ server.registerTool('health_check', {
177
+ description: 'Returns server health status and resolved team identity',
178
+ }, () => callMcpApi('health-check', undefined, 'GET'));
179
+ // ---------------------------------------------------------------------------
180
+ // Process-level error handlers
181
+ // ---------------------------------------------------------------------------
182
+ process.on('unhandledRejection', (reason) => {
183
+ console.error('[mcp] UNHANDLED REJECTION:', reason);
184
+ process.exit(1);
185
+ });
186
+ process.on('uncaughtException', (err) => {
187
+ console.error('[mcp] UNCAUGHT EXCEPTION:', err);
188
+ process.exit(1);
189
+ });
190
+ // ---------------------------------------------------------------------------
191
+ // Start
192
+ // ---------------------------------------------------------------------------
193
+ async function start() {
194
+ const transport = new StdioServerTransport();
195
+ await server.connect(transport);
196
+ }
197
+ start();
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@live-context/mcp",
3
+ "version": "0.3.0",
4
+ "private": false,
5
+ "description": "Live Context MCP — stdio proxy to live-context.com hosted API. Customer-side has no Supabase dependency.",
6
+ "type": "module",
7
+ "bin": {
8
+ "live-context-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "dev": "tsx index.ts",
16
+ "start": "node dist/index.js",
17
+ "build": "tsc"
18
+ },
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.15.0",
21
+ "zod": "^3.23.8"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^25.5.2",
25
+ "tsx": "^4.19.0",
26
+ "typescript": "^6.0.2"
27
+ },
28
+ "engines": {
29
+ "node": ">=18.0.0"
30
+ }
31
+ }