@agent-id/mcp 1.0.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 +121 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +170 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# agentID
|
|
2
|
+
|
|
3
|
+
MCP server that gives AI agents a verified identity. It authenticates the user via BankID, gets a signed JWT, and attaches it to every HTTP request the agent makes.
|
|
4
|
+
|
|
5
|
+
Websites can verify the token offline using agentID's public keys — no need to block AI agents when you can verify who's behind them.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
┌──────────────────┐
|
|
11
|
+
│ agentID API │
|
|
12
|
+
│ (BankID + JWT) │
|
|
13
|
+
└──────┬───────────┘
|
|
14
|
+
│ 1. authenticate via BankID
|
|
15
|
+
│ 2. receive signed JWT
|
|
16
|
+
│
|
|
17
|
+
User ← stdio → MCP Server ← HTTP + X-AgentID-Token → any website
|
|
18
|
+
│
|
|
19
|
+
│ website verifies JWT via /api/jwks
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Authentication flow
|
|
23
|
+
|
|
24
|
+
1. Agent calls `start_authentication` → gets a BankID auth URL
|
|
25
|
+
2. Agent shows the URL to the user → user completes BankID on their phone
|
|
26
|
+
3. Agent calls `complete_authentication` → MCP server polls until done, caches the JWT
|
|
27
|
+
4. Agent calls `authenticated_fetch` → every request carries `X-AgentID-Token: <jwt>`
|
|
28
|
+
|
|
29
|
+
The JWT contains a pseudonymous identity (no PII), expires after 1 hour, and is signed with RS256.
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install
|
|
35
|
+
npm run build
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Claude Desktop config
|
|
39
|
+
|
|
40
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"agentID": {
|
|
46
|
+
"command": "node",
|
|
47
|
+
"args": ["/absolute/path/to/agentID/dist/index.js"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Or if published to npm:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"agentID": {
|
|
59
|
+
"command": "npx",
|
|
60
|
+
"args": ["agentID"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## MCP Tools
|
|
67
|
+
|
|
68
|
+
### `start_authentication`
|
|
69
|
+
|
|
70
|
+
Start a BankID authentication session. Returns an `authUrl` to show the user and a `sessionId` for polling.
|
|
71
|
+
|
|
72
|
+
No parameters.
|
|
73
|
+
|
|
74
|
+
### `complete_authentication`
|
|
75
|
+
|
|
76
|
+
Poll until BankID authentication completes. Caches the JWT automatically.
|
|
77
|
+
|
|
78
|
+
| Param | Type | Required | Description |
|
|
79
|
+
|-------|------|----------|-------------|
|
|
80
|
+
| `sessionId` | string | yes | Session ID from `start_authentication` |
|
|
81
|
+
|
|
82
|
+
### `authenticated_fetch`
|
|
83
|
+
|
|
84
|
+
Make an HTTP request with the cached agentID token attached as `X-AgentID-Token`.
|
|
85
|
+
|
|
86
|
+
| Param | Type | Required | Description |
|
|
87
|
+
|-------|------|----------|-------------|
|
|
88
|
+
| `url` | string | yes | URL to fetch |
|
|
89
|
+
| `method` | GET / POST / PUT / DELETE | no (default GET) | HTTP method |
|
|
90
|
+
| `body` | string | no | Request body for POST/PUT |
|
|
91
|
+
| `headers` | object | no | Additional headers |
|
|
92
|
+
|
|
93
|
+
Returns an error if no valid token is cached — call `start_authentication` first.
|
|
94
|
+
|
|
95
|
+
## Example server
|
|
96
|
+
|
|
97
|
+
A demo Express server that checks for the `X-AgentID-Token` header:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Start it
|
|
101
|
+
npx ts-node example-server/server.ts
|
|
102
|
+
|
|
103
|
+
# Without token → 403
|
|
104
|
+
curl http://localhost:4000/whoami
|
|
105
|
+
|
|
106
|
+
# With token → 200
|
|
107
|
+
curl -H "X-AgentID-Token: any-token" http://localhost:4000/whoami
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Endpoints:
|
|
111
|
+
- `GET /whoami` — agent verification status
|
|
112
|
+
- `GET /data` — sample protected data
|
|
113
|
+
- `POST /echo` — echoes request body
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npm run dev # watch mode
|
|
119
|
+
npm run build # compile TypeScript
|
|
120
|
+
npm start # run the compiled server
|
|
121
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
8
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
// --- Config ---
|
|
12
|
+
const AGENTID_URL = "https://agentpass.vercel.app";
|
|
13
|
+
// --- Token cache ---
|
|
14
|
+
let cachedToken = null;
|
|
15
|
+
let tokenExpiresAt = 0;
|
|
16
|
+
function getToken() {
|
|
17
|
+
if (cachedToken && Date.now() < tokenExpiresAt) {
|
|
18
|
+
return cachedToken;
|
|
19
|
+
}
|
|
20
|
+
cachedToken = null;
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
function setToken(jwt) {
|
|
24
|
+
cachedToken = jwt;
|
|
25
|
+
// Parse exp from JWT payload (base64url-decoded, no verification needed here)
|
|
26
|
+
try {
|
|
27
|
+
const payload = JSON.parse(Buffer.from(jwt.split(".")[1], "base64url").toString());
|
|
28
|
+
// Expire 30s early to avoid edge cases
|
|
29
|
+
tokenExpiresAt = (payload.exp * 1000) - 30_000;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Fallback: 55 minutes
|
|
33
|
+
tokenExpiresAt = Date.now() + 55 * 60 * 1000;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// --- Axios client with token interceptor ---
|
|
37
|
+
const client = axios_1.default.create();
|
|
38
|
+
client.interceptors.request.use((config) => {
|
|
39
|
+
const token = getToken();
|
|
40
|
+
if (token) {
|
|
41
|
+
config.headers.set("X-AgentID-Token", token);
|
|
42
|
+
}
|
|
43
|
+
return config;
|
|
44
|
+
});
|
|
45
|
+
// --- Helpers ---
|
|
46
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
47
|
+
function textResult(data, isError = false) {
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
50
|
+
isError,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// --- MCP Server ---
|
|
54
|
+
const server = new mcp_js_1.McpServer({
|
|
55
|
+
name: "agentID",
|
|
56
|
+
version: "1.0.0",
|
|
57
|
+
});
|
|
58
|
+
// Tool 1: Start BankID authentication
|
|
59
|
+
server.tool("start_authentication", "Start a BankID authentication session via agentID. " +
|
|
60
|
+
"Returns an authUrl that the user must open to complete BankID on their phone, " +
|
|
61
|
+
"and a sessionId to pass to complete_authentication.", {}, async () => {
|
|
62
|
+
try {
|
|
63
|
+
const res = await axios_1.default.post(`${AGENTID_URL}/api/auth/start`);
|
|
64
|
+
const { sessionId, authUrl, expiresAt } = res.data;
|
|
65
|
+
return textResult({
|
|
66
|
+
message: "BankID authentication started. Show the authUrl to the user so they can authenticate.",
|
|
67
|
+
sessionId,
|
|
68
|
+
authUrl,
|
|
69
|
+
expiresAt,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return textResult({ error: error.response?.data ?? error.message }, true);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
// Tool 2: Poll until BankID authentication completes
|
|
77
|
+
server.tool("complete_authentication", "Poll agentID for BankID authentication completion. " +
|
|
78
|
+
"Call this after the user has been shown the authUrl from start_authentication. " +
|
|
79
|
+
"It will poll every 3 seconds until the user completes BankID, then cache the JWT.", {
|
|
80
|
+
sessionId: zod_1.z
|
|
81
|
+
.string()
|
|
82
|
+
.describe("The sessionId returned by start_authentication"),
|
|
83
|
+
}, async ({ sessionId }) => {
|
|
84
|
+
const maxAttempts = 40; // ~2 minutes
|
|
85
|
+
let attempts = 0;
|
|
86
|
+
while (attempts < maxAttempts) {
|
|
87
|
+
attempts++;
|
|
88
|
+
try {
|
|
89
|
+
const res = await axios_1.default.get(`${AGENTID_URL}/api/auth/status`, {
|
|
90
|
+
params: { sessionId },
|
|
91
|
+
});
|
|
92
|
+
if (res.data.status === "complete") {
|
|
93
|
+
setToken(res.data.jwt);
|
|
94
|
+
return textResult({
|
|
95
|
+
status: "authenticated",
|
|
96
|
+
message: "BankID authentication successful. The agent identity token is now cached " +
|
|
97
|
+
"and will be automatically attached to all authenticated_fetch requests.",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (res.data.status === "failed") {
|
|
101
|
+
return textResult({
|
|
102
|
+
status: "failed",
|
|
103
|
+
message: "BankID authentication failed.",
|
|
104
|
+
hintCode: res.data.hintCode,
|
|
105
|
+
}, true);
|
|
106
|
+
}
|
|
107
|
+
// Still pending — wait and retry
|
|
108
|
+
await sleep(3000);
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return textResult({ error: error.response?.data ?? error.message }, true);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return textResult({ status: "timeout", message: "Authentication timed out after 2 minutes." }, true);
|
|
115
|
+
});
|
|
116
|
+
// Tool 3: Make authenticated HTTP requests
|
|
117
|
+
server.tool("authenticated_fetch", "Make an HTTP request with the agent's agentID identity token automatically attached " +
|
|
118
|
+
"as 'X-AgentID-Token'. The user must complete authentication first via " +
|
|
119
|
+
"start_authentication and complete_authentication.", {
|
|
120
|
+
url: zod_1.z.string().url().describe("The URL to fetch"),
|
|
121
|
+
method: zod_1.z
|
|
122
|
+
.enum(["GET", "POST", "PUT", "DELETE"])
|
|
123
|
+
.default("GET")
|
|
124
|
+
.describe("HTTP method"),
|
|
125
|
+
body: zod_1.z
|
|
126
|
+
.string()
|
|
127
|
+
.optional()
|
|
128
|
+
.describe("Request body (for POST/PUT)"),
|
|
129
|
+
headers: zod_1.z
|
|
130
|
+
.record(zod_1.z.string(), zod_1.z.string())
|
|
131
|
+
.optional()
|
|
132
|
+
.describe("Additional headers to include"),
|
|
133
|
+
}, async ({ url, method, body, headers }) => {
|
|
134
|
+
if (!getToken()) {
|
|
135
|
+
return textResult({
|
|
136
|
+
error: "Not authenticated",
|
|
137
|
+
message: "No valid agentID token. Call start_authentication first, " +
|
|
138
|
+
"then complete_authentication after the user finishes BankID.",
|
|
139
|
+
}, true);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const response = await client.request({
|
|
143
|
+
url,
|
|
144
|
+
method,
|
|
145
|
+
data: body,
|
|
146
|
+
headers: (headers ?? {}),
|
|
147
|
+
});
|
|
148
|
+
return textResult({
|
|
149
|
+
status: response.status,
|
|
150
|
+
headers: response.headers,
|
|
151
|
+
data: response.data,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
return textResult({
|
|
156
|
+
status: error.response?.status ?? "unknown",
|
|
157
|
+
error: error.response?.data ?? error.message,
|
|
158
|
+
}, true);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
// --- Start ---
|
|
162
|
+
async function main() {
|
|
163
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
164
|
+
await server.connect(transport);
|
|
165
|
+
console.error("agentID MCP server running on stdio");
|
|
166
|
+
}
|
|
167
|
+
main().catch((err) => {
|
|
168
|
+
console.error("Fatal:", err);
|
|
169
|
+
process.exit(1);
|
|
170
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agent-id/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server that gives AI agents a verified identity via BankID",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agent-id-mcp": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"mcp",
|
|
20
|
+
"agent",
|
|
21
|
+
"identity",
|
|
22
|
+
"jwt",
|
|
23
|
+
"authentication",
|
|
24
|
+
"bankid"
|
|
25
|
+
],
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
29
|
+
"axios": "^1.13.5",
|
|
30
|
+
"zod": "^4.3.6"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^25.3.0",
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
}
|
|
36
|
+
}
|