@corbits/mcp 0.2.0 → 0.3.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 +5 -1
- package/dist/hub/discover.d.ts +2 -2
- package/dist/hub/discover.js +10 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ mountMcpDiscovery(mcpRoutes, {
|
|
|
112
112
|
|
|
113
113
|
Mount `mcpRoutes` on the hub app under `/api/tenants/:tenantId`, behind the hub's auth and tenant middleware.
|
|
114
114
|
|
|
115
|
-
`POST /api/tenants/:tenantId/mcp/discover` with `{ url, credentialId? }` returns `{ data: { serverInfo, tools } }`. `url` must be https; plain-http loopback is refused unless the host sets `allowLoopback: true` for local development. `credentialId` names a tenant credential whose secret is sent as a bearer; a credential holding `MCP_NO_TOKEN_SENTINEL` from `@corbits/credential-http` sends no `authorization` header. Errors: 400 for a bad body or URL, 404 for an unknown credential or one that is not active or has expired, 422 when the server fails discovery, the secret is not a valid header value, or the request would leave the credential's origin. A 422 carries only this package's own messages and never the upstream HTTP status; any other failure reads as a generic handshake error, so no response or `onError` text quotes the secret. `requireGrant` is the host's own grant middleware for this route.
|
|
115
|
+
`POST /api/tenants/:tenantId/mcp/discover` with `{ url, credentialId? }` returns `{ data: { serverInfo, tools } }`. `url` must be https; plain-http loopback is refused unless the host sets `allowLoopback: true` for local development. `credentialId` names a tenant credential whose secret is sent as a bearer; the credential's type must be `api_key` or `oauth_token`, and any other type reads as not found. An `oauth_token` credential's secret is its current access token, which [`@corbits/oauth-core`](https://github.com/corbitsdev/corbits-oauth-core)'s refresher renews in place, so nothing here refreshes it, and one past its `expiresAt` or marked `error` reads as not found until it is renewed or the user signs in again; a credential holding `MCP_NO_TOKEN_SENTINEL` from `@corbits/credential-http` sends no `authorization` header. Errors: 400 for a bad body or URL, 404 for an unknown credential or one that is not active or has expired, 422 when the server fails discovery, the secret is not a valid header value, or the request would leave the credential's origin. A 422 carries only this package's own messages and never the upstream HTTP status; any other failure reads as a generic handshake error, so no response or `onError` text quotes the secret. `requireGrant` is the host's own grant middleware for this route.
|
|
116
116
|
|
|
117
117
|
When a secret is sent, the fetch is pinned to the origin of the credential's provider `apiBaseUrl`; with no credential or a keyless one, to the URL's origin. Redirects are always refused, so the secret never leaves that origin. Each discovery request times out after 30 seconds.
|
|
118
118
|
|
|
@@ -163,6 +163,10 @@ Each catalog entry becomes a tool named `<handle>.<tool>`, with the remote `inpu
|
|
|
163
163
|
|
|
164
164
|
`handle` is the credential handle the host binds this server's token to. The bundle resolves it from the runtime `credentials` capability and expects an `http` credential, such as the one `@corbits/credential-http`'s MCP provider returns. The package manifest declares the `mcp-server` credential for this.
|
|
165
165
|
|
|
166
|
+
#### OAuth servers
|
|
167
|
+
|
|
168
|
+
A host records how a server authenticates in the credential's `metadata.mcp.auth`: `"none"` (the keyless sentinel), `"token"` (a pasted `api_key`) or `"oauth"` (an `oauth_token` login through `@corbits/oauth-core`, using `discoverMcpLoginEntry` and `mcpClientConfig` above). This package does not read the value; it reads the credential's type, so pass the `oauth_token` credential's id as `credentialId`. At run time the credential's provider shapes the handle from the credential's current secret, so a refreshed token reaches running agents without any change to the bundle.
|
|
169
|
+
|
|
166
170
|
### Grant access
|
|
167
171
|
|
|
168
172
|
Grant the agent's principal `tool:deepwiki.*` for the whole server, or `tool:deepwiki.<tool>` per tool, with `allow`, `ask` or `deny`.
|
package/dist/hub/discover.d.ts
CHANGED
|
@@ -34,8 +34,8 @@ export type McpCredential = {
|
|
|
34
34
|
/**
|
|
35
35
|
* Read a tenant credential's decrypted secret and the origin it is pinned to.
|
|
36
36
|
* Scoped by tenant so a credential id from another tenant reads as absent,
|
|
37
|
-
* not as a secret; a revoked, errored or expired credential
|
|
38
|
-
* too.
|
|
37
|
+
* not as a secret; a revoked, errored or expired credential, or one that holds no bearer
|
|
38
|
+
* token (a certificate, say), reads as absent too.
|
|
39
39
|
*/
|
|
40
40
|
export declare function readCredential(opts: {
|
|
41
41
|
readonly db: DB["db"];
|
package/dist/hub/discover.js
CHANGED
|
@@ -13,17 +13,24 @@ const DiscoverBody = type({
|
|
|
13
13
|
url: "string > 0",
|
|
14
14
|
"credentialId?": "string > 0",
|
|
15
15
|
});
|
|
16
|
+
/**
|
|
17
|
+
* Credential types whose secret is a bearer token. An `oauth_token` row's
|
|
18
|
+
* secret is the current access token, which `@corbits/oauth-core`'s refresher
|
|
19
|
+
* rewrites in place, so it is read here like any other and never refreshed.
|
|
20
|
+
*/
|
|
21
|
+
const BEARER_CREDENTIAL_TYPES = ["api_key", "oauth_token"];
|
|
16
22
|
/**
|
|
17
23
|
* Read a tenant credential's decrypted secret and the origin it is pinned to.
|
|
18
24
|
* Scoped by tenant so a credential id from another tenant reads as absent,
|
|
19
|
-
* not as a secret; a revoked, errored or expired credential
|
|
20
|
-
* too.
|
|
25
|
+
* not as a secret; a revoked, errored or expired credential, or one that holds no bearer
|
|
26
|
+
* token (a certificate, say), reads as absent too.
|
|
21
27
|
*/
|
|
22
28
|
export async function readCredential(opts) {
|
|
23
29
|
const [row] = await opts.db
|
|
24
30
|
.select({
|
|
25
31
|
id: credential.id,
|
|
26
32
|
secret: credential.secret,
|
|
33
|
+
type: credential.type,
|
|
27
34
|
status: credential.status,
|
|
28
35
|
expiresAt: credential.expiresAt,
|
|
29
36
|
apiBaseUrl: provider.apiBaseUrl,
|
|
@@ -33,6 +40,7 @@ export async function readCredential(opts) {
|
|
|
33
40
|
.where(and(eq(credential.id, opts.credentialId), eq(credential.tenantId, opts.tenantId)))
|
|
34
41
|
.limit(1);
|
|
35
42
|
if (row === undefined ||
|
|
43
|
+
!BEARER_CREDENTIAL_TYPES.includes(row.type) ||
|
|
36
44
|
row.status !== "active" ||
|
|
37
45
|
(row.expiresAt !== null && row.expiresAt.getTime() <= Date.now())) {
|
|
38
46
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@corbits/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP client tool package for Interchange: streamable HTTP, per-tool grants, OAuth.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"corbits",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"check": "bun run typecheck && bun run lint && bun run format:check && bun run test"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@corbits/oauth-core": "^0.
|
|
58
|
+
"@corbits/oauth-core": "^0.3.0",
|
|
59
59
|
"@intx/types": "^0.4.0",
|
|
60
60
|
"arktype": "^2.2.3"
|
|
61
61
|
},
|