@ucm/mcp-server 0.2.0 → 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 +22 -32
- package/dist/client.d.ts +9 -10
- package/dist/client.js +38 -120
- package/dist/index.js +24 -105
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -16,20 +16,7 @@ npx @ucm/mcp-server
|
|
|
16
16
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
|
-
### 1.
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
node -e "
|
|
23
|
-
import('@noble/ed25519').then(async ed => {
|
|
24
|
-
const priv = ed.utils.randomPrivateKey();
|
|
25
|
-
const pub = await ed.getPublicKeyAsync(priv);
|
|
26
|
-
console.log('UCM_PRIVATE_KEY=' + Buffer.from(priv).toString('hex'));
|
|
27
|
-
console.log('UCM_PUBLIC_KEY=' + Buffer.from(pub).toString('hex'));
|
|
28
|
-
})
|
|
29
|
-
"
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### 2. Configure your MCP client
|
|
19
|
+
### 1. Configure your MCP client
|
|
33
20
|
|
|
34
21
|
Add to your Claude Desktop `claude_desktop_config.json`:
|
|
35
22
|
|
|
@@ -40,40 +27,43 @@ Add to your Claude Desktop `claude_desktop_config.json`:
|
|
|
40
27
|
"command": "npx",
|
|
41
28
|
"args": ["@ucm/mcp-server"],
|
|
42
29
|
"env": {
|
|
43
|
-
"
|
|
44
|
-
"UCM_PRIVATE_KEY": "<your-private-key-hex>",
|
|
45
|
-
"UCM_PUBLIC_KEY": "<your-public-key-hex>"
|
|
30
|
+
"UCM_API_KEY": "<your-api-key>"
|
|
46
31
|
}
|
|
47
32
|
}
|
|
48
33
|
}
|
|
49
34
|
}
|
|
50
35
|
```
|
|
51
36
|
|
|
52
|
-
|
|
37
|
+
Don't have an API key? No problem — your agent can use `ucm_register` to self-register and get one automatically.
|
|
38
|
+
|
|
39
|
+
### 2. Use via your agent
|
|
53
40
|
|
|
54
|
-
Your agent now has access to
|
|
41
|
+
Your agent now has access to 7 tools:
|
|
55
42
|
|
|
56
43
|
| Tool | Description |
|
|
57
44
|
|------|-------------|
|
|
58
45
|
| `ucm_discover` | Search marketplace by natural language |
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `ucm_balance` | Check budget remaining |
|
|
46
|
+
| `ucm_call` | Call an API service (buy + execute + auto-refund on failure) |
|
|
47
|
+
| `ucm_balance` | Check credit balance |
|
|
62
48
|
| `ucm_history` | View transaction history |
|
|
63
49
|
| `ucm_service_info` | Get service details |
|
|
64
|
-
| `ucm_register` | Self-register as agent, get API key |
|
|
65
|
-
| `
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
50
|
+
| `ucm_register` | Self-register as agent, get API key + $1.00 credits |
|
|
51
|
+
| `ucm_list_services` | Browse the full service catalog |
|
|
52
|
+
|
|
53
|
+
### 3. Typical flow
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Agent: ucm_register(name: "my-agent") → gets API key + $1.00 credits
|
|
57
|
+
Agent: ucm_discover(need: "web search") → finds ucm/web-search
|
|
58
|
+
Agent: ucm_call(service_id: "ucm/web-search", endpoint: "search", body: {query: "..."})
|
|
59
|
+
```
|
|
69
60
|
|
|
70
61
|
## Environment Variables
|
|
71
62
|
|
|
72
63
|
| Variable | Required | Default | Description |
|
|
73
64
|
|----------|----------|---------|-------------|
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `UCM_REGISTRY_URL` | No | `http://localhost:3000` | UCM Registry URL |
|
|
65
|
+
| `UCM_API_KEY` | No | — | Agent API key (`ucm_key_*`). Can also be obtained via `ucm_register`. |
|
|
66
|
+
| `UCM_REGISTRY_URL` | No | `https://registry.ucm.ai` | UCM Registry URL |
|
|
77
67
|
|
|
78
68
|
## How It Works
|
|
79
69
|
|
|
@@ -83,8 +73,8 @@ Agent → MCP Client → UCM MCP Server → UCM Registry → Provider APIs
|
|
|
83
73
|
|
|
84
74
|
1. Your agent describes what it needs (e.g., "I need a web search API")
|
|
85
75
|
2. The MCP server searches the UCM marketplace
|
|
86
|
-
3. Agent
|
|
87
|
-
4.
|
|
76
|
+
3. Agent calls the service — purchase and execution happen atomically
|
|
77
|
+
4. If the upstream API fails, credits are automatically refunded
|
|
88
78
|
|
|
89
79
|
## License
|
|
90
80
|
|
package/dist/client.d.ts
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UCM Registry HTTP client with API key auth.
|
|
3
|
+
*/
|
|
1
4
|
export interface UCMConfig {
|
|
2
5
|
registryUrl: string;
|
|
3
|
-
|
|
4
|
-
agentPublicKey: string;
|
|
6
|
+
apiKey?: string;
|
|
5
7
|
}
|
|
6
8
|
export declare class UCMClient {
|
|
7
9
|
private config;
|
|
8
10
|
constructor(config: UCMConfig);
|
|
9
|
-
|
|
11
|
+
setApiKey(key: string): void;
|
|
12
|
+
private bearerFetch;
|
|
10
13
|
private unsignedFetch;
|
|
11
|
-
private
|
|
14
|
+
private handleResponse;
|
|
12
15
|
discover(need: string, options?: {
|
|
13
16
|
tags?: string[];
|
|
14
17
|
max_price?: string;
|
|
15
18
|
min_reputation?: number;
|
|
16
19
|
limit?: number;
|
|
17
20
|
}): Promise<unknown>;
|
|
21
|
+
call(serviceId: string, endpoint: string, body?: Record<string, unknown>): Promise<unknown>;
|
|
18
22
|
buy(serviceId: string, plan?: string): Promise<unknown>;
|
|
19
23
|
balance(): Promise<unknown>;
|
|
20
24
|
history(limit?: number): Promise<unknown>;
|
|
21
|
-
call(serviceId: string, method: string, params: Record<string, unknown>): Promise<any>;
|
|
22
25
|
getService(serviceId: string): Promise<unknown>;
|
|
23
|
-
register(name: string, description?: string): Promise<unknown>;
|
|
24
|
-
publish(descriptor: Record<string, unknown>): Promise<unknown>;
|
|
25
|
-
deleteService(serviceId: string): Promise<unknown>;
|
|
26
|
-
authorize(authorization: Record<string, unknown>): Promise<unknown>;
|
|
27
|
-
verifyToken(token: string): Promise<unknown>;
|
|
26
|
+
register(name: string, description?: string, ref?: string): Promise<unknown>;
|
|
28
27
|
listServices(options?: {
|
|
29
28
|
since?: string;
|
|
30
29
|
tags?: string;
|
package/dist/client.js
CHANGED
|
@@ -1,91 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* UCM Registry HTTP client with
|
|
2
|
+
* UCM Registry HTTP client with API key auth.
|
|
3
3
|
*/
|
|
4
|
-
import * as ed from '@noble/ed25519';
|
|
5
|
-
import { createHash } from 'crypto';
|
|
6
|
-
// Setup sha512 for noble/ed25519 v2
|
|
7
|
-
ed.etc.sha512Sync = (...m) => {
|
|
8
|
-
const h = createHash('sha512');
|
|
9
|
-
for (const msg of m)
|
|
10
|
-
h.update(msg);
|
|
11
|
-
return new Uint8Array(h.digest());
|
|
12
|
-
};
|
|
13
|
-
function hexToBytes(hex) {
|
|
14
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
15
|
-
for (let i = 0; i < hex.length; i += 2) {
|
|
16
|
-
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
17
|
-
}
|
|
18
|
-
return bytes;
|
|
19
|
-
}
|
|
20
|
-
function isHex(value) {
|
|
21
|
-
return value.length % 2 === 0 && /^[0-9a-fA-F]+$/.test(value);
|
|
22
|
-
}
|
|
23
|
-
function decodeKey(key) {
|
|
24
|
-
if (isHex(key)) {
|
|
25
|
-
return hexToBytes(key);
|
|
26
|
-
}
|
|
27
|
-
return new Uint8Array(Buffer.from(key, 'base64url'));
|
|
28
|
-
}
|
|
29
|
-
function sha256Hex(data) {
|
|
30
|
-
return createHash('sha256').update(data).digest('hex');
|
|
31
|
-
}
|
|
32
|
-
function isPlainObject(value) {
|
|
33
|
-
return Object.prototype.toString.call(value) === '[object Object]';
|
|
34
|
-
}
|
|
35
|
-
function canonicalize(value) {
|
|
36
|
-
if (Array.isArray(value)) {
|
|
37
|
-
return value.map(canonicalize);
|
|
38
|
-
}
|
|
39
|
-
if (isPlainObject(value)) {
|
|
40
|
-
const sortedKeys = Object.keys(value).sort();
|
|
41
|
-
const result = {};
|
|
42
|
-
for (const key of sortedKeys) {
|
|
43
|
-
result[key] = canonicalize(value[key]);
|
|
44
|
-
}
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
return value;
|
|
48
|
-
}
|
|
49
|
-
function canonicalJson(obj) {
|
|
50
|
-
return JSON.stringify(canonicalize(obj));
|
|
51
|
-
}
|
|
52
4
|
export class UCMClient {
|
|
53
5
|
config;
|
|
54
6
|
constructor(config) {
|
|
55
7
|
this.config = config;
|
|
56
8
|
}
|
|
57
|
-
|
|
9
|
+
setApiKey(key) {
|
|
10
|
+
this.config.apiKey = key;
|
|
11
|
+
}
|
|
12
|
+
async bearerFetch(path, method, body) {
|
|
13
|
+
if (!this.config.apiKey) {
|
|
14
|
+
throw new Error('No API key configured. Use ucm_register to get one, or set UCM_API_KEY.');
|
|
15
|
+
}
|
|
58
16
|
const url = new URL(path, this.config.registryUrl);
|
|
59
|
-
const bodyStr = body ? JSON.stringify(body) : '';
|
|
60
|
-
const timestamp = Date.now();
|
|
61
|
-
const nonce = crypto.randomUUID();
|
|
62
|
-
const pathWithQuery = `${url.pathname}${url.search}`;
|
|
63
|
-
const payload = canonicalJson({
|
|
64
|
-
body_hash: sha256Hex(bodyStr || ''),
|
|
65
|
-
method,
|
|
66
|
-
nonce,
|
|
67
|
-
path: pathWithQuery,
|
|
68
|
-
timestamp,
|
|
69
|
-
});
|
|
70
|
-
const signature = await this.sign(sha256Hex(payload));
|
|
71
|
-
const headers = {
|
|
72
|
-
'Content-Type': 'application/json',
|
|
73
|
-
'X-ACP-Pubkey': this.config.agentPublicKey,
|
|
74
|
-
'X-ACP-Timestamp': String(timestamp),
|
|
75
|
-
'X-ACP-Nonce': nonce,
|
|
76
|
-
'X-ACP-Signature': signature,
|
|
77
|
-
};
|
|
78
17
|
const res = await fetch(url, {
|
|
79
18
|
method,
|
|
80
|
-
headers
|
|
81
|
-
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json',
|
|
21
|
+
'Authorization': `Bearer ${this.config.apiKey}`,
|
|
22
|
+
},
|
|
23
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
82
24
|
});
|
|
83
|
-
|
|
84
|
-
if (!res.ok) {
|
|
85
|
-
const err = data.error;
|
|
86
|
-
throw new Error(`${err?.code ?? res.status}: ${err?.message ?? 'Request failed'}`);
|
|
87
|
-
}
|
|
88
|
-
return data;
|
|
25
|
+
return this.handleResponse(res);
|
|
89
26
|
}
|
|
90
27
|
async unsignedFetch(path, method, body) {
|
|
91
28
|
const url = new URL(path, this.config.registryUrl);
|
|
@@ -94,68 +31,49 @@ export class UCMClient {
|
|
|
94
31
|
headers: { 'Content-Type': 'application/json' },
|
|
95
32
|
body: body ? JSON.stringify(body) : undefined,
|
|
96
33
|
});
|
|
34
|
+
return this.handleResponse(res);
|
|
35
|
+
}
|
|
36
|
+
async handleResponse(res) {
|
|
97
37
|
const data = await res.json();
|
|
98
38
|
if (!res.ok) {
|
|
99
39
|
const err = data.error;
|
|
100
|
-
|
|
40
|
+
const base = `${err?.code ?? res.status}: ${err?.message ?? 'Request failed'}`;
|
|
41
|
+
const extras = [];
|
|
42
|
+
if (err?.claim_url)
|
|
43
|
+
extras.push(`Claim URL: ${err.claim_url}`);
|
|
44
|
+
if (err?.claim_instructions)
|
|
45
|
+
extras.push(`Instructions: ${err.claim_instructions}`);
|
|
46
|
+
if (err?.balance)
|
|
47
|
+
extras.push(`Current balance: ${err.balance} ${err.currency ?? ''}`);
|
|
48
|
+
throw new Error(extras.length > 0 ? `${base}\n${extras.join('\n')}` : base);
|
|
101
49
|
}
|
|
102
50
|
return data;
|
|
103
51
|
}
|
|
104
|
-
async sign(message) {
|
|
105
|
-
const msgBytes = new TextEncoder().encode(message);
|
|
106
|
-
const sig = await ed.signAsync(msgBytes, decodeKey(this.config.agentPrivateKey));
|
|
107
|
-
return Buffer.from(sig).toString('base64url');
|
|
108
|
-
}
|
|
109
52
|
async discover(need, options) {
|
|
110
53
|
return this.unsignedFetch('/v1/discover', 'POST', { need, ...options });
|
|
111
54
|
}
|
|
55
|
+
async call(serviceId, endpoint, body) {
|
|
56
|
+
return this.bearerFetch('/v1/call', 'POST', {
|
|
57
|
+
service_id: serviceId,
|
|
58
|
+
endpoint,
|
|
59
|
+
...(body && Object.keys(body).length > 0 ? body : {}),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
112
62
|
async buy(serviceId, plan) {
|
|
113
|
-
return this.
|
|
63
|
+
return this.bearerFetch('/v1/buy', 'POST', { service_id: serviceId, plan: plan ?? 'per-call' });
|
|
114
64
|
}
|
|
115
65
|
async balance() {
|
|
116
|
-
return this.
|
|
66
|
+
return this.bearerFetch('/v1/balance', 'GET');
|
|
117
67
|
}
|
|
118
68
|
async history(limit) {
|
|
119
69
|
const path = limit ? `/v1/history?limit=${limit}` : '/v1/history';
|
|
120
|
-
return this.
|
|
121
|
-
}
|
|
122
|
-
async call(serviceId, method, params) {
|
|
123
|
-
// 1. Buy to get a token
|
|
124
|
-
const buyResult = await this.buy(serviceId);
|
|
125
|
-
const token = buyResult.access_token;
|
|
126
|
-
const endpoint = buyResult.service_endpoint;
|
|
127
|
-
// 2. Call the provider API
|
|
128
|
-
const res = await fetch(`${endpoint}${method}`, {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
headers: {
|
|
131
|
-
'Content-Type': 'application/json',
|
|
132
|
-
'Authorization': `Bearer ${token}`,
|
|
133
|
-
},
|
|
134
|
-
body: JSON.stringify(params),
|
|
135
|
-
});
|
|
136
|
-
if (!res.ok) {
|
|
137
|
-
const text = await res.text();
|
|
138
|
-
throw new Error(`Provider API error ${res.status}: ${text}`);
|
|
139
|
-
}
|
|
140
|
-
return res.json();
|
|
70
|
+
return this.bearerFetch(path, 'GET');
|
|
141
71
|
}
|
|
142
72
|
async getService(serviceId) {
|
|
143
73
|
return this.unsignedFetch(`/v1/services/${serviceId}`, 'GET');
|
|
144
74
|
}
|
|
145
|
-
async register(name, description) {
|
|
146
|
-
return this.unsignedFetch('/v1/agents/register', 'POST', { name, description });
|
|
147
|
-
}
|
|
148
|
-
async publish(descriptor) {
|
|
149
|
-
return this.signedFetch('/v1/publish', 'POST', descriptor);
|
|
150
|
-
}
|
|
151
|
-
async deleteService(serviceId) {
|
|
152
|
-
return this.signedFetch(`/v1/publish/${serviceId}`, 'DELETE');
|
|
153
|
-
}
|
|
154
|
-
async authorize(authorization) {
|
|
155
|
-
return this.signedFetch('/v1/authorize', 'POST', authorization);
|
|
156
|
-
}
|
|
157
|
-
async verifyToken(token) {
|
|
158
|
-
return this.unsignedFetch('/v1/token/verify', 'POST', { token });
|
|
75
|
+
async register(name, description, ref) {
|
|
76
|
+
return this.unsignedFetch('/v1/agents/register', 'POST', { name, description, ref });
|
|
159
77
|
}
|
|
160
78
|
async listServices(options) {
|
|
161
79
|
const params = new URLSearchParams();
|
package/dist/index.js
CHANGED
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* UCM MCP Server
|
|
3
|
+
* UCM MCP Server v0.3.0
|
|
4
4
|
*
|
|
5
5
|
* Exposes UCM as MCP tools so any compatible agent (Claude, GPT, etc.)
|
|
6
|
-
* can discover
|
|
6
|
+
* can discover and use API services autonomously.
|
|
7
7
|
*
|
|
8
8
|
* Config via env vars:
|
|
9
|
-
* UCM_REGISTRY_URL — Registry base URL (default:
|
|
10
|
-
*
|
|
11
|
-
* UCM_PUBLIC_KEY — Agent Ed25519 public key (hex)
|
|
9
|
+
* UCM_REGISTRY_URL — Registry base URL (default: https://registry.ucm.ai)
|
|
10
|
+
* UCM_API_KEY — Agent API key (ucm_key_*). Optional — use ucm_register to get one.
|
|
12
11
|
*/
|
|
13
12
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
14
13
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
15
14
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
16
15
|
import { UCMClient } from './client.js';
|
|
17
|
-
const registryUrl = process.env.UCM_REGISTRY_URL ?? '
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
console.error('Error: UCM_PRIVATE_KEY and UCM_PUBLIC_KEY env vars are required.');
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
const client = new UCMClient({ registryUrl, agentPrivateKey: privateKey, agentPublicKey: publicKey });
|
|
25
|
-
const server = new Server({ name: 'ucm', version: '0.1.0' }, { capabilities: { tools: {} } });
|
|
16
|
+
const registryUrl = process.env.UCM_REGISTRY_URL ?? 'https://registry.ucm.ai';
|
|
17
|
+
const apiKey = process.env.UCM_API_KEY ?? '';
|
|
18
|
+
let client = new UCMClient({ registryUrl, apiKey: apiKey || undefined });
|
|
19
|
+
const server = new Server({ name: 'ucm', version: '0.3.0' }, { capabilities: { tools: {} } });
|
|
26
20
|
// --- tools/list ---
|
|
27
21
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
28
22
|
tools: [
|
|
@@ -40,34 +34,22 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
40
34
|
required: ['need'],
|
|
41
35
|
},
|
|
42
36
|
},
|
|
43
|
-
{
|
|
44
|
-
name: 'ucm_buy',
|
|
45
|
-
description: 'Purchase access to an API service. Returns an access token and the service endpoint. Charges are deducted from your budget.',
|
|
46
|
-
inputSchema: {
|
|
47
|
-
type: 'object',
|
|
48
|
-
properties: {
|
|
49
|
-
service_id: { type: 'string', description: 'Service ID, e.g. "acme/web-search"' },
|
|
50
|
-
plan: { type: 'string', description: 'Pricing plan: "per-call" (default) or "subscription"' },
|
|
51
|
-
},
|
|
52
|
-
required: ['service_id'],
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
37
|
{
|
|
56
38
|
name: 'ucm_call',
|
|
57
|
-
description: '
|
|
39
|
+
description: 'Call an API service in one step. Purchases access, executes the call, and returns the result. If the upstream API fails, credits are automatically refunded.',
|
|
58
40
|
inputSchema: {
|
|
59
41
|
type: 'object',
|
|
60
42
|
properties: {
|
|
61
|
-
service_id: { type: 'string', description: 'Service ID
|
|
62
|
-
|
|
63
|
-
|
|
43
|
+
service_id: { type: 'string', description: 'Service ID, e.g. "ucm/web-search"' },
|
|
44
|
+
endpoint: { type: 'string', description: 'Endpoint name, e.g. "search", "generate", "execute"' },
|
|
45
|
+
body: { type: 'object', description: 'Parameters for the API call (optional for endpoints with no required params)' },
|
|
64
46
|
},
|
|
65
|
-
required: ['service_id', '
|
|
47
|
+
required: ['service_id', 'endpoint'],
|
|
66
48
|
},
|
|
67
49
|
},
|
|
68
50
|
{
|
|
69
51
|
name: 'ucm_balance',
|
|
70
|
-
description: 'Check your current UCM
|
|
52
|
+
description: 'Check your current UCM credit balance and spending. Returns credits (balance, lifetime_added, lifetime_spent) and usage counters.',
|
|
71
53
|
inputSchema: { type: 'object', properties: {} },
|
|
72
54
|
},
|
|
73
55
|
{
|
|
@@ -93,62 +75,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
93
75
|
},
|
|
94
76
|
{
|
|
95
77
|
name: 'ucm_register',
|
|
96
|
-
description: 'Self-register as a new agent on UCM. Returns an agent ID and API key for
|
|
78
|
+
description: 'Self-register as a new agent on UCM. Returns an agent ID, API key, and $1.00 bonus credits. The API key is automatically saved for this session. Supports optional referral code for extra credits.',
|
|
97
79
|
inputSchema: {
|
|
98
80
|
type: 'object',
|
|
99
81
|
properties: {
|
|
100
82
|
name: { type: 'string', description: 'Agent name (1-255 characters)' },
|
|
101
83
|
description: { type: 'string', description: 'Optional agent description' },
|
|
84
|
+
ref: { type: 'string', description: 'Optional referral code' },
|
|
102
85
|
},
|
|
103
86
|
required: ['name'],
|
|
104
87
|
},
|
|
105
88
|
},
|
|
106
|
-
{
|
|
107
|
-
name: 'ucm_publish',
|
|
108
|
-
description: 'Publish (list) a new API service on the UCM marketplace. Requires Ed25519 signing.',
|
|
109
|
-
inputSchema: {
|
|
110
|
-
type: 'object',
|
|
111
|
-
properties: {
|
|
112
|
-
descriptor: { type: 'object', description: 'Full ACP service descriptor object with acp_version, service_id, name, provider, endpoints, pricing, sla, etc.' },
|
|
113
|
-
},
|
|
114
|
-
required: ['descriptor'],
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: 'ucm_delete_service',
|
|
119
|
-
description: 'Remove (delist) a service from the UCM marketplace. Only the service provider can delete it.',
|
|
120
|
-
inputSchema: {
|
|
121
|
-
type: 'object',
|
|
122
|
-
properties: {
|
|
123
|
-
service_id: { type: 'string', description: 'Service ID to delete' },
|
|
124
|
-
},
|
|
125
|
-
required: ['service_id'],
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
name: 'ucm_authorize',
|
|
130
|
-
description: 'Create a budget authorization for an agent. Sets per-transaction, daily, and monthly spending limits.',
|
|
131
|
-
inputSchema: {
|
|
132
|
-
type: 'object',
|
|
133
|
-
properties: {
|
|
134
|
-
agent: { type: 'string', description: 'Agent identifier (e.g. "solana:<pubkey>")' },
|
|
135
|
-
rules: { type: 'object', description: 'Budget rules: max_per_tx, max_daily, max_monthly, currency, expires_at, etc.' },
|
|
136
|
-
operator: { type: 'string', description: 'Operator identifier (your pubkey)' },
|
|
137
|
-
},
|
|
138
|
-
required: ['agent', 'rules', 'operator'],
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
name: 'ucm_verify_token',
|
|
143
|
-
description: 'Verify an ACP access token. Returns token validity and payload details.',
|
|
144
|
-
inputSchema: {
|
|
145
|
-
type: 'object',
|
|
146
|
-
properties: {
|
|
147
|
-
token: { type: 'string', description: 'Access token starting with "acp_sk_"' },
|
|
148
|
-
},
|
|
149
|
-
required: ['token'],
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
89
|
{
|
|
153
90
|
name: 'ucm_list_services',
|
|
154
91
|
description: 'Browse the full UCM service catalog, or check for newly added services since a given date.',
|
|
@@ -176,11 +113,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
176
113
|
limit: args.limit,
|
|
177
114
|
});
|
|
178
115
|
break;
|
|
179
|
-
case 'ucm_buy':
|
|
180
|
-
result = await client.buy(args.service_id, args.plan);
|
|
181
|
-
break;
|
|
182
116
|
case 'ucm_call':
|
|
183
|
-
result = await client.call(args.service_id, args.
|
|
117
|
+
result = await client.call(args.service_id, args.endpoint, args.body);
|
|
184
118
|
break;
|
|
185
119
|
case 'ucm_balance':
|
|
186
120
|
result = await client.balance();
|
|
@@ -191,29 +125,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
191
125
|
case 'ucm_service_info':
|
|
192
126
|
result = await client.getService(args.service_id);
|
|
193
127
|
break;
|
|
194
|
-
case 'ucm_register':
|
|
195
|
-
result = await client.register(args.name, args.description);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
case 'ucm_delete_service':
|
|
201
|
-
result = await client.deleteService(args.service_id);
|
|
202
|
-
break;
|
|
203
|
-
case 'ucm_authorize':
|
|
204
|
-
result = await client.authorize({
|
|
205
|
-
agent: args.agent,
|
|
206
|
-
rules: args.rules,
|
|
207
|
-
operator: args.operator,
|
|
208
|
-
acp_version: '0.1',
|
|
209
|
-
type: 'budget_authorization',
|
|
210
|
-
created_at: new Date().toISOString(),
|
|
211
|
-
signature: 'mcp-tool',
|
|
212
|
-
});
|
|
213
|
-
break;
|
|
214
|
-
case 'ucm_verify_token':
|
|
215
|
-
result = await client.verifyToken(args.token);
|
|
128
|
+
case 'ucm_register': {
|
|
129
|
+
result = await client.register(args.name, args.description, args.ref);
|
|
130
|
+
const apiKey = result.api_key;
|
|
131
|
+
if (apiKey) {
|
|
132
|
+
client.setApiKey(apiKey);
|
|
133
|
+
}
|
|
216
134
|
break;
|
|
135
|
+
}
|
|
217
136
|
case 'ucm_list_services':
|
|
218
137
|
result = await client.listServices({
|
|
219
138
|
since: args.since,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ucm/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP server for UCM — Agent-Native API Marketplace. Gives AI agents access to discover, purchase, and use API services.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,8 +39,7 @@
|
|
|
39
39
|
"prepublishOnly": "npm run build"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
43
|
-
"@noble/ed25519": "^2.2.0"
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
44
43
|
},
|
|
45
44
|
"devDependencies": {
|
|
46
45
|
"tsx": "^4.19.0",
|