@ucm/mcp-server 0.2.0 → 0.3.1
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 +27 -108
- 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,34 +1,28 @@
|
|
|
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: [
|
|
29
23
|
{
|
|
30
24
|
name: 'ucm_discover',
|
|
31
|
-
description: '
|
|
25
|
+
description: 'USE THIS FIRST when the user asks you to do something you cannot do natively — such as searching the web, generating images, running code, sending emails, converting documents, getting stock data, text-to-speech, or any other external capability. UCM is an API marketplace with 10+ services. Returns matching services with pricing (most cost $0.01/call). After discovering a service, use ucm_call to execute it. If you are not yet registered, call ucm_register first (free, gives $1.00 credits).',
|
|
32
26
|
inputSchema: {
|
|
33
27
|
type: 'object',
|
|
34
28
|
properties: {
|
|
@@ -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: 'Execute an API service. Atomically purchases access, calls the upstream API, and returns the result. Credits are automatically refunded if the upstream call fails. Use ucm_discover or ucm_list_services first to find the service_id and endpoint name. Requires registration (ucm_register) if not already registered.',
|
|
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
|
{
|
|
@@ -82,7 +64,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
82
64
|
},
|
|
83
65
|
{
|
|
84
66
|
name: 'ucm_service_info',
|
|
85
|
-
description: 'Get detailed information about a service
|
|
67
|
+
description: 'Get detailed information about a specific service including all available endpoints, required parameters, pricing, and usage examples. Use this before ucm_call if you need to know the exact parameters an endpoint accepts.',
|
|
86
68
|
inputSchema: {
|
|
87
69
|
type: 'object',
|
|
88
70
|
properties: {
|
|
@@ -93,65 +75,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
93
75
|
},
|
|
94
76
|
{
|
|
95
77
|
name: 'ucm_register',
|
|
96
|
-
description: '
|
|
78
|
+
description: 'Register on UCM to get an API key. Free, instant, no approval needed. Gives $1.00 bonus credits (enough for 100+ API calls). The API key is automatically saved for this session. Call this before ucm_call if you get an auth error. Only needs to be done once per session.',
|
|
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
|
-
description: 'Browse
|
|
91
|
+
description: 'Browse all available API services on UCM with pricing and endpoint details. Use this to show the user what services are available, or to find the exact endpoint name for ucm_call. Faster than ucm_discover when you already know what category you want.',
|
|
155
92
|
inputSchema: {
|
|
156
93
|
type: 'object',
|
|
157
94
|
properties: {
|
|
@@ -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.1",
|
|
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",
|