@absolutejs/mcp 0.4.2 → 0.5.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.
@@ -0,0 +1,124 @@
1
+ {
2
+ "contract": 2,
3
+ "identity": {
4
+ "accent": "#0ea5e9",
5
+ "category": "ai",
6
+ "description": "Serve a remote Model Context Protocol endpoint with OAuth discovery, agency action enforcement, approval receipts, and caller-bound durable Tasks. Bridge installed AbsoluteJS packages' manifests with `toMcpToolRegistry` and enforce their declared effects before execution.",
7
+ "docsUrl": "https://github.com/absolutejs/mcp",
8
+ "name": "@absolutejs/mcp",
9
+ "tagline": "Let AI assistants connect to your site and use its tools."
10
+ },
11
+ "requires": {
12
+ "peers": [
13
+ {
14
+ "name": "elysia",
15
+ "range": ">=1.1.0",
16
+ "reason": "plugin host"
17
+ }
18
+ ]
19
+ },
20
+ "settings": {
21
+ "type": "object",
22
+ "required": [
23
+ "issuer",
24
+ "path"
25
+ ],
26
+ "properties": {
27
+ "instructions": {
28
+ "description": "A short note shown to the AI about what this server does and how to use it.",
29
+ "title": "Instructions for the AI",
30
+ "type": "string"
31
+ },
32
+ "issuer": {
33
+ "description": "The web address of the sign-in server that issues access tokens for this endpoint. Usually your site's own address.",
34
+ "examples": [
35
+ "https://yoursite.com"
36
+ ],
37
+ "format": "uri",
38
+ "title": "Token issuer",
39
+ "type": "string"
40
+ },
41
+ "path": {
42
+ "default": "/mcp",
43
+ "description": "Where on your site the AI endpoint lives.",
44
+ "title": "Endpoint path",
45
+ "type": "string"
46
+ },
47
+ "scopesSupported": {
48
+ "description": "The permission scopes this endpoint advertises to connecting clients.",
49
+ "examples": [
50
+ [
51
+ "openid",
52
+ "mcp"
53
+ ]
54
+ ],
55
+ "title": "Advertised scopes",
56
+ "x-group": "advanced",
57
+ "type": "array",
58
+ "items": {
59
+ "type": "string"
60
+ }
61
+ },
62
+ "serveRootMetadata": {
63
+ "description": "Also answer discovery requests at the site root. Turn on for exactly one endpoint per site.",
64
+ "title": "Answer root discovery",
65
+ "x-group": "advanced",
66
+ "type": "boolean"
67
+ },
68
+ "serverInfo": {
69
+ "description": "How this server introduces itself to connecting AI clients.",
70
+ "title": "Server identity",
71
+ "type": "object",
72
+ "properties": {
73
+ "name": {
74
+ "title": "Machine name",
75
+ "examples": [
76
+ "your-site"
77
+ ],
78
+ "type": "string"
79
+ },
80
+ "title": {
81
+ "title": "Display name",
82
+ "examples": [
83
+ "Your Site"
84
+ ],
85
+ "type": "string"
86
+ },
87
+ "version": {
88
+ "title": "Version",
89
+ "examples": [
90
+ "1.0.0"
91
+ ],
92
+ "type": "string"
93
+ }
94
+ }
95
+ }
96
+ }
97
+ },
98
+ "wiring": [
99
+ {
100
+ "description": "Mount a remote MCP endpoint that serves the tools of installed AbsoluteJS packages — bridge each package's manifest and any MCP client (Claude, etc.) can call them.",
101
+ "id": "default",
102
+ "server": {
103
+ "code": ".use(\n\tmcpServer({\n\t\t// TODO: decide who may call this endpoint — see verifyBearer for\n\t\t// the standard OAuth bearer-token checks. Denies everyone until\n\t\t// you wire it.\n\t\tauthorize: async () => ({\n\t\t\tok: false,\n\t\t\treason: 'authorization not configured'\n\t\t}),\n\t\tinstructions: ${settings.instructions},\n\t\tissuer: ${settings.issuer},\n\t\tpath: ${settings.path},\n\t\tscopesSupported: ${settings.scopesSupported},\n\t\tserveRootMetadata: ${settings.serveRootMetadata},\n\t\tserverInfo: ${settings.serverInfo} ?? { name: 'my-site', version: '0.1.0' },\n\t\ttools: async () => {\n\t\t\t// Every installed AbsoluteJS package ships a manifest; bridging\n\t\t\t// it exposes that package's tools here. Pass the live instance\n\t\t\t// you constructed for the package as `runtime` — tools without\n\t\t\t// their binding are omitted (fail closed).\n\t\t\t// TODO: list the packages this endpoint should expose.\n\t\t\tconst exposed: { name: string; runtime?: unknown }[] = [];\n\t\t\tconst registries = await Promise.all(\n\t\t\t\texposed.map(async ({ name, runtime }) => {\n\t\t\t\t\tconst loaded = await loadManifest(name);\n\n\t\t\t\t\treturn loaded.ok\n\t\t\t\t\t\t? toMcpToolRegistry(loaded.manifest, { runtime })\n\t\t\t\t\t\t: {};\n\t\t\t\t})\n\t\t\t);\n\n\t\t\treturn Object.assign({}, ...registries);\n\t\t}\n\t})\n)",
104
+ "imports": [
105
+ {
106
+ "from": "@absolutejs/mcp",
107
+ "names": [
108
+ "mcpServer"
109
+ ]
110
+ },
111
+ {
112
+ "from": "@absolutejs/manifest",
113
+ "names": [
114
+ "loadManifest",
115
+ "toMcpToolRegistry"
116
+ ]
117
+ }
118
+ ],
119
+ "placement": "server-plugin"
120
+ },
121
+ "title": "Serve installed packages' tools over MCP"
122
+ }
123
+ ]
124
+ }
@@ -38,4 +38,5 @@ export { createMcpHandler } from "./handler";
38
38
  export { metadataPathFor, protectedResourceMetadata, type ProtectedResourceMetadata, } from "./metadata";
39
39
  export { mcpServer } from "./server";
40
40
  export { createSessionRegistry, type SessionRegistry } from "./sessions";
41
- export type { McpAudioContent, McpElicitAnswer, McpElicitationRequest, McpElicitBus, McpElicitResult, McpSessionStore, McpAuthResult, McpCallGate, McpCallMeta, McpContent, McpImageContent, McpPromptArgument, McpPromptDefinition, McpPrompts, McpResource, McpResourceLink, McpResources, McpServerConfig, McpServerInfo, McpTextContent, McpTool, McpToolAnnotations, McpToolCallContext, McpToolContext, McpToolRegistry, McpToolResult, McpToolReturn, } from "./types";
41
+ export { createMemoryMcpTaskStore, publicMcpTask } from "./tasks";
42
+ export type { McpAgencyOptions, McpAudioContent, McpElicitAnswer, McpElicitationRequest, McpElicitBus, McpElicitResult, McpSessionStore, McpAuthResult, McpCallGate, McpCallMeta, McpContent, McpImageContent, McpPromptArgument, McpPromptDefinition, McpPrompts, McpResource, McpResourceLink, McpResources, McpServerConfig, McpServerInfo, McpTextContent, McpTask, McpTaskStatus, McpTaskStore, McpTasksOptions, McpTool, McpToolAnnotations, McpToolCallContext, McpToolContext, McpToolRegistry, McpToolResult, McpToolReturn, } from "./types";
@@ -3,13 +3,14 @@ export declare const JSONRPC_INVALID_REQUEST = -32600;
3
3
  export declare const JSONRPC_METHOD_NOT_FOUND = -32601;
4
4
  export declare const JSONRPC_INVALID_PARAMS = -32602;
5
5
  export declare const JSONRPC_INTERNAL_ERROR = -32603;
6
+ export declare const JSONRPC_MISSING_REQUIRED_CLIENT_CAPABILITY = -32003;
6
7
  export declare const HTTP_ACCEPTED = 202;
7
8
  export declare const HTTP_NO_CONTENT = 204;
8
9
  export declare const HTTP_UNAUTHORIZED = 401;
9
10
  export declare const HTTP_METHOD_NOT_ALLOWED = 405;
10
11
  export type JsonRpcId = string | number | null;
11
12
  export declare const rpcResult: (id: JsonRpcId, result: unknown) => Response;
12
- export declare const rpcError: (id: JsonRpcId, code: number, message: string) => Response;
13
+ export declare const rpcError: (id: JsonRpcId, code: number, message: string, data?: Record<string, unknown>) => Response;
13
14
  export declare const notificationAck: () => Response;
14
15
  /** 401 with the RFC 9728 `WWW-Authenticate` challenge pointing at the
15
16
  * protected-resource metadata, so the client can discover the auth server. */
@@ -0,0 +1,2 @@
1
+ import type { McpServerConfig } from "./types";
2
+ export declare const manifest: import("@absolutejs/manifest").PackageManifest<McpServerConfig<unknown>, never>;
@@ -0,0 +1,14 @@
1
+ import type { McpTask, McpTaskStore } from "./types";
2
+ export declare const createMemoryMcpTaskStore: () => McpTaskStore;
3
+ export declare const publicMcpTask: ({ authorizationKey, ...task }: McpTask) => {
4
+ createdAt: string;
5
+ error?: Record<string, unknown>;
6
+ inputRequests?: Record<string, unknown>;
7
+ lastUpdatedAt: string;
8
+ pollIntervalMs?: number;
9
+ result?: Record<string, unknown>;
10
+ status: import("./types").McpTaskStatus;
11
+ statusMessage?: string;
12
+ taskId: string;
13
+ ttlMs: number | null;
14
+ };
@@ -1,3 +1,5 @@
1
+ import type { Agency, AgentActor } from "@absolutejs/agency";
2
+ import type { ToolAuthorization } from "@absolutejs/manifest";
1
3
  /** MCP behaviour hints, passed straight through to the client on `tools/list`.
2
4
  * Structurally identical to `@absolutejs/ai`'s `AIToolAnnotations`, so a tool
3
5
  * map from that package satisfies this without conversion. All optional. */
@@ -108,6 +110,9 @@ export type McpToolCallContext = {
108
110
  /** One callable tool. `inputSchema` is a JSON Schema object. */
109
111
  export type McpTool = {
110
112
  annotations?: McpToolAnnotations;
113
+ /** Enforceable semantic effects from manifest contract 2. A tool carrying
114
+ * this is hidden unless the server configures `agency`. */
115
+ authorization?: ToolAuthorization;
111
116
  description: string;
112
117
  handler: (args: unknown, context: McpToolCallContext) => McpToolReturn | Promise<McpToolReturn>;
113
118
  inputSchema: Record<string, unknown>;
@@ -165,6 +170,50 @@ export type McpToolContext<Caller> = {
165
170
  caller: Caller;
166
171
  meta: McpCallMeta;
167
172
  };
173
+ export type McpAgencyOptions<Caller> = {
174
+ enforcement: Agency;
175
+ resolveActor: (context: {
176
+ caller: Caller;
177
+ scopes: string[];
178
+ }) => Promise<AgentActor> | AgentActor;
179
+ serverId?: string;
180
+ };
181
+ export type McpTaskStatus = "cancelled" | "completed" | "failed" | "input_required" | "working";
182
+ export type McpTask = {
183
+ authorizationKey: string;
184
+ createdAt: string;
185
+ error?: Record<string, unknown>;
186
+ inputRequests?: Record<string, unknown>;
187
+ lastUpdatedAt: string;
188
+ pollIntervalMs?: number;
189
+ result?: Record<string, unknown>;
190
+ status: McpTaskStatus;
191
+ statusMessage?: string;
192
+ taskId: string;
193
+ ttlMs: number | null;
194
+ };
195
+ export type McpTaskStore = {
196
+ cancel: (taskId: string) => Promise<void> | void;
197
+ get: (taskId: string) => Promise<McpTask | null> | McpTask | null;
198
+ save: (task: McpTask) => Promise<void> | void;
199
+ update: (taskId: string, update: Partial<Omit<McpTask, "authorizationKey" | "createdAt" | "taskId">>) => Promise<McpTask | null> | McpTask | null;
200
+ };
201
+ export type McpTasksOptions<Caller> = {
202
+ authorizationKey: (caller: Caller) => Promise<string> | string;
203
+ onUpdate?: (context: {
204
+ caller: Caller;
205
+ inputResponses: Record<string, unknown>;
206
+ task: McpTask;
207
+ }) => Promise<void> | void;
208
+ pollIntervalMs?: number;
209
+ shouldCreate: (context: {
210
+ args: unknown;
211
+ caller: Caller;
212
+ name: string;
213
+ }) => Promise<boolean> | boolean;
214
+ store: McpTaskStore;
215
+ ttlMs?: number | null;
216
+ };
168
217
  export type McpPrompts<Caller> = {
169
218
  definitions: Record<string, McpPromptDefinition>;
170
219
  get: (ctx: {
@@ -190,6 +239,8 @@ export type McpServerInfo = {
190
239
  version: string;
191
240
  };
192
241
  export type McpServerConfig<Caller> = {
242
+ /** Per-tool action policy enforcement, approval, leases, and receipts. */
243
+ agency?: McpAgencyOptions<Caller>;
193
244
  /** Resolve the request into a caller, or a reason for the 401. The package
194
245
  * emits the 401 + RFC 9728 `WWW-Authenticate` challenge; you decide who is
195
246
  * allowed in. See {@link verifyBearer} for the standard token checks. */
@@ -244,6 +295,8 @@ export type McpServerConfig<Caller> = {
244
295
  /** Protocol versions this endpoint accepts; the first is the preferred one.
245
296
  * Defaults to the versions this package knows. */
246
297
  supportedProtocols?: string[];
298
+ /** Final SEP-2663 `io.modelcontextprotocol/tasks` extension support. */
299
+ tasks?: McpTasksOptions<Caller>;
247
300
  /** Build the tool registry for this caller. Called once per request. */
248
301
  tools: (ctx: McpToolContext<Caller>) => McpToolRegistry | Promise<McpToolRegistry>;
249
302
  };
package/package.json CHANGED
@@ -10,7 +10,28 @@
10
10
  "peerDependencies": {
11
11
  "elysia": ">=1.1.0"
12
12
  },
13
+ "dependencies": {
14
+ "@absolutejs/agency": "^0.1.0",
15
+ "@absolutejs/manifest": "^0.2.0",
16
+ "@sinclair/typebox": "^0.34.0"
17
+ },
13
18
  "license": "BUSL-1.1",
19
+ "absolutejs": {
20
+ "manifestContract": 2
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/src/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "default": "./dist/index.js"
27
+ },
28
+ "./manifest": {
29
+ "types": "./dist/src/manifest.d.ts",
30
+ "import": "./dist/manifest.js",
31
+ "default": "./dist/manifest.js"
32
+ },
33
+ "./manifest.json": "./dist/manifest.json"
34
+ },
14
35
  "publishConfig": {
15
36
  "access": "public"
16
37
  },
@@ -30,12 +51,12 @@
30
51
  "LICENSE"
31
52
  ],
32
53
  "scripts": {
33
- "build": "rm -rf dist && bun build src/index.ts --outdir dist --target=bun --external elysia && tsc --emitDeclarationOnly --project tsconfig.json",
54
+ "build": "rm -rf dist && bun build src/index.ts src/manifest.ts --outdir dist --root ./src --target=bun --external elysia && tsc --emitDeclarationOnly --project tsconfig.json && absolute-manifest emit",
34
55
  "format": "prettier --write \"./**/*.{ts,json,md}\"",
35
56
  "release": "bun run format && bun run test && bun run build && bun publish",
36
57
  "test": "bun test",
37
58
  "typecheck": "tsc --noEmit --project tsconfig.json"
38
59
  },
39
60
  "types": "./dist/src/index.d.ts",
40
- "version": "0.4.2"
61
+ "version": "0.5.0"
41
62
  }