opencontext-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/LICENSE +21 -0
- package/README.md +209 -0
- package/dist/context-store.d.ts +9 -0
- package/dist/context-store.js +59 -0
- package/dist/context-store.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +51 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +1 -0
- package/dist/validation.js +9 -0
- package/dist/validation.js.map +1 -0
- package/examples/build-agent.md +48 -0
- package/examples/plan-agent.md +53 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenContext Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# OpenContext MCP
|
|
2
|
+
|
|
3
|
+
[](https://modelcontextprotocol.io/)
|
|
4
|
+
[](https://www.typescriptlang.org/)
|
|
5
|
+
[](https://nodejs.org/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
OpenContext MCP gives AI agents persistent, project-local memory.
|
|
9
|
+
|
|
10
|
+
Most coding agents lose important decisions between sessions: architecture rules, naming conventions, API contracts, migration notes, and hard-won debugging context. OpenContext MCP solves that by exposing a tiny Model Context Protocol server that lets agents save and read markdown files in a local `.opencontext/` directory inside each project.
|
|
11
|
+
|
|
12
|
+
The result is simple and transparent: your agent gets memory, and you keep full control because the memory is plain markdown committed or ignored however you choose.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- `save_context`: save markdown context to `.opencontext/<topic>.md`
|
|
17
|
+
- `read_context`: read one saved topic or list all available topics
|
|
18
|
+
- Project-specific storage based on the client's current working directory
|
|
19
|
+
- No database, account, cloud sync, or hidden state
|
|
20
|
+
- Works with MCP-compatible clients over stdio
|
|
21
|
+
- Built with strict TypeScript and the official `@modelcontextprotocol/sdk`
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
You normally do not need to install OpenContext MCP manually. Configure your MCP client to run it with `npx`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx -y opencontext-mcp
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For local development in this repository:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm install
|
|
35
|
+
pnpm build
|
|
36
|
+
pnpm start
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## MCP Client Setup
|
|
40
|
+
|
|
41
|
+
### OpenCode
|
|
42
|
+
|
|
43
|
+
Add OpenContext MCP to your OpenCode MCP configuration:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcp": {
|
|
48
|
+
"opencontext": {
|
|
49
|
+
"type": "local",
|
|
50
|
+
"command": ["npx", "-y", "opencontext-mcp"],
|
|
51
|
+
"enabled": true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Cursor
|
|
58
|
+
|
|
59
|
+
Add this server to your Cursor MCP configuration:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"opencontext": {
|
|
65
|
+
"command": "npx",
|
|
66
|
+
"args": ["-y", "opencontext-mcp"]
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Claude Desktop
|
|
73
|
+
|
|
74
|
+
Add this entry to your Claude Desktop MCP configuration:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"opencontext": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["-y", "opencontext-mcp"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Restart your MCP client after updating configuration.
|
|
88
|
+
|
|
89
|
+
## How It Works
|
|
90
|
+
|
|
91
|
+
OpenContext MCP stores files relative to the process working directory used by your MCP client:
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
your-project/
|
|
95
|
+
.opencontext/
|
|
96
|
+
architecture.md
|
|
97
|
+
api-contracts.md
|
|
98
|
+
coding_rules.md
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Topics must be lowercase snake_case or kebab-case, such as `architecture`, `api-contracts`, or `coding_rules`.
|
|
102
|
+
|
|
103
|
+
## Available Tools
|
|
104
|
+
|
|
105
|
+
### `save_context`
|
|
106
|
+
|
|
107
|
+
Saves markdown content to `.opencontext/<topic>.md`. Existing files are overwritten.
|
|
108
|
+
|
|
109
|
+
Arguments:
|
|
110
|
+
|
|
111
|
+
| Name | Type | Required | Description |
|
|
112
|
+
| --------- | ------ | -------- | --------------------------------------------- |
|
|
113
|
+
| `topic` | string | yes | Lowercase snake_case or kebab-case topic name |
|
|
114
|
+
| `content` | string | yes | Markdown content to save |
|
|
115
|
+
|
|
116
|
+
Example use:
|
|
117
|
+
|
|
118
|
+
```text
|
|
119
|
+
Save our API conventions under topic api-contracts.
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `read_context`
|
|
123
|
+
|
|
124
|
+
Reads one saved topic, or lists all topics when no topic is provided.
|
|
125
|
+
|
|
126
|
+
Arguments:
|
|
127
|
+
|
|
128
|
+
| Name | Type | Required | Description |
|
|
129
|
+
| ------- | ------ | -------- | ------------- |
|
|
130
|
+
| `topic` | string | no | Topic to read |
|
|
131
|
+
|
|
132
|
+
Example use:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
Read the architecture context before changing the routing layer.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## How To Instruct Agents
|
|
139
|
+
|
|
140
|
+
MCP tools are most useful when your agent is explicitly told when to use them. Add instructions like these to your system prompt or project rules:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
Always use read_context before making code changes. First list available topics, then read any topic relevant to the task.
|
|
144
|
+
|
|
145
|
+
Use save_context whenever you learn a durable project rule, architectural decision, convention, API contract, or debugging note that future agents should know.
|
|
146
|
+
|
|
147
|
+
Prefer small, focused context topics in snake_case or kebab-case. Keep the content concise, factual, and written in markdown.
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Recommended workflow:
|
|
151
|
+
|
|
152
|
+
1. Ask an architect or planning agent to analyze the project and save durable decisions with `save_context`.
|
|
153
|
+
2. Ask build agents to call `read_context` before coding.
|
|
154
|
+
3. Let agents update context when they discover something that should survive the current chat.
|
|
155
|
+
4. Review `.opencontext/` files like normal project documentation.
|
|
156
|
+
|
|
157
|
+
Copy-ready agent prompts:
|
|
158
|
+
|
|
159
|
+
- [`examples/plan-agent.md`](examples/plan-agent.md) -- architect that analyzes requirements and saves context
|
|
160
|
+
- [`examples/build-agent.md`](examples/build-agent.md) -- developer that reads context before coding
|
|
161
|
+
|
|
162
|
+
## Version Control
|
|
163
|
+
|
|
164
|
+
You can commit `.opencontext/` when it contains team-wide knowledge:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git add .opencontext
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Or ignore it when context should stay local:
|
|
171
|
+
|
|
172
|
+
```gitignore
|
|
173
|
+
.opencontext/
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Because files are plain markdown, both approaches are safe and easy to audit.
|
|
177
|
+
|
|
178
|
+
## Development
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
pnpm install
|
|
182
|
+
pnpm build # compile TypeScript
|
|
183
|
+
pnpm typecheck # type-check without emitting
|
|
184
|
+
pnpm test # run vitest suite
|
|
185
|
+
pnpm start # start the MCP server
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Project structure:
|
|
189
|
+
|
|
190
|
+
```text
|
|
191
|
+
src/
|
|
192
|
+
index.ts CLI entrypoint (stdio transport, startup error handling)
|
|
193
|
+
server.ts McpServer factory and tool registration
|
|
194
|
+
context-store.ts Filesystem operations for .opencontext/
|
|
195
|
+
validation.ts Topic validation and input rules
|
|
196
|
+
types.ts Shared constants, error classes, and result helpers
|
|
197
|
+
test/
|
|
198
|
+
validation.test.ts Unit tests for topic validation
|
|
199
|
+
context-store.test.ts Unit tests for read/write/list operations
|
|
200
|
+
examples/
|
|
201
|
+
plan-agent.md Architect system prompt
|
|
202
|
+
build-agent.md Developer system prompt
|
|
203
|
+
README.md User documentation
|
|
204
|
+
LICENSE MIT license
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare class ContextStore {
|
|
2
|
+
private readonly basePath;
|
|
3
|
+
constructor(basePath?: string);
|
|
4
|
+
getContextDirectory(): string;
|
|
5
|
+
getTopicFilePath(topic: string): string;
|
|
6
|
+
saveContext(topicInput: string, content: string): Promise<string>;
|
|
7
|
+
readContext(topicInput?: string): Promise<string>;
|
|
8
|
+
listTopics(): Promise<string[]>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CONTEXT_DIRECTORY_NAME, UserInputError, isNodeError } from "./types.js";
|
|
4
|
+
import { validateTopic } from "./validation.js";
|
|
5
|
+
export class ContextStore {
|
|
6
|
+
basePath;
|
|
7
|
+
constructor(basePath = process.cwd()) {
|
|
8
|
+
this.basePath = basePath;
|
|
9
|
+
}
|
|
10
|
+
getContextDirectory() {
|
|
11
|
+
return path.join(this.basePath, CONTEXT_DIRECTORY_NAME);
|
|
12
|
+
}
|
|
13
|
+
getTopicFilePath(topic) {
|
|
14
|
+
return path.join(this.getContextDirectory(), `${topic}.md`);
|
|
15
|
+
}
|
|
16
|
+
async saveContext(topicInput, content) {
|
|
17
|
+
const topic = validateTopic(topicInput);
|
|
18
|
+
const contextDirectory = this.getContextDirectory();
|
|
19
|
+
const filePath = this.getTopicFilePath(topic);
|
|
20
|
+
await mkdir(contextDirectory, { recursive: true });
|
|
21
|
+
await writeFile(filePath, content, "utf8");
|
|
22
|
+
return `Saved context topic "${topic}" to ${CONTEXT_DIRECTORY_NAME}/${topic}.md.`;
|
|
23
|
+
}
|
|
24
|
+
async readContext(topicInput) {
|
|
25
|
+
if (topicInput !== undefined) {
|
|
26
|
+
const topic = validateTopic(topicInput);
|
|
27
|
+
try {
|
|
28
|
+
return await readFile(this.getTopicFilePath(topic), "utf8");
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
32
|
+
throw new UserInputError(`No context found for topic "${topic}" at ${CONTEXT_DIRECTORY_NAME}/${topic}.md.`);
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const topics = await this.listTopics();
|
|
38
|
+
if (topics.length === 0) {
|
|
39
|
+
return `No OpenContext topics found in ${CONTEXT_DIRECTORY_NAME}/. Use save_context to create one.`;
|
|
40
|
+
}
|
|
41
|
+
return `Available OpenContext topics:\n\n${topics.map((topic) => `- ${topic}`).join("\n")}`;
|
|
42
|
+
}
|
|
43
|
+
async listTopics() {
|
|
44
|
+
try {
|
|
45
|
+
const entries = await readdir(this.getContextDirectory(), { withFileTypes: true });
|
|
46
|
+
return entries
|
|
47
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
|
|
48
|
+
.map((entry) => entry.name.slice(0, -".md".length))
|
|
49
|
+
.sort((a, b) => a.localeCompare(b));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=context-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-store.js","sourceRoot":"","sources":["../src/context-store.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createOpenContextServer } from "./server.js";
|
|
4
|
+
import { SERVER_NAME, getErrorMessage } from "./types.js";
|
|
5
|
+
async function main() {
|
|
6
|
+
const server = createOpenContextServer();
|
|
7
|
+
const transport = new StdioServerTransport();
|
|
8
|
+
await server.connect(transport);
|
|
9
|
+
}
|
|
10
|
+
main().catch((error) => {
|
|
11
|
+
console.error(`Failed to start ${SERVER_NAME}: ${getErrorMessage(error)}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE1D,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,uBAAuB,EAAE,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,KAAK,CAAC,mBAAmB,WAAW,KAAK,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC3E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { ContextStore } from "./context-store.js";
|
|
4
|
+
import { SERVER_NAME, SERVER_VERSION, getErrorMessage, textResult } from "./types.js";
|
|
5
|
+
export function createOpenContextServer(basePath) {
|
|
6
|
+
const store = new ContextStore(basePath);
|
|
7
|
+
const server = new McpServer({
|
|
8
|
+
name: SERVER_NAME,
|
|
9
|
+
version: SERVER_VERSION,
|
|
10
|
+
});
|
|
11
|
+
server.registerTool("save_context", {
|
|
12
|
+
title: "Save Context",
|
|
13
|
+
description: "Persist markdown project context, architectural rules, or decisions into .opencontext/<topic>.md in the current working directory.",
|
|
14
|
+
inputSchema: {
|
|
15
|
+
topic: z
|
|
16
|
+
.string()
|
|
17
|
+
.min(1)
|
|
18
|
+
.describe("Context topic name in snake_case or kebab-case, for example api_contracts or auth-rules."),
|
|
19
|
+
content: z.string().min(1).describe("Markdown content to save for this project topic."),
|
|
20
|
+
},
|
|
21
|
+
}, async ({ topic, content }) => {
|
|
22
|
+
try {
|
|
23
|
+
const result = await store.saveContext(topic, content);
|
|
24
|
+
return textResult(result);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return textResult(`OpenContext error: ${getErrorMessage(error)}`, true);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
server.registerTool("read_context", {
|
|
31
|
+
title: "Read Context",
|
|
32
|
+
description: "Read a saved OpenContext topic, or list all available topics when no topic is provided.",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
topic: z
|
|
35
|
+
.string()
|
|
36
|
+
.min(1)
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Optional topic name in snake_case or kebab-case. Omit to list all saved topics."),
|
|
39
|
+
},
|
|
40
|
+
}, async ({ topic }) => {
|
|
41
|
+
try {
|
|
42
|
+
const result = await store.readContext(topic);
|
|
43
|
+
return textResult(result);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return textResult(`OpenContext error: ${getErrorMessage(error)}`, true);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
return server;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtF,MAAM,UAAU,uBAAuB,CAAC,QAAiB;IACvD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IAEzC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;KACxB,CAAC,CAAC;IAEH,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,oIAAoI;QACtI,WAAW,EAAE;YACX,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,CACP,0FAA0F,CAC3F;YACH,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,kDAAkD,CAAC;SACxF;KACF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACvD,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,UAAU,CAAC,sBAAsB,eAAe,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,yFAAyF;QAC3F,WAAW,EAAE;YACX,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,GAAG,CAAC,CAAC,CAAC;iBACN,QAAQ,EAAE;iBACV,QAAQ,CACP,iFAAiF,CAClF;SACJ;KACF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAClB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAC9C,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,UAAU,CAAC,sBAAsB,eAAe,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC,CACF,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const SERVER_NAME = "opencontext-mcp";
|
|
2
|
+
export declare const SERVER_VERSION = "0.1.0";
|
|
3
|
+
export declare const CONTEXT_DIRECTORY_NAME = ".opencontext";
|
|
4
|
+
export declare const TOPIC_PATTERN: RegExp;
|
|
5
|
+
export declare class UserInputError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
export declare function textResult(text: string, isError?: boolean): {
|
|
9
|
+
isError?: boolean;
|
|
10
|
+
content: {
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}[];
|
|
14
|
+
};
|
|
15
|
+
export declare function getErrorMessage(error: unknown): string;
|
|
16
|
+
export declare function isNodeError(error: unknown): error is NodeJS.ErrnoException;
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const SERVER_NAME = "opencontext-mcp";
|
|
2
|
+
export const SERVER_VERSION = "0.1.0";
|
|
3
|
+
export const CONTEXT_DIRECTORY_NAME = ".opencontext";
|
|
4
|
+
export const TOPIC_PATTERN = /^[a-z0-9]+(?:[_-][a-z0-9]+)*$/;
|
|
5
|
+
export class UserInputError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "UserInputError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function textResult(text, isError = false) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
...(isError ? { isError: true } : {}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function getErrorMessage(error) {
|
|
23
|
+
if (error instanceof Error) {
|
|
24
|
+
return error.message;
|
|
25
|
+
}
|
|
26
|
+
return "An unknown error occurred.";
|
|
27
|
+
}
|
|
28
|
+
export function isNodeError(error) {
|
|
29
|
+
return error instanceof Error && "code" in error;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AACtC,MAAM,CAAC,MAAM,sBAAsB,GAAG,cAAc,CAAC;AACrD,MAAM,CAAC,MAAM,aAAa,GAAG,+BAA+B,CAAC;AAE7D,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,OAAO,GAAG,KAAK;IACtD,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI;aACL;SACF;QACD,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,OAAO,4BAA4B,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function validateTopic(topicInput: string): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TOPIC_PATTERN, UserInputError } from "./types.js";
|
|
2
|
+
export function validateTopic(topicInput) {
|
|
3
|
+
const topic = topicInput.trim();
|
|
4
|
+
if (!TOPIC_PATTERN.test(topic)) {
|
|
5
|
+
throw new UserInputError("Topic must be snake_case or kebab-case using lowercase letters, numbers, underscores, or hyphens.");
|
|
6
|
+
}
|
|
7
|
+
return topic;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE3D,MAAM,UAAU,aAAa,CAAC,UAAkB;IAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAEhC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,cAAc,CACtB,mGAAmG,CACpG,CAAC;IACJ,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# OpenContext Build Agent
|
|
2
|
+
|
|
3
|
+
Copy this prompt into your AI client's system prompt to turn it into a developer that follows rules saved by the Plan Agent.
|
|
4
|
+
|
|
5
|
+
## System Prompt
|
|
6
|
+
|
|
7
|
+
You are the OpenContext Build Agent: a pragmatic senior developer responsible for implementing changes while strictly following project context saved by the Plan Agent.
|
|
8
|
+
|
|
9
|
+
Before writing or editing any code, you must use the `read_context` tool with no topic to list available OpenContext topics. Then read all topics relevant to the requested work. If relevant context exists, follow it. If the user's request conflicts with saved context, stop and ask for clarification before changing code.
|
|
10
|
+
|
|
11
|
+
Your responsibilities:
|
|
12
|
+
|
|
13
|
+
- Read OpenContext before coding.
|
|
14
|
+
- Inspect the codebase and make the smallest correct change.
|
|
15
|
+
- Preserve existing architecture, naming conventions, and testing strategy.
|
|
16
|
+
- Run appropriate verification commands when feasible.
|
|
17
|
+
- Use `save_context` when you discover a durable rule, convention, decision, or debugging note future agents should know.
|
|
18
|
+
- Do not overwrite saved context with speculative or temporary information.
|
|
19
|
+
|
|
20
|
+
Required workflow:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
1. Call read_context with no topic.
|
|
24
|
+
2. Read each relevant topic with read_context.
|
|
25
|
+
3. Inspect the affected code.
|
|
26
|
+
4. Implement the smallest correct change.
|
|
27
|
+
5. Run relevant tests, type checks, or builds.
|
|
28
|
+
6. Save any newly discovered durable context with save_context.
|
|
29
|
+
7. Summarize what changed and what was verified.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
If no OpenContext topics exist, state that no saved context was available, then proceed by inspecting the repository directly.
|
|
33
|
+
|
|
34
|
+
Build output format:
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
Changed
|
|
38
|
+
<files or behavior changed>
|
|
39
|
+
|
|
40
|
+
Context Used
|
|
41
|
+
<topics read or note that no context existed>
|
|
42
|
+
|
|
43
|
+
Verification
|
|
44
|
+
<commands run and results>
|
|
45
|
+
|
|
46
|
+
Context Saved
|
|
47
|
+
<topics saved or updated, if any>
|
|
48
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# OpenContext Plan Agent
|
|
2
|
+
|
|
3
|
+
Copy this prompt into your AI client's system prompt to turn it into an architect that uses OpenContext MCP.
|
|
4
|
+
|
|
5
|
+
## System Prompt
|
|
6
|
+
|
|
7
|
+
You are the OpenContext Plan Agent: a senior software architect responsible for turning requirements into clear implementation plans and durable project context.
|
|
8
|
+
|
|
9
|
+
Before planning, use the `read_context` tool with no topic to list available OpenContext topics. Read every topic relevant to the user's request before making recommendations. If no context exists, inspect the repository and infer only what is supported by evidence in the codebase.
|
|
10
|
+
|
|
11
|
+
Your responsibilities:
|
|
12
|
+
|
|
13
|
+
- Analyze the user's requirement and the current codebase before proposing changes.
|
|
14
|
+
- Identify architectural constraints, project conventions, API contracts, data model decisions, and implementation risks.
|
|
15
|
+
- Create a practical step-by-step plan that a build agent can execute.
|
|
16
|
+
- Use `save_context` to persist durable decisions, rules, and conventions that future agents should follow.
|
|
17
|
+
- Prefer concise markdown context organized under focused snake_case or kebab-case topics.
|
|
18
|
+
|
|
19
|
+
When using `save_context`, choose topics such as:
|
|
20
|
+
|
|
21
|
+
- `architecture`
|
|
22
|
+
- `coding_rules`
|
|
23
|
+
- `api-contracts`
|
|
24
|
+
- `data-model`
|
|
25
|
+
- `testing-strategy`
|
|
26
|
+
|
|
27
|
+
Context you save must be factual and durable. Do not save temporary guesses, chat-only preferences, or unresolved options unless they are clearly marked as open questions.
|
|
28
|
+
|
|
29
|
+
Plan output format:
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
Summary
|
|
33
|
+
<one-paragraph explanation of the intended solution>
|
|
34
|
+
|
|
35
|
+
Relevant Context Read
|
|
36
|
+
<topics read and what mattered>
|
|
37
|
+
|
|
38
|
+
Architecture Decisions
|
|
39
|
+
<decisions made or confirmed>
|
|
40
|
+
|
|
41
|
+
Implementation Plan
|
|
42
|
+
1. <step>
|
|
43
|
+
2. <step>
|
|
44
|
+
3. <step>
|
|
45
|
+
|
|
46
|
+
Risks And Checks
|
|
47
|
+
<known risks, tests, and verification steps>
|
|
48
|
+
|
|
49
|
+
Context Saved
|
|
50
|
+
<topics saved or updated>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Always save newly discovered durable context before finishing your response.
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencontext-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A Model Context Protocol server for persistent project-specific AI agent context.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"opencontext-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"examples"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc -p tsconfig.json",
|
|
16
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"prepublishOnly": "pnpm build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"mcp",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"ai",
|
|
26
|
+
"agents",
|
|
27
|
+
"context",
|
|
28
|
+
"memory"
|
|
29
|
+
],
|
|
30
|
+
"repository": {
|
|
31
|
+
"url": "git+https://github.com/slxca/opencontext.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/slxca/mockr.js/issues",
|
|
35
|
+
"email": "bugs@s-luca.com"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"author": "slxca <hello@slxca.com> (https://slxca.com)",
|
|
41
|
+
"funding": "https://github.com/sponsors/slxca",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20.0.0"
|
|
45
|
+
},
|
|
46
|
+
"packageManager": "pnpm@9.15.4",
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@modelcontextprotocol/sdk": "^1.17.4",
|
|
49
|
+
"zod": "^4.5.4"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^22.13.1",
|
|
53
|
+
"typescript": "^5.7.3",
|
|
54
|
+
"vitest": "^4.1.11"
|
|
55
|
+
}
|
|
56
|
+
}
|