Skip to content

Commit 15448f7

Browse files
authored
Merge pull request #18 from abskrj/feat/mcp-resources-prompts
feat: MCP resources, prompts, and client-specific setup
2 parents e8d4476 + e728093 commit 15448f7

10 files changed

Lines changed: 846 additions & 30 deletions

File tree

apps/admin/src/pages/MCPPage.tsx

Lines changed: 169 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,142 @@ interface Platform {
99
id: PlatformID
1010
label: string
1111
configPath: string
12+
snippetLabel: string
13+
description: string
1214
}
1315

1416
const PLATFORMS: Platform[] = [
15-
{ id: 'cursor', label: 'Cursor', configPath: '.cursor/mcp.json' },
16-
{ id: 'vscode', label: 'VS Code', configPath: '.vscode/mcp.json' },
17-
{ id: 'claude-code', label: 'Claude Code', configPath: '~/.claude/mcp.json' },
17+
{
18+
id: 'cursor',
19+
label: 'Cursor',
20+
configPath: '~/.cursor/mcp.json',
21+
snippetLabel: 'Config JSON',
22+
description: 'Native remote HTTP MCP configuration for Cursor.',
23+
},
24+
{
25+
id: 'vscode',
26+
label: 'VS Code',
27+
configPath: '.vscode/mcp.json',
28+
snippetLabel: 'Config JSON',
29+
description: 'Native remote HTTP MCP configuration for VS Code.',
30+
},
31+
{
32+
id: 'claude-code',
33+
label: 'Claude Code',
34+
configPath: 'Terminal',
35+
snippetLabel: 'CLI command',
36+
description: 'Adds Velane as a remote HTTP MCP server through the Claude Code CLI.',
37+
},
1838
{
1939
id: 'claude-desktop',
2040
label: 'Claude Desktop',
2141
configPath: '~/Library/Application Support/Claude/claude_desktop_config.json',
42+
snippetLabel: 'Config JSON',
43+
description: 'Uses mcp-remote to bridge Claude Desktop stdio MCP to Velane HTTP MCP.',
44+
},
45+
{
46+
id: 'codex',
47+
label: 'Codex',
48+
configPath: '~/.codex/config.toml',
49+
snippetLabel: 'Config TOML',
50+
description: 'Uses mcp-remote as a stdio bridge from Codex to Velane HTTP MCP.',
51+
},
52+
{
53+
id: 'gemini',
54+
label: 'Gemini',
55+
configPath: '~/.gemini/settings.json',
56+
snippetLabel: 'Config JSON',
57+
description: 'Uses mcp-remote as a stdio bridge from Gemini CLI to Velane HTTP MCP.',
2258
},
23-
{ id: 'codex', label: 'Codex', configPath: '.cursor/mcp.json' },
24-
{ id: 'gemini', label: 'Gemini', configPath: '~/.gemini/mcp.json' },
2559
]
2660

2761
const API_KEY_PLACEHOLDER = 'vl_YOUR_API_KEY'
2862

63+
function stdioBridgeConfig(mcpURL: string, token: string) {
64+
return {
65+
command: 'npx',
66+
args: ['-y', 'mcp-remote', mcpURL, '--header', 'Authorization:${AUTH_HEADER}'],
67+
env: {
68+
AUTH_HEADER: `Bearer ${token}`,
69+
},
70+
}
71+
}
72+
73+
function configForPlatform(platform: PlatformID, mcpURL: string, token: string) {
74+
if (!mcpURL) return ''
75+
76+
switch (platform) {
77+
case 'cursor':
78+
return JSON.stringify(
79+
{
80+
mcpServers: {
81+
velane: {
82+
name: 'velane',
83+
type: 'http',
84+
url: mcpURL,
85+
headers: {
86+
Authorization: `Bearer ${token}`,
87+
},
88+
},
89+
},
90+
},
91+
null,
92+
2,
93+
)
94+
case 'vscode':
95+
return JSON.stringify(
96+
{
97+
servers: {
98+
velane: {
99+
type: 'http',
100+
url: mcpURL,
101+
headers: {
102+
Authorization: `Bearer ${token}`,
103+
},
104+
},
105+
},
106+
},
107+
null,
108+
2,
109+
)
110+
case 'claude-code':
111+
return [
112+
'claude mcp add velane \\',
113+
` --transport http "${mcpURL}" \\`,
114+
` --header "Authorization:Bearer ${token}"`,
115+
].join('\n')
116+
case 'claude-desktop':
117+
return JSON.stringify(
118+
{
119+
mcpServers: {
120+
velane: stdioBridgeConfig(mcpURL, token),
121+
},
122+
},
123+
null,
124+
2,
125+
)
126+
case 'codex':
127+
return [
128+
'[mcp_servers.velane]',
129+
'command = "npx"',
130+
`args = ["-y", "mcp-remote", "${mcpURL}", "--header", "Authorization:\${AUTH_HEADER}"]`,
131+
'',
132+
'[mcp_servers.velane.env]',
133+
`AUTH_HEADER = "Bearer ${token}"`,
134+
].join('\n')
135+
case 'gemini':
136+
return JSON.stringify(
137+
{
138+
mcpServers: {
139+
velane: stdioBridgeConfig(mcpURL, token),
140+
},
141+
},
142+
null,
143+
2,
144+
)
145+
}
146+
}
147+
29148
export default function MCPPage() {
30149
const [activePlatform, setActivePlatform] = useState<PlatformID>('cursor')
31150
const [mcpURL, setMCPURL] = useState('')
@@ -56,25 +175,11 @@ export default function MCPPage() {
56175

57176
const token = createdAPIKey ?? API_KEY_PLACEHOLDER
58177

59-
const configJSON = useMemo(() => {
60-
if (!mcpURL) return ''
61-
return JSON.stringify(
62-
{
63-
mcpServers: {
64-
velane: {
65-
url: mcpURL,
66-
headers: {
67-
Authorization: `Bearer ${token}`,
68-
},
69-
},
70-
},
71-
},
72-
null,
73-
2,
74-
)
75-
}, [mcpURL, token])
76-
77178
const activePlatformConfig = PLATFORMS.find((p) => p.id === activePlatform) ?? PLATFORMS[0]
179+
const configSnippet = useMemo(
180+
() => configForPlatform(activePlatformConfig.id, mcpURL, token),
181+
[activePlatformConfig.id, mcpURL, token],
182+
)
78183

79184
const copyToClipboard = async (value: string, key: string) => {
80185
if (!value) return
@@ -163,15 +268,20 @@ export default function MCPPage() {
163268
) : (
164269
<div className="space-y-5">
165270
<div>
166-
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-gray-500">Config JSON</p>
271+
<div className="mb-2">
272+
<p className="text-xs font-medium uppercase tracking-wide text-gray-500">
273+
{activePlatformConfig.snippetLabel}
274+
</p>
275+
<p className="mt-1 text-xs text-gray-500">{activePlatformConfig.description}</p>
276+
</div>
167277
<div className="relative rounded-xl bg-gray-950 px-4 py-3">
168-
<pre className="overflow-x-auto text-xs leading-relaxed text-gray-100">{configJSON}</pre>
278+
<pre className="overflow-x-auto text-xs leading-relaxed text-gray-100">{configSnippet}</pre>
169279
<button
170280
type="button"
171281
className="absolute right-2 top-2 rounded p-1 text-gray-400 hover:text-gray-200"
172-
onClick={() => copyToClipboard(configJSON, 'json')}
282+
onClick={() => copyToClipboard(configSnippet, 'config')}
173283
>
174-
{copied === 'json' ? (
284+
{copied === 'config' ? (
175285
<Check className="h-4 w-4 text-green-400" />
176286
) : (
177287
<Copy className="h-4 w-4" />
@@ -180,6 +290,14 @@ export default function MCPPage() {
180290
</div>
181291
</div>
182292

293+
{['claude-desktop', 'codex', 'gemini'].includes(activePlatform) && (
294+
<p className="text-xs text-gray-500">
295+
This config uses <code className="rounded bg-gray-100 px-1">mcp-remote</code>, which runs through{' '}
296+
<code className="rounded bg-gray-100 px-1">npx</code> and forwards your Velane API key as an
297+
Authorization header.
298+
</p>
299+
)}
300+
183301
<div>
184302
<p className="mb-2 text-xs font-medium uppercase tracking-wide text-gray-500">Raw MCP URL</p>
185303
<div className="flex items-center gap-2 rounded-md border border-gray-200 bg-gray-50 px-3 py-2">
@@ -207,6 +325,30 @@ export default function MCPPage() {
207325
</div>
208326
)}
209327
</div>
328+
329+
<div className="mt-6 rounded-2xl border border-gray-200 bg-white p-6 shadow-sm">
330+
<h2 className="text-base font-semibold text-gray-900">What agents can access</h2>
331+
<div className="mt-4 grid gap-3 sm:grid-cols-3">
332+
<div className="rounded-xl border border-gray-100 bg-gray-50 p-4">
333+
<p className="text-sm font-medium text-gray-900">Tools</p>
334+
<p className="mt-1 text-xs leading-5 text-gray-500">
335+
Actions such as creating snippets, updating drafts, invoking runs, listing connections, and reading metrics.
336+
</p>
337+
</div>
338+
<div className="rounded-xl border border-gray-100 bg-gray-50 p-4">
339+
<p className="text-sm font-medium text-gray-900">Resources</p>
340+
<p className="mt-1 text-xs leading-5 text-gray-500">
341+
Bounded context such as the runtime contract, compact snippet catalog, and connected integrations.
342+
</p>
343+
</div>
344+
<div className="rounded-xl border border-gray-100 bg-gray-50 p-4">
345+
<p className="text-sm font-medium text-gray-900">Prompts</p>
346+
<p className="mt-1 text-xs leading-5 text-gray-500">
347+
Guided workflows for creating integration snippets, debugging failed invocations, and publishing after validation.
348+
</p>
349+
</div>
350+
</div>
351+
</div>
210352
</div>
211353
)
212354
}

apps/admin/src/pages/SnippetEditorPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ export default function SnippetEditorPage() {
282282
<div className="flex h-full flex-col">
283283
{toast && <Toast message={toast.message} type={toast.type} onDismiss={dismissToast} />}
284284

285-
<header className="flex shrink-0 items-center justify-between border-b border-gray-200 bg-white px-4 py-2">
285+
<header className="flex h-14 shrink-0 items-center justify-between border-b border-gray-200 bg-white px-4">
286286
<div className="flex items-center gap-3">
287287
<button
288288
className="text-sm text-gray-500 hover:text-gray-900"

docs/mcp/overview.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Add this to your MCP config:
3232
{
3333
"mcpServers": {
3434
"velane": {
35+
"name": "velane",
36+
"type": "http",
3537
"url": "http://localhost:8090/mcp",
3638
"headers": {
3739
"Authorization": "Bearer vl_xxxx"
@@ -43,6 +45,38 @@ Add this to your MCP config:
4345

4446
Use an API key with the minimum scope needed for your workflow.
4547

48+
## Tools, resources, and prompts
49+
50+
Velane's MCP server exposes three kinds of capability:
51+
52+
- **Tools** perform actions: create snippets, update drafts, publish versions, invoke snippets, list connections, read invocation records, and manage secrets.
53+
- **Resources** expose bounded read-only context. They help agents understand the current workspace without dumping every object at startup.
54+
- **Prompts** expose reusable Velane workflows so agents use tools in the right order.
55+
56+
### Resources
57+
58+
`resources/list` returns only resource descriptors. It does not return every snippet or every invocation. Clients read resource content explicitly with `resources/read`.
59+
60+
Current resources:
61+
62+
| URI | Purpose |
63+
|---|---|
64+
| `velane://runtime/contract` | Snippet handler shapes, integration helper usage, invocation/logging rules, and recommended MCP workflow. |
65+
| `velane://snippets` | Compact first page of snippets. The response is bounded and omits code. Use `get_snippet` for code, versions, and active environments. |
66+
| `velane://connections` | Compact first page of connected integrations. Use `list_connections` for filtering and pagination. |
67+
68+
This means a tenant with 500 snippets does **not** send all 500 snippets during MCP startup. Startup only advertises `velane://snippets`; content is read only when the agent asks for it, and the snippet catalog is compact and bounded.
69+
70+
### Prompts
71+
72+
Current prompts:
73+
74+
| Prompt | Purpose |
75+
|---|---|
76+
| `create_integration_snippet` | Guides an agent through connection discovery, provider docs lookup, snippet creation/update, dev invocation, and validation. |
77+
| `debug_failed_invocation` | Guides an agent through `get_invocation` / `get_logs`, code inspection, draft patching, and dev reruns. |
78+
| `publish_after_validation` | Guides an agent to validate a specific version before publishing it to a target environment. |
79+
4680
## Typical agent workflow
4781

4882
1. `list_connections`

0 commit comments

Comments
 (0)