# Directory Structure
```
├── build
│ └── index.js
├── Dockerfile
├── LICENSE
├── main.py
├── node_modules
│ ├── @google
│ │ └── generative-ai
│ │ ├── dist
│ │ │ ├── generative-ai.d.ts
│ │ │ ├── index.js
│ │ │ ├── index.js.map
│ │ │ ├── index.mjs
│ │ │ ├── index.mjs.map
│ │ │ ├── scripts
│ │ │ │ ├── check-format.d.ts
│ │ │ │ ├── format-patterns.d.ts
│ │ │ │ ├── license.d.ts
│ │ │ │ └── run-format.d.ts
│ │ │ ├── server
│ │ │ │ ├── index.js
│ │ │ │ ├── index.js.map
│ │ │ │ ├── index.mjs
│ │ │ │ ├── index.mjs.map
│ │ │ │ ├── scripts
│ │ │ │ │ ├── check-format.d.ts
│ │ │ │ │ ├── format-patterns.d.ts
│ │ │ │ │ ├── license.d.ts
│ │ │ │ │ └── run-format.d.ts
│ │ │ │ ├── server.d.ts
│ │ │ │ ├── src
│ │ │ │ │ ├── errors.d.ts
│ │ │ │ │ ├── gen-ai.d.ts
│ │ │ │ │ ├── index.d.ts
│ │ │ │ │ ├── methods
│ │ │ │ │ │ ├── chat-session-helpers.d.ts
│ │ │ │ │ │ ├── chat-session.d.ts
│ │ │ │ │ │ ├── count-tokens.d.ts
│ │ │ │ │ │ ├── embed-content.d.ts
│ │ │ │ │ │ └── generate-content.d.ts
│ │ │ │ │ ├── models
│ │ │ │ │ │ └── generative-model.d.ts
│ │ │ │ │ ├── requests
│ │ │ │ │ │ ├── request-helpers.d.ts
│ │ │ │ │ │ ├── request.d.ts
│ │ │ │ │ │ ├── response-helpers.d.ts
│ │ │ │ │ │ └── stream-reader.d.ts
│ │ │ │ │ └── server
│ │ │ │ │ ├── cache-manager.d.ts
│ │ │ │ │ ├── constants.d.ts
│ │ │ │ │ ├── file-manager.d.ts
│ │ │ │ │ ├── index.d.ts
│ │ │ │ │ └── request.d.ts
│ │ │ │ └── types
│ │ │ │ ├── content.d.ts
│ │ │ │ ├── enums.d.ts
│ │ │ │ ├── function-calling.d.ts
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── requests.d.ts
│ │ │ │ ├── responses.d.ts
│ │ │ │ ├── search-grounding.d.ts
│ │ │ │ └── server
│ │ │ │ ├── caching.d.ts
│ │ │ │ ├── files.d.ts
│ │ │ │ ├── index.d.ts
│ │ │ │ └── shared.d.ts
│ │ │ ├── src
│ │ │ │ ├── errors.d.ts
│ │ │ │ ├── gen-ai.d.ts
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── methods
│ │ │ │ │ ├── chat-session-helpers.d.ts
│ │ │ │ │ ├── chat-session.d.ts
│ │ │ │ │ ├── count-tokens.d.ts
│ │ │ │ │ ├── embed-content.d.ts
│ │ │ │ │ └── generate-content.d.ts
│ │ │ │ ├── models
│ │ │ │ │ └── generative-model.d.ts
│ │ │ │ ├── requests
│ │ │ │ │ ├── request-helpers.d.ts
│ │ │ │ │ ├── request.d.ts
│ │ │ │ │ ├── response-helpers.d.ts
│ │ │ │ │ └── stream-reader.d.ts
│ │ │ │ └── server
│ │ │ │ ├── cache-manager.d.ts
│ │ │ │ ├── constants.d.ts
│ │ │ │ ├── file-manager.d.ts
│ │ │ │ ├── index.d.ts
│ │ │ │ └── request.d.ts
│ │ │ ├── tsdoc-metadata.json
│ │ │ └── types
│ │ │ ├── content.d.ts
│ │ │ ├── enums.d.ts
│ │ │ ├── function-calling.d.ts
│ │ │ ├── index.d.ts
│ │ │ ├── requests.d.ts
│ │ │ ├── responses.d.ts
│ │ │ ├── search-grounding.d.ts
│ │ │ └── server
│ │ │ ├── caching.d.ts
│ │ │ ├── files.d.ts
│ │ │ ├── index.d.ts
│ │ │ └── shared.d.ts
│ │ ├── LICENSE
│ │ ├── package.json
│ │ ├── README.md
│ │ └── server
│ │ └── package.json
│ ├── @modelcontextprotocol
│ │ └── sdk
│ │ ├── 1
│ │ ├── dist
│ │ │ ├── 1
│ │ │ ├── cjs
│ │ │ │ ├── 1
│ │ │ │ ├── cli.d.ts
│ │ │ │ ├── cli.d.ts.map
│ │ │ │ ├── cli.js
│ │ │ │ ├── cli.js.map
│ │ │ │ ├── client
│ │ │ │ │ ├── auth.d.ts
│ │ │ │ │ ├── auth.d.ts.map
│ │ │ │ │ ├── auth.js
│ │ │ │ │ ├── auth.js.map
│ │ │ │ │ ├── index.d.ts
│ │ │ │ │ ├── index.d.ts.map
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── index.js.map
│ │ │ │ │ ├── sse.d.ts
│ │ │ │ │ ├── sse.d.ts.map
│ │ │ │ │ ├── sse.js
│ │ │ │ │ ├── sse.js.map
│ │ │ │ │ ├── stdio.d.ts
│ │ │ │ │ ├── stdio.d.ts.map
│ │ │ │ │ ├── stdio.js
│ │ │ │ │ ├── stdio.js.map
│ │ │ │ │ ├── websocket.d.ts
│ │ │ │ │ ├── websocket.d.ts.map
│ │ │ │ │ ├── websocket.js
│ │ │ │ │ └── websocket.js.map
│ │ │ │ ├── inMemory.d.ts
│ │ │ │ ├── inMemory.d.ts.map
│ │ │ │ ├── inMemory.js
│ │ │ │ ├── inMemory.js.map
│ │ │ │ ├── package.json
│ │ │ │ ├── server
│ │ │ │ │ └── auth
│ │ │ │ │ ├── clients.d.ts
│ │ │ │ │ ├── clients.d.ts.map
│ │ │ │ │ ├── clients.js
│ │ │ │ │ ├── clients.js.map
│ │ │ │ │ ├── errors.d.ts
│ │ │ │ │ ├── errors.d.ts.map
│ │ │ │ │ ├── errors.js
│ │ │ │ │ ├── errors.js.map
│ │ │ │ │ ├── handlers
│ │ │ │ │ │ ├── authorize.d.ts
│ │ │ │ │ │ ├── authorize.d.ts.map
│ │ │ │ │ │ ├── authorize.js
│ │ │ │ │ │ ├── authorize.js.map
│ │ │ │ │ │ ├── metadata.d.ts
│ │ │ │ │ │ ├── metadata.d.ts.map
│ │ │ │ │ │ ├── metadata.js
│ │ │ │ │ │ ├── metadata.js.map
│ │ │ │ │ │ ├── register.d.ts
│ │ │ │ │ │ ├── register.d.ts.map
│ │ │ │ │ │ ├── register.js
│ │ │ │ │ │ ├── register.js.map
│ │ │ │ │ │ ├── revoke.d.ts
│ │ │ │ │ │ ├── revoke.d.ts.map
│ │ │ │ │ │ ├── revoke.js
│ │ │ │ │ │ ├── revoke.js.map
│ │ │ │ │ │ ├── token.d.ts
│ │ │ │ │ │ ├── token.d.ts.map
│ │ │ │ │ │ ├── token.js
│ │ │ │ │ │ └── token.js.map
│ │ │ │ │ ├── middleware
│ │ │ │ │ │ ├── allowedMethods.d.ts
│ │ │ │ │ │ ├── allowedMethods.d.ts.map
│ │ │ │ │ │ ├── allowedMethods.js
│ │ │ │ │ │ ├── allowedMethods.js.map
│ │ │ │ │ │ ├── bearerAuth.d.ts
│ │ │ │ │ │ ├── bearerAuth.d.ts.map
│ │ │ │ │ │ ├── bearerAuth.js
│ │ │ │ │ │ ├── bearerAuth.js.map
│ │ │ │ │ │ ├── clientAuth.d.ts
│ │ │ │ │ │ ├── clientAuth.d.ts.map
│ │ │ │ │ │ ├── clientAuth.js
│ │ │ │ │ │ └── clientAuth.js.map
│ │ │ │ │ ├── provider.d.ts
│ │ │ │ │ ├── provider.d.ts.map
│ │ │ │ │ ├── provider.js
│ │ │ │ │ ├── provider.js.map
│ │ │ │ │ ├── providers
│ │ │ │ │ │ ├── proxyProvider.d.ts
│ │ │ │ │ │ ├── proxyProvider.d.ts.map
│ │ │ │ │ │ └── proxyProvider.js
│ │ │ │ │ ├── router.d.ts
│ │ │ │ │ ├── router.d.ts.map
│ │ │ │ │ ├── router.js
│ │ │ │ │ ├── router.js.map
│ │ │ │ │ ├── types.d.ts
│ │ │ │ │ ├── types.d.ts.map
│ │ │ │ │ ├── types.js
│ │ │ │ │ └── types.js.map
│ │ │ │ ├── shared
│ │ │ │ │ ├── auth.d.ts
│ │ │ │ │ ├── auth.d.ts.map
│ │ │ │ │ ├── auth.js
│ │ │ │ │ ├── auth.js.map
│ │ │ │ │ ├── protocol.d.ts
│ │ │ │ │ ├── protocol.d.ts.map
│ │ │ │ │ ├── protocol.js
│ │ │ │ │ ├── protocol.js.map
│ │ │ │ │ ├── stdio.d.ts
│ │ │ │ │ ├── stdio.d.ts.map
│ │ │ │ │ ├── stdio.js
│ │ │ │ │ ├── stdio.js.map
│ │ │ │ │ ├── transport.d.ts
│ │ │ │ │ ├── transport.d.ts.map
│ │ │ │ │ ├── transport.js
│ │ │ │ │ ├── transport.js.map
│ │ │ │ │ ├── uriTemplate.d.ts
│ │ │ │ │ ├── uriTemplate.d.ts.map
│ │ │ │ │ ├── uriTemplate.js
│ │ │ │ │ └── uriTemplate.js.map
│ │ │ │ ├── types.d.ts
│ │ │ │ ├── types.d.ts.map
│ │ │ │ ├── types.js
│ │ │ │ └── types.js.map
│ │ │ └── esm
│ │ │ ├── 1
│ │ │ ├── cli.d.ts
│ │ │ ├── cli.d.ts.map
│ │ │ ├── cli.js
│ │ │ ├── cli.js.map
│ │ │ ├── client
│ │ │ │ ├── auth.d.ts
│ │ │ │ ├── auth.d.ts.map
│ │ │ │ ├── auth.js
│ │ │ │ ├── auth.js.map
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.d.ts.map
│ │ │ │ ├── index.js
│ │ │ │ ├── index.js.map
│ │ │ │ ├── sse.d.ts
│ │ │ │ ├── sse.d.ts.map
│ │ │ │ ├── sse.js
│ │ │ │ ├── sse.js.map
│ │ │ │ ├── stdio.d.ts
│ │ │ │ ├── stdio.d.ts.map
│ │ │ │ ├── stdio.js
│ │ │ │ ├── stdio.js.map
│ │ │ │ ├── websocket.d.ts
│ │ │ │ ├── websocket.d.ts.map
│ │ │ │ ├── websocket.js
│ │ │ │ └── websocket.js.map
│ │ │ ├── inMemory.d.ts
│ │ │ ├── inMemory.d.ts.map
│ │ │ ├── inMemory.js
│ │ │ ├── inMemory.js.map
│ │ │ ├── package.json
│ │ │ ├── server
│ │ │ │ ├── auth
│ │ │ │ │ ├── clients.d.ts
│ │ │ │ │ ├── clients.d.ts.map
│ │ │ │ │ ├── clients.js
│ │ │ │ │ ├── clients.js.map
│ │ │ │ │ ├── errors.d.ts
│ │ │ │ │ ├── errors.d.ts.map
│ │ │ │ │ ├── errors.js
│ │ │ │ │ ├── errors.js.map
│ │ │ │ │ ├── handlers
│ │ │ │ │ │ ├── authorize.d.ts
│ │ │ │ │ │ ├── authorize.d.ts.map
│ │ │ │ │ │ ├── authorize.js
│ │ │ │ │ │ ├── authorize.js.map
│ │ │ │ │ │ ├── metadata.d.ts
│ │ │ │ │ │ ├── metadata.d.ts.map
│ │ │ │ │ │ ├── metadata.js
│ │ │ │ │ │ ├── metadata.js.map
│ │ │ │ │ │ ├── register.d.ts
│ │ │ │ │ │ ├── register.d.ts.map
│ │ │ │ │ │ ├── register.js
│ │ │ │ │ │ ├── register.js.map
│ │ │ │ │ │ ├── revoke.d.ts
│ │ │ │ │ │ ├── revoke.d.ts.map
│ │ │ │ │ │ ├── revoke.js
│ │ │ │ │ │ ├── revoke.js.map
│ │ │ │ │ │ ├── token.d.ts
│ │ │ │ │ │ ├── token.d.ts.map
│ │ │ │ │ │ ├── token.js
│ │ │ │ │ │ └── token.js.map
│ │ │ │ │ ├── middleware
│ │ │ │ │ │ ├── allowedMethods.d.ts
│ │ │ │ │ │ ├── allowedMethods.d.ts.map
│ │ │ │ │ │ ├── allowedMethods.js
│ │ │ │ │ │ ├── allowedMethods.js.map
│ │ │ │ │ │ ├── bearerAuth.d.ts
│ │ │ │ │ │ ├── bearerAuth.d.ts.map
│ │ │ │ │ │ ├── bearerAuth.js
│ │ │ │ │ │ ├── bearerAuth.js.map
│ │ │ │ │ │ ├── clientAuth.d.ts
│ │ │ │ │ │ ├── clientAuth.d.ts.map
│ │ │ │ │ │ ├── clientAuth.js
│ │ │ │ │ │ └── clientAuth.js.map
│ │ │ │ │ ├── provider.d.ts
│ │ │ │ │ ├── provider.d.ts.map
│ │ │ │ │ ├── provider.js
│ │ │ │ │ ├── provider.js.map
│ │ │ │ │ └── providers
│ │ │ │ │ ├── proxyProvider.d.ts
│ │ │ │ │ ├── proxyProvider.d.ts.map
│ │ │ │ │ └── proxyProvider.js
│ │ │ │ ├── completable.d.ts
│ │ │ │ ├── completable.d.ts.map
│ │ │ │ ├── completable.js
│ │ │ │ ├── completable.js.map
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.d.ts.map
│ │ │ │ ├── index.js
│ │ │ │ ├── index.js.map
│ │ │ │ ├── mcp.d.ts
│ │ │ │ ├── mcp.d.ts.map
│ │ │ │ ├── mcp.js
│ │ │ │ ├── mcp.js.map
│ │ │ │ ├── sse.d.ts
│ │ │ │ ├── sse.d.ts.map
│ │ │ │ ├── sse.js
│ │ │ │ ├── sse.js.map
│ │ │ │ ├── stdio.d.ts
│ │ │ │ ├── stdio.d.ts.map
│ │ │ │ ├── stdio.js
│ │ │ │ └── stdio.js.map
│ │ │ ├── shared
│ │ │ │ ├── auth.d.ts
│ │ │ │ ├── auth.d.ts.map
│ │ │ │ ├── auth.js
│ │ │ │ ├── auth.js.map
│ │ │ │ ├── protocol.d.ts
│ │ │ │ ├── protocol.d.ts.map
│ │ │ │ ├── protocol.js
│ │ │ │ ├── protocol.js.map
│ │ │ │ ├── stdio.d.ts
│ │ │ │ ├── stdio.d.ts.map
│ │ │ │ ├── stdio.js
│ │ │ │ ├── stdio.js.map
│ │ │ │ ├── transport.d.ts
│ │ │ │ ├── transport.d.ts.map
│ │ │ │ ├── transport.js
│ │ │ │ ├── transport.js.map
│ │ │ │ ├── uriTemplate.d.ts
│ │ │ │ ├── uriTemplate.d.ts.map
│ │ │ │ ├── uriTemplate.js
│ │ │ │ └── uriTemplate.js.map
│ │ │ ├── types.d.ts
│ │ │ ├── types.d.ts.map
│ │ │ ├── types.js
│ │ │ └── types.js.map
│ │ ├── LICENSE
│ │ ├── package.json
│ │ └── README.md
│ ├── 1
│ ├── natural
│ │ ├── 1
│ │ ├── CODE_OF_CONDUCT.md
│ │ ├── CONTRIBUTING.md
│ │ ├── gulpfile.js
│ │ ├── index.js
│ │ ├── lib
│ │ │ └── natural
│ │ │ ├── 1
│ │ │ ├── analyzers
│ │ │ │ ├── brill_pos_tagger
│ │ │ │ │ ├── data
│ │ │ │ │ │ ├── Dutch
│ │ │ │ │ │ │ ├── brill_CONTEXTRULES.jg
│ │ │ │ │ │ │ ├── brill_CONTEXTRULES.json
│ │ │ │ │ │ │ ├── brill_LEXICON.jg
│ │ │ │ │ │ │ ├── brill_Lexicon.json
│ │ │ │ │ │ │ └── README.txt
│ │ │ │ │ │ └── English
│ │ │ │ │ │ ├── lexicon_from_posjs.json
│ │ │ │ │ │ ├── README.txt
│ │ │ │ │ │ ├── tr_from_brill_paper.json
│ │ │ │ │ │ ├── tr_from_brill_paper.txt
│ │ │ │ │ │ ├── tr_from_posjs.json
│ │ │ │ │ │ └── tr_from_posjs.txt
│ │ │ │ │ ├── index.d.ts
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── lib
│ │ │ │ │ ├── Brill_POS_Tagger.js
│ │ │ │ │ ├── Brill_POS_Tester.js
│ │ │ │ │ ├── Brill_POS_Trainer.js
│ │ │ │ │ ├── Corpus.js
│ │ │ │ │ ├── Lexicon.js
│ │ │ │ │ ├── Predicate.js
│ │ │ │ │ ├── RuleSet.js
│ │ │ │ │ ├── RuleTemplate.js
│ │ │ │ │ ├── RuleTemplates.js
│ │ │ │ │ ├── Sentence.js
│ │ │ │ │ ├── TF_Parser.js
│ │ │ │ │ ├── TF_Parser.pegjs
│ │ │ │ │ └── TransformationRule.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── sentence_analyzer.js
│ │ │ │ └── SenType.ts
│ │ │ ├── classifiers
│ │ │ │ ├── bayes_classifier.js
│ │ │ │ ├── classifier_train_parallel.js
│ │ │ │ ├── classifier.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── logistic_regression_classifier.js
│ │ │ │ └── maxent
│ │ │ │ ├── Classifier.js
│ │ │ │ ├── Context.js
│ │ │ │ ├── Distribution.js
│ │ │ │ ├── Element.js
│ │ │ │ ├── Feature.js
│ │ │ │ ├── FeatureSet.js
│ │ │ │ ├── GISScaler.js
│ │ │ │ ├── POS
│ │ │ │ │ ├── ME_Corpus.js
│ │ │ │ │ ├── ME_Sentence.js
│ │ │ │ │ └── POS_Element.js
│ │ │ │ ├── Sample.js
│ │ │ │ └── SimpleExample
│ │ │ │ └── SE_Element.js
│ │ │ ├── distance
│ │ │ │ ├── dice_coefficient.js
│ │ │ │ ├── hamming_distance.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── jaro-winkler_distance.js
│ │ │ │ └── levenshtein_distance.js
│ │ │ ├── index.d.ts
│ │ │ ├── index.js
│ │ │ ├── inflectors
│ │ │ │ ├── count_inflector.js
│ │ │ │ ├── form_set.js
│ │ │ │ ├── fr
│ │ │ │ │ ├── count_inflector.js
│ │ │ │ │ └── noun_inflector.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── ja
│ │ │ │ │ └── noun_inflector.js
│ │ │ │ ├── noun_inflector.js
│ │ │ │ ├── present_verb_inflector.js
│ │ │ │ └── singular_plural_inflector.js
│ │ │ ├── ngrams
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── ngrams_zh.js
│ │ │ │ └── ngrams.js
│ │ │ ├── normalizers
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── normalizer_ja.js
│ │ │ │ ├── normalizer_no.js
│ │ │ │ ├── normalizer_sv.js
│ │ │ │ ├── normalizer.js
│ │ │ │ └── remove_diacritics.js
│ │ │ ├── phonetics
│ │ │ │ ├── dm_soundex.js
│ │ │ │ ├── double_metaphone.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── metaphone.js
│ │ │ │ ├── phonetic.js
│ │ │ │ ├── sentiment
│ │ │ │ │ ├── Basque
│ │ │ │ │ │ └── senticon_eu.json
│ │ │ │ │ ├── Catalan
│ │ │ │ │ │ └── senticon_ca.json
│ │ │ │ │ ├── Dutch
│ │ │ │ │ │ ├── negations_du.json
│ │ │ │ │ │ └── pattern-sentiment-nl.json
│ │ │ │ │ ├── English
│ │ │ │ │ │ ├── negations_en.json
│ │ │ │ │ │ ├── pattern-sentiment-en.json
│ │ │ │ │ │ └── senticon_en.json
│ │ │ │ │ ├── French
│ │ │ │ │ │ └── pattern-sentiment-fr.json
│ │ │ │ │ ├── Galician
│ │ │ │ │ │ └── senticon_gl.json
│ │ │ │ │ ├── German
│ │ │ │ │ │ ├── negations_de.json
│ │ │ │ │ │ └── pattern-sentiment-de.json
│ │ │ │ │ ├── index.d.ts
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── Italian
│ │ │ │ │ │ └── pattern-sentiment-it.json
│ │ │ │ │ ├── Portuguese
│ │ │ │ │ │ ├── afinnShortSortedPortuguese.json
│ │ │ │ │ │ └── negations_pt.json
│ │ │ │ │ ├── SentimentAnalyzer.js
│ │ │ │ │ ├── Spanish
│ │ │ │ │ │ ├── afinnShortSortedSpanish.json
│ │ │ │ │ │ ├── negations_es.json
│ │ │ │ │ │ └── senticon_es.json
│ │ │ │ │ └── tools
│ │ │ │ │ ├── README.md
│ │ │ │ │ ├── sentimentXmlParser.js
│ │ │ │ │ └── XmlParser4PatternData.js
│ │ │ │ └── soundex.js
│ │ │ ├── spellcheck
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ └── spellcheck.js
│ │ │ ├── stemmers
│ │ │ │ ├── Carry
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── stepConfs.js
│ │ │ │ │ └── utils.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── indonesian
│ │ │ │ │ ├── base_stemmer_id.js
│ │ │ │ │ ├── data
│ │ │ │ │ │ └── kata-dasar.json
│ │ │ │ │ ├── prefix_rules.js
│ │ │ │ │ ├── removal.js
│ │ │ │ │ ├── stemmer_id.js
│ │ │ │ │ └── suffix_rules.js
│ │ │ │ ├── lancaster_rules.js
│ │ │ │ ├── lancaster_stemmer.js
│ │ │ │ ├── porter_stemmer_de.js
│ │ │ │ ├── porter_stemmer_es.js
│ │ │ │ ├── porter_stemmer_fa.js
│ │ │ │ ├── porter_stemmer_fr.js
│ │ │ │ ├── porter_stemmer_it.js
│ │ │ │ ├── porter_stemmer_nl.js
│ │ │ │ ├── porter_stemmer_no.js
│ │ │ │ ├── porter_stemmer_pt.js
│ │ │ │ ├── porter_stemmer_ru.js
│ │ │ │ ├── porter_stemmer_sv.js
│ │ │ │ ├── porter_stemmer_uk.js
│ │ │ │ ├── porter_stemmer.js
│ │ │ │ ├── stemmer_de.js
│ │ │ │ ├── stemmer_es.js
│ │ │ │ ├── stemmer_fa.js
│ │ │ │ ├── stemmer_fr.js
│ │ │ │ ├── stemmer_it.js
│ │ │ │ ├── stemmer_ja.js
│ │ │ │ ├── stemmer_nl.js
│ │ │ │ ├── stemmer_no.js
│ │ │ │ ├── stemmer_pl.js
│ │ │ │ ├── stemmer_pt.js
│ │ │ │ ├── stemmer_ru.js
│ │ │ │ ├── stemmer_sv.js
│ │ │ │ ├── stemmer_uk.js
│ │ │ │ ├── stemmer.js
│ │ │ │ └── token.js
│ │ │ ├── tfidf
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ └── tfidf.js
│ │ │ ├── tokenizers
│ │ │ │ ├── aggressive_tokenizer_de.js
│ │ │ │ ├── aggressive_tokenizer_es.js
│ │ │ │ ├── aggressive_tokenizer_fa.js
│ │ │ │ ├── aggressive_tokenizer_fr.js
│ │ │ │ ├── aggressive_tokenizer_hi.js
│ │ │ │ ├── aggressive_tokenizer_id.js
│ │ │ │ ├── aggressive_tokenizer_it.js
│ │ │ │ ├── aggressive_tokenizer_nl.js
│ │ │ │ ├── aggressive_tokenizer_no.js
│ │ │ │ ├── aggressive_tokenizer_pl.js
│ │ │ │ ├── aggressive_tokenizer_pt.js
│ │ │ │ ├── aggressive_tokenizer_ru.js
│ │ │ │ ├── aggressive_tokenizer_sv.js
│ │ │ │ ├── aggressive_tokenizer_uk.js
│ │ │ │ ├── aggressive_tokenizer_vi.js
│ │ │ │ ├── aggressive_tokenizer.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── orthography_matchers.js
│ │ │ │ ├── regexp_tokenizer.js
│ │ │ │ ├── sentence_tokenizer_deprecated.js
│ │ │ │ ├── sentence_tokenizer.js
│ │ │ │ ├── tokenizer_case.js
│ │ │ │ ├── tokenizer_ja.js
│ │ │ │ ├── tokenizer.js
│ │ │ │ └── treebank_word_tokenizer.js
│ │ │ ├── transliterators
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ └── ja
│ │ │ │ └── index.js
│ │ │ ├── trie
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ └── trie.js
│ │ │ ├── util
│ │ │ │ ├── abbreviations_en.js
│ │ │ │ ├── bag.js
│ │ │ │ ├── directed_edge.js
│ │ │ │ ├── edge_weighted_digraph.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.js
│ │ │ │ ├── longest_path_tree.js
│ │ │ │ ├── shortest_path_tree.js
│ │ │ │ ├── stopwords_es.js
│ │ │ │ ├── stopwords_fa.js
│ │ │ │ ├── stopwords_fr.js
│ │ │ │ ├── stopwords_id.js
│ │ │ │ ├── stopwords_it.js
│ │ │ │ ├── stopwords_ja.js
│ │ │ │ ├── stopwords_nl.js
│ │ │ │ ├── stopwords_no.js
│ │ │ │ ├── stopwords_pl.js
│ │ │ │ ├── stopwords_pt.js
│ │ │ │ ├── stopwords_ru.js
│ │ │ │ ├── stopwords_sv.js
│ │ │ │ ├── stopwords_uk.js
│ │ │ │ ├── stopwords_zh.js
│ │ │ │ ├── stopwords.js
│ │ │ │ ├── storage
│ │ │ │ │ ├── docker-compose.yml
│ │ │ │ │ ├── File.js
│ │ │ │ │ ├── index.d.ts
│ │ │ │ │ ├── index.js
│ │ │ │ │ ├── Memcached.js
│ │ │ │ │ ├── MongoDB.js
│ │ │ │ │ ├── Postgres.js
│ │ │ │ │ ├── Redis.js
│ │ │ │ │ └── StorageBackend.js
│ │ │ │ ├── topological.js
│ │ │ │ └── utils.js
│ │ │ └── wordnet
│ │ │ ├── data_file.js
│ │ │ ├── index_file.js
│ │ │ ├── index.d.ts
│ │ │ ├── index.js
│ │ │ ├── wordnet_file.js
│ │ │ └── wordnet.js
│ │ ├── LICENSE.txt
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.mjs
│ │ ├── SECURITY.md
│ │ ├── tsconfig.json
│ │ └── webpack.config.js
│ └── string-similarity
│ ├── LICENSE
│ ├── package.json
│ ├── README.md
│ ├── src
│ │ └── index.js
│ └── umd
│ └── string-similarity.min.js
├── package-lock.json
├── package.json
├── README.md
├── Readme中文
├── requirements.txt
├── smithery.yaml
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
1 | # EntityIdentification
2 | Identify whether two sets of data are from the same entity. 识别两组数据是否来自同一主体
3 |
4 | This is a MCP (Model Context Protocol) server. 这是一个支持MCP协议的服务器。
5 |
6 |
7 | # Data Comparison Tool
8 |
9 | This tool provides a comprehensive way to compare two sets of data, evaluating both exact and semantic equality of their values. It leverages text normalization and a language model to determine if the data originates from the same entity.
10 |
11 | ## Features
12 |
13 | - **Text Normalization**: Converts text to lowercase, removes punctuation, and normalizes whitespace.
14 | - **Value Comparison**: Compares values directly and semantically (ignoring order for lists).
15 | - **JSON Traversal**: Iterates through each key in the JSON objects and compares corresponding values.
16 | - **Language Model Integration**: Uses a generative language model to assess semantic similarity and provide a final judgment on whether the data comes from the same entity.
17 |
18 | ## Installation
19 |
20 | To use this tool, ensure you have the necessary dependencies installed. You can install them using pip:
21 |
22 | ```bash
23 | pip install genai
24 | ```
25 |
26 | ## Usage
27 |
28 | ### Functions
29 |
30 | 1. **normalize_text(text)**:
31 | - Normalizes the input text by converting it to lowercase, removing punctuation, and normalizing whitespace.
32 |
33 | 2. **compare_values(val1, val2)**:
34 | - Compares two values both exactly and semantically.
35 | - If the values are lists, it ignores the order of elements for semantic comparison.
36 |
37 | 3. **compare_json(json1, json2)**:
38 | - Compares two JSON objects key by key.
39 | - Uses `compare_values` to evaluate each key's values.
40 | - Integrates a language model to assess semantic similarity and provides a final judgment.
41 |
42 | ### Example
43 |
44 | ```python
45 | import json
46 | import genai
47 | import re
48 |
49 | # Define your JSON objects
50 | json1 = {
51 | "name": "John Doe",
52 | "address": "123 Main St, Anytown, USA",
53 | "hobbies": ["reading", "hiking", "coding"]
54 | }
55 |
56 | json2 = {
57 | "name": "john doe",
58 | "address": "123 Main Street, Anytown, USA",
59 | "hobbies": ["coding", "hiking", "reading"]
60 | }
61 |
62 | # Compare the JSON objects
63 | comparison_results = compare_json(json1, json2)
64 |
65 | # Generate final matching result
66 | model1 = genai.GenerativeModel("gemini-2.0-flash-thinking-exp")
67 | result_matching = model1.generate_content("综合这些信息,你认为可以判断两个数据来自同一主体吗?"+json.dumps(comparison_results, ensure_ascii=False, indent=4))
68 | print(result_matching.text)
69 | ```
70 |
71 | ## Contributing
72 |
73 | Contributions are welcome! Please open an issue or submit a pull request.
74 |
75 | ## License
76 |
77 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
78 | ## Contact
79 |
80 | If you have any questions or suggestions, please contact me:
81 | - Email: [email protected]
82 | - GitHub: [[email protected]](mailto:[email protected])。
83 |
84 | Wechat
85 | 
86 |
```
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
```
1 | flask>=2.0.0
2 | mcp>=0.1.0
3 | google-generativeai>=0.3.0
4 | python-dotenv>=1.0.0
5 |
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "compilerOptions": {
3 | "target": "ES2020",
4 | "module": "ES2020",
5 | "moduleResolution": "node",
6 | "esModuleInterop": true,
7 | "strict": true,
8 | "outDir": "build",
9 | "skipLibCheck": true,
10 | "forceConsistentCasingInFileNames": true
11 | },
12 | "include": ["src/**/*"],
13 | "exclude": ["node_modules"]
14 | }
15 |
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
1 |
2 | # Smithery.ai configuration
3 | startCommand:
4 | type: stdio
5 | configSchema:
6 |
7 | commandFunction:
8 | # A function that produces the CLI command to start the MCP on stdio.
9 | |-
10 | (config) => ({
11 | "command": "node",
12 | "args": [
13 | "dist/index.js"
14 | ],
15 | "env": {
16 |
17 | }
18 | })
19 |
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
1 | # Use an official Node.js runtime as a parent image
2 | FROM node:18-alpine
3 |
4 | # Set the working directory in the container
5 | WORKDIR /app
6 |
7 | # Copy package.json and package-lock.json
8 | COPY package*.json ./
9 |
10 | # Install production dependencies only
11 | # Use --ignore-scripts for potentially improved security
12 | RUN npm install
13 |
14 | # Copy the compiled application code
15 | COPY build ./build
16 |
17 |
18 |
19 | # Define the command to run the application
20 | CMD ["node", "dist/index.js"]
21 |
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "entity-resolution-mcp",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "scripts": {
6 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\""
7 | },
8 | "dependencies": {
9 | "@google/generative-ai": "^0.24.0",
10 | "@modelcontextprotocol/sdk": "^1.0.0",
11 | "@types/natural": "^5.1.5",
12 | "natural": "^8.0.1",
13 | "string-similarity": "^4.0.4"
14 | },
15 | "devDependencies": {
16 | "@types/node": "^20.0.0",
17 | "@types/string-similarity": "^4.0.0",
18 | "typescript": "^5.0.0"
19 | }
20 | }
21 |
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4 | import {
5 | CallToolRequestSchema,
6 | ErrorCode,
7 | ListToolsRequestSchema,
8 | McpError,
9 | TextContent, // Corrected Import
10 | } from '@modelcontextprotocol/sdk/types.js';
11 | import stringSimilarity from 'string-similarity';
12 | import natural from 'natural';
13 | import { inspect } from 'util'; // For better debugging if needed
14 | import { GoogleGenerativeAI, HarmCategory, HarmBlockThreshold } from '@google/generative-ai';
15 |
16 | interface EntityInfo {
17 | [key: string]: any; // Allow various types, handle conversion later
18 | }
19 |
20 | // Function to normalize text: lowercase, remove punctuation, normalize whitespace
21 | function normalizeText(text: any): string {
22 | let str = String(text).toLowerCase();
23 | // Keep letters, numbers, and CJK characters, remove other punctuation
24 | str = str.replace(/[^\p{L}\p{N}\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\s]/gu, '');
25 | str = str.replace(/\s+/g, ' ').trim(); // Normalize whitespace
26 | return str;
27 | }
28 |
29 | // Helper to safely get text from Gemini response
30 | function getGeminiResponseText(response: any): string | null {
31 | try {
32 | // Check for different possible response structures
33 | if (response?.response?.candidates?.[0]?.content?.parts?.[0]?.text) {
34 | return response.response.candidates[0].content.parts[0].text.trim();
35 | }
36 | if (response?.text) { // Simpler structure if generateContent returns directly
37 | return typeof response.text === 'function' ? response.text().trim() : String(response.text).trim();
38 | }
39 | // Add more checks if needed based on actual Gemini SDK behavior
40 | } catch (e) {
41 | console.error("Error parsing Gemini response:", e);
42 | }
43 | return null; // Return null if text cannot be extracted
44 | }
45 |
46 | class EntityResolutionServer {
47 | private server: Server;
48 |
49 | constructor() {
50 | this.server = new Server(
51 | {
52 | name: 'entity-resolution-server',
53 | version: '0.2.1', // Incremented version for fix
54 | },
55 | {
56 | capabilities: {
57 | tools: {},
58 | },
59 | }
60 | );
61 |
62 | this.setupToolHandlers();
63 |
64 | this.server.onerror = (error) => console.error('[MCP Error]', error);
65 | process.on('SIGINT', async () => {
66 | await this.server.close();
67 | process.exit(0);
68 | });
69 | }
70 |
71 | // Calculates syntactic similarity scores and prepares data for LLM
72 | private calculateSyntacticSimilarities(entity1: EntityInfo, entity2: EntityInfo): {
73 | dice: number;
74 | levenshtein: number;
75 | fieldDetails: Record<string, {
76 | dice: number;
77 | levenshtein: number;
78 | normalized1: string;
79 | normalized2: string;
80 | value1: any; // Keep original value
81 | value2: any; // Keep original value
82 | }>
83 | } {
84 | let totalWeight = 0;
85 | let weightedDiceSimilarity = 0;
86 | let weightedLevenshteinSimilarity = 0;
87 | const fieldDetails: Record<string, { dice: number; levenshtein: number; normalized1: string; normalized2: string; value1: any; value2: any }> = {};
88 |
89 | const keys1 = Object.keys(entity1);
90 | const keys2 = Object.keys(entity2);
91 | const commonKeys = keys1.filter(key => keys2.includes(key));
92 |
93 | if (commonKeys.length === 0) {
94 | return { dice: 0, levenshtein: 0, fieldDetails: {} };
95 | }
96 |
97 | for (const key of commonKeys) {
98 | const value1 = entity1[key];
99 | const value2 = entity2[key];
100 |
101 | // Normalize values before comparison
102 | const normalized1 = normalizeText(value1);
103 | const normalized2 = normalizeText(value2);
104 |
105 | // Calculate Dice similarity
106 | const diceSim = stringSimilarity.compareTwoStrings(normalized1, normalized2);
107 |
108 | // Calculate Levenshtein similarity (normalized to 0-1 range)
109 | const levenshteinDist = natural.LevenshteinDistance(normalized1, normalized2, { insertion_cost: 1, deletion_cost: 1, substitution_cost: 1 });
110 | const maxLength = Math.max(normalized1.length, normalized2.length);
111 | const levenshteinSim = maxLength === 0 ? 1 : 1 - (levenshteinDist / maxLength);
112 |
113 | // Apply field weights (currently uniform)
114 | const weight = 1.0; // TODO: Make weights configurable
115 | totalWeight += weight;
116 | weightedDiceSimilarity += diceSim * weight;
117 | weightedLevenshteinSimilarity += levenshteinSim * weight;
118 |
119 | fieldDetails[key] = {
120 | dice: diceSim,
121 | levenshtein: levenshteinSim,
122 | normalized1: normalized1,
123 | normalized2: normalized2,
124 | value1: value1, // Store original value
125 | value2: value2, // Store original value
126 | };
127 | }
128 |
129 | const overallDice = totalWeight > 0 ? weightedDiceSimilarity / totalWeight : 0;
130 | const overallLevenshtein = totalWeight > 0 ? weightedLevenshteinSimilarity / totalWeight : 0;
131 |
132 | return {
133 | dice: overallDice,
134 | levenshtein: overallLevenshtein,
135 | fieldDetails: fieldDetails
136 | };
137 | }
138 |
139 | private setupToolHandlers() {
140 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
141 | tools: [
142 | {
143 | name: 'compare_entities',
144 | description: 'Compare two entities using syntactic and optional semantic (LLM) methods.',
145 | inputSchema: {
146 | type: 'object',
147 | properties: {
148 | entity1: {
149 | type: 'object',
150 | description: 'First entity information',
151 | additionalProperties: true
152 | },
153 | entity2: {
154 | type: 'object',
155 | description: 'Second entity information',
156 | additionalProperties: true
157 | },
158 | threshold: {
159 | type: 'number',
160 | description: 'Syntactic similarity threshold (0-1, based on Dice) to consider entities as matching',
161 | minimum: 0,
162 | maximum: 1,
163 | default: 0.8
164 | },
165 | apiKey: {
166 | type: 'string',
167 | description: '(Optional) Google Generative AI API Key for semantic comparison'
168 | },
169 | },
170 | // Make apiKey optional
171 | required: ['entity1', 'entity2']
172 | }
173 | }
174 | ]
175 | }));
176 |
177 | this.server.setRequestHandler(CallToolRequestSchema, async (request): Promise<{ content: TextContent[] }> => { // Corrected return type
178 | if (request.params.name !== 'compare_entities') {
179 | throw new McpError(
180 | ErrorCode.MethodNotFound,
181 | `Unknown tool: ${request.params.name}`
182 | );
183 | }
184 |
185 | // --- Input Validation ---
186 | const args = request.params.arguments as any;
187 | const entity1 = args.entity1 as EntityInfo;
188 | const entity2 = args.entity2 as EntityInfo;
189 | const threshold = typeof args.threshold === 'number' ? args.threshold : 0.8;
190 | const apiKey = args.apiKey as string | undefined; // apiKey is optional
191 |
192 | if (!entity1 || typeof entity1 !== 'object' || !entity2 || typeof entity2 !== 'object') {
193 | throw new McpError(ErrorCode.InvalidParams, 'Invalid entity format. Both entity1 and entity2 must be objects.');
194 | }
195 |
196 | // --- Syntactic Comparison ---
197 | const syntacticSimilarities = this.calculateSyntacticSimilarities(entity1, entity2);
198 | const isMatchSyntactic = syntacticSimilarities.dice >= threshold;
199 |
200 | // --- Semantic (LLM) Comparison ---
201 | let llmFieldResults: Record<string, { llmSaysEqual: boolean | string | null; error?: string }> = {};
202 | let finalLlmAnalysis: string | null = null;
203 | let llmError: string | null = null;
204 |
205 | // --- Semantic (LLM) Comparison (only if apiKey is provided) ---
206 | if (apiKey) {
207 | try {
208 | const genAI = new GoogleGenerativeAI(apiKey);
209 | const safetySettings = [
210 | { category: HarmCategory.HARM_CATEGORY_HARASSMENT, threshold: HarmBlockThreshold.BLOCK_NONE },
211 | { category: HarmCategory.HARM_CATEGORY_HATE_SPEECH, threshold: HarmBlockThreshold.BLOCK_NONE },
212 | { category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, threshold: HarmBlockThreshold.BLOCK_NONE },
213 | { category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, threshold: HarmBlockThreshold.BLOCK_NONE },
214 | ];
215 |
216 | const comparisonModel = genAI.getGenerativeModel({ model: "gemini-1.5-flash-latest", safetySettings });
217 |
218 | const fieldComparisonPromises = Object.entries(syntacticSimilarities.fieldDetails).map(async ([key, details]) => {
219 | const prompt = `请判断这两个值在语义层面是否一致,仅返回 "true" 或 "false":\n值1: ${JSON.stringify(details.value1)}\n值2: ${JSON.stringify(details.value2)}`;
220 | try {
221 | const result = await comparisonModel.generateContent(prompt);
222 | const textResult = getGeminiResponseText(result);
223 | let llmSaysEqual: boolean | string | null = null;
224 | if (textResult !== null) {
225 | if (textResult.toLowerCase() === 'true') {
226 | llmSaysEqual = true;
227 | } else if (textResult.toLowerCase() === 'false') {
228 | llmSaysEqual = false;
229 | } else {
230 | llmSaysEqual = `Unexpected LLM response: ${textResult}`;
231 | }
232 | }
233 | llmFieldResults[key] = { llmSaysEqual };
234 | } catch (error: any) {
235 | console.error(`LLM error comparing field ${key}:`, error);
236 | llmFieldResults[key] = { llmSaysEqual: null, error: error.message || 'Unknown LLM error' };
237 | }
238 | });
239 |
240 | await Promise.all(fieldComparisonPromises);
241 |
242 | // --- Final LLM Analysis ---
243 | const combinedResultsForFinalAnalysis = {
244 | syntactic: syntacticSimilarities,
245 | semanticFieldChecks: llmFieldResults
246 | };
247 | const finalPrompt = `综合以下字段的语法和语义比较信息,判断这两个实体是否可能指向同一个真实世界的主体?请提供简要分析和最终判断 (可能匹配/不太可能匹配)。\n\n比较详情:\n${JSON.stringify(combinedResultsForFinalAnalysis, null, 2)}`;
248 | try {
249 | const finalResult = await comparisonModel.generateContent(finalPrompt);
250 | finalLlmAnalysis = getGeminiResponseText(finalResult);
251 | } catch (error: any) {
252 | console.error(`LLM error during final analysis:`, error);
253 | finalLlmAnalysis = `Error during final analysis: ${error.message || 'Unknown LLM error'}`;
254 | }
255 |
256 | } catch (error: any) {
257 | console.error("Error during LLM processing:", error);
258 | llmError = `LLM Initialization or Processing Error: ${error.message || 'Unknown error'}`;
259 | // Ensure field results reflect the error if initialization failed
260 | Object.keys(syntacticSimilarities.fieldDetails).forEach(key => {
261 | if (!llmFieldResults[key]) {
262 | llmFieldResults[key] = { llmSaysEqual: null, error: llmError ?? 'Skipped due to init error' };
263 | }
264 | });
265 | finalLlmAnalysis = `Analysis skipped due to error: ${llmError}`;
266 | }
267 | } else {
268 | // --- Case where apiKey is NOT provided ---
269 | llmError = "API Key not provided. Skipping semantic analysis.";
270 | finalLlmAnalysis = "Semantic analysis skipped (no API key).";
271 | // Populate llmFieldResults to indicate skipping
272 | Object.keys(syntacticSimilarities.fieldDetails).forEach(key => {
273 | llmFieldResults[key] = { llmSaysEqual: null, error: 'Skipped (no API key)' };
274 | });
275 | }
276 |
277 | // --- Format Final Response ---
278 | const responseJson = {
279 | overallSyntacticSimilarity: {
280 | dice: syntacticSimilarities.dice,
281 | levenshtein: syntacticSimilarities.levenshtein,
282 | },
283 | isMatchSyntactic: isMatchSyntactic,
284 | threshold: threshold,
285 | matchDetailsSyntactic: `Entities are ${isMatchSyntactic ? 'likely' : 'unlikely'} to be the same based ONLY on Dice similarity (${(syntacticSimilarities.dice * 100).toFixed(2)}%) and threshold ${threshold}.`,
286 | fieldDetails: Object.entries(syntacticSimilarities.fieldDetails).reduce((acc, [key, details]) => {
287 | acc[key] = {
288 | ...details, // dice, levenshtein, normalized1/2, value1/2
289 | llmSemanticCheck: llmFieldResults[key] || { llmSaysEqual: null, error: 'Not processed' } // Add LLM result per field
290 | };
291 | return acc;
292 | }, {} as Record<string, any>),
293 | finalLlmAnalysis: finalLlmAnalysis, // Use the value determined above
294 | llmProcessingError: llmError // Include LLM error message if any
295 | };
296 |
297 | return {
298 | content: [
299 | {
300 | type: 'text',
301 | text: JSON.stringify(responseJson, null, 2) // Pretty print JSON
302 | }
303 | ]
304 | };
305 | });
306 | }
307 |
308 | async run() {
309 | const transport = new StdioServerTransport();
310 | await this.server.connect(transport);
311 | console.error('Entity Resolution MCP server running on stdio (v0.2.1)'); // Updated log
312 | }
313 | }
314 |
315 | const server = new EntityResolutionServer();
316 | server.run().catch(console.error);
317 |
```