forgetting-engine-mcp 0.4.0__py3-none-any.whl
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.
- forgetting_engine_mcp/__init__.py +14 -0
- forgetting_engine_mcp/__main__.py +17 -0
- forgetting_engine_mcp/proto/__init__.py +0 -0
- forgetting_engine_mcp/proto/forgetting_engine_pb2.py +72 -0
- forgetting_engine_mcp/proto/forgetting_engine_pb2_grpc.py +374 -0
- forgetting_engine_mcp/server.py +498 -0
- forgetting_engine_mcp-0.4.0.dist-info/METADATA +180 -0
- forgetting_engine_mcp-0.4.0.dist-info/RECORD +11 -0
- forgetting_engine_mcp-0.4.0.dist-info/WHEEL +5 -0
- forgetting_engine_mcp-0.4.0.dist-info/entry_points.txt +2 -0
- forgetting_engine_mcp-0.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
遗忘引擎 MCP 客户端。
|
|
3
|
+
|
|
4
|
+
通过 MCP stdio 协议与 Claude Desktop 等 MCP 宿主对接,
|
|
5
|
+
内部转调 gRPC 到 knownot.cc 遗忘引擎服务端。
|
|
6
|
+
|
|
7
|
+
零本地依赖(无数据库、无 LLM、无引擎逻辑),pip install 即可用。
|
|
8
|
+
|
|
9
|
+
环境变量:
|
|
10
|
+
FE_ENDPOINT — 服务端地址(默认 knownot.cc:50051)
|
|
11
|
+
FE_API_KEY — API Key(必填,从 knownot.cc 门户创建)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
入口:python -m forgetting_engine_mcp / mcp-forgetting-engine
|
|
3
|
+
|
|
4
|
+
读取环境变量 FE_ENDPOINT / FE_API_KEY,启动 MCP stdio 服务。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
from forgetting_engine_mcp.server import main as _main_async
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
asyncio.run(_main_async())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: forgetting_engine/proto/forgetting_engine.proto
|
|
5
|
+
# Protobuf Python Version: 6.31.1
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
from google.protobuf import descriptor as _descriptor
|
|
8
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
9
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
10
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
11
|
+
from google.protobuf.internal import builder as _builder
|
|
12
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
13
|
+
_runtime_version.Domain.PUBLIC,
|
|
14
|
+
6,
|
|
15
|
+
31,
|
|
16
|
+
1,
|
|
17
|
+
'',
|
|
18
|
+
'forgetting_engine/proto/forgetting_engine.proto'
|
|
19
|
+
)
|
|
20
|
+
# @@protoc_insertion_point(imports)
|
|
21
|
+
|
|
22
|
+
_sym_db = _symbol_database.Default()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n/forgetting_engine/proto/forgetting_engine.proto\x12\x11\x66orgetting_engine\"-\n\x0b\x46\x65\x61tureItem\x12\x10\n\x08\x63\x61tegory\x18\x01 \x01(\t\x12\x0c\n\x04text\x18\x02 \x01(\t\"\x90\x01\n\nKinProfile\x12\x0e\n\x06kin_id\x18\x01 \x01(\t\x12\r\n\x05roles\x18\x02 \x01(\t\x12\x11\n\tlanguages\x18\x03 \x01(\t\x12\x12\n\nexperience\x18\x04 \x01(\t\x12\x13\n\x0bpreferences\x18\x05 \x01(\t\x12\x13\n\x0braw_profile\x18\x06 \x01(\t\x12\x12\n\nupdated_at\x18\x07 \x01(\t\"i\n\x0bUserProfile\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x0e\n\x06traits\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x13\n\x0braw_profile\x18\x04 \x01(\t\x12\x12\n\nupdated_at\x18\x05 \x01(\t\";\n\x08Guidance\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x10\n\x08question\x18\x02 \x01(\t\x12\x0f\n\x07options\x18\x03 \x03(\t\"\x81\x01\n\rIngestRequest\x12\x0e\n\x06kin_id\x18\x01 \x01(\t\x12\x0c\n\x04role\x18\x02 \x01(\t\x12\x0c\n\x04text\x18\x03 \x01(\t\x12\x12\n\nsession_id\x18\x04 \x01(\t\x12\x30\n\x08\x66\x65\x61tures\x18\x05 \x03(\x0b\x32\x1e.forgetting_engine.FeatureItem\" \n\x0eIngestResponse\x12\x0e\n\x06msg_id\x18\x01 \x01(\t\"D\n\x0fRetrieveRequest\x12\x0e\n\x06kin_id\x18\x01 \x01(\t\x12\r\n\x05query\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\t\"\xbb\x01\n\x10RetrieveResponse\x12\x32\n\x0bkin_profile\x18\x01 \x01(\x0b\x32\x1d.forgetting_engine.KinProfile\x12\x34\n\x0cuser_profile\x18\x02 \x01(\x0b\x32\x1e.forgetting_engine.UserProfile\x12\x0e\n\x06prompt\x18\x03 \x01(\t\x12-\n\x08guidance\x18\x04 \x03(\x0b\x32\x1b.forgetting_engine.Guidance\"Y\n\x15IngestFeaturesRequest\x12\x0e\n\x06kin_id\x18\x01 \x01(\t\x12\x30\n\x08\x66\x65\x61tures\x18\x02 \x03(\x0b\x32\x1e.forgetting_engine.FeatureItem\"\'\n\x16IngestFeaturesResponse\x12\r\n\x05\x63ount\x18\x01 \x01(\x05\"\x14\n\x12GenerateKinRequest\"%\n\x13GenerateKinResponse\x12\x0e\n\x06kin_id\x18\x01 \x01(\t\"\x89\x01\n\x17UpdateKinProfileRequest\x12\x0e\n\x06kin_id\x18\x01 \x01(\t\x12\r\n\x05roles\x18\x02 \x01(\t\x12\x11\n\tlanguages\x18\x03 \x01(\t\x12\x12\n\nexperience\x18\x04 \x01(\t\x12\x13\n\x0bpreferences\x18\x05 \x01(\t\x12\x13\n\x0braw_profile\x18\x06 \x01(\t\"&\n\x18UpdateKinProfileResponse\x12\n\n\x02ok\x18\x01 \x01(\x08\"b\n\x18UpdateUserProfileRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x0e\n\x06traits\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x13\n\x0braw_profile\x18\x04 \x01(\t\"\'\n\x19UpdateUserProfileResponse\x12\n\n\x02ok\x18\x01 \x01(\x08\"3\n\x10HandshakeRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x0e\n\x06kin_id\x18\x02 \x01(\t\"D\n\x11HandshakeResponse\x12\x0e\n\x06kin_id\x18\x01 \x01(\t\x12\x0f\n\x07user_id\x18\x02 \x01(\t\x12\x0e\n\x06is_new\x18\x03 \x01(\x08\x32\xb0\x05\n\x10\x46orgettingEngine\x12M\n\x06Ingest\x12 .forgetting_engine.IngestRequest\x1a!.forgetting_engine.IngestResponse\x12S\n\x08Retrieve\x12\".forgetting_engine.RetrieveRequest\x1a#.forgetting_engine.RetrieveResponse\x12\x65\n\x0eIngestFeatures\x12(.forgetting_engine.IngestFeaturesRequest\x1a).forgetting_engine.IngestFeaturesResponse\x12\\\n\x0bGenerateKin\x12%.forgetting_engine.GenerateKinRequest\x1a&.forgetting_engine.GenerateKinResponse\x12k\n\x10UpdateKinProfile\x12*.forgetting_engine.UpdateKinProfileRequest\x1a+.forgetting_engine.UpdateKinProfileResponse\x12n\n\x11UpdateUserProfile\x12+.forgetting_engine.UpdateUserProfileRequest\x1a,.forgetting_engine.UpdateUserProfileResponse\x12V\n\tHandshake\x12#.forgetting_engine.HandshakeRequest\x1a$.forgetting_engine.HandshakeResponseb\x06proto3')
|
|
28
|
+
|
|
29
|
+
_globals = globals()
|
|
30
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
31
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'forgetting_engine.proto.forgetting_engine_pb2', _globals)
|
|
32
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
33
|
+
DESCRIPTOR._loaded_options = None
|
|
34
|
+
_globals['_FEATUREITEM']._serialized_start=70
|
|
35
|
+
_globals['_FEATUREITEM']._serialized_end=115
|
|
36
|
+
_globals['_KINPROFILE']._serialized_start=118
|
|
37
|
+
_globals['_KINPROFILE']._serialized_end=262
|
|
38
|
+
_globals['_USERPROFILE']._serialized_start=264
|
|
39
|
+
_globals['_USERPROFILE']._serialized_end=369
|
|
40
|
+
_globals['_GUIDANCE']._serialized_start=371
|
|
41
|
+
_globals['_GUIDANCE']._serialized_end=430
|
|
42
|
+
_globals['_INGESTREQUEST']._serialized_start=433
|
|
43
|
+
_globals['_INGESTREQUEST']._serialized_end=562
|
|
44
|
+
_globals['_INGESTRESPONSE']._serialized_start=564
|
|
45
|
+
_globals['_INGESTRESPONSE']._serialized_end=596
|
|
46
|
+
_globals['_RETRIEVEREQUEST']._serialized_start=598
|
|
47
|
+
_globals['_RETRIEVEREQUEST']._serialized_end=666
|
|
48
|
+
_globals['_RETRIEVERESPONSE']._serialized_start=669
|
|
49
|
+
_globals['_RETRIEVERESPONSE']._serialized_end=856
|
|
50
|
+
_globals['_INGESTFEATURESREQUEST']._serialized_start=858
|
|
51
|
+
_globals['_INGESTFEATURESREQUEST']._serialized_end=947
|
|
52
|
+
_globals['_INGESTFEATURESRESPONSE']._serialized_start=949
|
|
53
|
+
_globals['_INGESTFEATURESRESPONSE']._serialized_end=988
|
|
54
|
+
_globals['_GENERATEKINREQUEST']._serialized_start=990
|
|
55
|
+
_globals['_GENERATEKINREQUEST']._serialized_end=1010
|
|
56
|
+
_globals['_GENERATEKINRESPONSE']._serialized_start=1012
|
|
57
|
+
_globals['_GENERATEKINRESPONSE']._serialized_end=1049
|
|
58
|
+
_globals['_UPDATEKINPROFILEREQUEST']._serialized_start=1052
|
|
59
|
+
_globals['_UPDATEKINPROFILEREQUEST']._serialized_end=1189
|
|
60
|
+
_globals['_UPDATEKINPROFILERESPONSE']._serialized_start=1191
|
|
61
|
+
_globals['_UPDATEKINPROFILERESPONSE']._serialized_end=1229
|
|
62
|
+
_globals['_UPDATEUSERPROFILEREQUEST']._serialized_start=1231
|
|
63
|
+
_globals['_UPDATEUSERPROFILEREQUEST']._serialized_end=1329
|
|
64
|
+
_globals['_UPDATEUSERPROFILERESPONSE']._serialized_start=1331
|
|
65
|
+
_globals['_UPDATEUSERPROFILERESPONSE']._serialized_end=1370
|
|
66
|
+
_globals['_HANDSHAKEREQUEST']._serialized_start=1372
|
|
67
|
+
_globals['_HANDSHAKEREQUEST']._serialized_end=1423
|
|
68
|
+
_globals['_HANDSHAKERESPONSE']._serialized_start=1425
|
|
69
|
+
_globals['_HANDSHAKERESPONSE']._serialized_end=1493
|
|
70
|
+
_globals['_FORGETTINGENGINE']._serialized_start=1496
|
|
71
|
+
_globals['_FORGETTINGENGINE']._serialized_end=2184
|
|
72
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
|
2
|
+
"""Client and server classes corresponding to protobuf-defined services."""
|
|
3
|
+
import grpc
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from forgetting_engine.proto import forgetting_engine_pb2 as forgetting__engine_dot_proto_dot_forgetting__engine__pb2
|
|
7
|
+
|
|
8
|
+
GRPC_GENERATED_VERSION = '1.80.0'
|
|
9
|
+
GRPC_VERSION = grpc.__version__
|
|
10
|
+
_version_not_supported = False
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from grpc._utilities import first_version_is_lower
|
|
14
|
+
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
|
|
15
|
+
except ImportError:
|
|
16
|
+
_version_not_supported = True
|
|
17
|
+
|
|
18
|
+
if _version_not_supported:
|
|
19
|
+
raise RuntimeError(
|
|
20
|
+
f'The grpc package installed is at version {GRPC_VERSION},'
|
|
21
|
+
+ ' but the generated code in forgetting_engine/proto/forgetting_engine_pb2_grpc.py depends on'
|
|
22
|
+
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
|
|
23
|
+
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
|
|
24
|
+
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ForgettingEngineStub(object):
|
|
29
|
+
"""---------------------------------------------------------------------------
|
|
30
|
+
Service 定义
|
|
31
|
+
---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, channel):
|
|
36
|
+
"""Constructor.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
channel: A grpc.Channel.
|
|
40
|
+
"""
|
|
41
|
+
self.Ingest = channel.unary_unary(
|
|
42
|
+
'/forgetting_engine.ForgettingEngine/Ingest',
|
|
43
|
+
request_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestRequest.SerializeToString,
|
|
44
|
+
response_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestResponse.FromString,
|
|
45
|
+
_registered_method=True)
|
|
46
|
+
self.Retrieve = channel.unary_unary(
|
|
47
|
+
'/forgetting_engine.ForgettingEngine/Retrieve',
|
|
48
|
+
request_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.RetrieveRequest.SerializeToString,
|
|
49
|
+
response_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.RetrieveResponse.FromString,
|
|
50
|
+
_registered_method=True)
|
|
51
|
+
self.IngestFeatures = channel.unary_unary(
|
|
52
|
+
'/forgetting_engine.ForgettingEngine/IngestFeatures',
|
|
53
|
+
request_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestFeaturesRequest.SerializeToString,
|
|
54
|
+
response_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestFeaturesResponse.FromString,
|
|
55
|
+
_registered_method=True)
|
|
56
|
+
self.GenerateKin = channel.unary_unary(
|
|
57
|
+
'/forgetting_engine.ForgettingEngine/GenerateKin',
|
|
58
|
+
request_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.GenerateKinRequest.SerializeToString,
|
|
59
|
+
response_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.GenerateKinResponse.FromString,
|
|
60
|
+
_registered_method=True)
|
|
61
|
+
self.UpdateKinProfile = channel.unary_unary(
|
|
62
|
+
'/forgetting_engine.ForgettingEngine/UpdateKinProfile',
|
|
63
|
+
request_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateKinProfileRequest.SerializeToString,
|
|
64
|
+
response_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateKinProfileResponse.FromString,
|
|
65
|
+
_registered_method=True)
|
|
66
|
+
self.UpdateUserProfile = channel.unary_unary(
|
|
67
|
+
'/forgetting_engine.ForgettingEngine/UpdateUserProfile',
|
|
68
|
+
request_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateUserProfileRequest.SerializeToString,
|
|
69
|
+
response_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateUserProfileResponse.FromString,
|
|
70
|
+
_registered_method=True)
|
|
71
|
+
self.Handshake = channel.unary_unary(
|
|
72
|
+
'/forgetting_engine.ForgettingEngine/Handshake',
|
|
73
|
+
request_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.HandshakeRequest.SerializeToString,
|
|
74
|
+
response_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.HandshakeResponse.FromString,
|
|
75
|
+
_registered_method=True)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ForgettingEngineServicer(object):
|
|
79
|
+
"""---------------------------------------------------------------------------
|
|
80
|
+
Service 定义
|
|
81
|
+
---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def Ingest(self, request, context):
|
|
86
|
+
"""摄入一条对话消息
|
|
87
|
+
"""
|
|
88
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
89
|
+
context.set_details('Method not implemented!')
|
|
90
|
+
raise NotImplementedError('Method not implemented!')
|
|
91
|
+
|
|
92
|
+
def Retrieve(self, request, context):
|
|
93
|
+
"""检索记忆上下文 + Kin画像 + User画像
|
|
94
|
+
"""
|
|
95
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
96
|
+
context.set_details('Method not implemented!')
|
|
97
|
+
raise NotImplementedError('Method not implemented!')
|
|
98
|
+
|
|
99
|
+
def IngestFeatures(self, request, context):
|
|
100
|
+
"""接收客户回传的特征信息,写入加权层
|
|
101
|
+
"""
|
|
102
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
103
|
+
context.set_details('Method not implemented!')
|
|
104
|
+
raise NotImplementedError('Method not implemented!')
|
|
105
|
+
|
|
106
|
+
def GenerateKin(self, request, context):
|
|
107
|
+
"""生成新的 kin_id(11 位数字,全局唯一)
|
|
108
|
+
"""
|
|
109
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
110
|
+
context.set_details('Method not implemented!')
|
|
111
|
+
raise NotImplementedError('Method not implemented!')
|
|
112
|
+
|
|
113
|
+
def UpdateKinProfile(self, request, context):
|
|
114
|
+
"""更新 Kin(数字公民)画像
|
|
115
|
+
"""
|
|
116
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
117
|
+
context.set_details('Method not implemented!')
|
|
118
|
+
raise NotImplementedError('Method not implemented!')
|
|
119
|
+
|
|
120
|
+
def UpdateUserProfile(self, request, context):
|
|
121
|
+
"""更新 User(用户)画像
|
|
122
|
+
"""
|
|
123
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
124
|
+
context.set_details('Method not implemented!')
|
|
125
|
+
raise NotImplementedError('Method not implemented!')
|
|
126
|
+
|
|
127
|
+
def Handshake(self, request, context):
|
|
128
|
+
"""握手配对:Kin_id ←→ User_id
|
|
129
|
+
"""
|
|
130
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
131
|
+
context.set_details('Method not implemented!')
|
|
132
|
+
raise NotImplementedError('Method not implemented!')
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def add_ForgettingEngineServicer_to_server(servicer, server):
|
|
136
|
+
rpc_method_handlers = {
|
|
137
|
+
'Ingest': grpc.unary_unary_rpc_method_handler(
|
|
138
|
+
servicer.Ingest,
|
|
139
|
+
request_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestRequest.FromString,
|
|
140
|
+
response_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestResponse.SerializeToString,
|
|
141
|
+
),
|
|
142
|
+
'Retrieve': grpc.unary_unary_rpc_method_handler(
|
|
143
|
+
servicer.Retrieve,
|
|
144
|
+
request_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.RetrieveRequest.FromString,
|
|
145
|
+
response_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.RetrieveResponse.SerializeToString,
|
|
146
|
+
),
|
|
147
|
+
'IngestFeatures': grpc.unary_unary_rpc_method_handler(
|
|
148
|
+
servicer.IngestFeatures,
|
|
149
|
+
request_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestFeaturesRequest.FromString,
|
|
150
|
+
response_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestFeaturesResponse.SerializeToString,
|
|
151
|
+
),
|
|
152
|
+
'GenerateKin': grpc.unary_unary_rpc_method_handler(
|
|
153
|
+
servicer.GenerateKin,
|
|
154
|
+
request_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.GenerateKinRequest.FromString,
|
|
155
|
+
response_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.GenerateKinResponse.SerializeToString,
|
|
156
|
+
),
|
|
157
|
+
'UpdateKinProfile': grpc.unary_unary_rpc_method_handler(
|
|
158
|
+
servicer.UpdateKinProfile,
|
|
159
|
+
request_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateKinProfileRequest.FromString,
|
|
160
|
+
response_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateKinProfileResponse.SerializeToString,
|
|
161
|
+
),
|
|
162
|
+
'UpdateUserProfile': grpc.unary_unary_rpc_method_handler(
|
|
163
|
+
servicer.UpdateUserProfile,
|
|
164
|
+
request_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateUserProfileRequest.FromString,
|
|
165
|
+
response_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateUserProfileResponse.SerializeToString,
|
|
166
|
+
),
|
|
167
|
+
'Handshake': grpc.unary_unary_rpc_method_handler(
|
|
168
|
+
servicer.Handshake,
|
|
169
|
+
request_deserializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.HandshakeRequest.FromString,
|
|
170
|
+
response_serializer=forgetting__engine_dot_proto_dot_forgetting__engine__pb2.HandshakeResponse.SerializeToString,
|
|
171
|
+
),
|
|
172
|
+
}
|
|
173
|
+
generic_handler = grpc.method_handlers_generic_handler(
|
|
174
|
+
'forgetting_engine.ForgettingEngine', rpc_method_handlers)
|
|
175
|
+
server.add_generic_rpc_handlers((generic_handler,))
|
|
176
|
+
server.add_registered_method_handlers('forgetting_engine.ForgettingEngine', rpc_method_handlers)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# This class is part of an EXPERIMENTAL API.
|
|
180
|
+
class ForgettingEngine(object):
|
|
181
|
+
"""---------------------------------------------------------------------------
|
|
182
|
+
Service 定义
|
|
183
|
+
---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def Ingest(request,
|
|
189
|
+
target,
|
|
190
|
+
options=(),
|
|
191
|
+
channel_credentials=None,
|
|
192
|
+
call_credentials=None,
|
|
193
|
+
insecure=False,
|
|
194
|
+
compression=None,
|
|
195
|
+
wait_for_ready=None,
|
|
196
|
+
timeout=None,
|
|
197
|
+
metadata=None):
|
|
198
|
+
return grpc.experimental.unary_unary(
|
|
199
|
+
request,
|
|
200
|
+
target,
|
|
201
|
+
'/forgetting_engine.ForgettingEngine/Ingest',
|
|
202
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestRequest.SerializeToString,
|
|
203
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestResponse.FromString,
|
|
204
|
+
options,
|
|
205
|
+
channel_credentials,
|
|
206
|
+
insecure,
|
|
207
|
+
call_credentials,
|
|
208
|
+
compression,
|
|
209
|
+
wait_for_ready,
|
|
210
|
+
timeout,
|
|
211
|
+
metadata,
|
|
212
|
+
_registered_method=True)
|
|
213
|
+
|
|
214
|
+
@staticmethod
|
|
215
|
+
def Retrieve(request,
|
|
216
|
+
target,
|
|
217
|
+
options=(),
|
|
218
|
+
channel_credentials=None,
|
|
219
|
+
call_credentials=None,
|
|
220
|
+
insecure=False,
|
|
221
|
+
compression=None,
|
|
222
|
+
wait_for_ready=None,
|
|
223
|
+
timeout=None,
|
|
224
|
+
metadata=None):
|
|
225
|
+
return grpc.experimental.unary_unary(
|
|
226
|
+
request,
|
|
227
|
+
target,
|
|
228
|
+
'/forgetting_engine.ForgettingEngine/Retrieve',
|
|
229
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.RetrieveRequest.SerializeToString,
|
|
230
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.RetrieveResponse.FromString,
|
|
231
|
+
options,
|
|
232
|
+
channel_credentials,
|
|
233
|
+
insecure,
|
|
234
|
+
call_credentials,
|
|
235
|
+
compression,
|
|
236
|
+
wait_for_ready,
|
|
237
|
+
timeout,
|
|
238
|
+
metadata,
|
|
239
|
+
_registered_method=True)
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def IngestFeatures(request,
|
|
243
|
+
target,
|
|
244
|
+
options=(),
|
|
245
|
+
channel_credentials=None,
|
|
246
|
+
call_credentials=None,
|
|
247
|
+
insecure=False,
|
|
248
|
+
compression=None,
|
|
249
|
+
wait_for_ready=None,
|
|
250
|
+
timeout=None,
|
|
251
|
+
metadata=None):
|
|
252
|
+
return grpc.experimental.unary_unary(
|
|
253
|
+
request,
|
|
254
|
+
target,
|
|
255
|
+
'/forgetting_engine.ForgettingEngine/IngestFeatures',
|
|
256
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestFeaturesRequest.SerializeToString,
|
|
257
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.IngestFeaturesResponse.FromString,
|
|
258
|
+
options,
|
|
259
|
+
channel_credentials,
|
|
260
|
+
insecure,
|
|
261
|
+
call_credentials,
|
|
262
|
+
compression,
|
|
263
|
+
wait_for_ready,
|
|
264
|
+
timeout,
|
|
265
|
+
metadata,
|
|
266
|
+
_registered_method=True)
|
|
267
|
+
|
|
268
|
+
@staticmethod
|
|
269
|
+
def GenerateKin(request,
|
|
270
|
+
target,
|
|
271
|
+
options=(),
|
|
272
|
+
channel_credentials=None,
|
|
273
|
+
call_credentials=None,
|
|
274
|
+
insecure=False,
|
|
275
|
+
compression=None,
|
|
276
|
+
wait_for_ready=None,
|
|
277
|
+
timeout=None,
|
|
278
|
+
metadata=None):
|
|
279
|
+
return grpc.experimental.unary_unary(
|
|
280
|
+
request,
|
|
281
|
+
target,
|
|
282
|
+
'/forgetting_engine.ForgettingEngine/GenerateKin',
|
|
283
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.GenerateKinRequest.SerializeToString,
|
|
284
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.GenerateKinResponse.FromString,
|
|
285
|
+
options,
|
|
286
|
+
channel_credentials,
|
|
287
|
+
insecure,
|
|
288
|
+
call_credentials,
|
|
289
|
+
compression,
|
|
290
|
+
wait_for_ready,
|
|
291
|
+
timeout,
|
|
292
|
+
metadata,
|
|
293
|
+
_registered_method=True)
|
|
294
|
+
|
|
295
|
+
@staticmethod
|
|
296
|
+
def UpdateKinProfile(request,
|
|
297
|
+
target,
|
|
298
|
+
options=(),
|
|
299
|
+
channel_credentials=None,
|
|
300
|
+
call_credentials=None,
|
|
301
|
+
insecure=False,
|
|
302
|
+
compression=None,
|
|
303
|
+
wait_for_ready=None,
|
|
304
|
+
timeout=None,
|
|
305
|
+
metadata=None):
|
|
306
|
+
return grpc.experimental.unary_unary(
|
|
307
|
+
request,
|
|
308
|
+
target,
|
|
309
|
+
'/forgetting_engine.ForgettingEngine/UpdateKinProfile',
|
|
310
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateKinProfileRequest.SerializeToString,
|
|
311
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateKinProfileResponse.FromString,
|
|
312
|
+
options,
|
|
313
|
+
channel_credentials,
|
|
314
|
+
insecure,
|
|
315
|
+
call_credentials,
|
|
316
|
+
compression,
|
|
317
|
+
wait_for_ready,
|
|
318
|
+
timeout,
|
|
319
|
+
metadata,
|
|
320
|
+
_registered_method=True)
|
|
321
|
+
|
|
322
|
+
@staticmethod
|
|
323
|
+
def UpdateUserProfile(request,
|
|
324
|
+
target,
|
|
325
|
+
options=(),
|
|
326
|
+
channel_credentials=None,
|
|
327
|
+
call_credentials=None,
|
|
328
|
+
insecure=False,
|
|
329
|
+
compression=None,
|
|
330
|
+
wait_for_ready=None,
|
|
331
|
+
timeout=None,
|
|
332
|
+
metadata=None):
|
|
333
|
+
return grpc.experimental.unary_unary(
|
|
334
|
+
request,
|
|
335
|
+
target,
|
|
336
|
+
'/forgetting_engine.ForgettingEngine/UpdateUserProfile',
|
|
337
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateUserProfileRequest.SerializeToString,
|
|
338
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.UpdateUserProfileResponse.FromString,
|
|
339
|
+
options,
|
|
340
|
+
channel_credentials,
|
|
341
|
+
insecure,
|
|
342
|
+
call_credentials,
|
|
343
|
+
compression,
|
|
344
|
+
wait_for_ready,
|
|
345
|
+
timeout,
|
|
346
|
+
metadata,
|
|
347
|
+
_registered_method=True)
|
|
348
|
+
|
|
349
|
+
@staticmethod
|
|
350
|
+
def Handshake(request,
|
|
351
|
+
target,
|
|
352
|
+
options=(),
|
|
353
|
+
channel_credentials=None,
|
|
354
|
+
call_credentials=None,
|
|
355
|
+
insecure=False,
|
|
356
|
+
compression=None,
|
|
357
|
+
wait_for_ready=None,
|
|
358
|
+
timeout=None,
|
|
359
|
+
metadata=None):
|
|
360
|
+
return grpc.experimental.unary_unary(
|
|
361
|
+
request,
|
|
362
|
+
target,
|
|
363
|
+
'/forgetting_engine.ForgettingEngine/Handshake',
|
|
364
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.HandshakeRequest.SerializeToString,
|
|
365
|
+
forgetting__engine_dot_proto_dot_forgetting__engine__pb2.HandshakeResponse.FromString,
|
|
366
|
+
options,
|
|
367
|
+
channel_credentials,
|
|
368
|
+
insecure,
|
|
369
|
+
call_credentials,
|
|
370
|
+
compression,
|
|
371
|
+
wait_for_ready,
|
|
372
|
+
timeout,
|
|
373
|
+
metadata,
|
|
374
|
+
_registered_method=True)
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP stdio 服务 — gRPC 代理层。
|
|
3
|
+
|
|
4
|
+
通过 stdin/stdout JSON-RPC 与 MCP 宿主(Claude Desktop 等)通信,
|
|
5
|
+
内部转调 gRPC 到 knownot.cc 遗忘引擎服务端。
|
|
6
|
+
|
|
7
|
+
协议:MCP 2024-11-05 / JSON-RPC 2.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
import grpc
|
|
16
|
+
|
|
17
|
+
from forgetting_engine_mcp.proto import forgetting_engine_pb2 as pb
|
|
18
|
+
from forgetting_engine_mcp.proto import forgetting_engine_pb2_grpc as pb_grpc
|
|
19
|
+
|
|
20
|
+
# ---- Tool 定义 ----
|
|
21
|
+
|
|
22
|
+
TOOLS = [
|
|
23
|
+
{
|
|
24
|
+
"name": "forgetting_engine_ingest",
|
|
25
|
+
"description": (
|
|
26
|
+
"摄入一条对话消息到遗忘引擎长期记忆。"
|
|
27
|
+
"将用户或助手的消息写入持久记忆存储,自动推进 M-Clock。"
|
|
28
|
+
"可附带 features 回传上次 retrieve 中要求提取的特征信息。"
|
|
29
|
+
),
|
|
30
|
+
"inputSchema": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"kin_id": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Kin 标识(11 位数字,全局唯一)",
|
|
36
|
+
},
|
|
37
|
+
"role": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"enum": ["user", "assistant"],
|
|
40
|
+
"description": "角色:user(用户)或 assistant(助手)",
|
|
41
|
+
},
|
|
42
|
+
"text": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "消息正文",
|
|
45
|
+
},
|
|
46
|
+
"session_id": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"description": "会话标识",
|
|
49
|
+
},
|
|
50
|
+
"features": {
|
|
51
|
+
"type": "array",
|
|
52
|
+
"description": "客户端回传的特征信息(首次调用可为空)",
|
|
53
|
+
"items": {
|
|
54
|
+
"type": "object",
|
|
55
|
+
"properties": {
|
|
56
|
+
"category": {"type": "string"},
|
|
57
|
+
"text": {"type": "string"},
|
|
58
|
+
},
|
|
59
|
+
"required": ["category", "text"],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
"required": ["kin_id", "role", "text", "session_id"],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "forgetting_engine_handshake",
|
|
68
|
+
"description": (
|
|
69
|
+
"握手配对:将您的 User_id 与 Kin_id 建立配对关系。\n\n"
|
|
70
|
+
"如果您已有 Kin_id,请同时提供 Kin_id 和 User_id。\n"
|
|
71
|
+
"如果还没有 Kin_id,只需提供 User_id(8 位数字字母组合),引擎会为您生成新的 Kin_id。\n\n"
|
|
72
|
+
"⚠️ 每个新会话开始时,如果尚未配对,应当先调用此工具。"
|
|
73
|
+
),
|
|
74
|
+
"inputSchema": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"properties": {
|
|
77
|
+
"user_id": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "您的 User_id(8 位数字字母组合)",
|
|
80
|
+
},
|
|
81
|
+
"kin_id": {
|
|
82
|
+
"type": "string",
|
|
83
|
+
"description": "您的 Kin_id(11 位数字)(可选,如无则自动生成)",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
"required": ["user_id"],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "forgetting_engine_retrieve",
|
|
91
|
+
"description": (
|
|
92
|
+
"从遗忘引擎检索 Kin(数字公民)的长期记忆和画像。\n\n"
|
|
93
|
+
"返回内容包括:\n"
|
|
94
|
+
"1. Kin画像(必返):包含角色、语言、经验、偏好等信息\n"
|
|
95
|
+
"2. User画像(必返):包含当前用户的特质和身份信息\n"
|
|
96
|
+
"3. 相关记忆上下文(按需):聚焦窗口、最近记事、记忆摘要等\n"
|
|
97
|
+
"4. 引导问题(仅初次):未握手配对时返回握手引导\n\n"
|
|
98
|
+
"⚠️ 收到握手引导后,请调用 forgetting_engine_handshake 工具完成配对。"
|
|
99
|
+
),
|
|
100
|
+
"inputSchema": {
|
|
101
|
+
"type": "object",
|
|
102
|
+
"properties": {
|
|
103
|
+
"kin_id": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "Kin 标识(11 位数字,全局唯一)",
|
|
106
|
+
},
|
|
107
|
+
"query": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "检索查询语句(自然语言,如用户当前的问题)",
|
|
110
|
+
},
|
|
111
|
+
"session_id": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"description": "可选的会话过滤",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
"required": ["kin_id", "query"],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"name": "forgetting_engine_ingest_features",
|
|
121
|
+
"description": (
|
|
122
|
+
"接收从 Prompt 末尾 [特征提取请求] 中提取的用户关键特征信息,"
|
|
123
|
+
"写入加权记忆层。用于单独更新用户画像,不关联具体消息。"
|
|
124
|
+
),
|
|
125
|
+
"inputSchema": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"properties": {
|
|
128
|
+
"kin_id": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"description": "Kin 标识(11 位数字,全局唯一)",
|
|
131
|
+
},
|
|
132
|
+
"features": {
|
|
133
|
+
"type": "array",
|
|
134
|
+
"description": "特征列表",
|
|
135
|
+
"items": {
|
|
136
|
+
"type": "object",
|
|
137
|
+
"properties": {
|
|
138
|
+
"category": {"type": "string"},
|
|
139
|
+
"text": {"type": "string"},
|
|
140
|
+
},
|
|
141
|
+
"required": ["category", "text"],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
"required": ["kin_id", "features"],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "forgetting_engine_generate_kin",
|
|
150
|
+
"description": (
|
|
151
|
+
"生成一个新的 kin_id(11 位数字,全局唯一)。\n"
|
|
152
|
+
"Kin 是数字公民,拥有独立的记忆体和画像。\n"
|
|
153
|
+
"返回的 ID 需本地留存,下次调用 ingest/retrieve 时作为 kin_id 参数传入。"
|
|
154
|
+
),
|
|
155
|
+
"inputSchema": {
|
|
156
|
+
"type": "object",
|
|
157
|
+
"properties": {},
|
|
158
|
+
"required": [],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "forgetting_engine_update_kin_profile",
|
|
163
|
+
"description": (
|
|
164
|
+
"更新 Kin(数字公民)的画像信息。"
|
|
165
|
+
"在完成身份引导问答后调用此工具,将用户提供的角色、经验、偏好等信息写入Kin画像。"
|
|
166
|
+
"只能在 retrieve 返回 guidance 不为空时调用。"
|
|
167
|
+
),
|
|
168
|
+
"inputSchema": {
|
|
169
|
+
"type": "object",
|
|
170
|
+
"properties": {
|
|
171
|
+
"kin_id": {
|
|
172
|
+
"type": "string",
|
|
173
|
+
"description": "Kin 标识(11 位数字)",
|
|
174
|
+
},
|
|
175
|
+
"roles": {
|
|
176
|
+
"type": "string",
|
|
177
|
+
"description": "JSON数组:角色列表",
|
|
178
|
+
},
|
|
179
|
+
"languages": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"description": "JSON数组:语言列表",
|
|
182
|
+
},
|
|
183
|
+
"experience": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"description": "经验描述",
|
|
186
|
+
},
|
|
187
|
+
"preferences": {
|
|
188
|
+
"type": "string",
|
|
189
|
+
"description": "JSON对象:偏好键值对",
|
|
190
|
+
},
|
|
191
|
+
"raw_profile": {
|
|
192
|
+
"type": "string",
|
|
193
|
+
"description": "完整画像JSON",
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
"required": ["kin_id", "roles"],
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"name": "forgetting_engine_update_user_profile",
|
|
201
|
+
"description": (
|
|
202
|
+
"更新 User(用户自身)的画像信息。记录用户特质和身份。"
|
|
203
|
+
),
|
|
204
|
+
"inputSchema": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"properties": {
|
|
207
|
+
"user_id": {
|
|
208
|
+
"type": "string",
|
|
209
|
+
"description": "User标识(当前用kin_id兼任)",
|
|
210
|
+
},
|
|
211
|
+
"traits": {
|
|
212
|
+
"type": "string",
|
|
213
|
+
"description": "JSON数组:特质列表",
|
|
214
|
+
},
|
|
215
|
+
"identity": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"description": "身份描述",
|
|
218
|
+
},
|
|
219
|
+
"raw_profile": {
|
|
220
|
+
"type": "string",
|
|
221
|
+
"description": "完整画像JSON",
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
"required": ["user_id", "traits"],
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# ---- MCP Server (Stdio Proxy) ----
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class MCPProxyServer:
|
|
234
|
+
"""MCP 代理服务器:stdio JSON-RPC → gRPC 透传。"""
|
|
235
|
+
|
|
236
|
+
def __init__(self, endpoint: str, api_key: str | None = None):
|
|
237
|
+
self._endpoint = endpoint
|
|
238
|
+
self._metadata = []
|
|
239
|
+
if api_key:
|
|
240
|
+
self._metadata.append(("x-api-key", api_key))
|
|
241
|
+
|
|
242
|
+
self._channel = grpc.aio.insecure_channel(endpoint)
|
|
243
|
+
self._stub = pb_grpc.ForgettingEngineStub(self._channel)
|
|
244
|
+
|
|
245
|
+
async def close(self):
|
|
246
|
+
await self._channel.close()
|
|
247
|
+
|
|
248
|
+
# ---- Tool 调用 → gRPC ----
|
|
249
|
+
|
|
250
|
+
async def _call_handshake(self, args: dict) -> dict:
|
|
251
|
+
req = pb.HandshakeRequest(
|
|
252
|
+
user_id=args["user_id"],
|
|
253
|
+
kin_id=args.get("kin_id", ""),
|
|
254
|
+
)
|
|
255
|
+
try:
|
|
256
|
+
resp = await self._stub.Handshake(req, metadata=self._metadata)
|
|
257
|
+
return {"content": [{"type": "text", "text": json.dumps({
|
|
258
|
+
"kin_id": resp.kin_id,
|
|
259
|
+
"user_id": resp.user_id,
|
|
260
|
+
"is_new": resp.is_new,
|
|
261
|
+
"message": "配对成功!" if resp.is_new else "握手成功!",
|
|
262
|
+
}, ensure_ascii=False)}]}
|
|
263
|
+
except grpc.AioRpcError as e:
|
|
264
|
+
if e.code() in (grpc.StatusCode.NOT_FOUND, grpc.StatusCode.INVALID_ARGUMENT,
|
|
265
|
+
grpc.StatusCode.UNAUTHENTICATED, grpc.StatusCode.PERMISSION_DENIED):
|
|
266
|
+
return {"content": [{"type": "text", "text": json.dumps({"error": e.details()}, ensure_ascii=False)}], "isError": True}
|
|
267
|
+
raise
|
|
268
|
+
|
|
269
|
+
async def _call_ingest(self, args: dict) -> dict:
|
|
270
|
+
features = None
|
|
271
|
+
if args.get("features"):
|
|
272
|
+
features = [
|
|
273
|
+
pb.FeatureItem(category=f["category"], text=f["text"])
|
|
274
|
+
for f in args["features"]
|
|
275
|
+
]
|
|
276
|
+
req = pb.IngestRequest(
|
|
277
|
+
kin_id=args["kin_id"],
|
|
278
|
+
role=args["role"],
|
|
279
|
+
text=args["text"],
|
|
280
|
+
session_id=args["session_id"],
|
|
281
|
+
features=features,
|
|
282
|
+
)
|
|
283
|
+
try:
|
|
284
|
+
resp = await self._stub.Ingest(req, metadata=self._metadata)
|
|
285
|
+
return {"content": [{"type": "text", "text": json.dumps({"msg_id": resp.msg_id}, ensure_ascii=False)}]}
|
|
286
|
+
except grpc.AioRpcError as e:
|
|
287
|
+
if e.code() == grpc.StatusCode.INVALID_ARGUMENT:
|
|
288
|
+
return {"content": [{"type": "text", "text": json.dumps({"error": e.details(), "_status": 422}, ensure_ascii=False)}], "isError": True}
|
|
289
|
+
raise
|
|
290
|
+
|
|
291
|
+
async def _call_retrieve(self, args: dict) -> dict:
|
|
292
|
+
req = pb.RetrieveRequest(
|
|
293
|
+
kin_id=args["kin_id"],
|
|
294
|
+
query=args["query"],
|
|
295
|
+
session_id=args.get("session_id") or None,
|
|
296
|
+
)
|
|
297
|
+
try:
|
|
298
|
+
resp = await self._stub.Retrieve(req, metadata=self._metadata)
|
|
299
|
+
except grpc.AioRpcError as e:
|
|
300
|
+
if e.code() in (grpc.StatusCode.INVALID_ARGUMENT, grpc.StatusCode.UNAUTHENTICATED,
|
|
301
|
+
grpc.StatusCode.PERMISSION_DENIED):
|
|
302
|
+
return {"content": [{"type": "text", "text": json.dumps({"error": e.details()}, ensure_ascii=False)}], "isError": True}
|
|
303
|
+
raise
|
|
304
|
+
result = {
|
|
305
|
+
"kin_profile": {
|
|
306
|
+
"kin_id": resp.kin_profile.kin_id,
|
|
307
|
+
"roles": resp.kin_profile.roles,
|
|
308
|
+
"languages": resp.kin_profile.languages,
|
|
309
|
+
"experience": resp.kin_profile.experience,
|
|
310
|
+
"preferences": resp.kin_profile.preferences,
|
|
311
|
+
"raw_profile": resp.kin_profile.raw_profile,
|
|
312
|
+
},
|
|
313
|
+
"user_profile": {
|
|
314
|
+
"user_id": resp.user_profile.user_id,
|
|
315
|
+
"traits": resp.user_profile.traits,
|
|
316
|
+
"identity": resp.user_profile.identity,
|
|
317
|
+
"raw_profile": resp.user_profile.raw_profile,
|
|
318
|
+
},
|
|
319
|
+
"prompt": resp.prompt,
|
|
320
|
+
"guidance": [
|
|
321
|
+
{"type": g.type, "question": g.question, "options": list(g.options)}
|
|
322
|
+
for g in resp.guidance
|
|
323
|
+
],
|
|
324
|
+
}
|
|
325
|
+
return {"content": [{"type": "text", "text": json.dumps(result, ensure_ascii=False)}]}
|
|
326
|
+
|
|
327
|
+
async def _call_ingest_features(self, args: dict) -> dict:
|
|
328
|
+
features = [
|
|
329
|
+
pb.FeatureItem(category=f["category"], text=f["text"])
|
|
330
|
+
for f in args["features"]
|
|
331
|
+
]
|
|
332
|
+
req = pb.IngestFeaturesRequest(
|
|
333
|
+
kin_id=args["kin_id"],
|
|
334
|
+
features=features,
|
|
335
|
+
)
|
|
336
|
+
try:
|
|
337
|
+
resp = await self._stub.IngestFeatures(req, metadata=self._metadata)
|
|
338
|
+
return {"content": [{"type": "text", "text": json.dumps({"count": resp.count}, ensure_ascii=False)}]}
|
|
339
|
+
except grpc.AioRpcError as e:
|
|
340
|
+
if e.code() == grpc.StatusCode.INVALID_ARGUMENT:
|
|
341
|
+
return {"content": [{"type": "text", "text": json.dumps({"error": e.details(), "_status": 422}, ensure_ascii=False)}], "isError": True}
|
|
342
|
+
raise
|
|
343
|
+
|
|
344
|
+
async def _call_generate_kin(self, _args: dict) -> dict:
|
|
345
|
+
req = pb.GenerateKinRequest()
|
|
346
|
+
try:
|
|
347
|
+
resp = await self._stub.GenerateKin(req, metadata=self._metadata)
|
|
348
|
+
return {"content": [{"type": "text", "text": json.dumps({"kin_id": resp.kin_id}, ensure_ascii=False)}]}
|
|
349
|
+
except grpc.AioRpcError as e:
|
|
350
|
+
if e.code() == grpc.StatusCode.UNAUTHENTICATED:
|
|
351
|
+
return {"content": [{"type": "text", "text": json.dumps({"error": e.details()}, ensure_ascii=False)}], "isError": True}
|
|
352
|
+
raise
|
|
353
|
+
|
|
354
|
+
async def _call_update_kin_profile(self, args: dict) -> dict:
|
|
355
|
+
req = pb.UpdateKinProfileRequest(
|
|
356
|
+
kin_id=args["kin_id"],
|
|
357
|
+
roles=args.get("roles", ""),
|
|
358
|
+
languages=args.get("languages", ""),
|
|
359
|
+
experience=args.get("experience", ""),
|
|
360
|
+
preferences=args.get("preferences", ""),
|
|
361
|
+
raw_profile=args.get("raw_profile", ""),
|
|
362
|
+
)
|
|
363
|
+
try:
|
|
364
|
+
resp = await self._stub.UpdateKinProfile(req, metadata=self._metadata)
|
|
365
|
+
return {"content": [{"type": "text", "text": json.dumps({"ok": resp.ok}, ensure_ascii=False)}]}
|
|
366
|
+
except grpc.AioRpcError as e:
|
|
367
|
+
if e.code() == grpc.StatusCode.UNAUTHENTICATED:
|
|
368
|
+
return {"content": [{"type": "text", "text": json.dumps({"error": e.details()}, ensure_ascii=False)}], "isError": True}
|
|
369
|
+
raise
|
|
370
|
+
|
|
371
|
+
async def _call_update_user_profile(self, args: dict) -> dict:
|
|
372
|
+
req = pb.UpdateUserProfileRequest(
|
|
373
|
+
user_id=args["user_id"],
|
|
374
|
+
traits=args.get("traits", ""),
|
|
375
|
+
identity=args.get("identity", ""),
|
|
376
|
+
raw_profile=args.get("raw_profile", ""),
|
|
377
|
+
)
|
|
378
|
+
try:
|
|
379
|
+
resp = await self._stub.UpdateUserProfile(req, metadata=self._metadata)
|
|
380
|
+
return {"content": [{"type": "text", "text": json.dumps({"ok": resp.ok}, ensure_ascii=False)}]}
|
|
381
|
+
except grpc.AioRpcError as e:
|
|
382
|
+
if e.code() == grpc.StatusCode.UNAUTHENTICATED:
|
|
383
|
+
return {"content": [{"type": "text", "text": json.dumps({"error": e.details()}, ensure_ascii=False)}], "isError": True}
|
|
384
|
+
raise
|
|
385
|
+
|
|
386
|
+
async def call_tool(self, name: str, arguments: dict) -> dict:
|
|
387
|
+
if name == "forgetting_engine_ingest":
|
|
388
|
+
return await self._call_ingest(arguments)
|
|
389
|
+
elif name == "forgetting_engine_retrieve":
|
|
390
|
+
return await self._call_retrieve(arguments)
|
|
391
|
+
elif name == "forgetting_engine_ingest_features":
|
|
392
|
+
return await self._call_ingest_features(arguments)
|
|
393
|
+
elif name == "forgetting_engine_generate_kin":
|
|
394
|
+
return await self._call_generate_kin(arguments)
|
|
395
|
+
elif name == "forgetting_engine_handshake":
|
|
396
|
+
return await self._call_handshake(arguments)
|
|
397
|
+
elif name == "forgetting_engine_update_kin_profile":
|
|
398
|
+
return await self._call_update_kin_profile(arguments)
|
|
399
|
+
elif name == "forgetting_engine_update_user_profile":
|
|
400
|
+
return await self._call_update_user_profile(arguments)
|
|
401
|
+
else:
|
|
402
|
+
raise ValueError(f"未知的 MCP 工具: {name}")
|
|
403
|
+
|
|
404
|
+
# ---- JSON-RPC 处理 ----
|
|
405
|
+
|
|
406
|
+
async def handle_jsonrpc(self, request: dict) -> dict:
|
|
407
|
+
req_id = request.get("id")
|
|
408
|
+
method = request.get("method", "")
|
|
409
|
+
params = request.get("params", {})
|
|
410
|
+
|
|
411
|
+
try:
|
|
412
|
+
if method == "tools/list":
|
|
413
|
+
result = {"tools": TOOLS}
|
|
414
|
+
elif method == "tools/call":
|
|
415
|
+
tool_name = params.get("name", "")
|
|
416
|
+
tool_args = params.get("arguments", {})
|
|
417
|
+
result = await self.call_tool(tool_name, tool_args)
|
|
418
|
+
elif method == "initialize":
|
|
419
|
+
result = {
|
|
420
|
+
"protocolVersion": "2024-11-05",
|
|
421
|
+
"serverInfo": {
|
|
422
|
+
"name": "forgetting-engine",
|
|
423
|
+
"version": "0.4.0",
|
|
424
|
+
},
|
|
425
|
+
"capabilities": {"tools": {}},
|
|
426
|
+
}
|
|
427
|
+
else:
|
|
428
|
+
return {
|
|
429
|
+
"jsonrpc": "2.0",
|
|
430
|
+
"id": req_id,
|
|
431
|
+
"error": {"code": -32601, "message": f"未知方法: {method}"},
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return {"jsonrpc": "2.0", "id": req_id, "result": result}
|
|
435
|
+
|
|
436
|
+
except grpc.AioRpcError as e:
|
|
437
|
+
code = e.code()
|
|
438
|
+
if code == grpc.StatusCode.UNAUTHENTICATED:
|
|
439
|
+
msg = "API Key 无效或已停用"
|
|
440
|
+
elif code == grpc.StatusCode.PERMISSION_DENIED:
|
|
441
|
+
msg = f"无权访问该 Kin: {e.details()}"
|
|
442
|
+
else:
|
|
443
|
+
msg = f"服务端错误: {e.details()}"
|
|
444
|
+
return {
|
|
445
|
+
"jsonrpc": "2.0",
|
|
446
|
+
"id": req_id,
|
|
447
|
+
"error": {"code": -32000, "message": msg},
|
|
448
|
+
}
|
|
449
|
+
except Exception as e:
|
|
450
|
+
return {
|
|
451
|
+
"jsonrpc": "2.0",
|
|
452
|
+
"id": req_id,
|
|
453
|
+
"error": {"code": -32000, "message": str(e)},
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
# ---- Stdio 循环 ----
|
|
457
|
+
|
|
458
|
+
async def run_stdio(self) -> None:
|
|
459
|
+
loop = asyncio.get_event_loop()
|
|
460
|
+
|
|
461
|
+
try:
|
|
462
|
+
while True:
|
|
463
|
+
line = await loop.run_in_executor(None, sys.stdin.readline)
|
|
464
|
+
if not line:
|
|
465
|
+
break
|
|
466
|
+
|
|
467
|
+
line = line.strip()
|
|
468
|
+
if not line:
|
|
469
|
+
continue
|
|
470
|
+
|
|
471
|
+
try:
|
|
472
|
+
request = json.loads(line)
|
|
473
|
+
except json.JSONDecodeError:
|
|
474
|
+
continue
|
|
475
|
+
|
|
476
|
+
response = await self.handle_jsonrpc(request)
|
|
477
|
+
output = json.dumps(response, ensure_ascii=False) + "\n"
|
|
478
|
+
await loop.run_in_executor(None, sys.stdout.write, output)
|
|
479
|
+
await loop.run_in_executor(None, sys.stdout.flush)
|
|
480
|
+
|
|
481
|
+
finally:
|
|
482
|
+
await self.close()
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
# ---- 入口 ----
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
async def main():
|
|
489
|
+
endpoint = os.getenv("FE_ENDPOINT", "knownot.cc:50054")
|
|
490
|
+
api_key = os.getenv("FE_API_KEY", "")
|
|
491
|
+
|
|
492
|
+
if not api_key:
|
|
493
|
+
print("[MCP] 未设置 FE_API_KEY,请求将不携带鉴权信息", file=sys.stderr)
|
|
494
|
+
else:
|
|
495
|
+
print(f"[MCP] 连接 {endpoint} (API Key: {api_key[:12]}...)", file=sys.stderr)
|
|
496
|
+
|
|
497
|
+
server = MCPProxyServer(endpoint=endpoint, api_key=api_key or None)
|
|
498
|
+
await server.run_stdio()
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: forgetting-engine-mcp
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: 遗忘引擎 MCP 客户端 — 将 Claude Desktop 连接到 knownot.cc 长期记忆服务
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: grpcio>=1.80.0
|
|
9
|
+
Requires-Dist: protobuf>=4.21
|
|
10
|
+
|
|
11
|
+
# 遗忘引擎 MCP 客户端
|
|
12
|
+
|
|
13
|
+
将 Claude Desktop 连接到 [knownot.cc](https://knownot.cc) 遗忘引擎长期记忆服务。
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Claude Desktop ──stdio──→ MCP Proxy ──gRPC──→ knownot.cc:5005x
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 安装
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install forgetting-engine-mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## 获取 Kin ID
|
|
26
|
+
|
|
27
|
+
安装并配置好 MCP 后,在对话中调用:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
使用 forgetting_engine_generate_kin 工具
|
|
31
|
+
→ 返回 {"kin_id": "84729105361"}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
复制输出的 kin_id 留存,后续 ingest/retrieve 时作为 kin_id 传入。
|
|
35
|
+
|
|
36
|
+
## 配置
|
|
37
|
+
|
|
38
|
+
### Claude Desktop
|
|
39
|
+
|
|
40
|
+
编辑 `claude_desktop_config.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"mcpServers": {
|
|
45
|
+
"forgetting-engine": {
|
|
46
|
+
"command": "mcp-forgetting-engine",
|
|
47
|
+
"env": {
|
|
48
|
+
"FE_ENDPOINT": "knownot.cc:50051",
|
|
49
|
+
"FE_API_KEY": "fe_ak_your_key_here"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Claude Code
|
|
57
|
+
|
|
58
|
+
编辑 `~/.claude.json`:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"forgetting-engine": {
|
|
64
|
+
"command": "mcp-forgetting-engine",
|
|
65
|
+
"env": {
|
|
66
|
+
"FE_ENDPOINT": "knownot.cc:50051",
|
|
67
|
+
"FE_API_KEY": "fe_ak_your_key_here"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 选择实例
|
|
75
|
+
|
|
76
|
+
| 实例 | FE_ENDPOINT | 说明 |
|
|
77
|
+
|------|-------------|------|
|
|
78
|
+
| 通用版 | `knownot.cc:50051` | allergy, preference, constraint, danger |
|
|
79
|
+
| 护肤增强版 | `knownot.cc:50052` | 通用 + skincare_* |
|
|
80
|
+
| 法律增强版 | `knownot.cc:50053` | 通用 + legal_* |
|
|
81
|
+
| 编程增强版 | `knownot.cc:50054` | 通用 + coding_* |
|
|
82
|
+
|
|
83
|
+
## 环境变量
|
|
84
|
+
|
|
85
|
+
| 变量 | 必填 | 默认值 | 说明 |
|
|
86
|
+
|------|------|--------|------|
|
|
87
|
+
| `FE_ENDPOINT` | 否 | `knownot.cc:50051` | 遗忘引擎服务端地址 |
|
|
88
|
+
| `FE_API_KEY` | 是 | - | API Key(从 knownot.cc 门户创建) |
|
|
89
|
+
|
|
90
|
+
## 可用工具
|
|
91
|
+
|
|
92
|
+
| 工具 | 说明 |
|
|
93
|
+
|------|------|
|
|
94
|
+
| `forgetting_engine_generate_kin` | 生成新的 11 位 kin_id |
|
|
95
|
+
| `forgetting_engine_ingest` | 摄入对话消息到长期记忆 |
|
|
96
|
+
| `forgetting_engine_retrieve` | 检索记忆上下文 |
|
|
97
|
+
| `forgetting_engine_ingest_features` | 回传/更新用户特征信息 |
|
|
98
|
+
|
|
99
|
+
## 支持的 MCP 宿主
|
|
100
|
+
|
|
101
|
+
| 工具 | 配置文件 | 格式 | 状态 |
|
|
102
|
+
|------|---------|------|------|
|
|
103
|
+
| Claude Code | `~/.claude.json` | JSON | 已验证 |
|
|
104
|
+
| Claude Desktop | `claude_desktop_config.json` | JSON | 已验证 |
|
|
105
|
+
| Cursor | `~/.cursor/mcp.json` | JSON | 已验证 |
|
|
106
|
+
| Codex (OpenAI) | `~/.codex/config.toml` | TOML | 待验证 |
|
|
107
|
+
| Hermes | `~/.hermes/config.yaml` | YAML | 待验证 |
|
|
108
|
+
| OpenCode | `.opencode.json` | JSON/JSONC | 待验证 |
|
|
109
|
+
| OpenClaw | — | — | 不支持 MCP 客户端 |
|
|
110
|
+
|
|
111
|
+
### Cursor 配置
|
|
112
|
+
|
|
113
|
+
编辑 `~/.cursor/mcp.json`(全局)或 `.cursor/mcp.json`(项目):
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"mcpServers": {
|
|
118
|
+
"forgetting-engine": {
|
|
119
|
+
"command": "mcp-forgetting-engine",
|
|
120
|
+
"env": {
|
|
121
|
+
"FE_ENDPOINT": "knownot.cc:50051",
|
|
122
|
+
"FE_API_KEY": "fe_ak_your_key_here"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Codex 配置
|
|
130
|
+
|
|
131
|
+
编辑 `~/.codex/config.toml`:
|
|
132
|
+
|
|
133
|
+
```toml
|
|
134
|
+
[mcp_servers.forgetting-engine]
|
|
135
|
+
type = "stdio"
|
|
136
|
+
command = "mcp-forgetting-engine"
|
|
137
|
+
args = []
|
|
138
|
+
|
|
139
|
+
[mcp_servers.forgetting-engine.env]
|
|
140
|
+
FE_ENDPOINT = "knownot.cc:50051"
|
|
141
|
+
FE_API_KEY = "fe_ak_your_key_here"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Hermes 配置
|
|
145
|
+
|
|
146
|
+
编辑 `~/.hermes/config.yaml`:
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
mcp_servers:
|
|
150
|
+
forgetting-engine:
|
|
151
|
+
command: mcp-forgetting-engine
|
|
152
|
+
env:
|
|
153
|
+
FE_ENDPOINT: knownot.cc:50051
|
|
154
|
+
FE_API_KEY: fe_ak_your_key_here
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### OpenCode 配置
|
|
158
|
+
|
|
159
|
+
编辑项目根目录的 `.opencode.json` 或 `~/.opencode.json`:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"$schema": "https://opencode.ai/config.json",
|
|
164
|
+
"mcp": {
|
|
165
|
+
"forgetting-engine": {
|
|
166
|
+
"type": "local",
|
|
167
|
+
"command": ["mcp-forgetting-engine"],
|
|
168
|
+
"enabled": true,
|
|
169
|
+
"environment": {
|
|
170
|
+
"FE_ENDPOINT": "knownot.cc:50051",
|
|
171
|
+
"FE_API_KEY": "fe_ak_your_key_here"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 获取 API Key
|
|
179
|
+
|
|
180
|
+
访问 [knownot.cc](https://knownot.cc) 登录后在 "API Keys" 页面创建 Key。
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
forgetting_engine_mcp/__init__.py,sha256=GCh1n1rd3LA0p9m9WmtREQqDuEqc18w-8DOav0KPah4,426
|
|
2
|
+
forgetting_engine_mcp/__main__.py,sha256=wUywDPOrf0HTJ8espTh8Xt3fphFLD126WlDWa2qlb8I,310
|
|
3
|
+
forgetting_engine_mcp/server.py,sha256=Kl6zJxREZr-XzNao5aZGXHNkDa1N8W4v5c2t6pULG3Q,19673
|
|
4
|
+
forgetting_engine_mcp/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
forgetting_engine_mcp/proto/forgetting_engine_pb2.py,sha256=0Hs8ENGElZ5aPXJFVuCrpPVMCtu89ghbMTImAdkB-Kc,6743
|
|
6
|
+
forgetting_engine_mcp/proto/forgetting_engine_pb2_grpc.py,sha256=EqoLRfnFrgF3rJa_v831HHKEeiWncWzxgxYnmHy80Hs,16240
|
|
7
|
+
forgetting_engine_mcp-0.4.0.dist-info/METADATA,sha256=tehgCsiVfftlG2RyZNMnKVgUX6khcvk0_U566xZX9xo,4116
|
|
8
|
+
forgetting_engine_mcp-0.4.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
9
|
+
forgetting_engine_mcp-0.4.0.dist-info/entry_points.txt,sha256=fM3WyOfLYYqtSsln2mPV1yXP2q14sR8JnU4EVHg3re0,78
|
|
10
|
+
forgetting_engine_mcp-0.4.0.dist-info/top_level.txt,sha256=HngLBMTu_40m277T5-3NO4uPcysCK68qHSazRIlSm8M,22
|
|
11
|
+
forgetting_engine_mcp-0.4.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
forgetting_engine_mcp
|