@agentmanifest/mcp-server 0.0.0-stage → 0.1.2
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 +212 -2
- package/dist/create-server.d.ts +3 -0
- package/dist/create-server.d.ts.map +1 -0
- package/dist/create-server.js +95 -0
- package/dist/create-server.js.map +1 -0
- package/dist/http.d.ts +3 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +74 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/__tests__/check-permissions.test.d.ts +2 -0
- package/dist/lib/__tests__/check-permissions.test.d.ts.map +1 -0
- package/dist/lib/__tests__/check-permissions.test.js +29 -0
- package/dist/lib/__tests__/check-permissions.test.js.map +1 -0
- package/dist/lib/check-permissions.d.ts +13 -0
- package/dist/lib/check-permissions.d.ts.map +1 -0
- package/dist/lib/check-permissions.js +188 -0
- package/dist/lib/check-permissions.js.map +1 -0
- package/dist/lib/fetch-manifest.d.ts +5 -0
- package/dist/lib/fetch-manifest.d.ts.map +1 -0
- package/dist/lib/fetch-manifest.js +74 -0
- package/dist/lib/fetch-manifest.js.map +1 -0
- package/dist/lib/resolve-manifest-url.d.ts +5 -0
- package/dist/lib/resolve-manifest-url.d.ts.map +1 -0
- package/dist/lib/resolve-manifest-url.js +8 -0
- package/dist/lib/resolve-manifest-url.js.map +1 -0
- package/dist/lib/schemas.d.ts +3 -0
- package/dist/lib/schemas.d.ts.map +1 -0
- package/dist/lib/schemas.js +8 -0
- package/dist/lib/schemas.js.map +1 -0
- package/dist/lib/types.d.ts +84 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/validate-syntax.d.ts +3 -0
- package/dist/lib/validate-syntax.d.ts.map +1 -0
- package/dist/lib/validate-syntax.js +52 -0
- package/dist/lib/validate-syntax.js.map +1 -0
- package/package.json +62 -4
- package/schemas/v0.2/manifest.json +290 -0
- package/schemas/v0.3/manifest.json +488 -0
- package/smithery.yaml +31 -0
package/README.md
CHANGED
|
@@ -1,3 +1,213 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Agent Manifest Protocol (AMP) — Official MCP Server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://modelcontextprotocol.io)
|
|
4
|
+
[](https://agent-manifest.com)
|
|
5
|
+
|
|
6
|
+
Turn AMP’s **static web discovery** (`/.well-known/agent-manifest.json`) into **runtime MCP tools** so any LLM client (Claude Desktop, Cursor, Smithery, etc.) can discover APIs, validate manifests, and reason about legal/operational boundaries before calling them.
|
|
7
|
+
|
|
8
|
+
## How AMP complements MCP
|
|
9
|
+
|
|
10
|
+
| Layer | Role |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| **MCP** | Transport and tool surface between the model and your server |
|
|
13
|
+
| **AMP** | Publisher-hosted manifest describing endpoints, auth, pricing/payment, rate limits, and `agent_notes` for autonomous agents |
|
|
14
|
+
|
|
15
|
+
This server does **not** replace calling third-party APIs. It helps agents **decide whether and how** to call them in line with the publisher’s declared contract.
|
|
16
|
+
|
|
17
|
+
## Tools
|
|
18
|
+
|
|
19
|
+
### `fetch_manifest_by_domain`
|
|
20
|
+
|
|
21
|
+
Fetches and parses a manifest from an API origin.
|
|
22
|
+
|
|
23
|
+
**Input**
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"domain": "bakebase.agent-manifest.com"
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Behavior**
|
|
32
|
+
|
|
33
|
+
1. Normalizes the host (strips scheme/path).
|
|
34
|
+
2. Tries, in order:
|
|
35
|
+
- `/.well-known/agent-manifest.json` (canonical, RFC 8615)
|
|
36
|
+
- `/.well-known/amp.json`
|
|
37
|
+
- `/agent-manifest.json`
|
|
38
|
+
3. Returns `manifest`, `manifest_url`, `origin`, and `tried_urls`.
|
|
39
|
+
|
|
40
|
+
### `validate_manifest_syntax`
|
|
41
|
+
|
|
42
|
+
Validates JSON against the official AMP schema for `agentmanifest-0.2` or `agentmanifest-0.3`.
|
|
43
|
+
|
|
44
|
+
**Input (object)**
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"manifest": {
|
|
49
|
+
"spec_version": "agentmanifest-0.3",
|
|
50
|
+
"name": "My API",
|
|
51
|
+
"...": "..."
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Input (string)**
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"manifest": "{\"spec_version\":\"agentmanifest-0.3\",...}"
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Output**
|
|
65
|
+
|
|
66
|
+
- `valid: true` → `success_token` like `amp-schema-valid:agentmanifest-0.3:My API`
|
|
67
|
+
- `valid: false` → `errors[]` with AJV paths/messages
|
|
68
|
+
|
|
69
|
+
### `check_agent_permissions`
|
|
70
|
+
|
|
71
|
+
Checks an intended HTTP operation against a manifest’s declared endpoints and constraints.
|
|
72
|
+
|
|
73
|
+
**Input**
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"action": "POST /baking/validate-mix",
|
|
78
|
+
"manifest": { "...": "full manifest object from fetch_manifest_by_domain" },
|
|
79
|
+
"intends_unauthenticated": false,
|
|
80
|
+
"payment_onboarded": false
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Output**
|
|
85
|
+
|
|
86
|
+
- `verdict`: `compliant` | `violation` | `warning` | `unknown`
|
|
87
|
+
- `findings[]`: structured codes (`endpoint_not_declared`, `auth_required`, `payment_onboarding`, `agent_notes_usage_restriction`, …)
|
|
88
|
+
- `matched_endpoint` when a declared route matches (including `{id}` templates)
|
|
89
|
+
|
|
90
|
+
## Install & run locally
|
|
91
|
+
|
|
92
|
+
### Prerequisites
|
|
93
|
+
|
|
94
|
+
- Node.js **18+**
|
|
95
|
+
- Build the validator dependency once (local monorepo path):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
cd agentmanifest/amp-mcp && npm install && npm run build
|
|
99
|
+
# or from monorepo root:
|
|
100
|
+
cd agentmanifest && npm install && npm run build --workspace=amp-mcp
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Stdio (Cursor, Claude Desktop, CI)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npx @agentmanifest/mcp-server
|
|
107
|
+
# or after linking locally:
|
|
108
|
+
node dist/index.js
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### SSE HTTP hub (remote / agent-manifest.com)
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
PORT=8787 HOST=0.0.0.0 npm run start
|
|
115
|
+
# GET http://localhost:8787/mcp — establish SSE stream
|
|
116
|
+
# POST http://localhost:8787/messages?sessionId=... — JSON-RPC from client
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Environment variables:
|
|
120
|
+
|
|
121
|
+
| Variable | Default | Purpose |
|
|
122
|
+
| --- | --- | --- |
|
|
123
|
+
| `PORT` | `8787` | HTTP listen port |
|
|
124
|
+
| `HOST` | `0.0.0.0` | Bind address |
|
|
125
|
+
| `AMP_MCP_SSE_PATH` | `/mcp` | SSE endpoint |
|
|
126
|
+
| `AMP_MCP_MESSAGES_PATH` | `/messages` | POST endpoint |
|
|
127
|
+
| `AMP_ALLOWED_HOSTS` | `agent-manifest.com,...` | Host header allow list |
|
|
128
|
+
| `AMP_FETCH_TIMEOUT_MS` | `15000` | Manifest fetch timeout |
|
|
129
|
+
|
|
130
|
+
> **Note:** The MCP SDK marks legacy **HTTP+SSE** as deprecated in favor of Streamable HTTP. This package implements **SSE** as requested for broad client compatibility; you can add Streamable HTTP alongside later using `StreamableHTTPServerTransport`.
|
|
131
|
+
|
|
132
|
+
## Claude Desktop configuration
|
|
133
|
+
|
|
134
|
+
Add to `claude_desktop_config.json` (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"mcpServers": {
|
|
139
|
+
"agent-manifest-protocol": {
|
|
140
|
+
"command": "node",
|
|
141
|
+
"args": ["/absolute/path/to/AMP/amp-mcp/dist/index.js"],
|
|
142
|
+
"env": {
|
|
143
|
+
"AMP_FETCH_TIMEOUT_MS": "15000"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
After publishing to npm, replace with:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"mcpServers": {
|
|
155
|
+
"agent-manifest-protocol": {
|
|
156
|
+
"command": "npx",
|
|
157
|
+
"args": ["-y", "@agentmanifest/mcp-server"]
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Cursor
|
|
164
|
+
|
|
165
|
+
**Settings → MCP → Add server → Command:**
|
|
166
|
+
|
|
167
|
+
- Command: `node`
|
|
168
|
+
- Args: `/path/to/amp-mcp/dist/index.js`
|
|
169
|
+
|
|
170
|
+
Or use `npx -y @agentmanifest/mcp-server` once published.
|
|
171
|
+
|
|
172
|
+
## Smithery
|
|
173
|
+
|
|
174
|
+
This repo includes [`smithery.yaml`](./smithery.yaml) for one-command Smithery installs. Point Smithery at this package directory or published GitHub repo; optional env:
|
|
175
|
+
|
|
176
|
+
- `AMP_FETCH_TIMEOUT_MS`
|
|
177
|
+
- `AMP_VALIDATOR_URL` (reserved for future remote validation helpers)
|
|
178
|
+
|
|
179
|
+
## Registries (official MCP Registry, Smithery, npm)
|
|
180
|
+
|
|
181
|
+
Docs: **[REGISTRY.md](./REGISTRY.md)** and **[modelcontextprotocol.io/registry/about](https://modelcontextprotocol.io/registry/about)**.
|
|
182
|
+
|
|
183
|
+
The MCP Registry holds **metadata** (`server.json`); npm holds the package; Railway holds the remote URL. Host apps often discover you via **aggregators** that sync the registry — not by scraping your site.
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
brew install mcp-publisher # see registry quickstart
|
|
187
|
+
cd amp-mcp
|
|
188
|
+
# package.json mcpName must match server.json "name"
|
|
189
|
+
mcp-publisher login github # or DNS/HTTP for com.agent-manifest/*
|
|
190
|
+
mcp-publisher publish
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Development
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm run dev # SSE server with tsx watch
|
|
197
|
+
npm run dev:stdio # stdio server
|
|
198
|
+
npm test
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Architecture
|
|
202
|
+
|
|
203
|
+
```mermaid
|
|
204
|
+
flowchart LR
|
|
205
|
+
LLM[LLM Client] -->|stdio or SSE| MCP[AMP MCP Server]
|
|
206
|
+
MCP -->|HTTPS GET| Origin["API origin /.well-known/agent-manifest.json"]
|
|
207
|
+
MCP -->|AJV| Schema[AMP JSON Schema v0.2/v0.3]
|
|
208
|
+
MCP -->|rules engine| Perms[check_agent_permissions]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT © Agent Manifest Protocol
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-server.d.ts","sourceRoot":"","sources":["../src/create-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWpE,wBAAgB,kBAAkB,IAAI,SAAS,CAiH9C"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import * as z from 'zod';
|
|
3
|
+
import { fetchManifestByDomain } from './lib/fetch-manifest.js';
|
|
4
|
+
import { validateManifestSyntax } from './lib/validate-syntax.js';
|
|
5
|
+
import { checkAgentPermissions } from './lib/check-permissions.js';
|
|
6
|
+
const manifestObjectSchema = z
|
|
7
|
+
.record(z.unknown())
|
|
8
|
+
.describe('Parsed AMP manifest object (agentmanifest-0.2 or agentmanifest-0.3).');
|
|
9
|
+
export function createAmpMcpServer() {
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: 'agent-manifest-protocol',
|
|
12
|
+
version: '0.1.0',
|
|
13
|
+
}, {
|
|
14
|
+
instructions: [
|
|
15
|
+
'You are connected to the official Agent Manifest Protocol (AMP) MCP server.',
|
|
16
|
+
'Use fetch_manifest_by_domain to discover /.well-known/agent-manifest.json (and fallbacks) for any API origin.',
|
|
17
|
+
'Use validate_manifest_syntax before trusting manifest JSON.',
|
|
18
|
+
'Use check_agent_permissions before calling an API operation to see if it matches declared endpoints, auth, payment, and agent_notes constraints.',
|
|
19
|
+
'AMP complements MCP: MCP carries tools; AMP declares what remote agents/APIs allow, cost, and how to authenticate.',
|
|
20
|
+
].join(' '),
|
|
21
|
+
});
|
|
22
|
+
server.registerTool('fetch_manifest_by_domain', {
|
|
23
|
+
title: 'Fetch AMP manifest by domain',
|
|
24
|
+
description: 'Resolve an API origin from a domain (e.g. "bakebase.agent-manifest.com"), try standard AMP discovery URLs, and return parsed manifest JSON plus metadata.',
|
|
25
|
+
inputSchema: {
|
|
26
|
+
domain: z
|
|
27
|
+
.string()
|
|
28
|
+
.min(1)
|
|
29
|
+
.describe('API hostname or URL origin without a path, e.g. "example.com", "api.example.com", or "https://bakebase.agent-manifest.com".'),
|
|
30
|
+
},
|
|
31
|
+
}, async ({ domain }) => {
|
|
32
|
+
const result = await fetchManifestByDomain(domain);
|
|
33
|
+
return {
|
|
34
|
+
content: [
|
|
35
|
+
{
|
|
36
|
+
type: 'text',
|
|
37
|
+
text: JSON.stringify(result, null, 2),
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
server.registerTool('validate_manifest_syntax', {
|
|
43
|
+
title: 'Validate AMP manifest JSON Schema',
|
|
44
|
+
description: 'Validate a manifest JSON string or object against the official AMP JSON Schema for its spec_version (agentmanifest-0.2 or agentmanifest-0.3). Returns errors or a success_token.',
|
|
45
|
+
inputSchema: {
|
|
46
|
+
manifest: z
|
|
47
|
+
.union([z.string(), manifestObjectSchema])
|
|
48
|
+
.describe('Either a JSON string of the manifest, or a parsed manifest object with spec_version, name, endpoints, etc.'),
|
|
49
|
+
},
|
|
50
|
+
}, async ({ manifest }) => {
|
|
51
|
+
const result = validateManifestSyntax(manifest);
|
|
52
|
+
return {
|
|
53
|
+
content: [
|
|
54
|
+
{
|
|
55
|
+
type: 'text',
|
|
56
|
+
text: JSON.stringify(result, null, 2),
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
server.registerTool('check_agent_permissions', {
|
|
62
|
+
title: 'Check action against AMP manifest constraints',
|
|
63
|
+
description: 'Given an intended HTTP action (method + path or full URL) and a manifest, determine whether the action aligns with declared endpoints, authentication, payment onboarding, rate limits, and language in agent_notes.',
|
|
64
|
+
inputSchema: {
|
|
65
|
+
action: z
|
|
66
|
+
.string()
|
|
67
|
+
.min(1)
|
|
68
|
+
.describe('Intended operation, e.g. "POST /baking/validate-mix", "GET /health", or "POST https://api.example.com/v1/search".'),
|
|
69
|
+
manifest: manifestObjectSchema,
|
|
70
|
+
intends_unauthenticated: z
|
|
71
|
+
.boolean()
|
|
72
|
+
.optional()
|
|
73
|
+
.describe('Set true if the agent plans to call the API without credentials.'),
|
|
74
|
+
payment_onboarded: z
|
|
75
|
+
.boolean()
|
|
76
|
+
.optional()
|
|
77
|
+
.describe('Set true if AMP payment onboarding is already complete for this API.'),
|
|
78
|
+
},
|
|
79
|
+
}, async ({ action, manifest, intends_unauthenticated, payment_onboarded }) => {
|
|
80
|
+
const result = checkAgentPermissions(action, manifest, {
|
|
81
|
+
intends_unauthenticated,
|
|
82
|
+
payment_onboarded,
|
|
83
|
+
});
|
|
84
|
+
return {
|
|
85
|
+
content: [
|
|
86
|
+
{
|
|
87
|
+
type: 'text',
|
|
88
|
+
text: JSON.stringify(result, null, 2),
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
return server;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=create-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-server.js","sourceRoot":"","sources":["../src/create-server.ts"],"names":[],"mappings":"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"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":""}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js';
|
|
3
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
4
|
+
import { createAmpMcpServer } from './create-server.js';
|
|
5
|
+
const MCP_SSE_PATH = process.env.AMP_MCP_SSE_PATH ?? '/mcp';
|
|
6
|
+
const MCP_MESSAGES_PATH = process.env.AMP_MCP_MESSAGES_PATH ?? '/messages';
|
|
7
|
+
const PORT = Number.parseInt(process.env.PORT ?? '8787', 10);
|
|
8
|
+
const HOST = process.env.HOST ?? '0.0.0.0';
|
|
9
|
+
const ALLOWED_HOSTS = process.env.AMP_ALLOWED_HOSTS
|
|
10
|
+
? process.env.AMP_ALLOWED_HOSTS.split(',').map((h) => h.trim()).filter(Boolean)
|
|
11
|
+
: ['agent-manifest.com', 'www.agent-manifest.com', 'localhost', '127.0.0.1'];
|
|
12
|
+
const transports = {};
|
|
13
|
+
const app = createMcpExpressApp({
|
|
14
|
+
host: HOST,
|
|
15
|
+
allowedHosts: ALLOWED_HOSTS,
|
|
16
|
+
});
|
|
17
|
+
app.get('/health', (_req, res) => {
|
|
18
|
+
res.json({ ok: true, service: 'amp-mcp', transport: 'sse' });
|
|
19
|
+
});
|
|
20
|
+
app.get(MCP_SSE_PATH, async (req, res) => {
|
|
21
|
+
try {
|
|
22
|
+
const transport = new SSEServerTransport(MCP_MESSAGES_PATH, res);
|
|
23
|
+
const sessionId = transport.sessionId;
|
|
24
|
+
transports[sessionId] = transport;
|
|
25
|
+
transport.onclose = () => {
|
|
26
|
+
delete transports[sessionId];
|
|
27
|
+
};
|
|
28
|
+
const server = createAmpMcpServer();
|
|
29
|
+
await server.connect(transport);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
console.error('SSE connection error:', error);
|
|
33
|
+
if (!res.headersSent) {
|
|
34
|
+
res.status(500).send('Failed to establish SSE stream');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
app.post(MCP_MESSAGES_PATH, async (req, res) => {
|
|
39
|
+
const sessionId = req.query.sessionId;
|
|
40
|
+
if (typeof sessionId !== 'string' || !sessionId) {
|
|
41
|
+
res.status(400).send('Missing sessionId query parameter');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const transport = transports[sessionId];
|
|
45
|
+
if (!transport) {
|
|
46
|
+
res.status(404).send('Unknown session');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
await transport.handlePostMessage(req, res, req.body);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
console.error('Message handling error:', error);
|
|
54
|
+
if (!res.headersSent) {
|
|
55
|
+
res.status(500).send('Error handling MCP message');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
app.listen(PORT, HOST, () => {
|
|
60
|
+
console.log(`AMP MCP (SSE) listening on http://${HOST}:${PORT} — SSE GET ${MCP_SSE_PATH}, POST ${MCP_MESSAGES_PATH}`);
|
|
61
|
+
});
|
|
62
|
+
process.on('SIGINT', async () => {
|
|
63
|
+
for (const id of Object.keys(transports)) {
|
|
64
|
+
try {
|
|
65
|
+
await transports[id].close();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* ignore */
|
|
69
|
+
}
|
|
70
|
+
delete transports[id];
|
|
71
|
+
}
|
|
72
|
+
process.exit(0);
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createAmpMcpServer } from './create-server.js';
|
|
4
|
+
async function main() {
|
|
5
|
+
const server = createAmpMcpServer();
|
|
6
|
+
const transport = new StdioServerTransport();
|
|
7
|
+
await server.connect(transport);
|
|
8
|
+
}
|
|
9
|
+
main().catch((err) => {
|
|
10
|
+
console.error('AMP MCP server failed:', err);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
});
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,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,wBAAwB,EAAE,GAAG,CAAC,CAAC;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-permissions.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/check-permissions.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { checkAgentPermissions, parseAction } from '../check-permissions.js';
|
|
3
|
+
import { validateManifestSyntax } from '../validate-syntax.js';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
const fixture = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../../../validator/fixtures/v03-free-valid.json'), 'utf-8'));
|
|
8
|
+
describe('parseAction', () => {
|
|
9
|
+
it('parses method and path', () => {
|
|
10
|
+
expect(parseAction('POST /baking/validate-mix')).toEqual({
|
|
11
|
+
method: 'POST',
|
|
12
|
+
path: '/baking/validate-mix',
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe('checkAgentPermissions', () => {
|
|
17
|
+
const manifest = fixture;
|
|
18
|
+
it('flags undeclared endpoints', () => {
|
|
19
|
+
const result = checkAgentPermissions('GET /not-in-manifest', manifest);
|
|
20
|
+
expect(result.verdict).toBe('violation');
|
|
21
|
+
expect(result.findings.some((f) => f.code === 'endpoint_not_declared')).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it('validates fixture syntax', () => {
|
|
24
|
+
const syntax = validateManifestSyntax(manifest);
|
|
25
|
+
expect(syntax.valid).toBe(true);
|
|
26
|
+
expect(syntax.success_token).toMatch(/^amp-schema-valid:/);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=check-permissions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-permissions.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/check-permissions.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CACxB,YAAY,CACV,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,oDAAoD,CAAC,EACnG,OAAO,CACR,CACyB,CAAC;AAE7B,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC,CAAC,OAAO,CAAC;YACvD,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,sBAAsB;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,MAAM,QAAQ,GAAG,OAAkC,CAAC;IAEpD,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,MAAM,GAAG,qBAAqB,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QAClC,MAAM,MAAM,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AmpManifest, PermissionCheckResult } from './types.js';
|
|
2
|
+
export declare function parseAction(action: string): {
|
|
3
|
+
method: string | null;
|
|
4
|
+
path: string | null;
|
|
5
|
+
};
|
|
6
|
+
export interface CheckPermissionsOptions {
|
|
7
|
+
/** If true, treat unauthenticated access as a violation when auth is required. */
|
|
8
|
+
intends_unauthenticated?: boolean;
|
|
9
|
+
/** If true, agent claims payment/onboarding is already complete. */
|
|
10
|
+
payment_onboarded?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function checkAgentPermissions(action: string, manifest: AmpManifest, options?: CheckPermissionsOptions): PermissionCheckResult;
|
|
13
|
+
//# sourceMappingURL=check-permissions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-permissions.d.ts","sourceRoot":"","sources":["../../src/lib/check-permissions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAqB,MAAM,YAAY,CAAC;AA8BxF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAuB1F;AAsDD,MAAM,WAAW,uBAAuB;IACtC,kFAAkF;IAClF,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,WAAW,EACrB,OAAO,GAAE,uBAA4B,GACpC,qBAAqB,CA8GvB"}
|