Skip to content

python: add emem geospatial memory MCP example for Streamable HTTP - #14228

Open
kumari-jaya wants to merge 4 commits into
microsoft:mainfrom
kumari-jaya:add-emem-mcp-example
Open

python: add emem geospatial memory MCP example for Streamable HTTP#14228
kumari-jaya wants to merge 4 commits into
microsoft:mainfrom
kumari-jaya:add-emem-mcp-example

Conversation

@kumari-jaya

Copy link
Copy Markdown

Description

Adds a sample showing how to use MCPStreamableHttpPlugin with emem — a public, no-auth, Streamable HTTP MCP server providing Ed25519-signed Earth observation facts for any place on Earth.

emem (emem.dev) is a good fit for an MCP sample because:

  • It uses Streamable HTTP transport (the MCPStreamableHttpPlugin path, as opposed to stdio or SSE)
  • No API key, no signup, no local server setup — works out of the box with just pip install semantic-kernel
  • Demonstrates a realistic multi-step agent flow: locate → recall → verify receipt

Sample walkthrough

python/samples/concepts/mcp/emem_mcp_geospatial_agent.py

The agent runs three example queries:

  1. Resolve South Mumbai → recall elevation → verify the fact_cid receipt
  2. "Is the air quality in Delhi safe for outdoor exercise right now?"
  3. "Has there been recent deforestation near the Amazon at -3.47° N, -62.22° W?"

Each response includes an emem:fact: token that can be verified offline without calling emem again.

Checklist

  • Follows the same pattern as agent_with_http_mcp_plugin.py
  • Uses MCPStreamableHttpPlugin (Streamable HTTP transport)
  • Uses ChatCompletionAgent + OpenAIChatCompletion
  • No local server or Docker required — remote endpoint at https://emem.dev/mcp
  • No API key required for emem reads (Apache 2.0)
  • EMEM_MCP_URL env var override supported

Testing

export OPENAI_API_KEY="sk-..."
cd python/samples/concepts/mcp
python emem_mcp_geospatial_agent.py

Demonstrates using MCPStreamableHttpPlugin to connect to emem
(https://emem.dev/mcp), a public no-auth Streamable HTTP MCP server
providing Ed25519-signed Earth observation facts.

The example shows a three-step verification chain:
  1. emem_locate — resolve a place name to a canonical cell64 address
  2. emem_recall — read signed facts (elevation, AQI, flood extent, etc.)
  3. Report the emem:fact: receipt for offline verification

No API key or signup is needed for emem reads (Apache 2.0).
Copilot AI review requested due to automatic review settings July 28, 2026 17:33
@kumari-jaya
kumari-jaya requested a review from a team as a code owner July 28, 2026 17:33

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 5 | Confidence: 93%

✓ Correctness

This is a straightforward new sample file that closely follows the existing agent_with_http_mcp_plugin.py pattern. The MCPStreamableHttpPlugin API usage is correct (name, url, description all match the constructor signature). The ChatCompletionAgent setup and get_response call pattern are correct. Thread reuse across the loop is intentional for multi-step conversation context. The only issue is the missing copyright header that all other files in this directory include.

✓ Security Reliability

This new sample file follows the exact same pattern as the existing agent_with_http_mcp_plugin.py. The MCPStreamableHttpPlugin is used correctly with valid parameters (name, url, description). Resource cleanup is properly handled via async with context manager (which calls connect()/close() with AsyncExitStack), and thread.delete() is guarded with if thread. The sampling_auto_approve defaults to False (safe default), so the remote MCP server cannot issue sampling requests. The EMEM_MCP_URL environment variable override is user-controlled input on the runner's own machine, consistent with how OPENAI_API_KEY and similar env vars are used across samples. No security or reliability issues found.

✓ Test Coverage

This PR adds a new MCP sample file (em_mcp_geospatial_agent.py) with no corresponding tests. However, this is consistent with the established repo pattern: MCP sample files are demonstration code excluded from pytest discovery via testpaths='tests' in pyproject.toml. The closest existing analog (agent_with_http_mcp_plugin.py) also has no tests. The underlying MCPStreamableHttpPlugin class is already well-tested in tests/unit/connectors/mcp/test_mcp.py. Since this sample requires a live external service (em.dev) and a valid OPENAI_API_KEY, adding automated tests would be impractical without extensive mocking. No test coverage gap exists beyond what the repo already accepts for all MCP samples.

✓ Failure Modes

This is a new sample file that follows the exact same patterns as the existing agent_with_http_mcp_plugin.py sample. The async context manager properly handles MCP plugin cleanup via __aenter__/__aexit__ (connect/close). The thread lifecycle is correct — shared across queries intentionally for multi-step verification, with delete after the loop. The await ternary on line 66 parses safely as (await thread.delete()) if thread else None (confirmed via AST). No silent failures, swallowed exceptions, resource leaks, or operational failure modes were found.

✓ Design Approach

I found one design inconsistency in the new sample: although the PR describes this as three example queries and says it follows the existing Streamable HTTP sample pattern, the implementation reuses a single chat thread across all prompts. That makes the second and third prompts context-dependent on the first exchange instead of exercising the MCP workflow as standalone examples.

Suggestions

  • Create and clean up a fresh ChatHistoryAgentThread for each entry in USER_INPUTS so each example query is independent, matching the pattern in python/samples/concepts/mcp/agent_with_http_mcp_plugin.py:43-56.

Automated review by kumari-jaya's agents

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Python sample under python/samples/concepts/mcp/ demonstrating how to use MCPStreamableHttpPlugin (Streamable HTTP transport) with the public emem MCP server to retrieve signed, verifiable geospatial facts.

Changes:

  • Introduces emem_mcp_geospatial_agent.py, a ChatCompletionAgent sample that connects to https://emem.dev/mcp (with EMEM_MCP_URL override) and runs multiple geospatial queries.
  • Demonstrates a multi-step “locate → recall → receipt” flow intended to surface verifiable emem:fact: receipts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1 to +16
"""Semantic Kernel + emem MCP example — multi-step verification: locate, recall, verify.

Connects a Microsoft Semantic Kernel agent to the emem MCP server over
Streamable HTTP and runs a multi-step verification chain for South Mumbai:
resolve the place, recall elevation, then verify the receipt/fact CID.

Install:
pip install semantic-kernel

Usage:
export OPENAI_API_KEY="sk-..."
python emem_mcp_geospatial_agent.py

The agent will resolve South Mumbai, recall its elevation, then verify
the receipt/fact CID, showing each step in the chain.
"""
url=EMEM_MCP_URL,
) as emem_plugin:
agent = ChatCompletionAgent(
service=OpenAIChatCompletion(ai_model_id="gpt-4o"),
Comment on lines +48 to +53
"You are a geospatial verification agent grounded in emem's signed Earth memory. "
"For every place query: "
"1. Call emem_locate to resolve the place to a canonical cell64 address. "
"2. Call emem_recall to read signed facts (air quality, elevation, flood extent, etc.). "
"3. Report the emem:fact: receipt so the user can verify it offline. "
"Always cite the fact_cid from the receipt."
Comment on lines +58 to +66
thread: ChatHistoryAgentThread | None = None

for user_input in USER_INPUTS:
print(f"\n# User: {user_input}")
response = await agent.get_response(messages=user_input, thread=thread)
print(f"# {response.name}: {response}")
thread = response.thread

await thread.delete() if thread else None
@kumari-jaya

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@kumari-jaya

Copy link
Copy Markdown
Author

Thanks for the Copilot review — addressed all four points:

  1. Docstring mismatch — updated to describe all three example queries and added OPENAI_CHAT_MODEL_ID to the usage block.
  2. Hardcoded model id — switched to OpenAIChatCompletion() so it reads from OPENAI_CHAT_MODEL_ID env, consistent with other samples.
  3. Tool name references in instructions — replaced emem_locate / emem_recall with generic phrasing ("emem's locate tool" / "emem's recall tool") to stay tool-name-agnostic.
  4. Thread reuse — each query now creates and deletes its own ChatHistoryAgentThread, matching the pattern in the other MCP samples.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants