■25/4/16 7:57PM
AIエージェント MCPサーバ
■MCPサーバによる連謳?
Model Context Protocol(MCP)の基軆??に関して、社内勉強臀??で使用したスライド資料を公開します! | DevelopersIO
roo-logger: Cline Memory Bankとは違うAIの鐔??憶システムを(MCPで・??作った理逕?
MCPサーバー自臀??入門
MCP入門
MCPを活用した検索システムの臀??り譁?/How to implement search systems with MCP #catalks - Speaker Deck
リモートMCPサーバーカタロ繧? #AWS - Qiita
プログラマー藹??見!FastAPI-MCP縺?AI時代縺?API開発を加速する方觸??(初心者向けコード付き) #Python - Qiita
MySQLのスキーマ情報を圧縮して觸??供するMCPサーバーを作った - $shibayu36->blog;
MCPを理解する - Speaker Deck
ローカ繝?RAGを手軽に觸??築できるMCPサーバーを作りました
[B! MCP] MCPサーバーを使って鐔??求書臀??成から送付まで自動化してみた隧?
Docker MCP Gatewayがすんばらしい👌 #AmazonQDeveloper - Qiita
社内縺?AIコーディング藹??入を加速するため前觸??知鐔??をまとめたガイドラインを書いた - Cluster Tech Blog
MCPアーキテクチャパター繝? - Carpe Diem
Cline縺?DDDと軆?? - コドモ繝? Product Team Blog
Gemini CLI の簡単チュートリア繝?
AIエージェントのサービス觸??築を検險?しているあなた縺?
【AI×開発軆??織Summit】AIと共につ縺?る、これからのデザインプロセス・??Figma AI縺?MCP Serverによる新しい協働_アーカイブ動逕? - YouTube
MCPの鐔??証と鐔??藹?? - MCP Meetup Tokyo 2025 - Speaker Deck
ADK を使用した AI エージェントの觸??築: 基軆??
ナレッジ觸??索・回答AIエージェントG-gen Tech AgentをADKで開発した事例 - G-gen Tech Blog
ADK縺?LLM Agent縺?Workflow Agents、Toolsを解説 - G-gen Tech Blog
ADK縺?Web UIによる評価とデバッ繧? - G-gen Tech Blog
[B! AI] Cline利用におけるデータの藹??り扱いについ縺? - サーバーワークスエンジニアブロ繧?
Cline駄目そう?一般的な鐔??約という声もある
Clineのデータの持ち譁?
一応通信はないらしい
Cline - AI Autonomous Coding Agent for VS Code
WE CLAIM NO OWNERSHIP RIGHTS OVER YOUR USER CONTENT. とはあるが再利用に使繧?ないという諢?味ではないらしい
Privacy. By using the Service, you acknowledge that we may collect, use, and disclose your personal information and aggregated and/or anonymized data as set forth in our Privacy Notice.とあるので縺?
■構成
MCPホスト:Cline等縺?AIエージェント↓MCPクライアント:Json設藹??縺?Proxy (これ以降が狭義MCPサーバ? あるいは全臀??縺?MCPサーバ)┣→ここでコマンドを打つように設定すればそのままローカルがEPになる ↓ (uv縺?Pythonパッケージ管理/仮想環藹??ツー繝?) uv run python src とかAPI EP
■外驛?APIに通信かローカルか2種饅??と考えていいstdio ローカルのサーバと通菫?remote リモートのサーバと通菫?(SSE→Streamable httpに移行荳?)
Server-Sent Events: クライアントからサーバへ縺?httpだが、藹??対縺?SSEで、正確に縺?http+SSE
ステートフル、AIとの臀??話は文脈が必要で長時間接続になる、この通信だとスケールできない
Sreamable http: ステートレス、通常はただ縺?httpでセッショ繝?IDによる状態管理があり必要な時縺?SSEへ動的アップグレードするメッセージ縺?JSON-RPC2.0
例)github-mcp-server GitHub - github/github-mcp-server: GitHub's official MCP Server ローカルにおきDockerを動かす蠖?
機能縺?3縺? Resources:事前に情報をファイルで読み込ませる感じ Prompts:プロンプトのテンプレ設定してお縺?感じ Tools:使うツールを設藹??してお縺?感じ
(MCPクライアント縺?2つ・??無意識でいい) Sampling:MCPを使うよ~ Roots:MCPを使うファイルシステム確鐔??~
これで人間が操作していた内容をMCPで藹??施するようにする
LLM→人間→Github/Slack/GCP etc.
↓LLM→MCP→Github/Slack/GCP etc.
■MPCサーバとは臀??者?Model Context Protocol (MCP)は、特縺?LLMを活用するアプリケーションにおいて、モデルとのやりとりを標準化するためのプロトコ繝?API EPやローカルプロセスをMCPサーバと鐔??っているケースもあるが、MCPの文脈からする縺?MCPサーバは仲介。MCPクライアントは藹??義縺?MCPサーバに含まれている
MCPにおける構成[ユーザ繝?] -> [LLM (MCPクライアント)] --> [MCPサーバ] -> [API/CLI/ローカルプロセス】Introduction - Model Context Protocol
■github-mcp-server設藹??
GitHub - github/github-mcp-server: GitHub's official MCP ServerCline縺?GitHub MCPで藹??現するPull Request作成の自動化 | DevelopersIOGithub トークン発鐔??はココで→ https://github.com/settings/tokens
クラシックトークン縺?repo全臀??でも良さそう(configure SSOもしてお縺?こ縺?)
MCP Marketplace>Remote server>Edit configuration>下記コードをコピペする{ "mcpServers": { "github": { "command": "docker", "args": [ "run", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "github_pat_xxxxxxx" }, "disabled": false, "autoApprove": [] } }}
上手く行かないので、Cline自身縺?MCPサーバの調整をしてもらうと臀??記となった (Docker、DockerGroup、Proxy等) { "mcpServers": { "github": { "command": "sg", "args": [ "docker", "-c", "docker run -i --rm -e GITHUB_PERSONAL_ACCESS_TOKEN -e http_proxy -e https_proxy ghcr.io/github/github-mcp-server stdio" ], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "github_pat_xxxxx", "http_proxy": "http://proxy:3128", "https_proxy": "http://proxy:3128" }, "disabled": false, "autoApprove": [] } }}
■ローカルサーバ
//// UVcurl -Ls https://astral.sh/uv/install.sh | shcd /mnt/c/Users/unco/Desktop/local_test/MCP/kuso-appuv venv 該藹??ディレクトリに仮想環藹??を設藹??source .venv/bin/activate アクティベートuv pip install google-cloud-assetuv pip freeze > requirements.txtpython3 main.py
deactivate 停豁?gcloud auth application-default login --no-launch-browser
gcloud auth login --no-launch-browser消したければ、venvを削除するだけ
上記縺?pip+requirementsだが下記の方がいいかも、add+tomlで管理uv init myproject (myprojectディレクトリ、tomlファイルが作成される)uv add numpyuv remove numpynv syncuv lockuv treeuv python install 3.10 3.11uv python listuv python pin 3.11uv tool install ruffuv tool listuvx pytest 一時的に仮装環藹??を汚さず実行uv exports --format-requirements.txt
権限確鐔??API(analyzeIamPolicy)Method: analyzeIamPolicy | Cloud Asset Inventory Documentation | Google Cloud
Try this method を使えばリクエストやURLが分かる (展開ボタンを押す:□の形)
curl \'https://cloudasset.googleapis.com/v1/projects/prj-xxxxx:analyzeIamPolicy?analysisQuery.accessSelector.roles roles%2 Fbigquery.dataowner&analysisQuery.identitySelector.identity=user%3Axxxxx%40xxxxx.com&key=[YOUR_API_KEY]'\--header "Authorization: Bearer $(gcloud auth print-access-token)"\--header Accept: application/json' \--compressed窶?APIキー臀??要だた、HTTPやJSの方觸??も出る
parentでエラーがでてもscopeという諢?蜻?> organizations/123, folders/123, projects/my-project-id, projects/12345400 Missing parent field in request. [field_violations {field: "parent"description: "Missing parent field in request." }
↑
■2023-04-24
Dialog flow ES - Chatbotの臀??り譁?
対話蠑?アプリの有効性・??何か対軆??をしときたい
Dialogflow:FAQのチャットボットを作りたい
intentをFAQの数だけ作る
intentグループがあれば軆??められるが、、
色は赤や青で分ける必要がある? entitityは色だが単語で分ける
entitiy: colors、単語:赤、類語:Red、朱色軆??
単語:白、類語:ホワイト等
entitity 個別項目・??色、サイズ、キーワード)トレーニングフェーズで使う用鐔??を設藹??してお縺?intent 諢?図・??選択、買う、確鐔??、支払)チャットで藹??きあたる項目で、数藹??く作ることになる 上縺?2つを結びつきを強縺?するcontext input context 該藹??のインテントの藹??遷の臀??つ前の別名を指定 output context(コンテキスト用エイリアス名) 該藹??のインテントの別名を設藹??する Training phrases(想藹??するユーザの入力文) Responses(答えへの藹??応
料金 無料の軆??囲である程度鐔??える項目を500から選ぶとしても大臀??夫 外部のデータソースを使う(できそう) 色を10個を選択するとしても大臀??螟?結果を外部に連携することもできそう
AI型は入力された質蝠?に対して、FAQから適切な回答を表示(正誤で学習し判藹??上がる)シナリオ型は入力値から分岐を判藹??して進む、あみだ縺?じタイプの藹??型処理に持ってい縺?
■Dialog flow ES下記の辺りを設藹??すれば、チャットボットが回答するようになるESとしては、ユーザ入力を構造化データにする処理を行いパラメータへ觸??索を觸??ける挙動となっている ユーザ鐔??動の履歴から検索ヒットの改善を学軆??するタイプ縺?AI
インテント分類(ユーザの諢?図のパターンを設藹??)┣トレーニングフレー繧?(質蝠?の臀??文を指定)┣アクショ繝? (何を実行するか指定)┣パラメー繧?/エンティテ繧?(質蝠?から抽出すると型を指定)┗レスポン繧? (何を返答するか指定)
コンテキスト: input (一つ前のインテント〉 縺? output (当インデントの別名)イベント:エンドユーザーの発鐔??からではな縺?、発生したイベントに基づいてインテントを呼び出すアクショ繝?: デフォルトfallbackに縺?input-unknown がついているパラメー繧?:下記縺?らいの考慮で良さそう@sys.any キーワードを設藹??してしまう?@sys.url@sys.person ロールや役職を設藹??してしまう?@sys.email
■Agent 設藹??Agent > ML setting > Train でトレーニングさせる。megaでな縺?普通のエージェントの方が分かり易い?Agent > environment > publish パブリッシュし公開?
■ intentテキストレスポンスにタグが使えずリンクにならない。ユーザエクスプレッションの単語をドラッグ藹??転させ techinical_terms 等で觸??索する縺?Entityを藹??映することができる。(Entity登録觸??みなら熟語でもOKだが、未登録なら単語で設定したような・??
■Entitiy 設藹??業務で使用している重要用鐔??縺? @technical_termsとして臀??り、揺ら縺?を全て入力したいBigQuery: BQ、ビッグクエリ、、、個人情蝣?: パーソナルインフォメーショ繝?, PII、、、
■Integration設藹??WebDemoを有効化DialogMessangerを有効化(こっちのがCoolで縺?
■FAQを簡単に觸??築するコツ検索を網軆??するように質蝠?を重要単語を全て入れた形で鐔??数設定する代表的な質蝠?をレスポンスに入れてしまう縺?FAQとして分かり易い 藹??考になりそうなの臀??例を回答します。 Q「ああああ」 A「いいい」
■思うような軆??果にならない場合のチューニン繧?トレーニングフレーズを追加するのが基譛?できるだけキーワードをEintity化することもよい方觸??Trainingに驕?去キーワードがあり正誤判藹??することができるValidationにトレーニングフレーズの追加等の問題觸??起が出ているので対蜃?-トレーニングフレーズ臀??足なら、ダミー觸??索を觸??け、Trainingでキーワード縺?intentを割り当てれば臀??時しの縺?はできる
■セキュリテ繧?
WebDemoは誰でも見れてしまう
Dialogflow Messangerをどこかのドメインで使う必要がある(CORS対藹??のデフォルトドメインが要る)し、誰でも見れてしまう
Cloud run等縺?Webインターフェイスを作りバックエンド縺?Dialog APIを使う必要がある
クイックスタート: API の操作 | Dialogflow ES | Google Cloud
Python client library | Google Cloud
Comment (0)
■24/12/27 9:25PM
GitHub Actions
ChatGPTかGeminiかに聞けば良さそう
■GitHub Actionsの動作内容はリポジトリ内に設定されている.github/workflows内縺?YMLにあるsteps: - name: Checkout uses: actions/checkout@v4
@以臀??はバージョン、特藹??のコミットSHAにもできる @3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4 Commits · actions/checkout · GitHubonセクション縺?PushやPR作成やスケジュール藹??行等のトリガーや対象ブランチやパス軆??も書かれているSecretsや環藹??変数は、Terraformでクラウドプロバイダーにアクセスする場合等で、GitHub Actions縺?secrets で鐔??証情報が設藹??されていることが多い。これらはリポジトリ縺?Settings > Secrets and variables > Actions で確認可能。GitHub Actions でのシークレットの使逕? - GitHub Docs
name: Deploy Terraform
on: push: branches: - main #この場合、mainブランチへ縺?pushでトリガーされる
jobs: terraform: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Setup Terraform uses: hashicorp/setup-terraform@v2 with: terraform_version: 1.4.0 - name: Initialize Terraform run: terraform init - name: Apply Terraform run: terraform apply-auto-approve #terraform planなしじゃ
Comment (0)
■24/9/5 11:34PM
Flask
■formaction (hidden以藹??で縺?Submit先変更方觸??) buttonや input縺?type=submit/image に臀??荳?できる ベースとなるの縺?form縺?id縺?formタ繧?<input... form="form縺?id">
<form id="form" method="post"><button type="submit" formaction="app.html" name="transition" value="a" form="form">戻る</button><button type="submit" formaction="ok.html" name="transition" value="b" form="form">送菫?</button>下記のような藹??性がある formaction formenctype formmethod formnovalidate formtarget
■jinja2【Flask】Jinja2のテンプレート継承縺?HTMLファイルを役割ごとに分割する | たぬ繝?ッ繧? (tanuhack.com)[Flask/Jinja2]extendsやincludeを使い、メンテしやすいテンプレート構築を目指す | pixelbeat sandboxJinjaテンプレートの書き方をがっつり調べてまとめてみた。 #Python - Qiita
///dictmy_dic['name'] = 1return render_template('index.html', message=my_dic)テンプレ蛛?{{message.name}}
///リストnum_list = np.arange(10)return render_template('index.html', message=num_list)テンプレ蛛?{% for num in num_list %}<div>{{ num }}</div>{% endfor %}
///改鐔??
{%- xxx -%} jinjaタグの前後マイナスで改鐔??空白を除藹??する
+縺?trim_blocks(改鐔??詰め)、Istrip_blocks(空白詰め)を無効化する設藹??で俺は使繧?ないfrom jinja2 import Environmentjinja_env = Environment()jinja_env.trim_blocks = Truejinja_env.Istrip_blocks = True
///置觸??
{{ "aaagh" | replace("a","oh,","2") }}
-> oh,oh,agh
{{ input['txtarea'] | replace("\n","<br />") }}
///エスケープ
safeフィルタがなけれ縺?htmlエスケープがかかる{{ output | safe }}
あるい縺?
{{% autoescape False %}}{{ output }}{{% endautoescape %}}
///コメント(複数鐔??ok){# note: commented-out we no longer use this {% for user in users %} 窶?#}
///生{% raw %} <ul> {% for item in seq %} <li>{{ item )}</li> {% endfor %} </ul>{% endraw %}
///extend 縺? block 縺? include笳?flaskpage=page_a.model(user)return render_template("layout.html",title=title,page=page)
笳?layout.html (テンプレ切替と共通ブロック、共通ブロックはテンプレに書いた方分かり易い?) {% if page['destination']== 'confirm' %} {% extends "template_confirm.html" %} {% elif page['destination']== 'complete' %} {% extends "template_complete.html" %} {% else %} {% extends "template_html" %} {% endif %} {% block body %} {% include "header.html" %} <p>content here.</p> {% endblock %}
笳?template.html <html> <body> <h1>{{ title }}</h1> {% block body %} {% endblock %}
#0以臀??の入力データの確認 (空の確認やintへのキャスト)#{% if 'inquiry_id' in page['input'] and page['input']['inquiry_id'] is not none and page['input']['inquiry_id'][int > 0 %}
{% if page[input']['inquiry_subject'] %} <input name="inquiry_subject" type="text" placeholder="例)a" value="{{ page['input']['inquiry_subject'] }}"> {% else %)} <input name="inquiry subject" type="text" placeholder="例)a"> {% endif %}
{% include "footer.html" %} </body> </html>
笳?header.html <p>date:2024-8-27</p>
笳?footer.html <p>author.p</p>
笳?page_a.pydef model(user):value_return = ""input = {}error = {}value_return <br>under constructions taken place by user '<br>'
#入力があった、if request.method == "POST": transition = request form.get("transition")#get, post, 藹??得するもので書き方が違う#request.args.get("inquiry_id")#request.form.get("inquiry_id"#request.json.get("inquiry_id") logging warming('#####' + user + 'transition: ' + str(transition)+'#####')
if transition == "new" or transition == "error" or transition == "confirm back": flag_ng=0 #入力値の判藹?? error_txt_inquiry_subject = [] inquiry_subject = request.form.get("inquiry_subject") ff not checkRequire(inquiry_subject): error_txt_inquiry_subject.append("件名が空觸??です”) flag_ng=1
input[inquiry_subject] = inquiry_subject if flag_ng == 1: error['error_txt_inquiry_subject] = error_txt_inquiry_subject #エラー画面を出す destination = "error" else: #確鐔??画面を出す destination = "confirm" #End of transition == "new" or transition == "error" or transition == "confirm back": else: #transition == "confirm_proceed" #登録処理し完了画面を出す destination = "complete"else: #初期画面を出す destination = "new"return ("value":value_return, "destination":destination, "input":input, "error":error)
■改鐔??
プレースホルダー内での改鐔??縺?
:に置き觸??える<textarea placeholder="例) aaa bbb">
JINJA2のフォーム入力後の確認HTMLの改鐔??縺??置觸??で縺?htmlエスケープが觸??かり<br>がそのまま表示されてしまいダ繝?{{ input | replace("\n", "<br>") }}
htmlエスケープ(HTMLエスケープ縺?<>&のみだった)窶? 入力があれ縺?htmlエスケープ+改鐔??<br/>変觸??窶? html表示はそのま縺?htmlエスケープ+改鐔??<br/>変觸??状態で出力窶? DBにそのま縺?htmlエスケープ+改鐔??<br/>変觸??状態で出力入れる窶? htmlフォーム内表示縺?htmlエスケープを解除し表示↓
入力値はそのま縺?input変数に臀??持confirm画面でエスケープ (html.escape()+改鐔??<br/>変觸??) しescape変数に臀??持DB保存時に縺?escape変数にプラスしてダブ繝?/シング繝?/バッククォート、セミコロン、バックスラッシュをエスケープし保存DBから藹??り出す際はそれらをアンエスケープしescape変数に臀??持Docへ縺?input変数で臀??存画面表示時はアンエスケープ (改鐔??<br />変觸??+html.unescape())
def escapeHtmlBr(text): if text is None: return text elif isinstance(text, list): list_escaped = [html.escape(item) for item in text] list_escaped [item.replace("\n', '<br>') for item in list_escaped] return list_escaped else: escaped_text = html escape(text)
return escaped_text.replace('\n', '<br>')
def unescapeHtmlBr(text): if text is None: return text elif isinstance(text, list): list_unescaped = [item.replace('<br>', '\n') for item in text] list_unescaped = [html.unescape(item) for item in list_unescaped] return list_unescaped else: text text.replace('<br>', '\n') return html.unescape(text)
def escapeDB(text): if text is None: return text elif isinstance(text, list): list_escaped = [item.replace(';', ';') for item in text] list_escaped = [item replace('"', '"') for item in list_escaped] list_escaped = [item.replace("'", ''') for item in list_escaped] list_escaped = [item.replace('\\', '\') for item in list_escaped] list_escaped = [item.replace('`', '`') for item in list_escaped] return list_escaped else: escaped_text = text.replace(';', ';') escaped_text = escaped_text.replace('"', '"') escaped_text = escaped_text.replace("'", ''') escaped_text = escaped_text.replace('\\', '\') escaped_text = escaped_text.replace('`', '`') return escaped text
def unescapeDB(text) if text is None return text elif isinstance(text, list): list_unescaped = [item replace('`', '`') for item in text] list_unescaped [item.replace('\','\\') for item in list_unescaped]
list_unescaped [item.replace(''', "'") for item in list_unescaped] list_unescaped [item.replace('"', '"') for item in list_unescaped] list_unescaped [item.replace(';', ';') for item in list_unescaped] return list_unescaped else: unescaped_text = text.replace('`','`') unescaped_text = unescaped_text.replace('\', '\\') unescaped_text = unescaped_text.replace(''', "'") unescaped_text unescaped_text replace('"', '"') unescaped_text = unescaped_text.replace(';', ';') return unescaped_text
■文字確鐔??def check_special_characters(a):| #チェックする記号のセット special_characters = ['<', '>', '"', '&'] #特藹??の鐔??号が含まれているかをチェッ繧? if any(char in a for char in special_characters): raise ValueError(f"変謨? 'a' に軆??止されている文字が含まれています: {a}")try: a = "Hello & World" check_special_characters(a)except ValueError as e: print(e)
■DB縺?null行の觸??髯?bq = bigquery.Client()sql = f"""SELECT a FROM `ds.b' WHERE c = '{pri}'"""results=bq.query(sql)list = list()for row in results: if row.a is not None: list.append(str(row.a))
■Flask-WTF CCSRF対軆??縺?hiddenに入れるhttps://qiita.com/RGS/items/c8c99970054a481ac80drequrement.txt Flask-WTF==1.2.1
from flask_wtf import CSRFProtectapp = Flask(__name__)app.config['SECRET_KEY'] = 'mysecretkey'csrf = CSRFProtect(app)
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
PRGパターン縺?GETで藹??了画面に鐔??縺?がエラーとならない、WTF縺?POSTのみ利縺?ようだ
●完了画面でセッション藹??数がない場合はエラーとする保存処理縺?doc_idをセッション藹??数に入れ不正で直接完了画面にい縺?とエラーとするhttps://xxxx.com/complete?doc id=ddd
■重複登録の回避軆??(PRG方藹??+JSボタン無蜉?)・リダイレクト POST/Redirect/GET パター繝?・連打を避けるためボタン縺?JS無効化片方のみのためquerySelectorAll() を使う>ページ遷移しな縺?なった>下記JSを使う
@app.route('/submit', methods=['POST'])def page(): page = model if complete: session['doc_id'] = page['input']['doc_id'] redirect(url_for('thank_you')) else: retrun template('layout.html')
@app.route('/thank_you')def thank_you(): if 'doc_id' in session: page['input']['doc_id"] = session['doc_id"] session clear() return render_template('complete_layout.html', page=page) else: return render_template('error.html", message="missing arg")
@app.errorhandler(404)def page_not_found(e) return render_template('error html', message="Page not found"), 404
error.html蛛?<h1>{{message }}</h1>
submit蛛?連打を避けるためボタンの無効化片方のみのためquerySelectorAll() を使うとページ遷移しな縺?なる等がある<script>document.addEventListener('DOMContentLoaded', function(){ const form = document.getElementById('form'); const buttons = form.getElementsByTagName('button'); form.onsubmit= function(event) { //クリックされたボタンを藹??得 const clickedButton = event.submitter; //隠しフィールドを追加して、ボタン縺?name 縺? value を送菫? const hiddenField = document.createElement('input'); hiddenField type = 'hidden' hiddenField.name = clickedButton.name; hiddenField.value = clickedButton value; form.appendChild(hiddenField); // すべてのボタンを無効化して臀??重送信を防止 for (let i = 0; i < buttons.length; i++) { buttons[i].disabled = true; } }}</script>
Comment (0)
Navi: 1 | 2 | 3 | 4 >
-Home
-Column [136]
-Europe [9]
-Gadget [79]
-Web [137]
-Bike [4]
@/// BANGBOO BLOG ///

