@tethral/acr-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 +43 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +28 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/state.d.ts +8 -0
- package/dist/src/state.js +53 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/tools/check-entity.d.ts +2 -0
- package/dist/src/tools/check-entity.js +77 -0
- package/dist/src/tools/check-entity.js.map +1 -0
- package/dist/src/tools/check-environment.d.ts +2 -0
- package/dist/src/tools/check-environment.js +28 -0
- package/dist/src/tools/check-environment.js.map +1 -0
- package/dist/src/tools/get-friction-report.d.ts +2 -0
- package/dist/src/tools/get-friction-report.js +49 -0
- package/dist/src/tools/get-friction-report.js.map +1 -0
- package/dist/src/tools/log-interaction.d.ts +2 -0
- package/dist/src/tools/log-interaction.js +68 -0
- package/dist/src/tools/log-interaction.js.map +1 -0
- package/dist/src/tools/register-agent.d.ts +2 -0
- package/dist/src/tools/register-agent.js +51 -0
- package/dist/src/tools/register-agent.js.map +1 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tethral, Inc.
|
|
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,43 @@
|
|
|
1
|
+
# @tethral/acr-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for the [ACR](https://acr.nfkey.ai) (Agent Composition Records) network. Check skills before installing, log interactions, and get friction reports showing what's costing your agent the most.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"acr": {
|
|
11
|
+
"command": "npx",
|
|
12
|
+
"args": ["@tethral/acr-mcp"]
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
That's it. The server auto-registers your agent on first use.
|
|
19
|
+
|
|
20
|
+
## Tools
|
|
21
|
+
|
|
22
|
+
| Tool | Description | Required Params |
|
|
23
|
+
|------|-------------|----------------|
|
|
24
|
+
| `log_interaction` | Log an external interaction | `target_system_id`, `category`, `status` |
|
|
25
|
+
| `check_entity` | Check if a skill/agent/system is known | `entity_type`, `entity_id` |
|
|
26
|
+
| `get_friction_report` | See what's costing you the most | (none — uses auto-assigned ID) |
|
|
27
|
+
| `check_environment` | Get network threat overview | (none) |
|
|
28
|
+
| `register_agent` | Custom registration (optional) | `public_key`, `provider_class` |
|
|
29
|
+
|
|
30
|
+
## Configuration
|
|
31
|
+
|
|
32
|
+
| Env Var | Default | Description |
|
|
33
|
+
|---------|---------|-------------|
|
|
34
|
+
| `ACR_API_URL` | `https://acr.nfkey.ai` | Ingestion API URL |
|
|
35
|
+
| `ACR_RESOLVER_URL` | Same as API URL | Resolver API URL |
|
|
36
|
+
|
|
37
|
+
## Data Collection
|
|
38
|
+
|
|
39
|
+
ACR collects interaction metadata only: target system names, timing, status, and provider class. No request/response content, API keys, prompts, or PII is collected. [Full terms](https://acr.nfkey.ai/terms).
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { registerAgentTool } from './tools/register-agent.js';
|
|
5
|
+
import { logInteractionTool } from './tools/log-interaction.js';
|
|
6
|
+
import { checkEntityTool } from './tools/check-entity.js';
|
|
7
|
+
import { checkEnvironmentTool } from './tools/check-environment.js';
|
|
8
|
+
import { getFrictionReportTool } from './tools/get-friction-report.js';
|
|
9
|
+
const ACR_API_URL = process.env.ACR_API_URL ?? 'https://acr.nfkey.ai';
|
|
10
|
+
const ACR_RESOLVER_URL = process.env.ACR_RESOLVER_URL ?? ACR_API_URL;
|
|
11
|
+
const server = new McpServer({
|
|
12
|
+
name: 'acr-agent-registry',
|
|
13
|
+
version: '0.1.0',
|
|
14
|
+
});
|
|
15
|
+
registerAgentTool(server, ACR_API_URL);
|
|
16
|
+
logInteractionTool(server, ACR_API_URL);
|
|
17
|
+
checkEntityTool(server, ACR_API_URL, ACR_RESOLVER_URL);
|
|
18
|
+
checkEnvironmentTool(server, ACR_API_URL, ACR_RESOLVER_URL);
|
|
19
|
+
getFrictionReportTool(server, ACR_API_URL);
|
|
20
|
+
async function main() {
|
|
21
|
+
const transport = new StdioServerTransport();
|
|
22
|
+
await server.connect(transport);
|
|
23
|
+
}
|
|
24
|
+
main().catch((err) => {
|
|
25
|
+
console.error('ACR MCP server failed to start:', err);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,sBAAsB,CAAC;AACtE,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,WAAW,CAAC;AAErE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,oBAAoB;IAC1B,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,iBAAiB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACvC,kBAAkB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AACxC,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC;AACvD,oBAAoB,CAAC,MAAM,EAAE,WAAW,EAAE,gBAAgB,CAAC,CAAC;AAC5D,qBAAqB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE3C,KAAK,UAAU,IAAI;IACjB,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,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function getAgentId(): string | null;
|
|
2
|
+
export declare function setAgentId(id: string): void;
|
|
3
|
+
/**
|
|
4
|
+
* Ensure the agent is registered. Called before any tool that needs an agent_id.
|
|
5
|
+
* If not registered, auto-registers with a pseudo_ ID.
|
|
6
|
+
* Returns the agent_id.
|
|
7
|
+
*/
|
|
8
|
+
export declare function ensureRegistered(): Promise<string>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP server session state.
|
|
3
|
+
* Handles auto-registration so agents don't need to manually call register_agent.
|
|
4
|
+
*/
|
|
5
|
+
import { randomBytes } from 'node:crypto';
|
|
6
|
+
let agentId = null;
|
|
7
|
+
let registering = false;
|
|
8
|
+
const ACR_API_URL = process.env.ACR_API_URL ?? 'https://acr.nfkey.ai';
|
|
9
|
+
export function getAgentId() {
|
|
10
|
+
return agentId;
|
|
11
|
+
}
|
|
12
|
+
export function setAgentId(id) {
|
|
13
|
+
agentId = id;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Ensure the agent is registered. Called before any tool that needs an agent_id.
|
|
17
|
+
* If not registered, auto-registers with a pseudo_ ID.
|
|
18
|
+
* Returns the agent_id.
|
|
19
|
+
*/
|
|
20
|
+
export async function ensureRegistered() {
|
|
21
|
+
if (agentId)
|
|
22
|
+
return agentId;
|
|
23
|
+
if (registering) {
|
|
24
|
+
// Another call is already registering — wait briefly
|
|
25
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
26
|
+
if (agentId)
|
|
27
|
+
return agentId;
|
|
28
|
+
}
|
|
29
|
+
registering = true;
|
|
30
|
+
try {
|
|
31
|
+
const pseudoKey = `pseudo_${randomBytes(16).toString('hex')}`;
|
|
32
|
+
const res = await fetch(`${ACR_API_URL}/api/v1/register`, {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({
|
|
36
|
+
public_key: pseudoKey,
|
|
37
|
+
provider_class: 'unknown',
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
if (res.ok) {
|
|
41
|
+
const data = await res.json();
|
|
42
|
+
agentId = data.agent_id;
|
|
43
|
+
return agentId;
|
|
44
|
+
}
|
|
45
|
+
// Registration failed — use pseudo ID locally
|
|
46
|
+
agentId = `pseudo_${randomBytes(6).toString('hex')}`;
|
|
47
|
+
return agentId;
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
registering = false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,IAAI,OAAO,GAAkB,IAAI,CAAC;AAClC,IAAI,WAAW,GAAG,KAAK,CAAC;AAExB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,sBAAsB,CAAC;AAEtE,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,OAAO,GAAG,EAAE,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAC5B,IAAI,WAAW,EAAE,CAAC;QAChB,qDAAqD;QACrD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC9C,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC9B,CAAC;IAED,WAAW,GAAG,IAAI,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,UAAU,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAE9D,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,WAAW,kBAAkB,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,UAAU,EAAE,SAAS;gBACrB,cAAc,EAAE,SAAS;aAC1B,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAA0B,CAAC;YACtD,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;YACxB,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,8CAA8C;QAC9C,OAAO,GAAG,UAAU,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,OAAO,CAAC;IACjB,CAAC;YAAS,CAAC;QACT,WAAW,GAAG,KAAK,CAAC;IACtB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function checkEntityTool(server, apiUrl, resolverUrl) {
|
|
3
|
+
server.tool('check_entity', 'Check if a skill hash, agent, or system is known to the ACR network. Use before installing skills to verify safety. This is a read-only lookup — no data is sent to ACR.', {
|
|
4
|
+
entity_type: z.enum(['skill', 'agent', 'system']).describe('Type of entity to look up'),
|
|
5
|
+
entity_id: z.string().describe('The entity identifier: skill SHA-256 hash, agent_id, or system_id'),
|
|
6
|
+
}, async ({ entity_type, entity_id }) => {
|
|
7
|
+
try {
|
|
8
|
+
let url;
|
|
9
|
+
switch (entity_type) {
|
|
10
|
+
case 'skill':
|
|
11
|
+
url = `${resolverUrl}/v1/skill/${entity_id}`;
|
|
12
|
+
break;
|
|
13
|
+
case 'agent':
|
|
14
|
+
url = `${resolverUrl}/v1/agent/${entity_id}`;
|
|
15
|
+
break;
|
|
16
|
+
case 'system':
|
|
17
|
+
url = `${resolverUrl}/v1/system/${encodeURIComponent(entity_id)}/health`;
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
const res = await fetch(url);
|
|
21
|
+
const data = await res.json();
|
|
22
|
+
if (entity_type === 'skill') {
|
|
23
|
+
if (!data.found) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: `Unknown skill. This hash has not been seen in the ACR network. Exercise caution with unfamiliar skills.`,
|
|
28
|
+
}],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const level = (data.threat_level ?? 'none').toUpperCase();
|
|
32
|
+
let text = `Skill found.\n\nThreat Level: ${level}`;
|
|
33
|
+
if (data.skill_name)
|
|
34
|
+
text += `\nName: ${data.skill_name}`;
|
|
35
|
+
if (data.agent_count != null)
|
|
36
|
+
text += `\nAgents using: ${data.agent_count}`;
|
|
37
|
+
if (data.interaction_count != null)
|
|
38
|
+
text += `\nInteractions: ${data.interaction_count}`;
|
|
39
|
+
if (data.anomaly_rate != null)
|
|
40
|
+
text += `\nAnomaly rate: ${(data.anomaly_rate * 100).toFixed(1)}%`;
|
|
41
|
+
if (data.threat_level === 'high' || data.threat_level === 'critical') {
|
|
42
|
+
text += `\n\nWARNING: This skill has been flagged. Do not install without explicit user confirmation.`;
|
|
43
|
+
}
|
|
44
|
+
else if (data.threat_level === 'medium') {
|
|
45
|
+
text += `\n\nCaution: Elevated anomaly signals. Proceed only if the user confirms.`;
|
|
46
|
+
}
|
|
47
|
+
return { content: [{ type: 'text', text }] };
|
|
48
|
+
}
|
|
49
|
+
if (entity_type === 'agent') {
|
|
50
|
+
if (!data.found) {
|
|
51
|
+
return { content: [{ type: 'text', text: `Agent ${entity_id} not found in the network.` }] };
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
content: [{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: `Agent found.\n\nStatus: ${data.status}\nProvider: ${data.provider_class}\nRegistered: ${data.registered}\nLast active: ${data.last_active}`,
|
|
57
|
+
}],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// system
|
|
61
|
+
if (!data.found) {
|
|
62
|
+
return { content: [{ type: 'text', text: `System ${entity_id} not found.` }] };
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
content: [{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: `System found.\n\nHealth: ${data.health_status}\nType: ${data.system_type}\nTotal interactions: ${data.total_interactions}\nDistinct agents: ${data.distinct_agents}\nAnomaly rate: ${((data.anomaly_rate ?? 0) * 100).toFixed(1)}%`,
|
|
68
|
+
}],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
73
|
+
return { content: [{ type: 'text', text: `Lookup error: ${msg}` }] };
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=check-entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-entity.js","sourceRoot":"","sources":["../../../src/tools/check-entity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function checkEnvironmentTool(server, apiUrl, resolverUrl) {
|
|
2
|
+
server.tool('check_environment', 'Check the current ACR network environment: active threats and system health. Call on startup or when assessing risk.', {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const [threatsRes, healthRes] = await Promise.all([
|
|
5
|
+
fetch(`${resolverUrl}/v1/threats/active`),
|
|
6
|
+
fetch(`${apiUrl}/api/v1/health`),
|
|
7
|
+
]);
|
|
8
|
+
const threats = await threatsRes.json();
|
|
9
|
+
const health = await healthRes.json();
|
|
10
|
+
let text = `ACR Network Status: ${health.status ?? 'unknown'}\n`;
|
|
11
|
+
if (Array.isArray(threats) && threats.length > 0) {
|
|
12
|
+
text += `\nActive Threats: ${threats.length}\n`;
|
|
13
|
+
for (const t of threats) {
|
|
14
|
+
text += `- [${t.threat_level.toUpperCase()}] ${t.skill_name || t.skill_hash.substring(0, 16) + '...'} (${t.anomaly_signal_count} signals)\n`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
text += '\nNo active threats detected.';
|
|
19
|
+
}
|
|
20
|
+
return { content: [{ type: 'text', text }] };
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
24
|
+
return { content: [{ type: 'text', text: `Environment check error: ${msg}` }] };
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=check-environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-environment.js","sourceRoot":"","sources":["../../../src/tools/check-environment.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAc,EAAE,WAAmB;IACzF,MAAM,CAAC,IAAI,CACT,mBAAmB,EACnB,sHAAsH,EACtH,EAAE,EACF,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBAChD,KAAK,CAAC,GAAG,WAAW,oBAAoB,CAAC;gBACzC,KAAK,CAAC,GAAG,MAAM,gBAAgB,CAAC;aACjC,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;YAEtC,IAAI,IAAI,GAAG,uBAAuB,MAAM,CAAC,MAAM,IAAI,SAAS,IAAI,CAAC;YAEjE,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACjD,IAAI,IAAI,qBAAqB,OAAO,CAAC,MAAM,IAAI,CAAC;gBAChD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;oBACxB,IAAI,IAAI,MAAM,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,oBAAoB,aAAa,CAAC;gBAC/I,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,IAAI,+BAA+B,CAAC;YAC1C,CAAC;YAED,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACxD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACjE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,4BAA4B,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC;QAC3F,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ensureRegistered, getAgentId } from '../state.js';
|
|
3
|
+
export function getFrictionReportTool(server, apiUrl) {
|
|
4
|
+
server.tool('get_friction_report', "Get a friction analysis report showing what's costing this agent the most time and money. Shows which external systems are the biggest bottlenecks. This is a read-only query of your own data.", {
|
|
5
|
+
agent_id: z.string().optional().describe('Your ACR agent ID (auto-assigned if omitted)'),
|
|
6
|
+
scope: z.enum(['session', 'day', 'week']).optional().default('day').describe('Time window for the report'),
|
|
7
|
+
}, async ({ agent_id, scope }) => {
|
|
8
|
+
const id = agent_id || getAgentId() || await ensureRegistered();
|
|
9
|
+
try {
|
|
10
|
+
const res = await fetch(`${apiUrl}/api/v1/agent/${id}/friction?scope=${scope}`);
|
|
11
|
+
const data = await res.json();
|
|
12
|
+
if (data.error) {
|
|
13
|
+
return { content: [{ type: 'text', text: `Error: ${data.error.message}` }] };
|
|
14
|
+
}
|
|
15
|
+
const s = data.summary;
|
|
16
|
+
if (s.total_interactions === 0) {
|
|
17
|
+
return {
|
|
18
|
+
content: [{
|
|
19
|
+
type: 'text',
|
|
20
|
+
text: `No interactions recorded for scope "${scope}". Start logging interactions to see friction data.`,
|
|
21
|
+
}],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
let text = `Friction Report (${scope})\n`;
|
|
25
|
+
text += `Period: ${data.period_start} to ${data.period_end}\n\n`;
|
|
26
|
+
text += `Total interactions: ${s.total_interactions}\n`;
|
|
27
|
+
text += `Total wait time: ${(s.total_wait_time_ms / 1000).toFixed(1)}s\n`;
|
|
28
|
+
text += `Friction: ${s.friction_percentage.toFixed(2)}% of active time spent waiting\n`;
|
|
29
|
+
text += `Failures: ${s.total_failures} (${(s.failure_rate * 100).toFixed(1)}% failure rate)\n`;
|
|
30
|
+
if (data.top_targets && data.top_targets.length > 0) {
|
|
31
|
+
text += `\nTop Bottlenecks:\n`;
|
|
32
|
+
for (const t of data.top_targets) {
|
|
33
|
+
const pct = (t.proportion_of_total * 100).toFixed(1);
|
|
34
|
+
text += `\n ${t.target_system_id}\n`;
|
|
35
|
+
text += ` ${pct}% of wait time | ${t.interaction_count} calls | median ${t.median_duration_ms}ms\n`;
|
|
36
|
+
if (t.failure_count > 0) {
|
|
37
|
+
text += ` ${t.failure_count} failures\n`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { content: [{ type: 'text', text }] };
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
45
|
+
return { content: [{ type: 'text', text: `Friction report error: ${msg}` }] };
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=get-friction-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-friction-report.js","sourceRoot":"","sources":["../../../src/tools/get-friction-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ensureRegistered, getAgentId } from '../state.js';
|
|
3
|
+
function inferSystemType(systemId) {
|
|
4
|
+
const prefix = systemId.split(':')[0];
|
|
5
|
+
const map = {
|
|
6
|
+
mcp: 'mcp_server', api: 'api', agent: 'agent',
|
|
7
|
+
skill: 'skill', platform: 'platform',
|
|
8
|
+
};
|
|
9
|
+
return map[prefix ?? ''] ?? 'unknown';
|
|
10
|
+
}
|
|
11
|
+
const DATA_NOTICE = ' ACR collects interaction metadata (target names, timing, status) for threat detection and friction analysis. No request/response content is collected. Terms: https://acr.nfkey.ai/terms';
|
|
12
|
+
export function logInteractionTool(server, apiUrl) {
|
|
13
|
+
server.tool('log_interaction', 'Log an interaction receipt to the ACR network. Call after interacting with any external tool, API, or service.' + DATA_NOTICE, {
|
|
14
|
+
target_system_id: z.string().describe('Target in type:name format (e.g., mcp:github, api:stripe.com)'),
|
|
15
|
+
category: z.enum([
|
|
16
|
+
'tool_call', 'delegation', 'data_exchange', 'skill_install',
|
|
17
|
+
'commerce', 'research', 'code', 'communication',
|
|
18
|
+
]).describe('Interaction category'),
|
|
19
|
+
status: z.enum(['success', 'failure', 'timeout', 'partial']).describe('Outcome'),
|
|
20
|
+
duration_ms: z.number().nonnegative().optional().default(0).describe('Duration in ms (0 if unknown)'),
|
|
21
|
+
agent_id: z.string().optional().describe('Your ACR agent ID (auto-assigned if omitted)'),
|
|
22
|
+
anomaly_flagged: z.boolean().optional().default(false).describe('Set true if something seemed wrong'),
|
|
23
|
+
anomaly_detail: z.string().max(500).optional().describe('What seemed wrong. DO NOT include credentials or API keys.'),
|
|
24
|
+
}, async (params) => {
|
|
25
|
+
try {
|
|
26
|
+
const id = params.agent_id || getAgentId() || await ensureRegistered();
|
|
27
|
+
const res = await fetch(`${apiUrl}/api/v1/receipts`, {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
headers: { 'Content-Type': 'application/json' },
|
|
30
|
+
body: JSON.stringify({
|
|
31
|
+
emitter: {
|
|
32
|
+
agent_id: id,
|
|
33
|
+
provider_class: 'unknown',
|
|
34
|
+
},
|
|
35
|
+
target: {
|
|
36
|
+
system_id: params.target_system_id,
|
|
37
|
+
system_type: inferSystemType(params.target_system_id),
|
|
38
|
+
},
|
|
39
|
+
interaction: {
|
|
40
|
+
category: params.category,
|
|
41
|
+
status: params.status,
|
|
42
|
+
duration_ms: params.duration_ms,
|
|
43
|
+
request_timestamp_ms: Date.now() - (params.duration_ms ?? 0),
|
|
44
|
+
},
|
|
45
|
+
anomaly: {
|
|
46
|
+
flagged: params.anomaly_flagged,
|
|
47
|
+
detail: params.anomaly_detail,
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
const data = await res.json();
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
return { content: [{ type: 'text', text: `Failed to log: ${JSON.stringify(data)}` }] };
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
content: [{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: `Logged ${data.accepted} receipt(s). IDs: ${data.receipt_ids.join(', ')}`,
|
|
59
|
+
}],
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
64
|
+
return { content: [{ type: 'text', text: `Logging error: ${msg}` }] };
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=log-interaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-interaction.js","sourceRoot":"","sources":["../../../src/tools/log-interaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { setAgentId } from '../state.js';
|
|
3
|
+
const DATA_NOTICE = ' ACR collects interaction metadata (target names, timing, status) for threat detection and friction analysis. No request/response content is collected. Terms: https://acr.nfkey.ai/terms';
|
|
4
|
+
export function registerAgentTool(server, apiUrl) {
|
|
5
|
+
server.tool('register_agent', 'Register an agent with the ACR network. Optional — agents are auto-registered on first tool call.' + DATA_NOTICE, {
|
|
6
|
+
public_key: z.string().min(32).describe('Agent public key or unique identifier (min 32 chars)'),
|
|
7
|
+
provider_class: z.enum([
|
|
8
|
+
'anthropic', 'openai', 'google', 'openclaw', 'langchain',
|
|
9
|
+
'crewai', 'autogen', 'custom', 'unknown',
|
|
10
|
+
]).describe('Agent provider/framework'),
|
|
11
|
+
skills: z.array(z.string()).optional().describe('List of installed skill names'),
|
|
12
|
+
skill_hashes: z.array(z.string()).optional().describe('SHA-256 hashes of installed SKILL.md files'),
|
|
13
|
+
operational_domain: z.string().max(200).optional().describe('What domain this agent operates in'),
|
|
14
|
+
}, async ({ public_key, provider_class, skills, skill_hashes, operational_domain }) => {
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch(`${apiUrl}/api/v1/register`, {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
body: JSON.stringify({
|
|
20
|
+
public_key,
|
|
21
|
+
provider_class,
|
|
22
|
+
composition: (skills || skill_hashes) ? { skills, skill_hashes } : undefined,
|
|
23
|
+
operational_domain,
|
|
24
|
+
}),
|
|
25
|
+
});
|
|
26
|
+
const data = await res.json();
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
return { content: [{ type: 'text', text: `Registration failed: ${JSON.stringify(data)}` }] };
|
|
29
|
+
}
|
|
30
|
+
// Store agent_id for auto-use in other tools
|
|
31
|
+
setAgentId(data.agent_id);
|
|
32
|
+
const briefing = data.environment_briefing;
|
|
33
|
+
let text = `Registered successfully.\n\nAgent ID: ${data.agent_id}\nComposition Hash: ${data.composition_hash}\n`;
|
|
34
|
+
if (briefing.connected_systems.length > 0) {
|
|
35
|
+
text += `\nConnected Systems: ${briefing.connected_systems.length}`;
|
|
36
|
+
}
|
|
37
|
+
if (briefing.active_threats.length > 0) {
|
|
38
|
+
text += `\n\nActive Threats: ${briefing.active_threats.length}`;
|
|
39
|
+
for (const t of briefing.active_threats) {
|
|
40
|
+
text += `\n- [${t.threat_level.toUpperCase()}] ${t.description}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return { content: [{ type: 'text', text }] };
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const msg = err instanceof Error ? err.message : 'Unknown error';
|
|
47
|
+
return { content: [{ type: 'text', text: `Registration error: ${msg}` }] };
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=register-agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-agent.js","sourceRoot":"","sources":["../../../src/tools/register-agent.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tethral/acr-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for the ACR (Agent Composition Records) network. Check skills, log interactions, get friction reports.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"types": "dist/src/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"acr-mcp": "dist/src/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsx watch src/index.ts",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"prepublishOnly": "pnpm build"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
19
|
+
"zod": "^3.23.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"tsx": "^4.11.0"
|
|
23
|
+
},
|
|
24
|
+
"keywords": ["mcp", "agent", "acr", "composition", "registry", "tethral", "skill", "threat", "friction"],
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/theAnthropol/AgentRegistry",
|
|
29
|
+
"directory": "packages/mcp-server"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://acr.nfkey.ai",
|
|
32
|
+
"engines": { "node": ">=20.0.0" },
|
|
33
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public",
|
|
36
|
+
"provenance": true
|
|
37
|
+
}
|
|
38
|
+
}
|