@anyknown/mcp 0.1.3
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 +34 -0
- package/dist/catalog/types.d.ts +21 -0
- package/dist/errors.d.ts +16 -0
- package/dist/public.d.ts +4 -0
- package/dist/rpc/hub-rpc.d.ts +28 -0
- package/dist/rpc/types.d.ts +76 -0
- package/dist/schema.d.ts +32 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# anyknown/mcp
|
|
2
|
+
|
|
3
|
+
The MCP hub: the directory of MCP servers (admin catalogue plus each user's own `own_*` servers), the MCP client, OAuth (discovery, dynamic registration, PKCE, refresh), per-user credentials and the tool cache.
|
|
4
|
+
|
|
5
|
+
It has no public URL. The only way in is product's `MCP` service binding, which calls the `Hub` `WorkerEntrypoint` over RPC (`src/rpc/`). Product owns authentication, scope checks (`mcp:use`, `product:admin`), request validation, activity and the OAuth cookie; the hub trusts the `userId` product passes.
|
|
6
|
+
|
|
7
|
+
Tokens are sealed to the user's public key and opened only through product's `KeyringMcp` entrypoint (`KEYRING`, a service binding to `product` / `product-staging` that exposes only `seal` / `open` for kind `mcp`), so that entrypoint has to be deployed before this worker. A locked Keyring is `key_locked` (423); a user with no public key yet is `no_user_key` (409).
|
|
8
|
+
|
|
9
|
+
## Environments
|
|
10
|
+
|
|
11
|
+
| | Worker | D1 | `PRODUCT_BASE_URL` |
|
|
12
|
+
| ---------- | ------------- | ------------- | ---------------------------------- |
|
|
13
|
+
| production | `mcp` | `mcp` | `https://app.anyknown.com` |
|
|
14
|
+
| staging | `mcp-staging` | `mcp-staging` | `https://staging-app.anyknown.com` |
|
|
15
|
+
|
|
16
|
+
`PRODUCT_BASE_URL` is product's app host, not the hub's: the OAuth `redirect_uri` (`/api/mcp/callback`) and client id (`/api/mcp/client-metadata`) live there.
|
|
17
|
+
|
|
18
|
+
## Dev
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pnpm install
|
|
22
|
+
cp .dev.vars.example .dev.vars
|
|
23
|
+
pnpm dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Product's `pnpm dev` starts this worker alongside it (`-c ../../../mcp/wrangler.jsonc`), so the `MCP` binding resolves locally. `pnpm check` runs typecheck, oxlint and oxfmt; `pnpm test` runs the workerd tests.
|
|
27
|
+
|
|
28
|
+
## Secrets
|
|
29
|
+
|
|
30
|
+
GitHub Actions needs `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID`.
|
|
31
|
+
|
|
32
|
+
## Deploy
|
|
33
|
+
|
|
34
|
+
Only through GitHub Actions. A push to `main` runs `check.yml`; when it is green its `staging` job migrates `mcp-staging` and deploys `mcp-staging`. Production is the manual `deploy.yml` (environment `production`, `main` only): migrate `mcp`, then deploy `mcp`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpServerRow, McpToolRow } from "../schema";
|
|
2
|
+
export type CatalogServer = McpServerRow & {
|
|
3
|
+
tool_count: number;
|
|
4
|
+
};
|
|
5
|
+
export type ServerSummary = McpServerRow & {
|
|
6
|
+
connected: number;
|
|
7
|
+
tool_count: number;
|
|
8
|
+
synced_at: number | null;
|
|
9
|
+
};
|
|
10
|
+
export type ToolHit = {
|
|
11
|
+
tool: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
export type HitRow = {
|
|
16
|
+
server_id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
title: string | null;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
export type ToolListing = Pick<McpToolRow, "name" | "title" | "description" | "annotations">;
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type AuthChallenge = {
|
|
2
|
+
resourceMetadata: string | null;
|
|
3
|
+
scope: string | null;
|
|
4
|
+
};
|
|
5
|
+
export type McpErrorCode = "invalid" | "not_found" | "mcp_not_connected" | "mcp_upstream_error" | "no_user_key" | "key_locked";
|
|
6
|
+
export declare class McpError extends Error {
|
|
7
|
+
readonly code: McpErrorCode;
|
|
8
|
+
readonly status: 400 | 404 | 409 | 412 | 423 | 502;
|
|
9
|
+
readonly upstreamStatus: number | null;
|
|
10
|
+
readonly challenge: AuthChallenge | null;
|
|
11
|
+
constructor(code: McpErrorCode, status: 400 | 404 | 409 | 412 | 423 | 502, extra?: {
|
|
12
|
+
upstreamStatus?: number;
|
|
13
|
+
challenge?: AuthChallenge;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export declare function needsHandshake(err: unknown): boolean;
|
package/dist/public.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { AdminServerItem, CallInput, CompleteOauthInput, ConnectResult, DescribedItem, HubResult, McpServerRow, OwnServerInput, SearchInput, ServerItem, ToolHit, ToolItem, Transient, } from "./rpc/types";
|
|
2
|
+
export type { McpHubRpc } from "./rpc/hub-rpc";
|
|
3
|
+
export type { McpAuth } from "./schema";
|
|
4
|
+
export type { McpErrorCode } from "./errors";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AdminServerInput, AdminServerItem, CallInput, CompleteOauthInput, ConnectResult, DescribedItem, HubResult, McpServerRow, OwnServerInput, SearchInput, ServerItem, ToolHit, ToolItem } from "./types";
|
|
2
|
+
export interface McpHubRpc {
|
|
3
|
+
search(userId: string, input: SearchInput): Promise<HubResult<ToolHit[]>>;
|
|
4
|
+
describe(userId: string, tools: string[]): Promise<HubResult<{
|
|
5
|
+
items: DescribedItem[];
|
|
6
|
+
}>>;
|
|
7
|
+
call(userId: string, input: CallInput): Promise<HubResult<unknown>>;
|
|
8
|
+
listServers(userId: string): Promise<ServerItem[]>;
|
|
9
|
+
getServer(userId: string, serverId: string): Promise<McpServerRow | null>;
|
|
10
|
+
serverTools(userId: string, serverId: string): Promise<HubResult<{
|
|
11
|
+
items: ToolItem[];
|
|
12
|
+
}>>;
|
|
13
|
+
addServer(userId: string, input: OwnServerInput): Promise<McpServerRow>;
|
|
14
|
+
connectServer(userId: string, serverId: string, input: {
|
|
15
|
+
token?: string;
|
|
16
|
+
}): Promise<HubResult<ConnectResult>>;
|
|
17
|
+
completeOauth(userId: string, input: CompleteOauthInput): Promise<HubResult<{
|
|
18
|
+
server_id: string;
|
|
19
|
+
}>>;
|
|
20
|
+
refresh(userId: string, serverId: string): Promise<HubResult<{
|
|
21
|
+
tools: number;
|
|
22
|
+
}>>;
|
|
23
|
+
removeOwnServer(userId: string, serverId: string): Promise<void>;
|
|
24
|
+
disconnect(userId: string, serverId: string): Promise<void>;
|
|
25
|
+
adminServers(): Promise<AdminServerItem[]>;
|
|
26
|
+
upsertAdminServer(input: AdminServerInput): Promise<McpServerRow>;
|
|
27
|
+
clientMetadata(): Record<string, unknown>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { CatalogServer, ToolHit } from "../catalog/types";
|
|
2
|
+
import type { McpErrorCode } from "../errors";
|
|
3
|
+
import type { McpAuth } from "../schema";
|
|
4
|
+
export type { AdminServerInput, McpServerRow } from "../schema";
|
|
5
|
+
export type { ToolHit };
|
|
6
|
+
export type HubResult<T> = {
|
|
7
|
+
ok: true;
|
|
8
|
+
value: T;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
error: McpErrorCode;
|
|
12
|
+
status: 400 | 404 | 409 | 412 | 423 | 502;
|
|
13
|
+
};
|
|
14
|
+
export type Transient = {
|
|
15
|
+
user_id: string;
|
|
16
|
+
server_id: string;
|
|
17
|
+
state: string;
|
|
18
|
+
verifier: string;
|
|
19
|
+
issuer: string;
|
|
20
|
+
token_endpoint: string;
|
|
21
|
+
resource: string;
|
|
22
|
+
client_id: string;
|
|
23
|
+
exp: number;
|
|
24
|
+
};
|
|
25
|
+
export type SearchInput = {
|
|
26
|
+
q: string;
|
|
27
|
+
limit?: number;
|
|
28
|
+
};
|
|
29
|
+
export type CallInput = {
|
|
30
|
+
tool: string;
|
|
31
|
+
args?: unknown;
|
|
32
|
+
timeout_ms?: number;
|
|
33
|
+
};
|
|
34
|
+
export type DescribedItem = {
|
|
35
|
+
tool: string;
|
|
36
|
+
title: string | null;
|
|
37
|
+
description: string;
|
|
38
|
+
input_schema: unknown;
|
|
39
|
+
annotations: unknown;
|
|
40
|
+
};
|
|
41
|
+
export type ServerItem = {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
url: string;
|
|
45
|
+
auth: McpAuth;
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
own: boolean;
|
|
48
|
+
connected: boolean;
|
|
49
|
+
tool_count: number;
|
|
50
|
+
synced_at: number | null;
|
|
51
|
+
};
|
|
52
|
+
export type ToolItem = {
|
|
53
|
+
name: string;
|
|
54
|
+
title: string | null;
|
|
55
|
+
description: string;
|
|
56
|
+
annotations: unknown;
|
|
57
|
+
};
|
|
58
|
+
export type OwnServerInput = {
|
|
59
|
+
name: string;
|
|
60
|
+
url: string;
|
|
61
|
+
auth: McpAuth;
|
|
62
|
+
};
|
|
63
|
+
export type ConnectResult = {
|
|
64
|
+
connected: true;
|
|
65
|
+
tools: number;
|
|
66
|
+
} | {
|
|
67
|
+
authorize_url: string;
|
|
68
|
+
transient: Transient;
|
|
69
|
+
};
|
|
70
|
+
export type CompleteOauthInput = {
|
|
71
|
+
transient: Transient;
|
|
72
|
+
code: string;
|
|
73
|
+
state: string | null;
|
|
74
|
+
iss: string | null;
|
|
75
|
+
};
|
|
76
|
+
export type AdminServerItem = CatalogServer;
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export declare const AUTH_KINDS: readonly ["oauth", "bearer", "none"];
|
|
2
|
+
export type McpAuth = (typeof AUTH_KINDS)[number];
|
|
3
|
+
export type McpServerRow = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
url: string;
|
|
7
|
+
auth: McpAuth;
|
|
8
|
+
enabled: number;
|
|
9
|
+
owner: string | null;
|
|
10
|
+
updated_at: number;
|
|
11
|
+
};
|
|
12
|
+
export type McpToolRow = {
|
|
13
|
+
server_id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
title: string | null;
|
|
16
|
+
description: string;
|
|
17
|
+
input_schema: string;
|
|
18
|
+
annotations: string | null;
|
|
19
|
+
owner: string | null;
|
|
20
|
+
updated_at: number;
|
|
21
|
+
};
|
|
22
|
+
export type AdminServerInput = {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
url: string;
|
|
26
|
+
auth: McpAuth;
|
|
27
|
+
enabled?: boolean;
|
|
28
|
+
};
|
|
29
|
+
export declare function splitToolName(value: string): {
|
|
30
|
+
serverId: string;
|
|
31
|
+
name: string;
|
|
32
|
+
} | null;
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anyknown/mcp",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/anyknown-com/mcp.git"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/public.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "wrangler dev",
|
|
22
|
+
"typecheck": "wrangler types && tsc",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"migrate": "wrangler d1 migrations apply mcp --remote --env=\"\"",
|
|
25
|
+
"migrate:staging": "wrangler d1 migrations apply mcp-staging --remote --env staging",
|
|
26
|
+
"lint": "oxlint",
|
|
27
|
+
"lint:fix": "oxlint --fix",
|
|
28
|
+
"fmt": "oxfmt",
|
|
29
|
+
"fmt:check": "oxfmt --check",
|
|
30
|
+
"check": "pnpm typecheck && pnpm lint && pnpm fmt:check",
|
|
31
|
+
"types": "wrangler types",
|
|
32
|
+
"build:types": "tsc -p tsconfig.types.json"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"zod": "^4.5.4"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@cloudflare/vitest-pool-workers": "^0.22.0",
|
|
39
|
+
"@cloudflare/workers-types": "^5.20260830.1",
|
|
40
|
+
"@stylistic/eslint-plugin": "5.10.0",
|
|
41
|
+
"@types/node": "^26.4.0",
|
|
42
|
+
"oxfmt": "^0.65.0",
|
|
43
|
+
"oxlint": "^1.80.0",
|
|
44
|
+
"typescript": "^7.0.2",
|
|
45
|
+
"vitest": "^4.1.11",
|
|
46
|
+
"wrangler": "^4.127.1"
|
|
47
|
+
},
|
|
48
|
+
"packageManager": "pnpm@10.30.3"
|
|
49
|
+
}
|