mirror of
https://github.com/kennethnym/aris.git
synced 2026-06-13 11:01:18 +01:00
feat: add google maps mcp source (#125)
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
"@freya/core": "workspace:*",
|
||||
"@freya/source-caldav": "workspace:*",
|
||||
"@freya/source-google-calendar": "workspace:*",
|
||||
"@freya/source-google-maps": "workspace:*",
|
||||
"@freya/source-location": "workspace:*",
|
||||
"@freya/source-tfl": "workspace:*",
|
||||
"@freya/source-weatherkit": "workspace:*",
|
||||
|
||||
55
apps/freya-backend/src/google-maps/provider.test.ts
Normal file
55
apps/freya-backend/src/google-maps/provider.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { GoogleMapsSourceOptions } from "@freya/source-google-maps"
|
||||
|
||||
import { describe, expect, test } from "bun:test"
|
||||
|
||||
import { GoogleMapsSourceProvider } from "./provider.ts"
|
||||
|
||||
type McpClient = NonNullable<GoogleMapsSourceOptions["client"]>
|
||||
|
||||
class MockMcpClient implements McpClient {
|
||||
async listTools(): ReturnType<McpClient["listTools"]> {
|
||||
return { tools: [] }
|
||||
}
|
||||
|
||||
async readResource(
|
||||
_params: Parameters<McpClient["readResource"]>[0],
|
||||
): ReturnType<McpClient["readResource"]> {
|
||||
throw new Error("unexpected resource read")
|
||||
}
|
||||
|
||||
async callTool(_params: Parameters<McpClient["callTool"]>[0]): ReturnType<McpClient["callTool"]> {
|
||||
return { structuredContent: {} }
|
||||
}
|
||||
}
|
||||
|
||||
describe("GoogleMapsSourceProvider", () => {
|
||||
test("sourceId is freya.google-maps", () => {
|
||||
const provider = new GoogleMapsSourceProvider({ apiKey: "key" })
|
||||
expect(provider.sourceId).toBe("freya.google-maps")
|
||||
})
|
||||
|
||||
test("throws when service API key is empty", () => {
|
||||
expect(() => new GoogleMapsSourceProvider({ apiKey: "" })).toThrow(
|
||||
"Google Maps MCP API key must be configured",
|
||||
)
|
||||
})
|
||||
|
||||
test("returns source with service API key", async () => {
|
||||
const provider = new GoogleMapsSourceProvider({ apiKey: "key" })
|
||||
|
||||
const source = await provider.feedSourceForUser("user-1", {}, null)
|
||||
|
||||
expect(source.id).toBe("freya.google-maps")
|
||||
})
|
||||
|
||||
test("allows injected test client with service API key", async () => {
|
||||
const provider = new GoogleMapsSourceProvider({
|
||||
apiKey: "key",
|
||||
client: new MockMcpClient(),
|
||||
})
|
||||
|
||||
const source = await provider.feedSourceForUser("user-1", {}, null)
|
||||
|
||||
expect(source.id).toBe("freya.google-maps")
|
||||
})
|
||||
})
|
||||
39
apps/freya-backend/src/google-maps/provider.ts
Normal file
39
apps/freya-backend/src/google-maps/provider.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { GoogleMapsSource, type GoogleMapsSourceOptions } from "@freya/source-google-maps"
|
||||
|
||||
import type { FeedSourceProvider } from "../session/feed-source-provider.ts"
|
||||
|
||||
export interface GoogleMapsSourceProviderOptions {
|
||||
readonly apiKey: string
|
||||
readonly client?: GoogleMapsSourceOptions["client"]
|
||||
}
|
||||
|
||||
export class GoogleMapsSourceProvider implements FeedSourceProvider {
|
||||
readonly sourceId = "freya.google-maps"
|
||||
|
||||
private readonly apiKey: string
|
||||
private readonly client: GoogleMapsSourceProviderOptions["client"]
|
||||
|
||||
constructor(options: GoogleMapsSourceProviderOptions) {
|
||||
if (!nonEmptyString(options.apiKey)) {
|
||||
throw new Error("Google Maps MCP API key must be configured")
|
||||
}
|
||||
|
||||
this.apiKey = options.apiKey
|
||||
this.client = options.client
|
||||
}
|
||||
|
||||
async feedSourceForUser(
|
||||
_userId: string,
|
||||
_config: unknown,
|
||||
_credentials: unknown,
|
||||
): Promise<GoogleMapsSource> {
|
||||
return new GoogleMapsSource({
|
||||
apiKey: this.apiKey,
|
||||
client: this.client,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function nonEmptyString(value: string): boolean {
|
||||
return typeof value === "string" && value.trim().length > 0
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { createDatabase } from "./db/index.ts"
|
||||
import { registerFeedHttpHandlers } from "./engine/http.ts"
|
||||
import { createFeedEnhancer } from "./enhancement/enhance-feed.ts"
|
||||
import { createLlmClient } from "./enhancement/llm-client.ts"
|
||||
import { GoogleMapsSourceProvider } from "./google-maps/provider.ts"
|
||||
import { CredentialEncryptor } from "./lib/crypto.ts"
|
||||
import { registerLocationHttpHandlers } from "./location/http.ts"
|
||||
import { LocationSourceProvider } from "./location/provider.ts"
|
||||
@@ -47,6 +48,11 @@ function main() {
|
||||
)
|
||||
}
|
||||
|
||||
const googleMapsApiKey = process.env.GOOGLE_MAPS_API_KEY ?? process.env.GOOGLE_MAPS_MCP_API_KEY
|
||||
if (!googleMapsApiKey) {
|
||||
throw new Error("GOOGLE_MAPS_API_KEY or GOOGLE_MAPS_MCP_API_KEY must be set")
|
||||
}
|
||||
|
||||
const sessionManager = new UserSessionManager({
|
||||
db,
|
||||
providers: [
|
||||
@@ -62,6 +68,9 @@ function main() {
|
||||
}),
|
||||
new TflSourceProvider({ apiKey: process.env.TFL_API_KEY! }),
|
||||
new WebSearchSourceProvider({ apiKey: process.env.EXA_API_KEY }),
|
||||
new GoogleMapsSourceProvider({
|
||||
apiKey: googleMapsApiKey,
|
||||
}),
|
||||
],
|
||||
feedEnhancer,
|
||||
credentialEncryptor,
|
||||
|
||||
Reference in New Issue
Block a user