frinus-mcp 3.5.0 → 3.6.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/dist/client/cp-client.d.ts +3 -0
- package/dist/client/cp-client.js +16 -0
- package/dist/index.js +1 -1
- package/dist/tools/definitions.js +88 -3
- package/dist/tools/handlers.js +133 -2
- package/dist/types/index.d.ts +3 -0
- package/manifest.json +2 -2
- package/package.json +1 -1
|
@@ -17,4 +17,7 @@ export declare class CpClient implements CpClientInterface {
|
|
|
17
17
|
getCredential(integrationRef: string): Promise<any>;
|
|
18
18
|
listCredentials(): Promise<any>;
|
|
19
19
|
deleteCredential(integrationRef: string): Promise<void>;
|
|
20
|
+
listCredentialShares(integrationRef: string): Promise<any>;
|
|
21
|
+
shareCredential(integrationRef: string, userIdOrEmail: string): Promise<any>;
|
|
22
|
+
revokeCredentialShare(integrationRef: string, granteeUserId: string): Promise<void>;
|
|
20
23
|
}
|
package/dist/client/cp-client.js
CHANGED
|
@@ -57,4 +57,20 @@ export class CpClient {
|
|
|
57
57
|
async deleteCredential(integrationRef) {
|
|
58
58
|
await this.client.delete(`/api/v1/credentials/${integrationRef}`);
|
|
59
59
|
}
|
|
60
|
+
async listCredentialShares(integrationRef) {
|
|
61
|
+
const response = await this.client.get(`/api/v1/credentials/${integrationRef}/shares`);
|
|
62
|
+
return response.data;
|
|
63
|
+
}
|
|
64
|
+
async shareCredential(integrationRef, userIdOrEmail) {
|
|
65
|
+
// The API accepts either field; pick by shape so callers can pass
|
|
66
|
+
// whichever they have. An email is what a human or agent usually knows.
|
|
67
|
+
const body = userIdOrEmail.includes("@")
|
|
68
|
+
? { email: userIdOrEmail }
|
|
69
|
+
: { user_id: userIdOrEmail };
|
|
70
|
+
const response = await this.client.post(`/api/v1/credentials/${integrationRef}/shares`, body);
|
|
71
|
+
return response.data;
|
|
72
|
+
}
|
|
73
|
+
async revokeCredentialShare(integrationRef, granteeUserId) {
|
|
74
|
+
await this.client.delete(`/api/v1/credentials/${integrationRef}/shares/${granteeUserId}`);
|
|
75
|
+
}
|
|
60
76
|
}
|
package/dist/index.js
CHANGED
|
@@ -1654,10 +1654,14 @@ RULES:
|
|
|
1654
1654
|
},
|
|
1655
1655
|
{
|
|
1656
1656
|
name: "credential_list",
|
|
1657
|
-
description: `List
|
|
1657
|
+
description: `List every credential you can access in this organization.
|
|
1658
1658
|
|
|
1659
|
-
|
|
1660
|
-
|
|
1659
|
+
Covers credentials you OWN and credentials other members SHARED with you.
|
|
1660
|
+
Each entry says which it is: shared ones are marked with who shared them,
|
|
1661
|
+
and owned ones with how many people they reach.
|
|
1662
|
+
|
|
1663
|
+
Returns metadata only (ref, created_at, sharing info) — never secret data.
|
|
1664
|
+
Use a listed ref with 'credential_exec' to actually use the credential.`,
|
|
1661
1665
|
inputSchema: {
|
|
1662
1666
|
type: "object",
|
|
1663
1667
|
properties: {},
|
|
@@ -1680,4 +1684,85 @@ This cannot be undone.`,
|
|
|
1680
1684
|
required: ["ref"],
|
|
1681
1685
|
},
|
|
1682
1686
|
},
|
|
1687
|
+
{
|
|
1688
|
+
name: "credential_share",
|
|
1689
|
+
description: `Share a credential you own with another member of your organization.
|
|
1690
|
+
|
|
1691
|
+
The grant gives that member the SAME access you have: they can run
|
|
1692
|
+
'credential_exec' with the ref and read the value. There is no reduced
|
|
1693
|
+
"use but never reveal" level, and that is deliberate — 'credential_exec'
|
|
1694
|
+
takes an arbitrary argv, so any such tier would be one command away from
|
|
1695
|
+
being bypassed. Share only with people you would hand the secret to.
|
|
1696
|
+
|
|
1697
|
+
What stays yours alone is MANAGEMENT: only the owner can share, revoke,
|
|
1698
|
+
overwrite or delete a credential. Receiving a share never confers that.
|
|
1699
|
+
|
|
1700
|
+
Identify the recipient by 'email' (usual) or 'user_id' (Keycloak UUID).
|
|
1701
|
+
They must already be an active member of your organization.
|
|
1702
|
+
|
|
1703
|
+
Idempotent: re-sharing with the same person is a no-op, not an error.
|
|
1704
|
+
|
|
1705
|
+
Revoking later stops FUTURE access — it cannot un-read what was already
|
|
1706
|
+
read. If a secret may have been misused, rotate it.`,
|
|
1707
|
+
inputSchema: {
|
|
1708
|
+
type: "object",
|
|
1709
|
+
properties: {
|
|
1710
|
+
ref: {
|
|
1711
|
+
type: "string",
|
|
1712
|
+
description: "Credential reference you own (e.g., 'mysql_prod', 'jira_muza')",
|
|
1713
|
+
},
|
|
1714
|
+
email: {
|
|
1715
|
+
type: "string",
|
|
1716
|
+
description: "Email of the org member who will receive access",
|
|
1717
|
+
},
|
|
1718
|
+
user_id: {
|
|
1719
|
+
type: "string",
|
|
1720
|
+
description: "Keycloak UUID of the member (alternative to email)",
|
|
1721
|
+
},
|
|
1722
|
+
},
|
|
1723
|
+
required: ["ref"],
|
|
1724
|
+
},
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
name: "credential_unshare",
|
|
1728
|
+
description: `Revoke a member's access to a credential you own.
|
|
1729
|
+
|
|
1730
|
+
Stops future access only. It does NOT undo what the person already read:
|
|
1731
|
+
if you are revoking because the secret may have leaked, rotate the secret
|
|
1732
|
+
as well — revocation is not a recall.
|
|
1733
|
+
|
|
1734
|
+
Owner-only.`,
|
|
1735
|
+
inputSchema: {
|
|
1736
|
+
type: "object",
|
|
1737
|
+
properties: {
|
|
1738
|
+
ref: {
|
|
1739
|
+
type: "string",
|
|
1740
|
+
description: "Credential reference you own",
|
|
1741
|
+
},
|
|
1742
|
+
user_id: {
|
|
1743
|
+
type: "string",
|
|
1744
|
+
description: "Keycloak UUID of the member whose access is revoked",
|
|
1745
|
+
},
|
|
1746
|
+
},
|
|
1747
|
+
required: ["ref", "user_id"],
|
|
1748
|
+
},
|
|
1749
|
+
},
|
|
1750
|
+
{
|
|
1751
|
+
name: "credential_shares",
|
|
1752
|
+
description: `List who a credential you own is shared with.
|
|
1753
|
+
|
|
1754
|
+
Owner-only: holding a share does not let you see the other recipients.
|
|
1755
|
+
Returns each grantee (email/name when the directory resolves them), who
|
|
1756
|
+
granted it and when. No secret data.`,
|
|
1757
|
+
inputSchema: {
|
|
1758
|
+
type: "object",
|
|
1759
|
+
properties: {
|
|
1760
|
+
ref: {
|
|
1761
|
+
type: "string",
|
|
1762
|
+
description: "Credential reference you own",
|
|
1763
|
+
},
|
|
1764
|
+
},
|
|
1765
|
+
required: ["ref"],
|
|
1766
|
+
},
|
|
1767
|
+
},
|
|
1683
1768
|
];
|
package/dist/tools/handlers.js
CHANGED
|
@@ -2027,11 +2027,22 @@ const handlers = {
|
|
|
2027
2027
|
}],
|
|
2028
2028
|
};
|
|
2029
2029
|
}
|
|
2030
|
-
|
|
2030
|
+
// Mark provenance: owned vs shared-with-me. An agent picking a ref
|
|
2031
|
+
// should know whether it may manage it (share/revoke/delete) or only
|
|
2032
|
+
// use it — management is owner-only server-side.
|
|
2033
|
+
const lines = items.map((c) => {
|
|
2034
|
+
if (c.is_owner === false) {
|
|
2035
|
+
return `- ${c.integration_type} (shared with you; owner manages it)`;
|
|
2036
|
+
}
|
|
2037
|
+
const shared = c.share_count > 0
|
|
2038
|
+
? `, shared with ${c.share_count} member${c.share_count === 1 ? "" : "s"}`
|
|
2039
|
+
: "";
|
|
2040
|
+
return `- ${c.integration_type} (yours, created: ${c.created_at}${shared})`;
|
|
2041
|
+
});
|
|
2031
2042
|
return {
|
|
2032
2043
|
content: [{
|
|
2033
2044
|
type: "text",
|
|
2034
|
-
text: `
|
|
2045
|
+
text: `Credentials you can access (${items.length}):\n${lines.join("\n")}\n\nUse a ref with credential_exec to run a command with it injected.`,
|
|
2035
2046
|
}],
|
|
2036
2047
|
};
|
|
2037
2048
|
}
|
|
@@ -2068,6 +2079,126 @@ const handlers = {
|
|
|
2068
2079
|
throw error;
|
|
2069
2080
|
}
|
|
2070
2081
|
},
|
|
2082
|
+
// --- Sharing -------------------------------------------------------------
|
|
2083
|
+
// A share conveys the owner's full access (use + read). No reduced tier
|
|
2084
|
+
// exists on purpose: credential_exec takes an arbitrary argv, so a
|
|
2085
|
+
// "use but never reveal" level would be bypassable and would only give
|
|
2086
|
+
// false assurance. Management stays owner-only, enforced server-side.
|
|
2087
|
+
async credential_share(args, { cpClient }) {
|
|
2088
|
+
const ref = args.ref;
|
|
2089
|
+
const email = args.email;
|
|
2090
|
+
const userId = args.user_id;
|
|
2091
|
+
if (!email && !userId) {
|
|
2092
|
+
return {
|
|
2093
|
+
content: [{
|
|
2094
|
+
type: "text",
|
|
2095
|
+
text: "Provide either 'email' or 'user_id' of the org member to share with.",
|
|
2096
|
+
}],
|
|
2097
|
+
isError: true,
|
|
2098
|
+
};
|
|
2099
|
+
}
|
|
2100
|
+
if (email && userId) {
|
|
2101
|
+
return {
|
|
2102
|
+
content: [{ type: "text", text: "Provide either 'email' or 'user_id', not both." }],
|
|
2103
|
+
isError: true,
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
try {
|
|
2107
|
+
const share = await cpClient.shareCredential(ref, (userId ?? email));
|
|
2108
|
+
const who = share?.email || share?.name || share?.grantee_user_id || email || userId;
|
|
2109
|
+
return {
|
|
2110
|
+
content: [{
|
|
2111
|
+
type: "text",
|
|
2112
|
+
text: `Credential '${ref}' is now shared with ${who}.\n` +
|
|
2113
|
+
"They have the same access you do: they can use it and read its value.\n" +
|
|
2114
|
+
"You remain the only one who can share, revoke, overwrite or delete it.\n" +
|
|
2115
|
+
"Revoking later stops future access but cannot un-read what was already read — rotate the secret if it may leak.",
|
|
2116
|
+
}],
|
|
2117
|
+
};
|
|
2118
|
+
}
|
|
2119
|
+
catch (error) {
|
|
2120
|
+
const status = error?.response?.status;
|
|
2121
|
+
const detail = error?.response?.data?.detail;
|
|
2122
|
+
if (status === 404 || status === 400) {
|
|
2123
|
+
return {
|
|
2124
|
+
content: [{ type: "text", text: detail || `Could not share '${ref}'.` }],
|
|
2125
|
+
isError: true,
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
const apiErr = handleApiError(error);
|
|
2129
|
+
if (apiErr)
|
|
2130
|
+
return apiErr;
|
|
2131
|
+
throw error;
|
|
2132
|
+
}
|
|
2133
|
+
},
|
|
2134
|
+
async credential_unshare(args, { cpClient }) {
|
|
2135
|
+
const ref = args.ref;
|
|
2136
|
+
const userId = args.user_id;
|
|
2137
|
+
try {
|
|
2138
|
+
await cpClient.revokeCredentialShare(ref, userId);
|
|
2139
|
+
return {
|
|
2140
|
+
content: [{
|
|
2141
|
+
type: "text",
|
|
2142
|
+
text: `Access to '${ref}' revoked for ${userId}.\n` +
|
|
2143
|
+
"This stops FUTURE access only. If the secret may have been read and misused, rotate it.",
|
|
2144
|
+
}],
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
catch (error) {
|
|
2148
|
+
if (error?.response?.status === 404) {
|
|
2149
|
+
return {
|
|
2150
|
+
content: [{
|
|
2151
|
+
type: "text",
|
|
2152
|
+
text: error?.response?.data?.detail || `No share found for '${ref}' and that user.`,
|
|
2153
|
+
}],
|
|
2154
|
+
isError: true,
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
const apiErr = handleApiError(error);
|
|
2158
|
+
if (apiErr)
|
|
2159
|
+
return apiErr;
|
|
2160
|
+
throw error;
|
|
2161
|
+
}
|
|
2162
|
+
},
|
|
2163
|
+
async credential_shares(args, { cpClient }) {
|
|
2164
|
+
const ref = args.ref;
|
|
2165
|
+
try {
|
|
2166
|
+
const result = await cpClient.listCredentialShares(ref);
|
|
2167
|
+
const items = result?.items || [];
|
|
2168
|
+
if (items.length === 0) {
|
|
2169
|
+
return {
|
|
2170
|
+
content: [{ type: "text", text: `Credential '${ref}' is not shared with anyone.` }],
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
const lines = items.map((sh) => {
|
|
2174
|
+
const who = sh.email || sh.name || sh.grantee_user_id;
|
|
2175
|
+
return `- ${who} (user_id: ${sh.grantee_user_id}, since ${sh.granted_at})`;
|
|
2176
|
+
});
|
|
2177
|
+
return {
|
|
2178
|
+
content: [{
|
|
2179
|
+
type: "text",
|
|
2180
|
+
text: `'${ref}' is shared with ${items.length} member${items.length === 1 ? "" : "s"}:\n` +
|
|
2181
|
+
lines.join("\n") +
|
|
2182
|
+
"\n\nEach of them has the same access you do. Use credential_unshare(ref, user_id) to revoke.",
|
|
2183
|
+
}],
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
catch (error) {
|
|
2187
|
+
if (error?.response?.status === 404) {
|
|
2188
|
+
return {
|
|
2189
|
+
content: [{
|
|
2190
|
+
type: "text",
|
|
2191
|
+
text: error?.response?.data?.detail || `You do not own a credential named '${ref}'`,
|
|
2192
|
+
}],
|
|
2193
|
+
isError: true,
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
const apiErr = handleApiError(error);
|
|
2197
|
+
if (apiErr)
|
|
2198
|
+
return apiErr;
|
|
2199
|
+
throw error;
|
|
2200
|
+
}
|
|
2201
|
+
},
|
|
2071
2202
|
};
|
|
2072
2203
|
// ---------------------------------------------------------------------------
|
|
2073
2204
|
// Dispatcher: looks up the handler and executes it with error handling
|
package/dist/types/index.d.ts
CHANGED
|
@@ -75,6 +75,9 @@ export interface CpClientInterface {
|
|
|
75
75
|
getCredential(integrationRef: string): Promise<any>;
|
|
76
76
|
listCredentials(): Promise<any>;
|
|
77
77
|
deleteCredential(integrationRef: string): Promise<void>;
|
|
78
|
+
listCredentialShares(integrationRef: string): Promise<any>;
|
|
79
|
+
shareCredential(integrationRef: string, userIdOrEmail: string): Promise<any>;
|
|
80
|
+
revokeCredentialShare(integrationRef: string, granteeUserId: string): Promise<void>;
|
|
78
81
|
}
|
|
79
82
|
/** Public surface of the MemoryClient used by handlers. */
|
|
80
83
|
export interface MemoryClientInterface {
|
package/manifest.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"manifest_version": "0.3",
|
|
3
3
|
"name": "frinus",
|
|
4
4
|
"display_name": "Frinus — Memória Cognitiva",
|
|
5
|
-
"version": "3.
|
|
6
|
-
"description": "Memória cognitiva de longo prazo para o Claude (φρήν + νοῦς).
|
|
5
|
+
"version": "3.6.0",
|
|
6
|
+
"description": "Memória cognitiva de longo prazo para o Claude (φρήν + νοῦς). 83 ferramentas de memória, sessões, agentes e conhecimento.",
|
|
7
7
|
"long_description": "O Frinus dá ao Claude memória de longo prazo, identidade e inteligência persistente entre conversas. Conecta o Claude Desktop à plataforma Frinus (Memory Engine, Control Plane e Agent Service) via MCP. Basta colar sua chave de API pessoal — nada para instalar ou compilar.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Frinus",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frinus-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "Frinus - MCP Server for Cognitive Memory (φρήν + νοῦς). Long-term memory, sessions, agents and knowledge for Claude and any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|