@ompo-design/mcp-server 0.1.6 → 0.1.7
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/dist/index.js +89 -42
- package/dist/session.d.ts +9 -0
- package/dist/session.js +30 -0
- package/dist/tokens.d.ts +5 -0
- package/dist/tokens.js +49 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -6,9 +6,10 @@ import { buildApplyPlan, explainEdit } from './apply-plan.js';
|
|
|
6
6
|
import { runCli } from './cli.js';
|
|
7
7
|
import { editsStoreReady, listEdits, readEditBundle, recordEditApplied, recordEditPull } from './edit-store.js';
|
|
8
8
|
import { getOmpoEditsStorePath } from './edits-path.js';
|
|
9
|
+
import { consumeMcpToken, McpTokenError } from './tokens.js';
|
|
9
10
|
const server = new McpServer({
|
|
10
11
|
name: 'ompo-mcp-server',
|
|
11
|
-
version: '0.1.
|
|
12
|
+
version: '0.1.7'
|
|
12
13
|
});
|
|
13
14
|
function requireEditsStore() {
|
|
14
15
|
const storePath = getOmpoEditsStorePath();
|
|
@@ -17,47 +18,86 @@ function requireEditsStore() {
|
|
|
17
18
|
}
|
|
18
19
|
return storePath;
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
21
|
+
async function withMcpTokenUse(toolName, handler) {
|
|
22
|
+
const tokensRemaining = await consumeMcpToken(toolName);
|
|
23
|
+
return handler(tokensRemaining);
|
|
24
|
+
}
|
|
25
|
+
function tokenErrorResult(error) {
|
|
26
|
+
const message = error instanceof McpTokenError
|
|
27
|
+
? error.message
|
|
28
|
+
: 'Could not verify MCP token balance. Check your connection and try again.';
|
|
23
29
|
return {
|
|
24
30
|
content: [
|
|
25
31
|
{
|
|
26
32
|
type: 'text',
|
|
27
|
-
text:
|
|
33
|
+
text: message
|
|
28
34
|
}
|
|
29
|
-
]
|
|
35
|
+
],
|
|
36
|
+
isError: true
|
|
30
37
|
};
|
|
38
|
+
}
|
|
39
|
+
server.tool('list_edits', 'List Ompo edit bundles saved on this machine', {}, async () => {
|
|
40
|
+
try {
|
|
41
|
+
return await withMcpTokenUse('list_edits', async (tokensRemaining) => {
|
|
42
|
+
const storePath = requireEditsStore();
|
|
43
|
+
const edits = listEdits();
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: 'text',
|
|
48
|
+
text: JSON.stringify({ storePath, edits, tokensRemaining }, null, 2)
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return tokenErrorResult(error);
|
|
56
|
+
}
|
|
31
57
|
});
|
|
32
58
|
server.tool('get_edit', 'Load a specific Ompo edit bundle by id', {
|
|
33
59
|
id: z.string().describe('Edit id, e.g. ed_8K42P')
|
|
34
60
|
}, async ({ id }) => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
try {
|
|
62
|
+
return await withMcpTokenUse('get_edit', async (tokensRemaining) => {
|
|
63
|
+
requireEditsStore();
|
|
64
|
+
const bundle = readEditBundle(id);
|
|
65
|
+
recordEditPull(id);
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: 'text',
|
|
70
|
+
text: `${JSON.stringify(bundle, null, 2)}\n\nTokens remaining: ${tokensRemaining}`
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return tokenErrorResult(error);
|
|
78
|
+
}
|
|
46
79
|
});
|
|
47
80
|
server.tool('explain_edit', 'Summarize what an Ompo edit changes', {
|
|
48
81
|
id: z.string().describe('Edit id, e.g. ed_8K42P')
|
|
49
82
|
}, async ({ id }) => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
83
|
+
try {
|
|
84
|
+
return await withMcpTokenUse('explain_edit', async (tokensRemaining) => {
|
|
85
|
+
requireEditsStore();
|
|
86
|
+
const bundle = readEditBundle(id);
|
|
87
|
+
recordEditPull(id);
|
|
88
|
+
return {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
type: 'text',
|
|
92
|
+
text: `${explainEdit(bundle)}\n\nTokens remaining: ${tokensRemaining}`
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return tokenErrorResult(error);
|
|
100
|
+
}
|
|
61
101
|
});
|
|
62
102
|
server.tool('apply_edit', 'Build an apply plan for an Ompo edit. The agent should execute the plan against source files in the open codebase and only change listed properties.', {
|
|
63
103
|
id: z.string().describe('Edit id, e.g. ed_8K42P'),
|
|
@@ -66,21 +106,28 @@ server.tool('apply_edit', 'Build an apply plan for an Ompo edit. The agent shoul
|
|
|
66
106
|
.optional()
|
|
67
107
|
.describe('Set true after the agent successfully applies the edit')
|
|
68
108
|
}, async ({ id, markApplied }) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
content: [
|
|
78
|
-
{
|
|
79
|
-
type: 'text',
|
|
80
|
-
text: JSON.stringify(plan, null, 2)
|
|
109
|
+
try {
|
|
110
|
+
return await withMcpTokenUse('apply_edit', async (tokensRemaining) => {
|
|
111
|
+
requireEditsStore();
|
|
112
|
+
const bundle = readEditBundle(id);
|
|
113
|
+
recordEditPull(id);
|
|
114
|
+
const plan = buildApplyPlan(bundle);
|
|
115
|
+
if (markApplied) {
|
|
116
|
+
recordEditApplied(id);
|
|
81
117
|
}
|
|
82
|
-
|
|
83
|
-
|
|
118
|
+
return {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: 'text',
|
|
122
|
+
text: JSON.stringify({ ...plan, tokensRemaining }, null, 2)
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
return tokenErrorResult(error);
|
|
130
|
+
}
|
|
84
131
|
});
|
|
85
132
|
async function main() {
|
|
86
133
|
if (runCli(process.argv.slice(2)))
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type OmpoMcpSession = {
|
|
2
|
+
userId: string;
|
|
3
|
+
accessToken: string;
|
|
4
|
+
supabaseUrl: string;
|
|
5
|
+
supabaseAnonKey: string;
|
|
6
|
+
updatedAt: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function getOmpoMcpSessionPath(): string;
|
|
9
|
+
export declare function readOmpoMcpSession(): OmpoMcpSession | null;
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
export function getOmpoMcpSessionPath() {
|
|
5
|
+
return join(homedir(), '.ompo', 'session.json');
|
|
6
|
+
}
|
|
7
|
+
export function readOmpoMcpSession() {
|
|
8
|
+
const sessionPath = getOmpoMcpSessionPath();
|
|
9
|
+
if (!existsSync(sessionPath))
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
const parsed = JSON.parse(readFileSync(sessionPath, 'utf8'));
|
|
13
|
+
if (typeof parsed.userId !== 'string' ||
|
|
14
|
+
typeof parsed.accessToken !== 'string' ||
|
|
15
|
+
typeof parsed.supabaseUrl !== 'string' ||
|
|
16
|
+
typeof parsed.supabaseAnonKey !== 'string') {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
userId: parsed.userId,
|
|
21
|
+
accessToken: parsed.accessToken,
|
|
22
|
+
supabaseUrl: parsed.supabaseUrl,
|
|
23
|
+
supabaseAnonKey: parsed.supabaseAnonKey,
|
|
24
|
+
updatedAt: typeof parsed.updatedAt === 'string' ? parsed.updatedAt : new Date(0).toISOString()
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare class McpTokenError extends Error {
|
|
2
|
+
code: 'NOT_SIGNED_IN' | 'INSUFFICIENT_TOKENS' | 'TOKEN_SERVICE_UNAVAILABLE';
|
|
3
|
+
constructor(code: McpTokenError['code'], message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare function consumeMcpToken(toolName: string): Promise<number>;
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createClient } from '@supabase/supabase-js';
|
|
2
|
+
import { readOmpoMcpSession } from './session.js';
|
|
3
|
+
export class McpTokenError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'McpTokenError';
|
|
8
|
+
this.code = code;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function mapRpcError(message) {
|
|
12
|
+
const normalized = message.toUpperCase();
|
|
13
|
+
if (normalized.includes('NOT_AUTHENTICATED') || normalized.includes('JWT')) {
|
|
14
|
+
return new McpTokenError('NOT_SIGNED_IN', 'Sign in to Ompo and keep the app open so your MCP session stays active.');
|
|
15
|
+
}
|
|
16
|
+
if (normalized.includes('INSUFFICIENT_TOKENS')) {
|
|
17
|
+
return new McpTokenError('INSUFFICIENT_TOKENS', 'You are out of MCP tokens. Add more tokens to your account before using Ompo MCP tools.');
|
|
18
|
+
}
|
|
19
|
+
return new McpTokenError('TOKEN_SERVICE_UNAVAILABLE', 'Could not verify MCP token balance. Check your connection and try again.');
|
|
20
|
+
}
|
|
21
|
+
export async function consumeMcpToken(toolName) {
|
|
22
|
+
const session = readOmpoMcpSession();
|
|
23
|
+
if (!session) {
|
|
24
|
+
throw new McpTokenError('NOT_SIGNED_IN', 'Sign in to Ompo and keep the app open so your MCP session stays active.');
|
|
25
|
+
}
|
|
26
|
+
const supabase = createClient(session.supabaseUrl, session.supabaseAnonKey, {
|
|
27
|
+
auth: {
|
|
28
|
+
persistSession: false,
|
|
29
|
+
autoRefreshToken: false,
|
|
30
|
+
detectSessionInUrl: false
|
|
31
|
+
},
|
|
32
|
+
global: {
|
|
33
|
+
headers: {
|
|
34
|
+
Authorization: `Bearer ${session.accessToken}`
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const { data, error } = await supabase.rpc('consume_mcp_token', {
|
|
39
|
+
p_tool_name: toolName
|
|
40
|
+
});
|
|
41
|
+
if (error) {
|
|
42
|
+
throw mapRpcError(error.message);
|
|
43
|
+
}
|
|
44
|
+
const tokensRemaining = data?.tokens_remaining;
|
|
45
|
+
if (typeof tokensRemaining !== 'number') {
|
|
46
|
+
throw new McpTokenError('TOKEN_SERVICE_UNAVAILABLE', 'Could not verify MCP token balance. Check your connection and try again.');
|
|
47
|
+
}
|
|
48
|
+
return tokensRemaining;
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ompo-design/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "MCP server for applying Ompo visual edits to a codebase",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"claude-code"
|
|
13
13
|
],
|
|
14
14
|
"bin": {
|
|
15
|
-
"ompo-mcp": "
|
|
15
|
+
"ompo-mcp": "dist/index.js"
|
|
16
16
|
},
|
|
17
17
|
"main": "./dist/index.js",
|
|
18
18
|
"files": [
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
28
|
+
"@supabase/supabase-js": "^2.49.8",
|
|
28
29
|
"zod": "^3.24.2"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|