├── .python-version ├── src └── mcp_server_memos │ ├── proto_gen │ ├── __init__.py │ ├── memos │ │ ├── __init__.py │ │ └── api │ │ │ └── __init__.py │ └── google │ │ ├── __init__.py │ │ └── api │ │ └── __init__.py │ ├── __main__.py │ ├── config.py │ ├── __init__.py │ └── server.py ├── proto ├── gen.sh ├── update_deps.sh ├── buf.yaml ├── api │ └── v1 │ │ ├── common.proto │ │ ├── memo_relation_service.proto │ │ ├── reaction_service.proto │ │ ├── workspace_service.proto │ │ ├── activity_service.proto │ │ ├── auth_service.proto │ │ ├── inbox_service.proto │ │ ├── webhook_service.proto │ │ ├── idp_service.proto │ │ ├── workspace_setting_service.proto │ │ ├── resource_service.proto │ │ ├── markdown_service.proto │ │ ├── user_service.proto │ │ └── memo_service.proto ├── buf.gen.yaml └── buf.lock ├── .gitignore ├── hello.py ├── Dockerfile ├── .github ├── FUNDING.yml └── workflows │ └── release.yaml ├── pyproject.toml ├── LICENSE ├── smithery.yaml ├── README.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /src/mcp_server_memos/proto_gen/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /proto/gen.sh: -------------------------------------------------------------------------------- 1 | cd $(dirname $0) 2 | buf generate -------------------------------------------------------------------------------- /src/mcp_server_memos/proto_gen/memos/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mcp_server_memos/proto_gen/google/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mcp_server_memos/proto_gen/memos/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /proto/update_deps.sh: -------------------------------------------------------------------------------- 1 | cd $(dirname $0) 2 | buf build 3 | buf dep update -------------------------------------------------------------------------------- /src/mcp_server_memos/__main__.py: -------------------------------------------------------------------------------- 1 | # __main__.py 2 | 3 | from mcp_server_memos import main 4 | 5 | main() 6 | -------------------------------------------------------------------------------- /src/mcp_server_memos/config.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | 4 | @dataclass 5 | class Config: 6 | host: str 7 | port: int 8 | token: str -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | wheels/ 7 | *.egg-info 8 | 9 | # Virtual environments 10 | .venv 11 | 12 | .env -------------------------------------------------------------------------------- /hello.py: -------------------------------------------------------------------------------- 1 | from pydantic import TypeAdapter 2 | 3 | from mcp_server_memos import main 4 | from mcp_server_memos.proto_gen.memos.api.v1 import ListMemoTagsRequest 5 | 6 | if __name__ == "__main__": 7 | # main() 8 | print(TypeAdapter(ListMemoTagsRequest).json_schema()) 9 | -------------------------------------------------------------------------------- /proto/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | name: buf.build/yourselfhosted/memos 3 | breaking: 4 | use: 5 | - FILE 6 | lint: 7 | use: 8 | - BASIC 9 | except: 10 | - ENUM_VALUE_PREFIX 11 | - PACKAGE_DIRECTORY_MATCH 12 | - PACKAGE_VERSION_SUFFIX 13 | deps: 14 | - buf.build/googleapis/googleapis 15 | -------------------------------------------------------------------------------- /proto/api/v1/common.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | option go_package = "gen/api/v1"; 6 | 7 | enum RowStatus { 8 | ROW_STATUS_UNSPECIFIED = 0; 9 | ACTIVE = 1; 10 | ARCHIVED = 2; 11 | } 12 | 13 | // Used internally for obfuscating the page token. 14 | message PageToken { 15 | int32 limit = 1; 16 | int32 offset = 2; 17 | } 18 | -------------------------------------------------------------------------------- /proto/api/v1/memo_relation_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | option go_package = "gen/api/v1"; 6 | 7 | message MemoRelation { 8 | // The name of memo. 9 | // Format: "memos/{uid}" 10 | string memo = 1; 11 | 12 | // The name of related memo. 13 | // Format: "memos/{uid}" 14 | string related_memo = 2; 15 | 16 | enum Type { 17 | TYPE_UNSPECIFIED = 0; 18 | REFERENCE = 1; 19 | COMMENT = 2; 20 | } 21 | Type type = 3; 22 | } 23 | -------------------------------------------------------------------------------- /proto/buf.gen.yaml: -------------------------------------------------------------------------------- 1 | version: v2 2 | managed: 3 | enabled: true 4 | disable: 5 | - file_option: go_package 6 | module: buf.build/googleapis/googleapis 7 | plugins: 8 | # Google's official protoc-gen-python is rubbish. Use betterproto instead. 9 | # Only V2 support proto3 optional fields. Need install with `pip install --pre`. 10 | # https://github.com/danielgtaylor/python-betterproto/issues/574 11 | - local: protoc-gen-python_betterproto 12 | # https://github.com/danielgtaylor/python-betterproto 13 | out: ../src/mcp_server_memos/proto_gen 14 | strategy: directory 15 | -------------------------------------------------------------------------------- /proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: googleapis 6 | repository: googleapis 7 | commit: acd896313c55464b993332136ded1b6e 8 | digest: shake256:66626d5e4d9c8ecf25cd72bdbfbbf62b9a68e9e9c33dab6b9b39a53a67063eeba6b8493247dd6d6240b1ac1c32eb2dc311484e67dd7d271884a960c2e5ce8c9a 9 | - remote: buf.build 10 | owner: grpc-ecosystem 11 | repository: grpc-gateway 12 | commit: a48fcebcf8f140dd9d09359b9bb185a4 13 | digest: shake256:a926173f0ec3e1a929462c350acda846e546134b5ce2bb83fe44f02f9330a42b1c9b292f64b951b06a4d2c47e2ce4d477d6a2cb31502a15637ada35ecedefcf6 14 | -------------------------------------------------------------------------------- /proto/api/v1/reaction_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | option go_package = "gen/api/v1"; 6 | 7 | message Reaction { 8 | int32 id = 1; 9 | 10 | // The name of the creator. 11 | // Format: users/{id} 12 | string creator = 2; 13 | 14 | string content_id = 3; 15 | 16 | enum Type { 17 | TYPE_UNSPECIFIED = 0; 18 | THUMBS_UP = 1; 19 | THUMBS_DOWN = 2; 20 | HEART = 3; 21 | FIRE = 4; 22 | CLAPPING_HANDS = 5; 23 | LAUGH = 6; 24 | OK_HAND = 7; 25 | ROCKET = 8; 26 | EYES = 9; 27 | THINKING_FACE = 10; 28 | CLOWN_FACE = 11; 29 | QUESTION_MARK = 12; 30 | } 31 | Type reaction_type = 4; 32 | } 33 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | FROM python:3.12-slim 3 | 4 | # Set working directory 5 | WORKDIR /app 6 | 7 | # Install build dependencies 8 | RUN apt-get update && apt-get install -y gcc && rm -rf /var/lib/apt/lists/* 9 | 10 | # Copy the project files 11 | COPY . /app 12 | 13 | # Upgrade pip 14 | RUN pip install --upgrade pip 15 | 16 | # Install the package using pip in editable mode 17 | RUN pip install . 18 | 19 | # Expose the port that the MCP server listens on - default from config, here using 8080 20 | EXPOSE 8080 21 | 22 | # Run the MCP server. The MCP server entrypoint is provided via the setuptools entrypoint 'mcp-server-memos'. 23 | CMD ["mcp-server-memos", "--host", "localhost", "--port", "8080", "--token", ""] 24 | -------------------------------------------------------------------------------- /proto/api/v1/workspace_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "google/api/annotations.proto"; 6 | 7 | option go_package = "gen/api/v1"; 8 | 9 | service WorkspaceService { 10 | // GetWorkspaceProfile returns the workspace profile. 11 | rpc GetWorkspaceProfile(GetWorkspaceProfileRequest) returns (WorkspaceProfile) { 12 | option (google.api.http) = {get: "/api/v1/workspace/profile"}; 13 | } 14 | } 15 | 16 | message WorkspaceProfile { 17 | // The name of instance owner. 18 | // Format: "users/{id}" 19 | string owner = 1; 20 | // version is the current version of instance 21 | string version = 2; 22 | // mode is the instance mode (e.g. "prod", "dev" or "demo"). 23 | string mode = 3; 24 | } 25 | 26 | message GetWorkspaceProfileRequest {} 27 | -------------------------------------------------------------------------------- /src/mcp_server_memos/__init__.py: -------------------------------------------------------------------------------- 1 | from .config import Config 2 | from .server import new_server, serve_stdio 3 | 4 | 5 | def main(): 6 | import argparse 7 | import asyncio 8 | 9 | parser = argparse.ArgumentParser( 10 | description="Give a model the ability to access Memos Server" 11 | ) 12 | parser.add_argument( 13 | "--host", default="localhost", help="The host name to connect to" 14 | ) 15 | parser.add_argument( 16 | "--port", default=8080, help="The port number for the Memos Server" 17 | ) 18 | parser.add_argument( 19 | "--token", default="", help="The token to use for authentication" 20 | ) 21 | args = parser.parse_args() 22 | 23 | config = Config( 24 | host=args.host, 25 | port=args.port, 26 | token=args.token, 27 | ) 28 | 29 | asyncio.run(serve_stdio(config=config)) 30 | 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: RyoJerryYu 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: # Replace with a single Buy Me a Coffee username 14 | thanks_dev: # Replace with a single thanks.dev username 15 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "mcp-server-memos" 3 | version = "0.1.15" 4 | description = "Add your description here" 5 | readme = "README.md" 6 | requires-python = ">=3.12" 7 | license = { file = "LICENSE" } 8 | dependencies = [ 9 | "mcp>=1.1.0", 10 | "betterproto[compiler]>=2.0.0b6", 11 | "grpclib>=0.4.7", 12 | "pydantic>=2.10.3", 13 | ] 14 | classifiers = [ 15 | "Development Status :: 3 - Alpha", 16 | "Intended Audience :: Developers", 17 | "License :: OSI Approved :: MIT License", 18 | "Programming Language :: Python :: 3", 19 | "Programming Language :: Python :: 3.12", 20 | "Programming Language :: Python :: 3.13", 21 | ] 22 | [build-system] 23 | requires = ["hatchling"] 24 | build-backend = "hatchling.build" 25 | 26 | [project.scripts] 27 | mcp-server-memos = "mcp_server_memos:main" 28 | 29 | [project.urls] 30 | Repository = "https://github.com/RyoJerryYu/mcp-server-memos-py" 31 | 32 | [tool.uv] 33 | package = true 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 RyoJerryYu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - host 10 | - port 11 | - token 12 | properties: 13 | host: 14 | type: string 15 | default: localhost 16 | description: Memos server hostname 17 | port: 18 | type: number 19 | default: 8080 20 | description: Memos server port 21 | token: 22 | type: string 23 | default: "" 24 | description: Access token for authentication 25 | commandFunction: 26 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio. 27 | |- 28 | (config) => ({ 29 | command: 'mcp-server-memos', 30 | args: [ 31 | '--host', config.host, 32 | '--port', String(config.port), 33 | '--token', config.token 34 | ], 35 | env: {} 36 | }) 37 | exampleConfig: 38 | host: localhost 39 | port: 5230 40 | token: your-access-token-here 41 | -------------------------------------------------------------------------------- /proto/api/v1/activity_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/api/client.proto"; 7 | import "google/api/field_behavior.proto"; 8 | import "google/protobuf/timestamp.proto"; 9 | 10 | option go_package = "gen/api/v1"; 11 | 12 | service ActivityService { 13 | // GetActivity returns the activity with the given id. 14 | rpc GetActivity(GetActivityRequest) returns (Activity) { 15 | option (google.api.http) = {get: "/api/v1/activities/{id}"}; 16 | option (google.api.method_signature) = "id"; 17 | } 18 | } 19 | 20 | message Activity { 21 | // The system-generated unique identifier for the activity. 22 | int32 id = 1; 23 | // The system-generated unique identifier for the user who created the activity. 24 | int32 creator_id = 2; 25 | // The type of the activity. 26 | string type = 3; 27 | // The level of the activity. 28 | string level = 4; 29 | // The create time of the activity. 30 | google.protobuf.Timestamp create_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; 31 | // The payload of the activity. 32 | ActivityPayload payload = 6; 33 | } 34 | 35 | // ActivityMemoCommentPayload represents the payload of a memo comment activity. 36 | message ActivityMemoCommentPayload { 37 | // The memo id of comment. 38 | int32 memo_id = 1; 39 | // The memo id of related memo. 40 | int32 related_memo_id = 2; 41 | } 42 | 43 | message ActivityVersionUpdatePayload { 44 | // The updated version of memos. 45 | string version = 1; 46 | } 47 | 48 | message ActivityPayload { 49 | ActivityMemoCommentPayload memo_comment = 1; 50 | ActivityVersionUpdatePayload version_update = 2; 51 | } 52 | 53 | message GetActivityRequest { 54 | // The system-generated unique identifier for the activity. 55 | int32 id = 1; 56 | } 57 | -------------------------------------------------------------------------------- /proto/api/v1/auth_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "api/v1/user_service.proto"; 6 | import "google/api/annotations.proto"; 7 | import "google/protobuf/empty.proto"; 8 | 9 | option go_package = "gen/api/v1"; 10 | 11 | service AuthService { 12 | // GetAuthStatus returns the current auth status of the user. 13 | rpc GetAuthStatus(GetAuthStatusRequest) returns (User) { 14 | option (google.api.http) = {post: "/api/v1/auth/status"}; 15 | } 16 | // SignIn signs in the user with the given username and password. 17 | rpc SignIn(SignInRequest) returns (User) { 18 | option (google.api.http) = {post: "/api/v1/auth/signin"}; 19 | } 20 | // SignInWithSSO signs in the user with the given SSO code. 21 | rpc SignInWithSSO(SignInWithSSORequest) returns (User) { 22 | option (google.api.http) = {post: "/api/v1/auth/signin/sso"}; 23 | } 24 | // SignUp signs up the user with the given username and password. 25 | rpc SignUp(SignUpRequest) returns (User) { 26 | option (google.api.http) = {post: "/api/v1/auth/signup"}; 27 | } 28 | // SignOut signs out the user. 29 | rpc SignOut(SignOutRequest) returns (google.protobuf.Empty) { 30 | option (google.api.http) = {post: "/api/v1/auth/signout"}; 31 | } 32 | } 33 | 34 | message GetAuthStatusRequest {} 35 | 36 | message GetAuthStatusResponse { 37 | User user = 1; 38 | } 39 | 40 | message SignInRequest { 41 | // The username to sign in with. 42 | string username = 1; 43 | // The password to sign in with. 44 | string password = 2; 45 | // Whether the session should never expire. 46 | bool never_expire = 3; 47 | } 48 | 49 | message SignInWithSSORequest { 50 | // The ID of the SSO provider. 51 | int32 idp_id = 1; 52 | // The code to sign in with. 53 | string code = 2; 54 | // The redirect URI. 55 | string redirect_uri = 3; 56 | } 57 | 58 | message SignUpRequest { 59 | // The username to sign up with. 60 | string username = 1; 61 | // The password to sign up with. 62 | string password = 2; 63 | } 64 | 65 | message SignOutRequest {} 66 | -------------------------------------------------------------------------------- /proto/api/v1/inbox_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/api/client.proto"; 7 | import "google/protobuf/empty.proto"; 8 | import "google/protobuf/field_mask.proto"; 9 | import "google/protobuf/timestamp.proto"; 10 | 11 | option go_package = "gen/api/v1"; 12 | 13 | service InboxService { 14 | // ListInboxes lists inboxes for a user. 15 | rpc ListInboxes(ListInboxesRequest) returns (ListInboxesResponse) { 16 | option (google.api.http) = {get: "/api/v1/inboxes"}; 17 | } 18 | // UpdateInbox updates an inbox. 19 | rpc UpdateInbox(UpdateInboxRequest) returns (Inbox) { 20 | option (google.api.http) = { 21 | patch: "/api/v1/{inbox.name=inboxes/*}" 22 | body: "inbox" 23 | }; 24 | option (google.api.method_signature) = "inbox,update_mask"; 25 | } 26 | // DeleteInbox deletes an inbox. 27 | rpc DeleteInbox(DeleteInboxRequest) returns (google.protobuf.Empty) { 28 | option (google.api.http) = {delete: "/api/v1/{name=inboxes/*}"}; 29 | option (google.api.method_signature) = "name"; 30 | } 31 | } 32 | 33 | message Inbox { 34 | // The name of the inbox. 35 | // Format: inboxes/{id} 36 | string name = 1; 37 | // Format: users/{id} 38 | string sender = 2; 39 | // Format: users/{id} 40 | string receiver = 3; 41 | 42 | enum Status { 43 | STATUS_UNSPECIFIED = 0; 44 | UNREAD = 1; 45 | ARCHIVED = 2; 46 | } 47 | Status status = 4; 48 | 49 | google.protobuf.Timestamp create_time = 5; 50 | 51 | enum Type { 52 | TYPE_UNSPECIFIED = 0; 53 | MEMO_COMMENT = 1; 54 | VERSION_UPDATE = 2; 55 | } 56 | Type type = 6; 57 | 58 | optional int32 activity_id = 7; 59 | } 60 | 61 | message ListInboxesRequest { 62 | // Format: users/{id} 63 | string user = 1; 64 | } 65 | 66 | message ListInboxesResponse { 67 | repeated Inbox inboxes = 1; 68 | } 69 | 70 | message UpdateInboxRequest { 71 | Inbox inbox = 1; 72 | 73 | google.protobuf.FieldMask update_mask = 2; 74 | } 75 | 76 | message DeleteInboxRequest { 77 | // The name of the inbox to delete. 78 | // Format: inboxes/{id} 79 | string name = 1; 80 | } 81 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Create a Github Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: write 13 | 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v4 17 | 18 | # - name: Set up Python 19 | # uses: actions/setup-python@v5 20 | # with: 21 | # python-version: 3.12 22 | # cache: pip 23 | 24 | # - name: Install dependencies 25 | # run: | 26 | # python -m pip install --upgrade pip 27 | # pip install uv 28 | # uv sync 29 | 30 | # - name: Run tests 31 | # run: make test 32 | 33 | - name: Create Release 34 | id: create_release 35 | uses: softprops/action-gh-release@v1 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | with: 39 | files: | 40 | LICENSE 41 | README.md 42 | tag_name: ${{ github.ref_name }} 43 | release_name: Release ${{ github.ref_name }} 44 | draft: false 45 | prerelease: false 46 | 47 | publish_pypi: 48 | runs-on: ubuntu-latest 49 | steps: 50 | - name: Checkout code 51 | uses: actions/checkout@v4 52 | - name: Set up Python 53 | uses: actions/setup-python@v5 54 | with: 55 | python-version: 3.12 56 | - name: Install dependencies 57 | run: | 58 | python -m pip install --upgrade pip 59 | pip install uv 60 | uv sync 61 | - name: Update version 62 | run: | 63 | sed -i "s/^version = \"0.1.15\"$/version = \"$GITHUB_REF_NAME\"/" pyproject.toml 64 | - name: Build 65 | run: uv build 66 | - name: Publish to test PyPI 67 | run: uv publish --publish-url https://test.pypi.org/legacy/ 68 | env: 69 | UV_PUBLISH_USERNAME: __token__ 70 | UV_PUBLISH_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }} 71 | - name: Publish to PyPI 72 | run: uv publish 73 | env: 74 | UV_PUBLISH_USERNAME: __token__ 75 | UV_PUBLISH_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} 76 | -------------------------------------------------------------------------------- /proto/api/v1/webhook_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "api/v1/common.proto"; 6 | import "api/v1/memo_service.proto"; 7 | import "google/api/annotations.proto"; 8 | import "google/api/client.proto"; 9 | import "google/protobuf/empty.proto"; 10 | import "google/protobuf/field_mask.proto"; 11 | import "google/protobuf/timestamp.proto"; 12 | 13 | option go_package = "gen/api/v1"; 14 | 15 | service WebhookService { 16 | // CreateWebhook creates a new webhook. 17 | rpc CreateWebhook(CreateWebhookRequest) returns (Webhook) { 18 | option (google.api.http) = { 19 | post: "/api/v1/webhooks" 20 | body: "*" 21 | }; 22 | } 23 | // GetWebhook returns a webhook by id. 24 | rpc GetWebhook(GetWebhookRequest) returns (Webhook) { 25 | option (google.api.http) = {get: "/api/v1/webhooks/{id}"}; 26 | option (google.api.method_signature) = "id"; 27 | } 28 | // ListWebhooks returns a list of webhooks. 29 | rpc ListWebhooks(ListWebhooksRequest) returns (ListWebhooksResponse) { 30 | option (google.api.http) = {get: "/api/v1/webhooks"}; 31 | } 32 | // UpdateWebhook updates a webhook. 33 | rpc UpdateWebhook(UpdateWebhookRequest) returns (Webhook) { 34 | option (google.api.http) = { 35 | patch: "/api/v1/webhooks/{webhook.id}" 36 | body: "webhook" 37 | }; 38 | option (google.api.method_signature) = "webhook,update_mask"; 39 | } 40 | // DeleteWebhook deletes a webhook by id. 41 | rpc DeleteWebhook(DeleteWebhookRequest) returns (google.protobuf.Empty) { 42 | option (google.api.http) = {delete: "/api/v1/webhooks/{id}"}; 43 | option (google.api.method_signature) = "id"; 44 | } 45 | } 46 | 47 | message Webhook { 48 | int32 id = 1; 49 | 50 | int32 creator_id = 2; 51 | 52 | google.protobuf.Timestamp create_time = 3; 53 | 54 | google.protobuf.Timestamp update_time = 4; 55 | 56 | RowStatus row_status = 5; 57 | 58 | string name = 6; 59 | 60 | string url = 7; 61 | } 62 | 63 | message CreateWebhookRequest { 64 | string name = 1; 65 | 66 | string url = 2; 67 | } 68 | 69 | message GetWebhookRequest { 70 | int32 id = 1; 71 | } 72 | 73 | message ListWebhooksRequest { 74 | int32 creator_id = 1; 75 | } 76 | 77 | message ListWebhooksResponse { 78 | repeated Webhook webhooks = 1; 79 | } 80 | 81 | message UpdateWebhookRequest { 82 | Webhook webhook = 1; 83 | 84 | google.protobuf.FieldMask update_mask = 2; 85 | } 86 | 87 | message DeleteWebhookRequest { 88 | int32 id = 1; 89 | } 90 | 91 | message WebhookRequestPayload { 92 | string url = 1; 93 | 94 | string activity_type = 2; 95 | 96 | int32 creator_id = 3; 97 | 98 | google.protobuf.Timestamp create_time = 4; 99 | 100 | Memo memo = 5; 101 | } 102 | -------------------------------------------------------------------------------- /proto/api/v1/idp_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/api/client.proto"; 7 | import "google/protobuf/empty.proto"; 8 | import "google/protobuf/field_mask.proto"; 9 | 10 | option go_package = "gen/api/v1"; 11 | 12 | service IdentityProviderService { 13 | // ListIdentityProviders lists identity providers. 14 | rpc ListIdentityProviders(ListIdentityProvidersRequest) returns (ListIdentityProvidersResponse) { 15 | option (google.api.http) = {get: "/api/v1/identityProviders"}; 16 | } 17 | // GetIdentityProvider gets an identity provider. 18 | rpc GetIdentityProvider(GetIdentityProviderRequest) returns (IdentityProvider) { 19 | option (google.api.http) = {get: "/api/v1/{name=identityProviders/*}"}; 20 | option (google.api.method_signature) = "name"; 21 | } 22 | // CreateIdentityProvider creates an identity provider. 23 | rpc CreateIdentityProvider(CreateIdentityProviderRequest) returns (IdentityProvider) { 24 | option (google.api.http) = { 25 | post: "/api/v1/identityProviders", 26 | body: "identity_provider" 27 | }; 28 | } 29 | // UpdateIdentityProvider updates an identity provider. 30 | rpc UpdateIdentityProvider(UpdateIdentityProviderRequest) returns (IdentityProvider) { 31 | option (google.api.http) = { 32 | patch: "/api/v1/{identity_provider.name=identityProviders/*}" 33 | body: "identity_provider" 34 | }; 35 | option (google.api.method_signature) = "identity_provider,update_mask"; 36 | } 37 | // DeleteIdentityProvider deletes an identity provider. 38 | rpc DeleteIdentityProvider(DeleteIdentityProviderRequest) returns (google.protobuf.Empty) { 39 | option (google.api.http) = {delete: "/api/v1/{name=identityProviders/*}"}; 40 | option (google.api.method_signature) = "name"; 41 | } 42 | } 43 | 44 | message IdentityProvider { 45 | // The name of the identityProvider. 46 | // Format: identityProviders/{id} 47 | string name = 1; 48 | 49 | enum Type { 50 | TYPE_UNSPECIFIED = 0; 51 | OAUTH2 = 1; 52 | } 53 | Type type = 2; 54 | 55 | string title = 3; 56 | 57 | string identifier_filter = 4; 58 | 59 | IdentityProviderConfig config = 5; 60 | } 61 | 62 | message IdentityProviderConfig { 63 | oneof config { 64 | OAuth2Config oauth2_config = 1; 65 | } 66 | } 67 | 68 | message FieldMapping { 69 | string identifier = 1; 70 | string display_name = 2; 71 | string email = 3; 72 | } 73 | 74 | message OAuth2Config { 75 | string client_id = 1; 76 | string client_secret = 2; 77 | string auth_url = 3; 78 | string token_url = 4; 79 | string user_info_url = 5; 80 | repeated string scopes = 6; 81 | FieldMapping field_mapping = 7; 82 | } 83 | 84 | message ListIdentityProvidersRequest {} 85 | 86 | message ListIdentityProvidersResponse { 87 | repeated IdentityProvider identity_providers = 1; 88 | } 89 | 90 | message GetIdentityProviderRequest { 91 | // The name of the identityProvider to get. 92 | // Format: identityProviders/{id} 93 | string name = 1; 94 | } 95 | 96 | message CreateIdentityProviderRequest { 97 | // The identityProvider to create. 98 | IdentityProvider identity_provider = 1; 99 | } 100 | 101 | message UpdateIdentityProviderRequest { 102 | // The identityProvider to update. 103 | IdentityProvider identity_provider = 1; 104 | 105 | // The update mask applies to the resource. Only the top level fields of 106 | // IdentityProvider are supported. 107 | google.protobuf.FieldMask update_mask = 2; 108 | } 109 | 110 | message DeleteIdentityProviderRequest { 111 | // The name of the identityProvider to delete. 112 | // Format: identityProviders/{id} 113 | string name = 1; 114 | } 115 | -------------------------------------------------------------------------------- /proto/api/v1/workspace_setting_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/api/client.proto"; 7 | import "google/api/field_behavior.proto"; 8 | 9 | option go_package = "gen/api/v1"; 10 | 11 | service WorkspaceSettingService { 12 | // GetWorkspaceSetting returns the setting by name. 13 | rpc GetWorkspaceSetting(GetWorkspaceSettingRequest) returns (WorkspaceSetting) { 14 | option (google.api.http) = {get: "/api/v1/workspace/{name=settings/*}"}; 15 | option (google.api.method_signature) = "name"; 16 | } 17 | // SetWorkspaceSetting updates the setting. 18 | rpc SetWorkspaceSetting(SetWorkspaceSettingRequest) returns (WorkspaceSetting) { 19 | option (google.api.http) = { 20 | patch: "/api/v1/workspace/{setting.name=settings/*}", 21 | body: "setting" 22 | }; 23 | option (google.api.method_signature) = "setting"; 24 | } 25 | } 26 | 27 | message WorkspaceSetting { 28 | // name is the name of the setting. 29 | // Format: settings/{setting} 30 | string name = 1; 31 | oneof value { 32 | WorkspaceGeneralSetting general_setting = 2; 33 | WorkspaceStorageSetting storage_setting = 3; 34 | WorkspaceMemoRelatedSetting memo_related_setting = 4; 35 | } 36 | } 37 | 38 | message WorkspaceGeneralSetting { 39 | // disallow_signup is the flag to disallow signup. 40 | bool disallow_signup = 1; 41 | // disallow_password_login is the flag to disallow password login. 42 | bool disallow_password_login = 2; 43 | // additional_script is the additional script. 44 | string additional_script = 3; 45 | // additional_style is the additional style. 46 | string additional_style = 4; 47 | // custom_profile is the custom profile. 48 | WorkspaceCustomProfile custom_profile = 5; 49 | } 50 | 51 | message WorkspaceCustomProfile { 52 | string title = 1; 53 | string description = 2; 54 | string logo_url = 3; 55 | string locale = 4; 56 | string appearance = 5; 57 | } 58 | 59 | message WorkspaceStorageSetting { 60 | enum StorageType { 61 | STORAGE_TYPE_UNSPECIFIED = 0; 62 | // DATABASE is the database storage type. 63 | DATABASE = 1; 64 | // LOCAL is the local storage type. 65 | LOCAL = 2; 66 | // S3 is the S3 storage type. 67 | S3 = 3; 68 | } 69 | // storage_type is the storage type. 70 | StorageType storage_type = 1; 71 | // The template of file path. 72 | // e.g. assets/{timestamp}_{filename} 73 | string filepath_template = 2; 74 | // The max upload size in megabytes. 75 | int64 upload_size_limit_mb = 3; 76 | // Reference: https://developers.cloudflare.com/r2/examples/aws/aws-sdk-go/ 77 | message S3Config { 78 | string access_key_id = 1; 79 | string access_key_secret = 2; 80 | string endpoint = 3; 81 | string region = 4; 82 | string bucket = 5; 83 | } 84 | // The S3 config. 85 | S3Config s3_config = 4; 86 | } 87 | 88 | message WorkspaceMemoRelatedSetting { 89 | // disallow_public_share disallows set memo as public visible. 90 | bool disallow_public_visible = 1; 91 | // display_with_update_time orders and displays memo with update time. 92 | bool display_with_update_time = 2; 93 | // content_length_limit is the limit of content length. Unit is byte. 94 | int32 content_length_limit = 3; 95 | // enable_auto_compact enables auto compact for large content. 96 | bool enable_auto_compact = 4; 97 | // enable_double_click_edit enables editing on double click. 98 | bool enable_double_click_edit = 5; 99 | } 100 | 101 | message GetWorkspaceSettingRequest { 102 | // The resource name of the workspace setting. 103 | // Format: settings/{setting} 104 | string name = 1 [(google.api.field_behavior) = REQUIRED]; 105 | } 106 | 107 | message SetWorkspaceSettingRequest { 108 | // setting is the setting to update. 109 | WorkspaceSetting setting = 1; 110 | } 111 | -------------------------------------------------------------------------------- /proto/api/v1/resource_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "google/api/annotations.proto"; 6 | import "google/api/client.proto"; 7 | import "google/api/field_behavior.proto"; 8 | import "google/api/httpbody.proto"; 9 | import "google/protobuf/empty.proto"; 10 | import "google/protobuf/field_mask.proto"; 11 | import "google/protobuf/timestamp.proto"; 12 | 13 | option go_package = "gen/api/v1"; 14 | 15 | service ResourceService { 16 | // CreateResource creates a new resource. 17 | rpc CreateResource(CreateResourceRequest) returns (Resource) { 18 | option (google.api.http) = { 19 | post: "/api/v1/resources", 20 | body: "resource" 21 | }; 22 | } 23 | // ListResources lists all resources. 24 | rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse) { 25 | option (google.api.http) = {get: "/api/v1/resources"}; 26 | } 27 | // SearchResources searches memos. 28 | rpc SearchResources(SearchResourcesRequest) returns (SearchResourcesResponse) { 29 | option (google.api.http) = {get: "/api/v1/resources:search"}; 30 | } 31 | // GetResource returns a resource by name. 32 | rpc GetResource(GetResourceRequest) returns (Resource) { 33 | option (google.api.http) = {get: "/api/v1/{name=resources/*}"}; 34 | option (google.api.method_signature) = "name"; 35 | } 36 | // GetResourceBinary returns a resource binary by name. 37 | rpc GetResourceBinary(GetResourceBinaryRequest) returns (google.api.HttpBody) { 38 | option (google.api.http) = {get: "/file/{name=resources/*}/{filename}"}; 39 | option (google.api.method_signature) = "name,filename"; 40 | } 41 | // UpdateResource updates a resource. 42 | rpc UpdateResource(UpdateResourceRequest) returns (Resource) { 43 | option (google.api.http) = { 44 | patch: "/api/v1/{resource.name=resources/*}", 45 | body: "resource" 46 | }; 47 | option (google.api.method_signature) = "resource,update_mask"; 48 | } 49 | // DeleteResource deletes a resource by name. 50 | rpc DeleteResource(DeleteResourceRequest) returns (google.protobuf.Empty) { 51 | option (google.api.http) = {delete: "/api/v1/{name=resources/*}"}; 52 | option (google.api.method_signature) = "name"; 53 | } 54 | } 55 | 56 | message Resource { 57 | // The name of the resource. 58 | // Format: resources/{id} 59 | // id is the system generated unique identifier. 60 | string name = 1; 61 | 62 | // The user defined id of the resource. 63 | string uid = 2; 64 | 65 | google.protobuf.Timestamp create_time = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; 66 | 67 | string filename = 4; 68 | 69 | bytes content = 5 [(google.api.field_behavior) = INPUT_ONLY]; 70 | 71 | string external_link = 6; 72 | 73 | string type = 7; 74 | 75 | int64 size = 8; 76 | 77 | // The related memo. 78 | // Format: memos/{id} 79 | optional string memo = 9; 80 | } 81 | 82 | message CreateResourceRequest { 83 | Resource resource = 1; 84 | } 85 | 86 | message ListResourcesRequest {} 87 | 88 | message ListResourcesResponse { 89 | repeated Resource resources = 1; 90 | } 91 | 92 | message SearchResourcesRequest { 93 | string filter = 1; 94 | } 95 | 96 | message SearchResourcesResponse { 97 | repeated Resource resources = 1; 98 | } 99 | 100 | message GetResourceRequest { 101 | // The name of the resource. 102 | // Format: resources/{id} 103 | // id is the system generated unique identifier. 104 | string name = 1; 105 | } 106 | 107 | message GetResourceBinaryRequest { 108 | // The name of the resource. 109 | // Format: resources/{id} 110 | // id is the system generated unique identifier. 111 | string name = 1; 112 | 113 | // The filename of the resource. Mainly used for downloading. 114 | string filename = 2; 115 | } 116 | 117 | message UpdateResourceRequest { 118 | Resource resource = 1; 119 | 120 | google.protobuf.FieldMask update_mask = 2; 121 | } 122 | 123 | message DeleteResourceRequest { 124 | // The name of the resource. 125 | // Format: resources/{id} 126 | // id is the system generated unique identifier. 127 | string name = 1; 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![MseeP.ai Security Assessment Badge](https://mseep.net/pr/ryojerryyu-mcp-server-memos-py-badge.png)](https://mseep.ai/app/ryojerryyu-mcp-server-memos-py) 2 | 3 | # MCP Server Memos 📝 4 | 5 | [![PyPI version](https://img.shields.io/pypi/v/mcp-server-memos.svg)](https://pypi.org/project/mcp-server-memos/) 6 | [![Python Version](https://img.shields.io/pypi/pyversions/mcp-server-memos.svg)](https://pypi.org/project/mcp-server-memos/) 7 | [![License](https://img.shields.io/github/license/RyoJerryYu/mcp-server-memos-py.svg)](https://github.com/RyoJerryYu/mcp-server-memos-py/blob/master/LICENSE) 8 | [![smithery badge](https://smithery.ai/badge/@RyoJerryYu/mcp-server-memos-py)](https://smithery.ai/server/@RyoJerryYu/mcp-server-memos-py) 9 | 10 | A Python package that provides LLM models with the ability to interact with [Memos](https://github.com/usememos/memos) server through the [MCP (Model Context Protocol)](https://modelcontextprotocol.io/introduction) interface. 11 | 12 | ## 🚀 Features 13 | 14 | - 🔍 Search memos with keywords 15 | - ✨ Create new memos with customizable visibility 16 | - 📖 Retrieve memo content by ID 17 | - 🏷️ List and manage memo tags 18 | - 🔐 Secure authentication using access tokens 19 | 20 | ## 🛠️ Usage 21 | 22 | You can include this package in your config file as bellow, just as you use other Python MCP plugins. 23 | 24 | ```jsonc 25 | { 26 | ..., 27 | "mcpServers": { 28 | "fetch": { // other mcp servers 29 | "command": "uvx", 30 | "args": ["mcp-server-fetch"] 31 | }, 32 | "memos": { // add this to your config 33 | "command": "uvx", 34 | "args": [ 35 | "--prerelease=allow", 36 | "mcp-server-memos", 37 | "--host", 38 | "localhost", 39 | "--port", 40 | "5230", 41 | "--token", 42 | "your-access-token-here" 43 | ] 44 | } 45 | } 46 | } 47 | ``` 48 | 49 |
50 | Other ways to use this package 51 | 52 | ### 📦 Installation 53 | 54 | #### Installing via Smithery 55 | 56 | To install mcp-server-memos-py for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@RyoJerryYu/mcp-server-memos-py): 57 | 58 | ```bash 59 | npx -y @smithery/cli install @RyoJerryYu/mcp-server-memos-py --client claude 60 | ``` 61 | 62 | #### Installing Manually 63 | ```bash 64 | pip install mcp-server-memos 65 | ``` 66 | 67 | ### Command Line 68 | 69 | ```bash 70 | mcp-server-memos --host localhost --port 8080 --token YOUR_ACCESS_TOKEN 71 | ``` 72 | 73 | ### As a Library 74 | 75 | ```python 76 | from mcp_server_memos import Config, serve_stdio 77 | 78 | config = Config( 79 | host="localhost", 80 | port=8080, 81 | token="YOUR_ACCESS_TOKEN" 82 | ) 83 | 84 | await serve_stdio(config=config) 85 | ``` 86 | 87 |
88 | 89 | ## 🔧 Configuration 90 | 91 | | Parameter | Description | Default | 92 | |-----------|-------------|---------| 93 | | `host` | Memos server hostname | `localhost` | 94 | | `port` | Memos server port | `8080` | 95 | | `token` | Access token for authentication | `""` | 96 | 97 | ## 🤝 Available Tools 98 | 99 | This MCP server provides the following tools for interacting with Memos: 100 | 101 | | Tool Name | Description | Parameters | 102 | |-----------|-------------|------------| 103 | | `list_memo_tags` | List all existing memo tags | - `parent`: The parent who owns the tags (format: memos/{id}, default: "memos/-")
- `visibility`: Tag visibility (PUBLIC/PROTECTED/PRIVATE, default: PRIVATE) | 104 | | `search_memo` | Search for memos using keywords | - `key_word`: The keywords to search for in memo content | 105 | | `create_memo` | Create a new memo | - `content`: The content of the memo
- `visibility`: Memo visibility (PUBLIC/PROTECTED/PRIVATE, default: PRIVATE) | 106 | | `get_memo` | Get a specific memo by ID | - `name`: The name/ID of the memo (format: memos/{id}) | 107 | 108 | ## 🤝 Contributing 109 | 110 | Contributions are welcome! Please feel free to submit a Pull Request. 111 | 112 | ## 📄 License 113 | 114 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 115 | 116 | ## 🙏 Acknowledgments 117 | 118 | - [Memos](https://github.com/usememos/memos) - A lightweight, self-hosted memo hub 119 | - [MCP (Model Context Protocol)](https://modelcontextprotocol.io/introduction) - Protocol for LLM model applications 120 | -------------------------------------------------------------------------------- /proto/api/v1/markdown_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "google/api/annotations.proto"; 6 | 7 | option go_package = "gen/api/v1"; 8 | 9 | service MarkdownService { 10 | // Parses the given markdown content and returns a list of nodes. 11 | rpc ParseMarkdown(ParseMarkdownRequest) returns (ParseMarkdownResponse) { 12 | option (google.api.http) = { 13 | post: "/api/v1/markdown/parse" 14 | body: "*" 15 | }; 16 | } 17 | // Restores the given nodes to markdown content. 18 | rpc RestoreMarkdown(RestoreMarkdownRequest) returns (RestoreMarkdownResponse) { 19 | option (google.api.http) = { 20 | post: "/api/v1/markdown:restore" 21 | body: "*" 22 | }; 23 | } 24 | // GetLinkMetadata returns metadata for a given link. 25 | rpc GetLinkMetadata(GetLinkMetadataRequest) returns (LinkMetadata) { 26 | option (google.api.http) = {get: "/api/v1/markdown/link:metadata"}; 27 | } 28 | } 29 | 30 | message ParseMarkdownRequest { 31 | string markdown = 1; 32 | } 33 | 34 | message ParseMarkdownResponse { 35 | repeated Node nodes = 1; 36 | } 37 | 38 | message RestoreMarkdownRequest { 39 | repeated Node nodes = 1; 40 | } 41 | 42 | message RestoreMarkdownResponse { 43 | string markdown = 1; 44 | } 45 | 46 | message GetLinkMetadataRequest { 47 | string link = 1; 48 | } 49 | 50 | message LinkMetadata { 51 | string title = 1; 52 | string description = 2; 53 | string image = 3; 54 | } 55 | 56 | enum NodeType { 57 | NODE_UNSPECIFIED = 0; 58 | LINE_BREAK = 1; 59 | PARAGRAPH = 2; 60 | CODE_BLOCK = 3; 61 | HEADING = 4; 62 | HORIZONTAL_RULE = 5; 63 | BLOCKQUOTE = 6; 64 | ORDERED_LIST = 7; 65 | UNORDERED_LIST = 8; 66 | TASK_LIST = 9; 67 | MATH_BLOCK = 10; 68 | TABLE = 11; 69 | EMBEDDED_CONTENT = 12; 70 | TEXT = 13; 71 | BOLD = 14; 72 | ITALIC = 15; 73 | BOLD_ITALIC = 16; 74 | CODE = 17; 75 | IMAGE = 18; 76 | LINK = 19; 77 | AUTO_LINK = 20; 78 | TAG = 21; 79 | STRIKETHROUGH = 22; 80 | ESCAPING_CHARACTER = 23; 81 | MATH = 24; 82 | HIGHLIGHT = 25; 83 | SUBSCRIPT = 26; 84 | SUPERSCRIPT = 27; 85 | REFERENCED_CONTENT = 28; 86 | SPOILER = 29; 87 | } 88 | 89 | message Node { 90 | NodeType type = 1; 91 | oneof node { 92 | LineBreakNode line_break_node = 2; 93 | ParagraphNode paragraph_node = 3; 94 | CodeBlockNode code_block_node = 4; 95 | HeadingNode heading_node = 5; 96 | HorizontalRuleNode horizontal_rule_node = 6; 97 | BlockquoteNode blockquote_node = 7; 98 | OrderedListNode ordered_list_node = 8; 99 | UnorderedListNode unordered_list_node = 9; 100 | TaskListNode task_list_node = 10; 101 | MathBlockNode math_block_node = 11; 102 | TableNode table_node = 12; 103 | EmbeddedContentNode embedded_content_node = 13; 104 | TextNode text_node = 14; 105 | BoldNode bold_node = 15; 106 | ItalicNode italic_node = 16; 107 | BoldItalicNode bold_italic_node = 17; 108 | CodeNode code_node = 18; 109 | ImageNode image_node = 19; 110 | LinkNode link_node = 20; 111 | AutoLinkNode auto_link_node = 21; 112 | TagNode tag_node = 22; 113 | StrikethroughNode strikethrough_node = 23; 114 | EscapingCharacterNode escaping_character_node = 24; 115 | MathNode math_node = 25; 116 | HighlightNode highlight_node = 26; 117 | SubscriptNode subscript_node = 27; 118 | SuperscriptNode superscript_node = 28; 119 | ReferencedContentNode referenced_content_node = 29; 120 | SpoilerNode spoiler_node = 30; 121 | } 122 | } 123 | 124 | message LineBreakNode {} 125 | 126 | message ParagraphNode { 127 | repeated Node children = 1; 128 | } 129 | 130 | message CodeBlockNode { 131 | string language = 1; 132 | string content = 2; 133 | } 134 | 135 | message HeadingNode { 136 | int32 level = 1; 137 | repeated Node children = 2; 138 | } 139 | 140 | message HorizontalRuleNode { 141 | string symbol = 1; 142 | } 143 | 144 | message BlockquoteNode { 145 | repeated Node children = 1; 146 | } 147 | 148 | message OrderedListNode { 149 | string number = 1; 150 | int32 indent = 2; 151 | repeated Node children = 3; 152 | } 153 | 154 | message UnorderedListNode { 155 | string symbol = 1; 156 | int32 indent = 2; 157 | repeated Node children = 3; 158 | } 159 | 160 | message TaskListNode { 161 | string symbol = 1; 162 | int32 indent = 2; 163 | bool complete = 3; 164 | repeated Node children = 4; 165 | } 166 | 167 | message MathBlockNode { 168 | string content = 1; 169 | } 170 | 171 | message TableNode { 172 | repeated string header = 1; 173 | repeated string delimiter = 2; 174 | 175 | message Row { 176 | repeated string cells = 1; 177 | } 178 | repeated Row rows = 3; 179 | } 180 | 181 | message EmbeddedContentNode { 182 | string resource_name = 1; 183 | string params = 2; 184 | } 185 | 186 | message TextNode { 187 | string content = 1; 188 | } 189 | 190 | message BoldNode { 191 | string symbol = 1; 192 | repeated Node children = 2; 193 | } 194 | 195 | message ItalicNode { 196 | string symbol = 1; 197 | string content = 2; 198 | } 199 | 200 | message BoldItalicNode { 201 | string symbol = 1; 202 | string content = 2; 203 | } 204 | 205 | message CodeNode { 206 | string content = 1; 207 | } 208 | 209 | message ImageNode { 210 | string alt_text = 1; 211 | string url = 2; 212 | } 213 | 214 | message LinkNode { 215 | string text = 1; 216 | string url = 2; 217 | } 218 | 219 | message AutoLinkNode { 220 | string url = 1; 221 | bool is_raw_text = 2; 222 | } 223 | 224 | message TagNode { 225 | string content = 1; 226 | } 227 | 228 | message StrikethroughNode { 229 | string content = 1; 230 | } 231 | 232 | message EscapingCharacterNode { 233 | string symbol = 1; 234 | } 235 | 236 | message MathNode { 237 | string content = 1; 238 | } 239 | 240 | message HighlightNode { 241 | string content = 1; 242 | } 243 | 244 | message SubscriptNode { 245 | string content = 1; 246 | } 247 | 248 | message SuperscriptNode { 249 | string content = 1; 250 | } 251 | 252 | message ReferencedContentNode { 253 | string resource_name = 1; 254 | string params = 2; 255 | } 256 | 257 | message SpoilerNode { 258 | string content = 1; 259 | } 260 | -------------------------------------------------------------------------------- /proto/api/v1/user_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "api/v1/common.proto"; 6 | import "google/api/annotations.proto"; 7 | import "google/api/client.proto"; 8 | import "google/api/field_behavior.proto"; 9 | import "google/api/httpbody.proto"; 10 | import "google/protobuf/empty.proto"; 11 | import "google/protobuf/field_mask.proto"; 12 | import "google/protobuf/timestamp.proto"; 13 | 14 | option go_package = "gen/api/v1"; 15 | 16 | service UserService { 17 | // ListUsers returns a list of users. 18 | rpc ListUsers(ListUsersRequest) returns (ListUsersResponse) { 19 | option (google.api.http) = {get: "/api/v1/users"}; 20 | } 21 | // SearchUsers searches users by filter. 22 | rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse) { 23 | option (google.api.http) = {get: "/api/v1/users:search"}; 24 | } 25 | // GetUser gets a user by name. 26 | rpc GetUser(GetUserRequest) returns (User) { 27 | option (google.api.http) = {get: "/api/v1/{name=users/*}"}; 28 | option (google.api.method_signature) = "name"; 29 | } 30 | // GetUserAvatarBinary gets the avatar of a user. 31 | rpc GetUserAvatarBinary(GetUserAvatarBinaryRequest) returns (google.api.HttpBody) { 32 | option (google.api.http) = {get: "/file/{name=users/*}/avatar"}; 33 | option (google.api.method_signature) = "name"; 34 | } 35 | // CreateUser creates a new user. 36 | rpc CreateUser(CreateUserRequest) returns (User) { 37 | option (google.api.http) = { 38 | post: "/api/v1/users" 39 | body: "user" 40 | }; 41 | option (google.api.method_signature) = "user"; 42 | } 43 | // UpdateUser updates a user. 44 | rpc UpdateUser(UpdateUserRequest) returns (User) { 45 | option (google.api.http) = { 46 | patch: "/api/v1/{user.name=users/*}" 47 | body: "user" 48 | }; 49 | option (google.api.method_signature) = "user,update_mask"; 50 | } 51 | // DeleteUser deletes a user. 52 | rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty) { 53 | option (google.api.http) = {delete: "/api/v1/{name=users/*}"}; 54 | option (google.api.method_signature) = "name"; 55 | } 56 | // GetUserSetting gets the setting of a user. 57 | rpc GetUserSetting(GetUserSettingRequest) returns (UserSetting) { 58 | option (google.api.http) = {get: "/api/v1/{name=users/*}/setting"}; 59 | option (google.api.method_signature) = "name"; 60 | } 61 | // UpdateUserSetting updates the setting of a user. 62 | rpc UpdateUserSetting(UpdateUserSettingRequest) returns (UserSetting) { 63 | option (google.api.http) = { 64 | patch: "/api/v1/{setting.name=users/*/setting}" 65 | body: "setting" 66 | }; 67 | option (google.api.method_signature) = "setting,update_mask"; 68 | } 69 | // ListUserAccessTokens returns a list of access tokens for a user. 70 | rpc ListUserAccessTokens(ListUserAccessTokensRequest) returns (ListUserAccessTokensResponse) { 71 | option (google.api.http) = {get: "/api/v1/{name=users/*}/access_tokens"}; 72 | option (google.api.method_signature) = "name"; 73 | } 74 | // CreateUserAccessToken creates a new access token for a user. 75 | rpc CreateUserAccessToken(CreateUserAccessTokenRequest) returns (UserAccessToken) { 76 | option (google.api.http) = { 77 | post: "/api/v1/{name=users/*}/access_tokens" 78 | body: "*" 79 | }; 80 | option (google.api.method_signature) = "name"; 81 | } 82 | // DeleteUserAccessToken deletes an access token for a user. 83 | rpc DeleteUserAccessToken(DeleteUserAccessTokenRequest) returns (google.protobuf.Empty) { 84 | option (google.api.http) = {delete: "/api/v1/{name=users/*}/access_tokens/{access_token}"}; 85 | option (google.api.method_signature) = "name,access_token"; 86 | } 87 | } 88 | 89 | message User { 90 | // The name of the user. 91 | // Format: users/{id} 92 | string name = 1; 93 | 94 | // The system generated uid of the user. 95 | int32 id = 2; 96 | 97 | enum Role { 98 | ROLE_UNSPECIFIED = 0; 99 | HOST = 1; 100 | ADMIN = 2; 101 | USER = 3; 102 | } 103 | Role role = 3; 104 | 105 | string username = 4; 106 | 107 | string email = 5; 108 | 109 | string nickname = 6; 110 | 111 | string avatar_url = 7; 112 | 113 | string description = 8; 114 | 115 | string password = 9 [(google.api.field_behavior) = INPUT_ONLY]; 116 | 117 | RowStatus row_status = 10; 118 | 119 | google.protobuf.Timestamp create_time = 11; 120 | 121 | google.protobuf.Timestamp update_time = 12; 122 | } 123 | 124 | message ListUsersRequest {} 125 | 126 | message ListUsersResponse { 127 | repeated User users = 1; 128 | } 129 | 130 | message SearchUsersRequest { 131 | // Filter is used to filter users returned in the list. 132 | // Format: "username == 'frank'" 133 | string filter = 1; 134 | } 135 | 136 | message SearchUsersResponse { 137 | repeated User users = 1; 138 | } 139 | 140 | message GetUserRequest { 141 | // The name of the user. 142 | // Format: users/{id} 143 | string name = 1; 144 | } 145 | 146 | message GetUserAvatarBinaryRequest { 147 | // The name of the user. 148 | // Format: users/{id} 149 | string name = 1; 150 | 151 | // The raw HTTP body is bound to this field. 152 | google.api.HttpBody http_body = 2; 153 | } 154 | 155 | message CreateUserRequest { 156 | User user = 1; 157 | } 158 | 159 | message UpdateUserRequest { 160 | User user = 1 [(google.api.field_behavior) = REQUIRED]; 161 | 162 | google.protobuf.FieldMask update_mask = 2; 163 | } 164 | 165 | message DeleteUserRequest { 166 | // The name of the user. 167 | // Format: users/{id} 168 | string name = 1; 169 | } 170 | 171 | message UserSetting { 172 | // The name of the user. 173 | // Format: users/{id} 174 | string name = 1; 175 | // The preferred locale of the user. 176 | string locale = 2; 177 | // The preferred appearance of the user. 178 | string appearance = 3; 179 | // The default visibility of the memo. 180 | string memo_visibility = 4; 181 | } 182 | 183 | message GetUserSettingRequest { 184 | // The name of the user. 185 | // Format: users/{id} 186 | string name = 1; 187 | } 188 | 189 | message UpdateUserSettingRequest { 190 | UserSetting setting = 1 [(google.api.field_behavior) = REQUIRED]; 191 | 192 | google.protobuf.FieldMask update_mask = 2; 193 | } 194 | 195 | message UserAccessToken { 196 | string access_token = 1; 197 | string description = 2; 198 | google.protobuf.Timestamp issued_at = 3; 199 | google.protobuf.Timestamp expires_at = 4; 200 | } 201 | 202 | message ListUserAccessTokensRequest { 203 | // The name of the user. 204 | // Format: users/{id} 205 | string name = 1; 206 | } 207 | 208 | message ListUserAccessTokensResponse { 209 | repeated UserAccessToken access_tokens = 1; 210 | } 211 | 212 | message CreateUserAccessTokenRequest { 213 | // The name of the user. 214 | // Format: users/{id} 215 | string name = 1; 216 | 217 | string description = 2; 218 | 219 | optional google.protobuf.Timestamp expires_at = 3; 220 | } 221 | 222 | message DeleteUserAccessTokenRequest { 223 | // The name of the user. 224 | // Format: users/{id} 225 | string name = 1; 226 | // access_token is the access token to delete. 227 | string access_token = 2; 228 | } 229 | -------------------------------------------------------------------------------- /src/mcp_server_memos/server.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import Annotated 3 | 4 | from grpclib.client import Channel 5 | from mcp import McpError, stdio_server, types 6 | from mcp.server import Server 7 | from pydantic import BaseModel, Field, TypeAdapter 8 | 9 | from .config import Config 10 | from .proto_gen.memos.api import v1 as memos_api_v1 11 | 12 | 13 | class MemosTools(str, Enum): 14 | LIST_MEMO_TAGS = "list_memo_tags" 15 | SEARCH_MEMO = "search_memo" 16 | CREATE_MEMO = "create_memo" 17 | GET_MEMO = "get_memo" 18 | 19 | 20 | class Visibility(str, Enum): 21 | PUBLIC = "PUBLIC" 22 | PROTECTED = "PROTECTED" 23 | PRIVATE = "PRIVATE" 24 | 25 | def to_proto(self): 26 | return memos_api_v1.Visibility.from_string(self.value) 27 | 28 | 29 | class SearchMemoRequest(BaseModel): 30 | """Request to search memo""" 31 | 32 | key_word: Annotated[ 33 | str, 34 | Field( 35 | description="""The key words to search for in the memo content.""", 36 | ), 37 | ] 38 | 39 | 40 | class CreateMemoRequest(BaseModel): 41 | """Request to create memo""" 42 | 43 | content: Annotated[ 44 | str, 45 | Field( 46 | description="""The content of the memo.""", 47 | ), 48 | ] 49 | visibility: Annotated[ 50 | Visibility, 51 | Field(default=Visibility.PRIVATE, description="""The visibility of the memo."""), 52 | ] 53 | 54 | 55 | class GetMemoRequest(BaseModel): 56 | """Request to get memo""" 57 | 58 | name: Annotated[ 59 | str, 60 | Field( 61 | description="""The name of the memo. 62 | Format: memos/{id} 63 | """ 64 | ), 65 | ] 66 | 67 | 68 | class ListMemoTagsRequest(BaseModel): 69 | """Request to list memo tags""" 70 | 71 | parent: Annotated[ 72 | str, 73 | Field( 74 | default="memos/-", 75 | description="""The parent, who owns the tags. 76 | Format: memos/{id}. Use "memos/-" to list all tags. 77 | """, 78 | ), 79 | ] 80 | visibility: Annotated[ 81 | Visibility, 82 | Field(default=Visibility.PRIVATE, description="""The visibility of the tags."""), 83 | ] 84 | 85 | 86 | class MemoServiceToolAdapter: 87 | memo_service: memos_api_v1.MemoServiceStub 88 | 89 | def __init__(self, config: Config): 90 | metadata = {"authorization": f"Bearer {config.token}"} 91 | channel = Channel(config.host, config.port) 92 | self.memo_service = memos_api_v1.MemoServiceStub(channel, metadata=metadata) 93 | 94 | # search 95 | async def search_memo(self, args: dict) -> list[types.TextContent]: 96 | try: 97 | params = SearchMemoRequest.model_validate(args) 98 | except Exception as e: 99 | raise McpError(types.INVALID_PARAMS, str(e)) 100 | 101 | req = memos_api_v1.ListMemosRequest( 102 | filter=f"row_status == 'NORMAL' && content_search == ['{params.key_word}']" 103 | ) 104 | res = await self.memo_service.list_memos(list_memos_request=req) 105 | content = ", ".join([memo.content for memo in res.memos]) 106 | content = f"Search result:\n{content}" 107 | return [types.TextContent(type="text", text=content)] 108 | 109 | # create 110 | async def create_memo(self, args: dict) -> list[types.TextContent]: 111 | try: 112 | params = CreateMemoRequest.model_validate(args) 113 | except Exception as e: 114 | raise McpError(types.INVALID_PARAMS, str(e)) 115 | 116 | req = memos_api_v1.CreateMemoRequest( 117 | content=params.content, 118 | visibility=params.visibility.to_proto(), 119 | ) 120 | res = await self.memo_service.create_memo(create_memo_request=req) 121 | content = f"Memo created: {res.name}" 122 | return [types.TextContent(type="text", text=content)] 123 | 124 | # get 125 | async def get_memo(self, args: dict) -> list[types.TextContent]: 126 | try: 127 | params = GetMemoRequest.model_validate(args) 128 | except Exception as e: 129 | raise McpError(types.INVALID_PARAMS, str(e)) 130 | 131 | req = memos_api_v1.GetMemoRequest(name=params.name) 132 | res = await self.memo_service.get_memo(get_memo_request=req) 133 | content = f"Memo:\n{res.content}" 134 | return [types.TextContent(type="text", text=content)] 135 | 136 | # list tags 137 | async def list_memo_tags(self, args: dict) -> list[types.TextContent]: 138 | try: 139 | params = ListMemoTagsRequest.model_validate(args) 140 | except Exception as e: 141 | raise McpError(types.INVALID_PARAMS, str(e)) 142 | 143 | req = memos_api_v1.ListMemoTagsRequest( 144 | parent=params.parent, 145 | filter=f"visibilities == ['{params.visibility.value}']", 146 | ) 147 | res = await self.memo_service.list_memo_tags(list_memo_tags_request=req) 148 | content = ", ".join(res.tag_amounts.keys()) 149 | content = f"Tags:\n{content}" 150 | return [types.TextContent(type="text", text=content)] 151 | 152 | 153 | def new_server(config: Config) -> Server: 154 | tool_adapter = MemoServiceToolAdapter(config) 155 | server = Server("mcp-server-memos") 156 | 157 | @server.list_tools() 158 | async def list_tools() -> list[types.Tool]: 159 | return [ 160 | types.Tool( 161 | name=MemosTools.SEARCH_MEMO, 162 | description="Search for memos", 163 | inputSchema=SearchMemoRequest.model_json_schema(), 164 | ), 165 | types.Tool( 166 | name=MemosTools.CREATE_MEMO, 167 | description="Create a new memo", 168 | inputSchema=CreateMemoRequest.model_json_schema(), 169 | ), 170 | types.Tool( 171 | name=MemosTools.GET_MEMO, 172 | description="Get a memo", 173 | inputSchema=GetMemoRequest.model_json_schema(), 174 | ), 175 | types.Tool( 176 | name=MemosTools.LIST_MEMO_TAGS, 177 | description="List all existing memo tags", 178 | inputSchema=ListMemoTagsRequest.model_json_schema(), 179 | ), 180 | ] 181 | 182 | # search 183 | @server.call_tool() 184 | async def call_tool(name: str, args: dict) -> list[types.TextContent]: 185 | if name == MemosTools.SEARCH_MEMO: 186 | return await tool_adapter.search_memo(args) 187 | elif name == MemosTools.CREATE_MEMO: 188 | return await tool_adapter.create_memo(args) 189 | elif name == MemosTools.GET_MEMO: 190 | return await tool_adapter.get_memo(args) 191 | elif name == MemosTools.LIST_MEMO_TAGS: 192 | return await tool_adapter.list_memo_tags(args) 193 | else: 194 | raise McpError(types.INVALID_PARAMS, f"Unknown tool: {name}") 195 | 196 | return server 197 | 198 | 199 | async def serve_stdio(config: Config): 200 | server = new_server(config) 201 | options = server.create_initialization_options() 202 | # print("serve_stdio, options:", options) 203 | async with stdio_server() as (read_stream, write_stream): 204 | await server.run(read_stream, write_stream, options, raise_exceptions=True) 205 | -------------------------------------------------------------------------------- /proto/api/v1/memo_service.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package memos.api.v1; 4 | 5 | import "api/v1/common.proto"; 6 | import "api/v1/markdown_service.proto"; 7 | import "api/v1/memo_relation_service.proto"; 8 | import "api/v1/reaction_service.proto"; 9 | import "api/v1/resource_service.proto"; 10 | import "google/api/annotations.proto"; 11 | import "google/api/client.proto"; 12 | import "google/api/field_behavior.proto"; 13 | import "google/protobuf/empty.proto"; 14 | import "google/protobuf/field_mask.proto"; 15 | import "google/protobuf/timestamp.proto"; 16 | 17 | option go_package = "gen/api/v1"; 18 | 19 | service MemoService { 20 | // CreateMemo creates a memo. 21 | rpc CreateMemo(CreateMemoRequest) returns (Memo) { 22 | option (google.api.http) = { 23 | post: "/api/v1/memos" 24 | body: "*" 25 | }; 26 | } 27 | // ListMemos lists memos with pagination and filter. 28 | rpc ListMemos(ListMemosRequest) returns (ListMemosResponse) { 29 | option (google.api.http) = {get: "/api/v1/memos"}; 30 | } 31 | // SearchMemos searches memos. 32 | rpc SearchMemos(SearchMemosRequest) returns (SearchMemosResponse) { 33 | option (google.api.http) = {get: "/api/v1/memos:search"}; 34 | } 35 | // GetMemo gets a memo. 36 | rpc GetMemo(GetMemoRequest) returns (Memo) { 37 | option (google.api.http) = {get: "/api/v1/{name=memos/*}"}; 38 | option (google.api.method_signature) = "name"; 39 | } 40 | // UpdateMemo updates a memo. 41 | rpc UpdateMemo(UpdateMemoRequest) returns (Memo) { 42 | option (google.api.http) = { 43 | patch: "/api/v1/{memo.name=memos/*}" 44 | body: "memo" 45 | }; 46 | option (google.api.method_signature) = "memo,update_mask"; 47 | } 48 | // DeleteMemo deletes a memo. 49 | rpc DeleteMemo(DeleteMemoRequest) returns (google.protobuf.Empty) { 50 | option (google.api.http) = {delete: "/api/v1/{name=memos/*}"}; 51 | option (google.api.method_signature) = "name"; 52 | } 53 | // ExportMemos exports memos. 54 | rpc ExportMemos(ExportMemosRequest) returns (ExportMemosResponse) { 55 | option (google.api.http) = { 56 | post: "/api/v1/memos:export", 57 | body: "*" 58 | }; 59 | } 60 | // ListMemoProperties lists memo properties. 61 | rpc ListMemoProperties(ListMemoPropertiesRequest) returns (ListMemoPropertiesResponse) { 62 | option (google.api.http) = {get: "/api/v1/{name=memos/*}/properties"}; 63 | } 64 | // RebuildMemoProperty rebuilds a memo property. 65 | rpc RebuildMemoProperty(RebuildMemoPropertyRequest) returns (google.protobuf.Empty) { 66 | option (google.api.http) = { 67 | post: "/api/v1/{name=memos/*}/properties:rebuild" 68 | body: "*" 69 | }; 70 | } 71 | // ListMemoTags lists tags for a memo. 72 | rpc ListMemoTags(ListMemoTagsRequest) returns (ListMemoTagsResponse) { 73 | option (google.api.http) = {get: "/api/v1/{parent=memos/*}/tags"}; 74 | } 75 | // RenameMemoTag renames a tag for a memo. 76 | rpc RenameMemoTag(RenameMemoTagRequest) returns (google.protobuf.Empty) { 77 | option (google.api.http) = { 78 | patch: "/api/v1/{parent=memos/*}/tags:rename" 79 | body: "*" 80 | }; 81 | } 82 | // DeleteMemoTag deletes a tag for a memo. 83 | rpc DeleteMemoTag(DeleteMemoTagRequest) returns (google.protobuf.Empty) { 84 | option (google.api.http) = {delete: "/api/v1/{parent=memos/*}/tags/{tag}"}; 85 | } 86 | // SetMemoResources sets resources for a memo. 87 | rpc SetMemoResources(SetMemoResourcesRequest) returns (google.protobuf.Empty) { 88 | option (google.api.http) = { 89 | patch: "/api/v1/{name=memos/*}/resources" 90 | body: "*" 91 | }; 92 | option (google.api.method_signature) = "name"; 93 | } 94 | // ListMemoResources lists resources for a memo. 95 | rpc ListMemoResources(ListMemoResourcesRequest) returns (ListMemoResourcesResponse) { 96 | option (google.api.http) = {get: "/api/v1/{name=memos/*}/resources"}; 97 | option (google.api.method_signature) = "name"; 98 | } 99 | // SetMemoRelations sets relations for a memo. 100 | rpc SetMemoRelations(SetMemoRelationsRequest) returns (google.protobuf.Empty) { 101 | option (google.api.http) = { 102 | patch: "/api/v1/{name=memos/*}/relations" 103 | body: "*" 104 | }; 105 | option (google.api.method_signature) = "name"; 106 | } 107 | // ListMemoRelations lists relations for a memo. 108 | rpc ListMemoRelations(ListMemoRelationsRequest) returns (ListMemoRelationsResponse) { 109 | option (google.api.http) = {get: "/api/v1/{name=memos/*}/relations"}; 110 | option (google.api.method_signature) = "name"; 111 | } 112 | // CreateMemoComment creates a comment for a memo. 113 | rpc CreateMemoComment(CreateMemoCommentRequest) returns (Memo) { 114 | option (google.api.http) = { 115 | post: "/api/v1/{name=memos/*}/comments", 116 | body: "comment" 117 | }; 118 | option (google.api.method_signature) = "name"; 119 | } 120 | // ListMemoComments lists comments for a memo. 121 | rpc ListMemoComments(ListMemoCommentsRequest) returns (ListMemoCommentsResponse) { 122 | option (google.api.http) = {get: "/api/v1/{name=memos/*}/comments"}; 123 | option (google.api.method_signature) = "name"; 124 | } 125 | // GetUserMemosStats gets stats of memos for a user. 126 | rpc GetUserMemosStats(GetUserMemosStatsRequest) returns (GetUserMemosStatsResponse) { 127 | option (google.api.http) = {get: "/api/v1/memos/stats"}; 128 | option (google.api.method_signature) = "username"; 129 | } 130 | // ListMemoReactions lists reactions for a memo. 131 | rpc ListMemoReactions(ListMemoReactionsRequest) returns (ListMemoReactionsResponse) { 132 | option (google.api.http) = {get: "/api/v1/{name=memos/*}/reactions"}; 133 | option (google.api.method_signature) = "name"; 134 | } 135 | // UpsertMemoReaction upserts a reaction for a memo. 136 | rpc UpsertMemoReaction(UpsertMemoReactionRequest) returns (Reaction) { 137 | option (google.api.http) = { 138 | post: "/api/v1/{name=memos/*}/reactions", 139 | body: "*" 140 | }; 141 | option (google.api.method_signature) = "name"; 142 | } 143 | // DeleteMemoReaction deletes a reaction for a memo. 144 | rpc DeleteMemoReaction(DeleteMemoReactionRequest) returns (google.protobuf.Empty) { 145 | option (google.api.http) = {delete: "/api/v1/reactions/{reaction_id}"}; 146 | option (google.api.method_signature) = "reaction_id"; 147 | } 148 | } 149 | 150 | enum Visibility { 151 | VISIBILITY_UNSPECIFIED = 0; 152 | PRIVATE = 1; 153 | PROTECTED = 2; 154 | PUBLIC = 3; 155 | } 156 | 157 | message Memo { 158 | // The name of the memo. 159 | // Format: memos/{id} 160 | // id is the system generated id. 161 | string name = 1; 162 | 163 | // The user defined id of the memo. 164 | string uid = 2; 165 | 166 | RowStatus row_status = 3; 167 | 168 | // The name of the creator. 169 | // Format: users/{id} 170 | string creator = 4; 171 | 172 | google.protobuf.Timestamp create_time = 5; 173 | 174 | google.protobuf.Timestamp update_time = 6; 175 | 176 | google.protobuf.Timestamp display_time = 7; 177 | 178 | string content = 8; 179 | 180 | repeated Node nodes = 9 [(google.api.field_behavior) = OUTPUT_ONLY]; 181 | 182 | Visibility visibility = 10; 183 | 184 | repeated string tags = 11; 185 | 186 | bool pinned = 12; 187 | 188 | optional int32 parent_id = 13 [ 189 | deprecated = true, 190 | (google.api.field_behavior) = OUTPUT_ONLY 191 | ]; 192 | 193 | repeated Resource resources = 14 [(google.api.field_behavior) = OUTPUT_ONLY]; 194 | 195 | repeated MemoRelation relations = 15 [(google.api.field_behavior) = OUTPUT_ONLY]; 196 | 197 | repeated Reaction reactions = 16 [(google.api.field_behavior) = OUTPUT_ONLY]; 198 | 199 | MemoProperty property = 17 [(google.api.field_behavior) = OUTPUT_ONLY]; 200 | 201 | // The name of the parent memo. 202 | // Format: memos/{id} 203 | optional string parent = 18 [(google.api.field_behavior) = OUTPUT_ONLY]; 204 | } 205 | 206 | message MemoProperty { 207 | repeated string tags = 1; 208 | bool has_link = 2; 209 | bool has_task_list = 3; 210 | bool has_code = 4; 211 | bool has_incomplete_tasks = 5; 212 | } 213 | 214 | message CreateMemoRequest { 215 | string content = 1; 216 | 217 | Visibility visibility = 2; 218 | } 219 | 220 | message ListMemosRequest { 221 | // The maximum number of memos to return. 222 | int32 page_size = 1; 223 | 224 | // A page token, received from a previous `ListMemos` call. 225 | // Provide this to retrieve the subsequent page. 226 | string page_token = 2; 227 | 228 | // Filter is used to filter memos returned in the list. 229 | // Format: "creator == 'users/{uid}' && visibilities == ['PUBLIC', 'PROTECTED']" 230 | string filter = 3; 231 | } 232 | 233 | message ListMemosResponse { 234 | repeated Memo memos = 1; 235 | 236 | // A token, which can be sent as `page_token` to retrieve the next page. 237 | // If this field is omitted, there are no subsequent pages. 238 | string next_page_token = 2; 239 | } 240 | 241 | message SearchMemosRequest { 242 | // Filter is used to filter memos returned. 243 | // Format: "creator == 'users/{uid}' && visibilities == ['PUBLIC', 'PROTECTED']" 244 | string filter = 1; 245 | } 246 | 247 | message SearchMemosResponse { 248 | repeated Memo memos = 1; 249 | } 250 | 251 | message GetMemoRequest { 252 | // The name of the memo. 253 | // Format: memos/{id} 254 | string name = 1; 255 | } 256 | 257 | message UpdateMemoRequest { 258 | Memo memo = 1; 259 | 260 | google.protobuf.FieldMask update_mask = 2; 261 | } 262 | 263 | message DeleteMemoRequest { 264 | // The name of the memo. 265 | // Format: memos/{id} 266 | string name = 1; 267 | } 268 | 269 | message ExportMemosRequest { 270 | // Same as ListMemosRequest.filter 271 | string filter = 1; 272 | } 273 | 274 | message ExportMemosResponse { 275 | bytes content = 1; 276 | } 277 | 278 | message ListMemoPropertiesRequest { 279 | // The name of the memo. 280 | // Format: memos/{id}. Use "memos/-" to list all properties. 281 | string name = 1; 282 | } 283 | 284 | message ListMemoPropertiesResponse { 285 | repeated MemoProperty properties = 1; 286 | } 287 | 288 | message RebuildMemoPropertyRequest { 289 | // The name of the memo. 290 | // Format: memos/{id}. Use "memos/-" to rebuild all memos. 291 | string name = 1; 292 | } 293 | 294 | message ListMemoTagsRequest { 295 | // The parent, who owns the tags. 296 | // Format: memos/{id}. Use "memos/-" to list all tags. 297 | string parent = 1; 298 | 299 | // Filter is used to filter memos. 300 | // Format: "creator == 'users/{uid}' && visibilities == ['PUBLIC', 'PROTECTED']" 301 | string filter = 2; 302 | } 303 | 304 | message ListMemoTagsResponse { 305 | // tag_amounts is the amount of tags. 306 | // key is the tag name. e.g. "tag1". 307 | // value is the amount of the tag. 308 | map tag_amounts = 1; 309 | } 310 | 311 | message RenameMemoTagRequest { 312 | // The parent, who owns the tags. 313 | // Format: memos/{id}. Use "memos/-" to rename all tags. 314 | string parent = 1; 315 | string old_tag = 2; 316 | string new_tag = 3; 317 | } 318 | 319 | message DeleteMemoTagRequest { 320 | // The parent, who owns the tags. 321 | // Format: memos/{id}. Use "memos/-" to delete all tags. 322 | string parent = 1; 323 | string tag = 2; 324 | bool delete_related_memos = 3; 325 | } 326 | 327 | message SetMemoResourcesRequest { 328 | // The name of the memo. 329 | // Format: memos/{id} 330 | string name = 1; 331 | 332 | repeated Resource resources = 2; 333 | } 334 | 335 | message ListMemoResourcesRequest { 336 | // The name of the memo. 337 | // Format: memos/{id} 338 | string name = 1; 339 | } 340 | 341 | message ListMemoResourcesResponse { 342 | repeated Resource resources = 1; 343 | } 344 | 345 | message SetMemoRelationsRequest { 346 | // The name of the memo. 347 | // Format: memos/{id} 348 | string name = 1; 349 | 350 | repeated MemoRelation relations = 2; 351 | } 352 | 353 | message ListMemoRelationsRequest { 354 | // The name of the memo. 355 | // Format: memos/{id} 356 | string name = 1; 357 | } 358 | 359 | message ListMemoRelationsResponse { 360 | repeated MemoRelation relations = 1; 361 | } 362 | 363 | message CreateMemoCommentRequest { 364 | // The name of the memo. 365 | // Format: memos/{id} 366 | string name = 1; 367 | 368 | CreateMemoRequest comment = 2; 369 | } 370 | 371 | message ListMemoCommentsRequest { 372 | // The name of the memo. 373 | // Format: memos/{id} 374 | string name = 1; 375 | } 376 | 377 | message ListMemoCommentsResponse { 378 | repeated Memo memos = 1; 379 | } 380 | 381 | message GetUserMemosStatsRequest { 382 | // name is the name of the user to get stats for. 383 | // Format: users/{id} 384 | string name = 1; 385 | 386 | // timezone location 387 | // Format: uses tz identifier 388 | // https://en.wikipedia.org/wiki/List_of_tz_database_time_zones 389 | string timezone = 2; 390 | 391 | // Same as ListMemosRequest.filter 392 | string filter = 3; 393 | } 394 | 395 | message GetUserMemosStatsResponse { 396 | // stats is the stats of memo creating/updating activities. 397 | // key is the year-month-day string. e.g. "2020-01-01". 398 | map stats = 1; 399 | } 400 | 401 | message ListMemoReactionsRequest { 402 | // The name of the memo. 403 | // Format: memos/{id} 404 | string name = 1; 405 | } 406 | 407 | message ListMemoReactionsResponse { 408 | repeated Reaction reactions = 1; 409 | } 410 | 411 | message UpsertMemoReactionRequest { 412 | // The name of the memo. 413 | // Format: memos/{id} 414 | string name = 1; 415 | 416 | Reaction reaction = 2; 417 | } 418 | 419 | message DeleteMemoReactionRequest { 420 | int32 reaction_id = 1; 421 | } 422 | -------------------------------------------------------------------------------- /src/mcp_server_memos/proto_gen/google/api/__init__.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # sources: google/api/annotations.proto, google/api/client.proto, google/api/field_behavior.proto, google/api/http.proto, google/api/httpbody.proto, google/api/launch_stage.proto 3 | # plugin: python-betterproto 4 | # This file has been @generated 5 | import warnings 6 | from dataclasses import dataclass 7 | from datetime import timedelta 8 | from typing import ( 9 | Dict, 10 | List, 11 | ) 12 | 13 | import betterproto 14 | import betterproto.lib.google.protobuf as betterproto_lib_google_protobuf 15 | 16 | 17 | class LaunchStage(betterproto.Enum): 18 | """ 19 | The launch stage as defined by [Google Cloud Platform 20 | Launch Stages](https://cloud.google.com/terms/launch-stages). 21 | """ 22 | 23 | UNSPECIFIED = 0 24 | """Do not use this default value.""" 25 | 26 | UNIMPLEMENTED = 6 27 | """The feature is not yet implemented. Users can not use it.""" 28 | 29 | PRELAUNCH = 7 30 | """ 31 | Prelaunch features are hidden from users and are only visible internally. 32 | """ 33 | 34 | EARLY_ACCESS = 1 35 | """ 36 | Early Access features are limited to a closed group of testers. To use 37 | these features, you must sign up in advance and sign a Trusted Tester 38 | agreement (which includes confidentiality provisions). These features may 39 | be unstable, changed in backward-incompatible ways, and are not 40 | guaranteed to be released. 41 | """ 42 | 43 | ALPHA = 2 44 | """ 45 | Alpha is a limited availability test for releases before they are cleared 46 | for widespread use. By Alpha, all significant design issues are resolved 47 | and we are in the process of verifying functionality. Alpha customers 48 | need to apply for access, agree to applicable terms, and have their 49 | projects allowlisted. Alpha releases don't have to be feature complete, 50 | no SLAs are provided, and there are no technical support obligations, but 51 | they will be far enough along that customers can actually use them in 52 | test environments or for limited-use tests -- just like they would in 53 | normal production cases. 54 | """ 55 | 56 | BETA = 3 57 | """ 58 | Beta is the point at which we are ready to open a release for any 59 | customer to use. There are no SLA or technical support obligations in a 60 | Beta release. Products will be complete from a feature perspective, but 61 | may have some open outstanding issues. Beta releases are suitable for 62 | limited production use cases. 63 | """ 64 | 65 | GA = 4 66 | """ 67 | GA features are open to all developers and are considered stable and 68 | fully qualified for production use. 69 | """ 70 | 71 | DEPRECATED = 5 72 | """ 73 | Deprecated features are scheduled to be shut down and removed. For more 74 | information, see the "Deprecation Policy" section of our [Terms of 75 | Service](https://cloud.google.com/terms/) 76 | and the [Google Cloud Platform Subject to the Deprecation 77 | Policy](https://cloud.google.com/terms/deprecation) documentation. 78 | """ 79 | 80 | 81 | class ClientLibraryOrganization(betterproto.Enum): 82 | """ 83 | The organization for which the client libraries are being published. 84 | Affects the url where generated docs are published, etc. 85 | """ 86 | 87 | UNSPECIFIED = 0 88 | """Not useful.""" 89 | 90 | CLOUD = 1 91 | """Google Cloud Platform Org.""" 92 | 93 | ADS = 2 94 | """Ads (Advertising) Org.""" 95 | 96 | PHOTOS = 3 97 | """Photos Org.""" 98 | 99 | STREET_VIEW = 4 100 | """Street View Org.""" 101 | 102 | SHOPPING = 5 103 | """Shopping Org.""" 104 | 105 | GEO = 6 106 | """Geo Org.""" 107 | 108 | GENERATIVE_AI = 7 109 | """Generative AI - https://developers.generativeai.google""" 110 | 111 | 112 | class ClientLibraryDestination(betterproto.Enum): 113 | """To where should client libraries be published?""" 114 | 115 | UNSPECIFIED = 0 116 | """ 117 | Client libraries will neither be generated nor published to package 118 | managers. 119 | """ 120 | 121 | GITHUB = 10 122 | """ 123 | Generate the client library in a repo under github.com/googleapis, 124 | but don't publish it to package managers. 125 | """ 126 | 127 | PACKAGE_MANAGER = 20 128 | """ 129 | Publish the library to package managers like nuget.org and npmjs.com. 130 | """ 131 | 132 | 133 | class FieldBehavior(betterproto.Enum): 134 | """ 135 | An indicator of the behavior of a given field (for example, that a field 136 | is required in requests, or given as output but ignored as input). 137 | This **does not** change the behavior in protocol buffers itself; it only 138 | denotes the behavior and may affect how API tooling handles the field. 139 | 140 | Note: This enum **may** receive new values in the future. 141 | """ 142 | 143 | UNSPECIFIED = 0 144 | """Conventional default for enums. Do not use this.""" 145 | 146 | OPTIONAL = 1 147 | """ 148 | Specifically denotes a field as optional. 149 | While all fields in protocol buffers are optional, this may be specified 150 | for emphasis if appropriate. 151 | """ 152 | 153 | REQUIRED = 2 154 | """ 155 | Denotes a field as required. 156 | This indicates that the field **must** be provided as part of the request, 157 | and failure to do so will cause an error (usually `INVALID_ARGUMENT`). 158 | """ 159 | 160 | OUTPUT_ONLY = 3 161 | """ 162 | Denotes a field as output only. 163 | This indicates that the field is provided in responses, but including the 164 | field in a request does nothing (the server *must* ignore it and 165 | *must not* throw an error as a result of the field's presence). 166 | """ 167 | 168 | INPUT_ONLY = 4 169 | """ 170 | Denotes a field as input only. 171 | This indicates that the field is provided in requests, and the 172 | corresponding field is not included in output. 173 | """ 174 | 175 | IMMUTABLE = 5 176 | """ 177 | Denotes a field as immutable. 178 | This indicates that the field may be set once in a request to create a 179 | resource, but may not be changed thereafter. 180 | """ 181 | 182 | UNORDERED_LIST = 6 183 | """ 184 | Denotes that a (repeated) field is an unordered list. 185 | This indicates that the service may provide the elements of the list 186 | in any arbitrary order, rather than the order the user originally 187 | provided. Additionally, the list's order may or may not be stable. 188 | """ 189 | 190 | NON_EMPTY_DEFAULT = 7 191 | """ 192 | Denotes that this field returns a non-empty default value if not set. 193 | This indicates that if the user provides the empty value in a request, 194 | a non-empty value will be returned. The user will not be aware of what 195 | non-empty value to expect. 196 | """ 197 | 198 | IDENTIFIER = 8 199 | """ 200 | Denotes that the field in a resource (a message annotated with 201 | google.api.resource) is used in the resource name to uniquely identify the 202 | resource. For AIP-compliant APIs, this should only be applied to the 203 | `name` field on the resource. 204 | 205 | This behavior should not be applied to references to other resources within 206 | the message. 207 | 208 | The identifier field of resources often have different field behavior 209 | depending on the request it is embedded in (e.g. for Create methods name 210 | is optional and unused, while for Update methods it is required). Instead 211 | of method-specific annotations, only `IDENTIFIER` is required. 212 | """ 213 | 214 | 215 | @dataclass(eq=False, repr=False) 216 | class Http(betterproto.Message): 217 | """ 218 | Defines the HTTP configuration for an API service. It contains a list of 219 | [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method 220 | to one or more HTTP REST API methods. 221 | """ 222 | 223 | rules: List["HttpRule"] = betterproto.message_field(1) 224 | """ 225 | A list of HTTP configuration rules that apply to individual API methods. 226 | 227 | **NOTE:** All service configuration rules follow "last one wins" order. 228 | """ 229 | 230 | fully_decode_reserved_expansion: bool = betterproto.bool_field(2) 231 | """ 232 | When set to true, URL path parameters will be fully URI-decoded except in 233 | cases of single segment matches in reserved expansion, where "%2F" will be 234 | left encoded. 235 | 236 | The default behavior is to not decode RFC 6570 reserved characters in multi 237 | segment matches. 238 | """ 239 | 240 | 241 | @dataclass(eq=False, repr=False) 242 | class HttpRule(betterproto.Message): 243 | """ 244 | gRPC Transcoding 245 | 246 | gRPC Transcoding is a feature for mapping between a gRPC method and one or 247 | more HTTP REST endpoints. It allows developers to build a single API service 248 | that supports both gRPC APIs and REST APIs. Many systems, including [Google 249 | APIs](https://github.com/googleapis/googleapis), 250 | [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC 251 | Gateway](https://github.com/grpc-ecosystem/grpc-gateway), 252 | and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature 253 | and use it for large scale production services. 254 | 255 | `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies 256 | how different portions of the gRPC request message are mapped to the URL 257 | path, URL query parameters, and HTTP request body. It also controls how the 258 | gRPC response message is mapped to the HTTP response body. `HttpRule` is 259 | typically specified as an `google.api.http` annotation on the gRPC method. 260 | 261 | Each mapping specifies a URL path template and an HTTP method. The path 262 | template may refer to one or more fields in the gRPC request message, as long 263 | as each field is a non-repeated field with a primitive (non-message) type. 264 | The path template controls how fields of the request message are mapped to 265 | the URL path. 266 | 267 | Example: 268 | 269 | service Messaging { 270 | rpc GetMessage(GetMessageRequest) returns (Message) { 271 | option (google.api.http) = { 272 | get: "/v1/{name=messages/*}" 273 | }; 274 | } 275 | } 276 | message GetMessageRequest { 277 | string name = 1; // Mapped to URL path. 278 | } 279 | message Message { 280 | string text = 1; // The resource content. 281 | } 282 | 283 | This enables an HTTP REST to gRPC mapping as below: 284 | 285 | - HTTP: `GET /v1/messages/123456` 286 | - gRPC: `GetMessage(name: "messages/123456")` 287 | 288 | Any fields in the request message which are not bound by the path template 289 | automatically become HTTP query parameters if there is no HTTP request body. 290 | For example: 291 | 292 | service Messaging { 293 | rpc GetMessage(GetMessageRequest) returns (Message) { 294 | option (google.api.http) = { 295 | get:"/v1/messages/{message_id}" 296 | }; 297 | } 298 | } 299 | message GetMessageRequest { 300 | message SubMessage { 301 | string subfield = 1; 302 | } 303 | string message_id = 1; // Mapped to URL path. 304 | int64 revision = 2; // Mapped to URL query parameter `revision`. 305 | SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. 306 | } 307 | 308 | This enables a HTTP JSON to RPC mapping as below: 309 | 310 | - HTTP: `GET /v1/messages/123456?revision=2&sub.subfield=foo` 311 | - gRPC: `GetMessage(message_id: "123456" revision: 2 sub: 312 | SubMessage(subfield: "foo"))` 313 | 314 | Note that fields which are mapped to URL query parameters must have a 315 | primitive type or a repeated primitive type or a non-repeated message type. 316 | In the case of a repeated type, the parameter can be repeated in the URL 317 | as `...?param=A¶m=B`. In the case of a message type, each field of the 318 | message is mapped to a separate parameter, such as 319 | `...?foo.a=A&foo.b=B&foo.c=C`. 320 | 321 | For HTTP methods that allow a request body, the `body` field 322 | specifies the mapping. Consider a REST update method on the 323 | message resource collection: 324 | 325 | service Messaging { 326 | rpc UpdateMessage(UpdateMessageRequest) returns (Message) { 327 | option (google.api.http) = { 328 | patch: "/v1/messages/{message_id}" 329 | body: "message" 330 | }; 331 | } 332 | } 333 | message UpdateMessageRequest { 334 | string message_id = 1; // mapped to the URL 335 | Message message = 2; // mapped to the body 336 | } 337 | 338 | The following HTTP JSON to RPC mapping is enabled, where the 339 | representation of the JSON in the request body is determined by 340 | protos JSON encoding: 341 | 342 | - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` 343 | - gRPC: `UpdateMessage(message_id: "123456" message { text: "Hi!" })` 344 | 345 | The special name `*` can be used in the body mapping to define that 346 | every field not bound by the path template should be mapped to the 347 | request body. This enables the following alternative definition of 348 | the update method: 349 | 350 | service Messaging { 351 | rpc UpdateMessage(Message) returns (Message) { 352 | option (google.api.http) = { 353 | patch: "/v1/messages/{message_id}" 354 | body: "*" 355 | }; 356 | } 357 | } 358 | message Message { 359 | string message_id = 1; 360 | string text = 2; 361 | } 362 | 363 | 364 | The following HTTP JSON to RPC mapping is enabled: 365 | 366 | - HTTP: `PATCH /v1/messages/123456 { "text": "Hi!" }` 367 | - gRPC: `UpdateMessage(message_id: "123456" text: "Hi!")` 368 | 369 | Note that when using `*` in the body mapping, it is not possible to 370 | have HTTP parameters, as all fields not bound by the path end in 371 | the body. This makes this option more rarely used in practice when 372 | defining REST APIs. The common usage of `*` is in custom methods 373 | which don't use the URL at all for transferring data. 374 | 375 | It is possible to define multiple HTTP methods for one RPC by using 376 | the `additional_bindings` option. Example: 377 | 378 | service Messaging { 379 | rpc GetMessage(GetMessageRequest) returns (Message) { 380 | option (google.api.http) = { 381 | get: "/v1/messages/{message_id}" 382 | additional_bindings { 383 | get: "/v1/users/{user_id}/messages/{message_id}" 384 | } 385 | }; 386 | } 387 | } 388 | message GetMessageRequest { 389 | string message_id = 1; 390 | string user_id = 2; 391 | } 392 | 393 | This enables the following two alternative HTTP JSON to RPC mappings: 394 | 395 | - HTTP: `GET /v1/messages/123456` 396 | - gRPC: `GetMessage(message_id: "123456")` 397 | 398 | - HTTP: `GET /v1/users/me/messages/123456` 399 | - gRPC: `GetMessage(user_id: "me" message_id: "123456")` 400 | 401 | Rules for HTTP mapping 402 | 403 | 1. Leaf request fields (recursive expansion nested messages in the request 404 | message) are classified into three categories: 405 | - Fields referred by the path template. They are passed via the URL path. 406 | - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They 407 | are passed via the HTTP 408 | request body. 409 | - All other fields are passed via the URL query parameters, and the 410 | parameter name is the field path in the request message. A repeated 411 | field can be represented as multiple query parameters under the same 412 | name. 413 | 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL 414 | query parameter, all fields 415 | are passed via URL path and HTTP request body. 416 | 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP 417 | request body, all 418 | fields are passed via URL path and URL query parameters. 419 | 420 | Path template syntax 421 | 422 | Template = "/" Segments [ Verb ] ; 423 | Segments = Segment { "/" Segment } ; 424 | Segment = "*" | "**" | LITERAL | Variable ; 425 | Variable = "{" FieldPath [ "=" Segments ] "}" ; 426 | FieldPath = IDENT { "." IDENT } ; 427 | Verb = ":" LITERAL ; 428 | 429 | The syntax `*` matches a single URL path segment. The syntax `**` matches 430 | zero or more URL path segments, which must be the last part of the URL path 431 | except the `Verb`. 432 | 433 | The syntax `Variable` matches part of the URL path as specified by its 434 | template. A variable template must not contain other variables. If a variable 435 | matches a single path segment, its template may be omitted, e.g. `{var}` 436 | is equivalent to `{var=*}`. 437 | 438 | The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` 439 | contains any reserved character, such characters should be percent-encoded 440 | before the matching. 441 | 442 | If a variable contains exactly one path segment, such as `"{var}"` or 443 | `"{var=*}"`, when such a variable is expanded into a URL path on the client 444 | side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The 445 | server side does the reverse decoding. Such variables show up in the 446 | [Discovery 447 | Document](https://developers.google.com/discovery/v1/reference/apis) as 448 | `{var}`. 449 | 450 | If a variable contains multiple path segments, such as `"{var=foo/*}"` 451 | or `"{var=**}"`, when such a variable is expanded into a URL path on the 452 | client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. 453 | The server side does the reverse decoding, except "%2F" and "%2f" are left 454 | unchanged. Such variables show up in the 455 | [Discovery 456 | Document](https://developers.google.com/discovery/v1/reference/apis) as 457 | `{+var}`. 458 | 459 | Using gRPC API Service Configuration 460 | 461 | gRPC API Service Configuration (service config) is a configuration language 462 | for configuring a gRPC service to become a user-facing product. The 463 | service config is simply the YAML representation of the `google.api.Service` 464 | proto message. 465 | 466 | As an alternative to annotating your proto file, you can configure gRPC 467 | transcoding in your service config YAML files. You do this by specifying a 468 | `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same 469 | effect as the proto annotation. This can be particularly useful if you 470 | have a proto that is reused in multiple services. Note that any transcoding 471 | specified in the service config will override any matching transcoding 472 | configuration in the proto. 473 | 474 | The following example selects a gRPC method and applies an `HttpRule` to it: 475 | 476 | http: 477 | rules: 478 | - selector: example.v1.Messaging.GetMessage 479 | get: /v1/messages/{message_id}/{sub.subfield} 480 | 481 | Special notes 482 | 483 | When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the 484 | proto to JSON conversion must follow the [proto3 485 | specification](https://developers.google.com/protocol-buffers/docs/proto3#json). 486 | 487 | While the single segment variable follows the semantics of 488 | [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String 489 | Expansion, the multi segment variable **does not** follow RFC 6570 Section 490 | 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion 491 | does not expand special characters like `?` and `#`, which would lead 492 | to invalid URLs. As the result, gRPC Transcoding uses a custom encoding 493 | for multi segment variables. 494 | 495 | The path variables **must not** refer to any repeated or mapped field, 496 | because client libraries are not capable of handling such variable expansion. 497 | 498 | The path variables **must not** capture the leading "/" character. The reason 499 | is that the most common use case "{var}" does not capture the leading "/" 500 | character. For consistency, all path variables must share the same behavior. 501 | 502 | Repeated message fields must not be mapped to URL query parameters, because 503 | no client library can support such complicated mapping. 504 | 505 | If an API needs to use a JSON array for request or response body, it can map 506 | the request or response body to a repeated field. However, some gRPC 507 | Transcoding implementations may not support this feature. 508 | """ 509 | 510 | selector: str = betterproto.string_field(1) 511 | """ 512 | Selects a method to which this rule applies. 513 | 514 | Refer to [selector][google.api.DocumentationRule.selector] for syntax 515 | details. 516 | """ 517 | 518 | get: str = betterproto.string_field(2, group="pattern") 519 | """ 520 | Maps to HTTP GET. Used for listing and getting information about 521 | resources. 522 | """ 523 | 524 | put: str = betterproto.string_field(3, group="pattern") 525 | """Maps to HTTP PUT. Used for replacing a resource.""" 526 | 527 | post: str = betterproto.string_field(4, group="pattern") 528 | """ 529 | Maps to HTTP POST. Used for creating a resource or performing an action. 530 | """ 531 | 532 | delete: str = betterproto.string_field(5, group="pattern") 533 | """Maps to HTTP DELETE. Used for deleting a resource.""" 534 | 535 | patch: str = betterproto.string_field(6, group="pattern") 536 | """Maps to HTTP PATCH. Used for updating a resource.""" 537 | 538 | custom: "CustomHttpPattern" = betterproto.message_field(8, group="pattern") 539 | """ 540 | The custom pattern is used for specifying an HTTP method that is not 541 | included in the `pattern` field, such as HEAD, or "*" to leave the 542 | HTTP method unspecified for this rule. The wild-card rule is useful 543 | for services that provide content to Web (HTML) clients. 544 | """ 545 | 546 | body: str = betterproto.string_field(7) 547 | """ 548 | The name of the request field whose value is mapped to the HTTP request 549 | body, or `*` for mapping all request fields not captured by the path 550 | pattern to the HTTP body, or omitted for not having any HTTP request body. 551 | 552 | NOTE: the referred field must be present at the top-level of the request 553 | message type. 554 | """ 555 | 556 | response_body: str = betterproto.string_field(12) 557 | """ 558 | Optional. The name of the response field whose value is mapped to the HTTP 559 | response body. When omitted, the entire response message will be used 560 | as the HTTP response body. 561 | 562 | NOTE: The referred field must be present at the top-level of the response 563 | message type. 564 | """ 565 | 566 | additional_bindings: List["HttpRule"] = betterproto.message_field(11) 567 | """ 568 | Additional HTTP bindings for the selector. Nested bindings must 569 | not contain an `additional_bindings` field themselves (that is, 570 | the nesting may only be one level deep). 571 | """ 572 | 573 | 574 | @dataclass(eq=False, repr=False) 575 | class CustomHttpPattern(betterproto.Message): 576 | """A custom pattern is used for defining custom HTTP verb.""" 577 | 578 | kind: str = betterproto.string_field(1) 579 | """The name of this custom HTTP verb.""" 580 | 581 | path: str = betterproto.string_field(2) 582 | """The path matched by this custom verb.""" 583 | 584 | 585 | @dataclass(eq=False, repr=False) 586 | class CommonLanguageSettings(betterproto.Message): 587 | """Required information for every language.""" 588 | 589 | reference_docs_uri: str = betterproto.string_field(1) 590 | """ 591 | Link to automatically generated reference documentation. Example: 592 | https://cloud.google.com/nodejs/docs/reference/asset/latest 593 | """ 594 | 595 | destinations: List["ClientLibraryDestination"] = betterproto.enum_field(2) 596 | """ 597 | The destination where API teams want this client library to be published. 598 | """ 599 | 600 | selective_gapic_generation: "SelectiveGapicGeneration" = betterproto.message_field( 601 | 3 602 | ) 603 | """ 604 | Configuration for which RPCs should be generated in the GAPIC client. 605 | """ 606 | 607 | def __post_init__(self) -> None: 608 | super().__post_init__() 609 | if self.is_set("reference_docs_uri"): 610 | warnings.warn( 611 | "CommonLanguageSettings.reference_docs_uri is deprecated", 612 | DeprecationWarning, 613 | ) 614 | 615 | 616 | @dataclass(eq=False, repr=False) 617 | class ClientLibrarySettings(betterproto.Message): 618 | """Details about how and where to publish client libraries.""" 619 | 620 | version: str = betterproto.string_field(1) 621 | """ 622 | Version of the API to apply these settings to. This is the full protobuf 623 | package for the API, ending in the version element. 624 | Examples: "google.cloud.speech.v1" and "google.spanner.admin.database.v1". 625 | """ 626 | 627 | launch_stage: "LaunchStage" = betterproto.enum_field(2) 628 | """Launch stage of this version of the API.""" 629 | 630 | rest_numeric_enums: bool = betterproto.bool_field(3) 631 | """ 632 | When using transport=rest, the client request will encode enums as 633 | numbers rather than strings. 634 | """ 635 | 636 | java_settings: "JavaSettings" = betterproto.message_field(21) 637 | """Settings for legacy Java features, supported in the Service YAML.""" 638 | 639 | cpp_settings: "CppSettings" = betterproto.message_field(22) 640 | """Settings for C++ client libraries.""" 641 | 642 | php_settings: "PhpSettings" = betterproto.message_field(23) 643 | """Settings for PHP client libraries.""" 644 | 645 | python_settings: "PythonSettings" = betterproto.message_field(24) 646 | """Settings for Python client libraries.""" 647 | 648 | node_settings: "NodeSettings" = betterproto.message_field(25) 649 | """Settings for Node client libraries.""" 650 | 651 | dotnet_settings: "DotnetSettings" = betterproto.message_field(26) 652 | """Settings for .NET client libraries.""" 653 | 654 | ruby_settings: "RubySettings" = betterproto.message_field(27) 655 | """Settings for Ruby client libraries.""" 656 | 657 | go_settings: "GoSettings" = betterproto.message_field(28) 658 | """Settings for Go client libraries.""" 659 | 660 | 661 | @dataclass(eq=False, repr=False) 662 | class Publishing(betterproto.Message): 663 | """ 664 | This message configures the settings for publishing [Google Cloud Client 665 | libraries](https://cloud.google.com/apis/docs/cloud-client-libraries) 666 | generated from the service config. 667 | """ 668 | 669 | method_settings: List["MethodSettings"] = betterproto.message_field(2) 670 | """ 671 | A list of API method settings, e.g. the behavior for methods that use the 672 | long-running operation pattern. 673 | """ 674 | 675 | new_issue_uri: str = betterproto.string_field(101) 676 | """ 677 | Link to a *public* URI where users can report issues. Example: 678 | https://issuetracker.google.com/issues/new?component=190865&template=1161103 679 | """ 680 | 681 | documentation_uri: str = betterproto.string_field(102) 682 | """ 683 | Link to product home page. Example: 684 | https://cloud.google.com/asset-inventory/docs/overview 685 | """ 686 | 687 | api_short_name: str = betterproto.string_field(103) 688 | """ 689 | Used as a tracking tag when collecting data about the APIs developer 690 | relations artifacts like docs, packages delivered to package managers, 691 | etc. Example: "speech". 692 | """ 693 | 694 | github_label: str = betterproto.string_field(104) 695 | """ 696 | GitHub label to apply to issues and pull requests opened for this API. 697 | """ 698 | 699 | codeowner_github_teams: List[str] = betterproto.string_field(105) 700 | """ 701 | GitHub teams to be added to CODEOWNERS in the directory in GitHub 702 | containing source code for the client libraries for this API. 703 | """ 704 | 705 | doc_tag_prefix: str = betterproto.string_field(106) 706 | """ 707 | A prefix used in sample code when demarking regions to be included in 708 | documentation. 709 | """ 710 | 711 | organization: "ClientLibraryOrganization" = betterproto.enum_field(107) 712 | """For whom the client library is being published.""" 713 | 714 | library_settings: List["ClientLibrarySettings"] = betterproto.message_field(109) 715 | """ 716 | Client library settings. If the same version string appears multiple 717 | times in this list, then the last one wins. Settings from earlier 718 | settings with the same version string are discarded. 719 | """ 720 | 721 | proto_reference_documentation_uri: str = betterproto.string_field(110) 722 | """ 723 | Optional link to proto reference documentation. Example: 724 | https://cloud.google.com/pubsub/lite/docs/reference/rpc 725 | """ 726 | 727 | rest_reference_documentation_uri: str = betterproto.string_field(111) 728 | """ 729 | Optional link to REST reference documentation. Example: 730 | https://cloud.google.com/pubsub/lite/docs/reference/rest 731 | """ 732 | 733 | 734 | @dataclass(eq=False, repr=False) 735 | class JavaSettings(betterproto.Message): 736 | """Settings for Java client libraries.""" 737 | 738 | library_package: str = betterproto.string_field(1) 739 | """ 740 | The package name to use in Java. Clobbers the java_package option 741 | set in the protobuf. This should be used **only** by APIs 742 | who have already set the language_settings.java.package_name" field 743 | in gapic.yaml. API teams should use the protobuf java_package option 744 | where possible. 745 | 746 | Example of a YAML configuration:: 747 | 748 | publishing: 749 | java_settings: 750 | library_package: com.google.cloud.pubsub.v1 751 | """ 752 | 753 | service_class_names: Dict[str, str] = betterproto.map_field( 754 | 2, betterproto.TYPE_STRING, betterproto.TYPE_STRING 755 | ) 756 | """ 757 | Configure the Java class name to use instead of the service's for its 758 | corresponding generated GAPIC client. Keys are fully-qualified 759 | service names as they appear in the protobuf (including the full 760 | the language_settings.java.interface_names" field in gapic.yaml. API 761 | teams should otherwise use the service name as it appears in the 762 | protobuf. 763 | 764 | Example of a YAML configuration:: 765 | 766 | publishing: 767 | java_settings: 768 | service_class_names: 769 | - google.pubsub.v1.Publisher: TopicAdmin 770 | - google.pubsub.v1.Subscriber: SubscriptionAdmin 771 | """ 772 | 773 | common: "CommonLanguageSettings" = betterproto.message_field(3) 774 | """Some settings.""" 775 | 776 | 777 | @dataclass(eq=False, repr=False) 778 | class CppSettings(betterproto.Message): 779 | """Settings for C++ client libraries.""" 780 | 781 | common: "CommonLanguageSettings" = betterproto.message_field(1) 782 | """Some settings.""" 783 | 784 | 785 | @dataclass(eq=False, repr=False) 786 | class PhpSettings(betterproto.Message): 787 | """Settings for Php client libraries.""" 788 | 789 | common: "CommonLanguageSettings" = betterproto.message_field(1) 790 | """Some settings.""" 791 | 792 | 793 | @dataclass(eq=False, repr=False) 794 | class PythonSettings(betterproto.Message): 795 | """Settings for Python client libraries.""" 796 | 797 | common: "CommonLanguageSettings" = betterproto.message_field(1) 798 | """Some settings.""" 799 | 800 | experimental_features: "PythonSettingsExperimentalFeatures" = ( 801 | betterproto.message_field(2) 802 | ) 803 | """ 804 | Experimental features to be included during client library generation. 805 | """ 806 | 807 | 808 | @dataclass(eq=False, repr=False) 809 | class PythonSettingsExperimentalFeatures(betterproto.Message): 810 | """ 811 | Experimental features to be included during client library generation. 812 | These fields will be deprecated once the feature graduates and is enabled 813 | by default. 814 | """ 815 | 816 | rest_async_io_enabled: bool = betterproto.bool_field(1) 817 | """ 818 | Enables generation of asynchronous REST clients if `rest` transport is 819 | enabled. By default, asynchronous REST clients will not be generated. 820 | This feature will be enabled by default 1 month after launching the 821 | feature in preview packages. 822 | """ 823 | 824 | protobuf_pythonic_types_enabled: bool = betterproto.bool_field(2) 825 | """ 826 | Enables generation of protobuf code using new types that are more 827 | Pythonic which are included in `protobuf>=5.29.x`. This feature will be 828 | enabled by default 1 month after launching the feature in preview 829 | packages. 830 | """ 831 | 832 | 833 | @dataclass(eq=False, repr=False) 834 | class NodeSettings(betterproto.Message): 835 | """Settings for Node client libraries.""" 836 | 837 | common: "CommonLanguageSettings" = betterproto.message_field(1) 838 | """Some settings.""" 839 | 840 | 841 | @dataclass(eq=False, repr=False) 842 | class DotnetSettings(betterproto.Message): 843 | """Settings for Dotnet client libraries.""" 844 | 845 | common: "CommonLanguageSettings" = betterproto.message_field(1) 846 | """Some settings.""" 847 | 848 | renamed_services: Dict[str, str] = betterproto.map_field( 849 | 2, betterproto.TYPE_STRING, betterproto.TYPE_STRING 850 | ) 851 | """ 852 | Map from original service names to renamed versions. 853 | This is used when the default generated types 854 | would cause a naming conflict. (Neither name is 855 | fully-qualified.) 856 | Example: Subscriber to SubscriberServiceApi. 857 | """ 858 | 859 | renamed_resources: Dict[str, str] = betterproto.map_field( 860 | 3, betterproto.TYPE_STRING, betterproto.TYPE_STRING 861 | ) 862 | """ 863 | Map from full resource types to the effective short name 864 | for the resource. This is used when otherwise resource 865 | named from different services would cause naming collisions. 866 | Example entry: 867 | "datalabeling.googleapis.com/Dataset": "DataLabelingDataset" 868 | """ 869 | 870 | ignored_resources: List[str] = betterproto.string_field(4) 871 | """ 872 | List of full resource types to ignore during generation. 873 | This is typically used for API-specific Location resources, 874 | which should be handled by the generator as if they were actually 875 | the common Location resources. 876 | Example entry: "documentai.googleapis.com/Location" 877 | """ 878 | 879 | forced_namespace_aliases: List[str] = betterproto.string_field(5) 880 | """ 881 | Namespaces which must be aliased in snippets due to 882 | a known (but non-generator-predictable) naming collision 883 | """ 884 | 885 | handwritten_signatures: List[str] = betterproto.string_field(6) 886 | """ 887 | Method signatures (in the form "service.method(signature)") 888 | which are provided separately, so shouldn't be generated. 889 | Snippets *calling* these methods are still generated, however. 890 | """ 891 | 892 | 893 | @dataclass(eq=False, repr=False) 894 | class RubySettings(betterproto.Message): 895 | """Settings for Ruby client libraries.""" 896 | 897 | common: "CommonLanguageSettings" = betterproto.message_field(1) 898 | """Some settings.""" 899 | 900 | 901 | @dataclass(eq=False, repr=False) 902 | class GoSettings(betterproto.Message): 903 | """Settings for Go client libraries.""" 904 | 905 | common: "CommonLanguageSettings" = betterproto.message_field(1) 906 | """Some settings.""" 907 | 908 | renamed_services: Dict[str, str] = betterproto.map_field( 909 | 2, betterproto.TYPE_STRING, betterproto.TYPE_STRING 910 | ) 911 | """ 912 | Map of service names to renamed services. Keys are the package relative 913 | service names and values are the name to be used for the service client 914 | and call options. 915 | 916 | publishing: 917 | go_settings: 918 | renamed_services: 919 | Publisher: TopicAdmin 920 | """ 921 | 922 | 923 | @dataclass(eq=False, repr=False) 924 | class MethodSettings(betterproto.Message): 925 | """Describes the generator configuration for a method.""" 926 | 927 | selector: str = betterproto.string_field(1) 928 | """ 929 | The fully qualified name of the method, for which the options below apply. 930 | This is used to find the method to apply the options. 931 | 932 | Example: 933 | 934 | publishing: 935 | method_settings: 936 | - selector: google.storage.control.v2.StorageControl.CreateFolder 937 | # method settings for CreateFolder... 938 | """ 939 | 940 | long_running: "MethodSettingsLongRunning" = betterproto.message_field(2) 941 | """ 942 | Describes settings to use for long-running operations when generating 943 | API methods for RPCs. Complements RPCs that use the annotations in 944 | google/longrunning/operations.proto. 945 | 946 | Example of a YAML configuration:: 947 | 948 | publishing: 949 | method_settings: 950 | - selector: google.cloud.speech.v2.Speech.BatchRecognize 951 | long_running: 952 | initial_poll_delay: 60s # 1 minute 953 | poll_delay_multiplier: 1.5 954 | max_poll_delay: 360s # 6 minutes 955 | total_poll_timeout: 54000s # 90 minutes 956 | """ 957 | 958 | auto_populated_fields: List[str] = betterproto.string_field(3) 959 | """ 960 | List of top-level fields of the request message, that should be 961 | automatically populated by the client libraries based on their 962 | (google.api.field_info).format. Currently supported format: UUID4. 963 | 964 | Example of a YAML configuration: 965 | 966 | publishing: 967 | method_settings: 968 | - selector: google.example.v1.ExampleService.CreateExample 969 | auto_populated_fields: 970 | - request_id 971 | """ 972 | 973 | 974 | @dataclass(eq=False, repr=False) 975 | class MethodSettingsLongRunning(betterproto.Message): 976 | """ 977 | Describes settings to use when generating API methods that use the 978 | long-running operation pattern. 979 | All default values below are from those used in the client library 980 | generators (e.g. 981 | [Java](https://github.com/googleapis/gapic-generator-java/blob/04c2faa191a9b5a10b92392fe8482279c4404803/src/main/java/com/google/api/generator/gapic/composer/common/RetrySettingsComposer.java)). 982 | """ 983 | 984 | initial_poll_delay: timedelta = betterproto.message_field(1) 985 | """ 986 | Initial delay after which the first poll request will be made. 987 | Default value: 5 seconds. 988 | """ 989 | 990 | poll_delay_multiplier: float = betterproto.float_field(2) 991 | """ 992 | Multiplier to gradually increase delay between subsequent polls until it 993 | reaches max_poll_delay. 994 | Default value: 1.5. 995 | """ 996 | 997 | max_poll_delay: timedelta = betterproto.message_field(3) 998 | """ 999 | Maximum time between two subsequent poll requests. 1000 | Default value: 45 seconds. 1001 | """ 1002 | 1003 | total_poll_timeout: timedelta = betterproto.message_field(4) 1004 | """ 1005 | Total polling timeout. 1006 | Default value: 5 minutes. 1007 | """ 1008 | 1009 | 1010 | @dataclass(eq=False, repr=False) 1011 | class SelectiveGapicGeneration(betterproto.Message): 1012 | """ 1013 | This message is used to configure the generation of a subset of the RPCs in 1014 | a service for client libraries. 1015 | """ 1016 | 1017 | methods: List[str] = betterproto.string_field(1) 1018 | """ 1019 | An allowlist of the fully qualified names of RPCs that should be included 1020 | on public client surfaces. 1021 | """ 1022 | 1023 | 1024 | @dataclass(eq=False, repr=False) 1025 | class HttpBody(betterproto.Message): 1026 | """ 1027 | Message that represents an arbitrary HTTP body. It should only be used for 1028 | payload formats that can't be represented as JSON, such as raw binary or 1029 | an HTML page. 1030 | 1031 | 1032 | This message can be used both in streaming and non-streaming API methods in 1033 | the request as well as the response. 1034 | 1035 | It can be used as a top-level request field, which is convenient if one 1036 | wants to extract parameters from either the URL or HTTP template into the 1037 | request fields and also want access to the raw HTTP body. 1038 | 1039 | Example: 1040 | 1041 | message GetResourceRequest { 1042 | // A unique request id. 1043 | string request_id = 1; 1044 | 1045 | // The raw HTTP body is bound to this field. 1046 | google.api.HttpBody http_body = 2; 1047 | 1048 | } 1049 | 1050 | service ResourceService { 1051 | rpc GetResource(GetResourceRequest) 1052 | returns (google.api.HttpBody); 1053 | rpc UpdateResource(google.api.HttpBody) 1054 | returns (google.protobuf.Empty); 1055 | 1056 | } 1057 | 1058 | Example with streaming methods: 1059 | 1060 | service CaldavService { 1061 | rpc GetCalendar(stream google.api.HttpBody) 1062 | returns (stream google.api.HttpBody); 1063 | rpc UpdateCalendar(stream google.api.HttpBody) 1064 | returns (stream google.api.HttpBody); 1065 | 1066 | } 1067 | 1068 | Use of this type only changes how the request and response bodies are 1069 | handled, all other features will continue to work unchanged. 1070 | """ 1071 | 1072 | content_type: str = betterproto.string_field(1) 1073 | """ 1074 | The HTTP Content-Type header value specifying the content type of the body. 1075 | """ 1076 | 1077 | data: bytes = betterproto.bytes_field(2) 1078 | """The HTTP request/response body as raw binary.""" 1079 | 1080 | extensions: List["betterproto_lib_google_protobuf.Any"] = betterproto.message_field( 1081 | 3 1082 | ) 1083 | """ 1084 | Application specific response metadata. Must be set in the first response 1085 | for streaming APIs. 1086 | """ 1087 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.12" 3 | 4 | [[package]] 5 | name = "annotated-types" 6 | version = "0.7.0" 7 | source = { registry = "https://pypi.org/simple" } 8 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 9 | wheels = [ 10 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 11 | ] 12 | 13 | [[package]] 14 | name = "anyio" 15 | version = "4.7.0" 16 | source = { registry = "https://pypi.org/simple" } 17 | dependencies = [ 18 | { name = "idna" }, 19 | { name = "sniffio" }, 20 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 21 | ] 22 | sdist = { url = "https://files.pythonhosted.org/packages/f6/40/318e58f669b1a9e00f5c4453910682e2d9dd594334539c7b7817dabb765f/anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", size = 177076 } 23 | wheels = [ 24 | { url = "https://files.pythonhosted.org/packages/a0/7a/4daaf3b6c08ad7ceffea4634ec206faeff697526421c20f07628c7372156/anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352", size = 93052 }, 25 | ] 26 | 27 | [[package]] 28 | name = "betterproto" 29 | version = "2.0.0b7" 30 | source = { registry = "https://pypi.org/simple" } 31 | dependencies = [ 32 | { name = "grpclib" }, 33 | { name = "python-dateutil" }, 34 | { name = "typing-extensions" }, 35 | ] 36 | sdist = { url = "https://files.pythonhosted.org/packages/4e/94/930a1368fbed92adc897a9a1fae282e3f9d608547dbf805034ca549f381a/betterproto-2.0.0b7.tar.gz", hash = "sha256:1b1458ca5278d519bcd62556a4c236f998a91d503f0f71c67b0b954747052af2", size = 101447 } 37 | wheels = [ 38 | { url = "https://files.pythonhosted.org/packages/a7/9a/5db824c378f6f88b5c6366429f2f5169a10315e9e3d5ab754c4a8d1202aa/betterproto-2.0.0b7-py3-none-any.whl", hash = "sha256:401ab8055e2f814e77b9c88a74d0e1ae3d1e8a969cced6aeb1b59f71ad63fbd2", size = 105386 }, 39 | ] 40 | 41 | [package.optional-dependencies] 42 | compiler = [ 43 | { name = "black" }, 44 | { name = "isort" }, 45 | { name = "jinja2" }, 46 | ] 47 | 48 | [[package]] 49 | name = "black" 50 | version = "24.10.0" 51 | source = { registry = "https://pypi.org/simple" } 52 | dependencies = [ 53 | { name = "click" }, 54 | { name = "mypy-extensions" }, 55 | { name = "packaging" }, 56 | { name = "pathspec" }, 57 | { name = "platformdirs" }, 58 | ] 59 | sdist = { url = "https://files.pythonhosted.org/packages/d8/0d/cc2fb42b8c50d80143221515dd7e4766995bd07c56c9a3ed30baf080b6dc/black-24.10.0.tar.gz", hash = "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", size = 645813 } 60 | wheels = [ 61 | { url = "https://files.pythonhosted.org/packages/90/04/bf74c71f592bcd761610bbf67e23e6a3cff824780761f536512437f1e655/black-24.10.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", size = 1644256 }, 62 | { url = "https://files.pythonhosted.org/packages/4c/ea/a77bab4cf1887f4b2e0bce5516ea0b3ff7d04ba96af21d65024629afedb6/black-24.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", size = 1448534 }, 63 | { url = "https://files.pythonhosted.org/packages/4e/3e/443ef8bc1fbda78e61f79157f303893f3fddf19ca3c8989b163eb3469a12/black-24.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", size = 1761892 }, 64 | { url = "https://files.pythonhosted.org/packages/52/93/eac95ff229049a6901bc84fec6908a5124b8a0b7c26ea766b3b8a5debd22/black-24.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", size = 1434796 }, 65 | { url = "https://files.pythonhosted.org/packages/d0/a0/a993f58d4ecfba035e61fca4e9f64a2ecae838fc9f33ab798c62173ed75c/black-24.10.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", size = 1643986 }, 66 | { url = "https://files.pythonhosted.org/packages/37/d5/602d0ef5dfcace3fb4f79c436762f130abd9ee8d950fa2abdbf8bbc555e0/black-24.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", size = 1448085 }, 67 | { url = "https://files.pythonhosted.org/packages/47/6d/a3a239e938960df1a662b93d6230d4f3e9b4a22982d060fc38c42f45a56b/black-24.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", size = 1760928 }, 68 | { url = "https://files.pythonhosted.org/packages/dd/cf/af018e13b0eddfb434df4d9cd1b2b7892bab119f7a20123e93f6910982e8/black-24.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", size = 1436875 }, 69 | { url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898 }, 70 | ] 71 | 72 | [[package]] 73 | name = "certifi" 74 | version = "2024.8.30" 75 | source = { registry = "https://pypi.org/simple" } 76 | sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } 77 | wheels = [ 78 | { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, 79 | ] 80 | 81 | [[package]] 82 | name = "click" 83 | version = "8.1.7" 84 | source = { registry = "https://pypi.org/simple" } 85 | dependencies = [ 86 | { name = "colorama", marker = "platform_system == 'Windows'" }, 87 | ] 88 | sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } 89 | wheels = [ 90 | { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, 91 | ] 92 | 93 | [[package]] 94 | name = "colorama" 95 | version = "0.4.6" 96 | source = { registry = "https://pypi.org/simple" } 97 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 98 | wheels = [ 99 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 100 | ] 101 | 102 | [[package]] 103 | name = "grpclib" 104 | version = "0.4.7" 105 | source = { registry = "https://pypi.org/simple" } 106 | dependencies = [ 107 | { name = "h2" }, 108 | { name = "multidict" }, 109 | ] 110 | sdist = { url = "https://files.pythonhosted.org/packages/79/b9/55936e462a5925190d7427e880b3033601d1effd13809b483d13a926061a/grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3", size = 61254 } 111 | 112 | [[package]] 113 | name = "h11" 114 | version = "0.14.0" 115 | source = { registry = "https://pypi.org/simple" } 116 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 117 | wheels = [ 118 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 119 | ] 120 | 121 | [[package]] 122 | name = "h2" 123 | version = "4.1.0" 124 | source = { registry = "https://pypi.org/simple" } 125 | dependencies = [ 126 | { name = "hpack" }, 127 | { name = "hyperframe" }, 128 | ] 129 | sdist = { url = "https://files.pythonhosted.org/packages/2a/32/fec683ddd10629ea4ea46d206752a95a2d8a48c22521edd70b142488efe1/h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb", size = 2145593 } 130 | wheels = [ 131 | { url = "https://files.pythonhosted.org/packages/2a/e5/db6d438da759efbb488c4f3fbdab7764492ff3c3f953132efa6b9f0e9e53/h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d", size = 57488 }, 132 | ] 133 | 134 | [[package]] 135 | name = "hpack" 136 | version = "4.0.0" 137 | source = { registry = "https://pypi.org/simple" } 138 | sdist = { url = "https://files.pythonhosted.org/packages/3e/9b/fda93fb4d957db19b0f6b370e79d586b3e8528b20252c729c476a2c02954/hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095", size = 49117 } 139 | wheels = [ 140 | { url = "https://files.pythonhosted.org/packages/d5/34/e8b383f35b77c402d28563d2b8f83159319b509bc5f760b15d60b0abf165/hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c", size = 32611 }, 141 | ] 142 | 143 | [[package]] 144 | name = "httpcore" 145 | version = "1.0.7" 146 | source = { registry = "https://pypi.org/simple" } 147 | dependencies = [ 148 | { name = "certifi" }, 149 | { name = "h11" }, 150 | ] 151 | sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } 152 | wheels = [ 153 | { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, 154 | ] 155 | 156 | [[package]] 157 | name = "httpx" 158 | version = "0.28.1" 159 | source = { registry = "https://pypi.org/simple" } 160 | dependencies = [ 161 | { name = "anyio" }, 162 | { name = "certifi" }, 163 | { name = "httpcore" }, 164 | { name = "idna" }, 165 | ] 166 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 167 | wheels = [ 168 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 169 | ] 170 | 171 | [[package]] 172 | name = "httpx-sse" 173 | version = "0.4.0" 174 | source = { registry = "https://pypi.org/simple" } 175 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 176 | wheels = [ 177 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 178 | ] 179 | 180 | [[package]] 181 | name = "hyperframe" 182 | version = "6.0.1" 183 | source = { registry = "https://pypi.org/simple" } 184 | sdist = { url = "https://files.pythonhosted.org/packages/5a/2a/4747bff0a17f7281abe73e955d60d80aae537a5d203f417fa1c2e7578ebb/hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914", size = 25008 } 185 | wheels = [ 186 | { url = "https://files.pythonhosted.org/packages/d7/de/85a784bcc4a3779d1753a7ec2dee5de90e18c7bcf402e71b51fcf150b129/hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15", size = 12389 }, 187 | ] 188 | 189 | [[package]] 190 | name = "idna" 191 | version = "3.10" 192 | source = { registry = "https://pypi.org/simple" } 193 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 194 | wheels = [ 195 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 196 | ] 197 | 198 | [[package]] 199 | name = "isort" 200 | version = "5.13.2" 201 | source = { registry = "https://pypi.org/simple" } 202 | sdist = { url = "https://files.pythonhosted.org/packages/87/f9/c1eb8635a24e87ade2efce21e3ce8cd6b8630bb685ddc9cdaca1349b2eb5/isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", size = 175303 } 203 | wheels = [ 204 | { url = "https://files.pythonhosted.org/packages/d1/b3/8def84f539e7d2289a02f0524b944b15d7c75dab7628bedf1c4f0992029c/isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6", size = 92310 }, 205 | ] 206 | 207 | [[package]] 208 | name = "jinja2" 209 | version = "3.1.4" 210 | source = { registry = "https://pypi.org/simple" } 211 | dependencies = [ 212 | { name = "markupsafe" }, 213 | ] 214 | sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } 215 | wheels = [ 216 | { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, 217 | ] 218 | 219 | [[package]] 220 | name = "markupsafe" 221 | version = "3.0.2" 222 | source = { registry = "https://pypi.org/simple" } 223 | sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } 224 | wheels = [ 225 | { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, 226 | { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, 227 | { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, 228 | { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, 229 | { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, 230 | { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, 231 | { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, 232 | { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, 233 | { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, 234 | { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, 235 | { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, 236 | { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, 237 | { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, 238 | { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, 239 | { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, 240 | { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, 241 | { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, 242 | { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, 243 | { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, 244 | { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, 245 | { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, 246 | { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, 247 | { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, 248 | { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, 249 | { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, 250 | { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, 251 | { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, 252 | { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, 253 | { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, 254 | { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, 255 | ] 256 | 257 | [[package]] 258 | name = "mcp" 259 | version = "1.1.0" 260 | source = { registry = "https://pypi.org/simple" } 261 | dependencies = [ 262 | { name = "anyio" }, 263 | { name = "httpx" }, 264 | { name = "httpx-sse" }, 265 | { name = "pydantic" }, 266 | { name = "sse-starlette" }, 267 | { name = "starlette" }, 268 | ] 269 | sdist = { url = "https://files.pythonhosted.org/packages/77/f2/067b1fc114e8d3ae4af02fc4f4ed8971a2c4900362d976fabe0f4e9a3418/mcp-1.1.0.tar.gz", hash = "sha256:e3c8d6df93a4de90230ea944dd667730744a3cd91a4cc0ee66a5acd53419e100", size = 83802 } 270 | wheels = [ 271 | { url = "https://files.pythonhosted.org/packages/b9/3e/aef19ac08a6f9a347c086c4e628c2f7329659828cbe92ffd524ec2aac833/mcp-1.1.0-py3-none-any.whl", hash = "sha256:44aa4d2e541f0924d6c344aa7f96b427a6ee1df2fab70b5f9ae2f8777b3f05f2", size = 36576 }, 272 | ] 273 | 274 | [[package]] 275 | name = "mcp-server-memos" 276 | version = "0.1.15" 277 | source = { editable = "." } 278 | dependencies = [ 279 | { name = "betterproto", extra = ["compiler"] }, 280 | { name = "grpclib" }, 281 | { name = "mcp" }, 282 | { name = "pydantic" }, 283 | ] 284 | 285 | [package.metadata] 286 | requires-dist = [ 287 | { name = "betterproto", extras = ["compiler"], specifier = ">=2.0.0b6" }, 288 | { name = "grpclib", specifier = ">=0.4.7" }, 289 | { name = "mcp", specifier = ">=1.1.0" }, 290 | { name = "pydantic", specifier = ">=2.10.3" }, 291 | ] 292 | 293 | [[package]] 294 | name = "multidict" 295 | version = "6.1.0" 296 | source = { registry = "https://pypi.org/simple" } 297 | sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 } 298 | wheels = [ 299 | { url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 }, 300 | { url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 }, 301 | { url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 }, 302 | { url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 }, 303 | { url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 }, 304 | { url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 }, 305 | { url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 }, 306 | { url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 }, 307 | { url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 }, 308 | { url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 }, 309 | { url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 }, 310 | { url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 }, 311 | { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 }, 312 | { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 }, 313 | { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 }, 314 | { url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 }, 315 | { url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 }, 316 | { url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 }, 317 | { url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 }, 318 | { url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 }, 319 | { url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 }, 320 | { url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 }, 321 | { url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 }, 322 | { url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 }, 323 | { url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 }, 324 | { url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 }, 325 | { url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 }, 326 | { url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 }, 327 | { url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 }, 328 | { url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 }, 329 | { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, 330 | ] 331 | 332 | [[package]] 333 | name = "mypy-extensions" 334 | version = "1.0.0" 335 | source = { registry = "https://pypi.org/simple" } 336 | sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 } 337 | wheels = [ 338 | { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 }, 339 | ] 340 | 341 | [[package]] 342 | name = "packaging" 343 | version = "24.2" 344 | source = { registry = "https://pypi.org/simple" } 345 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 346 | wheels = [ 347 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 348 | ] 349 | 350 | [[package]] 351 | name = "pathspec" 352 | version = "0.12.1" 353 | source = { registry = "https://pypi.org/simple" } 354 | sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043 } 355 | wheels = [ 356 | { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191 }, 357 | ] 358 | 359 | [[package]] 360 | name = "platformdirs" 361 | version = "4.3.6" 362 | source = { registry = "https://pypi.org/simple" } 363 | sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } 364 | wheels = [ 365 | { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, 366 | ] 367 | 368 | [[package]] 369 | name = "pydantic" 370 | version = "2.10.3" 371 | source = { registry = "https://pypi.org/simple" } 372 | dependencies = [ 373 | { name = "annotated-types" }, 374 | { name = "pydantic-core" }, 375 | { name = "typing-extensions" }, 376 | ] 377 | sdist = { url = "https://files.pythonhosted.org/packages/45/0f/27908242621b14e649a84e62b133de45f84c255eecb350ab02979844a788/pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9", size = 786486 } 378 | wheels = [ 379 | { url = "https://files.pythonhosted.org/packages/62/51/72c18c55cf2f46ff4f91ebcc8f75aa30f7305f3d726be3f4ebffb4ae972b/pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d", size = 456997 }, 380 | ] 381 | 382 | [[package]] 383 | name = "pydantic-core" 384 | version = "2.27.1" 385 | source = { registry = "https://pypi.org/simple" } 386 | dependencies = [ 387 | { name = "typing-extensions" }, 388 | ] 389 | sdist = { url = "https://files.pythonhosted.org/packages/a6/9f/7de1f19b6aea45aeb441838782d68352e71bfa98ee6fa048d5041991b33e/pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235", size = 412785 } 390 | wheels = [ 391 | { url = "https://files.pythonhosted.org/packages/be/51/2e9b3788feb2aebff2aa9dfbf060ec739b38c05c46847601134cc1fed2ea/pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f", size = 1895239 }, 392 | { url = "https://files.pythonhosted.org/packages/7b/9e/f8063952e4a7d0127f5d1181addef9377505dcce3be224263b25c4f0bfd9/pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02", size = 1805070 }, 393 | { url = "https://files.pythonhosted.org/packages/2c/9d/e1d6c4561d262b52e41b17a7ef8301e2ba80b61e32e94520271029feb5d8/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c", size = 1828096 }, 394 | { url = "https://files.pythonhosted.org/packages/be/65/80ff46de4266560baa4332ae3181fffc4488ea7d37282da1a62d10ab89a4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac", size = 1857708 }, 395 | { url = "https://files.pythonhosted.org/packages/d5/ca/3370074ad758b04d9562b12ecdb088597f4d9d13893a48a583fb47682cdf/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb", size = 2037751 }, 396 | { url = "https://files.pythonhosted.org/packages/b1/e2/4ab72d93367194317b99d051947c071aef6e3eb95f7553eaa4208ecf9ba4/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529", size = 2733863 }, 397 | { url = "https://files.pythonhosted.org/packages/8a/c6/8ae0831bf77f356bb73127ce5a95fe115b10f820ea480abbd72d3cc7ccf3/pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35", size = 2161161 }, 398 | { url = "https://files.pythonhosted.org/packages/f1/f4/b2fe73241da2429400fc27ddeaa43e35562f96cf5b67499b2de52b528cad/pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089", size = 1993294 }, 399 | { url = "https://files.pythonhosted.org/packages/77/29/4bb008823a7f4cc05828198153f9753b3bd4c104d93b8e0b1bfe4e187540/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381", size = 2001468 }, 400 | { url = "https://files.pythonhosted.org/packages/f2/a9/0eaceeba41b9fad851a4107e0cf999a34ae8f0d0d1f829e2574f3d8897b0/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb", size = 2091413 }, 401 | { url = "https://files.pythonhosted.org/packages/d8/36/eb8697729725bc610fd73940f0d860d791dc2ad557faaefcbb3edbd2b349/pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae", size = 2154735 }, 402 | { url = "https://files.pythonhosted.org/packages/52/e5/4f0fbd5c5995cc70d3afed1b5c754055bb67908f55b5cb8000f7112749bf/pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c", size = 1833633 }, 403 | { url = "https://files.pythonhosted.org/packages/ee/f2/c61486eee27cae5ac781305658779b4a6b45f9cc9d02c90cb21b940e82cc/pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16", size = 1986973 }, 404 | { url = "https://files.pythonhosted.org/packages/df/a6/e3f12ff25f250b02f7c51be89a294689d175ac76e1096c32bf278f29ca1e/pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e", size = 1883215 }, 405 | { url = "https://files.pythonhosted.org/packages/0f/d6/91cb99a3c59d7b072bded9959fbeab0a9613d5a4935773c0801f1764c156/pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073", size = 1895033 }, 406 | { url = "https://files.pythonhosted.org/packages/07/42/d35033f81a28b27dedcade9e967e8a40981a765795c9ebae2045bcef05d3/pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08", size = 1807542 }, 407 | { url = "https://files.pythonhosted.org/packages/41/c2/491b59e222ec7e72236e512108ecad532c7f4391a14e971c963f624f7569/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf", size = 1827854 }, 408 | { url = "https://files.pythonhosted.org/packages/e3/f3/363652651779113189cefdbbb619b7b07b7a67ebb6840325117cc8cc3460/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737", size = 1857389 }, 409 | { url = "https://files.pythonhosted.org/packages/5f/97/be804aed6b479af5a945daec7538d8bf358d668bdadde4c7888a2506bdfb/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2", size = 2037934 }, 410 | { url = "https://files.pythonhosted.org/packages/42/01/295f0bd4abf58902917e342ddfe5f76cf66ffabfc57c2e23c7681a1a1197/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107", size = 2735176 }, 411 | { url = "https://files.pythonhosted.org/packages/9d/a0/cd8e9c940ead89cc37812a1a9f310fef59ba2f0b22b4e417d84ab09fa970/pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51", size = 2160720 }, 412 | { url = "https://files.pythonhosted.org/packages/73/ae/9d0980e286627e0aeca4c352a60bd760331622c12d576e5ea4441ac7e15e/pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a", size = 1992972 }, 413 | { url = "https://files.pythonhosted.org/packages/bf/ba/ae4480bc0292d54b85cfb954e9d6bd226982949f8316338677d56541b85f/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc", size = 2001477 }, 414 | { url = "https://files.pythonhosted.org/packages/55/b7/e26adf48c2f943092ce54ae14c3c08d0d221ad34ce80b18a50de8ed2cba8/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960", size = 2091186 }, 415 | { url = "https://files.pythonhosted.org/packages/ba/cc/8491fff5b608b3862eb36e7d29d36a1af1c945463ca4c5040bf46cc73f40/pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23", size = 2154429 }, 416 | { url = "https://files.pythonhosted.org/packages/78/d8/c080592d80edd3441ab7f88f865f51dae94a157fc64283c680e9f32cf6da/pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05", size = 1833713 }, 417 | { url = "https://files.pythonhosted.org/packages/83/84/5ab82a9ee2538ac95a66e51f6838d6aba6e0a03a42aa185ad2fe404a4e8f/pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337", size = 1987897 }, 418 | { url = "https://files.pythonhosted.org/packages/df/c3/b15fb833926d91d982fde29c0624c9f225da743c7af801dace0d4e187e71/pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", size = 1882983 }, 419 | ] 420 | 421 | [[package]] 422 | name = "python-dateutil" 423 | version = "2.9.0.post0" 424 | source = { registry = "https://pypi.org/simple" } 425 | dependencies = [ 426 | { name = "six" }, 427 | ] 428 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } 429 | wheels = [ 430 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, 431 | ] 432 | 433 | [[package]] 434 | name = "six" 435 | version = "1.17.0" 436 | source = { registry = "https://pypi.org/simple" } 437 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } 438 | wheels = [ 439 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, 440 | ] 441 | 442 | [[package]] 443 | name = "sniffio" 444 | version = "1.3.1" 445 | source = { registry = "https://pypi.org/simple" } 446 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 447 | wheels = [ 448 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 449 | ] 450 | 451 | [[package]] 452 | name = "sse-starlette" 453 | version = "2.1.3" 454 | source = { registry = "https://pypi.org/simple" } 455 | dependencies = [ 456 | { name = "anyio" }, 457 | { name = "starlette" }, 458 | { name = "uvicorn" }, 459 | ] 460 | sdist = { url = "https://files.pythonhosted.org/packages/72/fc/56ab9f116b2133521f532fce8d03194cf04dcac25f583cf3d839be4c0496/sse_starlette-2.1.3.tar.gz", hash = "sha256:9cd27eb35319e1414e3d2558ee7414487f9529ce3b3cf9b21434fd110e017169", size = 19678 } 461 | wheels = [ 462 | { url = "https://files.pythonhosted.org/packages/52/aa/36b271bc4fa1d2796311ee7c7283a3a1c348bad426d37293609ca4300eef/sse_starlette-2.1.3-py3-none-any.whl", hash = "sha256:8ec846438b4665b9e8c560fcdea6bc8081a3abf7942faa95e5a744999d219772", size = 9383 }, 463 | ] 464 | 465 | [[package]] 466 | name = "starlette" 467 | version = "0.41.3" 468 | source = { registry = "https://pypi.org/simple" } 469 | dependencies = [ 470 | { name = "anyio" }, 471 | ] 472 | sdist = { url = "https://files.pythonhosted.org/packages/1a/4c/9b5764bd22eec91c4039ef4c55334e9187085da2d8a2df7bd570869aae18/starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835", size = 2574159 } 473 | wheels = [ 474 | { url = "https://files.pythonhosted.org/packages/96/00/2b325970b3060c7cecebab6d295afe763365822b1306a12eeab198f74323/starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7", size = 73225 }, 475 | ] 476 | 477 | [[package]] 478 | name = "typing-extensions" 479 | version = "4.12.2" 480 | source = { registry = "https://pypi.org/simple" } 481 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } 482 | wheels = [ 483 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, 484 | ] 485 | 486 | [[package]] 487 | name = "uvicorn" 488 | version = "0.32.1" 489 | source = { registry = "https://pypi.org/simple" } 490 | dependencies = [ 491 | { name = "click" }, 492 | { name = "h11" }, 493 | ] 494 | sdist = { url = "https://files.pythonhosted.org/packages/6a/3c/21dba3e7d76138725ef307e3d7ddd29b763119b3aa459d02cc05fefcff75/uvicorn-0.32.1.tar.gz", hash = "sha256:ee9519c246a72b1c084cea8d3b44ed6026e78a4a309cbedae9c37e4cb9fbb175", size = 77630 } 495 | wheels = [ 496 | { url = "https://files.pythonhosted.org/packages/50/c1/2d27b0a15826c2b71dcf6e2f5402181ef85acf439617bb2f1453125ce1f3/uvicorn-0.32.1-py3-none-any.whl", hash = "sha256:82ad92fd58da0d12af7482ecdb5f2470a04c9c9a53ced65b9bbb4a205377602e", size = 63828 }, 497 | ] 498 | --------------------------------------------------------------------------------